(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

View File

@@ -79,8 +79,8 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\ParamsEditor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\RectTransform.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\VideoPlayer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\Widget.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\CharacterInventory.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\Sprite.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\SpriteSheet.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\StatusEffects\StatusEffect.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\LocalizationCSVtoXML.cs">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Compile>

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.9.6")]
[assembly: AssemblyFileVersion("0.8.9.6")]
[assembly: AssemblyVersion("0.8.9.8")]
[assembly: AssemblyFileVersion("0.8.9.8")]

View File

@@ -13,13 +13,39 @@ namespace Barotrauma
var pos = new Vector2(WorldPosition.X, -WorldPosition.Y);
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, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
}
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, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
}
}
}

View File

@@ -16,18 +16,17 @@ namespace Barotrauma
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)
{
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;
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.Font.DrawString(spriteBatch, $"{SelectedAiTarget.Entity.ToString()} ({targetValue.ToString()})", pos - Vector2.UnitY * 20.0f, Color.Red);
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity.ToString()} ({targetValue.FormatZeroDecimal()})", Color.Red, Color.Black);
}
/*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);
if (latchOntoAI != null)
if (LatchOntoAI != null)
{
foreach (Joint attachJoint in latchOntoAI.AttachJoints)
foreach (Joint attachJoint in LatchOntoAI.AttachJoints)
{
GUI.DrawLine(spriteBatch,
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)
{
GUI.DrawLine(spriteBatch, pos,
@@ -77,22 +67,34 @@ namespace Barotrauma
}
}
GUI.DrawLine(spriteBatch,
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++)
if (steeringManager is IndoorsSteeringManager pathSteering)
{
GUI.DrawLine(spriteBatch,
new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y),
new Vector2(pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.Y),
Color.Orange * 0.6f, 0, 3);
var path = pathSteering.CurrentPath;
if (path != null)
{
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,
pathSteering.CurrentPath.Nodes[i].ID.ToString(),
new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y - 10),
Color.LightGreen);
GUI.DrawString(spriteBatch, pos - new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
}
for (int i = 1; i < path.Nodes.Count; i++)
{
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(Steering.X, -Steering.Y)), Color.Blue, width: 3);

View File

@@ -75,11 +75,12 @@ namespace Barotrauma
GUI.SmallFont.DrawString(spriteBatch,
currentNode.ID.ToString(),
new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y - 10),
Color.LightGreen);
new Vector2(currentNode.DrawPosition.X + 20, -currentNode.DrawPosition.Y - 20),
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);
//if (Character.IsKeyDown(InputType.Aim))

View File

@@ -88,13 +88,13 @@ namespace Barotrauma
Collider.AngularVelocity = newAngularVelocity;
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 > 10.0f || !character.AllowInput)
{
Collider.TargetRotation = newRotation;
SetPosition(newPosition, lerp: distSqrd < 5.0f);
SetPosition(newPosition, lerp: distSqrd < 1.0f);
}
else
{
@@ -108,15 +108,8 @@ namespace Barotrauma
// -> we need to correct it manually
if (!character.AllowInput)
{
float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition);
float mainLimbErrorTolerance = 0.1f;
//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;
}
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
MainLimb.PullJointEnabled = true;
}
}
character.MemLocalState.Clear();

View File

@@ -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);
}
@@ -327,6 +341,8 @@ namespace Barotrauma
debugInteractablesAtCursor.Clear();
debugInteractablesNearCursor.Clear();
bool draggingItemToWorld = CharacterInventory.DraggingItemToWorld;
//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
//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.ParentInventory != null) continue;
if (ignoredItems != null && ignoredItems.Contains(item)) continue;
if (draggingItemToWorld)
{
if (item.OwnInventory == null ||
!item.OwnInventory.CanBePut(CharacterInventory.draggingItem))
{
continue;
}
}
float distanceToItem = float.PositiveInfinity;
if (item.IsInsideTrigger(displayPosition, out Rectangle transformedTrigger))

View File

@@ -248,7 +248,7 @@ namespace Barotrauma
scale: scale);
}
if (!GUI.DisableItemHighlights)
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
{
var hudTexts = focusedItem.GetHUDTexts(character);

View File

@@ -241,10 +241,12 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(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();
skillLevels.Add(jobPrefab.Skills[i].Identifier, skillLevel);
skillLevels.Add(skillIdentifier, skillLevel);
}
}
@@ -254,7 +256,6 @@ namespace Barotrauma
ID = infoID,
};
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)
{
foreach (KeyValuePair<string, float> skill in skillLevels)
@@ -262,11 +263,12 @@ namespace Barotrauma
Skill matchingSkill = ch.Job.Skills.Find(s => s.Identifier == skill.Key);
if (matchingSkill == null)
{
DebugConsole.ThrowError("Skill \"" + skill.Key + "\" not found in character \"" + newName + "\"");
ch.Job.Skills.Add(new Skill(skill.Key, skill.Value));
continue;
}
matchingSkill.Level = skill.Value;
}
ch.Job.Skills.RemoveAll(s => !skillLevels.ContainsKey(s.Identifier));
}
return ch;
}

View File

@@ -3,7 +3,6 @@ using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Collections.Generic;
namespace Barotrauma
{

View File

@@ -68,6 +68,14 @@ namespace Barotrauma
private float dropItemAnimDuration = 0.5f;
private float dropItemAnimTimer;
public Item DroppedItem
{
get
{
return droppedItem;
}
}
private Item droppedItem;
private GUIComponent draggingMed;
@@ -100,13 +108,15 @@ namespace Barotrauma
if (openHealthWindow == value) return;
if (value != null && !value.UseHealthWindow) return;
var prevOpenHealthWindow = openHealthWindow;
openHealthWindow = value;
toggledThisFrame = true;
if (Character.Controlled == null) { return; }
if (value == null &&
Character.Controlled?.SelectedCharacter?.CharacterHealth != null &&
Character.Controlled.SelectedCharacter.CharacterHealth == openHealthWindow &&
Character.Controlled.SelectedCharacter.CharacterHealth == prevOpenHealthWindow &&
!Character.Controlled.SelectedCharacter.CanInventoryBeAccessed)
{
Character.Controlled.DeselectCharacter();
@@ -537,11 +547,13 @@ namespace Barotrauma
}
if (PlayerInput.KeyHit(InputType.Health) && GUI.KeyboardDispatcher.Subscriber == null &&
Character.AllowInput && Character.FocusedCharacter == null && !toggledThisFrame)
Character.Controlled.AllowInput && !toggledThisFrame)
{
if (openHealthWindow != null)
{
OpenHealthWindow = null;
else
}
else if (Character.Controlled == Character && Character.Controlled.FocusedCharacter == null)
{
OpenHealthWindow = this;
forceAfflictionContainerUpdate = true;
@@ -554,7 +566,10 @@ namespace Barotrauma
HUD.CloseHUD(HUDLayoutSettings.HealthWindowAreaLeft))
{
//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;
}
}

View File

@@ -403,6 +403,11 @@ namespace Barotrauma
AssignRelayToServer("help", false);
AssignRelayToServer("verboselogging", false);
AssignRelayToServer("freecam", false);
#if DEBUG
AssignRelayToServer("simulatedlatency", false);
AssignRelayToServer("simulatedloss", false);
AssignRelayToServer("simulatedduplicateschance", false);
#endif
commands.Add(new Command("clientlist", "", (string[] args) => { }));
AssignRelayToServer("clientlist", true);
@@ -957,6 +962,8 @@ namespace Barotrauma
}
}
}
element.Value = lines[i];
i++;
}
}, isCheat: false));
#endif

View File

@@ -178,10 +178,15 @@ namespace Barotrauma
string displayedText = message.TranslatedText;
string senderName = "";
Color senderColor = Color.White;
if (!string.IsNullOrWhiteSpace(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,
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)
{ 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
};
@@ -230,7 +235,7 @@ namespace Barotrauma
Visible = false
};
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
};
@@ -328,6 +333,7 @@ namespace Barotrauma
var popupMsg = popupMessages.Count > 0 ? popupMessages.Peek() : 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;
//popup messages appear and disappear faster when there's more pending messages
popupMessageTimer += deltaTime * popupMessages.Count * popupMessages.Count;
@@ -335,7 +341,7 @@ namespace Barotrauma
{
//move the message out of the screen and delete it
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)
{
popupMessageTimer = 0.0f;
@@ -347,7 +353,7 @@ namespace Barotrauma
{
//move the message on the screen
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);
}
}
}

View File

@@ -56,7 +56,7 @@ namespace Barotrauma
private static List<GUIMessage> messages = new List<GUIMessage>();
private static Sound[] sounds;
private static bool pauseMenuOpen, settingsMenuOpen;
private static GUIFrame pauseMenu;
public static GUIFrame PauseMenu { get; private set; }
private static Sprite arrow, lockIcon, checkmarkIcon, timerIcon;
public static KeyboardDispatcher KeyboardDispatcher { get; set; }
@@ -69,6 +69,9 @@ namespace Barotrauma
public static ScalableFont Font => Style?.Font;
public static ScalableFont SmallFont => Style?.SmallFont;
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;
@@ -530,14 +533,19 @@ namespace Barotrauma
if (list.Count == 0) { return; }
foreach (var item in list)
{
int i = updateList.Count - 1;
while (updateList[i].UpdateOrder > item.UpdateOrder)
int index = 0;
if (updateList.Count > 0)
{
i--;
index = updateList.Count - 1;
while (updateList[index].UpdateOrder > item.UpdateOrder)
{
index--;
if (index == 0) { break; }
}
}
if (!updateListSet.Contains(item))
{
updateList.Insert(Math.Max(i, 0), item);
updateList.Insert(index, item);
updateListSet.Add(item);
}
}
@@ -553,7 +561,7 @@ namespace Barotrauma
if (pauseMenuOpen)
{
pauseMenu.AddToGUIUpdateList();
PauseMenu.AddToGUIUpdateList();
}
if (settingsMenuOpen)
{
@@ -648,6 +656,7 @@ namespace Barotrauma
msg.Timer -= deltaTime;
msg.Pos += msg.Velocity * deltaTime;
}
}
messages.RemoveAll(m => m.Timer <= 0.0f);
}
@@ -721,6 +730,10 @@ namespace Barotrauma
Vector2 textSize = font.MeasureString(text);
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);
}
@@ -1412,9 +1425,9 @@ namespace Barotrauma
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))
{

View File

@@ -12,6 +12,9 @@ namespace Barotrauma
public ScalableFont Font { get; private set; }
public ScalableFont SmallFont { 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; }
@@ -48,6 +51,15 @@ namespace Barotrauma
case "largefont":
LargeFont = new ScalableFont(subElement, graphicsDevice);
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":
CursorSprite = new Sprite(subElement);
break;

View File

@@ -50,6 +50,11 @@ namespace Barotrauma
get; private set;
}
public static Rectangle ObjectiveAnchor
{
get; private set;
}
public static Rectangle InventoryAreaLower
{
get; private set;
@@ -156,6 +161,10 @@ namespace Barotrauma
new Rectangle(Padding, 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);
InventoryAreaLower = new Rectangle(Padding, GameMain.GraphicsHeight - lowerAreaHeight, GameMain.GraphicsWidth - Padding * 2, lowerAreaHeight);
@@ -191,6 +200,9 @@ namespace Barotrauma
{
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
if (GUI.MouseOn != null) return false;

View File

@@ -601,8 +601,10 @@ namespace Barotrauma
{
((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();
}
}

View File

@@ -130,7 +130,12 @@ namespace Barotrauma
{
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];
@@ -976,7 +981,7 @@ namespace Barotrauma
foreach (GUIComponent c in prevCharacterListBox.Content.Children)
{
Character character = c.UserData as Character;
if (character == null) continue;
if (character == null || character.IsDead || character.Removed) continue;
AddCharacter(character);
DisplayCharacterOrder(character, character.CurrentOrder);
}

View File

@@ -44,7 +44,7 @@ namespace Barotrauma
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();
}

View File

@@ -1,9 +1,10 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Xml.Linq;
using System;
using Microsoft.Xna.Framework;
using Barotrauma.Items.Components;
using System.Linq;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma.Tutorials
{
@@ -13,38 +14,59 @@ namespace Barotrauma.Tutorials
public static bool ContentRunning = 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 List<TutorialSegment> segments;
private SpriteSheetPlayer spriteSheetPlayer;
private VideoPlayer videoPlayer;
private Steering navConsole;
private Reactor reactor;
private Sonar sonar;
private Vector2 subStartingPosition;
private List<Character> crew;
private Character mechanic;
private Character engineer;
private Character injuredMember = null;
private List<Pair<Character, float>> characterTimeOnSonar;
private float requiredTimeOnSonar = 5f;
private bool started = false;
private string playableContentPath;
private float tutorialTimer;
private float degrading2ActivationCountdown;
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
{
public string Name;
public string Id;
public string Objective;
public ContentTypes ContentType;
public XElement Content;
public XElement TextContent;
public XElement VideoContent;
public bool IsTriggered;
public GUIButton ReplayButton;
public GUITextBlock LinkedTitle, LinkedText;
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);
IsTriggered = config.GetAttributeBool("istriggered", false);
@@ -53,10 +75,11 @@ namespace Barotrauma.Tutorials
case ContentTypes.None:
break;
case ContentTypes.Video:
Content = config.Element("Video");
VideoContent = config.Element("Video");
TextContent = config.Element("Text");
break;
case ContentTypes.Text:
Content = config.Element("Text");
case ContentTypes.TextOnly:
TextContent = config.Element("Text");
break;
}
}
@@ -77,17 +100,17 @@ namespace Barotrauma.Tutorials
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++)
{
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)
@@ -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)
{
XElement tutorialElement = new XElement("contextualtutorial");
@@ -151,13 +165,13 @@ namespace Barotrauma.Tutorials
{
if (!Initialized) return;
PreloadVideoContent();
base.Start();
injuredMember = null;
activeObjectives.Clear();
objectiveTranslated = TextManager.Get("Objective");
CreateObjectiveFrame();
activeSegment = null;
tutorialTimer = 0.0f;
degrading2ActivationCountdown = -1;
tutorialTimer = floodTutorialTimer = medicalTutorialTimer = 0.0f;
subStartingPosition = Vector2.Zero;
characterTimeOnSonar.Clear();
@@ -193,6 +207,9 @@ namespace Barotrauma.Tutorials
}
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
started = true;
}
@@ -208,71 +225,185 @@ namespace Barotrauma.Tutorials
public void Stop()
{
started = ContentRunning = Initialized = false;
spriteSheetPlayer.Remove();
spriteSheetPlayer = null;
videoPlayer.Remove();
videoPlayer = 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()
{
base.AddToGUIUpdateList();
if (spriteSheetPlayer != null)
if (videoPlayer != 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)
{
if (videoPlayer != null)
{
videoPlayer.Update();
}
if (infoBox != null)
{
if (PlayerInput.KeyHit(Keys.Enter) || PlayerInput.KeyHit(Keys.Escape))
{
CloseInfoFrame(null, null);
}
}
if (!started || ContentRunning) return;
deltaTime *= 0.5f;
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
{
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()
{
if (!string.IsNullOrEmpty(activeSegment.Objective))
{
AddNewObjective(activeSegment);
}
activeSegment = null;
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)
{
switch (index)
{
case 0: // Welcome: Game Start [Text]
if (tutorialTimer < 0.5f)
if (tutorialTimer < 1.0f)
{
tutorialTimer += deltaTime;
return false;
}
break;
case 1: // Command Reactor: 10 seconds after 'Welcome' dismissed and only if no command given to start reactor [Video]
if (tutorialTimer < 10.5f)
case 1: // Command Reactor: 2 seconds after 'Welcome' dismissed and only if no command given to start reactor [Video]
if (!segments[0].IsTriggered) return false;
if (tutorialTimer < 3.0f)
{
tutorialTimer += deltaTime;
if (HasOrder("operatereactor"))
{
segments[index].IsTriggered = true;
tutorialTimer = 10.5f;
tutorialTimer = 2.5f;
}
return false;
}
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 (!segments[1].IsTriggered) return false; // Do not advance tutorial timer based on this segment if reactor has not been powered up
if (tutorialTimer < 30.5f)
{
if (tutorialTimer < 4.5f)
{
tutorialTimer += deltaTime;
return false;
@@ -280,20 +411,13 @@ namespace Barotrauma.Tutorials
}
else
{
if (!segments[1].IsTriggered || !HasOrder("operatereactor")) // If reactor has not been powered up or ordered to be, default to that one first
{
if (tutorialTimer < 10.5f)
{
tutorialTimer = 10.5f;
}
return false;
}
tutorialTimer = 30.5f;
tutorialTimer = 4.5f;
}
break;
TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name);
return true;
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;
}
@@ -307,6 +431,11 @@ namespace Barotrauma.Tutorials
{
return false;
}
else if (floodTutorialTimer < floodTutorialDelay)
{
floodTutorialTimer += deltaTime;
return false;
}
break;
case 5: // Reactor: Player uses reactor for the first time [Video]
if (Character.Controlled?.SelectedConstruction != reactor.Item)
@@ -321,19 +450,23 @@ namespace Barotrauma.Tutorials
}
break;
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;
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;
break;
}
if (degradedEquipmentFound)
{
degrading2ActivationCountdown = 5f;
if (HasOrder("repairsystems"))
if (HasOrder("repairsystems", "jobspecific"))
{
segments[index].IsTriggered = true;
return false;
@@ -344,43 +477,44 @@ namespace Barotrauma.Tutorials
return false;
}
break;
case 8: // Degrading2: 5 seconds after 'Degrading1' dismissed, and only if player has not assigned any crew to perform maintenance [Video]
if (degrading2ActivationCountdown == -1f)
case 8: // Medical: Crewmember is injured but not killed [Video]
if (injuredMember == null)
{
return false;
}
else if (degrading2ActivationCountdown > 0.0f)
{
degrading2ActivationCountdown -= deltaTime;
if (HasOrder("repairsystems"))
for (int i = 0; i < crew.Count; i++)
{
segments[index].IsTriggered = true;
Character member = crew[i];
if (member.Vitality < member.MaxVitality && !member.IsDead)
{
injuredMember = member;
break;
}
}
return false;
}
break;
case 9: // Medical: Crewmember is injured but not killed [Video]
bool injuredFound = false;
for (int i = 0; i < crew.Count; i++)
else if (medicalTutorialTimer < medicalTutorialDelay)
{
Character member = crew[i];
if (member.Vitality < member.MaxVitality && !member.IsDead)
{
injuredFound = true;
break;
}
medicalTutorialTimer += deltaTime;
return false;
}
if (!injuredFound) return false;
break;
case 10: // Approach1: Destination is within ~100m [Video]
else
{
TriggerTutorialSegment(index, new string[] { injuredMember.Info.DisplayName,
(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)
{
return false;
}
break;
case 11: // Approach2: Sub is docked [Text]
else
{
TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name);
return true;
}
case 10: // Approach2: Sub is docked [Text]
if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0)
{
return false;
@@ -392,11 +526,128 @@ namespace Barotrauma.Tutorials
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++)
{
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;
@@ -406,10 +657,12 @@ namespace Barotrauma.Tutorials
{
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.Submarine == null) continue;
if (gap.Submarine.IsOutpost) continue;
if (gap.Submarine != Submarine.MainSub) continue;
if (gap.FlowTargetHull == null || gap.FlowTargetHull.WaterPercentage <= 0.0f) continue;
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)
{
Inventory.draggingItem = null;
ContentRunning = true;
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)
{
case ContentTypes.None:
break;
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;
case ContentTypes.Text:
infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Name), TextManager.GetFormatted(activeSegment.Content.GetAttributeString("tag", ""), false, args),
activeSegment.Content.GetAttributeInt("width", 300),
activeSegment.Content.GetAttributeInt("height", 80),
activeSegment.Content.GetAttributeString("anchor", "Center"), true, CurrentSegmentStopCallback);
case ContentTypes.TextOnly:
infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Id), tutorialText,
activeSegment.TextContent.GetAttributeInt("width", 300),
activeSegment.TextContent.GetAttributeInt("height", 80),
activeSegment.TextContent.GetAttributeString("anchor", "Center"), true, CurrentSegmentStopCallback);
break;
}
@@ -479,6 +756,13 @@ namespace Barotrauma.Tutorials
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()
{
while (ContentRunning) yield return null;

View File

@@ -121,7 +121,7 @@ namespace Barotrauma.Tutorials
public virtual void AddToGUIUpdateList()
{
if (infoBox != null) infoBox.AddToGUIUpdateList();
if (infoBox != null) infoBox.AddToGUIUpdateList(order: 100);
}
public virtual void Update(float deltaTime)
@@ -188,17 +188,19 @@ namespace Barotrauma.Tutorials
Anchor anchor = Anchor.TopRight;
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);
if (title.Length > 0)
{
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),
text, wrap: true);
textBlock.TextScale = GUI.Scale;
infoBoxClosedCallback = callback;

View File

@@ -14,12 +14,13 @@ namespace Barotrauma
{
private enum Tab
{
General,
Graphics,
Audio,
Controls,
}
private readonly Point MinSupportedResolution = new Point(1024, 540);
private GUIFrame settingsFrame;
private GUIButton applyButton;
@@ -71,16 +72,79 @@ namespace Barotrauma
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),
TextManager.Get("Settings"), textAlignment: Alignment.Center, font: GUI.LargeFont);
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);
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.8f), settingsFrame.RectTransform, Anchor.Center)
{ RelativeOffset = new Vector2(0.0f, 0.06f) }, style: null);
/// General tab --------------------------------------------------------------
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];
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)
{ 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)
{ RelativeOffset = new Vector2(0.02f, 0.0f) })
{ RelativeSpacing = 0.01f, Stretch = true };
{ RelativeSpacing = 0.01f };
var supportedDisplayModes = new List<DisplayMode>();
foreach (DisplayMode mode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
@@ -135,6 +199,7 @@ namespace Barotrauma
foreach (DisplayMode mode in supportedDisplayModes)
{
if (mode.Width < MinSupportedResolution.X || mode.Height < MinSupportedResolution.Y) { continue; }
resolutionDD.AddItem(mode.Width + "x" + mode.Height, mode);
if (GraphicsWidth == mode.Width && GraphicsHeight == mode.Height) resolutionDD.SelectItem(mode);
}
@@ -173,9 +238,6 @@ namespace Barotrauma
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"))
{
ToolTip = TextManager.Get("EnableVSyncToolTip"),
@@ -191,8 +253,6 @@ namespace Barotrauma
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"));
GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
barSize: 0.1f)
@@ -209,8 +269,6 @@ namespace Barotrauma
};
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"));
var losModeDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform));
losModeDD.AddItem(TextManager.Get("LosModeNone"), LosMode.None);
@@ -229,8 +287,6 @@ namespace Barotrauma
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"))
{
ToolTip = TextManager.Get("LightMapScaleToolTip")
@@ -251,8 +307,6 @@ namespace Barotrauma
};
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"))
{
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"))
{
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"));
GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
barSize: 0.1f)
@@ -300,9 +349,6 @@ namespace Barotrauma
};
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"));
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);
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), rightColumn.RectTransform), style: null);
/// Audio tab ----------------------------------------------------------------
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));
for (int i = 0; i < inputNames.Length; i++)
{
var inputContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.06f), inputFrame.RectTransform), style: null);
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), inputContainer.RectTransform), TextManager.Get("InputType." + ((InputType)i)) + ": ", font: GUI.SmallFont);
var keyBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), inputContainer.RectTransform, Anchor.TopRight),
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.3f, 1.0f), inputContainer.RectTransform, Anchor.TopLeft) { MinSize = new Point(150, 0) },
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)
{
UserData = i
@@ -593,88 +637,11 @@ namespace Barotrauma
};
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
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),
TextManager.Get("Cancel"))
TextManager.Get("Cancel"), style: "GUIButtonLarge")
{
IgnoreLayoutGroups = true,
OnClicked = (x, y) =>
@@ -690,7 +657,7 @@ namespace Barotrauma
};
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,
Enabled = false
@@ -698,7 +665,7 @@ namespace Barotrauma
applyButton.OnClicked = ApplyClicked;
UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created
SelectTab(Tab.General);
SelectTab(Tab.Graphics);
}
private void SelectTab(Tab tab)

View File

@@ -93,7 +93,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing)
{
Color color = Color.White;
Color color = item.SpriteColor;
if (brokenSprite == null)
{
//broken doors turn black if no broken sprite has been configured
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
weldSpritePos.Y = -weldSpritePos.Y;
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)

View File

@@ -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;
@@ -553,7 +496,11 @@ namespace Barotrauma.Items.Components
{
msg = msg.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameMain.Config.KeyBind(inputType).ToString());
}
Msg = msg;
DisplayMsg = msg;
}
else
{
DisplayMsg = Msg;
}
}
}

View File

@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
{
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
DeattachTimer = msg.ReadSingle();
deattachTimer = msg.ReadSingle();
if (deattachTimer >= DeattachDuration)
{
holdable.DeattachFromWall();

View File

@@ -25,6 +25,12 @@ namespace Barotrauma.Items.Components
DrawHUDBack, 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)),
style: "InnerFrame")
{
@@ -47,7 +53,7 @@ namespace Barotrauma.Items.Components
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
if (hasPower) hullInfoFrame.AddToGUIUpdateList();
hullInfoFrame.AddToGUIUpdateList();
}
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)
{
Hull mouseOnHull = null;
@@ -221,7 +248,7 @@ namespace Barotrauma.Items.Components
foreach (Submarine sub in subs)
{
if (sub.HullVertices == null) { continue; }
Rectangle worldBorders = sub.GetDockedBorders();
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);
}
}
}
private void GetLinkedHulls(Hull hull, List<Hull> linkedHulls)

View File

@@ -107,7 +107,10 @@ namespace Barotrauma.Items.Components
{
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)) },
TextManager.Get(warningTexts[i]), style: "IndicatorButton");
TextManager.Get(warningTexts[i]), style: "IndicatorButton")
{
CanBeFocused = false
};
var btnText = warningBtn.GetChild<GUITextBlock>();
btnText.Font = GUI.SmallFont;

View File

@@ -785,8 +785,9 @@ namespace Barotrauma.Items.Components
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 (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr)

View File

@@ -127,6 +127,18 @@ namespace Barotrauma.Items.Components
Stretch = true,
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),
TextManager.Get("SteeringMaintainPos"), font: GUI.SmallFont)
@@ -484,26 +496,14 @@ namespace Barotrauma.Items.Components
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);
Vector2 input = Vector2.Zero;
if (PlayerInput.KeyDown(InputType.Left))
{
input -= Vector2.UnitX;
}
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(InputType.Left)) { input -= Vector2.UnitX; }
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))
{
SteeringInput += input * deltaTime * 200;

View File

@@ -1,6 +1,7 @@
using Barotrauma.Particles;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -11,12 +12,21 @@ using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class RepairTool
#if DEBUG
: IDrawableComponent
#endif
{
public ParticleEmitter ParticleEmitter
{
get;
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> 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);
}
}
#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
}
}

View File

@@ -1,4 +1,6 @@
using Barotrauma.Particles;
using Barotrauma.Networking;
using Barotrauma.Particles;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
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");
foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
{
Skill skill = c.UserData as Skill;
if (skill == null) continue;
if (!(c.UserData is Skill skill)) continue;
GUITextBlock textBlock = (GUITextBlock)c;
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
}
}
}

View File

@@ -169,20 +169,6 @@ namespace Barotrauma.Items.Components
if (draggingConnected.Connect(this, !alreadyConnected, true))
{
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);
}
}

View File

@@ -181,7 +181,7 @@ namespace Barotrauma.Items.Components
Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>();
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))
{
combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength;

View File

@@ -237,13 +237,13 @@ namespace Barotrauma.Items.Components
railSprite?.Draw(spriteBatch,
drawPos,
Color.White,
item.SpriteColor,
rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth));
barrelSprite?.Draw(spriteBatch,
drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale,
Color.White,
drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale,
item.SpriteColor,
rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));

View File

@@ -301,7 +301,7 @@ namespace Barotrauma
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.
if (selectedSlot != null || draggingItem != null)
if (selectedSlot != null && !DraggingItemToWorld)
{
cam.Freeze = true;
}
@@ -609,8 +609,17 @@ namespace Barotrauma
if (selectedSlot == null)
{
draggingItem.Drop(Character.Controlled);
GUI.PlayUISound(GUISoundType.DropItem);
if (DraggingItemToWorld &&
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)
{
@@ -702,10 +711,27 @@ namespace Barotrauma
int iconSize = (int)(64 * GUI.Scale);
float scale = Math.Min(Math.Min(iconSize / sprite.size.X, iconSize / sprite.size.Y), 1.5f);
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,
sprite == draggingItem.Sprite ? draggingItem.GetSpriteColor() : draggingItem.GetInventoryIconColor(),
sprite.Draw(spriteBatch,
itemPos,
sprite == draggingItem.Sprite ? draggingItem.GetSpriteColor() : draggingItem.GetInventoryIconColor(),
scale: scale);
}
}

View File

@@ -10,12 +10,6 @@ namespace Barotrauma
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);
if (BackgroundFrame.Contains(PlayerInput.MousePosition))
{

View File

@@ -232,11 +232,11 @@ namespace Barotrauma
{
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
{
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);
}
}
}

View File

@@ -1,14 +1,13 @@
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Input;
using System.Linq;
using Lidgren.Network;
namespace Barotrauma
{
@@ -19,6 +18,8 @@ namespace Barotrauma
private List<Decal> decals = new List<Decal>();
private float serverUpdateDelay;
private float remoteWaterVolume, remoteOxygenPercentage;
private List<Vector3> remoteFireSources;
private bool networkUpdatePending;
private float networkUpdateTimer;
@@ -139,6 +140,10 @@ namespace Barotrauma
partial void UpdateProjSpecific(float deltaTime, Camera cam)
{
serverUpdateDelay -= deltaTime;
if (serverUpdateDelay <= 0.0f)
{
ApplyRemoteState();
}
if (networkUpdatePending)
{
@@ -547,18 +552,18 @@ namespace Barotrauma
public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime)
{
float newWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
float newOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
bool hasFireSources = message.ReadBoolean();
int fireSourceCount = 0;
List<Vector3> newFireSources = new List<Vector3>();
remoteFireSources = new List<Vector3>();
if (hasFireSources)
{
fireSourceCount = message.ReadRangedInteger(0, 16);
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),
message.ReadRangedSingle(0.0f, 1.0f, 8)));
@@ -567,41 +572,6 @@ namespace Barotrauma
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();
}

View File

@@ -247,15 +247,11 @@ namespace Barotrauma.Lights
public void Rotate(Vector2 origin, float amount)
{
Matrix rotationMatrix = Matrix.CreateRotationZ(amount);
Vector2[] newVerts = new Vector2[vertices.Length];
for (int i = 0; i < vertices.Length; i++)
{
newVerts[i] = Vector2.Transform(vertices[i].Pos - origin, rotationMatrix) + origin;
}
SetVertices(newVerts);
Matrix rotationMatrix =
Matrix.CreateTranslation(-origin.X, -origin.Y, 0.0f) *
Matrix.CreateRotationZ(amount) *
Matrix.CreateTranslation(origin.X, origin.Y, 0.0f);
SetVertices(vertices.Select(v => v.Pos).ToArray(), rotationMatrix);
}
private void CalculateDimensions()

View File

@@ -453,7 +453,10 @@ namespace Barotrauma.Lights
//raster pattern on top of everything
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();
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"];

View File

@@ -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)
{
zoom += PlayerInput.ScrollWheelSpeed / 1000.0f;
zoom = MathHelper.Clamp(zoom, 1.0f, 4.0f);
if (PlayerInput.MidButtonHeld())
{
drawOffset += PlayerInput.MouseSpeed / zoom;
}
if (PlayerInput.MidButtonHeld()) { drawOffset += PlayerInput.MouseSpeed / zoom; }
#if DEBUG
if (PlayerInput.DoubleClicked() && highlightedLocation != null)
{
@@ -551,11 +559,6 @@ namespace Barotrauma
null, connectionColor * MathHelper.Clamp(a, 0.1f, 0.5f), MathUtils.VectorToAngle(end - start),
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)
{

View File

@@ -44,7 +44,7 @@ namespace Barotrauma
drawRect.Location -= Submarine.MainSub.Position.ToPoint();
}
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)
{

View File

@@ -75,7 +75,10 @@ namespace Barotrauma
};
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);
}
@@ -292,8 +295,7 @@ namespace Barotrauma
{
if (damageEffect != null)
{
float newCutoff = Sections[i].damage > 0 ?
MathHelper.Lerp(0.2f, 0.65f, Sections[i].damage / Prefab.Health) : 0.0f;
float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / Prefab.Health);
if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f || color != Submarine.DamageEffectColor)
{
@@ -360,6 +362,7 @@ namespace Barotrauma
-Bodies[i].Rotation, Color.White);
}
}
AiTarget?.Draw(spriteBatch);
}
}

View File

@@ -35,7 +35,7 @@ namespace Barotrauma
public override void DrawPlacing(SpriteBatch spriteBatch, Rectangle placeRect, float scale = 1.0f)
{
// 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);
}
}
}

View File

@@ -141,8 +141,23 @@ namespace Barotrauma.Networking
{
OnClicked = (btn, userdata) =>
{
if (!permissions.HasFlag(ClientPermissions.ManageRound)) return false;
RequestRoundEnd();
if (!permissions.HasFlag(ClientPermissions.ManageRound)) { return false; }
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;
},
Visible = false
@@ -622,7 +637,7 @@ namespace Barotrauma.Networking
if (gameStarted) SetRadioButtonColor();
if (ShowNetStats)
if (ShowNetStats && client?.ServerConnection != null)
{
netStats.AddValue(NetStats.NetStatType.ReceivedBytes, client.ServerConnection.Statistics.ReceivedBytes);
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 + ").";
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(""));
yield return CoroutineStatus.Failure;
}
@@ -1381,9 +1396,6 @@ namespace Barotrauma.Networking
case ServerNetObject.CLIENT_LIST:
ReadClientList(inc);
break;
case ServerNetObject.CLIENT_LIST:
ReadClientList(inc);
break;
case ServerNetObject.CHAT_MESSAGE:
ChatMessage.ClientRead(inc);
break;
@@ -1439,7 +1451,11 @@ namespace Barotrauma.Networking
break;
case ServerNetObject.ENTITY_EVENT:
case ServerNetObject.ENTITY_EVENT_INITIAL:
if (!entityEventManager.Read(objHeader, inc, sendingTime, entities)) { break; }
if (!entityEventManager.Read(objHeader, inc, sendingTime, entities))
{
eventReadFailed = true;
break;
}
break;
case ServerNetObject.CHAT_MESSAGE:
ChatMessage.ClientRead(inc);
@@ -1703,6 +1719,8 @@ namespace Barotrauma.Networking
SaveUtil.LoadGame(GameMain.GameSession.SavePath, GameMain.GameSession);
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
//(as there may have been campaign updates after the save file was created)
campaign.LastUpdateID--;
@@ -1720,7 +1738,7 @@ namespace Barotrauma.Networking
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);
}
@@ -1989,7 +2007,8 @@ namespace Barotrauma.Networking
msg.Write(true); msg.WritePadBits();
msg.Write(savePath);
msg.Write(mapSeed);
msg.Write(sub.FilePath);
msg.Write(sub.Name);
msg.Write(sub.MD5Hash.Hash);
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);

View File

@@ -62,7 +62,7 @@ namespace Barotrauma
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);
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);
}
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))
{
OnSelected = SelectSaveFile
@@ -274,7 +269,9 @@ namespace Barotrauma
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")
{
UserData = saveFile
@@ -282,25 +279,38 @@ namespace Barotrauma
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform),
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", "");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft),
text: submarineName, font: GUI.SmallFont)
if (!isMultiplayer)
{
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),
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()
{
if (isMultiplayer) return;

View File

@@ -457,11 +457,14 @@ namespace Barotrauma
{
IgnoreLayoutGroups = true,
OnClicked = (GUIButton btn, object obj) => { StartRound?.Invoke(); return true; },
Enabled = true,
Visible = GameMain.Client == null ||
GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)
Enabled = true
};
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);

View File

@@ -118,6 +118,19 @@ namespace Barotrauma
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()
{
base.Deselect();
@@ -128,15 +141,7 @@ namespace Barotrauma
isEndlessRunner = false;
if (character != null)
{
AnimParams.ForEach(a => a.Reset(true));
RagdollParams.Reset(true);
RagdollParams.ClearHistory();
CurrentAnimation.ClearHistory();
if (!character.Removed)
{
character.Remove();
}
character = null;
Reset();
}
GameMain.World.ProcessChanges();
}
@@ -393,6 +398,12 @@ namespace Barotrauma
}
if (!isFreezed)
{
if (character.AnimController.Invalid)
{
Reset();
SpawnCharacter(currentCharacterConfig);
}
Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position);
Submarine.MainSub.Update((float)deltaTime);
@@ -1215,7 +1226,7 @@ namespace Barotrauma
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();
if (contentPackage == null)
@@ -1228,23 +1239,22 @@ namespace Barotrauma
#if !DEBUG
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;
}
#endif
string speciesName = name;
string mainFolder = $"Content/Characters/{speciesName}";
// 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)))
{
// Create the config file
XElement mainElement = new XElement("Character",
new XAttribute("name", speciesName),
new XAttribute("humanoid", isHumanoid),
new XElement("ragdolls"),
new XElement("animations"),
new XElement("ragdolls", new XAttribute("folder", Path.Combine(mainFolder, $"Ragdolls/").Replace(@"\", @"/"))),
new XElement("animations", new XAttribute("folder", Path.Combine(mainFolder, $"Animations/").Replace(@"\", @"/"))),
new XElement("health"),
new XElement("ai"));
XDocument doc = new XDocument(mainElement);
@@ -1259,13 +1269,13 @@ namespace Barotrauma
DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path));
}
// Ragdoll
string ragdollFolder = RagdollParams.GetDefaultFolder(speciesName);
string ragdollFolder = RagdollParams.GetFolder(speciesName);
string ragdollPath = RagdollParams.GetDefaultFile(speciesName);
RagdollParams ragdollParams = isHumanoid
? RagdollParams.CreateDefault<HumanRagdollParams>(ragdollPath, speciesName, ragdollConfig)
: RagdollParams.CreateDefault<FishRagdollParams>(ragdollPath, speciesName, ragdollConfig) as RagdollParams;
// Animations
string animFolder = AnimationParams.GetDefaultFolder(speciesName);
string animFolder = AnimationParams.GetFolder(speciesName);
foreach (AnimationType animType in Enum.GetValues(typeof(AnimationType)))
{
if (animType != AnimationType.NotDefined)
@@ -1869,7 +1879,7 @@ namespace Barotrauma
#if !DEBUG
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;
}
#endif
@@ -1884,7 +1894,7 @@ namespace Barotrauma
#if !DEBUG
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;
}
#endif
@@ -1956,7 +1966,7 @@ namespace Barotrauma
#if !DEBUG
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();
return false;
}
@@ -2089,7 +2099,7 @@ namespace Barotrauma
#if !DEBUG
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();
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)
{
rotationOffset -= MathHelper.ToRadians(RagdollParams.SpritesheetOrientation);
rotationOffset += MathHelper.ToRadians(RagdollParams.SpritesheetOrientation);
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 =>
{
@@ -4652,7 +4662,7 @@ namespace Barotrauma
LimbXElements.Values,
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);
}

View File

@@ -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) },
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)
})
@@ -61,7 +61,7 @@ namespace Barotrauma
};
// === 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")
{
@@ -107,7 +107,7 @@ namespace Barotrauma
};
// === 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")
{
@@ -152,7 +152,7 @@ namespace Barotrauma
};
// === 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")
{
@@ -209,7 +209,7 @@ namespace Barotrauma
}
// === 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")
{
@@ -219,7 +219,7 @@ namespace Barotrauma
//spacing
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))
{
@@ -241,7 +241,7 @@ namespace Barotrauma
//debug button for quickly starting a new round
#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)
{
IgnoreLayoutGroups = true,
@@ -255,7 +255,7 @@ namespace Barotrauma
#endif
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")
{
@@ -270,16 +270,21 @@ namespace Barotrauma
SetupButtons(buttons);
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 maxSize = new Point(900, 600);
var anchor = Anchor.Center;
var pivot = Pivot.Center;
menuTabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length + 1];
var maxSize = new Point(2000, 1500);
var anchor = Anchor.CenterRight;
var pivot = Pivot.CenterRight;
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);
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);
campaignSetupUI = new CampaignSetupUI(false, paddedNewGame, paddedLoadGame, Submarine.SavedSubmarines)
@@ -290,13 +295,13 @@ namespace Barotrauma
var hostServerScale = new Vector2(0.7f, 1.0f);
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();
//----------------------------------------------------------------------
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
var tutorialList = new GUIListBox(
@@ -377,6 +382,7 @@ namespace Barotrauma
return false;
}
GameMain.Config.ResetSettingsFrame();
selectedTab = (Tab)obj;
switch (selectedTab)
@@ -389,8 +395,9 @@ namespace Barotrauma
campaignSetupUI.UpdateLoadMenu();
break;
case Tab.Settings:
GameMain.Config.ResetSettingsFrame();
menuTabs[(int)Tab.Settings] = GameMain.Config.SettingsFrame;
menuTabs[(int)Tab.Settings].RectTransform.ClearChildren();
GameMain.Config.SettingsFrame.RectTransform.Parent = menuTabs[(int)Tab.Settings].RectTransform;
GameMain.Config.SettingsFrame.RectTransform.RelativeSize = Vector2.One;
break;
case Tab.JoinServer:
GameMain.ServerListScreen.Select();

View File

@@ -233,7 +233,8 @@ namespace Barotrauma
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;
//lastUpdateID++;
@@ -726,6 +727,12 @@ namespace Barotrauma
spectateButton.Visible = GameMain.Client.GameStarted;
ReadyToStartBox.Visible = !GameMain.Client.GameStarted;
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);
}
else
@@ -847,9 +854,9 @@ namespace Barotrauma
if (campaignUI?.StartButton != null)
{
campaignUI.StartButton.Visible =
GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign);
campaignUI.StartButton.Visible = !GameMain.Client.GameStarted &&
(GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign));
}
}
@@ -860,18 +867,21 @@ namespace Barotrauma
spectateButton.Enabled = true;
}
public void SetCampaignCharacterInfo(CharacterInfo characterInfo)
{
if (CampaignCharacterDiscarded) return;
campaignCharacterInfo = characterInfo;
if (campaignCharacterInfo != null)
public void SetCampaignCharacterInfo(CharacterInfo newCampaignCharacterInfo)
{
if (newCampaignCharacterInfo != 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);
}
}

View File

@@ -337,7 +337,24 @@ namespace Barotrauma
var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform), TextManager.Get("ShowLighting"))
{
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"))
{
@@ -564,6 +581,9 @@ namespace Barotrauma
MapEntityPrefab.Selected = null;
saveFrame = null;
loadFrame = null;
MapEntity.DeselectAll();
MapEntity.SelectionGroups.Clear();
@@ -766,6 +786,20 @@ namespace Barotrauma
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)
{
Submarine.MainSub.RequiredContentPackages.Add(contentPackage.Name);
@@ -2040,6 +2074,10 @@ namespace Barotrauma
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);
}

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.9.6")]
[assembly: AssemblyFileVersion("0.8.9.6")]
[assembly: AssemblyVersion("0.8.9.8")]
[assembly: AssemblyFileVersion("0.8.9.8")]

View File

@@ -20,8 +20,10 @@ namespace Barotrauma
if (Job != null)
{
msg.Write(Job.Prefab.Identifier);
msg.Write((byte)Job.Skills.Count);
foreach (Skill skill in Job.Skills)
{
msg.Write(skill.Identifier);
msg.Write(skill.Level);
}
}

View File

@@ -15,6 +15,8 @@ namespace Barotrauma
private bool networkUpdateSent;
private double LastInputTime;
public float GetPositionUpdateInterval(Client recipient)
{
if (!Enabled) { return 1000.0f; }
@@ -61,6 +63,15 @@ namespace Barotrauma
else if (memInput.Count == 0)
{
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
{
@@ -190,6 +201,7 @@ namespace Barotrauma
networkUpdateID = (ushort)(networkUpdateID - i)
};
memInput.Insert(i, newMem);
LastInputTime = Timing.TotalTime;
}
}

View File

@@ -11,11 +11,11 @@ namespace Barotrauma
{
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;
GameMain.GameSession = new GameSession(new Submarine(subName, ""), savePath,
GameMain.GameSession = new GameSession(new Submarine(subPath, ""), savePath,
GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign"));
var campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode);
campaign.GenerateMap(seed);
@@ -251,6 +251,7 @@ namespace Barotrauma
}
lastSaveID++;
DebugConsole.Log("Campaign saved, save ID " + lastSaveID);
}
}
}

View File

@@ -1,9 +1,5 @@
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
namespace Barotrauma.Items.Components
{
@@ -14,5 +10,16 @@ namespace Barotrauma.Items.Components
//let the clients know the initial deterioration delay
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);
}
}
}

View File

@@ -20,6 +20,16 @@ namespace Barotrauma
partial void UpdateProjSpecific(float deltaTime, Camera cam)
{
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%
//or if oxygen percentage has changed by 5%
if (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f ||
@@ -31,8 +41,8 @@ namespace Barotrauma
GameMain.NetworkMember.CreateEntityEvent(this);
lastSentVolume = waterVolume;
lastSentOxygen = OxygenPercentage;
sendUpdateTimer = NetworkUpdateInterval;
}
sendUpdateTimer = NetConfig.HullUpdateInterval;
}
}
}

View File

@@ -46,7 +46,7 @@ namespace Barotrauma.Networking
//when was a specific entity event last sent to the client
// 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
// key = entity id, value = NetTime.Now when sending

View File

@@ -187,7 +187,7 @@ namespace Barotrauma.Networking
transfer.WaitTimer -= deltaTime;
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;
@@ -202,15 +202,6 @@ namespace Barotrauma.Networking
{
message = peer.CreateMessage();
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)
//we don't need to send anything, the client can just read the file directly

View File

@@ -10,6 +10,7 @@ using System.Text;
using System.IO.Compression;
using System.IO;
using Barotrauma.Steam;
using System.Xml.Linq;
namespace Barotrauma.Networking
{
@@ -715,23 +716,25 @@ namespace Barotrauma.Networking
{
string savePath = 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(
TextManager.Get("CampaignStartFailedSubNotFound").Replace("[subpath]", subPath),
TextManager.Get("CampaignStartFailedSubNotFound").Replace("[subname]", subName),
connectedClient, ChatMessageType.MessageBox);
}
else
{
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, subPath, seed);
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, matchingSub.FilePath, seed);
}
}
else
{
string saveName = inc.ReadString();
if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.LoadCampaign(saveName);
}
break;
@@ -811,7 +814,16 @@ namespace Barotrauma.Networking
Log(c.Name + " has reported an error: " + errorStr, ServerLog.MessageType.Error);
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)
@@ -1028,7 +1040,7 @@ namespace Barotrauma.Networking
if (command == ClientPermissions.ManageRound && inc.PeekBoolean() &&
GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign)
{
if (!mpCampaign.AllowedToEndRound(sender.Character))
if (!mpCampaign.AllowedToEndRound(sender.Character) && !sender.HasPermission(command))
{
return;
}
@@ -1114,9 +1126,22 @@ namespace Barotrauma.Networking
UInt16 modeIndex = inc.ReadUInt16();
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();
msg.Write((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO);
string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer);
msg.Write((UInt16)saveFiles.Count());
foreach (string saveFile in saveFiles)
{
@@ -1201,6 +1226,7 @@ namespace Barotrauma.Networking
ClientWriteLobby(c);
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign &&
GameMain.NetLobbyScreen.SelectedMode == campaign.Preset &&
NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave))
{
//already sent an up-to-date campaign save
@@ -1335,10 +1361,6 @@ namespace Barotrauma.Networking
WriteClientList(c, outmsg);
clientListBytes = outmsg.LengthBytes - clientListBytes;
int eventManagerBytes = outmsg.LengthBytes;
entityEventManager.Write(c, outmsg, out List<NetEntityEvent> sentEvents);
eventManagerBytes = outmsg.LengthBytes - eventManagerBytes;
int chatMessageBytes = outmsg.LengthBytes;
WriteChatMessages(outmsg, c);
chatMessageBytes = outmsg.LengthBytes - chatMessageBytes;
@@ -1348,7 +1370,8 @@ namespace Barotrauma.Networking
while (!c.NeedsMidRoundSync && c.PendingPositionUpdates.Count > 0)
{
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();
continue;
@@ -1387,25 +1410,55 @@ namespace Barotrauma.Networking
errorMsg +=
" Client list size: " + clientListBytes + " bytes\n" +
" Chat message size: " + chatMessageBytes + " bytes\n" +
" Event size: " + eventManagerBytes + " bytes\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);
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);
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)
@@ -1492,7 +1545,8 @@ namespace Barotrauma.Networking
}
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.WritePadBits();

View File

@@ -164,7 +164,7 @@ namespace Barotrauma.Networking
if (connectedClient != null)
{
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())
{
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);
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)
{
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);
return;
}
@@ -376,7 +376,7 @@ namespace Barotrauma.Networking
{
List<string> packageStrs = new List<string>();
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);
return;
}
@@ -399,7 +399,7 @@ namespace Barotrauma.Networking
if (incompatiblePackages.Count == 1)
{
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);
return;
}
@@ -408,7 +408,7 @@ namespace Barotrauma.Networking
List<string> packageStrs = new List<string>();
incompatiblePackages.ForEach(cp => packageStrs.Add(GetPackageStr2(cp)));
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);
return;
}
@@ -494,13 +494,21 @@ namespace Barotrauma.Networking
}
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)
{
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 != null)
{

View File

@@ -228,7 +228,7 @@ namespace Barotrauma.Networking
GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected old event "
+ (c.LastRecvEntityEventID + 1).ToString() +
" (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");
}
);
@@ -242,7 +242,7 @@ namespace Barotrauma.Networking
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);
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");
});
}
@@ -251,7 +251,7 @@ namespace Barotrauma.Networking
var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);
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");
}
@@ -305,19 +305,7 @@ namespace Barotrauma.Networking
}
//too many events for one packet
if (eventsToSync.Count > MaxEventsPerWrite)
{
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)
{
if (eventsToSync.Count > 200 && !client.NeedsMidRoundSync)
{
@@ -350,7 +338,7 @@ namespace Barotrauma.Networking
msg.Write(client.UnreceivedEntityEventCount);
msg.Write(client.FirstNewEventID);
Write(msg, eventsToSync, client);
Write(msg, eventsToSync, out sentEvents, client);
}
else
{
@@ -387,10 +375,10 @@ namespace Barotrauma.Networking
for (int i = startIndex; i < eventList.Count; i++)
{
//find the first event that hasn't been sent in 1.5 * roundtriptime or at all
client.EntityEventLastSent.TryGetValue(eventList[i].ID, out float lastSent);
//find the first event that hasn't been sent in roundtriptime or at all
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))
{
@@ -416,7 +404,7 @@ namespace Barotrauma.Networking
}
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);
client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count;

View File

@@ -337,37 +337,56 @@ namespace Barotrauma.Networking
continue;
}
string permissionsStr = clientElement.GetAttributeString("permissions", "");
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.");
continue;
}
List<DebugConsole.Command> permittedCommands = new List<DebugConsole.Command>();
if (permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands))
{
foreach (XElement commandElement in clientElement.Elements())
string presetName = clientElement.GetAttributeString("preset", "");
PermissionPreset preset = PermissionPreset.List.Find(p => p.Name == presetName);
if (preset == null)
{
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);
DebugConsole.ThrowError("Failed to restore saved permissions to the client \"" + clientName + "\". Permission preset \"" + presetName + "\" not found.");
return;
}
else
{
permissions = preset.Permissions;
permittedCommands = preset.PermittedCommands.ToList();
}
}
@@ -440,9 +459,14 @@ namespace Barotrauma.Networking
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",
new XAttribute("name", clientPermission.Name),
new XAttribute("permissions", clientPermission.Permissions.ToString()));
new XAttribute("name", clientPermission.Name));
if (clientPermission.SteamID > 0)
{
@@ -453,14 +477,21 @@ namespace Barotrauma.Networking
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);
}

View File

@@ -56,6 +56,7 @@
<Character file="Content/Characters/Husk/Husk.xml" />
<Character file="Content/Characters/Humanhusk/Humanhusk.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/Legacymoloch/Legacymoloch.xml" />
<Character file="Content/Characters/Mudraptor/Mudraptor.xml" />
@@ -70,10 +71,11 @@
<Outpost file="Content/Map/Outposts/Outpost.sub" />
<Submarine file="Submarines/Orca.sub" />
<Submarine file="Submarines/Typhon.sub" />
<Submarine file="Submarines/Muikku.sub" />
<Submarine file="Submarines/Selkie.sub" />
<Submarine file="Submarines/Bunyip.sub" />
<Submarine file="Submarines/Humpback.sub" />
<Submarine file="Submarines/Dugong.sub" />
<Submarine file="Submarines/PAX.sub" />
<Submarine file="Submarines/Remora.sub" />
<Submarine file="Submarines/RemoraDrone.sub" />
<Text file="Content/Texts/EnglishVanilla.xml" />

View File

@@ -13,6 +13,13 @@
<Command name="autorestart"/>
<Command name="autorestartinterval"/>
<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="kick"/>
<Command name="kickid"/>
@@ -28,6 +35,8 @@
<Command name="autorestart"/>
<Command name="autorestartinterval"/>
<Command name="autorestarttimer"/>
<Command name="botcount"/>
<Command name="botspawnmode"/>
<Command name="difficulty"/>
<Command name="kick"/>
<Command name="kickid"/>

View File

@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<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\Legacycrawler\" />
<Folder Include="$(MSBuildThisFileDirectory)Content\Characters\Endworm\Animations\" />
@@ -45,12 +45,27 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Ragdolls\CarrierDefaultRagdoll.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</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">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Ragdolls\CharybdisDefaultRagdoll.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</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">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -342,6 +357,36 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Effects\waterbump.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</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">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -897,13 +942,13 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\Animations\CarrierSwimSlow.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\charybdis.png">
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\charybdis.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimFast.xml">
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Animations\LegacycharybdisSwimFast.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\Animations\CharybdisSwimSlow.xml">
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\Animations\LegacycharybdisSwimSlow.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\Animations\CoelanthSwimFast.xml">
@@ -1955,6 +2000,9 @@
<Content Include="$(MSBuildThisFileDirectory)serversettings.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Misc\GuitarClown.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\CARRIER_alarmLoop.ogg">
@@ -2356,7 +2404,7 @@
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Mudraptor\MUDRAPTOR_idle3.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Charybdis\charybdisattack.ogg">
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Legacycharybdis\charybdisattack.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Coelanth\attack1.ogg">
@@ -3114,7 +3162,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Muikku.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Orca.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -3134,6 +3182,9 @@
<None Include="$(MSBuildThisFileDirectory)Submarines\RemoraDrone.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Selkie.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Typhon.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@@ -115,7 +115,7 @@ namespace Barotrauma
SonarLabel = element.GetAttributeString("sonarlabel", "");
}
public AITarget(Entity e)
public AITarget(Entity e, float sightRange = -1, float soundRange = 0)
{
Entity = e;
if (sightRange < 0)

View File

@@ -193,7 +193,7 @@ namespace Barotrauma
// is not attached or is attached to something else
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);
}

View File

@@ -115,7 +115,8 @@ namespace Barotrauma
unreachable.Add(goToObjective.Target as Hull);
}
goToObjective = null;
SteeringManager.SteeringWander();
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
//SteeringManager.SteeringWander();
}
}
else if (currentHull != null)

View File

@@ -99,7 +99,8 @@ namespace Barotrauma
FindTargetItem();
if (targetItem == null || moveToTarget == null)
{
SteeringManager.SteeringWander();
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
//SteeringManager.SteeringWander();
return;
}

View File

@@ -98,63 +98,67 @@ namespace Barotrauma
PathSteering.Reset();
return;
}
if (standStillTimer < -walkDuration)
{
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);
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)
{
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<float> hullWeights = new List<float>(20);

View File

@@ -369,9 +369,18 @@ namespace Barotrauma
return;
}
float movementAngle = MathUtils.VectorToAngle(movement) - MathHelper.PiOver2;
float mainLimbAngle = (MainLimb.type == LimbType.Torso ? TorsoAngle.Value : HeadAngle.Value) * Dir;
Vector2 transformedMovement = reverse ? -movement : movement;
float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
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)
{
movementAngle += MathHelper.TwoPi;
@@ -379,7 +388,7 @@ namespace Barotrauma
while (MainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
{
movementAngle -= MathHelper.TwoPi;
}
}
if (CurrentSwimParams.RotateTowardsMovement)
{
@@ -403,7 +412,6 @@ namespace Barotrauma
if (TailAngle.HasValue)
{
Limb tail = GetLimb(LimbType.Tail);
//tail?.body.SmoothRotate(movementAngle + TailAngle.Value * Dir, TailTorque);
if (tail != null)
{
SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, MainLimb, TailTorque);
@@ -413,6 +421,10 @@ namespace Barotrauma
else
{
movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
if (reverse)
{
movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
}
if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
{
Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque);
@@ -442,7 +454,7 @@ namespace Barotrauma
var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude);
if (waveLength > 0 && waveAmplitude > 0)
{
WalkPos -= movement.Length() / Math.Abs(waveLength);
WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
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);
}
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)

View File

@@ -99,7 +99,7 @@ namespace Barotrauma
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";
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);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")

View File

@@ -79,7 +79,7 @@ namespace Barotrauma
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);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")

View File

@@ -1035,8 +1035,6 @@ namespace Barotrauma
CheckValidity();
CheckValidity();
UpdateNetPlayerPosition(deltaTime);
CheckDistFromCollider();
UpdateCollisionCategories();
@@ -1297,17 +1295,42 @@ namespace Barotrauma
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)
{
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 bodyName = body.UserData is Limb ? "Limb" : "Collider";
@@ -1359,7 +1382,7 @@ namespace Barotrauma
limb.body.ResetDynamics();
}
SetInitialLimbPositions();
return;
return false;
}
return true;
}

View File

@@ -28,6 +28,7 @@ namespace Barotrauma
public enum AIBehaviorAfterAttack
{
FallBack,
FallBackUntilCanAttack,
PursueIfCanAttack,
Pursue
}
@@ -81,6 +82,9 @@ namespace Barotrauma
[Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")]
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.")]
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.")]
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)]
public float StructureDamage { get; private set; }
@@ -182,7 +189,7 @@ namespace Barotrauma
public readonly List<Affliction> Afflictions = new List<Affliction>();
/// <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>
public List<PropertyConditional> Conditionals { get; private set; } = new List<PropertyConditional>();
@@ -444,8 +451,10 @@ namespace Barotrauma
public void SetCoolDown()
{
CoolDownTimer = CoolDown;
SecondaryCoolDownTimer = SecondaryCoolDown;
float randomFraction = CoolDown * CoolDownRandomFactor;
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()

View File

@@ -810,6 +810,7 @@ namespace Barotrauma
public void LoadHeadAttachments()
{
if (Info == null) { return; }
if (AnimController == null) { return; }
var head = AnimController.GetLimb(LimbType.Head);
if (head == null) { return; }
@@ -1112,13 +1113,15 @@ namespace Barotrauma
ViewTarget = null;
if (!AllowInput) return;
Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition;
if (Controlled == this)
if (Controlled == this || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer))
{
SmoothedCursorPosition = cursorPosition;
}
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);
SmoothedCursorPosition = cursorPosition - smoothedCursorDiff;
}
@@ -1185,6 +1188,10 @@ namespace Barotrauma
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.F))
{
AnimController.ReleaseStuckLimbs();
if (AIController != null && AIController is EnemyAIController enemyAI)
{
enemyAI.LatchOntoAI?.DeattachFromBody();
}
}
#endif
@@ -1264,15 +1271,7 @@ namespace Barotrauma
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 (IsKeyDown(InputType.Use)) SelectedConstruction.Use(deltaTime, this);
@@ -1650,7 +1649,8 @@ namespace Barotrauma
#if CLIENT
if (isLocalPlayer)
{
if (GUI.MouseOn == null && !CharacterInventory.IsMouseOnInventory())
if (GUI.MouseOn == null &&
(!CharacterInventory.IsMouseOnInventory() || CharacterInventory.DraggingItemToWorld))
{
if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen)
{
@@ -1665,10 +1665,12 @@ namespace Barotrauma
focusedItem = null;
}
findFocusedTimer -= deltaTime;
}
}
#endif
//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 isControlled = Controlled == this;
@@ -1679,6 +1681,19 @@ namespace Barotrauma
float minDist = float.PositiveInfinity;
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)
{
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();
}

View File

@@ -804,6 +804,12 @@ namespace Barotrauma
var newItem = Item.Load(itemElement, inventory.Owner.Submarine, createNetworkEvent: true);
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 });
if (!slotIndices.Any())
{

View File

@@ -153,7 +153,10 @@ namespace Barotrauma
//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 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 DamageOverlayAlpha;
@@ -257,6 +260,8 @@ namespace Barotrauma
ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f));
MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f);
ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f));
DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f);
BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f);

View File

@@ -460,6 +460,7 @@ namespace Barotrauma
{
affliction.Strength = 0.0f;
}
CalculateVitality();
}
private void AddLimbAffliction(Limb limb, Affliction newAffliction)

View File

@@ -520,14 +520,15 @@ namespace Barotrauma
// Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example.
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;
}
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;
}
}

View File

@@ -103,12 +103,6 @@ namespace Barotrauma
if (missionType == MissionType.Random)
{
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)
{
@@ -124,6 +118,11 @@ namespace Barotrauma
{
allowedMissions.RemoveAll(m => !m.IsAllowed(locations[0], locations[1]));
}
if (allowedMissions.Count == 0)
{
return null;
}
int probabilitySum = allowedMissions.Sum(m => m.Commonness);
int randomNumber = rand.NextInt32() % probabilitySum;

View File

@@ -139,7 +139,7 @@ namespace Barotrauma
MTRandom rand = new MTRandom(ToolBox.StringToInt(seed));
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;
foreach (DockingPort port in DockingPort.List)
{
if (port.IsHorizontal) { continue; }
if (port.IsHorizontal || port.Docked) { continue; }
if (port.Item.Submarine == level.StartOutpost)
{
outPostPort = port;

View File

@@ -880,8 +880,7 @@ namespace Barotrauma.Items.Components
List<MapEntity> linked = new List<MapEntity>(item.linkedTo);
foreach (MapEntity entity in linked)
{
Item linkedItem = entity as Item;
if (linkedItem == null) { continue; }
if (!(entity is Item linkedItem)) { continue; }
var dockingPort = linkedItem.GetComponent<DockingPort>();
if (dockingPort != null)

View File

@@ -426,11 +426,11 @@ namespace Barotrauma.Items.Components
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.IsKeyDown(InputType.Aim)) return false;
if (!CanBeAttached()) return false;
if (!character.IsKeyDown(InputType.Aim)) { return false; }
if (!CanBeAttached()) { return false; }
#if SERVER
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);
}
#endif
item.Drop(character);
}
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
if (character != null) { item.Drop(character); }
AttachToWall();
}
return true;
@@ -550,7 +555,7 @@ namespace Barotrauma.Items.Components
public override void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
base.ServerWrite(msg, c, extraData);
if (!attachable || body == null) return;
if (!attachable || body == null) { return; }
msg.Write(Attached);
msg.Write(body.SimPosition.X);

View File

@@ -2,12 +2,15 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class LevelResource : ItemComponent, IServerSerializable
{
private float lastSentDeattachTimer;
[Serialize(1.0f, false)]
public float DeattachDuration
{
@@ -21,12 +24,29 @@ namespace Barotrauma.Items.Components
get { return deattachTimer; }
set
{
deattachTimer = Math.Max(0.0f, value);
//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();
}
#endif
}
}
@@ -67,7 +87,10 @@ namespace Barotrauma.Items.Components
return;
}
holdable.Reattachable = false;
holdable.PickingTime = float.MaxValue;
if (requiredItems.Any())
{
holdable.PickingTime = float.MaxValue;
}
var body = item.body ?? holdable.Body;

View File

@@ -17,6 +17,8 @@ namespace Barotrauma.Items.Components
private readonly List<string> fixableEntities;
private Vector2 pickedPosition;
private float activeTimer;
private Vector2 debugRayStartPos, debugRayEndPos;
[Serialize(0.0f, false)]
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;
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)
{
FixBody(user, deltaTime, degreeOfSuccess, body);
@@ -164,7 +166,7 @@ namespace Barotrauma.Items.Components
}
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)
@@ -247,6 +249,7 @@ namespace Barotrauma.Items.Components
var levelResource = targetItem.GetComponent<LevelResource>();
if (levelResource != null && levelResource.IsActive &&
levelResource.requiredItems.Any() &&
levelResource.HasRequiredItems(user, addMessage: false))
{
levelResource.DeattachTimer += deltaTime;

View File

@@ -104,8 +104,8 @@ namespace Barotrauma.Items.Components
#if SERVER
GameServer.Log(picker.LogName + " threw " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif
item.Drop(picker, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
Character thrower = picker;
item.Drop(thrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f);
ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector*10.0f);

View File

@@ -189,7 +189,7 @@ namespace Barotrauma.Items.Components
{
get { return name; }
}
[Editable, Serialize("", true)]
public string Msg
{
@@ -203,12 +203,6 @@ namespace Barotrauma.Items.Components
set;
}
public AITarget AITarget
{
get;
private set;
}
public AITarget AITarget
{
get;
@@ -253,7 +247,7 @@ namespace Barotrauma.Items.Components
{
DebugConsole.ThrowError("Invalid pick key in " + element + "!", e);
}
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
ParseMsg();

View File

@@ -155,13 +155,14 @@ namespace Barotrauma.Items.Components
{
if (item.Container != null) { return false; }
if (AutoInteractWithContained)
if (AutoInteractWithContained && character.SelectedConstruction == null)
{
foreach (Item contained in Inventory.Items)
{
if (contained == null) continue;
if (contained.TryInteract(character))
{
character.FocusedItem = contained;
return false;
}
}
@@ -178,6 +179,7 @@ namespace Barotrauma.Items.Components
if (contained == null) continue;
if (contained.TryInteract(picker))
{
picker.FocusedItem = contained;
return true;
}
}

View File

@@ -90,7 +90,7 @@ namespace Barotrauma.Items.Components
Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f);
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
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition);

View File

@@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components
private bool shutDown;
const float AIUpdateInterval = 1.0f;
const float AIUpdateInterval = 0.2f;
private float aiUpdateTimer;
private Character lastUser;
@@ -209,10 +209,10 @@ namespace Barotrauma.Items.Components
allowedFissionRate = Vector2.Lerp(new Vector2(20, AvailableFuel), new Vector2(10, AvailableFuel), degreeOfSuccess);
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;
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);
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)
{
if (temperature > allowedTemperature.Y)
@@ -367,6 +409,11 @@ namespace Barotrauma.Items.Components
targetFissionRate = Math.Min(targetFissionRate + speed * 2 * deltaTime, 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)
@@ -441,12 +488,18 @@ namespace Barotrauma.Items.Components
}
}
//we need more fuel
if (-currPowerConsumption < load * 0.5f && prevAvailableFuel <= 0.0f)
if (aiUpdateTimer > 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>())
{
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) =>
{
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);
aiUpdateTimer = AIUpdateInterval;
return false;
}
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)
{
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
@@ -506,7 +554,7 @@ namespace Barotrauma.Items.Components
{
AutoTemp = false;
unsentChanges = true;
UpdateAutoTemp(2.0f + degreeOfSuccess * 5.0f, 1.0f);
UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
}
#if CLIENT
onOffSwitch.BarScroll = 0.0f;

View File

@@ -153,7 +153,7 @@ namespace Barotrauma.Items.Components
}
continue;
}
if (!pt.IsActive) { continue; }
if (!pt.IsActive || !pt.CanTransfer) { continue; }
gridLoad += pt.PowerLoad;
gridPower -= pt.CurrPowerConsumption;
@@ -209,9 +209,9 @@ namespace Barotrauma.Items.Components
Charge -= CurrPowerOutput / 3600.0f;
}
item.SendSignal(0, Charge.ToString(), "charge", null);
item.SendSignal(0, ((Charge / capacity) * 100).ToString(), "charge_%", null);
item.SendSignal(0, ((RechargeSpeed / maxRechargeSpeed) * 100).ToString(), "charge_rate", null);
item.SendSignal(0, ((int)Charge).ToString(), "charge", null);
item.SendSignal(0, ((int)((Charge / capacity) * 100)).ToString(), "charge_%", null);
item.SendSignal(0, ((int)((RechargeSpeed / maxRechargeSpeed) * 100)).ToString(), "charge_rate", null);
foreach (Pair<Powered, Connection> connected in directlyConnected)
{

View File

@@ -180,7 +180,8 @@ namespace Barotrauma.Items.Components
#endif
//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 (!item.Repairables.Any() || !CanBeOverloaded) continue;
@@ -319,7 +320,13 @@ namespace Barotrauma.Items.Components
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)
{
@@ -357,6 +364,8 @@ namespace Barotrauma.Items.Components
continue;
}
float addLoad = 0.0f;
float addPower = 0.0f;
if (powered is PowerContainer powerContainer)
{
if (recipient.Name == "power_in")
@@ -365,7 +374,7 @@ namespace Barotrauma.Items.Components
}
else
{
fullPower += Math.Min(powerContainer.CurrPowerOutput, maxPower);
addPower = powerContainer.CurrPowerOutput;
}
}
else
@@ -380,10 +389,16 @@ namespace Barotrauma.Items.Components
//negative power consumption = the construction is a
//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; }
}
}
}
}

View File

@@ -12,8 +12,6 @@ namespace Barotrauma.Items.Components
public static float SkillIncreaseMultiplier = 0.4f;
private string header;
private float fixDurationLowSkill, fixDurationHighSkill;
private float deteriorationTimer;
@@ -52,17 +50,20 @@ namespace Barotrauma.Items.Components
set;
}
/*private float repairProgress;
public 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 FixDurationLowSkill
{
get { return repairProgress; }
set
{
repairProgress = MathHelper.Clamp(value, 0.0f, 1.0f);
if (repairProgress >= 1.0f && currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None;
}
}*/
get;
set;
}
[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;
public Character CurrentFixer
{
@@ -83,8 +84,6 @@ namespace Barotrauma.Items.Components
this.item = item;
header = element.GetAttributeString("name", "");
fixDurationLowSkill = element.GetAttributeFloat("fixdurationlowskill", 100.0f);
fixDurationHighSkill = element.GetAttributeFloat("fixdurationhighskill", 5.0f);
InitProjSpecific(element);
}
@@ -160,7 +159,7 @@ namespace Barotrauma.Items.Components
}
bool wasBroken = !item.IsFullCondition;
float fixDuration = MathHelper.Lerp(fixDurationLowSkill, fixDurationHighSkill, successFactor);
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
if (fixDuration <= 0.0f)
{
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));
}
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);
}
}
}

View File

@@ -163,6 +163,14 @@ namespace Barotrauma.Items.Components
{
wires[index] = wire;
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)

View File

@@ -356,7 +356,7 @@ namespace Barotrauma.Items.Components
projectile.body.ResetDynamics();
projectile.body.Enabled = true;
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 projectileComponent = projectile.GetComponent<Projectile>();

View File

@@ -59,7 +59,7 @@ namespace Barotrauma
public PhysicsBody body;
public readonly XElement StaticBodyConfig;
private float lastSentCondition;
private float sendConditionUpdateTimer;
private bool conditionUpdatePending;
@@ -211,14 +211,14 @@ namespace Barotrauma
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
{
get;
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
{
get;
@@ -274,12 +274,11 @@ namespace Barotrauma
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)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
lastSentCondition = condition;
conditionUpdatePending = true;
}
}
}
@@ -996,6 +995,21 @@ namespace Barotrauma
aiTarget.SightRange -= 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);
@@ -1272,7 +1286,11 @@ namespace Barotrauma
LastSentSignalRecipients.Clear();
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;

View File

@@ -554,6 +554,39 @@ namespace Barotrauma
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);
}

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