(ae643deeb) Added alive checks to a couple of diving gear status effects (don't consume tanks when dead)

This commit is contained in:
Joonas Rikkonen
2019-04-01 22:47:22 +03:00
parent ec7e23061b
commit 2eaf22683d
129 changed files with 2451 additions and 1336 deletions
@@ -79,8 +79,8 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\ParamsEditor.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\GUI\ParamsEditor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\RectTransform.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\GUI\RectTransform.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\ShapeExtensions.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\GUI\ShapeExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\SpriteSheetPlayer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\UISprite.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\GUI\UISprite.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\VideoPlayer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\Widget.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\GUI\Widget.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\CharacterInventory.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Items\CharacterInventory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Door.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Door.cs" />
@@ -213,6 +213,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DeformAnimations\SpriteDeformation.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DeformAnimations\SpriteDeformation.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\Sprite.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\Sprite.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\SpriteSheet.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\SpriteSheet.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\StatusEffects\StatusEffect.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\LocalizationCSVtoXML.cs"> <Compile Include="$(MSBuildThisFileDirectory)Source\Utils\LocalizationCSVtoXML.cs">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Compile> </Compile>
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.9.6")] [assembly: AssemblyVersion("0.8.9.8")]
[assembly: AssemblyFileVersion("0.8.9.6")] [assembly: AssemblyFileVersion("0.8.9.8")]
@@ -13,13 +13,39 @@ namespace Barotrauma
var pos = new Vector2(WorldPosition.X, -WorldPosition.Y); var pos = new Vector2(WorldPosition.X, -WorldPosition.Y);
if (soundRange > 0.0f) if (soundRange > 0.0f)
{ {
Color color = Entity is Character ? Color.Yellow : Color.Orange; Color color;
if (Entity is Character)
{
color = Color.Yellow;
}
else if (Entity is Item)
{
color = Color.Orange;
}
else
{
color = Color.OrangeRed;
}
ShapeExtensions.DrawCircle(spriteBatch, pos, SoundRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom); ShapeExtensions.DrawCircle(spriteBatch, pos, SoundRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
ShapeExtensions.DrawCircle(spriteBatch, pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
} }
if (sightRange > 0.0f) if (sightRange > 0.0f)
{ {
Color color = Entity is Character ? Color.CornflowerBlue : Color.CadetBlue; Color color;
if (Entity is Character)
{
color = Color.CornflowerBlue;
}
else if (Entity is Item)
{
color = Color.CadetBlue;
}
else
{
color = Color.WhiteSmoke;
}
ShapeExtensions.DrawCircle(spriteBatch, pos, SightRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom); ShapeExtensions.DrawCircle(spriteBatch, pos, SightRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
ShapeExtensions.DrawCircle(spriteBatch, pos, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
} }
} }
} }
@@ -16,18 +16,17 @@ namespace Barotrauma
if (SelectedAiTarget?.Entity != null) if (SelectedAiTarget?.Entity != null)
{ {
GUI.DrawLine(spriteBatch, pos, new Vector2(SelectedAiTarget.WorldPosition.X, -SelectedAiTarget.WorldPosition.Y), Color.Red * 0.3f, 0, 5); GUI.DrawLine(spriteBatch, pos, new Vector2(SelectedAiTarget.WorldPosition.X, -SelectedAiTarget.WorldPosition.Y), Color.Red * 0.5f, 0, 4);
if (wallTarget != null) if (wallTarget != null)
{ {
Vector2 wallTargetPos = wallTarget.Position; Vector2 wallTargetPos = wallTarget.Position;
if (wallTarget.Structure.Submarine != null) wallTargetPos += wallTarget.Structure.Submarine.Position; if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.Position; }
wallTargetPos.Y = -wallTargetPos.Y; wallTargetPos.Y = -wallTargetPos.Y;
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Red, false); GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false);
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5); GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
} }
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity.ToString()} ({targetValue.FormatZeroDecimal()})", Color.Red, Color.Black);
GUI.Font.DrawString(spriteBatch, $"{SelectedAiTarget.Entity.ToString()} ({targetValue.ToString()})", pos - Vector2.UnitY * 20.0f, Color.Red);
} }
/*GUI.Font.DrawString(spriteBatch, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, Color.Red); /*GUI.Font.DrawString(spriteBatch, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, Color.Red);
@@ -52,24 +51,15 @@ namespace Barotrauma
} }
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black); GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black);
if (latchOntoAI != null) if (LatchOntoAI != null)
{ {
foreach (Joint attachJoint in latchOntoAI.AttachJoints) foreach (Joint attachJoint in LatchOntoAI.AttachJoints)
{ {
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorA.X, -attachJoint.WorldAnchorA.Y)), ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorA.X, -attachJoint.WorldAnchorA.Y)),
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), Color.Orange * 0.6f, 0, 5); ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), Color.Green, 0, 4);
} }
if (latchOntoAI.WallAttachPos.HasValue)
{
GUI.DrawLine(spriteBatch, pos,
ConvertUnits.ToDisplayUnits(new Vector2(latchOntoAI.WallAttachPos.Value.X, -latchOntoAI.WallAttachPos.Value.Y)), Color.Orange * 0.6f, 0, 3);
}
}
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);
if (LatchOntoAI.WallAttachPos.HasValue) if (LatchOntoAI.WallAttachPos.HasValue)
{ {
GUI.DrawLine(spriteBatch, pos, GUI.DrawLine(spriteBatch, pos,
@@ -77,22 +67,34 @@ namespace Barotrauma
} }
} }
GUI.DrawLine(spriteBatch, if (steeringManager is IndoorsSteeringManager pathSteering)
new Vector2(Character.DrawPosition.X, -Character.DrawPosition.Y),
new Vector2(pathSteering.CurrentPath.CurrentNode.DrawPosition.X, -pathSteering.CurrentPath.CurrentNode.DrawPosition.Y),
Color.Orange * 0.6f, 0, 3);
for (int i = 1; i < pathSteering.CurrentPath.Nodes.Count; i++)
{ {
GUI.DrawLine(spriteBatch, var path = pathSteering.CurrentPath;
new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y), if (path != null)
new Vector2(pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.Y), {
Color.Orange * 0.6f, 0, 3); if (path.CurrentNode != null)
{
GUI.DrawLine(spriteBatch, pos,
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
Color.DarkViolet, 0, 3);
GUI.SmallFont.DrawString(spriteBatch, GUI.DrawString(spriteBatch, pos - new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
pathSteering.CurrentPath.Nodes[i].ID.ToString(), }
new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y - 10), for (int i = 1; i < path.Nodes.Count; i++)
Color.LightGreen); {
var previousNode = path.Nodes[i - 1];
var currentNode = path.Nodes[i];
GUI.DrawLine(spriteBatch,
new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y),
new Vector2(previousNode.DrawPosition.X, -previousNode.DrawPosition.Y),
Color.Red * 0.5f, 0, 3);
GUI.SmallFont.DrawString(spriteBatch,
currentNode.ID.ToString(),
new Vector2(currentNode.DrawPosition.X + 20, -currentNode.DrawPosition.Y - 20),
Color.Red);
}
}
} }
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2); GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2);
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3); GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);
@@ -75,11 +75,12 @@ namespace Barotrauma
GUI.SmallFont.DrawString(spriteBatch, GUI.SmallFont.DrawString(spriteBatch,
currentNode.ID.ToString(), currentNode.ID.ToString(),
new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y - 10), new Vector2(currentNode.DrawPosition.X + 20, -currentNode.DrawPosition.Y - 20),
Color.LightGreen); Color.SkyBlue);
} }
} }
} }
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2);
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3); GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);
//if (Character.IsKeyDown(InputType.Aim)) //if (Character.IsKeyDown(InputType.Aim))
@@ -88,13 +88,13 @@ namespace Barotrauma
Collider.AngularVelocity = newAngularVelocity; Collider.AngularVelocity = newAngularVelocity;
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition); float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
float errorTolerance = character.AllowInput ? 0.01f : 0.2f; float errorTolerance = character.AllowInput ? 0.01f : 0.1f;
if (distSqrd > errorTolerance) if (distSqrd > errorTolerance)
{ {
if (distSqrd > 10.0f || !character.AllowInput) if (distSqrd > 10.0f || !character.AllowInput)
{ {
Collider.TargetRotation = newRotation; Collider.TargetRotation = newRotation;
SetPosition(newPosition, lerp: distSqrd < 5.0f); SetPosition(newPosition, lerp: distSqrd < 1.0f);
} }
else else
{ {
@@ -108,15 +108,8 @@ namespace Barotrauma
// -> we need to correct it manually // -> we need to correct it manually
if (!character.AllowInput) if (!character.AllowInput)
{ {
float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition); MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
float mainLimbErrorTolerance = 0.1f; MainLimb.PullJointEnabled = true;
//if the main limb is roughly at the correct position and the collider isn't moving (much at least),
//don't attempt to correct the position.
if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f)
{
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
MainLimb.PullJointEnabled = true;
}
} }
} }
character.MemLocalState.Clear(); character.MemLocalState.Clear();
@@ -224,6 +224,20 @@ namespace Barotrauma
} }
} }
if (SelectedConstruction != null && SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)))
{
if (GameMain.Client != null)
{
//emulate a Select input to get the character to deselect the item server-side
keys[(int)InputType.Select].Hit = true;
}
//reset focus to prevent us from accidentally interacting with another entity
focusedItem = null;
focusedCharacter = null;
findFocusedTimer = 0.2f;
SelectedConstruction = null;
}
DoInteractionUpdate(deltaTime, mouseSimPos); DoInteractionUpdate(deltaTime, mouseSimPos);
} }
@@ -327,6 +341,8 @@ namespace Barotrauma
debugInteractablesAtCursor.Clear(); debugInteractablesAtCursor.Clear();
debugInteractablesNearCursor.Clear(); debugInteractablesNearCursor.Clear();
bool draggingItemToWorld = CharacterInventory.DraggingItemToWorld;
//reduce the amount of aim assist if an item has been selected //reduce the amount of aim assist if an item has been selected
//= can't switch selection to another item without deselecting the current one first UNLESS the cursor is directly on the item //= can't switch selection to another item without deselecting the current one first UNLESS the cursor is directly on the item
//otherwise it would be too easy to accidentally switch the selected item when rewiring items //otherwise it would be too easy to accidentally switch the selected item when rewiring items
@@ -348,6 +364,15 @@ namespace Barotrauma
if (item.body != null && !item.body.Enabled) continue; if (item.body != null && !item.body.Enabled) continue;
if (item.ParentInventory != null) continue; if (item.ParentInventory != null) continue;
if (ignoredItems != null && ignoredItems.Contains(item)) continue; if (ignoredItems != null && ignoredItems.Contains(item)) continue;
if (draggingItemToWorld)
{
if (item.OwnInventory == null ||
!item.OwnInventory.CanBePut(CharacterInventory.draggingItem))
{
continue;
}
}
float distanceToItem = float.PositiveInfinity; float distanceToItem = float.PositiveInfinity;
if (item.IsInsideTrigger(displayPosition, out Rectangle transformedTrigger)) if (item.IsInsideTrigger(displayPosition, out Rectangle transformedTrigger))
@@ -248,7 +248,7 @@ namespace Barotrauma
scale: scale); scale: scale);
} }
if (!GUI.DisableItemHighlights) if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
{ {
var hudTexts = focusedItem.GetHUDTexts(character); var hudTexts = focusedItem.GetHUDTexts(character);
@@ -241,10 +241,12 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(jobIdentifier)) if (!string.IsNullOrEmpty(jobIdentifier))
{ {
jobPrefab = JobPrefab.List.Find(jp => jp.Identifier == jobIdentifier); jobPrefab = JobPrefab.List.Find(jp => jp.Identifier == jobIdentifier);
for (int i = 0; i < jobPrefab.Skills.Count; i++) byte skillCount = inc.ReadByte();
for (int i = 0; i < skillCount; i++)
{ {
string skillIdentifier = inc.ReadString();
float skillLevel = inc.ReadSingle(); float skillLevel = inc.ReadSingle();
skillLevels.Add(jobPrefab.Skills[i].Identifier, skillLevel); skillLevels.Add(skillIdentifier, skillLevel);
} }
} }
@@ -254,7 +256,6 @@ namespace Barotrauma
ID = infoID, ID = infoID,
}; };
ch.RecreateHead(headSpriteID,(Race)race, (Gender)gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex); ch.RecreateHead(headSpriteID,(Race)race, (Gender)gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
System.Diagnostics.Debug.Assert(skillLevels.Count == ch.Job.Skills.Count);
if (ch.Job != null) if (ch.Job != null)
{ {
foreach (KeyValuePair<string, float> skill in skillLevels) foreach (KeyValuePair<string, float> skill in skillLevels)
@@ -262,11 +263,12 @@ namespace Barotrauma
Skill matchingSkill = ch.Job.Skills.Find(s => s.Identifier == skill.Key); Skill matchingSkill = ch.Job.Skills.Find(s => s.Identifier == skill.Key);
if (matchingSkill == null) if (matchingSkill == null)
{ {
DebugConsole.ThrowError("Skill \"" + skill.Key + "\" not found in character \"" + newName + "\""); ch.Job.Skills.Add(new Skill(skill.Key, skill.Value));
continue; continue;
} }
matchingSkill.Level = skill.Value; matchingSkill.Level = skill.Value;
} }
ch.Job.Skills.RemoveAll(s => !skillLevels.ContainsKey(s.Identifier));
} }
return ch; return ch;
} }
@@ -3,7 +3,6 @@ using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Linq; using System.Linq;
using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
{ {
@@ -68,6 +68,14 @@ namespace Barotrauma
private float dropItemAnimDuration = 0.5f; private float dropItemAnimDuration = 0.5f;
private float dropItemAnimTimer; private float dropItemAnimTimer;
public Item DroppedItem
{
get
{
return droppedItem;
}
}
private Item droppedItem; private Item droppedItem;
private GUIComponent draggingMed; private GUIComponent draggingMed;
@@ -100,13 +108,15 @@ namespace Barotrauma
if (openHealthWindow == value) return; if (openHealthWindow == value) return;
if (value != null && !value.UseHealthWindow) return; if (value != null && !value.UseHealthWindow) return;
var prevOpenHealthWindow = openHealthWindow;
openHealthWindow = value; openHealthWindow = value;
toggledThisFrame = true; toggledThisFrame = true;
if (Character.Controlled == null) { return; } if (Character.Controlled == null) { return; }
if (value == null && if (value == null &&
Character.Controlled?.SelectedCharacter?.CharacterHealth != null && Character.Controlled?.SelectedCharacter?.CharacterHealth != null &&
Character.Controlled.SelectedCharacter.CharacterHealth == openHealthWindow && Character.Controlled.SelectedCharacter.CharacterHealth == prevOpenHealthWindow &&
!Character.Controlled.SelectedCharacter.CanInventoryBeAccessed) !Character.Controlled.SelectedCharacter.CanInventoryBeAccessed)
{ {
Character.Controlled.DeselectCharacter(); Character.Controlled.DeselectCharacter();
@@ -537,11 +547,13 @@ namespace Barotrauma
} }
if (PlayerInput.KeyHit(InputType.Health) && GUI.KeyboardDispatcher.Subscriber == null && if (PlayerInput.KeyHit(InputType.Health) && GUI.KeyboardDispatcher.Subscriber == null &&
Character.AllowInput && Character.FocusedCharacter == null && !toggledThisFrame) Character.Controlled.AllowInput && !toggledThisFrame)
{ {
if (openHealthWindow != null) if (openHealthWindow != null)
{
OpenHealthWindow = null; OpenHealthWindow = null;
else }
else if (Character.Controlled == Character && Character.Controlled.FocusedCharacter == null)
{ {
OpenHealthWindow = this; OpenHealthWindow = this;
forceAfflictionContainerUpdate = true; forceAfflictionContainerUpdate = true;
@@ -554,7 +566,10 @@ namespace Barotrauma
HUD.CloseHUD(HUDLayoutSettings.HealthWindowAreaLeft)) HUD.CloseHUD(HUDLayoutSettings.HealthWindowAreaLeft))
{ {
//emulate a Health input to get the character to deselect the item server-side //emulate a Health input to get the character to deselect the item server-side
Character.Keys[(int)InputType.Health].Hit = true; if (GameMain.Client != null)
{
Character.Controlled.Keys[(int)InputType.Health].Hit = true;
}
OpenHealthWindow = null; OpenHealthWindow = null;
} }
} }
@@ -403,6 +403,11 @@ namespace Barotrauma
AssignRelayToServer("help", false); AssignRelayToServer("help", false);
AssignRelayToServer("verboselogging", false); AssignRelayToServer("verboselogging", false);
AssignRelayToServer("freecam", false); AssignRelayToServer("freecam", false);
#if DEBUG
AssignRelayToServer("simulatedlatency", false);
AssignRelayToServer("simulatedloss", false);
AssignRelayToServer("simulatedduplicateschance", false);
#endif
commands.Add(new Command("clientlist", "", (string[] args) => { })); commands.Add(new Command("clientlist", "", (string[] args) => { }));
AssignRelayToServer("clientlist", true); AssignRelayToServer("clientlist", true);
@@ -957,6 +962,8 @@ namespace Barotrauma
} }
} }
} }
element.Value = lines[i];
i++;
} }
}, isCheat: false)); }, isCheat: false));
#endif #endif
@@ -178,10 +178,15 @@ namespace Barotrauma
string displayedText = message.TranslatedText; string displayedText = message.TranslatedText;
string senderName = ""; string senderName = "";
Color senderColor = Color.White;
if (!string.IsNullOrWhiteSpace(message.SenderName)) if (!string.IsNullOrWhiteSpace(message.SenderName))
{ {
senderName = (message.Type == ChatMessageType.Private ? "[PM] " : "") + message.SenderName; senderName = (message.Type == ChatMessageType.Private ? "[PM] " : "") + message.SenderName;
} }
if (message.Sender?.Info?.Job != null)
{
senderColor = Color.Lerp(message.Sender.Info.Job.Prefab.UIColor, Color.White, 0.25f);
}
var msgHolder = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.0f), chatBox.Content.RectTransform, Anchor.TopCenter), style: null, var msgHolder = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.0f), chatBox.Content.RectTransform, Anchor.TopCenter), style: null,
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f); color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f);
@@ -191,7 +196,7 @@ namespace Barotrauma
{ {
senderNameBlock = new GUITextBlock(new RectTransform(new Vector2(0.98f, 0.0f), msgHolder.RectTransform) senderNameBlock = new GUITextBlock(new RectTransform(new Vector2(0.98f, 0.0f), msgHolder.RectTransform)
{ AbsoluteOffset = new Point((int)(5 * GUI.Scale), 0) }, { AbsoluteOffset = new Point((int)(5 * GUI.Scale), 0) },
senderName, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null) senderName, textColor: senderColor, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null)
{ {
CanBeFocused = true CanBeFocused = true
}; };
@@ -230,7 +235,7 @@ namespace Barotrauma
Visible = false Visible = false
}; };
var senderText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), popupMsg.RectTransform, Anchor.TopRight), var senderText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), popupMsg.RectTransform, Anchor.TopRight),
senderName, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopRight) senderName, textColor: senderColor, font: GUI.SmallFont, textAlignment: Alignment.TopRight)
{ {
CanBeFocused = false CanBeFocused = false
}; };
@@ -328,6 +333,7 @@ namespace Barotrauma
var popupMsg = popupMessages.Count > 0 ? popupMessages.Peek() : null; var popupMsg = popupMessages.Count > 0 ? popupMessages.Peek() : null;
if (popupMsg != null) if (popupMsg != null)
{ {
int offset = -popupMsg.Rect.Width - toggleButton.Rect.Width * 2 - (int)(50 * GUI.Scale) - (guiFrame.Rect.X - GameMain.GraphicsWidth);
popupMsg.Visible = true; popupMsg.Visible = true;
//popup messages appear and disappear faster when there's more pending messages //popup messages appear and disappear faster when there's more pending messages
popupMessageTimer += deltaTime * popupMessages.Count * popupMessages.Count; popupMessageTimer += deltaTime * popupMessages.Count * popupMessages.Count;
@@ -335,7 +341,7 @@ namespace Barotrauma
{ {
//move the message out of the screen and delete it //move the message out of the screen and delete it
popupMsg.RectTransform.ScreenSpaceOffset = popupMsg.RectTransform.ScreenSpaceOffset =
new Point((int)MathHelper.SmoothStep(-popupMsg.Rect.Width - toggleButton.Rect.Width * 2, 10, (popupMessageTimer - PopupMessageDuration) * 5.0f), 0); new Point((int)MathHelper.SmoothStep(offset, 10, (popupMessageTimer - PopupMessageDuration) * 5.0f), 0);
if (popupMessageTimer > PopupMessageDuration + 1.0f) if (popupMessageTimer > PopupMessageDuration + 1.0f)
{ {
popupMessageTimer = 0.0f; popupMessageTimer = 0.0f;
@@ -347,7 +353,7 @@ namespace Barotrauma
{ {
//move the message on the screen //move the message on the screen
popupMsg.RectTransform.ScreenSpaceOffset = new Point( popupMsg.RectTransform.ScreenSpaceOffset = new Point(
(int)MathHelper.SmoothStep(0, -popupMsg.Rect.Width - toggleButton.Rect.Width * 2 - (int)(35 * GUI.Scale), popupMessageTimer * 5.0f), 0); (int)MathHelper.SmoothStep(0, offset, popupMessageTimer * 5.0f), 0);
} }
} }
} }
+21 -8
View File
@@ -56,7 +56,7 @@ namespace Barotrauma
private static List<GUIMessage> messages = new List<GUIMessage>(); private static List<GUIMessage> messages = new List<GUIMessage>();
private static Sound[] sounds; private static Sound[] sounds;
private static bool pauseMenuOpen, settingsMenuOpen; private static bool pauseMenuOpen, settingsMenuOpen;
private static GUIFrame pauseMenu; public static GUIFrame PauseMenu { get; private set; }
private static Sprite arrow, lockIcon, checkmarkIcon, timerIcon; private static Sprite arrow, lockIcon, checkmarkIcon, timerIcon;
public static KeyboardDispatcher KeyboardDispatcher { get; set; } public static KeyboardDispatcher KeyboardDispatcher { get; set; }
@@ -69,6 +69,9 @@ namespace Barotrauma
public static ScalableFont Font => Style?.Font; public static ScalableFont Font => Style?.Font;
public static ScalableFont SmallFont => Style?.SmallFont; public static ScalableFont SmallFont => Style?.SmallFont;
public static ScalableFont LargeFont => Style?.LargeFont; public static ScalableFont LargeFont => Style?.LargeFont;
public static ScalableFont VideoTitleFont => Style?.VideoTitleFont;
public static ScalableFont ObjectiveTitleFont => Style?.ObjectiveTitleFont;
public static ScalableFont ObjectiveNameFont => Style?.ObjectiveNameFont;
public static UISprite UIGlow => Style.UIGlow; public static UISprite UIGlow => Style.UIGlow;
@@ -530,14 +533,19 @@ namespace Barotrauma
if (list.Count == 0) { return; } if (list.Count == 0) { return; }
foreach (var item in list) foreach (var item in list)
{ {
int i = updateList.Count - 1; int index = 0;
while (updateList[i].UpdateOrder > item.UpdateOrder) if (updateList.Count > 0)
{ {
i--; index = updateList.Count - 1;
while (updateList[index].UpdateOrder > item.UpdateOrder)
{
index--;
if (index == 0) { break; }
}
} }
if (!updateListSet.Contains(item)) if (!updateListSet.Contains(item))
{ {
updateList.Insert(Math.Max(i, 0), item); updateList.Insert(index, item);
updateListSet.Add(item); updateListSet.Add(item);
} }
} }
@@ -553,7 +561,7 @@ namespace Barotrauma
if (pauseMenuOpen) if (pauseMenuOpen)
{ {
pauseMenu.AddToGUIUpdateList(); PauseMenu.AddToGUIUpdateList();
} }
if (settingsMenuOpen) if (settingsMenuOpen)
{ {
@@ -648,6 +656,7 @@ namespace Barotrauma
msg.Timer -= deltaTime; msg.Timer -= deltaTime;
msg.Pos += msg.Velocity * deltaTime; msg.Pos += msg.Velocity * deltaTime;
} }
}
messages.RemoveAll(m => m.Timer <= 0.0f); messages.RemoveAll(m => m.Timer <= 0.0f);
} }
@@ -721,6 +730,10 @@ namespace Barotrauma
Vector2 textSize = font.MeasureString(text); Vector2 textSize = font.MeasureString(text);
DrawRectangle(sb, pos - Vector2.One * backgroundPadding, textSize + Vector2.One * 2.0f * backgroundPadding, (Color)backgroundColor, true); DrawRectangle(sb, pos - Vector2.One * backgroundPadding, textSize + Vector2.One * 2.0f * backgroundPadding, (Color)backgroundColor, true);
} }
else
{
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y + rect.Height - thickness, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
font.DrawString(sb, text, pos, color); font.DrawString(sb, text, pos, color);
} }
@@ -1412,9 +1425,9 @@ namespace Barotrauma
if (pauseMenuOpen) if (pauseMenuOpen)
{ {
pauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas), style: null, color: Color.Black * 0.5f); PauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas), style: null, color: Color.Black * 0.5f);
var pauseMenuInner = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.3f), pauseMenu.RectTransform, Anchor.Center) { MinSize = new Point(200, 300) }); var pauseMenuInner = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.3f), PauseMenu.RectTransform, Anchor.Center) { MinSize = new Point(200, 300) });
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.85f, 0.85f), pauseMenuInner.RectTransform, Anchor.Center)) var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.85f, 0.85f), pauseMenuInner.RectTransform, Anchor.Center))
{ {
@@ -12,6 +12,9 @@ namespace Barotrauma
public ScalableFont Font { get; private set; } public ScalableFont Font { get; private set; }
public ScalableFont SmallFont { get; private set; } public ScalableFont SmallFont { get; private set; }
public ScalableFont LargeFont { get; private set; } public ScalableFont LargeFont { get; private set; }
public ScalableFont VideoTitleFont { get; private set; }
public ScalableFont ObjectiveTitleFont { get; private set; }
public ScalableFont ObjectiveNameFont { get; private set; }
public Sprite CursorSprite { get; private set; } public Sprite CursorSprite { get; private set; }
@@ -48,6 +51,15 @@ namespace Barotrauma
case "largefont": case "largefont":
LargeFont = new ScalableFont(subElement, graphicsDevice); LargeFont = new ScalableFont(subElement, graphicsDevice);
break; break;
case "objectivetitle":
ObjectiveTitleFont = new ScalableFont(subElement, graphicsDevice);
break;
case "objectivename":
ObjectiveNameFont = new ScalableFont(subElement, graphicsDevice);
break;
case "videotitle":
VideoTitleFont = new ScalableFont(subElement, graphicsDevice);
break;
case "cursor": case "cursor":
CursorSprite = new Sprite(subElement); CursorSprite = new Sprite(subElement);
break; break;
@@ -50,6 +50,11 @@ namespace Barotrauma
get; private set; get; private set;
} }
public static Rectangle ObjectiveAnchor
{
get; private set;
}
public static Rectangle InventoryAreaLower public static Rectangle InventoryAreaLower
{ {
get; private set; get; private set;
@@ -156,6 +161,10 @@ namespace Barotrauma
new Rectangle(Padding, CrewArea.Y, chatBoxWidth, chatBoxHeight) : new Rectangle(Padding, CrewArea.Y, chatBoxWidth, chatBoxHeight) :
new Rectangle(GameMain.GraphicsWidth - Padding - chatBoxWidth, CrewArea.Y, chatBoxWidth, chatBoxHeight); new Rectangle(GameMain.GraphicsWidth - Padding - chatBoxWidth, CrewArea.Y, chatBoxWidth, chatBoxHeight);
int objectiveAnchorWidth = (int)(250 * GUI.Scale);
int objectiveAnchorOffsetY = (int)(100 * GUI.Scale);
ObjectiveAnchor = new Rectangle(GameMain.GraphicsWidth - Padding - objectiveAnchorWidth, CrewArea.Y + crewAreaHeight + objectiveAnchorOffsetY, objectiveAnchorWidth, 0);
int lowerAreaHeight = (int)Math.Min(GameMain.GraphicsHeight * 0.25f, 280); int lowerAreaHeight = (int)Math.Min(GameMain.GraphicsHeight * 0.25f, 280);
InventoryAreaLower = new Rectangle(Padding, GameMain.GraphicsHeight - lowerAreaHeight, GameMain.GraphicsWidth - Padding * 2, lowerAreaHeight); InventoryAreaLower = new Rectangle(Padding, GameMain.GraphicsHeight - lowerAreaHeight, GameMain.GraphicsWidth - Padding * 2, lowerAreaHeight);
@@ -191,6 +200,9 @@ namespace Barotrauma
{ {
public static bool CloseHUD(Rectangle rect) public static bool CloseHUD(Rectangle rect)
{ {
// Always close when hitting escape
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) { return true; }
//don't close when the cursor is on a UI element //don't close when the cursor is on a UI element
if (GUI.MouseOn != null) return false; if (GUI.MouseOn != null) return false;
@@ -601,8 +601,10 @@ namespace Barotrauma
{ {
((GUIMessageBox)GUIMessageBox.VisibleBox).Close(); ((GUIMessageBox)GUIMessageBox.VisibleBox).Close();
} }
else // Otherwise toggle pausing. else if ((Character.Controlled?.SelectedConstruction == null || !Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null))
&& Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null)
{ {
// Otherwise toggle pausing, unless another window/interface is open.
GUI.TogglePauseMenu(); GUI.TogglePauseMenu();
} }
} }
@@ -130,7 +130,12 @@ namespace Barotrauma
{ {
OnEnterMessage = (textbox, text) => OnEnterMessage = (textbox, text) =>
{ {
if (Character.Controlled == null) { return true; } if (Character.Controlled?.Info == null)
{
textbox.Deselect();
textbox.Text = "";
return true;
}
textbox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default]; textbox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default];
@@ -976,7 +981,7 @@ namespace Barotrauma
foreach (GUIComponent c in prevCharacterListBox.Content.Children) foreach (GUIComponent c in prevCharacterListBox.Content.Children)
{ {
Character character = c.UserData as Character; Character character = c.UserData as Character;
if (character == null) continue; if (character == null || character.IsDead || character.Removed) continue;
AddCharacter(character); AddCharacter(character);
DisplayCharacterOrder(character, character.CurrentOrder); DisplayCharacterOrder(character, character.CurrentOrder);
} }
@@ -44,7 +44,7 @@ namespace Barotrauma
ContextualTutorial = Tutorial.Tutorials.Find(t => t is ContextualTutorial) as ContextualTutorial; ContextualTutorial = Tutorial.Tutorials.Find(t => t is ContextualTutorial) as ContextualTutorial;
if (ContextualTutorial.Selected && !ContextualTutorial.Initialized) // Selected when starting a new game -> initialize if (ContextualTutorial.Selected) // Selected when starting a new game -> initialize
{ {
ContextualTutorial.Initialize(); ContextualTutorial.Initialize();
} }
@@ -1,9 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Linq; using System.Xml.Linq;
using System; using System;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using System.Linq; using System.Linq;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma.Tutorials namespace Barotrauma.Tutorials
{ {
@@ -13,38 +14,59 @@ namespace Barotrauma.Tutorials
public static bool ContentRunning = false; public static bool ContentRunning = false;
public static bool Initialized = false; public static bool Initialized = false;
private enum ContentTypes { None = 0, Video = 1, Text = 2 }; private enum ContentTypes { None = 0, Video = 1, TextOnly = 2 };
private TutorialSegment activeSegment; private TutorialSegment activeSegment;
private List<TutorialSegment> segments; private List<TutorialSegment> segments;
private SpriteSheetPlayer spriteSheetPlayer; private VideoPlayer videoPlayer;
private Steering navConsole; private Steering navConsole;
private Reactor reactor; private Reactor reactor;
private Sonar sonar; private Sonar sonar;
private Vector2 subStartingPosition; private Vector2 subStartingPosition;
private List<Character> crew; private List<Character> crew;
private Character mechanic;
private Character engineer;
private Character injuredMember = null;
private List<Pair<Character, float>> characterTimeOnSonar; private List<Pair<Character, float>> characterTimeOnSonar;
private float requiredTimeOnSonar = 5f; private float requiredTimeOnSonar = 5f;
private bool started = false; private bool started = false;
private string playableContentPath; private string playableContentPath;
private float tutorialTimer; private float tutorialTimer;
private float degrading2ActivationCountdown;
private bool disableTutorialOnDeficiencyFound = true; private bool disableTutorialOnDeficiencyFound = true;
private GUIFrame holderFrame, objectiveFrame;
private List<TutorialSegment> activeObjectives = new List<TutorialSegment>();
private string objectiveTranslated;
private float floodTutorialTimer = 0.0f;
private const float floodTutorialDelay = 2.0f;
private float medicalTutorialTimer = 0.0f;
private const float medicalTutorialDelay = 2.0f;
private Point screenResolution;
private float prevUIScale;
private class TutorialSegment private class TutorialSegment
{ {
public string Name; public string Id;
public string Objective;
public ContentTypes ContentType; public ContentTypes ContentType;
public XElement Content; public XElement TextContent;
public XElement VideoContent;
public bool IsTriggered; public bool IsTriggered;
public GUIButton ReplayButton;
public GUITextBlock LinkedTitle, LinkedText;
public TutorialSegment(XElement config) public TutorialSegment(XElement config)
{ {
Name = config.GetAttributeString("name", "Missing Name"); Id = config.GetAttributeString("id", "Missing ID");
Objective = TextManager.Get(config.GetAttributeString("objective", string.Empty), true);
Enum.TryParse(config.GetAttributeString("contenttype", "None"), true, out ContentType); Enum.TryParse(config.GetAttributeString("contenttype", "None"), true, out ContentType);
IsTriggered = config.GetAttributeBool("istriggered", false); IsTriggered = config.GetAttributeBool("istriggered", false);
@@ -53,10 +75,11 @@ namespace Barotrauma.Tutorials
case ContentTypes.None: case ContentTypes.None:
break; break;
case ContentTypes.Video: case ContentTypes.Video:
Content = config.Element("Video"); VideoContent = config.Element("Video");
TextContent = config.Element("Text");
break; break;
case ContentTypes.Text: case ContentTypes.TextOnly:
Content = config.Element("Text"); TextContent = config.Element("Text");
break; break;
} }
} }
@@ -77,17 +100,17 @@ namespace Barotrauma.Tutorials
public override void Initialize() public override void Initialize()
{ {
if (Initialized) return;
Initialized = true;
base.Initialize();
spriteSheetPlayer = new SpriteSheetPlayer();
characterTimeOnSonar = new List<Pair<Character, float>>();
for (int i = 0; i < segments.Count; i++) for (int i = 0; i < segments.Count; i++)
{ {
segments[i].IsTriggered = false; segments[i].IsTriggered = false;
} }
if (Initialized) return;
Initialized = true;
base.Initialize();
videoPlayer = new VideoPlayer();
characterTimeOnSonar = new List<Pair<Character, float>>();
} }
public void LoadPartiallyComplete(XElement element) public void LoadPartiallyComplete(XElement element)
@@ -111,15 +134,6 @@ namespace Barotrauma.Tutorials
} }
} }
private void PreloadVideoContent()
{
for (int i = 0; i < segments.Count; i++)
{
if (segments[i].ContentType != ContentTypes.Video || segments[i].IsTriggered) continue;
spriteSheetPlayer.PreloadContent(playableContentPath, "tutorial", segments[i].Name, segments[i].Content);
}
}
public void SavePartiallyComplete(XElement element) public void SavePartiallyComplete(XElement element)
{ {
XElement tutorialElement = new XElement("contextualtutorial"); XElement tutorialElement = new XElement("contextualtutorial");
@@ -151,13 +165,13 @@ namespace Barotrauma.Tutorials
{ {
if (!Initialized) return; if (!Initialized) return;
PreloadVideoContent();
base.Start(); base.Start();
injuredMember = null;
activeObjectives.Clear();
objectiveTranslated = TextManager.Get("Objective");
CreateObjectiveFrame();
activeSegment = null; activeSegment = null;
tutorialTimer = 0.0f; tutorialTimer = floodTutorialTimer = medicalTutorialTimer = 0.0f;
degrading2ActivationCountdown = -1;
subStartingPosition = Vector2.Zero; subStartingPosition = Vector2.Zero;
characterTimeOnSonar.Clear(); characterTimeOnSonar.Clear();
@@ -193,6 +207,9 @@ namespace Barotrauma.Tutorials
} }
crew = GameMain.GameSession.CrewManager.GetCharacters().ToList(); crew = GameMain.GameSession.CrewManager.GetCharacters().ToList();
mechanic = CrewMemberWithJob("mechanic");
engineer = CrewMemberWithJob("engineer");
Completed = true; // Trigger completed at start to prevent the contextual tutorial from automatically activating on starting new campaigns after this one Completed = true; // Trigger completed at start to prevent the contextual tutorial from automatically activating on starting new campaigns after this one
started = true; started = true;
} }
@@ -208,71 +225,185 @@ namespace Barotrauma.Tutorials
public void Stop() public void Stop()
{ {
started = ContentRunning = Initialized = false; started = ContentRunning = Initialized = false;
spriteSheetPlayer.Remove(); videoPlayer.Remove();
spriteSheetPlayer = null; videoPlayer = null;
characterTimeOnSonar = null; characterTimeOnSonar = null;
} }
private void CreateObjectiveFrame()
{
holderFrame = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center));
objectiveFrame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ObjectiveAnchor, holderFrame.RectTransform), style: null);
for (int i = 0; i < activeObjectives.Count; i++)
{
CreateObjectiveGUI(activeObjectives[i], i);
}
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
prevUIScale = GUI.Scale;
}
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
base.AddToGUIUpdateList(); if (videoPlayer != null)
if (spriteSheetPlayer != null)
{ {
spriteSheetPlayer.AddToGUIUpdateList(); videoPlayer.AddToGUIUpdateList(order: 100);
} }
if (GUI.DisableHUD) return;
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale)
{
CreateObjectiveFrame();
}
if (objectiveFrame != null && activeObjectives.Count > 0)
{
objectiveFrame.AddToGUIUpdateList(order: -1);
}
base.AddToGUIUpdateList();
} }
public override void Update(float deltaTime) public override void Update(float deltaTime)
{ {
if (videoPlayer != null)
{
videoPlayer.Update();
}
if (infoBox != null)
{
if (PlayerInput.KeyHit(Keys.Enter) || PlayerInput.KeyHit(Keys.Escape))
{
CloseInfoFrame(null, null);
}
}
if (!started || ContentRunning) return; if (!started || ContentRunning) return;
deltaTime *= 0.5f; deltaTime *= 0.5f;
for (int i = 0; i < segments.Count; i++) for (int i = 0; i < segments.Count; i++)
{ {
if (segments[i].IsTriggered) continue; if (segments[i].IsTriggered || activeObjectives.Contains(segments[i])) continue;
if (CheckContextualTutorials(i, deltaTime)) // Found a relevant tutorial, halt finding new ones if (CheckContextualTutorials(i, deltaTime)) // Found a relevant tutorial, halt finding new ones
{ {
break; break;
} }
} }
for (int i = 0; i < activeObjectives.Count; i++)
{
CheckActiveObjectives(activeObjectives[i], deltaTime);
}
}
private void ClosePreTextAndTriggerVideoCallback()
{
videoPlayer.LoadContent(playableContentPath, new VideoPlayer.VideoSettings(activeSegment.VideoContent), new VideoPlayer.TextSettings(activeSegment.VideoContent), activeSegment.Id, true, activeSegment.Objective, CurrentSegmentStopCallback);
} }
private void CurrentSegmentStopCallback() private void CurrentSegmentStopCallback()
{ {
if (!string.IsNullOrEmpty(activeSegment.Objective))
{
AddNewObjective(activeSegment);
}
activeSegment = null; activeSegment = null;
ContentRunning = false; ContentRunning = false;
} }
private void AddNewObjective(TutorialSegment segment)
{
activeObjectives.Add(segment);
CreateObjectiveGUI(segment, activeObjectives.Count - 1);
}
private void CreateObjectiveGUI(TutorialSegment segment, int index)
{
Point replayButtonSize = new Point((int)(GUI.ObjectiveNameFont.MeasureString(segment.Objective).X * GUI.Scale), (int)(GUI.ObjectiveNameFont.MeasureString(segment.Objective).Y * 1.45f * GUI.Scale));
segment.ReplayButton = new GUIButton(new RectTransform(replayButtonSize, objectiveFrame.RectTransform, Anchor.TopRight, Pivot.TopRight) { AbsoluteOffset = new Point(0, (replayButtonSize.Y + (int)(20f * GUI.Scale)) * index) }, style: null);
segment.ReplayButton.OnClicked += (GUIButton btn, object userdata) =>
{
ReplaySegmentVideo(segment);
return true;
};
int yOffset = (int)((GUI.ObjectiveNameFont.MeasureString(objectiveTranslated).Y / 2f + 5) * GUI.Scale);
segment.LinkedTitle = new GUITextBlock(new RectTransform(new Point(replayButtonSize.X, yOffset), segment.ReplayButton.RectTransform, Anchor.Center, Pivot.BottomCenter) { AbsoluteOffset = new Point((int)(10 * GUI.Scale), 0) }, objectiveTranslated, textColor: Color.White, font: GUI.ObjectiveTitleFont, textAlignment: Alignment.CenterRight);
segment.LinkedText = new GUITextBlock(new RectTransform(new Point(replayButtonSize.X, yOffset), segment.ReplayButton.RectTransform, Anchor.Center, Pivot.TopCenter) { AbsoluteOffset = new Point((int)(10 * GUI.Scale), 0) }, segment.Objective, textColor: new Color(4, 180, 108), font: GUI.ObjectiveNameFont, textAlignment: Alignment.CenterRight);
segment.LinkedTitle.TextScale = segment.LinkedText.TextScale = GUI.Scale;
segment.LinkedTitle.Color = segment.LinkedTitle.HoverColor = segment.LinkedTitle.PressedColor = segment.LinkedTitle.SelectedColor = Color.Transparent;
segment.LinkedText.Color = segment.LinkedText.HoverColor = segment.LinkedText.PressedColor = segment.LinkedText.SelectedColor = Color.Transparent;
segment.ReplayButton.Color = segment.ReplayButton.HoverColor = segment.ReplayButton.PressedColor = segment.ReplayButton.SelectedColor = Color.Transparent;
}
private void RemoveCompletedObjective(TutorialSegment objective)
{
objective.IsTriggered = true;
int checkMarkHeight = (int)(objective.ReplayButton.Rect.Height * 1.2f);
int checkMarkWidth = (int)(checkMarkHeight * 0.93f);
Color color = new Color(4, 180, 108);
RectTransform rectTA = new RectTransform(new Point(checkMarkWidth, checkMarkHeight), objective.ReplayButton.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft);
rectTA.AbsoluteOffset = new Point(-rectTA.Rect.Width - 5, 0);
GUIImage checkmark = new GUIImage(rectTA, "CheckMark");
checkmark.Color = color;
RectTransform rectTB = new RectTransform(new Vector2(1.1f, .8f), objective.LinkedText.RectTransform, Anchor.Center, Pivot.Center);
GUIImage stroke = new GUIImage(rectTB, "Stroke");
stroke.Color = color;
CoroutineManager.StartCoroutine(WaitForObjectiveEnd(objective));
}
private IEnumerable<object> WaitForObjectiveEnd(TutorialSegment objective)
{
yield return new WaitForSeconds(2.0f);
objectiveFrame.RemoveChild(objective.ReplayButton);
activeObjectives.Remove(objective);
for (int i = 0; i < activeObjectives.Count; i++)
{
activeObjectives[i].ReplayButton.RectTransform.AbsoluteOffset = new Point(0, (activeObjectives[i].ReplayButton.Rect.Height + 20) * i);
}
}
private bool CheckContextualTutorials(int index, float deltaTime) private bool CheckContextualTutorials(int index, float deltaTime)
{ {
switch (index) switch (index)
{ {
case 0: // Welcome: Game Start [Text] case 0: // Welcome: Game Start [Text]
if (tutorialTimer < 0.5f) if (tutorialTimer < 1.0f)
{ {
tutorialTimer += deltaTime; tutorialTimer += deltaTime;
return false; return false;
} }
break; break;
case 1: // Command Reactor: 10 seconds after 'Welcome' dismissed and only if no command given to start reactor [Video] case 1: // Command Reactor: 2 seconds after 'Welcome' dismissed and only if no command given to start reactor [Video]
if (tutorialTimer < 10.5f) if (!segments[0].IsTriggered) return false;
if (tutorialTimer < 3.0f)
{ {
tutorialTimer += deltaTime; tutorialTimer += deltaTime;
if (HasOrder("operatereactor")) if (HasOrder("operatereactor"))
{ {
segments[index].IsTriggered = true; segments[index].IsTriggered = true;
tutorialTimer = 10.5f; tutorialTimer = 2.5f;
} }
return false; return false;
} }
break; break;
case 2: // Nav Console: 20 seconds after 'Command Reactor' dismissed or if nav console is activated [Video] case 2: // Nav Console: 2 seconds after 'Command Reactor' dismissed or if nav console is activated [Video]
if (!IsReactorPoweredUp()) return false; // Do not advance tutorial based on this segment if reactor has not been powered up
if (Character.Controlled?.SelectedConstruction != navConsole.Item) if (Character.Controlled?.SelectedConstruction != navConsole.Item)
{ {
if (!segments[1].IsTriggered) return false; // Do not advance tutorial timer based on this segment if reactor has not been powered up if (tutorialTimer < 4.5f)
if (tutorialTimer < 30.5f)
{ {
tutorialTimer += deltaTime; tutorialTimer += deltaTime;
return false; return false;
@@ -280,20 +411,13 @@ namespace Barotrauma.Tutorials
} }
else else
{ {
if (!segments[1].IsTriggered || !HasOrder("operatereactor")) // If reactor has not been powered up or ordered to be, default to that one first tutorialTimer = 4.5f;
{
if (tutorialTimer < 10.5f)
{
tutorialTimer = 10.5f;
}
return false;
}
tutorialTimer = 30.5f;
} }
break;
TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name);
return true;
case 3: // Objective: Travel ~150 meters and while sub is not flooding [Text] case 3: // Objective: Travel ~150 meters and while sub is not flooding [Text]
if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 12000f || IsFlooding()) if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 8000f || IsFlooding())
{ {
return false; return false;
} }
@@ -307,6 +431,11 @@ namespace Barotrauma.Tutorials
{ {
return false; return false;
} }
else if (floodTutorialTimer < floodTutorialDelay)
{
floodTutorialTimer += deltaTime;
return false;
}
break; break;
case 5: // Reactor: Player uses reactor for the first time [Video] case 5: // Reactor: Player uses reactor for the first time [Video]
if (Character.Controlled?.SelectedConstruction != reactor.Item) if (Character.Controlled?.SelectedConstruction != reactor.Item)
@@ -321,19 +450,23 @@ namespace Barotrauma.Tutorials
} }
break; break;
case 7: // Degrading1: Any equipment degrades to 50% health or less and player has not assigned any crew to perform maintenance [Text] case 7: // Degrading1: Any equipment degrades to 50% health or less and player has not assigned any crew to perform maintenance [Text]
if ((mechanic == null || mechanic.IsDead) && (engineer == null || engineer.IsDead)) // Both engineer and mechanic are dead or do not exist -> do not display
{
return false;
}
bool degradedEquipmentFound = false; bool degradedEquipmentFound = false;
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (!item.Repairables.Any() || item.ConditionPercentage > 50) continue; if (!item.Repairables.Any() || item.Condition > 50.0f) continue;
degradedEquipmentFound = true; degradedEquipmentFound = true;
break; break;
} }
if (degradedEquipmentFound) if (degradedEquipmentFound)
{ {
degrading2ActivationCountdown = 5f; if (HasOrder("repairsystems", "jobspecific"))
if (HasOrder("repairsystems"))
{ {
segments[index].IsTriggered = true; segments[index].IsTriggered = true;
return false; return false;
@@ -344,43 +477,44 @@ namespace Barotrauma.Tutorials
return false; return false;
} }
break; break;
case 8: // Degrading2: 5 seconds after 'Degrading1' dismissed, and only if player has not assigned any crew to perform maintenance [Video] case 8: // Medical: Crewmember is injured but not killed [Video]
if (degrading2ActivationCountdown == -1f)
if (injuredMember == null)
{ {
return false; for (int i = 0; i < crew.Count; i++)
}
else if (degrading2ActivationCountdown > 0.0f)
{
degrading2ActivationCountdown -= deltaTime;
if (HasOrder("repairsystems"))
{ {
segments[index].IsTriggered = true; Character member = crew[i];
if (member.Vitality < member.MaxVitality && !member.IsDead)
{
injuredMember = member;
break;
}
} }
return false; return false;
} }
break; else if (medicalTutorialTimer < medicalTutorialDelay)
case 9: // Medical: Crewmember is injured but not killed [Video]
bool injuredFound = false;
for (int i = 0; i < crew.Count; i++)
{ {
Character member = crew[i]; medicalTutorialTimer += deltaTime;
if (member.Vitality < member.MaxVitality && !member.IsDead) return false;
{
injuredFound = true;
break;
}
} }
else
if (!injuredFound) return false; {
break; TriggerTutorialSegment(index, new string[] { injuredMember.Info.DisplayName,
case 10: // Approach1: Destination is within ~100m [Video] (injuredMember.Info.Gender == Gender.Male) ? TextManager.Get("PronounPossessiveMale").ToLower() : TextManager.Get("PronounPossessiveFemale").ToLower() });
return true;
}
case 9: // Approach1: Destination is within ~100m [Video]
if (Vector2.Distance(Submarine.MainSub.WorldPosition, Level.Loaded.EndPosition) > 8000f) if (Vector2.Distance(Submarine.MainSub.WorldPosition, Level.Loaded.EndPosition) > 8000f)
{ {
return false; return false;
} }
break; else
case 11: // Approach2: Sub is docked [Text] {
TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name);
return true;
}
case 10: // Approach2: Sub is docked [Text]
if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0) if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0)
{ {
return false; return false;
@@ -392,11 +526,128 @@ namespace Barotrauma.Tutorials
return true; return true;
} }
private bool HasOrder(string aiTag) private bool HasObjective(string objectiveName)
{
for (int i = 0; i < activeObjectives.Count; i++)
{
if (activeObjectives[i].Id == objectiveName) return true;
}
return false;
}
private void CheckActiveObjectives(TutorialSegment objective, float deltaTime)
{
switch(objective.Id)
{
case "ReactorCommand": // Reactor commanded
if (!IsReactorPoweredUp())
{
if (!HasOrder("operatereactor")) return;
}
break;
case "NavConsole": // traveled 50 meters
if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 4000f)
{
return;
}
break;
case "Flood": // Hull breaches repaired
if (IsFlooding()) return;
break;
case "Medical":
if (injuredMember != null && !injuredMember.IsDead)
{
if (injuredMember.CharacterHealth.DroppedItem == null) return;
}
break;
case "EnemyOnSonar": // Enemy dispatched
if (HasEnemyOnSonarForDuration(deltaTime))
{
return;
}
break;
case "Degrading": // Fixed
if (mechanic != null && !mechanic.IsDead)
{
HumanAIController humanAI = mechanic.AIController as HumanAIController;
if (mechanic.CurrentOrder?.AITag != "repairsystems" || humanAI.CurrentOrderOption != "jobspecific")
{
return;
}
}
if (engineer != null && !engineer.IsDead)
{
HumanAIController humanAI = engineer.AIController as HumanAIController;
if (engineer.CurrentOrder?.AITag != "repairsystems" || humanAI.CurrentOrderOption != "jobspecific")
{
return;
}
}
break;
case "Approach1": // Wait until docked
if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0)
{
return;
}
break;
}
RemoveCompletedObjective(objective);
}
private bool IsReactorPoweredUp()
{
float load = 0.0f;
List<Connection> connections = reactor.Item.Connections;
if (connections != null && connections.Count > 0)
{
foreach (Connection connection in connections)
{
if (!connection.IsPower) continue;
foreach (Connection recipient in connection.Recipients)
{
if (!(recipient.Item is Item it)) continue;
PowerTransfer pt = it.GetComponent<PowerTransfer>();
if (pt == null) continue;
load = Math.Max(load, pt.PowerLoad);
}
}
}
return Math.Abs(load + reactor.CurrPowerConsumption) < 10;
}
private Character CrewMemberWithJob(string job)
{ {
for (int i = 0; i < crew.Count; i++) for (int i = 0; i < crew.Count; i++)
{ {
if (crew[i].CurrentOrder?.AITag == aiTag) return true; if (crew[i].Info.Job.Name == job) return crew[i];
}
return null;
}
private bool HasOrder(string aiTag, string option = null)
{
for (int i = 0; i < crew.Count; i++)
{
if (crew[i].CurrentOrder?.AITag == aiTag)
{
if (option == null)
{
return true;
}
else
{
HumanAIController humanAI = crew[i].AIController as HumanAIController;
return humanAI.CurrentOrderOption == option;
}
}
} }
return false; return false;
@@ -406,10 +657,12 @@ namespace Barotrauma.Tutorials
{ {
foreach (Gap gap in Gap.GapList) foreach (Gap gap in Gap.GapList)
{ {
if (gap.ConnectedWall == null) continue; if (gap.ConnectedWall == null || gap.IsRoomToRoom) continue;
if (gap.ConnectedDoor != null || gap.Open <= 0.0f) continue; if (gap.ConnectedDoor != null || gap.Open <= 0.0f) continue;
if (gap.Submarine == null) continue; if (gap.Submarine == null) continue;
if (gap.Submarine.IsOutpost) continue;
if (gap.Submarine != Submarine.MainSub) continue; if (gap.Submarine != Submarine.MainSub) continue;
if (gap.FlowTargetHull == null || gap.FlowTargetHull.WaterPercentage <= 0.0f) continue;
return true; return true;
} }
@@ -447,27 +700,51 @@ namespace Barotrauma.Tutorials
} }
} }
return characterTimeOnSonar.Find(ct => ct.Second >= requiredTimeOnSonar) != null; return characterTimeOnSonar.Find(ct => ct.Second >= requiredTimeOnSonar && !ct.First.IsDead) != null;
} }
private void TriggerTutorialSegment(int index, params object[] args) private void TriggerTutorialSegment(int index, params object[] args)
{ {
Inventory.draggingItem = null;
ContentRunning = true; ContentRunning = true;
activeSegment = segments[index]; activeSegment = segments[index];
activeSegment.IsTriggered = true;
string tutorialText = TextManager.GetFormatted(activeSegment.TextContent.GetAttributeString("tag", ""), true, args);
string objectiveText = string.Empty;
if (!string.IsNullOrEmpty(activeSegment.Objective))
{
if (args.Length == 0)
{
objectiveText = activeSegment.Objective;
}
else
{
objectiveText = string.Format(activeSegment.Objective, args);
}
activeSegment.Objective = objectiveText;
}
else
{
activeSegment.IsTriggered = true; // Complete at this stage only if no related objective
}
switch (activeSegment.ContentType) switch (activeSegment.ContentType)
{ {
case ContentTypes.None: case ContentTypes.None:
break; break;
case ContentTypes.Video: case ContentTypes.Video:
spriteSheetPlayer.LoadContent(playableContentPath, activeSegment.Content, activeSegment.Name, true, true, CurrentSegmentStopCallback); infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Id), tutorialText,
activeSegment.TextContent.GetAttributeInt("width", 300),
activeSegment.TextContent.GetAttributeInt("height", 80),
activeSegment.TextContent.GetAttributeString("anchor", "Center"), true, ClosePreTextAndTriggerVideoCallback);
break; break;
case ContentTypes.Text: case ContentTypes.TextOnly:
infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Name), TextManager.GetFormatted(activeSegment.Content.GetAttributeString("tag", ""), false, args), infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Id), tutorialText,
activeSegment.Content.GetAttributeInt("width", 300), activeSegment.TextContent.GetAttributeInt("width", 300),
activeSegment.Content.GetAttributeInt("height", 80), activeSegment.TextContent.GetAttributeInt("height", 80),
activeSegment.Content.GetAttributeString("anchor", "Center"), true, CurrentSegmentStopCallback); activeSegment.TextContent.GetAttributeString("anchor", "Center"), true, CurrentSegmentStopCallback);
break; break;
} }
@@ -479,6 +756,13 @@ namespace Barotrauma.Tutorials
CoroutineManager.StartCoroutine(WaitToStop()); // Completed CoroutineManager.StartCoroutine(WaitToStop()); // Completed
} }
private void ReplaySegmentVideo(TutorialSegment segment)
{
if (ContentRunning) return;
ContentRunning = true;
videoPlayer.LoadContent(playableContentPath, new VideoPlayer.VideoSettings(segment.VideoContent), new VideoPlayer.TextSettings(segment.VideoContent), segment.Id, true, callback: () => ContentRunning = false);
}
private IEnumerable<object> WaitToStop() private IEnumerable<object> WaitToStop()
{ {
while (ContentRunning) yield return null; while (ContentRunning) yield return null;
@@ -121,7 +121,7 @@ namespace Barotrauma.Tutorials
public virtual void AddToGUIUpdateList() public virtual void AddToGUIUpdateList()
{ {
if (infoBox != null) infoBox.AddToGUIUpdateList(); if (infoBox != null) infoBox.AddToGUIUpdateList(order: 100);
} }
public virtual void Update(float deltaTime) public virtual void Update(float deltaTime)
@@ -188,17 +188,19 @@ namespace Barotrauma.Tutorials
Anchor anchor = Anchor.TopRight; Anchor anchor = Anchor.TopRight;
Enum.TryParse(anchorStr, out anchor); Enum.TryParse(anchorStr, out anchor);
var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), GUI.Canvas, anchor) { AbsoluteOffset = new Point(20) }); var infoBlock = new GUIFrame(new RectTransform(new Point((int)(width * GUI.Scale), (int)(height * GUI.Scale)), GUI.Canvas, anchor) { AbsoluteOffset = new Point(20) });
infoBlock.Flash(Color.Green); infoBlock.Flash(Color.Green);
if (title.Length > 0) if (title.Length > 0)
{ {
var titleBlock = new GUITextBlock(new RectTransform(new Vector2(1f, .35f), infoBlock.RectTransform, Anchor.TopCenter, var titleBlock = new GUITextBlock(new RectTransform(new Vector2(1f, .35f), infoBlock.RectTransform, Anchor.TopCenter,
Pivot.TopCenter), title, font: GUI.LargeFont, textAlignment: Alignment.Center); Pivot.TopCenter), title, font: GUI.VideoTitleFont, textAlignment: Alignment.Center, textColor: new Color(253, 174, 0));
titleBlock.TextScale = GUI.Scale;
} }
var textBlock = new GUITextBlock(new RectTransform(new Vector2(0.9f, 1f), infoBlock.RectTransform, Anchor.BottomCenter), var textBlock = new GUITextBlock(new RectTransform(new Vector2(0.9f, 1f), infoBlock.RectTransform, Anchor.BottomCenter),
text, wrap: true); text, wrap: true);
textBlock.TextScale = GUI.Scale;
infoBoxClosedCallback = callback; infoBoxClosedCallback = callback;
@@ -14,12 +14,13 @@ namespace Barotrauma
{ {
private enum Tab private enum Tab
{ {
General,
Graphics, Graphics,
Audio, Audio,
Controls, Controls,
} }
private readonly Point MinSupportedResolution = new Point(1024, 540);
private GUIFrame settingsFrame; private GUIFrame settingsFrame;
private GUIButton applyButton; private GUIButton applyButton;
@@ -71,16 +72,79 @@ namespace Barotrauma
private void CreateSettingsFrame() private void CreateSettingsFrame()
{ {
settingsFrame = new GUIFrame(new RectTransform(new Point(500, 500), GUI.Canvas, Anchor.Center)); settingsFrame = new GUIFrame(new RectTransform(new Vector2(0.8f, 0.8f), GUI.Canvas, Anchor.Center));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), settingsFrame.RectTransform), var settingsFramePadding = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), settingsFrame.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.05f) }, style: null);
TextManager.Get("Settings"), textAlignment: Alignment.Center, font: GUI.LargeFont);
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.8f), settingsFrame.RectTransform, Anchor.Center) /// General tab --------------------------------------------------------------
{ RelativeOffset = new Vector2(0.0f, 0.06f) }, style: null);
var leftPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1.0f), settingsFramePadding.RectTransform, Anchor.TopLeft));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform),
TextManager.Get("Settings"), textAlignment: Alignment.TopLeft, font: GUI.LargeFont);
var generalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), leftPanel.RectTransform, Anchor.TopLeft));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), generalLayoutGroup.RectTransform), TextManager.Get("ContentPackages"));
var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.75f), generalLayoutGroup.RectTransform))
{
CanBeFocused = false
};
foreach (ContentPackage contentPackage in ContentPackage.List)
{
var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.067f), contentPackageList.Content.RectTransform, minSize: new Point(0, 15)), contentPackage.Name)
{
UserData = contentPackage,
OnSelected = SelectContentPackage,
Selected = SelectedContentPackages.Contains(contentPackage)
};
if (!contentPackage.IsCompatible())
{
tickBox.TextColor = Color.Red;
tickBox.Enabled = false;
tickBox.ToolTip = TextManager.Get(contentPackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage")
.Replace("[packagename]", contentPackage.Name)
.Replace("[packageversion]", contentPackage.GameVersion.ToString())
.Replace("[gameversion]", GameMain.Version.ToString());
}
else if (contentPackage.CorePackage && !contentPackage.ContainsRequiredCorePackageFiles(out List<ContentType> missingContentTypes))
{
tickBox.TextColor = Color.Red;
tickBox.Enabled = false;
tickBox.ToolTip = TextManager.Get("ContentPackageMissingCoreFiles")
.Replace("[packagename]", contentPackage.Name)
.Replace("[missingfiletypes]", string.Join(", ", missingContentTypes));
}
}
new GUITextBlock(new RectTransform(new Vector2(0.92f, 0.05f), generalLayoutGroup.RectTransform), TextManager.Get("Language"));
var languageDD = new GUIDropDown(new RectTransform(new Vector2(0.92f, 0.05f), generalLayoutGroup.RectTransform));
foreach (string language in TextManager.AvailableLanguages)
{
languageDD.AddItem(TextManager.Get("Language." + language), language);
}
languageDD.SelectItem(TextManager.Language);
languageDD.OnSelected = (guiComponent, obj) =>
{
string newLanguage = obj as string;
if (newLanguage == Language) return true;
UnsavedSettings = true;
Language = newLanguage;
new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredLanguage"));
return true;
};
var rightPanel = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - leftPanel.RectTransform.RelativeSize.X, 0.95f),
settingsFramePadding.RectTransform, Anchor.TopRight));
var tabButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), rightPanel.RectTransform, Anchor.TopCenter), isHorizontal: true);
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), rightPanel.RectTransform, Anchor.Center), style: null);
var tabButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.05f), settingsFrame.RectTransform, Anchor.TopCenter)
{ RelativeOffset = new Vector2(0.0f, 0.11f) }, isHorizontal: true);
tabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length]; tabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length];
tabButtons = new GUIButton[tabs.Length]; tabButtons = new GUIButton[tabs.Length];
@@ -104,10 +168,10 @@ namespace Barotrauma
var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.CenterLeft) var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.CenterLeft)
{ RelativeOffset = new Vector2(0.02f, 0.0f) }) { RelativeOffset = new Vector2(0.02f, 0.0f) })
{ RelativeSpacing = 0.01f, Stretch = true }; { RelativeSpacing = 0.01f };
var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.CenterRight) var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.CenterRight)
{ RelativeOffset = new Vector2(0.02f, 0.0f) }) { RelativeOffset = new Vector2(0.02f, 0.0f) })
{ RelativeSpacing = 0.01f, Stretch = true }; { RelativeSpacing = 0.01f };
var supportedDisplayModes = new List<DisplayMode>(); var supportedDisplayModes = new List<DisplayMode>();
foreach (DisplayMode mode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes) foreach (DisplayMode mode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
@@ -135,6 +199,7 @@ namespace Barotrauma
foreach (DisplayMode mode in supportedDisplayModes) foreach (DisplayMode mode in supportedDisplayModes)
{ {
if (mode.Width < MinSupportedResolution.X || mode.Height < MinSupportedResolution.Y) { continue; }
resolutionDD.AddItem(mode.Width + "x" + mode.Height, mode); resolutionDD.AddItem(mode.Width + "x" + mode.Height, mode);
if (GraphicsWidth == mode.Width && GraphicsHeight == mode.Height) resolutionDD.SelectItem(mode); if (GraphicsWidth == mode.Width && GraphicsHeight == mode.Height) resolutionDD.SelectItem(mode);
} }
@@ -173,9 +238,6 @@ namespace Barotrauma
return true; return true;
}; };
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform), style: null);
GUITickBox vsyncTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("EnableVSync")) GUITickBox vsyncTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("EnableVSync"))
{ {
ToolTip = TextManager.Get("EnableVSyncToolTip"), ToolTip = TextManager.Get("EnableVSyncToolTip"),
@@ -191,8 +253,6 @@ namespace Barotrauma
Selected = VSyncEnabled Selected = VSyncEnabled
}; };
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), leftColumn.RectTransform), style: null);
GUITextBlock particleLimitText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ParticleLimit")); GUITextBlock particleLimitText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ParticleLimit"));
GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
barSize: 0.1f) barSize: 0.1f)
@@ -209,8 +269,6 @@ namespace Barotrauma
}; };
particleScrollBar.OnMoved(particleScrollBar, particleScrollBar.BarScroll); particleScrollBar.OnMoved(particleScrollBar, particleScrollBar.BarScroll);
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), rightColumn.RectTransform), style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LosEffect")); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LosEffect"));
var losModeDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform)); var losModeDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform));
losModeDD.AddItem(TextManager.Get("LosModeNone"), LosMode.None); losModeDD.AddItem(TextManager.Get("LosModeNone"), LosMode.None);
@@ -229,8 +287,6 @@ namespace Barotrauma
return true; return true;
}; };
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: null);
GUITextBlock LightText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LightMapScale")) GUITextBlock LightText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LightMapScale"))
{ {
ToolTip = TextManager.Get("LightMapScaleToolTip") ToolTip = TextManager.Get("LightMapScaleToolTip")
@@ -251,8 +307,6 @@ namespace Barotrauma
}; };
lightScrollBar.OnMoved(lightScrollBar, lightScrollBar.BarScroll); lightScrollBar.OnMoved(lightScrollBar, lightScrollBar.BarScroll);
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: null);
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("SpecularLighting")) new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("SpecularLighting"))
{ {
ToolTip = TextManager.Get("SpecularLightingToolTip"), ToolTip = TextManager.Get("SpecularLightingToolTip"),
@@ -265,8 +319,6 @@ namespace Barotrauma
} }
}; };
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), rightColumn.RectTransform), style: null);
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ChromaticAberration")) new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ChromaticAberration"))
{ {
ToolTip = TextManager.Get("ChromaticAberrationToolTip"), ToolTip = TextManager.Get("ChromaticAberrationToolTip"),
@@ -279,9 +331,6 @@ namespace Barotrauma
} }
}; };
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: null);
GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("HUDScale")); GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("HUDScale"));
GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
barSize: 0.1f) barSize: 0.1f)
@@ -300,9 +349,6 @@ namespace Barotrauma
}; };
HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll); HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll);
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), rightColumn.RectTransform), style: null);
GUITextBlock inventoryScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("InventoryScale")); GUITextBlock inventoryScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("InventoryScale"));
GUIScrollBar inventoryScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), barSize: 0.1f) GUIScrollBar inventoryScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), barSize: 0.1f)
{ {
@@ -319,9 +365,6 @@ namespace Barotrauma
}; };
inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll); inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll);
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), rightColumn.RectTransform), style: null);
/// Audio tab ---------------------------------------------------------------- /// Audio tab ----------------------------------------------------------------
var audioSliders = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.4f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopCenter) var audioSliders = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.4f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopCenter)
@@ -567,9 +610,10 @@ namespace Barotrauma
var inputNames = Enum.GetValues(typeof(InputType)); var inputNames = Enum.GetValues(typeof(InputType));
for (int i = 0; i < inputNames.Length; i++) for (int i = 0; i < inputNames.Length; i++)
{ {
var inputContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.06f), inputFrame.RectTransform), style: null); var inputContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.06f), inputFrame.RectTransform)) { Stretch = true, IsHorizontal = true, RelativeSpacing = 0.05f };
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), inputContainer.RectTransform), TextManager.Get("InputType." + ((InputType)i)) + ": ", font: GUI.SmallFont); new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), inputContainer.RectTransform, Anchor.TopLeft) { MinSize = new Point(150, 0) },
var keyBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), inputContainer.RectTransform, Anchor.TopRight), TextManager.Get("InputType." + ((InputType)i)) + ": ", font: GUI.SmallFont) { ForceUpperCase = true };
var keyBox = new GUITextBox(new RectTransform(new Vector2(0.7f, 1.0f), inputContainer.RectTransform),
text: keyMapping[i].ToString(), font: GUI.SmallFont) text: keyMapping[i].ToString(), font: GUI.SmallFont)
{ {
UserData = i UserData = i
@@ -593,88 +637,11 @@ namespace Barotrauma
}; };
aimAssistSlider.OnMoved(aimAssistSlider, aimAssistSlider.BarScroll); aimAssistSlider.OnMoved(aimAssistSlider, aimAssistSlider.BarScroll);
/// General tab --------------------------------------------------------------
var generalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), tabs[(int)Tab.General].RectTransform, Anchor.Center)
{ RelativeOffset = new Vector2(0.0f, 0.0f) }) { RelativeSpacing = 0.01f, Stretch = true };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), generalLayoutGroup.RectTransform), TextManager.Get("ContentPackages"));
var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), generalLayoutGroup.RectTransform))
{
CanBeFocused = false
};
foreach (ContentPackage contentPackage in ContentPackage.List)
{
var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), contentPackageList.Content.RectTransform, minSize: new Point(0, 15)), contentPackage.Name)
{
UserData = contentPackage,
OnSelected = SelectContentPackage,
Selected = SelectedContentPackages.Contains(contentPackage)
};
if (!contentPackage.IsCompatible())
{
tickBox.TextColor = Color.Red;
tickBox.Enabled = false;
tickBox.ToolTip = TextManager.Get(contentPackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage")
.Replace("[packagename]", contentPackage.Name)
.Replace("[packageversion]", contentPackage.GameVersion.ToString())
.Replace("[gameversion]", GameMain.Version.ToString());
}
else if (contentPackage.CorePackage && !contentPackage.ContainsRequiredCorePackageFiles(out List<ContentType> missingContentTypes))
{
tickBox.TextColor = Color.Red;
tickBox.Enabled = false;
tickBox.ToolTip = TextManager.Get("ContentPackageMissingCoreFiles")
.Replace("[packagename]", contentPackage.Name)
.Replace("[missingfiletypes]", string.Join(", ", missingContentTypes));
}
}
GUITextBlock aimAssistText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), controlsLayoutGroup.RectTransform), TextManager.Get("AimAssist"));
GUIScrollBar aimAssistSlider = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), controlsLayoutGroup.RectTransform),
barSize: 0.1f)
{
UserData = aimAssistText,
BarScroll = MathUtils.InverseLerp(0.0f, 5.0f, AimAssistAmount),
OnMoved = (scrollBar, scroll) =>
{
ChangeSliderText(scrollBar, scroll);
AimAssistAmount = MathHelper.Lerp(0.0f, 5.0f, scroll);
return true;
},
Step = 0.1f
};
aimAssistSlider.OnMoved(aimAssistSlider, aimAssistSlider.BarScroll);
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), generalLayoutGroup.RectTransform), TextManager.Get("Language"));
var languageDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), generalLayoutGroup.RectTransform));
foreach (string language in TextManager.AvailableLanguages)
{
languageDD.AddItem(TextManager.Get("Language." + language), language);
}
languageDD.SelectItem(TextManager.Language);
languageDD.OnSelected = (guiComponent, obj) =>
{
string newLanguage = obj as string;
if (newLanguage == Language) return true;
UnsavedSettings = true;
Language = newLanguage;
new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredLanguage"));
return true;
};
//spacing //spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null);
new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft), new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft),
TextManager.Get("Cancel")) TextManager.Get("Cancel"), style: "GUIButtonLarge")
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
OnClicked = (x, y) => OnClicked = (x, y) =>
@@ -690,7 +657,7 @@ namespace Barotrauma
}; };
applyButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform, Anchor.BottomRight), applyButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform, Anchor.BottomRight),
TextManager.Get("ApplySettingsButton")) TextManager.Get("ApplySettingsButton"), style: "GUIButtonLarge")
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
Enabled = false Enabled = false
@@ -698,7 +665,7 @@ namespace Barotrauma
applyButton.OnClicked = ApplyClicked; applyButton.OnClicked = ApplyClicked;
UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created
SelectTab(Tab.General); SelectTab(Tab.Graphics);
} }
private void SelectTab(Tab tab) private void SelectTab(Tab tab)
@@ -93,7 +93,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing) public void Draw(SpriteBatch spriteBatch, bool editing)
{ {
Color color = Color.White; Color color = item.SpriteColor;
if (brokenSprite == null) if (brokenSprite == null)
{ {
//broken doors turn black if no broken sprite has been configured //broken doors turn black if no broken sprite has been configured
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
weldSpritePos.Y = -weldSpritePos.Y; weldSpritePos.Y = -weldSpritePos.Y;
weldedSprite.Draw(spriteBatch, weldedSprite.Draw(spriteBatch,
weldSpritePos, Color.White * (stuck / 100.0f), scale: item.Scale); weldSpritePos, item.SpriteColor * (stuck / 100.0f), scale: item.Scale);
} }
if (openState == 1.0f) if (openState == 1.0f)
@@ -175,63 +175,6 @@ namespace Barotrauma.Items.Components
} }
} }
} }
public void ApplyTo(RectTransform target)
{
if (RelativeOffset.HasValue)
{
target.RelativeOffset = RelativeOffset.Value;
}
else if (AbsoluteOffset.HasValue)
{
target.AbsoluteOffset = AbsoluteOffset.Value;
}
if (RelativeSize.HasValue)
{
target.RelativeSize = RelativeSize.Value;
}
else if (AbsoluteSize.HasValue)
{
target.NonScaledSize = AbsoluteSize.Value;
}
if (Anchor.HasValue)
{
target.Anchor = Anchor.Value;
}
if (Pivot.HasValue)
{
target.Pivot = Pivot.Value;
}
else
{
target.Pivot = RectTransform.MatchPivotToAnchor(target.Anchor);
}
target.RecalculateChildren(true, true);
}
}
public GUIFrame GuiFrame { get; protected set; }
[Serialize(false, false)]
public bool AllowUIOverlap
{
get;
set;
}
private ItemComponent linkToUIComponent;
[Serialize("", false)]
public string LinkUIToComponent
{
get;
set;
}
[Serialize(0, false)]
public int HudPriority
{
get;
private set;
} }
private bool shouldMuffleLooping; private bool shouldMuffleLooping;
@@ -553,7 +496,11 @@ namespace Barotrauma.Items.Components
{ {
msg = msg.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameMain.Config.KeyBind(inputType).ToString()); msg = msg.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameMain.Config.KeyBind(inputType).ToString());
} }
Msg = msg; DisplayMsg = msg;
}
else
{
DisplayMsg = Msg;
} }
} }
} }
@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
{ {
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{ {
DeattachTimer = msg.ReadSingle(); deattachTimer = msg.ReadSingle();
if (deattachTimer >= DeattachDuration) if (deattachTimer >= DeattachDuration)
{ {
holdable.DeattachFromWall(); holdable.DeattachFromWall();
@@ -25,6 +25,12 @@ namespace Barotrauma.Items.Components
DrawHUDBack, null); DrawHUDBack, null);
submarineContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), style: null); submarineContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), style: null);
new GUICustomComponent(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center),
DrawHUDFront, null)
{
CanBeFocused = false
};
hullInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.13f), GUI.Canvas, minSize: new Point(250, 150)), hullInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.13f), GUI.Canvas, minSize: new Point(250, 150)),
style: "InnerFrame") style: "InnerFrame")
{ {
@@ -47,7 +53,7 @@ namespace Barotrauma.Items.Components
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
base.AddToGUIUpdateList(); base.AddToGUIUpdateList();
if (hasPower) hullInfoFrame.AddToGUIUpdateList(); hullInfoFrame.AddToGUIUpdateList();
} }
public override void OnMapLoaded() public override void OnMapLoaded()
@@ -96,6 +102,27 @@ namespace Barotrauma.Items.Components
} }
} }
private void DrawHUDFront(SpriteBatch spriteBatch, GUICustomComponent container)
{
foreach (GUIComponent child in submarineContainer.Children.First().Children)
{
if (child.UserData is Hull hull)
{
if (hull.Submarine == null || !hull.Submarine.IsOutpost) { continue; }
string text = TextManager.Get("MiniMapOutpostDockingInfo").Replace("[outpost]", hull.Submarine.Name);
Vector2 textSize = GUI.Font.MeasureString(text);
Vector2 textPos = child.Center;
if (textPos.X + textSize.X / 2 > submarineContainer.Rect.Right)
textPos.X -= ((textPos.X + textSize.X / 2) - submarineContainer.Rect.Right) + 10;
if (textPos.X - textSize.X / 2 < submarineContainer.Rect.X)
textPos.X += (submarineContainer.Rect.X - (textPos.X - textSize.X / 2)) + 10;
GUI.DrawString(spriteBatch, textPos - textSize / 2, text,
Color.Orange * (float)Math.Abs(Math.Sin(Timing.TotalTime)), Color.Black * 0.8f);
break;
}
}
}
private void DrawHUDBack(SpriteBatch spriteBatch, GUICustomComponent container) private void DrawHUDBack(SpriteBatch spriteBatch, GUICustomComponent container)
{ {
Hull mouseOnHull = null; Hull mouseOnHull = null;
@@ -221,7 +248,7 @@ namespace Barotrauma.Items.Components
foreach (Submarine sub in subs) foreach (Submarine sub in subs)
{ {
if (sub.HullVertices == null) { continue; } if (sub.HullVertices == null) { continue; }
Rectangle worldBorders = sub.GetDockedBorders(); Rectangle worldBorders = sub.GetDockedBorders();
worldBorders.Location += sub.WorldPosition.ToPoint(); worldBorders.Location += sub.WorldPosition.ToPoint();
@@ -242,6 +269,8 @@ namespace Barotrauma.Items.Components
GUI.DrawLine(spriteBatch, center + start, center + end, Color.DarkCyan * Rand.Range(0.3f, 0.35f), width: 10); GUI.DrawLine(spriteBatch, center + start, center + end, Color.DarkCyan * Rand.Range(0.3f, 0.35f), width: 10);
} }
} }
} }
private void GetLinkedHulls(Hull hull, List<Hull> linkedHulls) private void GetLinkedHulls(Hull hull, List<Hull> linkedHulls)
@@ -107,7 +107,10 @@ namespace Barotrauma.Items.Components
{ {
var warningBtn = new GUIButton(new RectTransform(new Point(buttonWidth, buttonHeight), columnLeft.RectTransform) var warningBtn = new GUIButton(new RectTransform(new Point(buttonWidth, buttonHeight), columnLeft.RectTransform)
{ AbsoluteOffset = new Point((i % buttonsPerRow) * (buttonWidth + spacing), (int)Math.Floor(i / (float)buttonsPerRow) * (buttonHeight + spacing)) }, { AbsoluteOffset = new Point((i % buttonsPerRow) * (buttonWidth + spacing), (int)Math.Floor(i / (float)buttonsPerRow) * (buttonHeight + spacing)) },
TextManager.Get(warningTexts[i]), style: "IndicatorButton"); TextManager.Get(warningTexts[i]), style: "IndicatorButton")
{
CanBeFocused = false
};
var btnText = warningBtn.GetChild<GUITextBlock>(); var btnText = warningBtn.GetChild<GUITextBlock>();
btnText.Font = GUI.SmallFont; btnText.Font = GUI.SmallFont;
@@ -785,8 +785,9 @@ namespace Barotrauma.Items.Components
foreach (Limb limb in c.AnimController.Limbs) foreach (Limb limb in c.AnimController.Limbs)
{ {
float pointDist = ((limb.WorldPosition - pingSource) * displayScale).LengthSquared(); if (!limb.body.Enabled) { continue; }
float pointDist = ((limb.WorldPosition - pingSource) * displayScale).LengthSquared();
if (limb.SimPosition == Vector2.Zero || pointDist > DisplayRadius * DisplayRadius) continue; if (limb.SimPosition == Vector2.Zero || pointDist > DisplayRadius * DisplayRadius) continue;
if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr) if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr)
@@ -127,6 +127,18 @@ namespace Barotrauma.Items.Components
Stretch = true, Stretch = true,
RelativeSpacing = 0.03f RelativeSpacing = 0.03f
}; };
autopilotTickBox = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.3f), paddedControlContainer.RectTransform),
TextManager.Get("SteeringAutoPilot"), style: "GUIRadioButton")
{
OnSelected = (GUITickBox box) =>
{
AutoPilot = box.Selected;
if (AutoPilot && MaintainPos)
{
posToMaintain = controlledSub == null ? item.WorldPosition : controlledSub.WorldPosition;
}
unsentChanges = true;
user = Character.Controlled;
maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), paddedAutoPilotControls.RectTransform), maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), paddedAutoPilotControls.RectTransform),
TextManager.Get("SteeringMaintainPos"), font: GUI.SmallFont) TextManager.Get("SteeringMaintainPos"), font: GUI.SmallFont)
@@ -484,26 +496,14 @@ namespace Barotrauma.Items.Components
user = Character.Controlled; user = Character.Controlled;
} }
} }
if (!AutoPilot && Character.DisableControls) if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null)
{ {
steeringAdjustSpeed = character == null ? 0.2f : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f); steeringAdjustSpeed = character == null ? 0.2f : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f);
Vector2 input = Vector2.Zero; Vector2 input = Vector2.Zero;
if (PlayerInput.KeyDown(InputType.Left)) if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; }
{ if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; }
input -= Vector2.UnitX; if (PlayerInput.KeyDown(InputType.Up)) { input += Vector2.UnitY; }
} if (PlayerInput.KeyDown(InputType.Down)) { input -= Vector2.UnitY; }
if (PlayerInput.KeyDown(InputType.Right))
{
input += Vector2.UnitX;
}
if (PlayerInput.KeyDown(InputType.Up))
{
input += Vector2.UnitY;
}
if (PlayerInput.KeyDown(InputType.Down))
{
input -= Vector2.UnitY;
}
if (PlayerInput.KeyDown(Keys.LeftShift)) if (PlayerInput.KeyDown(Keys.LeftShift))
{ {
SteeringInput += input * deltaTime * 200; SteeringInput += input * deltaTime * 200;
@@ -1,6 +1,7 @@
using Barotrauma.Particles; using Barotrauma.Particles;
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -11,12 +12,21 @@ using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
partial class RepairTool partial class RepairTool
#if DEBUG
: IDrawableComponent
#endif
{ {
public ParticleEmitter ParticleEmitter public ParticleEmitter ParticleEmitter
{ {
get; get;
private set; private set;
} }
#if DEBUG
public Vector2 DrawSize
{
get { return GameMain.DebugDraw ? Vector2.One * Range : Vector2.Zero; }
}
#endif
private List<ParticleEmitter> ParticleEmitterHitStructure = new List<ParticleEmitter>(); private List<ParticleEmitter> ParticleEmitterHitStructure = new List<ParticleEmitter>();
private List<ParticleEmitter> ParticleEmitterHitCharacter = new List<ParticleEmitter>(); private List<ParticleEmitter> ParticleEmitterHitCharacter = new List<ParticleEmitter>();
@@ -122,5 +132,17 @@ namespace Barotrauma.Items.Components
emitter.Second.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi); emitter.Second.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
} }
} }
#if DEBUG
public void Draw(SpriteBatch spriteBatch, bool editing)
{
if (GameMain.DebugDraw && IsActive)
{
GUI.DrawLine(spriteBatch,
new Vector2(debugRayStartPos.X, -debugRayStartPos.Y),
new Vector2(debugRayEndPos.X, -debugRayEndPos.Y),
Color.Yellow);
}
}
#endif
} }
} }
@@ -1,4 +1,6 @@
using Barotrauma.Particles; using Barotrauma.Networking;
using Barotrauma.Particles;
using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic; using System.Collections.Generic;
@@ -112,8 +114,7 @@ namespace Barotrauma.Items.Components
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts"); System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
foreach (GUIComponent c in GuiFrame.GetChild(0).Children) foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
{ {
Skill skill = c.UserData as Skill; if (!(c.UserData is Skill skill)) continue;
if (skill == null) continue;
GUITextBlock textBlock = (GUITextBlock)c; GUITextBlock textBlock = (GUITextBlock)c;
if (character.GetSkillLevel(skill.Identifier) < skill.Level) if (character.GetSkillLevel(skill.Identifier) < skill.Level)
@@ -126,5 +127,15 @@ namespace Barotrauma.Items.Components
} }
} }
} }
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
deteriorationTimer = msg.ReadSingle();
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
//no need to write anything, just letting the server know we started repairing
}
} }
} }
@@ -169,20 +169,6 @@ namespace Barotrauma.Items.Components
if (draggingConnected.Connect(this, !alreadyConnected, true)) if (draggingConnected.Connect(this, !alreadyConnected, true))
{ {
var otherConnection = draggingConnected.OtherConnection(this); var otherConnection = draggingConnected.OtherConnection(this);
#if SERVER
//TODO: ffs
if (otherConnection == null)
{
GameServer.Log(Character.Controlled.LogName + " connected a wire to " +
Item.Name + " (" + Name + ")", ServerLog.MessageType.ItemInteraction);
}
else
{
GameServer.Log(Character.Controlled.LogName + " connected a wire from " +
Item.Name + " (" + Name + ") to " + otherConnection.item.Name + " (" + otherConnection.Name + ")", ServerLog.MessageType.ItemInteraction);
}
#endif
SetWire(index, draggingConnected); SetWire(index, draggingConnected);
} }
} }
@@ -181,7 +181,7 @@ namespace Barotrauma.Items.Components
Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>(); Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>();
foreach (Affliction affliction in allAfflictions) foreach (Affliction affliction in allAfflictions)
{ {
if (affliction.Strength < affliction.Prefab.ActivationThreshold || affliction.Strength <= 0.0f) continue; if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold || affliction.Strength <= 0.0f) continue;
if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab)) if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab))
{ {
combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength; combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength;
@@ -237,13 +237,13 @@ namespace Barotrauma.Items.Components
railSprite?.Draw(spriteBatch, railSprite?.Draw(spriteBatch,
drawPos, drawPos,
Color.White, item.SpriteColor,
rotation + MathHelper.PiOver2, item.Scale, rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth)); SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth));
barrelSprite?.Draw(spriteBatch, barrelSprite?.Draw(spriteBatch,
drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale, drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale,
Color.White, item.SpriteColor,
rotation + MathHelper.PiOver2, item.Scale, rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth)); SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
@@ -301,7 +301,7 @@ namespace Barotrauma
protected virtual void ControlInput(Camera cam) protected virtual void ControlInput(Camera cam)
{ {
// Note that these targets are static. Therefore the outcome is the same if this method is called multiple times or only once. // Note that these targets are static. Therefore the outcome is the same if this method is called multiple times or only once.
if (selectedSlot != null || draggingItem != null) if (selectedSlot != null && !DraggingItemToWorld)
{ {
cam.Freeze = true; cam.Freeze = true;
} }
@@ -609,8 +609,17 @@ namespace Barotrauma
if (selectedSlot == null) if (selectedSlot == null)
{ {
draggingItem.Drop(Character.Controlled); if (DraggingItemToWorld &&
GUI.PlayUISound(GUISoundType.DropItem); Character.Controlled.FocusedItem?.OwnInventory != null &&
Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled))
{
GUI.PlayUISound(GUISoundType.PickItem);
}
else
{
GUI.PlayUISound(GUISoundType.DropItem);
draggingItem.Drop(Character.Controlled);
}
} }
else if (selectedSlot.ParentInventory.Items[selectedSlot.SlotIndex] != draggingItem) else if (selectedSlot.ParentInventory.Items[selectedSlot.SlotIndex] != draggingItem)
{ {
@@ -702,10 +711,27 @@ namespace Barotrauma
int iconSize = (int)(64 * GUI.Scale); int iconSize = (int)(64 * GUI.Scale);
float scale = Math.Min(Math.Min(iconSize / sprite.size.X, iconSize / sprite.size.Y), 1.5f); float scale = Math.Min(Math.Min(iconSize / sprite.size.X, iconSize / sprite.size.Y), 1.5f);
Vector2 itemPos = PlayerInput.MousePosition; Vector2 itemPos = PlayerInput.MousePosition;
if (GUI.MouseOn == null && selectedSlot == null)
{
var shadowSprite = GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0];
string toolTip = Character.Controlled.FocusedItem != null ?
TextManager.Get("PutItemIn").Replace("[itemname]", Character.Controlled.FocusedItem.Name) :
TextManager.Get("DropItem");
int textWidth = (int)Math.Max(GUI.Font.MeasureString(draggingItem.Name).X, GUI.SmallFont.MeasureString(toolTip).X);
int textSpacing = (int)(15 * GUI.Scale);
Point shadowBorders = (new Point(40, 10)).Multiply(GUI.Scale);
shadowSprite.Draw(spriteBatch,
new Rectangle(itemPos.ToPoint() - new Point(iconSize / 2) - shadowBorders, new Point(iconSize + textWidth + textSpacing, iconSize) + shadowBorders.Multiply(2)), Color.Black * 0.8f);
GUI.DrawString(spriteBatch, new Vector2(itemPos.X + iconSize / 2 + textSpacing, itemPos.Y - iconSize / 2), draggingItem.Name, Color.White);
GUI.DrawString(spriteBatch, new Vector2(itemPos.X + iconSize / 2 + textSpacing, itemPos.Y), toolTip,
color: Character.Controlled.FocusedItem == null ? Color.Red : Color.LightGreen,
font: GUI.SmallFont);
}
sprite.Draw(spriteBatch, itemPos + Vector2.One * 2, Color.Black, scale: scale); sprite.Draw(spriteBatch, itemPos + Vector2.One * 2, Color.Black, scale: scale);
sprite.Draw(spriteBatch, sprite.Draw(spriteBatch,
itemPos, itemPos,
sprite == draggingItem.Sprite ? draggingItem.GetSpriteColor() : draggingItem.GetInventoryIconColor(), sprite == draggingItem.Sprite ? draggingItem.GetSpriteColor() : draggingItem.GetInventoryIconColor(),
scale: scale); scale: scale);
} }
} }
@@ -10,12 +10,6 @@ namespace Barotrauma
protected override void ControlInput(Camera cam) protected override void ControlInput(Camera cam)
{ {
if (draggingItem == null && HUD.CloseHUD(BackgroundFrame))
{
// TODO: fix so that works with the server side
Character.Controlled.SelectedConstruction = null;
return;
}
base.ControlInput(cam); base.ControlInput(cam);
if (BackgroundFrame.Contains(PlayerInput.MousePosition)) if (BackgroundFrame.Contains(PlayerInput.MousePosition))
{ {
@@ -232,11 +232,11 @@ namespace Barotrauma
{ {
if (!ResizeHorizontal && !ResizeVertical) if (!ResizeHorizontal && !ResizeVertical)
{ {
sprite.Draw(spriteBatch, new Vector2(placeRect.Center.X, -(placeRect.Y - placeRect.Height / 2)), SpriteColor, scale: Scale * scale); sprite.Draw(spriteBatch, new Vector2(placeRect.Center.X, -(placeRect.Y - placeRect.Height / 2)), SpriteColor * 0.8f, scale: Scale * scale);
} }
else else
{ {
if (sprite != null) sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), null, SpriteColor); if (sprite != null) sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), null, SpriteColor * 0.8f);
} }
} }
} }
+11 -41
View File
@@ -1,14 +1,13 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Barotrauma.Particles; using Barotrauma.Particles;
using Barotrauma.Sounds; using Barotrauma.Sounds;
using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Xna.Framework.Input;
using System.Linq; using System.Linq;
using Lidgren.Network;
namespace Barotrauma namespace Barotrauma
{ {
@@ -19,6 +18,8 @@ namespace Barotrauma
private List<Decal> decals = new List<Decal>(); private List<Decal> decals = new List<Decal>();
private float serverUpdateDelay; private float serverUpdateDelay;
private float remoteWaterVolume, remoteOxygenPercentage;
private List<Vector3> remoteFireSources;
private bool networkUpdatePending; private bool networkUpdatePending;
private float networkUpdateTimer; private float networkUpdateTimer;
@@ -139,6 +140,10 @@ namespace Barotrauma
partial void UpdateProjSpecific(float deltaTime, Camera cam) partial void UpdateProjSpecific(float deltaTime, Camera cam)
{ {
serverUpdateDelay -= deltaTime; serverUpdateDelay -= deltaTime;
if (serverUpdateDelay <= 0.0f)
{
ApplyRemoteState();
}
if (networkUpdatePending) if (networkUpdatePending)
{ {
@@ -547,18 +552,18 @@ namespace Barotrauma
public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime) public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime)
{ {
float newWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
float newOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
bool hasFireSources = message.ReadBoolean(); bool hasFireSources = message.ReadBoolean();
int fireSourceCount = 0; int fireSourceCount = 0;
List<Vector3> newFireSources = new List<Vector3>(); remoteFireSources = new List<Vector3>();
if (hasFireSources) if (hasFireSources)
{ {
fireSourceCount = message.ReadRangedInteger(0, 16); fireSourceCount = message.ReadRangedInteger(0, 16);
for (int i = 0; i < fireSourceCount; i++) for (int i = 0; i < fireSourceCount; i++)
{ {
newFireSources.Add(new Vector3( remoteFireSources.Add(new Vector3(
MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
message.ReadRangedSingle(0.0f, 1.0f, 8))); message.ReadRangedSingle(0.0f, 1.0f, 8)));
@@ -567,41 +572,6 @@ namespace Barotrauma
if (serverUpdateDelay > 0.0f) { return; } if (serverUpdateDelay > 0.0f) { return; }
WaterVolume = newWaterVolume;
OxygenPercentage = newOxygenPercentage;
for (int i = 0; i < fireSourceCount; i++)
{
Vector2 pos = new Vector2(
rect.X + rect.Width * newFireSources[i].X,
rect.Y - rect.Height + (rect.Height * newFireSources[i].Y));
float size = newFireSources[i].Z * rect.Width;
var newFire = i < FireSources.Count ?
FireSources[i] :
new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true);
newFire.Position = pos;
newFire.Size = new Vector2(size, newFire.Size.Y);
//ignore if the fire wasn't added to this room (invalid position)?
if (!FireSources.Contains(newFire))
{
newFire.Remove();
continue;
}
}
for (int i = FireSources.Count - 1; i >= fireSourceCount; i--)
{
FireSources[i].Remove();
if (i < FireSources.Count)
{
FireSources.RemoveAt(i);
}
}
if (serverUpdateDelay > 0.0f) { return; }
ApplyRemoteState(); ApplyRemoteState();
} }
@@ -247,15 +247,11 @@ namespace Barotrauma.Lights
public void Rotate(Vector2 origin, float amount) public void Rotate(Vector2 origin, float amount)
{ {
Matrix rotationMatrix = Matrix.CreateRotationZ(amount); Matrix rotationMatrix =
Matrix.CreateTranslation(-origin.X, -origin.Y, 0.0f) *
Vector2[] newVerts = new Vector2[vertices.Length]; Matrix.CreateRotationZ(amount) *
for (int i = 0; i < vertices.Length; i++) Matrix.CreateTranslation(origin.X, origin.Y, 0.0f);
{ SetVertices(vertices.Select(v => v.Pos).ToArray(), rotationMatrix);
newVerts[i] = Vector2.Transform(vertices[i].Pos - origin, rotationMatrix) + origin;
}
SetVertices(newVerts);
} }
private void CalculateDimensions() private void CalculateDimensions()
@@ -453,7 +453,10 @@ namespace Barotrauma.Lights
//raster pattern on top of everything //raster pattern on top of everything
spriteBatch.Begin(blendState: BlendState.AlphaBlend, samplerState: SamplerState.LinearWrap); spriteBatch.Begin(blendState: BlendState.AlphaBlend, samplerState: SamplerState.LinearWrap);
spriteBatch.Draw(highlightRaster, new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height), new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height), Color.White * 0.5f); spriteBatch.Draw(highlightRaster,
new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height),
new Rectangle(0, 0, (int)(HighlightMap.Width / currLightMapScale * 0.5f), (int)(HighlightMap.Height / currLightMapScale * 0.5f)),
Color.White * 0.5f);
spriteBatch.End(); spriteBatch.End();
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"]; DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"];
@@ -364,15 +364,23 @@ namespace Barotrauma
} }
} }
if (GUI.KeyboardDispatcher.Subscriber == null)
{
float moveSpeed = 1000.0f;
Vector2 moveAmount = Vector2.Zero;
if (PlayerInput.KeyDown(InputType.Left)) { moveAmount += Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Right)) { moveAmount -= Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Up)) { moveAmount += Vector2.UnitY; }
if (PlayerInput.KeyDown(InputType.Down)) { moveAmount -= Vector2.UnitY; }
drawOffset += moveAmount * moveSpeed / zoom * deltaTime;
}
if (GUI.MouseOn == mapContainer) if (GUI.MouseOn == mapContainer)
{ {
zoom += PlayerInput.ScrollWheelSpeed / 1000.0f; zoom += PlayerInput.ScrollWheelSpeed / 1000.0f;
zoom = MathHelper.Clamp(zoom, 1.0f, 4.0f); zoom = MathHelper.Clamp(zoom, 1.0f, 4.0f);
if (PlayerInput.MidButtonHeld()) if (PlayerInput.MidButtonHeld()) { drawOffset += PlayerInput.MouseSpeed / zoom; }
{
drawOffset += PlayerInput.MouseSpeed / zoom;
}
#if DEBUG #if DEBUG
if (PlayerInput.DoubleClicked() && highlightedLocation != null) if (PlayerInput.DoubleClicked() && highlightedLocation != null)
{ {
@@ -551,11 +559,6 @@ namespace Barotrauma
null, connectionColor * MathHelper.Clamp(a, 0.1f, 0.5f), MathUtils.VectorToAngle(end - start), null, connectionColor * MathHelper.Clamp(a, 0.1f, 0.5f), MathUtils.VectorToAngle(end - start),
new Vector2(0, 16), SpriteEffects.None, 0.01f); new Vector2(0, 16), SpriteEffects.None, 0.01f);
} }
}
rect.Inflate(8, 8);
GUI.DrawRectangle(spriteBatch, rect, Color.Black, false, 0.0f, 8);
GUI.DrawRectangle(spriteBatch, rect, Color.LightGray);
if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames) if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames)
{ {
@@ -44,7 +44,7 @@ namespace Barotrauma
drawRect.Location -= Submarine.MainSub.Position.ToPoint(); drawRect.Location -= Submarine.MainSub.Position.ToPoint();
} }
drawRect.Y = -drawRect.Y; drawRect.Y = -drawRect.Y;
GUI.DrawRectangle(spriteBatch, drawRect, Color.DarkBlue); GUI.DrawRectangle(spriteBatch, drawRect, Color.White);
} }
public void DrawListLine(SpriteBatch spriteBatch, Vector2 pos, Color color) public void DrawListLine(SpriteBatch spriteBatch, Vector2 pos, Color color)
{ {
@@ -75,7 +75,10 @@ namespace Barotrauma
}; };
var h = new ConvexHull(verts, Color.Black, this); var h = new ConvexHull(verts, Color.Black, this);
h.Rotate(position, rotation); if (Math.Abs(rotation) > 0.001f)
{
h.Rotate(position, rotation);
}
convexHulls.Add(h); convexHulls.Add(h);
} }
@@ -292,8 +295,7 @@ namespace Barotrauma
{ {
if (damageEffect != null) if (damageEffect != null)
{ {
float newCutoff = Sections[i].damage > 0 ? float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / Prefab.Health);
MathHelper.Lerp(0.2f, 0.65f, Sections[i].damage / Prefab.Health) : 0.0f;
if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f || color != Submarine.DamageEffectColor) if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f || color != Submarine.DamageEffectColor)
{ {
@@ -360,6 +362,7 @@ namespace Barotrauma
-Bodies[i].Rotation, Color.White); -Bodies[i].Rotation, Color.White);
} }
} }
AiTarget?.Draw(spriteBatch);
} }
} }
@@ -35,7 +35,7 @@ namespace Barotrauma
public override void DrawPlacing(SpriteBatch spriteBatch, Rectangle placeRect, float scale = 1.0f) public override void DrawPlacing(SpriteBatch spriteBatch, Rectangle placeRect, float scale = 1.0f)
{ {
// TODO: the scale property is not used // TODO: the scale property is not used
sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), new Vector2(placeRect.Width, placeRect.Height), textureScale: TextureScale * Scale); sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), new Vector2(placeRect.Width, placeRect.Height), color: Color.White * 0.8f, textureScale: TextureScale * Scale);
} }
} }
} }
@@ -141,8 +141,23 @@ namespace Barotrauma.Networking
{ {
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
if (!permissions.HasFlag(ClientPermissions.ManageRound)) return false; if (!permissions.HasFlag(ClientPermissions.ManageRound)) { return false; }
RequestRoundEnd(); if (!Submarine.MainSub.AtStartPosition && !Submarine.MainSub.AtEndPosition)
{
var msgBox = new GUIMessageBox("", TextManager.Get("EndRoundSubNotAtLevelEnd"),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
msgBox.Buttons[0].OnClicked = (_, __) =>
{
GameMain.Client.RequestRoundEnd();
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
}
else
{
RequestRoundEnd();
}
return true; return true;
}, },
Visible = false Visible = false
@@ -622,7 +637,7 @@ namespace Barotrauma.Networking
if (gameStarted) SetRadioButtonColor(); if (gameStarted) SetRadioButtonColor();
if (ShowNetStats) if (ShowNetStats && client?.ServerConnection != null)
{ {
netStats.AddValue(NetStats.NetStatType.ReceivedBytes, client.ServerConnection.Statistics.ReceivedBytes); netStats.AddValue(NetStats.NetStatType.ReceivedBytes, client.ServerConnection.Statistics.ReceivedBytes);
netStats.AddValue(NetStats.NetStatType.SentBytes, client.ServerConnection.Statistics.SentBytes); netStats.AddValue(NetStats.NetStatType.SentBytes, client.ServerConnection.Statistics.SentBytes);
@@ -1109,7 +1124,7 @@ namespace Barotrauma.Networking
{ {
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ")."; string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ").";
DebugConsole.ThrowError(errorMsg, createMessageBox: true); DebugConsole.ThrowError(errorMsg, createMessageBox: true);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch"+levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
CoroutineManager.StartCoroutine(EndGame("")); CoroutineManager.StartCoroutine(EndGame(""));
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
@@ -1381,9 +1396,6 @@ namespace Barotrauma.Networking
case ServerNetObject.CLIENT_LIST: case ServerNetObject.CLIENT_LIST:
ReadClientList(inc); ReadClientList(inc);
break; break;
case ServerNetObject.CLIENT_LIST:
ReadClientList(inc);
break;
case ServerNetObject.CHAT_MESSAGE: case ServerNetObject.CHAT_MESSAGE:
ChatMessage.ClientRead(inc); ChatMessage.ClientRead(inc);
break; break;
@@ -1439,7 +1451,11 @@ namespace Barotrauma.Networking
break; break;
case ServerNetObject.ENTITY_EVENT: case ServerNetObject.ENTITY_EVENT:
case ServerNetObject.ENTITY_EVENT_INITIAL: case ServerNetObject.ENTITY_EVENT_INITIAL:
if (!entityEventManager.Read(objHeader, inc, sendingTime, entities)) { break; } if (!entityEventManager.Read(objHeader, inc, sendingTime, entities))
{
eventReadFailed = true;
break;
}
break; break;
case ServerNetObject.CHAT_MESSAGE: case ServerNetObject.CHAT_MESSAGE:
ChatMessage.ClientRead(inc); ChatMessage.ClientRead(inc);
@@ -1703,6 +1719,8 @@ namespace Barotrauma.Networking
SaveUtil.LoadGame(GameMain.GameSession.SavePath, GameMain.GameSession); SaveUtil.LoadGame(GameMain.GameSession.SavePath, GameMain.GameSession);
campaign.LastSaveID = campaign.PendingSaveID; campaign.LastSaveID = campaign.PendingSaveID;
DebugConsole.Log("Campaign save received, save ID " + campaign.LastSaveID);
//decrement campaign update ID so the server will send us the latest data //decrement campaign update ID so the server will send us the latest data
//(as there may have been campaign updates after the save file was created) //(as there may have been campaign updates after the save file was created)
campaign.LastUpdateID--; campaign.LastUpdateID--;
@@ -1720,7 +1738,7 @@ namespace Barotrauma.Networking
public override void CreateEntityEvent(INetSerializable entity, object[] extraData) public override void CreateEntityEvent(INetSerializable entity, object[] extraData)
{ {
if (!(entity is IClientSerializable)) throw new InvalidCastException("entity is not IClientSerializable"); if (!(entity is IClientSerializable)) throw new InvalidCastException("Entity is not IClientSerializable");
entityEventManager.CreateEvent(entity as IClientSerializable, extraData); entityEventManager.CreateEvent(entity as IClientSerializable, extraData);
} }
@@ -1989,7 +2007,8 @@ namespace Barotrauma.Networking
msg.Write(true); msg.WritePadBits(); msg.Write(true); msg.WritePadBits();
msg.Write(savePath); msg.Write(savePath);
msg.Write(mapSeed); msg.Write(mapSeed);
msg.Write(sub.FilePath); msg.Write(sub.Name);
msg.Write(sub.MD5Hash.Hash);
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
@@ -62,7 +62,7 @@ namespace Barotrauma
UpdateSubList(submarines); UpdateSubList(submarines);
// New game right side // New game right sideon
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), rightColumn.RectTransform), TextManager.Get("SaveName") + ":", textAlignment: Alignment.BottomLeft); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), rightColumn.RectTransform), TextManager.Get("SaveName") + ":", textAlignment: Alignment.BottomLeft);
saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.1f), rightColumn.RectTransform), string.Empty); saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.1f), rightColumn.RectTransform), string.Empty);
@@ -262,11 +262,6 @@ namespace Barotrauma
saveFiles = SaveUtil.GetSaveFiles(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer); saveFiles = SaveUtil.GetSaveFiles(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer);
} }
saveList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), loadGameContainer.RectTransform, Anchor.CenterLeft))
{
OnSelected = SelectSaveFile
};
saveList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), loadGameContainer.RectTransform, Anchor.CenterLeft)) saveList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), loadGameContainer.RectTransform, Anchor.CenterLeft))
{ {
OnSelected = SelectSaveFile OnSelected = SelectSaveFile
@@ -274,7 +269,9 @@ namespace Barotrauma
foreach (string saveFile in saveFiles) foreach (string saveFile in saveFiles)
{ {
XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile); string fileName = saveFile;
string subName = "";
string saveTime = "";
var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform), style: "ListBoxElement") var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform), style: "ListBoxElement")
{ {
UserData = saveFile UserData = saveFile
@@ -282,25 +279,38 @@ namespace Barotrauma
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform), var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform),
text: Path.GetFileNameWithoutExtension(saveFile)); text: Path.GetFileNameWithoutExtension(saveFile));
if (doc?.Root == null)
{
DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted.");
nameText.Color = Color.Red;
continue;
}
string submarineName = doc.Root.GetAttributeString("submarine", ""); if (!isMultiplayer)
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft),
text: submarineName, font: GUI.SmallFont)
{ {
UserData = saveFile XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile);
if (doc?.Root == null)
{
DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted.");
nameText.Color = Color.Red;
continue;
}
subName = doc.Root.GetAttributeString("submarine", "");
saveTime = doc.Root.GetAttributeString("savetime", "");
}
else
{
string[] splitSaveFile = saveFile.Split(';');
saveFrame.UserData = splitSaveFile[0];
fileName = nameText.Text = Path.GetFileNameWithoutExtension(splitSaveFile[0]);
if (splitSaveFile.Length > 1) { subName = splitSaveFile[1]; }
if (splitSaveFile.Length > 2) { saveTime = splitSaveFile[2]; }
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft),
text: subName, font: GUI.SmallFont)
{
UserData = fileName
}; };
string saveTime = doc.Root.GetAttributeString("savetime", "");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform),
text: saveTime, textAlignment: Alignment.Right, font: GUI.SmallFont) text: saveTime, textAlignment: Alignment.Right, font: GUI.SmallFont)
{ {
UserData = saveFile UserData = fileName
}; };
} }
@@ -345,13 +355,6 @@ namespace Barotrauma
}; };
} }
public void UpdateTutorialSelection()
{
if (isMultiplayer) return;
Tutorial contextualTutorial = Tutorial.Tutorials.Find(t => t is ContextualTutorial);
contextualTutorialBox.Selected = (contextualTutorial != null) ? !GameMain.Config.CompletedTutorialNames.Contains(contextualTutorial.Name) : true;
}
public void UpdateTutorialSelection() public void UpdateTutorialSelection()
{ {
if (isMultiplayer) return; if (isMultiplayer) return;
@@ -457,11 +457,14 @@ namespace Barotrauma
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
OnClicked = (GUIButton btn, object obj) => { StartRound?.Invoke(); return true; }, OnClicked = (GUIButton btn, object obj) => { StartRound?.Invoke(); return true; },
Enabled = true, Enabled = true
Visible = GameMain.Client == null ||
GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)
}; };
if (GameMain.Client != null)
{
startButton.Visible = !GameMain.Client.GameStarted &&
(GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign));
}
} }
OnLocationSelected?.Invoke(location, connection); OnLocationSelected?.Invoke(location, connection);
@@ -118,6 +118,19 @@ namespace Barotrauma
instance = this; instance = this;
} }
private void Reset()
{
AnimParams.ForEach(a => a.Reset(true));
RagdollParams.Reset(true);
RagdollParams.ClearHistory();
CurrentAnimation.ClearHistory();
if (!character.Removed)
{
character.Remove();
}
character = null;
}
public override void Deselect() public override void Deselect()
{ {
base.Deselect(); base.Deselect();
@@ -128,15 +141,7 @@ namespace Barotrauma
isEndlessRunner = false; isEndlessRunner = false;
if (character != null) if (character != null)
{ {
AnimParams.ForEach(a => a.Reset(true)); Reset();
RagdollParams.Reset(true);
RagdollParams.ClearHistory();
CurrentAnimation.ClearHistory();
if (!character.Removed)
{
character.Remove();
}
character = null;
} }
GameMain.World.ProcessChanges(); GameMain.World.ProcessChanges();
} }
@@ -393,6 +398,12 @@ namespace Barotrauma
} }
if (!isFreezed) if (!isFreezed)
{ {
if (character.AnimController.Invalid)
{
Reset();
SpawnCharacter(currentCharacterConfig);
}
Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position); Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position);
Submarine.MainSub.Update((float)deltaTime); Submarine.MainSub.Update((float)deltaTime);
@@ -1215,7 +1226,7 @@ namespace Barotrauma
Cam.Position = character.WorldPosition; Cam.Position = character.WorldPosition;
} }
private bool CreateCharacter(string name, bool isHumanoid, params object[] ragdollConfig) private bool CreateCharacter(string name, string mainFolder, bool isHumanoid, params object[] ragdollConfig)
{ {
var contentPackage = GameMain.Config.SelectedContentPackages.LastOrDefault(); var contentPackage = GameMain.Config.SelectedContentPackages.LastOrDefault();
if (contentPackage == null) if (contentPackage == null)
@@ -1228,23 +1239,22 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (vanilla != null && contentPackage == vanilla) if (vanilla != null && contentPackage == vanilla)
{ {
GUI.AddMessage($"Cannot edit the Vanilla content!", Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
return false; return false;
} }
#endif #endif
string speciesName = name; string speciesName = name;
string mainFolder = $"Content/Characters/{speciesName}";
// Config file // Config file
string configFilePath = $"{mainFolder}/{speciesName}.xml"; string configFilePath = Path.Combine(mainFolder, $"{speciesName}.xml").Replace(@"\", @"/");
if (ContentPackage.GetFilesOfType(GameMain.SelectedPackages, ContentType.Character).None(path => path.Contains(speciesName))) if (ContentPackage.GetFilesOfType(GameMain.SelectedPackages, ContentType.Character).None(path => path.Contains(speciesName)))
{ {
// Create the config file // Create the config file
XElement mainElement = new XElement("Character", XElement mainElement = new XElement("Character",
new XAttribute("name", speciesName), new XAttribute("name", speciesName),
new XAttribute("humanoid", isHumanoid), new XAttribute("humanoid", isHumanoid),
new XElement("ragdolls"), new XElement("ragdolls", new XAttribute("folder", Path.Combine(mainFolder, $"Ragdolls/").Replace(@"\", @"/"))),
new XElement("animations"), new XElement("animations", new XAttribute("folder", Path.Combine(mainFolder, $"Animations/").Replace(@"\", @"/"))),
new XElement("health"), new XElement("health"),
new XElement("ai")); new XElement("ai"));
XDocument doc = new XDocument(mainElement); XDocument doc = new XDocument(mainElement);
@@ -1259,13 +1269,13 @@ namespace Barotrauma
DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path)); DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path));
} }
// Ragdoll // Ragdoll
string ragdollFolder = RagdollParams.GetDefaultFolder(speciesName); string ragdollFolder = RagdollParams.GetFolder(speciesName);
string ragdollPath = RagdollParams.GetDefaultFile(speciesName); string ragdollPath = RagdollParams.GetDefaultFile(speciesName);
RagdollParams ragdollParams = isHumanoid RagdollParams ragdollParams = isHumanoid
? RagdollParams.CreateDefault<HumanRagdollParams>(ragdollPath, speciesName, ragdollConfig) ? RagdollParams.CreateDefault<HumanRagdollParams>(ragdollPath, speciesName, ragdollConfig)
: RagdollParams.CreateDefault<FishRagdollParams>(ragdollPath, speciesName, ragdollConfig) as RagdollParams; : RagdollParams.CreateDefault<FishRagdollParams>(ragdollPath, speciesName, ragdollConfig) as RagdollParams;
// Animations // Animations
string animFolder = AnimationParams.GetDefaultFolder(speciesName); string animFolder = AnimationParams.GetFolder(speciesName);
foreach (AnimationType animType in Enum.GetValues(typeof(AnimationType))) foreach (AnimationType animType in Enum.GetValues(typeof(AnimationType)))
{ {
if (animType != AnimationType.NotDefined) if (animType != AnimationType.NotDefined)
@@ -1869,7 +1879,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
return false; return false;
} }
#endif #endif
@@ -1884,7 +1894,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
return false; return false;
} }
#endif #endif
@@ -1956,7 +1966,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
box.Close(); box.Close();
return false; return false;
} }
@@ -2089,7 +2099,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
box.Close(); box.Close();
return false; return false;
} }
@@ -3918,7 +3928,7 @@ namespace Barotrauma
private void DrawJointLimitWidgets(SpriteBatch spriteBatch, Limb limb, LimbJoint joint, Vector2 drawPos, bool autoFreeze, bool allowPairEditing, float rotationOffset = 0) private void DrawJointLimitWidgets(SpriteBatch spriteBatch, Limb limb, LimbJoint joint, Vector2 drawPos, bool autoFreeze, bool allowPairEditing, float rotationOffset = 0)
{ {
rotationOffset -= MathHelper.ToRadians(RagdollParams.SpritesheetOrientation); rotationOffset += MathHelper.ToRadians(RagdollParams.SpritesheetOrientation);
Color angleColor = joint.UpperLimit - joint.LowerLimit > 0 ? Color.LightGreen * 0.5f : Color.Red; Color angleColor = joint.UpperLimit - joint.LowerLimit > 0 ? Color.LightGreen * 0.5f : Color.Red;
DrawRadialWidget(spriteBatch, drawPos, MathHelper.ToDegrees(joint.UpperLimit), $"joint.jointParams.Name {GetCharacterEditorTranslation("UpperLimit")}", Color.Cyan, angle => DrawRadialWidget(spriteBatch, drawPos, MathHelper.ToDegrees(joint.UpperLimit), $"joint.jointParams.Name {GetCharacterEditorTranslation("UpperLimit")}", Color.Cyan, angle =>
{ {
@@ -4652,7 +4662,7 @@ namespace Barotrauma
LimbXElements.Values, LimbXElements.Values,
JointXElements JointXElements
}; };
if (CharacterEditorScreen.instance.CreateCharacter(Name, IsHumanoid, ragdollParams)) if (CharacterEditorScreen.instance.CreateCharacter(Name, Path.GetDirectoryName(XMLPath), IsHumanoid, ragdollParams))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CharacterCreated").Replace("[name]", Name), Color.Green, font: GUI.Font); GUI.AddMessage(GetCharacterEditorTranslation("CharacterCreated").Replace("[name]", Name), Color.Green, font: GUI.Font);
} }
@@ -51,7 +51,7 @@ namespace Barotrauma
new GUIImage(new RectTransform(new Vector2(0.35f, 0.2f), Frame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.05f, 0.05f) }, new GUIImage(new RectTransform(new Vector2(0.35f, 0.2f), Frame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.05f, 0.05f) },
style: "TitleText"); style: "TitleText");
buttonsParent = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.85f), parent: Frame.RectTransform, anchor: Anchor.BottomLeft, pivot: Pivot.BottomLeft) buttonsParent = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.85f), parent: Frame.RectTransform, anchor: Anchor.BottomLeft, pivot: Pivot.BottomLeft)
{ {
AbsoluteOffset = new Point(50, 0) AbsoluteOffset = new Point(50, 0)
}) })
@@ -61,7 +61,7 @@ namespace Barotrauma
}; };
// === CAMPAIGN // === CAMPAIGN
var campaignHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.1f, 0.0f) }, isHorizontal: true); var campaignHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.1f, 0.0f) }, isHorizontal: true);
new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), campaignHolder.RectTransform), "MainMenuCampaignIcon") new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), campaignHolder.RectTransform), "MainMenuCampaignIcon")
{ {
@@ -107,7 +107,7 @@ namespace Barotrauma
}; };
// === MULTIPLAYER // === MULTIPLAYER
var multiplayerHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.0f) }, isHorizontal: true); var multiplayerHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.0f) }, isHorizontal: true);
new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), multiplayerHolder.RectTransform), "MainMenuMultiplayerIcon") new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), multiplayerHolder.RectTransform), "MainMenuMultiplayerIcon")
{ {
@@ -152,7 +152,7 @@ namespace Barotrauma
}; };
// === CUSTOMIZE // === CUSTOMIZE
var customizeHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.15f, 0.0f) }, isHorizontal: true); var customizeHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.15f, 0.0f) }, isHorizontal: true);
new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), customizeHolder.RectTransform), "MainMenuCustomizeIcon") new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), customizeHolder.RectTransform), "MainMenuCustomizeIcon")
{ {
@@ -209,7 +209,7 @@ namespace Barotrauma
} }
// === OPTION // === OPTION
var optionHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.5f), parent: buttonsParent.RectTransform), isHorizontal: true); var optionHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.5f), parent: buttonsParent.RectTransform), isHorizontal: true);
new GUIImage(new RectTransform(new Vector2(0.15f, 0.6f), optionHolder.RectTransform), "MainMenuOptionIcon") new GUIImage(new RectTransform(new Vector2(0.15f, 0.6f), optionHolder.RectTransform), "MainMenuOptionIcon")
{ {
@@ -219,7 +219,7 @@ namespace Barotrauma
//spacing //spacing
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.0f), optionHolder.RectTransform), style: null); new GUIFrame(new RectTransform(new Vector2(0.01f, 0.0f), optionHolder.RectTransform), style: null);
var optionButtons = new GUILayoutGroup(new RectTransform(new Vector2(0.55f, 1.0f), parent: optionHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.15f) }); var optionButtons = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), parent: optionHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.15f) });
var optionList = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.15f), parent: optionButtons.RectTransform)) var optionList = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.15f), parent: optionButtons.RectTransform))
{ {
@@ -241,7 +241,7 @@ namespace Barotrauma
//debug button for quickly starting a new round //debug button for quickly starting a new round
#if DEBUG #if DEBUG
new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), buttonsParent.RectTransform, Anchor.TopLeft, Pivot.BottomLeft) { AbsoluteOffset = new Point(0, -40) }, new GUIButton(new RectTransform(new Vector2(0.8f, 0.1f), buttonsParent.RectTransform, Anchor.TopLeft, Pivot.BottomLeft) { AbsoluteOffset = new Point(0, -40) },
"Quickstart (dev)", style: "GUIButtonLarge", color: Color.Red) "Quickstart (dev)", style: "GUIButtonLarge", color: Color.Red)
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
@@ -255,7 +255,7 @@ namespace Barotrauma
#endif #endif
var minButtonSize = new Point(120, 20); var minButtonSize = new Point(120, 20);
var maxButtonSize = new Point(240, 40); var maxButtonSize = new Point(480, 80);
/*new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonsParent.RectTransform), TextManager.Get("TutorialButton"), style: "GUIButtonLarge") /*new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonsParent.RectTransform), TextManager.Get("TutorialButton"), style: "GUIButtonLarge")
{ {
@@ -270,16 +270,21 @@ namespace Barotrauma
SetupButtons(buttons); SetupButtons(buttons);
buttons.ForEach(b => b.TextBlock.SetTextPos());*/ buttons.ForEach(b => b.TextBlock.SetTextPos());*/
var relativeSize = new Vector2(0.5f, 0.5f); var relativeSize = new Vector2(0.6f, 0.5f);
var minSize = new Point(600, 400); var minSize = new Point(600, 400);
var maxSize = new Point(900, 600); var maxSize = new Point(2000, 1500);
var anchor = Anchor.Center; var anchor = Anchor.CenterRight;
var pivot = Pivot.Center; var pivot = Pivot.CenterRight;
menuTabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length + 1]; Vector2 relativeSpacing = new Vector2(0.05f, 0.0f);
menuTabs[(int)Tab.NewGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize)); menuTabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length + 1];
menuTabs[(int)Tab.Settings] = new GUIFrame(new RectTransform(new Vector2(relativeSize.X, 0.8f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing },
style: null);
menuTabs[(int)Tab.NewGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
var paddedNewGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[(int)Tab.NewGame].RectTransform, Anchor.Center), style: null); var paddedNewGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[(int)Tab.NewGame].RectTransform, Anchor.Center), style: null);
menuTabs[(int)Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize)); menuTabs[(int)Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
var paddedLoadGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[(int)Tab.LoadGame].RectTransform, Anchor.Center), style: null); var paddedLoadGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[(int)Tab.LoadGame].RectTransform, Anchor.Center), style: null);
campaignSetupUI = new CampaignSetupUI(false, paddedNewGame, paddedLoadGame, Submarine.SavedSubmarines) campaignSetupUI = new CampaignSetupUI(false, paddedNewGame, paddedLoadGame, Submarine.SavedSubmarines)
@@ -290,13 +295,13 @@ namespace Barotrauma
var hostServerScale = new Vector2(0.7f, 1.0f); var hostServerScale = new Vector2(0.7f, 1.0f);
menuTabs[(int)Tab.HostServer] = new GUIFrame(new RectTransform( menuTabs[(int)Tab.HostServer] = new GUIFrame(new RectTransform(
Vector2.Multiply(relativeSize, hostServerScale), GUI.Canvas, anchor, pivot, minSize.Multiply(hostServerScale), maxSize.Multiply(hostServerScale))); Vector2.Multiply(relativeSize, hostServerScale), GUI.Canvas, anchor, pivot, minSize.Multiply(hostServerScale), maxSize.Multiply(hostServerScale)) { RelativeOffset = relativeSpacing });
CreateHostServerFields(); CreateHostServerFields();
//---------------------------------------------------------------------- //----------------------------------------------------------------------
menuTabs[(int)Tab.Tutorials] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize)); menuTabs[(int)Tab.Tutorials] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
//PLACEHOLDER //PLACEHOLDER
var tutorialList = new GUIListBox( var tutorialList = new GUIListBox(
@@ -377,6 +382,7 @@ namespace Barotrauma
return false; return false;
} }
GameMain.Config.ResetSettingsFrame();
selectedTab = (Tab)obj; selectedTab = (Tab)obj;
switch (selectedTab) switch (selectedTab)
@@ -389,8 +395,9 @@ namespace Barotrauma
campaignSetupUI.UpdateLoadMenu(); campaignSetupUI.UpdateLoadMenu();
break; break;
case Tab.Settings: case Tab.Settings:
GameMain.Config.ResetSettingsFrame(); menuTabs[(int)Tab.Settings].RectTransform.ClearChildren();
menuTabs[(int)Tab.Settings] = GameMain.Config.SettingsFrame; GameMain.Config.SettingsFrame.RectTransform.Parent = menuTabs[(int)Tab.Settings].RectTransform;
GameMain.Config.SettingsFrame.RectTransform.RelativeSize = Vector2.One;
break; break;
case Tab.JoinServer: case Tab.JoinServer:
GameMain.ServerListScreen.Select(); GameMain.ServerListScreen.Select();
@@ -233,7 +233,8 @@ namespace Barotrauma
levelSeed = value; levelSeed = value;
backgroundSprite = LocationType.Random(levelSeed)?.GetPortrait(ToolBox.StringToInt(levelSeed)); int intSeed = ToolBox.StringToInt(levelSeed);
backgroundSprite = LocationType.Random(new MTRandom(intSeed))?.GetPortrait(intSeed);
seedBox.Text = levelSeed; seedBox.Text = levelSeed;
//lastUpdateID++; //lastUpdateID++;
@@ -726,6 +727,12 @@ namespace Barotrauma
spectateButton.Visible = GameMain.Client.GameStarted; spectateButton.Visible = GameMain.Client.GameStarted;
ReadyToStartBox.Visible = !GameMain.Client.GameStarted; ReadyToStartBox.Visible = !GameMain.Client.GameStarted;
ReadyToStartBox.Selected = false; ReadyToStartBox.Selected = false;
if (campaignUI?.StartButton != null)
{
campaignUI.StartButton.Visible = !GameMain.Client.GameStarted &&
(GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign));
}
GameMain.Client.SetReadyToStart(ReadyToStartBox); GameMain.Client.SetReadyToStart(ReadyToStartBox);
} }
else else
@@ -847,9 +854,9 @@ namespace Barotrauma
if (campaignUI?.StartButton != null) if (campaignUI?.StartButton != null)
{ {
campaignUI.StartButton.Visible = campaignUI.StartButton.Visible = !GameMain.Client.GameStarted &&
GameMain.Client.HasPermission(ClientPermissions.ManageRound) || (GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign); GameMain.Client.HasPermission(ClientPermissions.ManageCampaign));
} }
} }
@@ -860,18 +867,21 @@ namespace Barotrauma
spectateButton.Enabled = true; spectateButton.Enabled = true;
} }
public void SetCampaignCharacterInfo(CharacterInfo characterInfo) public void SetCampaignCharacterInfo(CharacterInfo newCampaignCharacterInfo)
{ {
if (CampaignCharacterDiscarded) return; if (newCampaignCharacterInfo != null)
campaignCharacterInfo = characterInfo;
if (campaignCharacterInfo != null)
{ {
UpdatePlayerFrame(campaignCharacterInfo, false); if (CampaignCharacterDiscarded) { return; }
if (campaignCharacterInfo != newCampaignCharacterInfo)
{
campaignCharacterInfo = newCampaignCharacterInfo;
UpdatePlayerFrame(campaignCharacterInfo, false);
}
} }
else else if (campaignCharacterInfo != null)
{ {
UpdatePlayerFrame(null, true); campaignCharacterInfo = null;
UpdatePlayerFrame(campaignCharacterInfo, false);
} }
} }
@@ -337,7 +337,24 @@ namespace Barotrauma
var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform), TextManager.Get("ShowLighting")) var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform), TextManager.Get("ShowLighting"))
{ {
Selected = lightingEnabled, Selected = lightingEnabled,
OnSelected = (GUITickBox obj) => { lightingEnabled = obj.Selected; return true; } OnSelected = (GUITickBox obj) =>
{
lightingEnabled = obj.Selected;
if (lightingEnabled)
{
//turn off lights that are inside containers
foreach (Item item in Item.ItemList)
{
foreach (LightComponent lightComponent in item.GetComponents<LightComponent>())
{
lightComponent.Light.Color = item.Container != null || (item.body != null && !item.body.Enabled) ?
Color.Transparent :
lightComponent.LightColor;
}
}
}
return true;
}
}; };
tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform), TextManager.Get("ShowWalls")) tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform), TextManager.Get("ShowWalls"))
{ {
@@ -564,6 +581,9 @@ namespace Barotrauma
MapEntityPrefab.Selected = null; MapEntityPrefab.Selected = null;
saveFrame = null;
loadFrame = null;
MapEntity.DeselectAll(); MapEntity.DeselectAll();
MapEntity.SelectionGroups.Clear(); MapEntity.SelectionGroups.Clear();
@@ -766,6 +786,20 @@ namespace Barotrauma
savePath = Path.Combine(Submarine.SavePath, savePath); savePath = Path.Combine(Submarine.SavePath, savePath);
} }
#if !DEBUG
var vanilla = GameMain.VanillaContent;
if (vanilla != null)
{
var vanillaSubs = vanilla.GetFilesOfType(ContentType.Submarine);
string pathToCompare = savePath.Replace(@"\", @"/").ToLowerInvariant();
if (vanillaSubs.Any(sub => sub.Replace(@"\", @"/").ToLowerInvariant() == pathToCompare))
{
GUI.AddMessage(TextManager.Get("CannotEditVanillaSubs"), Color.Red, font: GUI.LargeFont);
return false;
}
}
#endif
/*foreach (var contentPackage in GameMain.Config.SelectedContentPackages) /*foreach (var contentPackage in GameMain.Config.SelectedContentPackages)
{ {
Submarine.MainSub.RequiredContentPackages.Add(contentPackage.Name); Submarine.MainSub.RequiredContentPackages.Add(contentPackage.Name);
@@ -2040,6 +2074,10 @@ namespace Barotrauma
dummyCharacter.SelectedConstruction = null; dummyCharacter.SelectedConstruction = null;
} }
} }
else if (MapEntity.SelectedList.Count == 1)
{
(MapEntity.SelectedList[0] as Item)?.UpdateHUD(cam, dummyCharacter, (float)deltaTime);
}
CharacterHUD.Update((float)deltaTime, dummyCharacter, cam); CharacterHUD.Update((float)deltaTime, dummyCharacter, cam);
} }
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.9.6")] [assembly: AssemblyVersion("0.8.9.8")]
[assembly: AssemblyFileVersion("0.8.9.6")] [assembly: AssemblyFileVersion("0.8.9.8")]
@@ -20,8 +20,10 @@ namespace Barotrauma
if (Job != null) if (Job != null)
{ {
msg.Write(Job.Prefab.Identifier); msg.Write(Job.Prefab.Identifier);
msg.Write((byte)Job.Skills.Count);
foreach (Skill skill in Job.Skills) foreach (Skill skill in Job.Skills)
{ {
msg.Write(skill.Identifier);
msg.Write(skill.Level); msg.Write(skill.Level);
} }
} }
@@ -15,6 +15,8 @@ namespace Barotrauma
private bool networkUpdateSent; private bool networkUpdateSent;
private double LastInputTime;
public float GetPositionUpdateInterval(Client recipient) public float GetPositionUpdateInterval(Client recipient)
{ {
if (!Enabled) { return 1000.0f; } if (!Enabled) { return 1000.0f; }
@@ -61,6 +63,15 @@ namespace Barotrauma
else if (memInput.Count == 0) else if (memInput.Count == 0)
{ {
AnimController.Frozen = true; AnimController.Frozen = true;
if (Timing.TotalTime > LastInputTime + 0.5)
{
//no inputs have been received in 0.5 seconds, reset input
//(if there's a temporary network hiccup that prevents us from receiving inputs, we assume the inputs haven't changed,
//but if it takes too long, for example due to a client crashing/disconnecting, we don't want to keep the character
//firing a welding tool or whatever else they were doing until the kill disconnect timer kicks in)
prevDequeuedInput = dequeuedInput =
dequeuedInput.HasFlag(InputNetFlags.FacingLeft) ? InputNetFlags.FacingLeft : InputNetFlags.None;
}
} }
else else
{ {
@@ -190,6 +201,7 @@ namespace Barotrauma
networkUpdateID = (ushort)(networkUpdateID - i) networkUpdateID = (ushort)(networkUpdateID - i)
}; };
memInput.Insert(i, newMem); memInput.Insert(i, newMem);
LastInputTime = Timing.TotalTime;
} }
} }
@@ -11,11 +11,11 @@ namespace Barotrauma
{ {
private List<CharacterCampaignData> characterData = new List<CharacterCampaignData>(); private List<CharacterCampaignData> characterData = new List<CharacterCampaignData>();
public static void StartNewCampaign(string savePath, string subName, string seed) public static void StartNewCampaign(string savePath, string subPath, string seed)
{ {
if (string.IsNullOrWhiteSpace(savePath)) return; if (string.IsNullOrWhiteSpace(savePath)) return;
GameMain.GameSession = new GameSession(new Submarine(subName, ""), savePath, GameMain.GameSession = new GameSession(new Submarine(subPath, ""), savePath,
GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign")); GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign"));
var campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode); var campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode);
campaign.GenerateMap(seed); campaign.GenerateMap(seed);
@@ -251,6 +251,7 @@ namespace Barotrauma
} }
lastSaveID++; lastSaveID++;
DebugConsole.Log("Campaign saved, save ID " + lastSaveID);
} }
} }
} }
@@ -1,9 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using System; using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -14,5 +10,16 @@ namespace Barotrauma.Items.Components
//let the clients know the initial deterioration delay //let the clients know the initial deterioration delay
item.CreateServerEvent(this); item.CreateServerEvent(this);
} }
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
if (c.Character == null) return;
StartRepairing(c.Character);
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.Write(deteriorationTimer);
}
} }
} }
+12 -2
View File
@@ -20,6 +20,16 @@ namespace Barotrauma
partial void UpdateProjSpecific(float deltaTime, Camera cam) partial void UpdateProjSpecific(float deltaTime, Camera cam)
{ {
if (IdFreed) { return; } if (IdFreed) { return; }
//don't create updates if all clients are very far from the hull
float hullUpdateDistanceSqr = NetConfig.HullUpdateDistance * NetConfig.HullUpdateDistance;
if (!GameMain.Server.ConnectedClients.Any(c =>
c.Character != null &&
Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr))
{
return;
}
//update client hulls if the amount of water has changed by >10% //update client hulls if the amount of water has changed by >10%
//or if oxygen percentage has changed by 5% //or if oxygen percentage has changed by 5%
if (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f || if (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f ||
@@ -31,8 +41,8 @@ namespace Barotrauma
GameMain.NetworkMember.CreateEntityEvent(this); GameMain.NetworkMember.CreateEntityEvent(this);
lastSentVolume = waterVolume; lastSentVolume = waterVolume;
lastSentOxygen = OxygenPercentage; lastSentOxygen = OxygenPercentage;
sendUpdateTimer = NetworkUpdateInterval; sendUpdateTimer = NetConfig.HullUpdateInterval;
} }
} }
} }
@@ -46,7 +46,7 @@ namespace Barotrauma.Networking
//when was a specific entity event last sent to the client //when was a specific entity event last sent to the client
// key = event id, value = NetTime.Now when sending // key = event id, value = NetTime.Now when sending
public readonly Dictionary<UInt16, float> EntityEventLastSent = new Dictionary<UInt16, float>(); public readonly Dictionary<UInt16, double> EntityEventLastSent = new Dictionary<UInt16, double>();
//when was a position update for a given entity last sent to the client //when was a position update for a given entity last sent to the client
// key = entity id, value = NetTime.Now when sending // key = entity id, value = NetTime.Now when sending
@@ -187,7 +187,7 @@ namespace Barotrauma.Networking
transfer.WaitTimer -= deltaTime; transfer.WaitTimer -= deltaTime;
if (transfer.WaitTimer > 0.0f) continue; if (transfer.WaitTimer > 0.0f) continue;
if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, 1)) continue; if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel)) continue;
transfer.WaitTimer = 0.05f;// transfer.Connection.AverageRoundtripTime; transfer.WaitTimer = 0.05f;// transfer.Connection.AverageRoundtripTime;
@@ -202,15 +202,6 @@ namespace Barotrauma.Networking
{ {
message = peer.CreateMessage(); message = peer.CreateMessage();
message.Write((byte)ServerPacketHeader.FILE_TRANSFER); message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
message.Write((byte)FileTransferMessageType.Initiate);
message.Write((byte)transfer.FileType);
message.Write((ushort)chunkLen);
message.Write((ulong)transfer.Data.Length);
message.Write(transfer.FileName);
GameMain.Server.CompressOutgoingMessage(message);
transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel);
transfer.Status = FileTransferStatus.Sending;
//if the recipient is the owner of the server (= a client running the server from the main exe) //if the recipient is the owner of the server (= a client running the server from the main exe)
//we don't need to send anything, the client can just read the file directly //we don't need to send anything, the client can just read the file directly
@@ -10,6 +10,7 @@ using System.Text;
using System.IO.Compression; using System.IO.Compression;
using System.IO; using System.IO;
using Barotrauma.Steam; using Barotrauma.Steam;
using System.Xml.Linq;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -715,23 +716,25 @@ namespace Barotrauma.Networking
{ {
string savePath = inc.ReadString(); string savePath = inc.ReadString();
string seed = inc.ReadString(); string seed = inc.ReadString();
string subPath = inc.ReadString(); string subName = inc.ReadString();
string subHash = inc.ReadString();
if (!File.Exists(subPath)) var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash);
if (matchingSub == null)
{ {
SendDirectChatMessage( SendDirectChatMessage(
TextManager.Get("CampaignStartFailedSubNotFound").Replace("[subpath]", subPath), TextManager.Get("CampaignStartFailedSubNotFound").Replace("[subname]", subName),
connectedClient, ChatMessageType.MessageBox); connectedClient, ChatMessageType.MessageBox);
} }
else else
{ {
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, subPath, seed); if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, matchingSub.FilePath, seed);
} }
} }
else else
{ {
string saveName = inc.ReadString(); string saveName = inc.ReadString();
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.LoadCampaign(saveName); if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.LoadCampaign(saveName);
} }
break; break;
@@ -811,7 +814,16 @@ namespace Barotrauma.Networking
Log(c.Name + " has reported an error: " + errorStr, ServerLog.MessageType.Error); Log(c.Name + " has reported an error: " + errorStr, ServerLog.MessageType.Error);
GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:LevelsDontMatch" + error, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr); GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:LevelsDontMatch" + error, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr);
KickClient(c, errorStr);
if (c.Connection == OwnerConnection)
{
SendDirectChatMessage(errorStr, c, ChatMessageType.MessageBox);
EndGame();
}
else
{
KickClient(c, errorStr);
}
} }
public override void CreateEntityEvent(INetSerializable entity, object[] extraData = null) public override void CreateEntityEvent(INetSerializable entity, object[] extraData = null)
@@ -1028,7 +1040,7 @@ namespace Barotrauma.Networking
if (command == ClientPermissions.ManageRound && inc.PeekBoolean() && if (command == ClientPermissions.ManageRound && inc.PeekBoolean() &&
GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign) GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign)
{ {
if (!mpCampaign.AllowedToEndRound(sender.Character)) if (!mpCampaign.AllowedToEndRound(sender.Character) && !sender.HasPermission(command))
{ {
return; return;
} }
@@ -1114,9 +1126,22 @@ namespace Barotrauma.Networking
UInt16 modeIndex = inc.ReadUInt16(); UInt16 modeIndex = inc.ReadUInt16();
if (GameMain.NetLobbyScreen.GameModes[modeIndex].Identifier.ToLowerInvariant() == "multiplayercampaign") if (GameMain.NetLobbyScreen.GameModes[modeIndex].Identifier.ToLowerInvariant() == "multiplayercampaign")
{ {
string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer);
for (int i = 0; i < saveFiles.Length; i++)
{
XDocument doc = SaveUtil.LoadGameSessionDoc(saveFiles[i]);
if (doc?.Root != null)
{
saveFiles[i] =
string.Join(";",
saveFiles[i].Replace(';', ' '),
doc.Root.GetAttributeString("submarine", ""),
doc.Root.GetAttributeString("savetime", ""));
}
}
NetOutgoingMessage msg = server.CreateMessage(); NetOutgoingMessage msg = server.CreateMessage();
msg.Write((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO); msg.Write((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO);
string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer);
msg.Write((UInt16)saveFiles.Count()); msg.Write((UInt16)saveFiles.Count());
foreach (string saveFile in saveFiles) foreach (string saveFile in saveFiles)
{ {
@@ -1201,6 +1226,7 @@ namespace Barotrauma.Networking
ClientWriteLobby(c); ClientWriteLobby(c);
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign && if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign &&
GameMain.NetLobbyScreen.SelectedMode == campaign.Preset &&
NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave)) NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave))
{ {
//already sent an up-to-date campaign save //already sent an up-to-date campaign save
@@ -1335,10 +1361,6 @@ namespace Barotrauma.Networking
WriteClientList(c, outmsg); WriteClientList(c, outmsg);
clientListBytes = outmsg.LengthBytes - clientListBytes; clientListBytes = outmsg.LengthBytes - clientListBytes;
int eventManagerBytes = outmsg.LengthBytes;
entityEventManager.Write(c, outmsg, out List<NetEntityEvent> sentEvents);
eventManagerBytes = outmsg.LengthBytes - eventManagerBytes;
int chatMessageBytes = outmsg.LengthBytes; int chatMessageBytes = outmsg.LengthBytes;
WriteChatMessages(outmsg, c); WriteChatMessages(outmsg, c);
chatMessageBytes = outmsg.LengthBytes - chatMessageBytes; chatMessageBytes = outmsg.LengthBytes - chatMessageBytes;
@@ -1348,7 +1370,8 @@ namespace Barotrauma.Networking
while (!c.NeedsMidRoundSync && c.PendingPositionUpdates.Count > 0) while (!c.NeedsMidRoundSync && c.PendingPositionUpdates.Count > 0)
{ {
var entity = c.PendingPositionUpdates.Peek(); var entity = c.PendingPositionUpdates.Peek();
if (entity == null || entity.Removed) if (entity == null || entity.Removed ||
(entity is Item item && item.PositionUpdateInterval == float.PositiveInfinity))
{ {
c.PendingPositionUpdates.Dequeue(); c.PendingPositionUpdates.Dequeue();
continue; continue;
@@ -1387,25 +1410,55 @@ namespace Barotrauma.Networking
errorMsg += errorMsg +=
" Client list size: " + clientListBytes + " bytes\n" + " Client list size: " + clientListBytes + " bytes\n" +
" Chat message size: " + chatMessageBytes + " bytes\n" + " Chat message size: " + chatMessageBytes + " bytes\n" +
" Event size: " + eventManagerBytes + " bytes\n" +
" Position update size: " + positionUpdateBytes + " bytes\n\n"; " Position update size: " + positionUpdateBytes + " bytes\n\n";
if (sentEvents != null && sentEvents.Count > 0)
{
errorMsg += "Sent events: \n";
foreach (var entityEvent in sentEvents)
{
errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n";
}
}
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
} }
CompressOutgoingMessage(outmsg); CompressOutgoingMessage(outmsg);
server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable); server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable);
//---------------------------------------------------------------------------
for (int i = 0; i < NetConfig.MaxEventPacketsPerUpdate; i++)
{
outmsg = server.CreateMessage();
outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME);
outmsg.Write((float)NetTime.Now);
int eventManagerBytes = outmsg.LengthBytes;
entityEventManager.Write(c, outmsg, out List<NetEntityEvent> sentEvents);
eventManagerBytes = outmsg.LengthBytes - eventManagerBytes;
if (sentEvents.Count == 0)
{
break;
}
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
if (outmsg.LengthBytes > NetPeerConfiguration.MaximumTransmissionUnit)
{
string errorMsg = "Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + NetPeerConfiguration.MaximumTransmissionUnit + ")\n";
errorMsg +=
" Event size: " + eventManagerBytes + " bytes\n";
if (sentEvents != null && sentEvents.Count > 0)
{
errorMsg += "Sent events: \n";
foreach (var entityEvent in sentEvents)
{
errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n";
}
}
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame2:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
CompressOutgoingMessage(outmsg);
server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable);
}
} }
private void WriteClientList(Client c, NetOutgoingMessage outmsg) private void WriteClientList(Client c, NetOutgoingMessage outmsg)
@@ -1492,7 +1545,8 @@ namespace Barotrauma.Networking
} }
var campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign; var campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
if (campaign != null && NetIdUtils.IdMoreRecent(campaign.LastUpdateID, c.LastRecvCampaignUpdate)) if (campaign != null && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode &&
NetIdUtils.IdMoreRecent(campaign.LastUpdateID, c.LastRecvCampaignUpdate))
{ {
outmsg.Write(true); outmsg.Write(true);
outmsg.WritePadBits(); outmsg.WritePadBits();
@@ -164,7 +164,7 @@ namespace Barotrauma.Networking
if (connectedClient != null) if (connectedClient != null)
{ {
Log("Disconnecting client " + connectedClient.Name + " (Steam ID: " + steamID + "). Steam authentication no longer valid (" + status + ").", ServerLog.MessageType.ServerMessage); Log("Disconnecting client " + connectedClient.Name + " (Steam ID: " + steamID + "). Steam authentication no longer valid (" + status + ").", ServerLog.MessageType.ServerMessage);
KickClient(connectedClient, $"DisconnectMessage.SteamAuthNoLongerValid_[status]={status.ToString()}"); KickClient(connectedClient, $"DisconnectMessage.SteamAuthNoLongerValid~[status]={status.ToString()}");
} }
}*/ }*/
} }
@@ -342,7 +342,7 @@ namespace Barotrauma.Networking
if (clVersion != GameMain.Version.ToString()) if (clVersion != GameMain.Version.ToString())
{ {
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidVersion, DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion_[version]={GameMain.Version.ToString()}_[clientversion]={clVersion}"); $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version.ToString()}~[clientversion]={clVersion}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", Color.Red); DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", Color.Red);
@@ -368,7 +368,7 @@ namespace Barotrauma.Networking
if (missingPackages.Count == 1) if (missingPackages.Count == 1)
{ {
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage_[missingcontentpackage]={GetPackageStr(missingPackages[0])}"); DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error);
return; return;
} }
@@ -376,7 +376,7 @@ namespace Barotrauma.Networking
{ {
List<string> packageStrs = new List<string>(); List<string> packageStrs = new List<string>();
missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp))); missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages_[missingcontentpackages]={string.Join(", ", packageStrs)}"); DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return; return;
} }
@@ -399,7 +399,7 @@ namespace Barotrauma.Networking
if (incompatiblePackages.Count == 1) if (incompatiblePackages.Count == 1)
{ {
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage, DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackage_[incompatiblecontentpackage]={GetPackageStr2(incompatiblePackages[0])}"); $"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr2(incompatiblePackages[0])}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content package " + GetPackageStr2(incompatiblePackages[0]) + ")", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content package " + GetPackageStr2(incompatiblePackages[0]) + ")", ServerLog.MessageType.Error);
return; return;
} }
@@ -408,7 +408,7 @@ namespace Barotrauma.Networking
List<string> packageStrs = new List<string>(); List<string> packageStrs = new List<string>();
incompatiblePackages.ForEach(cp => packageStrs.Add(GetPackageStr2(cp))); incompatiblePackages.ForEach(cp => packageStrs.Add(GetPackageStr2(cp)));
DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage, DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage,
$"DisconnectMessage.IncompatibleContentPackages_[incompatiblecontentpackages]={string.Join(", ", packageStrs)}"); $"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
return; return;
} }
@@ -494,13 +494,21 @@ namespace Barotrauma.Networking
} }
else else
{ {
newClient.SetPermissions(ClientPermissions.None, new List<DebugConsole.Command>()); var defaultPerms = PermissionPreset.List.Find(p => p.Name == "None");
if (defaultPerms != null)
{
newClient.SetPermissions(defaultPerms.Permissions, defaultPerms.PermittedCommands);
}
else
{
newClient.SetPermissions(ClientPermissions.None, new List<DebugConsole.Command>());
}
} }
} }
private void DisconnectUnauthClient(NetIncomingMessage inc, UnauthenticatedClient unauthClient, DisconnectReason reason, string message) private void DisconnectUnauthClient(NetIncomingMessage inc, UnauthenticatedClient unauthClient, DisconnectReason reason, string message)
{ {
inc.SenderConnection.Disconnect(reason.ToString() + "/ " + message); inc.SenderConnection.Disconnect(reason.ToString() + "/ " + TextManager.GetServerMessage(message));
if (unauthClient.SteamID > 0) { Steam.SteamManager.StopAuthSession(unauthClient.SteamID); } if (unauthClient.SteamID > 0) { Steam.SteamManager.StopAuthSession(unauthClient.SteamID); }
if (unauthClient != null) if (unauthClient != null)
{ {
@@ -228,7 +228,7 @@ namespace Barotrauma.Networking
GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected old event " GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected old event "
+ (c.LastRecvEntityEventID + 1).ToString() + + (c.LastRecvEntityEventID + 1).ToString() +
" (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" + " (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" +
" Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.ServerMessage); " Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent"); server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent");
} }
); );
@@ -242,7 +242,7 @@ namespace Barotrauma.Networking
toKick.ForEach(c => toKick.ForEach(c =>
{ {
DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red); DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.ServerMessage); GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error);
server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent"); server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent");
}); });
} }
@@ -251,7 +251,7 @@ namespace Barotrauma.Networking
var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut); var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);
foreach (Client timedOutClient in timedOutClients) foreach (Client timedOutClient in timedOutClients)
{ {
GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.ServerMessage); GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
GameMain.Server.DisconnectClient(timedOutClient, "", "ServerMessage.SyncTimeout"); GameMain.Server.DisconnectClient(timedOutClient, "", "ServerMessage.SyncTimeout");
} }
@@ -305,19 +305,7 @@ namespace Barotrauma.Networking
} }
//too many events for one packet //too many events for one packet
if (eventsToSync.Count > MaxEventsPerWrite) if (eventsToSync.Count > 200)
{
if (eventsToSync.Count > MaxEventsPerWrite * 3 && !client.NeedsMidRoundSync)
{
Color color = eventsToSync.Count > MaxEventsPerWrite * 20 ? Color.Red : Color.Orange;
if (eventsToSync.Count < MaxEventsPerWrite * 5) { color = Color.Yellow; }
DebugConsole.NewMessage("WARNING: event count very high: " + eventsToSync.Count + "/" + MaxEventsPerWrite, color);
}
eventsToSync.RemoveRange(MaxEventsPerWrite, eventsToSync.Count - MaxEventsPerWrite);
}
foreach (NetEntityEvent entityEvent in eventsToSync)
{ {
if (eventsToSync.Count > 200 && !client.NeedsMidRoundSync) if (eventsToSync.Count > 200 && !client.NeedsMidRoundSync)
{ {
@@ -350,7 +338,7 @@ namespace Barotrauma.Networking
msg.Write(client.UnreceivedEntityEventCount); msg.Write(client.UnreceivedEntityEventCount);
msg.Write(client.FirstNewEventID); msg.Write(client.FirstNewEventID);
Write(msg, eventsToSync, client); Write(msg, eventsToSync, out sentEvents, client);
} }
else else
{ {
@@ -387,10 +375,10 @@ namespace Barotrauma.Networking
for (int i = startIndex; i < eventList.Count; i++) for (int i = startIndex; i < eventList.Count; i++)
{ {
//find the first event that hasn't been sent in 1.5 * roundtriptime or at all //find the first event that hasn't been sent in roundtriptime or at all
client.EntityEventLastSent.TryGetValue(eventList[i].ID, out float lastSent); client.EntityEventLastSent.TryGetValue(eventList[i].ID, out double lastSent);
float minInterval = Math.Max(client.Connection.AverageRoundtripTime * 1.5f, (float)server.UpdateInterval.TotalSeconds * 2); float minInterval = Math.Max(client.Connection.AverageRoundtripTime, (float)server.UpdateInterval.TotalSeconds * 2);
if (lastSent > NetTime.Now - Math.Min(minInterval, 0.5f)) if (lastSent > NetTime.Now - Math.Min(minInterval, 0.5f))
{ {
@@ -416,7 +404,7 @@ namespace Barotrauma.Networking
} }
else else
{ {
double midRoundSyncTimeOut = uniqueEvents.Count / MaxEventsPerWrite * server.UpdateInterval.TotalSeconds; double midRoundSyncTimeOut = uniqueEvents.Count / 100 * server.UpdateInterval.TotalSeconds;
midRoundSyncTimeOut = Math.Max(10.0f, midRoundSyncTimeOut * 10.0f); midRoundSyncTimeOut = Math.Max(10.0f, midRoundSyncTimeOut * 10.0f);
client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count; client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count;
@@ -337,37 +337,56 @@ namespace Barotrauma.Networking
continue; continue;
} }
string permissionsStr = clientElement.GetAttributeString("permissions", "");
ClientPermissions permissions = Networking.ClientPermissions.None; ClientPermissions permissions = Networking.ClientPermissions.None;
if (permissionsStr.ToLowerInvariant() == "all") List<DebugConsole.Command> permittedCommands = new List<DebugConsole.Command>();
if (clientElement.Attribute("preset") == null)
{ {
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) string permissionsStr = clientElement.GetAttributeString("permissions", "");
if (permissionsStr.ToLowerInvariant() == "all")
{ {
permissions |= permission; foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
{
permissions |= permission;
}
}
else if (!Enum.TryParse(permissionsStr, out permissions))
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + permissionsStr + "\" is not a valid client permission.");
continue;
}
if (permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands))
{
foreach (XElement commandElement in clientElement.Elements())
{
if (commandElement.Name.ToString().ToLowerInvariant() != "command") continue;
string commandName = commandElement.GetAttributeString("name", "");
DebugConsole.Command command = DebugConsole.FindCommand(commandName);
if (command == null)
{
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + commandName + "\" is not a valid console command.");
continue;
}
permittedCommands.Add(command);
}
} }
} }
else if (!Enum.TryParse(permissionsStr, out permissions)) else
{ {
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + permissionsStr + "\" is not a valid client permission."); string presetName = clientElement.GetAttributeString("preset", "");
continue; PermissionPreset preset = PermissionPreset.List.Find(p => p.Name == presetName);
} if (preset == null)
List<DebugConsole.Command> permittedCommands = new List<DebugConsole.Command>();
if (permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands))
{
foreach (XElement commandElement in clientElement.Elements())
{ {
if (commandElement.Name.ToString().ToLowerInvariant() != "command") continue; DebugConsole.ThrowError("Failed to restore saved permissions to the client \"" + clientName + "\". Permission preset \"" + presetName + "\" not found.");
return;
string commandName = commandElement.GetAttributeString("name", ""); }
DebugConsole.Command command = DebugConsole.FindCommand(commandName); else
if (command == null) {
{ permissions = preset.Permissions;
DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + commandName + "\" is not a valid console command."); permittedCommands = preset.PermittedCommands.ToList();
continue;
}
permittedCommands.Add(command);
} }
} }
@@ -440,9 +459,14 @@ namespace Barotrauma.Networking
foreach (SavedClientPermission clientPermission in ClientPermissions) foreach (SavedClientPermission clientPermission in ClientPermissions)
{ {
var matchingPreset = PermissionPreset.List.Find(p => p.MatchesPermissions(clientPermission.Permissions, clientPermission.PermittedCommands));
if (matchingPreset != null && matchingPreset.Name == "None")
{
continue;
}
XElement clientElement = new XElement("Client", XElement clientElement = new XElement("Client",
new XAttribute("name", clientPermission.Name), new XAttribute("name", clientPermission.Name));
new XAttribute("permissions", clientPermission.Permissions.ToString()));
if (clientPermission.SteamID > 0) if (clientPermission.SteamID > 0)
{ {
@@ -453,14 +477,21 @@ namespace Barotrauma.Networking
clientElement.Add(new XAttribute("ip", clientPermission.IP)); clientElement.Add(new XAttribute("ip", clientPermission.IP));
} }
if (clientPermission.Permissions.HasFlag(Barotrauma.Networking.ClientPermissions.ConsoleCommands)) if (matchingPreset == null)
{ {
foreach (DebugConsole.Command command in clientPermission.PermittedCommands) clientElement.Add(new XAttribute("permissions", clientPermission.Permissions.ToString()));
if (clientPermission.Permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands))
{ {
clientElement.Add(new XElement("command", new XAttribute("name", command.names[0]))); foreach (DebugConsole.Command command in clientPermission.PermittedCommands)
{
clientElement.Add(new XElement("command", new XAttribute("name", command.names[0])));
}
} }
} }
else
{
clientElement.Add(new XAttribute("preset", matchingPreset.Name));
}
doc.Root.Add(clientElement); doc.Root.Add(clientElement);
} }
@@ -56,6 +56,7 @@
<Character file="Content/Characters/Husk/Husk.xml" /> <Character file="Content/Characters/Husk/Husk.xml" />
<Character file="Content/Characters/Humanhusk/Humanhusk.xml" /> <Character file="Content/Characters/Humanhusk/Humanhusk.xml" />
<Character file="Content/Characters/Legacyhusk/Legacyhusk.xml" /> <Character file="Content/Characters/Legacyhusk/Legacyhusk.xml" />
<Character file="Content/Characters/Legacycharybdis/Legacycharybdis.xml" />
<Character file="Content/Characters/Legacycrawler/Legacycrawler.xml" /> <Character file="Content/Characters/Legacycrawler/Legacycrawler.xml" />
<Character file="Content/Characters/Legacymoloch/Legacymoloch.xml" /> <Character file="Content/Characters/Legacymoloch/Legacymoloch.xml" />
<Character file="Content/Characters/Mudraptor/Mudraptor.xml" /> <Character file="Content/Characters/Mudraptor/Mudraptor.xml" />
@@ -70,10 +71,11 @@
<Outpost file="Content/Map/Outposts/Outpost.sub" /> <Outpost file="Content/Map/Outposts/Outpost.sub" />
<Submarine file="Submarines/Orca.sub" /> <Submarine file="Submarines/Orca.sub" />
<Submarine file="Submarines/Typhon.sub" /> <Submarine file="Submarines/Typhon.sub" />
<Submarine file="Submarines/Muikku.sub" /> <Submarine file="Submarines/Selkie.sub" />
<Submarine file="Submarines/Bunyip.sub" /> <Submarine file="Submarines/Bunyip.sub" />
<Submarine file="Submarines/Humpback.sub" /> <Submarine file="Submarines/Humpback.sub" />
<Submarine file="Submarines/Dugong.sub" /> <Submarine file="Submarines/Dugong.sub" />
<Submarine file="Submarines/PAX.sub" />
<Submarine file="Submarines/Remora.sub" /> <Submarine file="Submarines/Remora.sub" />
<Submarine file="Submarines/RemoraDrone.sub" /> <Submarine file="Submarines/RemoraDrone.sub" />
<Text file="Content/Texts/EnglishVanilla.xml" /> <Text file="Content/Texts/EnglishVanilla.xml" />
@@ -13,6 +13,13 @@
<Command name="autorestart"/> <Command name="autorestart"/>
<Command name="autorestartinterval"/> <Command name="autorestartinterval"/>
<Command name="autorestarttimer"/> <Command name="autorestarttimer"/>
<Command name="botcount"/>
<Command name="botspawnmode"/>
<Command name="gamemode"/>
<Command name="sub"/>
<Command name="shuttle"/>
<Command name="water"/>
<Command name="fire"/>
<Command name="difficulty"/> <Command name="difficulty"/>
<Command name="kick"/> <Command name="kick"/>
<Command name="kickid"/> <Command name="kickid"/>
@@ -28,6 +35,8 @@
<Command name="autorestart"/> <Command name="autorestart"/>
<Command name="autorestartinterval"/> <Command name="autorestartinterval"/>
<Command name="autorestarttimer"/> <Command name="autorestarttimer"/>
<Command name="botcount"/>
<Command name="botspawnmode"/>
<Command name="difficulty"/> <Command name="difficulty"/>
<Command name="kick"/> <Command name="kick"/>
<Command name="kickid"/> <Command name="kickid"/>
@@ -10,7 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Animations\" /> <Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Animations\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\" /> <Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Animations\" /> <Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Animations\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycrawler\" /> <Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycrawler\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Endworm\Animations\" /> <Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Endworm\Animations\" />
@@ -45,12 +45,27 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Ragdolls\CarrierDefaultRagdoll.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Ragdolls\CarrierDefaultRagdoll.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimFast.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimSlow.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Charybdis.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Charybdis.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Charybdis.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Ragdolls\CharybdisDefaultRagdoll.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Ragdolls\CharybdisDefaultRagdoll.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Legacycharybdis.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Ragdolls\LegacycharybdisDefaultRagdoll.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Coelanth.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Coelanth.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -342,6 +357,36 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Effects\waterbump.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Effects\waterbump.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Tutorials\ContextualTutorial\1_CommandReactor\BaroTutorial_CommandReactor.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Tutorials\ContextualTutorial\2_NavConsole\BaroTutorial_NavConsole.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Tutorials\ContextualTutorial\3_Flood\BaroTutorial_Flood.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Tutorials\ContextualTutorial\4_Reactor\BaroTutorial_Reactor.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Tutorials\ContextualTutorial\5_EnemyOnSonar\BaroTutorial_EnemyOnSonar.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Tutorials\ContextualTutorial\6_Degrading2\BaroTutorial_Degrading.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Tutorials\ContextualTutorial\7_Medical\BaroTutorial_Medical.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Tutorials\ContextualTutorial\8_Approach1\BaroTutorial_Approach.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\UI\tutorialAtlas.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="$(MSBuildThisFileDirectory)Content\Fonts\BebasNeue-Regular.otf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="$(MSBuildThisFileDirectory)Content\Fonts\NotoSans\LICENSE_OFL.txt"> <Content Include="$(MSBuildThisFileDirectory)Content\Fonts\NotoSans\LICENSE_OFL.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -897,13 +942,13 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Animations\CarrierSwimSlow.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Animations\CarrierSwimSlow.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\charybdis.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\charybdis.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimFast.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Animations\LegacycharybdisSwimFast.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimSlow.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Animations\LegacycharybdisSwimSlow.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Animations\CoelanthSwimFast.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Animations\CoelanthSwimFast.xml">
@@ -1955,6 +2000,9 @@
<Content Include="$(MSBuildThisFileDirectory)serversettings.xml"> <Content Include="$(MSBuildThisFileDirectory)serversettings.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Misc\GuitarClown.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\CARRIER_alarmLoop.ogg"> <None Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\CARRIER_alarmLoop.ogg">
@@ -2356,7 +2404,7 @@
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Mudraptor\MUDRAPTOR_idle3.ogg"> <None Include="$(MSBuildThisFileDirectory)Content\Characters\Mudraptor\MUDRAPTOR_idle3.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\charybdisattack.ogg"> <None Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\charybdisattack.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\attack1.ogg"> <None Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\attack1.ogg">
@@ -3114,7 +3162,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Muikku.sub"> <None Include="$(MSBuildThisFileDirectory)Submarines\Muikku.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Orca.sub"> <None Include="$(MSBuildThisFileDirectory)Submarines\Orca.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -3134,6 +3182,9 @@
<None Include="$(MSBuildThisFileDirectory)Submarines\RemoraDrone.sub"> <None Include="$(MSBuildThisFileDirectory)Submarines\RemoraDrone.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Selkie.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Typhon.sub"> <None Include="$(MSBuildThisFileDirectory)Submarines\Typhon.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@@ -115,7 +115,7 @@ namespace Barotrauma
SonarLabel = element.GetAttributeString("sonarlabel", ""); SonarLabel = element.GetAttributeString("sonarlabel", "");
} }
public AITarget(Entity e) public AITarget(Entity e, float sightRange = -1, float soundRange = 0)
{ {
Entity = e; Entity = e;
if (sightRange < 0) if (sightRange < 0)
File diff suppressed because it is too large Load Diff
@@ -193,7 +193,7 @@ namespace Barotrauma
// is not attached or is attached to something else // is not attached or is attached to something else
if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody) if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody)
{ {
if (Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(transformedAttachPos), enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.Range * enemyAI.AttackingLimb.attack.Range) if (Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(transformedAttachPos), enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.DamageRange * enemyAI.AttackingLimb.attack.DamageRange)
{ {
AttachToBody(character.AnimController.Collider, attachLimb, attachTargetBody, transformedAttachPos); AttachToBody(character.AnimController.Collider, attachLimb, attachTargetBody, transformedAttachPos);
} }
@@ -115,7 +115,8 @@ namespace Barotrauma
unreachable.Add(goToObjective.Target as Hull); unreachable.Add(goToObjective.Target as Hull);
} }
goToObjective = null; goToObjective = null;
SteeringManager.SteeringWander(); HumanAIController.ObjectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
//SteeringManager.SteeringWander();
} }
} }
else if (currentHull != null) else if (currentHull != null)
@@ -99,7 +99,8 @@ namespace Barotrauma
FindTargetItem(); FindTargetItem();
if (targetItem == null || moveToTarget == null) if (targetItem == null || moveToTarget == null)
{ {
SteeringManager.SteeringWander(); HumanAIController.ObjectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
//SteeringManager.SteeringWander();
return; return;
} }
@@ -98,63 +98,67 @@ namespace Barotrauma
PathSteering.Reset(); PathSteering.Reset();
return; return;
} }
if (standStillTimer < -walkDuration) if (standStillTimer < -walkDuration)
{ {
standStillTimer = Rand.Range(standStillMin, standStillMax); standStillTimer = Rand.Range(standStillMin, standStillMax);
} }
//steer away from edges of the hull
if (character.AnimController.CurrentHull != null && !character.IsClimbing)
{
standStillTimer = Rand.Range(standStillMin, standStillMax);
}
Wander(deltaTime); Wander(deltaTime);
return; return;
} }
if (leftDist < WallAvoidDistance && rightDist < WallAvoidDistance)
{
if (Math.Abs(rightDist - leftDist) > WallAvoidDistance / 2)
{
PathSteering.SteeringManual(deltaTime, Vector2.UnitX * Math.Sign(rightDist - leftDist));
}
else
{
PathSteering.Reset();
return;
}
}
else if (leftDist < WallAvoidDistance)
{
PathSteering.SteeringManual(deltaTime, Vector2.UnitX * (WallAvoidDistance-leftDist) / WallAvoidDistance);
PathSteering.WanderAngle = 0.0f;
return;
}
else
{
PathSteering.SteeringManual(deltaTime, -Vector2.UnitX * (WallAvoidDistance-rightDist) / WallAvoidDistance);
PathSteering.WanderAngle = MathHelper.Pi;
return;
}
}
character.AIController.SteeringManager.SteeringWander();
if (!character.IsClimbing && !character.AnimController.InWater)
{
//reset vertical steering to prevent dropping down from platforms etc
character.AIController.SteeringManager.ResetY();
}
return;
}
if (currentTarget != null) if (currentTarget != null)
{ {
character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition); character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition);
} }
} }
public void Wander(float deltaTime)
{
//steer away from edges of the hull
if (character.AnimController.CurrentHull != null && !character.IsClimbing)
{
float leftDist = character.Position.X - character.AnimController.CurrentHull.Rect.X;
float rightDist = character.AnimController.CurrentHull.Rect.Right - character.Position.X;
if (leftDist < WallAvoidDistance && rightDist < WallAvoidDistance)
{
if (Math.Abs(rightDist - leftDist) > WallAvoidDistance / 2)
{
PathSteering.SteeringManual(deltaTime, Vector2.UnitX * Math.Sign(rightDist - leftDist));
}
else
{
PathSteering.Reset();
}
}
else if (leftDist < WallAvoidDistance)
{
//PathSteering.SteeringManual(deltaTime, Vector2.UnitX * (WallAvoidDistance - leftDist) / WallAvoidDistance);
PathSteering.SteeringManual(deltaTime, Vector2.UnitX);
PathSteering.WanderAngle = 0.0f;
}
else if (rightDist < WallAvoidDistance)
{
//PathSteering.SteeringManual(deltaTime, -Vector2.UnitX * (WallAvoidDistance - rightDist) / WallAvoidDistance);
PathSteering.SteeringManual(deltaTime, -Vector2.UnitX);
PathSteering.WanderAngle = MathHelper.Pi;
}
else
{
SteeringManager.SteeringWander();
}
}
else
{
SteeringManager.SteeringWander();
}
if (!character.IsClimbing && !character.AnimController.InWater)
{
//reset vertical steering to prevent dropping down from platforms etc
character.AIController.SteeringManager.ResetY();
}
}
private readonly List<Hull> targetHulls = new List<Hull>(20); private readonly List<Hull> targetHulls = new List<Hull>(20);
private readonly List<float> hullWeights = new List<float>(20); private readonly List<float> hullWeights = new List<float>(20);
@@ -369,9 +369,18 @@ namespace Barotrauma
return; return;
} }
float movementAngle = MathUtils.VectorToAngle(movement) - MathHelper.PiOver2; Vector2 transformedMovement = reverse ? -movement : movement;
float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
float mainLimbAngle = (MainLimb.type == LimbType.Torso ? TorsoAngle.Value : HeadAngle.Value) * Dir; float mainLimbAngle = 0;
if (MainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
{
mainLimbAngle = TorsoAngle.Value;
}
else if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
{
mainLimbAngle = HeadAngle.Value;
}
mainLimbAngle *= Dir;
while (MainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi) while (MainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
{ {
movementAngle += MathHelper.TwoPi; movementAngle += MathHelper.TwoPi;
@@ -379,7 +388,7 @@ namespace Barotrauma
while (MainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi) while (MainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
{ {
movementAngle -= MathHelper.TwoPi; movementAngle -= MathHelper.TwoPi;
} }
if (CurrentSwimParams.RotateTowardsMovement) if (CurrentSwimParams.RotateTowardsMovement)
{ {
@@ -403,7 +412,6 @@ namespace Barotrauma
if (TailAngle.HasValue) if (TailAngle.HasValue)
{ {
Limb tail = GetLimb(LimbType.Tail); Limb tail = GetLimb(LimbType.Tail);
//tail?.body.SmoothRotate(movementAngle + TailAngle.Value * Dir, TailTorque);
if (tail != null) if (tail != null)
{ {
SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, MainLimb, TailTorque); SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, MainLimb, TailTorque);
@@ -413,6 +421,10 @@ namespace Barotrauma
else else
{ {
movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2; movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
if (reverse)
{
movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
}
if (MainLimb.type == LimbType.Head && HeadAngle.HasValue) if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
{ {
Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque); Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque);
@@ -442,7 +454,7 @@ namespace Barotrauma
var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude); var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude);
if (waveLength > 0 && waveAmplitude > 0) if (waveLength > 0 && waveAmplitude > 0)
{ {
WalkPos -= movement.Length() / Math.Abs(waveLength); WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
WalkPos = MathUtils.WrapAngleTwoPi(WalkPos); WalkPos = MathUtils.WrapAngleTwoPi(WalkPos);
} }
@@ -685,12 +697,6 @@ namespace Barotrauma
limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength); limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength);
} }
while (referenceLimb.Rotation - angle < -MathHelper.TwoPi)
{
angle -= MathHelper.TwoPi;
}
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
} }
private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque) private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque)
@@ -99,7 +99,7 @@ namespace Barotrauma
public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Animations/"; public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Animations/";
public static string GetDefaultFile(string speciesName, AnimationType animType) => $"{GetFolder(speciesName)}{GetDefaultFileName(speciesName, animType)}.xml"; public static string GetDefaultFile(string speciesName, AnimationType animType) => $"{GetFolder(speciesName)}{GetDefaultFileName(speciesName, animType)}.xml";
protected static string GetFolder(string speciesName) public static string GetFolder(string speciesName)
{ {
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty); var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default") if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
@@ -79,7 +79,7 @@ namespace Barotrauma
new XAttribute("sourcerect", $"0, 0, 1, 1"))) new XAttribute("sourcerect", $"0, 0, 1, 1")))
}; };
protected static string GetFolder(string speciesName) public static string GetFolder(string speciesName)
{ {
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty); var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default") if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
@@ -1035,8 +1035,6 @@ namespace Barotrauma
CheckValidity(); CheckValidity();
CheckValidity();
UpdateNetPlayerPosition(deltaTime); UpdateNetPlayerPosition(deltaTime);
CheckDistFromCollider(); CheckDistFromCollider();
UpdateCollisionCategories(); UpdateCollisionCategories();
@@ -1297,17 +1295,42 @@ namespace Barotrauma
UpdateProjSpecific(deltaTime); UpdateProjSpecific(deltaTime);
} }
private void CheckValidity() public bool Invalid { get; private set; }
private int validityResets;
private bool CheckValidity()
{ {
CheckValidity(Collider); bool isColliderValid = CheckValidity(Collider);
bool limbsValid = true;
foreach (Limb limb in limbs) foreach (Limb limb in limbs)
{ {
if (limb.body == null || !limb.body.Enabled) { continue; } if (limb.body == null || !limb.body.Enabled) { continue; }
CheckValidity(limb.body); if (!CheckValidity(limb.body))
{
limbsValid = false;
break;
}
} }
bool isValid = isColliderValid && limbsValid;
if (!isValid)
{
validityResets++;
if (validityResets > 1)
{
Invalid = true;
DebugConsole.ThrowError("Invalid ragdoll physics. Ragdoll freezed to prevent crashes.");
Collider.SetTransform(Vector2.Zero, 0.0f);
foreach (Limb limb in Limbs)
{
limb.body.SetTransform(Collider.SimPosition, 0.0f);
limb.body.ResetDynamics();
}
Frozen = true;
}
}
return isValid;
} }
private void CheckValidity(PhysicsBody body) private bool CheckValidity(PhysicsBody body)
{ {
string errorMsg = null; string errorMsg = null;
string bodyName = body.UserData is Limb ? "Limb" : "Collider"; string bodyName = body.UserData is Limb ? "Limb" : "Collider";
@@ -1359,7 +1382,7 @@ namespace Barotrauma
limb.body.ResetDynamics(); limb.body.ResetDynamics();
} }
SetInitialLimbPositions(); SetInitialLimbPositions();
return; return false;
} }
return true; return true;
} }
@@ -28,6 +28,7 @@ namespace Barotrauma
public enum AIBehaviorAfterAttack public enum AIBehaviorAfterAttack
{ {
FallBack, FallBack,
FallBackUntilCanAttack,
PursueIfCanAttack, PursueIfCanAttack,
Pursue Pursue
} }
@@ -81,6 +82,9 @@ namespace Barotrauma
[Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")] [Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")]
public AIBehaviorAfterAttack AfterAttack { get; private set; } public AIBehaviorAfterAttack AfterAttack { get; private set; }
[Serialize(false, true), Editable(ToolTip = "Should the ai try to reverse when aiming with this attack?")]
public bool Reverse { get; private set; }
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target before the AI tries to attack.")] [Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target before the AI tries to attack.")]
public float Range { get; private set; } public float Range { get; private set; }
@@ -96,6 +100,9 @@ namespace Barotrauma
[Serialize(0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "Used as the attack cooldown between different kind of attacks. Does not have effect, if set to 0.")] [Serialize(0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "Used as the attack cooldown between different kind of attacks. Does not have effect, if set to 0.")]
public float SecondaryCoolDown { get; private set; } = 0; public float SecondaryCoolDown { get; private set; } = 0;
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2, ToolTip = "Random factor applied to all cooldowns. Example: 0.1 -> adds a random value between -10% and 10% of the cooldown. Min 0 (default), Max 1 (could disable or double the cooldown in extreme cases).")]
public float CoolDownRandomFactor { get; private set; } = 0;
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)] [Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)]
public float StructureDamage { get; private set; } public float StructureDamage { get; private set; }
@@ -182,7 +189,7 @@ namespace Barotrauma
public readonly List<Affliction> Afflictions = new List<Affliction>(); public readonly List<Affliction> Afflictions = new List<Affliction>();
/// <summary> /// <summary>
/// Only affects ai decision making. /// Only affects ai decision making. All the conditionals has to be met in order to select the attack. TODO: allow to define conditionals using any (implemented in StatusEffect -> move from there to PropertyConditional?)
/// </summary> /// </summary>
public List<PropertyConditional> Conditionals { get; private set; } = new List<PropertyConditional>(); public List<PropertyConditional> Conditionals { get; private set; } = new List<PropertyConditional>();
@@ -444,8 +451,10 @@ namespace Barotrauma
public void SetCoolDown() public void SetCoolDown()
{ {
CoolDownTimer = CoolDown; float randomFraction = CoolDown * CoolDownRandomFactor;
SecondaryCoolDownTimer = SecondaryCoolDown; CoolDownTimer = CoolDown + MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value(Rand.RandSync.Server));
randomFraction = SecondaryCoolDown * CoolDownRandomFactor;
SecondaryCoolDownTimer = SecondaryCoolDown + MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value(Rand.RandSync.Server));
} }
public void ResetCoolDown() public void ResetCoolDown()
@@ -810,6 +810,7 @@ namespace Barotrauma
public void LoadHeadAttachments() public void LoadHeadAttachments()
{ {
if (Info == null) { return; }
if (AnimController == null) { return; } if (AnimController == null) { return; }
var head = AnimController.GetLimb(LimbType.Head); var head = AnimController.GetLimb(LimbType.Head);
if (head == null) { return; } if (head == null) { return; }
@@ -1112,13 +1113,15 @@ namespace Barotrauma
ViewTarget = null; ViewTarget = null;
if (!AllowInput) return; if (!AllowInput) return;
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition; if (Controlled == this || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer))
if (Controlled == this)
{ {
SmoothedCursorPosition = cursorPosition; SmoothedCursorPosition = cursorPosition;
} }
else else
{ {
//apply some smoothing to the cursor positions of remote players when playing as a client
//to make aiming look a little less choppy
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition;
smoothedCursorDiff = NetConfig.InterpolateCursorPositionError(smoothedCursorDiff); smoothedCursorDiff = NetConfig.InterpolateCursorPositionError(smoothedCursorDiff);
SmoothedCursorPosition = cursorPosition - smoothedCursorDiff; SmoothedCursorPosition = cursorPosition - smoothedCursorDiff;
} }
@@ -1185,6 +1188,10 @@ namespace Barotrauma
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.F)) if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.F))
{ {
AnimController.ReleaseStuckLimbs(); AnimController.ReleaseStuckLimbs();
if (AIController != null && AIController is EnemyAIController enemyAI)
{
enemyAI.LatchOntoAI?.DeattachFromBody();
}
} }
#endif #endif
@@ -1264,15 +1271,7 @@ namespace Barotrauma
if (IsKeyDown(InputType.Aim) && selectedItems[i] != null) selectedItems[i].SecondaryUse(deltaTime, this); if (IsKeyDown(InputType.Aim) && selectedItems[i] != null) selectedItems[i].SecondaryUse(deltaTime, this);
} }
} }
#if CLIENT
if (SelectedConstruction != null && SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)))
{
//emulate a Select input to get the character to deselect the item server-side
keys[(int)InputType.Select].Hit = true;
SelectedConstruction = null;
}
#endif
if (SelectedConstruction != null) if (SelectedConstruction != null)
{ {
if (IsKeyDown(InputType.Use)) SelectedConstruction.Use(deltaTime, this); if (IsKeyDown(InputType.Use)) SelectedConstruction.Use(deltaTime, this);
@@ -1650,7 +1649,8 @@ namespace Barotrauma
#if CLIENT #if CLIENT
if (isLocalPlayer) if (isLocalPlayer)
{ {
if (GUI.MouseOn == null && !CharacterInventory.IsMouseOnInventory()) if (GUI.MouseOn == null &&
(!CharacterInventory.IsMouseOnInventory() || CharacterInventory.DraggingItemToWorld))
{ {
if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen) if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen)
{ {
@@ -1665,10 +1665,12 @@ namespace Barotrauma
focusedItem = null; focusedItem = null;
} }
findFocusedTimer -= deltaTime; findFocusedTimer -= deltaTime;
} }
#endif #endif
//climb ladders automatically when pressing up/down inside their trigger area //climb ladders automatically when pressing up/down inside their trigger area
if (SelectedConstruction == null && !AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen) Ladder currentLadder = SelectedConstruction?.GetComponent<Ladder>();
if ((SelectedConstruction == null || currentLadder != null) &&
!AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen)
{ {
bool climbInput = IsKeyDown(InputType.Up) || IsKeyDown(InputType.Down); bool climbInput = IsKeyDown(InputType.Up) || IsKeyDown(InputType.Down);
bool isControlled = Controlled == this; bool isControlled = Controlled == this;
@@ -1679,6 +1681,19 @@ namespace Barotrauma
float minDist = float.PositiveInfinity; float minDist = float.PositiveInfinity;
foreach (Ladder ladder in Ladder.List) foreach (Ladder ladder in Ladder.List)
{ {
if (ladder == currentLadder)
{
continue;
}
else if (currentLadder != null)
{
//only switch from ladder to another if the ladders are above the current ladders and pressing up, or vice versa
if (ladder.Item.WorldPosition.Y > currentLadder.Item.WorldPosition.Y != IsKeyDown(InputType.Up))
{
continue;
}
}
if (CanInteractWith(ladder.Item, out float dist, checkLinked: false) && dist < minDist) if (CanInteractWith(ladder.Item, out float dist, checkLinked: false) && dist < minDist)
{ {
minDist = dist; minDist = dist;
@@ -1695,7 +1710,7 @@ namespace Barotrauma
} }
} }
if (SelectedCharacter != null && IsKeyHit(InputType.Grab)) //Let people use ladders and buttons and stuff when dragging chars if (SelectedCharacter != null && (IsKeyHit(InputType.Grab) || IsKeyHit(InputType.Health))) //Let people use ladders and buttons and stuff when dragging chars
{ {
DeselectCharacter(); DeselectCharacter();
} }
@@ -804,6 +804,12 @@ namespace Barotrauma
var newItem = Item.Load(itemElement, inventory.Owner.Submarine, createNetworkEvent: true); var newItem = Item.Load(itemElement, inventory.Owner.Submarine, createNetworkEvent: true);
if (newItem == null) { continue; } if (newItem == null) { continue; }
if (!MathUtils.NearlyEqual(newItem.Condition, newItem.MaxCondition) &&
GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.NetworkMember.CreateEntityEvent(newItem, new object[] { NetEntityEvent.Type.Status });
}
int[] slotIndices = itemElement.GetAttributeIntArray("i", new int[] { 0 }); int[] slotIndices = itemElement.GetAttributeIntArray("i", new int[] { 0 });
if (!slotIndices.Any()) if (!slotIndices.Any())
{ {
@@ -153,7 +153,10 @@ namespace Barotrauma
//how high the strength has to be for the affliction icon to be shown in the UI //how high the strength has to be for the affliction icon to be shown in the UI
public readonly float ShowIconThreshold = 0.05f; public readonly float ShowIconThreshold = 0.05f;
public readonly float MaxStrength = 100.0f; public readonly float MaxStrength = 100.0f;
//how high the strength has to be for the affliction icon to be shown with a health scanner
public readonly float ShowInHealthScannerThreshold = 0.05f;
public float BurnOverlayAlpha; public float BurnOverlayAlpha;
public float DamageOverlayAlpha; public float DamageOverlayAlpha;
@@ -257,6 +260,8 @@ namespace Barotrauma
ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f)); ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f));
MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f); MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f);
ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f));
DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f); DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f);
BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f); BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f);
@@ -460,6 +460,7 @@ namespace Barotrauma
{ {
affliction.Strength = 0.0f; affliction.Strength = 0.0f;
} }
CalculateVitality();
} }
private void AddLimbAffliction(Limb limb, Affliction newAffliction) private void AddLimbAffliction(Limb limb, Affliction newAffliction)
@@ -520,14 +520,15 @@ namespace Barotrauma
// Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example. // Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example.
wasHit = true; wasHit = true;
} }
else if (damageTarget is Structure && structureBody?.UserData is Structure) else if (damageTarget is Structure wall && structureBody != null &&
(structureBody.UserData is Structure || (structureBody.UserData is Submarine sub && sub == wall.Submarine)))
{ {
// If the attack is aimed to a structure and hits a structure, it's successful // If the attack is aimed to a structure (wall) and hits a structure or the sub, it's successful
wasHit = true; wasHit = true;
} }
else else
{ {
// If the attack is aimed to a character but hits a structure, the hit is blocked. // If there is nothing between, the hit is successful
wasHit = structureBody == null; wasHit = structureBody == null;
} }
} }
@@ -103,12 +103,6 @@ namespace Barotrauma
if (missionType == MissionType.Random) if (missionType == MissionType.Random)
{ {
allowedMissions.AddRange(MissionPrefab.List); allowedMissions.AddRange(MissionPrefab.List);
#if SERVER
if (GameMain.Server != null)
{
allowedMissions.RemoveAll(mission => !GameMain.Server.ServerSettings.AllowedRandomMissionTypes.Contains(mission.type));
}
#endif
} }
else if (missionType == MissionType.None) else if (missionType == MissionType.None)
{ {
@@ -124,6 +118,11 @@ namespace Barotrauma
{ {
allowedMissions.RemoveAll(m => !m.IsAllowed(locations[0], locations[1])); allowedMissions.RemoveAll(m => !m.IsAllowed(locations[0], locations[1]));
} }
if (allowedMissions.Count == 0)
{
return null;
}
int probabilitySum = allowedMissions.Sum(m => m.Commonness); int probabilitySum = allowedMissions.Sum(m => m.Commonness);
int randomNumber = rand.NextInt32() % probabilitySum; int randomNumber = rand.NextInt32() % probabilitySum;
@@ -139,7 +139,7 @@ namespace Barotrauma
MTRandom rand = new MTRandom(ToolBox.StringToInt(seed)); MTRandom rand = new MTRandom(ToolBox.StringToInt(seed));
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), null); dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), null, rand);
} }
} }
@@ -208,7 +208,7 @@ namespace Barotrauma
DockingPort myPort = null, outPostPort = null; DockingPort myPort = null, outPostPort = null;
foreach (DockingPort port in DockingPort.List) foreach (DockingPort port in DockingPort.List)
{ {
if (port.IsHorizontal) { continue; } if (port.IsHorizontal || port.Docked) { continue; }
if (port.Item.Submarine == level.StartOutpost) if (port.Item.Submarine == level.StartOutpost)
{ {
outPostPort = port; outPostPort = port;
@@ -880,8 +880,7 @@ namespace Barotrauma.Items.Components
List<MapEntity> linked = new List<MapEntity>(item.linkedTo); List<MapEntity> linked = new List<MapEntity>(item.linkedTo);
foreach (MapEntity entity in linked) foreach (MapEntity entity in linked)
{ {
Item linkedItem = entity as Item; if (!(entity is Item linkedItem)) { continue; }
if (linkedItem == null) { continue; }
var dockingPort = linkedItem.GetComponent<DockingPort>(); var dockingPort = linkedItem.GetComponent<DockingPort>();
if (dockingPort != null) if (dockingPort != null)
@@ -426,11 +426,11 @@ namespace Barotrauma.Items.Components
public override bool Use(float deltaTime, Character character = null) public override bool Use(float deltaTime, Character character = null)
{ {
if (!attachable || item.body == null) return (character == null || character.IsKeyDown(InputType.Aim)); if (!attachable || item.body == null) { return character == null || character.IsKeyDown(InputType.Aim); }
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()) { return false; }
#if SERVER #if SERVER
if (GameMain.Server != null) if (GameMain.Server != null)
{ {
@@ -438,7 +438,12 @@ namespace Barotrauma.Items.Components
GameServer.Log(character.LogName + " attached " + item.Name + " to a wall", ServerLog.MessageType.ItemInteraction); GameServer.Log(character.LogName + " attached " + item.Name + " to a wall", ServerLog.MessageType.ItemInteraction);
} }
#endif #endif
item.Drop(character); }
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
if (character != null) { item.Drop(character); }
AttachToWall();
} }
return true; return true;
@@ -550,7 +555,7 @@ namespace Barotrauma.Items.Components
public override void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) public override void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{ {
base.ServerWrite(msg, c, extraData); base.ServerWrite(msg, c, extraData);
if (!attachable || body == null) return; if (!attachable || body == null) { return; }
msg.Write(Attached); msg.Write(Attached);
msg.Write(body.SimPosition.X); msg.Write(body.SimPosition.X);
@@ -2,12 +2,15 @@
using Lidgren.Network; using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
partial class LevelResource : ItemComponent, IServerSerializable partial class LevelResource : ItemComponent, IServerSerializable
{ {
private float lastSentDeattachTimer;
[Serialize(1.0f, false)] [Serialize(1.0f, false)]
public float DeattachDuration public float DeattachDuration
{ {
@@ -21,12 +24,29 @@ namespace Barotrauma.Items.Components
get { return deattachTimer; } get { return deattachTimer; }
set set
{ {
deattachTimer = Math.Max(0.0f, value);
//clients don't deattach the item until the server says so (handled in ClientRead) //clients don't deattach the item until the server says so (handled in ClientRead)
if ((GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) && deattachTimer >= DeattachDuration) if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
return;
}
deattachTimer = Math.Max(0.0f, value);
#if SERVER
if (deattachTimer >= DeattachDuration)
{
if (holdable.Attached){ item.CreateServerEvent(this); }
holdable.DeattachFromWall();
}
else if (Math.Abs(lastSentDeattachTimer - deattachTimer) > 0.1f)
{
item.CreateServerEvent(this);
lastSentDeattachTimer = deattachTimer;
}
#else
if (deattachTimer >= DeattachDuration)
{ {
holdable.DeattachFromWall(); holdable.DeattachFromWall();
} }
#endif
} }
} }
@@ -67,7 +87,10 @@ namespace Barotrauma.Items.Components
return; return;
} }
holdable.Reattachable = false; holdable.Reattachable = false;
holdable.PickingTime = float.MaxValue; if (requiredItems.Any())
{
holdable.PickingTime = float.MaxValue;
}
var body = item.body ?? holdable.Body; var body = item.body ?? holdable.Body;
@@ -17,6 +17,8 @@ namespace Barotrauma.Items.Components
private readonly List<string> fixableEntities; private readonly List<string> fixableEntities;
private Vector2 pickedPosition; private Vector2 pickedPosition;
private float activeTimer; private float activeTimer;
private Vector2 debugRayStartPos, debugRayEndPos;
[Serialize(0.0f, false)] [Serialize(0.0f, false)]
public float Range { get; set; } public float Range { get; set; }
@@ -156,7 +158,7 @@ namespace Barotrauma.Items.Components
var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair; var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair;
if (RepairThroughWalls) if (RepairThroughWalls)
{ {
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false); var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true);
foreach (Body body in bodies) foreach (Body body in bodies)
{ {
FixBody(user, deltaTime, degreeOfSuccess, body); FixBody(user, deltaTime, degreeOfSuccess, body);
@@ -164,7 +166,7 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
FixBody(user, deltaTime, degreeOfSuccess, Submarine.PickBody(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false)); FixBody(user, deltaTime, degreeOfSuccess, Submarine.PickBody(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true));
} }
if (ExtinguishAmount > 0.0f && item.CurrentHull != null) if (ExtinguishAmount > 0.0f && item.CurrentHull != null)
@@ -247,6 +249,7 @@ namespace Barotrauma.Items.Components
var levelResource = targetItem.GetComponent<LevelResource>(); var levelResource = targetItem.GetComponent<LevelResource>();
if (levelResource != null && levelResource.IsActive && if (levelResource != null && levelResource.IsActive &&
levelResource.requiredItems.Any() &&
levelResource.HasRequiredItems(user, addMessage: false)) levelResource.HasRequiredItems(user, addMessage: false))
{ {
levelResource.DeattachTimer += deltaTime; levelResource.DeattachTimer += deltaTime;
@@ -104,8 +104,8 @@ namespace Barotrauma.Items.Components
#if SERVER #if SERVER
GameServer.Log(picker.LogName + " threw " + item.Name, ServerLog.MessageType.ItemInteraction); GameServer.Log(picker.LogName + " threw " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif #endif
Character thrower = picker;
item.Drop(picker, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer); item.Drop(thrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f); item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f);
ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector*10.0f); ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector*10.0f);
@@ -189,7 +189,7 @@ namespace Barotrauma.Items.Components
{ {
get { return name; } get { return name; }
} }
[Editable, Serialize("", true)] [Editable, Serialize("", true)]
public string Msg public string Msg
{ {
@@ -203,12 +203,6 @@ namespace Barotrauma.Items.Components
set; set;
} }
public AITarget AITarget
{
get;
private set;
}
public AITarget AITarget public AITarget AITarget
{ {
get; get;
@@ -253,7 +247,7 @@ namespace Barotrauma.Items.Components
{ {
DebugConsole.ThrowError("Invalid pick key in " + element + "!", e); DebugConsole.ThrowError("Invalid pick key in " + element + "!", e);
} }
SerializableProperties = SerializableProperty.DeserializeProperties(this, element); SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
ParseMsg(); ParseMsg();
@@ -155,13 +155,14 @@ namespace Barotrauma.Items.Components
{ {
if (item.Container != null) { return false; } if (item.Container != null) { return false; }
if (AutoInteractWithContained) if (AutoInteractWithContained && character.SelectedConstruction == null)
{ {
foreach (Item contained in Inventory.Items) foreach (Item contained in Inventory.Items)
{ {
if (contained == null) continue; if (contained == null) continue;
if (contained.TryInteract(character)) if (contained.TryInteract(character))
{ {
character.FocusedItem = contained;
return false; return false;
} }
} }
@@ -178,6 +179,7 @@ namespace Barotrauma.Items.Components
if (contained == null) continue; if (contained == null) continue;
if (contained.TryInteract(picker)) if (contained.TryInteract(picker))
{ {
picker.FocusedItem = contained;
return true; return true;
} }
} }
@@ -90,7 +90,7 @@ namespace Barotrauma.Items.Components
Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f); Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f);
if (Math.Abs(Force) > 1.0f) if (Math.Abs(Force) > 1.0f)
{ {
Vector2 currForce = new Vector2((force / 100.0f) * maxForce * Math.Min(voltage / minVoltage, 1.0f), 0.0f); Vector2 currForce = new Vector2((force / 10.0f) * maxForce * Math.Min(voltage / minVoltage, 1.0f), 0.0f);
//less effective when in a bad condition //less effective when in a bad condition
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition); currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition);
@@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components
private bool shutDown; private bool shutDown;
const float AIUpdateInterval = 1.0f; const float AIUpdateInterval = 0.2f;
private float aiUpdateTimer; private float aiUpdateTimer;
private Character lastUser; private Character lastUser;
@@ -209,10 +209,10 @@ namespace Barotrauma.Items.Components
allowedFissionRate = Vector2.Lerp(new Vector2(20, AvailableFuel), new Vector2(10, AvailableFuel), degreeOfSuccess); allowedFissionRate = Vector2.Lerp(new Vector2(20, AvailableFuel), new Vector2(10, AvailableFuel), degreeOfSuccess);
allowedFissionRate.X = Math.Min(allowedFissionRate.X, allowedFissionRate.Y - 10); allowedFissionRate.X = Math.Min(allowedFissionRate.X, allowedFissionRate.Y - 10);
float heatAmount = fissionRate * (AvailableFuel / 100.0f) * 2.0f; float heatAmount = GetGeneratedHeat(fissionRate);
float temperatureDiff = (heatAmount - turbineOutput) - Temperature; float temperatureDiff = (heatAmount - turbineOutput) - Temperature;
Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff)); Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff));
if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime; //if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime;
FissionRate = MathHelper.Lerp(fissionRate, Math.Min(targetFissionRate, AvailableFuel), deltaTime); FissionRate = MathHelper.Lerp(fissionRate, Math.Min(targetFissionRate, AvailableFuel), deltaTime);
TurbineOutput = MathHelper.Lerp(turbineOutput, targetTurbineOutput, deltaTime); TurbineOutput = MathHelper.Lerp(turbineOutput, targetTurbineOutput, deltaTime);
@@ -313,6 +313,48 @@ namespace Barotrauma.Items.Components
} }
} }
private float GetGeneratedHeat(float fissionRate)
{
return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f;
}
/// <summary>
/// Do we need more fuel to generate enough power to match the current load.
/// </summary>
/// <param name="minimumOutputRatio">How low we allow the output/load ratio to go before loading more fuel.
/// 1.0 = always load more fuel when maximum output is too low, 0.5 = load more if max output is 50% of the load</param>
private bool NeedMoreFuel(float minimumOutputRatio)
{
if (prevAvailableFuel <= 0.0f && load > 0.0f)
{
return true;
}
//fission rate is clamped to the amount of available fuel
float maxFissionRate = Math.Min(prevAvailableFuel, 100.0f);
float maxTurbineOutput = 100.0f;
//calculate the maximum output if the fission rate is cranked as high as it goes and turbine output is at max
float theoreticalMaxHeat = GetGeneratedHeat(fissionRate: maxFissionRate);
float temperatureFactor = Math.Min(theoreticalMaxHeat / 50.0f, 1.0f);
float theoreticalMaxOutput = Math.Min(maxTurbineOutput / 100.0f, temperatureFactor) * MaxPowerOutput;
//maximum output not enough, we need more fuel
return theoreticalMaxOutput < load * minimumOutputRatio;
}
private bool TooMuchFuel()
{
var containedItems = item.ContainedItems;
if (containedItems != null && containedItems.Count() <= 1) { return false; }
//get the amount of heat we'd generate if the fission rate was at the low end of the optimal range
float minimumHeat = GetGeneratedHeat(optimalFissionRate.X);
//if we need a very high turbine output to keep the engine from overheating, there's too much fuel
return minimumHeat > Math.Min(correctTurbineOutput * 1.5f, 90);
}
private void UpdateFailures(float deltaTime) private void UpdateFailures(float deltaTime)
{ {
if (temperature > allowedTemperature.Y) if (temperature > allowedTemperature.Y)
@@ -367,6 +409,11 @@ namespace Barotrauma.Items.Components
targetFissionRate = Math.Min(targetFissionRate + speed * 2 * deltaTime, 100.0f); targetFissionRate = Math.Min(targetFissionRate + speed * 2 * deltaTime, 100.0f);
} }
targetFissionRate = MathHelper.Clamp(targetFissionRate, 0.0f, 100.0f); targetFissionRate = MathHelper.Clamp(targetFissionRate, 0.0f, 100.0f);
//don't push the target too far from the current fission rate
//otherwise we may "overshoot", cranking the target fission rate all the way up because it takes a while
//for the actual fission rate and temperature to follow
targetFissionRate = MathHelper.Clamp(targetFissionRate, FissionRate - 5, FissionRate + 5);
} }
public override void UpdateBroken(float deltaTime, Camera cam) public override void UpdateBroken(float deltaTime, Camera cam)
@@ -441,12 +488,18 @@ namespace Barotrauma.Items.Components
} }
} }
//we need more fuel if (aiUpdateTimer > 0.0f)
if (-currPowerConsumption < load * 0.5f && prevAvailableFuel <= 0.0f) {
aiUpdateTimer -= deltaTime;
return false;
}
//load more fuel if the current maximum output is only 50% of the current load
if (NeedMoreFuel(minimumOutputRatio: 0.5f))
{ {
var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent<ItemContainer>()) var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent<ItemContainer>())
{ {
MinContainedAmount = containedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1, MinContainedAmount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1,
GetItemPriority = (Item fuelItem) => GetItemPriority = (Item fuelItem) =>
{ {
if (fuelItem.ParentInventory?.Owner is Item) if (fuelItem.ParentInventory?.Owner is Item)
@@ -461,6 +514,7 @@ namespace Barotrauma.Items.Components
character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f); character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
aiUpdateTimer = AIUpdateInterval;
return false; return false;
} }
else if (TooMuchFuel()) else if (TooMuchFuel())
@@ -479,12 +533,6 @@ namespace Barotrauma.Items.Components
} }
} }
if (aiUpdateTimer > 0.0f)
{
aiUpdateTimer -= deltaTime;
return false;
}
if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item) if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item)
{ {
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f); character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
@@ -506,7 +554,7 @@ namespace Barotrauma.Items.Components
{ {
AutoTemp = false; AutoTemp = false;
unsentChanges = true; unsentChanges = true;
UpdateAutoTemp(2.0f + degreeOfSuccess * 5.0f, 1.0f); UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
} }
#if CLIENT #if CLIENT
onOffSwitch.BarScroll = 0.0f; onOffSwitch.BarScroll = 0.0f;
@@ -153,7 +153,7 @@ namespace Barotrauma.Items.Components
} }
continue; continue;
} }
if (!pt.IsActive) { continue; } if (!pt.IsActive || !pt.CanTransfer) { continue; }
gridLoad += pt.PowerLoad; gridLoad += pt.PowerLoad;
gridPower -= pt.CurrPowerConsumption; gridPower -= pt.CurrPowerConsumption;
@@ -209,9 +209,9 @@ namespace Barotrauma.Items.Components
Charge -= CurrPowerOutput / 3600.0f; Charge -= CurrPowerOutput / 3600.0f;
} }
item.SendSignal(0, Charge.ToString(), "charge", null); item.SendSignal(0, ((int)Charge).ToString(), "charge", null);
item.SendSignal(0, ((Charge / capacity) * 100).ToString(), "charge_%", null); item.SendSignal(0, ((int)((Charge / capacity) * 100)).ToString(), "charge_%", null);
item.SendSignal(0, ((RechargeSpeed / maxRechargeSpeed) * 100).ToString(), "charge_rate", null); item.SendSignal(0, ((int)((RechargeSpeed / maxRechargeSpeed) * 100)).ToString(), "charge_rate", null);
foreach (Pair<Powered, Connection> connected in directlyConnected) foreach (Pair<Powered, Connection> connected in directlyConnected)
{ {
@@ -180,7 +180,8 @@ namespace Barotrauma.Items.Components
#endif #endif
//items in a bad condition are more sensitive to overvoltage //items in a bad condition are more sensitive to overvoltage
float maxOverVoltage = MathHelper.Lerp(Math.Min(OverloadVoltage, 1.0f), OverloadVoltage, item.Condition / item.MaxCondition); float maxOverVoltage = MathHelper.Lerp(OverloadVoltage * 0.75f, OverloadVoltage, item.Condition / item.MaxCondition);
maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
//if the item can't be fixed, don't allow it to break //if the item can't be fixed, don't allow it to break
if (!item.Repairables.Any() || !CanBeOverloaded) continue; if (!item.Repairables.Any() || !CanBeOverloaded) continue;
@@ -319,7 +320,13 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null); ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity; //float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity;
RelayComponent thisRelayComponent = this as RelayComponent;
if (thisRelayComponent != null)
{
clampPower = Math.Min(Math.Min(clampPower, thisRelayComponent.MaxPower), powerLoad);
clampLoad = Math.Min(clampLoad, thisRelayComponent.MaxPower);
}
foreach (Connection c in PowerConnections) foreach (Connection c in PowerConnections)
{ {
@@ -357,6 +364,8 @@ namespace Barotrauma.Items.Components
continue; continue;
} }
float addLoad = 0.0f;
float addPower = 0.0f;
if (powered is PowerContainer powerContainer) if (powered is PowerContainer powerContainer)
{ {
if (recipient.Name == "power_in") if (recipient.Name == "power_in")
@@ -365,7 +374,7 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
fullPower += Math.Min(powerContainer.CurrPowerOutput, maxPower); addPower = powerContainer.CurrPowerOutput;
} }
} }
else else
@@ -380,10 +389,16 @@ namespace Barotrauma.Items.Components
//negative power consumption = the construction is a //negative power consumption = the construction is a
//generator/battery or another junction box //generator/battery or another junction box
{ {
fullPower -= Math.Max(powered.CurrPowerConsumption, -maxPower); addPower -= powered.CurrPowerConsumption;
} }
} }
}
if (addPower + fullPower > clampPower) { addPower -= (addPower + fullPower) - clampPower; };
if (addPower > 0) { fullPower += addPower; }
if (addLoad + fullLoad > clampLoad) { addLoad -= (addLoad + fullLoad) - clampLoad; };
if (addLoad > 0) { fullLoad += addLoad; }
}
} }
} }
} }
@@ -12,8 +12,6 @@ namespace Barotrauma.Items.Components
public static float SkillIncreaseMultiplier = 0.4f; public static float SkillIncreaseMultiplier = 0.4f;
private string header; private string header;
private float fixDurationLowSkill, fixDurationHighSkill;
private float deteriorationTimer; private float deteriorationTimer;
@@ -52,17 +50,20 @@ namespace Barotrauma.Items.Components
set; set;
} }
/*private float repairProgress; [Serialize(100.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with insufficient skill levels.")]
public float RepairProgress public float FixDurationLowSkill
{ {
get { return repairProgress; } get;
set set;
{ }
repairProgress = MathHelper.Clamp(value, 0.0f, 1.0f);
if (repairProgress >= 1.0f && currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None; [Serialize(10.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with sufficient skill levels.")]
} public float FixDurationHighSkill
}*/ {
get;
set;
}
private Character currentFixer; private Character currentFixer;
public Character CurrentFixer public Character CurrentFixer
{ {
@@ -83,8 +84,6 @@ namespace Barotrauma.Items.Components
this.item = item; this.item = item;
header = element.GetAttributeString("name", ""); header = element.GetAttributeString("name", "");
fixDurationLowSkill = element.GetAttributeFloat("fixdurationlowskill", 100.0f);
fixDurationHighSkill = element.GetAttributeFloat("fixdurationhighskill", 5.0f);
InitProjSpecific(element); InitProjSpecific(element);
} }
@@ -160,7 +159,7 @@ namespace Barotrauma.Items.Components
} }
bool wasBroken = !item.IsFullCondition; bool wasBroken = !item.IsFullCondition;
float fixDuration = MathHelper.Lerp(fixDurationLowSkill, fixDurationHighSkill, successFactor); float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
if (fixDuration <= 0.0f) if (fixDuration <= 0.0f)
{ {
item.Condition = item.MaxCondition; item.Condition = item.MaxCondition;
@@ -186,26 +185,5 @@ namespace Barotrauma.Items.Components
{ {
character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f)); character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f));
} }
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.Write(deteriorationTimer);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
deteriorationTimer = msg.ReadSingle();
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
{
//no need to write anything, just letting the server know we started repairing
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
if (c.Character == null) return;
StartRepairing(c.Character);
}
} }
} }
@@ -163,6 +163,14 @@ namespace Barotrauma.Items.Components
{ {
wires[index] = wire; wires[index] = wire;
recipientsDirty = true; recipientsDirty = true;
if (wire != null)
{
var otherConnection = wire.OtherConnection(this);
if (otherConnection != null)
{
otherConnection.recipientsDirty = true;
}
}
} }
public void SendSignal(int stepsTaken, string signal, Item source, Character sender, float power, float signalStrength = 1.0f) public void SendSignal(int stepsTaken, string signal, Item source, Character sender, float power, float signalStrength = 1.0f)
@@ -356,7 +356,7 @@ namespace Barotrauma.Items.Components
projectile.body.ResetDynamics(); projectile.body.ResetDynamics();
projectile.body.Enabled = true; projectile.body.Enabled = true;
projectile.SetTransform(ConvertUnits.ToSimUnits(new Vector2(item.WorldRect.X + transformedBarrelPos.X, item.WorldRect.Y - transformedBarrelPos.Y)), -rotation); projectile.SetTransform(ConvertUnits.ToSimUnits(new Vector2(item.WorldRect.X + transformedBarrelPos.X, item.WorldRect.Y - transformedBarrelPos.Y)), -rotation);
projectile.FindHull(); projectile.UpdateTransform();
projectile.Submarine = projectile.body.Submarine; projectile.Submarine = projectile.body.Submarine;
Projectile projectileComponent = projectile.GetComponent<Projectile>(); Projectile projectileComponent = projectile.GetComponent<Projectile>();
@@ -59,7 +59,7 @@ namespace Barotrauma
public PhysicsBody body; public PhysicsBody body;
public readonly XElement StaticBodyConfig; public readonly XElement StaticBodyConfig;
private float lastSentCondition; private float lastSentCondition;
private float sendConditionUpdateTimer; private float sendConditionUpdateTimer;
private bool conditionUpdatePending; private bool conditionUpdatePending;
@@ -211,14 +211,14 @@ namespace Barotrauma
set { spriteColor = value; } set { spriteColor = value; }
} }
[Serialize("1.0,1.0,1.0,1.0", false), Editable] [Serialize("1.0,1.0,1.0,1.0", true), Editable]
public Color InventoryIconColor public Color InventoryIconColor
{ {
get; get;
protected set; protected set;
} }
[Serialize("1.0,1.0,1.0,1.0", false), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")] [Serialize("1.0,1.0,1.0,1.0", true), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")]
public Color ContainerColor public Color ContainerColor
{ {
get; get;
@@ -274,12 +274,11 @@ namespace Barotrauma
SetActiveSprite(); SetActiveSprite();
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && lastSentCondition != condition) if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !MathUtils.NearlyEqual(lastSentCondition, condition))
{ {
if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health) if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health)
{ {
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status }); conditionUpdatePending = true;
lastSentCondition = condition;
} }
} }
} }
@@ -996,6 +995,21 @@ namespace Barotrauma
aiTarget.SightRange -= deltaTime * 1000.0f; aiTarget.SightRange -= deltaTime * 1000.0f;
aiTarget.SoundRange -= deltaTime * 1000.0f; aiTarget.SoundRange -= deltaTime * 1000.0f;
} }
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
sendConditionUpdateTimer -= deltaTime;
if (conditionUpdatePending)
{
if (sendConditionUpdateTimer <= 0.0f)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
lastSentCondition = condition;
sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval;
conditionUpdatePending = false;
}
}
}
ApplyStatusEffects(ActionType.Always, deltaTime, null); ApplyStatusEffects(ActionType.Always, deltaTime, null);
@@ -1272,7 +1286,11 @@ namespace Barotrauma
LastSentSignalRecipients.Clear(); LastSentSignalRecipients.Clear();
if (connections == null) return; if (connections == null) return;
stepsTaken++; public List<T> GetConnectedComponentsRecursive<T>(Connection c) where T : ItemComponent
{
List<T> connectedComponents = new List<T>();
List<Item> alreadySearched = new List<Item>() { this };
GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents);
if (!connections.TryGetValue(connectionName, out Connection c)) return; if (!connections.TryGetValue(connectionName, out Connection c)) return;
@@ -554,6 +554,39 @@ namespace Barotrauma
AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList(); AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList();
if (sprite == null)
{
DebugConsole.ThrowError("Item \"" + Name + "\" has no sprite!");
#if SERVER
sprite = new Sprite("", Vector2.Zero);
sprite.SourceRect = new Rectangle(0, 0, 32, 32);
#else
sprite = new Sprite(TextureLoader.PlaceHolderTexture, null, null)
{
Origin = TextureLoader.PlaceHolderTexture.Bounds.Size.ToVector2() / 2
};
#endif
size = sprite.size;
sprite.EntityID = identifier;
}
if (!category.HasFlag(MapEntityCategory.Legacy) && string.IsNullOrEmpty(identifier))
{
DebugConsole.ThrowError(
"Item prefab \"" + name + "\" has no identifier. All item prefabs have a unique identifier string that's used to differentiate between items during saving and loading.");
}
if (!string.IsNullOrEmpty(identifier))
{
MapEntityPrefab existingPrefab = List.Find(e => e.Identifier == identifier);
if (existingPrefab != null)
{
DebugConsole.ThrowError(
"Map entity prefabs \"" + name + "\" and \"" + existingPrefab.Name + "\" have the same identifier!");
}
}
AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList();
List.Add(this); List.Add(this);
} }

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