Merge remote-tracking branch 'upstream/master' into develop
This commit is contained in:
@@ -66,6 +66,8 @@ namespace Barotrauma
|
||||
public Vector2 ShakePosition { get; private set; }
|
||||
private float shakeTimer;
|
||||
|
||||
public float MovementLockTimer;
|
||||
|
||||
private float globalZoomScale = 1.0f;
|
||||
|
||||
//used to smooth out the movement when in freecam
|
||||
@@ -257,13 +259,15 @@ namespace Barotrauma
|
||||
|
||||
float moveSpeed = 20.0f / zoom;
|
||||
|
||||
MovementLockTimer -= deltaTime;
|
||||
|
||||
Vector2 moveCam = Vector2.Zero;
|
||||
if (TargetPos == Vector2.Zero)
|
||||
{
|
||||
Vector2 moveInput = Vector2.Zero;
|
||||
if (allowMove && !Freeze)
|
||||
{
|
||||
if (GUI.KeyboardDispatcher.Subscriber == null && allowInput)
|
||||
if (GUI.KeyboardDispatcher.Subscriber == null && allowInput && MovementLockTimer <= 0.0f)
|
||||
{
|
||||
if (PlayerInput.KeyDown(Keys.LeftShift)) { moveSpeed *= 2.0f; }
|
||||
if (PlayerInput.KeyDown(Keys.LeftControl)) { moveSpeed *= 0.5f; }
|
||||
|
||||
@@ -558,14 +558,17 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.Client != null) { chatMessage += " " + TextManager.Get("DeathChatNotification"); }
|
||||
|
||||
GameMain.NetworkMember.RespawnManager?.ShowRespawnPromptIfNeeded();
|
||||
RespawnManager.ShowDeathPromptIfNeeded();
|
||||
|
||||
GameMain.NetworkMember.AddChatMessage(chatMessage.Value, ChatMessageType.Dead);
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
controlled = null;
|
||||
if (!(Screen.Selected?.Cam is null))
|
||||
if (Screen.Selected?.Cam is Camera cam)
|
||||
{
|
||||
Screen.Selected.Cam.TargetPos = Vector2.Zero;
|
||||
cam.TargetPos = Vector2.Zero;
|
||||
//briefly lock moving the camera with arrow keys
|
||||
//(it's annoying to have the camera fly off when you die while trying to move to safety)
|
||||
cam.MovementLockTimer = 2.0f;
|
||||
Lights.LightManager.ViewTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace Barotrauma
|
||||
|
||||
if (character.Params.CanInteract && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
|
||||
{
|
||||
if (character.SelectedCharacter.CanInventoryBeAccessed)
|
||||
if (character.SelectedCharacter.IsInventoryAccessibleTo(character))
|
||||
{
|
||||
character.SelectedCharacter.Inventory.Update(deltaTime, cam);
|
||||
}
|
||||
@@ -677,7 +677,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.Params.CanInteract && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
|
||||
{
|
||||
if (character.SelectedCharacter.CanInventoryBeAccessed)
|
||||
if (character.SelectedCharacter.IsInventoryAccessibleTo(character))
|
||||
{
|
||||
character.SelectedCharacter.Inventory.Locked = false;
|
||||
character.SelectedCharacter.Inventory.CurrentLayout = CharacterInventory.Layout.Left;
|
||||
@@ -759,7 +759,7 @@ namespace Barotrauma
|
||||
textPos.Y += largeTextSize.Y;
|
||||
}
|
||||
|
||||
if (character.FocusedCharacter.CanBeDragged)
|
||||
if (character.FocusedCharacter.CanBeDraggedBy(character))
|
||||
{
|
||||
string text = character.CanEat ? "EatHint" : "GrabHint";
|
||||
GUI.DrawString(spriteBatch, textPos, GetCachedHudText(text, InputType.Grab),
|
||||
@@ -767,11 +767,7 @@ namespace Barotrauma
|
||||
textPos.Y += largeTextSize.Y;
|
||||
}
|
||||
|
||||
if (!character.DisableHealthWindow &&
|
||||
character.IsFriendly(character.FocusedCharacter) &&
|
||||
character.FocusedCharacter.CharacterHealth.UseHealthWindow &&
|
||||
character.CanInteractWith(character.FocusedCharacter, 160f, false) &&
|
||||
!character.IsClimbing)
|
||||
if (character.FocusedCharacter.CanBeHealedBy(character))
|
||||
{
|
||||
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("HealHint", InputType.Health),
|
||||
GUIStyle.Green, Color.Black, 2, GUIStyle.SmallFont);
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace Barotrauma
|
||||
{
|
||||
Color textColor = Color.White * (0.5f + skill.Level / 200.0f);
|
||||
|
||||
var skillName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillsArea.RectTransform), TextManager.Get("SkillName." + skill.Identifier), textColor: textColor, font: font) { Padding = Vector4.Zero };
|
||||
var skillName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillsArea.RectTransform), skill.DisplayName, textColor: textColor, font: font) { Padding = Vector4.Zero };
|
||||
|
||||
float modifiedSkillLevel = skill.Level;
|
||||
if (Character != null)
|
||||
@@ -582,6 +582,7 @@ namespace Barotrauma
|
||||
|
||||
ch.ExperiencePoints = inc.ReadInt32();
|
||||
ch.AdditionalTalentPoints = inc.ReadRangedInteger(0, MaxAdditionalTalentPoints);
|
||||
ch.PermanentlyDead = inc.ReadBoolean();
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
@@ -723,11 +723,19 @@ namespace Barotrauma
|
||||
character.ReadStatus(inc);
|
||||
}
|
||||
|
||||
if (character.IsHuman && character.TeamID != CharacterTeamType.FriendlyNPC && character.TeamID != CharacterTeamType.None && !character.IsDead)
|
||||
if (character.IsHuman && character.TeamID != CharacterTeamType.FriendlyNPC && character.TeamID != CharacterTeamType.None)
|
||||
{
|
||||
CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID);
|
||||
GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo);
|
||||
GameMain.GameSession.CrewManager.AddCharacter(character);
|
||||
if (character.isDead)
|
||||
{
|
||||
//just add the info if dead (displayed in the round summary, and crew list if the character is revived)
|
||||
GameMain.GameSession.CrewManager.AddCharacterInfo(character.info);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.GameSession.CrewManager.AddCharacter(character);
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.Client.SessionId == ownerId)
|
||||
|
||||
@@ -152,8 +152,7 @@ namespace Barotrauma
|
||||
|
||||
if (value == null &&
|
||||
Character.Controlled?.SelectedCharacter?.CharacterHealth != null &&
|
||||
Character.Controlled.SelectedCharacter.CharacterHealth == prevOpenHealthWindow/* &&
|
||||
!Character.Controlled.SelectedCharacter.CanInventoryBeAccessed*/)
|
||||
Character.Controlled.SelectedCharacter.CharacterHealth == prevOpenHealthWindow)
|
||||
{
|
||||
Character.Controlled.DeselectCharacter();
|
||||
}
|
||||
|
||||
@@ -289,18 +289,19 @@ namespace Barotrauma
|
||||
spriteAnimState.Add(decorativeSprite, new SpriteState());
|
||||
}
|
||||
TintMask = null;
|
||||
float sourceRectScale = ragdoll.RagdollParams.SourceRectScale;
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
Sprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.normalSpriteParams, ref _texturePath));
|
||||
Sprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.normalSpriteParams, ref _texturePath), sourceRectScale: sourceRectScale);
|
||||
break;
|
||||
case "damagedsprite":
|
||||
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams, ref _damagedTexturePath));
|
||||
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams, ref _damagedTexturePath), sourceRectScale: sourceRectScale);
|
||||
break;
|
||||
case "conditionalsprite":
|
||||
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null, ref _texturePath));
|
||||
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null, ref _texturePath), sourceRectScale: sourceRectScale);
|
||||
ConditionalSprites.Add(conditionalSprite);
|
||||
if (conditionalSprite.DeformableSprite != null)
|
||||
{
|
||||
@@ -310,7 +311,7 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
case "deformablesprite":
|
||||
_deformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams, ref _texturePath));
|
||||
_deformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams, ref _texturePath), sourceRectScale: sourceRectScale);
|
||||
var deformations = CreateDeformations(subElement);
|
||||
Deformations.AddRange(deformations);
|
||||
NonConditionalDeformations.AddRange(deformations);
|
||||
@@ -339,7 +340,7 @@ namespace Barotrauma
|
||||
ContentPath tintMaskPath = subElement.GetAttributeContentPath("texture");
|
||||
if (!tintMaskPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
TintMask = new Sprite(subElement, file: GetSpritePath(tintMaskPath));
|
||||
TintMask = new Sprite(subElement, file: GetSpritePath(tintMaskPath), sourceRectScale: sourceRectScale);
|
||||
TintHighlightThreshold = subElement.GetAttributeFloat("highlightthreshold", 0.6f);
|
||||
TintHighlightMultiplier = subElement.GetAttributeFloat("highlightmultiplier", 0.8f);
|
||||
}
|
||||
@@ -348,7 +349,7 @@ namespace Barotrauma
|
||||
ContentPath huskMaskPath = subElement.GetAttributeContentPath("texture");
|
||||
if (!huskMaskPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
HuskMask = new Sprite(subElement, file: GetSpritePath(huskMaskPath));
|
||||
HuskMask = new Sprite(subElement, file: GetSpritePath(huskMaskPath), sourceRectScale: sourceRectScale);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -700,31 +701,34 @@ namespace Barotrauma
|
||||
if (spriteParams == null || Alpha <= 0) { return; }
|
||||
float burn = spriteParams.IgnoreTint ? 0 : burnOverLayStrength;
|
||||
float brightness = Math.Max(1.0f - burn, 0.2f);
|
||||
Color clr = spriteParams.Color;
|
||||
Color tintedColor = spriteParams.Color;
|
||||
if (!spriteParams.IgnoreTint)
|
||||
{
|
||||
clr = clr.Multiply(ragdoll.RagdollParams.Color);
|
||||
tintedColor = tintedColor.Multiply(ragdoll.RagdollParams.Color);
|
||||
if (character.Info != null)
|
||||
{
|
||||
clr = clr.Multiply(character.Info.Head.SkinColor);
|
||||
tintedColor = tintedColor.Multiply(character.Info.Head.SkinColor);
|
||||
}
|
||||
if (character.CharacterHealth.FaceTint.A > 0 && type == LimbType.Head)
|
||||
{
|
||||
clr = Color.Lerp(clr, character.CharacterHealth.FaceTint.Opaque(), character.CharacterHealth.FaceTint.A / 255.0f);
|
||||
tintedColor = Color.Lerp(tintedColor, character.CharacterHealth.FaceTint.Opaque(), character.CharacterHealth.FaceTint.A / 255.0f);
|
||||
}
|
||||
if (character.CharacterHealth.BodyTint.A > 0)
|
||||
{
|
||||
clr = Color.Lerp(clr, character.CharacterHealth.BodyTint.Opaque(), character.CharacterHealth.BodyTint.A / 255.0f);
|
||||
tintedColor = Color.Lerp(tintedColor, character.CharacterHealth.BodyTint.Opaque(), character.CharacterHealth.BodyTint.A / 255.0f);
|
||||
}
|
||||
}
|
||||
Color color = new Color((byte)(clr.R * brightness), (byte)(clr.G * brightness), (byte)(clr.B * brightness), clr.A);
|
||||
Color color = new Color(tintedColor.Multiply(brightness), tintedColor.A);
|
||||
Color colorWithoutTint = new Color(spriteParams.Color.Multiply(brightness), spriteParams.Color.A);
|
||||
Color blankColor = new Color(brightness, brightness, brightness, 1);
|
||||
if (deadTimer > 0)
|
||||
{
|
||||
color = Color.Lerp(color, spriteParams.DeadColor, MathUtils.InverseLerp(0, spriteParams.DeadColorTime, deadTimer));
|
||||
colorWithoutTint = Color.Lerp(colorWithoutTint, spriteParams.DeadColor, MathUtils.InverseLerp(0, spriteParams.DeadColorTime, deadTimer));
|
||||
}
|
||||
|
||||
color = overrideColor ?? color;
|
||||
colorWithoutTint = overrideColor ?? colorWithoutTint;
|
||||
blankColor = overrideColor ?? blankColor;
|
||||
color *= Alpha;
|
||||
blankColor *= Alpha;
|
||||
@@ -739,6 +743,7 @@ namespace Barotrauma
|
||||
else if (severedFadeOutTimer > SeveredFadeOutTime - 1.0f)
|
||||
{
|
||||
color *= SeveredFadeOutTime - severedFadeOutTimer;
|
||||
colorWithoutTint *= SeveredFadeOutTime - severedFadeOutTimer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -956,7 +961,7 @@ namespace Barotrauma
|
||||
{
|
||||
DamagedSprite.Draw(spriteBatch,
|
||||
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
|
||||
color * damageOverlayStrength, activeSprite.Origin,
|
||||
colorWithoutTint * damageOverlayStrength, activeSprite.Origin,
|
||||
-body.DrawRotation,
|
||||
Scale, spriteEffect, activeSprite.Depth - depthStep * Math.Max(1, WearingItems.Count * 2)); // Multiply by 2 to get rid of z-fighting with some clothing combos
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -75,7 +75,7 @@ namespace Barotrauma
|
||||
foreach (ItemComponent ic in Item.Components)
|
||||
{
|
||||
if (ic is Holdable) { continue; }
|
||||
if (!ic.AllowInGameEditing) { continue; }
|
||||
if (!ic.AllowInGameEditing && Screen.Selected is not { IsEditor: true }) { continue; }
|
||||
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0 &&
|
||||
!SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable(ic)))
|
||||
{
|
||||
|
||||
@@ -29,6 +29,12 @@ namespace Barotrauma
|
||||
Length = Rect.Width + Padding + Label.Size.X;
|
||||
}
|
||||
|
||||
public void SetLabel(LocalizedString label, CircuitBoxNode node)
|
||||
{
|
||||
Label = new CircuitBoxLabel(label, GUIStyle.SubHeadingFont);
|
||||
Length = Rect.Width + Padding + Label.Size.X;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 drawPos, Vector2 parentPos, Color color)
|
||||
{
|
||||
if (CircuitBox.UI is not { } circuitBoxUi) { return; }
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal sealed partial class CircuitBoxInputOutputNode
|
||||
{
|
||||
private const string PromptUserData = "InputOutputEditPrompt";
|
||||
|
||||
public void PromptEdit(GUIComponent parent)
|
||||
{
|
||||
CircuitBox.UI?.SetMenuVisibility(false);
|
||||
GUIFrame backgroundBlocker = new(new RectTransform(Vector2.One, parent.RectTransform), style: "GUIBackgroundBlocker")
|
||||
{
|
||||
UserData = PromptUserData
|
||||
};
|
||||
|
||||
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.8f), backgroundBlocker.RectTransform, Anchor.Center), isHorizontal: false, childAnchor: Anchor.TopCenter);
|
||||
GUIFrame labelArea = new(new RectTransform(new Vector2(1f, 0.8f), mainLayout.RectTransform, Anchor.Center));
|
||||
|
||||
GUILayoutGroup labelLayout = new GUILayoutGroup(new RectTransform(Vector2.One, labelArea.RectTransform), childAnchor: Anchor.Center);
|
||||
GUIListBox labelList = new GUIListBox(new RectTransform(ToolBox.PaddingSizeParentRelative(labelLayout.RectTransform, 0.9f), labelLayout.RectTransform));
|
||||
|
||||
Dictionary<string, GUITextBox> textBoxes = new();
|
||||
|
||||
foreach (var conn in Connectors)
|
||||
{
|
||||
bool found = ConnectionLabelOverrides.TryGetValue(conn.Name, out string labelOverride);
|
||||
|
||||
GUILayoutGroup connLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.12f), labelList.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.4f, 1f), connLayout.RectTransform), text: conn.Connection.DisplayName, font: GUIStyle.SubHeadingFont);
|
||||
GUITextBox box = GUI.CreateTextBoxWithPlaceholder(new RectTransform(new Vector2(0.6f, 1f), connLayout.RectTransform), text: found ? labelOverride : string.Empty, conn.Connection.DisplayName.Value);
|
||||
box.MaxTextLength = MaxConnectionLabelLength;
|
||||
|
||||
textBoxes.Add(conn.Name, box);
|
||||
}
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), mainLayout.RectTransform), text: TextManager.Get("confirm"))
|
||||
{
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
var newOverrides = textBoxes.ToDictionary(
|
||||
static pair => pair.Key,
|
||||
static pair => pair.Value.Text);
|
||||
|
||||
foreach (var (key, value) in newOverrides.ToImmutableDictionary())
|
||||
{
|
||||
if (ConnectionLabelOverrides.TryGetValue(key, out string newValue))
|
||||
{
|
||||
if (newValue == value)
|
||||
{
|
||||
newOverrides.Remove(key);
|
||||
}
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
newOverrides.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
CircuitBox.SetConnectionLabelOverrides(this, newOverrides);
|
||||
RemoveEditPrompt(parent);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), mainLayout.RectTransform), text: TextManager.Get("cancel"))
|
||||
{
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
RemoveEditPrompt(parent);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void RemoveEditPrompt(GUIComponent parent)
|
||||
{
|
||||
if (parent.FindChild(PromptUserData) is not { } promptParent) { return; }
|
||||
parent.RemoveChild(promptParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
@@ -80,6 +81,18 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
|
||||
var characterLimit = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), frame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.03f, 0.02f) }, text: $"{bodyTextBox.Text.Length}/{NetLimitedString.MaxLength}", font: GUIStyle.SmallFont, textAlignment: Alignment.Right);
|
||||
|
||||
bodyTextBox.OnTextChanged += (textBox, _) =>
|
||||
{
|
||||
textBox.TextColor = textBox.TextBlock.SelectedTextColor = textBox.Text.Length > NetLimitedString.MaxLength
|
||||
? GUIStyle.Red
|
||||
: GUIStyle.TextColorNormal;
|
||||
|
||||
characterLimit.Text = $"{textBox.Text.Length}/{NetLimitedString.MaxLength}";
|
||||
return true;
|
||||
};
|
||||
|
||||
static void UpdateLabelColor(GUITextBox box)
|
||||
{
|
||||
bool found = TextManager.ContainsTag(box.Text);
|
||||
@@ -97,8 +110,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
bodyTextBox.OnDeselected += (textBox, _) => UpdateLabelColor(textBox);
|
||||
headerTextBox.OnDeselected += (textBox, _) => UpdateLabelColor(textBox);
|
||||
bodyTextBox.OnDeselected += static (textBox, _) => UpdateLabelColor(textBox);
|
||||
headerTextBox.OnDeselected += static (textBox, _) => UpdateLabelColor(textBox);
|
||||
UpdateLabelColor(bodyTextBox);
|
||||
UpdateLabelColor(headerTextBox);
|
||||
|
||||
|
||||
+41
-33
@@ -87,6 +87,16 @@ namespace Barotrauma
|
||||
SnapshotMoveAffectedNodes();
|
||||
startClick = cursorPos;
|
||||
}
|
||||
|
||||
public void ClearSnapshot()
|
||||
{
|
||||
lastNodesUnderCursor = ImmutableHashSet<CircuitBoxNode>.Empty;
|
||||
lastSelectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty;
|
||||
moveAffectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty;
|
||||
LastConnectorUnderCursor = Option.None;
|
||||
LastWireUnderCursor = Option.None;
|
||||
LastResizeAffectedNode = Option.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all connections and gathers them into a single list for easier iteration.
|
||||
@@ -168,38 +178,36 @@ namespace Barotrauma
|
||||
LastResizeAffectedNode = FindResizeBorderUnderCursor(lastNodesUnderCursor, cursorPos);
|
||||
}
|
||||
|
||||
private static Option<(CircuitBoxResizeDirection, CircuitBoxNode)> FindResizeBorderUnderCursor(ImmutableHashSet<CircuitBoxNode> nodes, Vector2 cursorPos)
|
||||
private Option<(CircuitBoxResizeDirection, CircuitBoxNode)> FindResizeBorderUnderCursor(ImmutableHashSet<CircuitBoxNode> nodes, Vector2 cursorPos)
|
||||
{
|
||||
foreach (var node in nodes)
|
||||
if (!nodes.Any()) { return Option.None; }
|
||||
|
||||
var node = circuitBoxUi.GetTopmostNode(nodes);
|
||||
if (node is null || !node.IsResizable) { return Option.None; }
|
||||
|
||||
const float borderSize = 32f;
|
||||
|
||||
var rect = node.Rect;
|
||||
RectangleF bottomBorder = new(rect.X, rect.Top, rect.Width, borderSize);
|
||||
RectangleF rightBorder = new(rect.Right - borderSize, rect.Y, borderSize, rect.Height);
|
||||
RectangleF leftBorder = new(rect.X, rect.Y, borderSize, rect.Height);
|
||||
|
||||
bool hoverBottom = bottomBorder.Contains(cursorPos),
|
||||
hoverRight = rightBorder.Contains(cursorPos),
|
||||
hoverLeft = leftBorder.Contains(cursorPos);
|
||||
|
||||
var dir = CircuitBoxResizeDirection.None;
|
||||
|
||||
if (hoverBottom) { dir |= CircuitBoxResizeDirection.Down; }
|
||||
if (hoverRight) { dir |= CircuitBoxResizeDirection.Right; }
|
||||
if (hoverLeft) { dir |= CircuitBoxResizeDirection.Left; }
|
||||
|
||||
if (dir is CircuitBoxResizeDirection.None)
|
||||
{
|
||||
if (!node.IsResizable) { continue; }
|
||||
|
||||
const float borderSize = 32f;
|
||||
|
||||
var rect = node.Rect;
|
||||
RectangleF bottomBorder = new(rect.X, rect.Top, rect.Width, borderSize);
|
||||
RectangleF rightBorder = new(rect.Right - borderSize, rect.Y, borderSize, rect.Height);
|
||||
RectangleF leftBorder = new(rect.X, rect.Y, borderSize, rect.Height);
|
||||
|
||||
bool hoverBottom = bottomBorder.Contains(cursorPos),
|
||||
hoverRight = rightBorder.Contains(cursorPos),
|
||||
hoverLeft = leftBorder.Contains(cursorPos);
|
||||
|
||||
var dir = CircuitBoxResizeDirection.None;
|
||||
|
||||
if (hoverBottom) { dir |= CircuitBoxResizeDirection.Down; }
|
||||
if (hoverRight) { dir |= CircuitBoxResizeDirection.Right; }
|
||||
if (hoverLeft) { dir |= CircuitBoxResizeDirection.Left; }
|
||||
|
||||
if (dir is CircuitBoxResizeDirection.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return Option.Some((dir, node));
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
return Option.Some((dir, node));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -281,14 +289,14 @@ namespace Barotrauma
|
||||
if (circuitBoxUi.Locked) { return; }
|
||||
bool isDragThresholdExceeded = Vector2.DistanceSquared(startClick, cursorPos) > dragTreshold * dragTreshold;
|
||||
|
||||
if (LastResizeAffectedNode.IsSome())
|
||||
{
|
||||
IsResizing |= isDragThresholdExceeded;
|
||||
}
|
||||
else if (LastConnectorUnderCursor.IsSome())
|
||||
if (LastConnectorUnderCursor.IsSome())
|
||||
{
|
||||
IsWiring |= isDragThresholdExceeded;
|
||||
}
|
||||
else if (LastResizeAffectedNode.IsSome())
|
||||
{
|
||||
IsResizing |= isDragThresholdExceeded;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsDragging |= isDragThresholdExceeded;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -350,6 +350,10 @@ namespace Barotrauma
|
||||
{
|
||||
component.Sprite.Draw(spriteBatch, PlayerInput.MousePosition);
|
||||
}
|
||||
if (PlayerInput.PrimaryMouseButtonHeld() && MouseSnapshotHandler.LastConnectorUnderCursor.IsSome())
|
||||
{
|
||||
CircuitBoxWire.SelectedWirePrefab.Sprite.Draw(spriteBatch, PlayerInput.MousePosition, CircuitBoxWire.SelectedWirePrefab.SpriteColor, scale: camera.Zoom);
|
||||
}
|
||||
|
||||
foreach (var c in CircuitBox.Components)
|
||||
{
|
||||
@@ -360,11 +364,11 @@ namespace Barotrauma
|
||||
{
|
||||
n.DrawHUD(spriteBatch, camera);
|
||||
}
|
||||
|
||||
|
||||
if (Locked)
|
||||
{
|
||||
LocalizedString lockedText = TextManager.Get("CircuitBoxLocked")
|
||||
.Fallback(TextManager.Get("ConnectionLocked"));
|
||||
.Fallback(TextManager.Get("ConnectionLocked"), useDefaultLanguageIfFound: false);
|
||||
|
||||
Vector2 size = GUIStyle.LargeFont.MeasureString(lockedText);
|
||||
Vector2 pos = new Vector2(screenRect.Center.X - size.X / 2, screenRect.Top + screenRect.Height * 0.05f);
|
||||
@@ -579,9 +583,25 @@ namespace Barotrauma
|
||||
|
||||
if (isMouseOn)
|
||||
{
|
||||
if (CircuitBox.HeldComponent.IsNone() && PlayerInput.PrimaryMouseButtonDown())
|
||||
if (PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
MouseSnapshotHandler.StartDragging();
|
||||
if (CircuitBox.HeldComponent.IsNone())
|
||||
{
|
||||
MouseSnapshotHandler.StartDragging();
|
||||
}
|
||||
else
|
||||
{
|
||||
MouseSnapshotHandler.ClearSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
if (PlayerInput.DoubleClicked() && MouseSnapshotHandler.FindWireUnderCursor(cursorPos).IsNone())
|
||||
{
|
||||
var topmostNode = GetTopmostNode(MouseSnapshotHandler.FindNodesUnderCursor(cursorPos));
|
||||
if (topmostNode is CircuitBoxLabelNode label && circuitComponent is not null)
|
||||
{
|
||||
label.PromptEditText(circuitComponent);
|
||||
}
|
||||
}
|
||||
|
||||
if (PlayerInput.MidButtonHeld() || (PlayerInput.IsAltDown() && PlayerInput.PrimaryMouseButtonHeld()))
|
||||
@@ -629,6 +649,7 @@ namespace Barotrauma
|
||||
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
bool selectedNode = false;
|
||||
if (MouseSnapshotHandler.IsResizing && MouseSnapshotHandler.LastResizeAffectedNode.TryUnwrap(out var r))
|
||||
{
|
||||
var (dir, node) = r;
|
||||
@@ -647,7 +668,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (!MouseSnapshotHandler.IsWiring)
|
||||
{
|
||||
TrySelectComponentsUnderCursor();
|
||||
selectedNode = TrySelectComponentsUnderCursor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,8 +679,15 @@ namespace Barotrauma
|
||||
CircuitBox.AddWire(one, two);
|
||||
}
|
||||
}
|
||||
|
||||
CircuitBox.SelectWires(MouseSnapshotHandler.LastWireUnderCursor.TryUnwrap(out var wire) ? ImmutableArray.Create(wire) : ImmutableArray<CircuitBoxWire>.Empty, !PlayerInput.IsShiftDown());
|
||||
|
||||
if (MouseSnapshotHandler.LastWireUnderCursor.TryUnwrap(out var wire) && !MouseSnapshotHandler.IsDragging && !selectedNode)
|
||||
{
|
||||
CircuitBox.SelectWires(ImmutableArray.Create(wire), !PlayerInput.IsShiftDown());
|
||||
}
|
||||
else if (CircuitBox.Wires.Any(static wire => wire.IsSelectedByMe))
|
||||
{
|
||||
CircuitBox.SelectWires(ImmutableArray<CircuitBoxWire>.Empty, !PlayerInput.IsShiftDown());
|
||||
}
|
||||
|
||||
CircuitBox.HeldComponent = Option.None;
|
||||
MouseSnapshotHandler.EndDragging();
|
||||
@@ -732,11 +760,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void TrySelectComponentsUnderCursor()
|
||||
private bool TrySelectComponentsUnderCursor()
|
||||
{
|
||||
CircuitBoxNode? foundNode = GetTopmostNode(MouseSnapshotHandler.GetLastComponentsUnderCursor());
|
||||
|
||||
if (foundNode is CircuitBoxLabelNode && MouseSnapshotHandler.LastWireUnderCursor.IsSome())
|
||||
{
|
||||
foundNode = null;
|
||||
}
|
||||
|
||||
CircuitBox.SelectComponents(foundNode is null ? ImmutableArray<CircuitBoxNode>.Empty : ImmutableArray.Create(foundNode), !PlayerInput.IsShiftDown());
|
||||
return foundNode is not null;
|
||||
}
|
||||
|
||||
private void OpenContextMenu()
|
||||
@@ -767,17 +801,26 @@ namespace Barotrauma
|
||||
|
||||
var editLabel = new ContextMenuOption(TextManager.Get("circuitboxeditlabel"), isEnabled: nodeOption is CircuitBoxLabelNode && !Locked, () =>
|
||||
{
|
||||
if (nodeOption is not CircuitBoxLabelNode label || circuitComponent is null) { return; }
|
||||
if (circuitComponent is null) { return; }
|
||||
if (nodeOption is not CircuitBoxLabelNode label) { return; }
|
||||
|
||||
label.PromptEditText(circuitComponent);
|
||||
});
|
||||
|
||||
var editConnections = new ContextMenuOption(TextManager.Get("circuitboxrenameconnections"), isEnabled: nodeOption is CircuitBoxInputOutputNode && !Locked, () =>
|
||||
{
|
||||
if (circuitComponent is null) { return; }
|
||||
if (nodeOption is not CircuitBoxInputOutputNode io) { return; }
|
||||
|
||||
io.PromptEdit(circuitComponent);
|
||||
});
|
||||
|
||||
var addLabelOption = new ContextMenuOption(TextManager.Get("circuitboxaddlabel"), isEnabled: !Locked, () =>
|
||||
{
|
||||
CircuitBox.AddLabel(cursorPos);
|
||||
});
|
||||
|
||||
ContextMenuOption[] allOptions = { addLabelOption, editLabel, option };
|
||||
ContextMenuOption[] allOptions = { addLabelOption, editLabel, editConnections, option };
|
||||
|
||||
// show component name in the header to better indicate what is about to be deleted
|
||||
if (nodeOption is CircuitBoxComponent comp)
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Barotrauma
|
||||
textBox.MaxTextLength = maxLength;
|
||||
textBox.OnKeyHit += (sender, key) =>
|
||||
{
|
||||
if (key != Keys.Tab)
|
||||
if (key != Keys.Tab && key != Keys.LeftShift)
|
||||
{
|
||||
ResetAutoComplete();
|
||||
}
|
||||
@@ -181,7 +181,8 @@ namespace Barotrauma
|
||||
|
||||
if (PlayerInput.KeyHit(Keys.Tab) && !textBox.IsIMEActive)
|
||||
{
|
||||
textBox.Text = AutoComplete(textBox.Text, increment: string.IsNullOrEmpty(currentAutoCompletedCommand) ? 0 : 1 );
|
||||
int increment = PlayerInput.KeyDown(Keys.LeftShift) ? -1 : 1;
|
||||
textBox.Text = AutoComplete(textBox.Text, increment: string.IsNullOrEmpty(currentAutoCompletedCommand) ? 0 : increment );
|
||||
}
|
||||
|
||||
if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl))
|
||||
@@ -644,6 +645,20 @@ namespace Barotrauma
|
||||
{
|
||||
NewMessage("Ready checks can only be commenced in multiplayer.", Color.Red);
|
||||
}));
|
||||
|
||||
commands.Add(new Command("setsalary", "setsalary [0-100] [character/default]: Sets the salary of a certain character or the default salary to a percentage.", (string[] args) =>
|
||||
{
|
||||
ThrowError("This command can only be used in multiplayer campaign.");
|
||||
}, isCheat: true, getValidArgs: () =>
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new[]{ "0", "100" },
|
||||
Enumerable.Union(
|
||||
new string[] { "default" },
|
||||
Character.CharacterList.Select(c => c.Name).Distinct().OrderBy(n => n)).ToArray(),
|
||||
};
|
||||
}));
|
||||
|
||||
commands.Add(new Command("bindkey", "bindkey [key] [command]: Binds a key to a command.", (string[] args) =>
|
||||
{
|
||||
@@ -803,6 +818,7 @@ namespace Barotrauma
|
||||
AssignRelayToServer("money", true);
|
||||
AssignRelayToServer("showmoney", true);
|
||||
AssignRelayToServer("setskill", true);
|
||||
AssignRelayToServer("setsalary", true);
|
||||
AssignRelayToServer("readycheck", true);
|
||||
commands.Add(new Command("debugjobassignment", "", (string[] args) => { }));
|
||||
AssignRelayToServer("debugjobassignment", true);
|
||||
@@ -848,11 +864,8 @@ namespace Barotrauma
|
||||
|
||||
AssignOnExecute("teleportcharacter|teleport", (string[] args) =>
|
||||
{
|
||||
Character tpCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, false);
|
||||
if (tpCharacter != null)
|
||||
{
|
||||
tpCharacter.TeleportTo(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition));
|
||||
}
|
||||
Vector2 cursorWorldPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
TeleportCharacter(cursorWorldPos, Character.Controlled, args);
|
||||
});
|
||||
|
||||
AssignOnExecute("spawn|spawncharacter", (string[] args) =>
|
||||
@@ -1435,6 +1448,9 @@ namespace Barotrauma
|
||||
|
||||
AssignRelayToServer("water|editwater", false);
|
||||
AssignRelayToServer("fire|editfire", false);
|
||||
#if DEBUG
|
||||
AssignRelayToServer("debugvoip", true);
|
||||
#endif
|
||||
|
||||
commands.Add(new Command("mute", "mute [name]: Prevent the client from speaking to anyone through the voice chat. Using this command requires a permission from the server host.",
|
||||
null,
|
||||
@@ -2355,6 +2371,11 @@ namespace Barotrauma
|
||||
}));
|
||||
|
||||
#if DEBUG
|
||||
commands.Add(new Command("deathprompt", "Shows the death prompt for testing purposes.", (string[] args) =>
|
||||
{
|
||||
DeathPrompt.Create(delay: 1.0f);
|
||||
}));
|
||||
|
||||
commands.Add(new Command("listspamfilters", "Lists filters that are in the global spam filter.", (string[] args) =>
|
||||
{
|
||||
if (!SpamServerFilters.GlobalSpamFilter.TryUnwrap(out var filter))
|
||||
@@ -3103,12 +3124,12 @@ namespace Barotrauma
|
||||
{
|
||||
if (Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
ThrowError("Reloading the package while in GameScreen may break things; to do it anyway, type 'reloadcorepackage [name] force'");
|
||||
ThrowError("Reloading the package while in GameScreen may break things; to do it anyway, type 'reloadpackage [name] force'");
|
||||
return;
|
||||
}
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
ThrowError("Reloading the core package while in sub editor may break thingg; to do it anyway, type 'reloadcorepackage [name] force'");
|
||||
ThrowError("Reloading the core package while in sub editor may break things; to do it anyway, type 'reloadpackage [name] force'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class AlienRuinMission : Mission
|
||||
partial class EliminateTargetsMission : Mission
|
||||
{
|
||||
public override bool DisplayAsCompleted => State > 0;
|
||||
public override bool DisplayAsFailed => false;
|
||||
@@ -409,9 +409,9 @@ namespace Barotrauma
|
||||
{
|
||||
senderName = (message.Type == ChatMessageType.Private ? "[PM] " : "") + message.SenderName;
|
||||
}
|
||||
if (message.Sender?.Info?.Job != null)
|
||||
if (message.SenderCharacter?.Info?.Job != null)
|
||||
{
|
||||
senderColor = Color.Lerp(message.Sender.Info.Job.Prefab.UIColor, Color.White, 0.25f);
|
||||
senderColor = Color.Lerp(message.SenderCharacter.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,
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Barotrauma
|
||||
private readonly CampaignUI campaignUI;
|
||||
private readonly GUIComponent parentComponent;
|
||||
|
||||
private GUILayoutGroup pendingAndCrewGroup;
|
||||
private GUIListBox hireableList, pendingList, crewList;
|
||||
private GUIFrame characterPreviewFrame;
|
||||
private GUIDropDown sortingDropDown;
|
||||
@@ -24,7 +25,14 @@ namespace Barotrauma
|
||||
private PlayerBalanceElement? playerBalanceElement;
|
||||
|
||||
private List<CharacterInfo> PendingHires => campaign.Map?.CurrentLocation?.HireManager?.PendingHires;
|
||||
private bool HasPermission => CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageHires);
|
||||
|
||||
// Is the player hiring a new character for themselves instead of bots for the crew?
|
||||
// The window can only be used for one of these purposes at the same time.
|
||||
private static bool HiringNewCharacter => GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath, IronmanMode: false } &&
|
||||
GameMain.Client?.CharacterInfo is { PermanentlyDead: true };
|
||||
|
||||
private static bool HasPermissionToHire => CampaignMode.AllowedToManageCampaign(
|
||||
HiringNewCharacter ? ClientPermissions.ManageMoney : ClientPermissions.ManageHires);
|
||||
|
||||
private Point resolutionWhenCreated;
|
||||
|
||||
@@ -60,20 +68,20 @@ namespace Barotrauma
|
||||
RefreshCrewFrames(hireableList);
|
||||
RefreshCrewFrames(crewList);
|
||||
RefreshCrewFrames(pendingList);
|
||||
if (clearAllButton != null) { clearAllButton.Enabled = HasPermission; }
|
||||
if (clearAllButton != null) { clearAllButton.Enabled = HasPermissionToHire; }
|
||||
}
|
||||
|
||||
private void RefreshCrewFrames(GUIListBox listBox)
|
||||
{
|
||||
if (listBox == null) { return; }
|
||||
listBox.CanBeFocused = HasPermission;
|
||||
listBox.CanBeFocused = HasPermissionToHire;
|
||||
foreach (GUIComponent child in listBox.Content.Children)
|
||||
{
|
||||
if (child.FindChild(c => c is GUIButton && c.UserData is CharacterInfo, true) is GUIButton buyButton)
|
||||
{
|
||||
CharacterInfo characterInfo = buyButton.UserData as CharacterInfo;
|
||||
bool enoughReputationToHire = EnoughReputationToHire(characterInfo);
|
||||
buyButton.Enabled = HasPermission && enoughReputationToHire;
|
||||
bool enougMoneyToHire = !HiringNewCharacter || campaign.CanAfford(HireManager.GetSalaryFor(characterInfo));
|
||||
buyButton.Enabled = HasPermissionToHire && EnoughReputationToHire(characterInfo) && enougMoneyToHire;
|
||||
foreach (GUITextBlock text in child.GetAllChildren<GUITextBlock>())
|
||||
{
|
||||
text.TextColor = new Color(text.TextColor, buyButton.Enabled ? 1.0f : 0.6f);
|
||||
@@ -174,7 +182,7 @@ namespace Barotrauma
|
||||
|
||||
playerBalanceElement = CampaignUI.AddBalanceElement(pendingAndCrewMainGroup, new Vector2(1.0f, 0.75f / 14.0f));
|
||||
|
||||
var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
|
||||
pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
|
||||
parent: new GUIFrame(new RectTransform(new Vector2(1.0f, 13.25f / 14.0f), pendingAndCrewMainGroup.RectTransform)
|
||||
{
|
||||
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
|
||||
@@ -222,7 +230,7 @@ namespace Barotrauma
|
||||
{
|
||||
ClickSound = GUISoundType.Cart,
|
||||
ForceUpperCase = ForceUpperCase.Yes,
|
||||
Enabled = HasPermission,
|
||||
Enabled = HasPermissionToHire,
|
||||
OnClicked = (b, o) => RemoveAllPendingHires()
|
||||
};
|
||||
GUITextBlock.AutoScaleAndNormalize(validateHiresButton.TextBlock, clearAllButton.TextBlock);
|
||||
@@ -277,7 +285,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (CharacterInfo c in hireableCharacters)
|
||||
{
|
||||
if (c == null) { continue; }
|
||||
if (c == null || PendingHires.Contains(c)) { continue; }
|
||||
CreateCharacterFrame(c, hireableList);
|
||||
}
|
||||
}
|
||||
@@ -289,8 +297,8 @@ namespace Barotrauma
|
||||
{
|
||||
HireManager hireManager = location.HireManager;
|
||||
if (hireManager == null) { return; }
|
||||
int hireVal = hireManager.AvailableCharacters.Aggregate(0, (curr, hire) => curr + hire.GetIdentifier());
|
||||
int newVal = availableHires.Aggregate(0, (curr, hire) => curr + hire.GetIdentifier());
|
||||
int hireVal = hireManager.AvailableCharacters.Aggregate(0, (curr, hire) => curr + hire.ID);
|
||||
int newVal = availableHires.Aggregate(0, (curr, hire) => curr + hire.ID);
|
||||
if (hireVal != newVal)
|
||||
{
|
||||
location.HireManager.AvailableCharacters = availableHires;
|
||||
@@ -371,7 +379,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCharacterFrame(CharacterInfo characterInfo, GUIListBox listBox)
|
||||
public GUIComponent CreateCharacterFrame(CharacterInfo characterInfo, GUIListBox listBox, bool hideSalary = false)
|
||||
{
|
||||
Skill skill = null;
|
||||
Color? jobColor = null;
|
||||
@@ -442,33 +450,41 @@ namespace Barotrauma
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
|
||||
if (listBox != crewList)
|
||||
|
||||
if (!hideSalary)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform),
|
||||
TextManager.FormatCurrency(HireManager.GetSalaryFor(characterInfo)),
|
||||
textAlignment: Alignment.Center)
|
||||
if (listBox != crewList)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just a bit of padding to make list layouts similar
|
||||
new GUIFrame(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false };
|
||||
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform),
|
||||
TextManager.FormatCurrency(HireManager.GetSalaryFor(characterInfo)),
|
||||
textAlignment: Alignment.Center)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just a bit of padding to make list layouts similar
|
||||
new GUIFrame(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false };
|
||||
}
|
||||
}
|
||||
|
||||
if (listBox == hireableList)
|
||||
{
|
||||
var hireButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
|
||||
{
|
||||
ToolTip = TextManager.Get("hirebutton"),
|
||||
ClickSound = GUISoundType.Cart,
|
||||
UserData = characterInfo,
|
||||
Enabled = CanHire(characterInfo),
|
||||
Enabled = CanHire(characterInfo) && !HiringNewCharacter,
|
||||
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
|
||||
};
|
||||
hireButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
|
||||
{
|
||||
if (HiringNewCharacter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (PendingHires.Count + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
|
||||
{
|
||||
if (btn.Enabled)
|
||||
@@ -483,6 +499,41 @@ namespace Barotrauma
|
||||
btn.Enabled = CanHire(characterInfo);
|
||||
}
|
||||
};
|
||||
|
||||
if (HiringNewCharacter)
|
||||
{
|
||||
bool canHire = CanHire(characterInfo) && campaign.CanAfford(HireManager.GetSalaryFor(characterInfo));
|
||||
var takeoverButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementTakeControlButton")
|
||||
{
|
||||
ToolTip = canHire ? TextManager.Get("hireandtakecontrol") : TextManager.Get("hireandtakecontroldisabled"),
|
||||
ClickSound = GUISoundType.ConfirmTransaction,
|
||||
UserData = characterInfo,
|
||||
Enabled = canHire,
|
||||
OnClicked = (b, o) =>
|
||||
{
|
||||
if (GameMain.Client is not GameClient gameClient)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Client client = gameClient.ConnectedClients.FirstOrDefault(c => c.SessionId == gameClient.SessionId);
|
||||
if (!campaign.TryPurchase(client, HireManager.GetSalaryFor(characterInfo)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
gameClient.SendTakeOverBotRequest(characterInfo);
|
||||
needsHireableRefresh = true;
|
||||
campaign.ShowCampaignUI = false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
takeoverButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
|
||||
{
|
||||
bool canHireCurrently = HiringNewCharacter && CanHire(characterInfo) && campaign.CanAfford(HireManager.GetSalaryFor(characterInfo));
|
||||
btn.ToolTip = TextManager.Get(canHireCurrently ? "hireandtakecontrol" : "hireandtakecontroldisabled");
|
||||
btn.Visible = GameMain.GameSession is { AllowHrManagerBotTakeover: true };
|
||||
btn.Enabled = canHireCurrently;
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (listBox == pendingList)
|
||||
{
|
||||
@@ -501,7 +552,7 @@ namespace Barotrauma
|
||||
{
|
||||
UserData = characterInfo,
|
||||
//can't fire if there's only one character in the crew
|
||||
Enabled = currentCrew.Contains(characterInfo) && currentCrew.Count() > 1 && HasPermission,
|
||||
Enabled = currentCrew.Contains(characterInfo) && currentCrew.Count() > 1 && HasPermissionToHire,
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
var confirmDialog = new GUIMessageBox(
|
||||
@@ -534,11 +585,13 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
bool CanHire(CharacterInfo characterInfo)
|
||||
bool CanHire(CharacterInfo thisCharacterInfo)
|
||||
{
|
||||
if (!HasPermission) { return false; }
|
||||
return EnoughReputationToHire(characterInfo);
|
||||
if (!HasPermissionToHire) { return false; }
|
||||
return EnoughReputationToHire(thisCharacterInfo);
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
private bool EnoughReputationToHire(CharacterInfo characterInfo)
|
||||
@@ -709,10 +762,10 @@ namespace Barotrauma
|
||||
totalBlock.Text = TextManager.FormatCurrency(total);
|
||||
bool enoughMoney = campaign == null || campaign.CanAfford(total);
|
||||
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
|
||||
validateHiresButton.Enabled = enoughMoney && HasPermission && pendingList.Content.RectTransform.Children.Any();
|
||||
validateHiresButton.Enabled = enoughMoney && HasPermissionToHire && pendingList.Content.RectTransform.Children.Any();
|
||||
}
|
||||
|
||||
public bool ValidateHires(List<CharacterInfo> hires, bool takeMoney = true, bool createNetworkEvent = false)
|
||||
public bool ValidateHires(List<CharacterInfo> hires, bool takeMoney = true, bool createNetworkEvent = false, bool createNotification = true)
|
||||
{
|
||||
if (hires == null || hires.None()) { return false; }
|
||||
|
||||
@@ -750,11 +803,14 @@ namespace Barotrauma
|
||||
{
|
||||
UpdateLocationView(campaign.Map.CurrentLocation, true);
|
||||
SelectCharacter(null, null, null);
|
||||
var dialog = new GUIMessageBox(
|
||||
TextManager.Get("newcrewmembers"),
|
||||
TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName),
|
||||
new LocalizedString[] { TextManager.Get("Ok") });
|
||||
dialog.Buttons[0].OnClicked += dialog.Close;
|
||||
if (createNotification)
|
||||
{
|
||||
var dialog = new GUIMessageBox(
|
||||
TextManager.Get("newcrewmembers"),
|
||||
TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName),
|
||||
new LocalizedString[] { TextManager.Get("Ok") });
|
||||
dialog.Buttons[0].OnClicked += dialog.Close;
|
||||
}
|
||||
}
|
||||
|
||||
if (createNetworkEvent)
|
||||
@@ -767,7 +823,7 @@ namespace Barotrauma
|
||||
|
||||
private bool CreateRenamingComponent(GUIButton button, object userData)
|
||||
{
|
||||
if (!HasPermission || userData is not CharacterInfo characterInfo) { return false; }
|
||||
if (!HasPermissionToHire || userData is not CharacterInfo characterInfo) { return false; }
|
||||
var outerGlowFrame = new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), parentComponent.RectTransform, Anchor.Center),
|
||||
style: "OuterGlow", color: Color.Black * 0.7f);
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.4f), outerGlowFrame.RectTransform, anchor: Anchor.Center)
|
||||
@@ -875,6 +931,9 @@ namespace Barotrauma
|
||||
{
|
||||
playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement);
|
||||
}
|
||||
|
||||
// When showing this window to someone hiring a new character, the right side panels aren't needed
|
||||
pendingAndCrewGroup.Visible = !HiringNewCharacter;
|
||||
|
||||
if (needsHireableRefresh)
|
||||
{
|
||||
@@ -949,7 +1008,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPendingHires(List<int> characterInfos, Location location)
|
||||
public void SetPendingHires(List<UInt16> characterInfos, Location location)
|
||||
{
|
||||
List<CharacterInfo> oldHires = PendingHires.ToList();
|
||||
foreach (CharacterInfo pendingHire in oldHires)
|
||||
@@ -957,9 +1016,9 @@ namespace Barotrauma
|
||||
RemovePendingHire(pendingHire, createNetworkMessage: false);
|
||||
}
|
||||
PendingHires.Clear();
|
||||
foreach (int identifier in characterInfos)
|
||||
foreach (UInt16 identifier in characterInfos)
|
||||
{
|
||||
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifierUsingOriginalName() == identifier);
|
||||
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.ID == identifier);
|
||||
if (match != null)
|
||||
{
|
||||
AddPendingHire(match, createNetworkMessage: false);
|
||||
@@ -992,7 +1051,7 @@ namespace Barotrauma
|
||||
msg.WriteUInt16((ushort)PendingHires.Count);
|
||||
foreach (CharacterInfo pendingHire in PendingHires)
|
||||
{
|
||||
msg.WriteInt32(pendingHire.GetIdentifierUsingOriginalName());
|
||||
msg.WriteUInt16(pendingHire.ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1002,17 +1061,16 @@ namespace Barotrauma
|
||||
msg.WriteBoolean(validRenaming);
|
||||
if (validRenaming)
|
||||
{
|
||||
int identifier = renameCharacter.info.GetIdentifierUsingOriginalName();
|
||||
msg.WriteInt32(identifier);
|
||||
msg.WriteUInt16(renameCharacter.info.ID);
|
||||
msg.WriteString(renameCharacter.newName);
|
||||
bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.GetIdentifierUsingOriginalName() == identifier) ?? false;
|
||||
bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.ID == renameCharacter.info.ID) ?? false;
|
||||
msg.WriteBoolean(existingCrewMember);
|
||||
}
|
||||
|
||||
msg.WriteBoolean(firedCharacter != null);
|
||||
if (firedCharacter != null)
|
||||
{
|
||||
msg.WriteInt32(firedCharacter.GetIdentifier());
|
||||
msg.WriteUInt16(firedCharacter.ID);
|
||||
}
|
||||
|
||||
GameMain.Client.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
#nullable enable
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
internal class DeathPrompt
|
||||
{
|
||||
private static CoroutineHandle? createPromptCoroutine;
|
||||
|
||||
private GUIComponent? skillPanel;
|
||||
private GUIComponent? newCharacterPanel;
|
||||
private GUIComponent? takeOverBotPanel;
|
||||
|
||||
private GUIComponent? content;
|
||||
|
||||
public static GUIComponent? takeOverBotPanelFrame;
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor, because these should only be created using the Show method
|
||||
/// </summary>
|
||||
private DeathPrompt() { }
|
||||
|
||||
public static void Create(float delay)
|
||||
{
|
||||
if (!RespawnManager.UseDeathPrompt) { return; }
|
||||
if (GameMain.GameSession.DeathPrompt != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (createPromptCoroutine != null && CoroutineManager.IsCoroutineRunning(createPromptCoroutine)) { return; }
|
||||
if ((GameMain.GameSession is not { IsRunning: true })) { return; }
|
||||
|
||||
createPromptCoroutine = CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (GameMain.GameSession != null)
|
||||
{
|
||||
GameMain.GameSession.DeathPrompt = new DeathPrompt();
|
||||
GameMain.GameSession.DeathPrompt.CreatePrompt();
|
||||
SoundPlayer.OverrideMusicType = "crewdead".ToIdentifier();
|
||||
SoundPlayer.OverrideMusicDuration = 25.0f;
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
content?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
private void CreatePrompt()
|
||||
{
|
||||
const float FadeInInterval = 1.0f;
|
||||
const float FadeInDuration = 1.0f;
|
||||
|
||||
bool permadeath = GameMain.NetworkMember is { ServerSettings.RespawnMode: RespawnMode.Permadeath };
|
||||
bool ironman = GameMain.NetworkMember is { ServerSettings: { RespawnMode: RespawnMode.Permadeath, IronmanMode: true } };
|
||||
|
||||
var background = new GUICustomComponent(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), onDraw: DrawBackground)
|
||||
{
|
||||
UserData = this
|
||||
};
|
||||
background.FadeIn(wait: 0, duration: 5.0f);
|
||||
|
||||
var foreground = new GUIImage(new RectTransform(new Vector2(1.0f, GUI.RelativeHorizontalAspectRatio), background.RectTransform, Anchor.BottomCenter) { AbsoluteOffset = new Point(0, GUI.IntScale(-20)) }, "DeathScreenForeground")
|
||||
{
|
||||
Color = Color.White
|
||||
};
|
||||
foreground.FadeIn(wait: 0, duration: 5.0f);
|
||||
foreground.Pulsate(startScale: Vector2.One, Vector2.One * 0.8f, duration: 25.0f);
|
||||
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.3f), background.RectTransform, Anchor.Center))
|
||||
{
|
||||
UserData = this
|
||||
};
|
||||
frame.FadeIn(wait: 0, duration: FadeInDuration);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.1f), background.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.2f) }, string.Empty, font: GUIStyle.LargeFont, textAlignment: Alignment.TopCenter)
|
||||
{
|
||||
TextGetter = () =>
|
||||
{
|
||||
return GameMain.Client.EndRoundTimeRemaining > 0.0f ?
|
||||
TextManager.GetWithVariable("endinground", "[time]", ToolBox.SecondsToReadableTime(GameMain.Client.EndRoundTimeRemaining))
|
||||
.Fallback(ToolBox.SecondsToReadableTime(GameMain.Client.EndRoundTimeRemaining), useDefaultLanguageIfFound: false) :
|
||||
string.Empty;
|
||||
}
|
||||
};
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), frame.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
//"you have died" header
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.Get("deathprompt.header"), font: GUIStyle.LargeFont, textAlignment: Alignment.Center)
|
||||
.FadeIn(wait: 0, duration: FadeInDuration);
|
||||
|
||||
var causeOfDeath = GameMain.Client?.Character?.CauseOfDeath;
|
||||
if (causeOfDeath != null && causeOfDeath.Type != CauseOfDeathType.Unknown)
|
||||
{
|
||||
var causeOfDeathDescription = causeOfDeath.Affliction != null ?
|
||||
causeOfDeath.Affliction.SelfCauseOfDeathDescription :
|
||||
TextManager.Get("Self_CauseOfDeathDescription." + causeOfDeath.Type.ToString(), "Self_CauseOfDeathDescription.Damage");
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), causeOfDeathDescription)
|
||||
.FadeIn(wait: FadeInInterval * 2, duration: FadeInDuration);
|
||||
}
|
||||
|
||||
if (permadeath)
|
||||
{
|
||||
if (ironman)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
|
||||
TextManager.Get("deathprompt.permadeathnotification") + "\n\n" + TextManager.Get("deathprompt.ironmanexplanation"), wrap: true)
|
||||
.FadeIn(wait: FadeInInterval * 3, duration: FadeInDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
|
||||
TextManager.Get("deathprompt.permadeathnotification") + '\n' + TextManager.Get("deathprompt.takeoverbotexplanation"), wrap: true)
|
||||
.FadeIn(wait: FadeInInterval * 3, duration: FadeInDuration);
|
||||
}
|
||||
}
|
||||
else if (RespawnManager.SkillLossPercentageOnDeath > 0)
|
||||
{
|
||||
string skillLossAmount = ((int)RespawnManager.SkillLossPercentageOnDeath).ToString();
|
||||
string skillLossText = $"‖color: { XMLExtensions.ToStringHex(GUIStyle.Red)}‖{skillLossAmount}‖end‖";
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
|
||||
RichString.Rich(TextManager.GetWithVariable("respawnskillpenalty", "[percentage]", skillLossText)))
|
||||
.FadeIn(wait: FadeInInterval * 3, duration: FadeInDuration);
|
||||
};
|
||||
|
||||
//"what do you want to do" buttons in the middle
|
||||
//-------------------------------------------------------------------------------------------------------
|
||||
|
||||
var decisionButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), content.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
if (ironman)
|
||||
{
|
||||
// The only option is to spectate
|
||||
var buttonContainerMiddle = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), decisionButtonContainer.RectTransform), childAnchor: Anchor.Center);
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainerMiddle.RectTransform), TextManager.Get("spectatebutton"))
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: true);
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
}.FadeIn(wait: FadeInInterval * 4, duration: FadeInDuration, alsoChildren: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var buttonContainerLeft = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), decisionButtonContainer.RectTransform));
|
||||
var buttonContainerRight = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), decisionButtonContainer.RectTransform));
|
||||
|
||||
// The default "I'll wait" button
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), buttonContainerLeft.RectTransform), TextManager.Get("respawnquestionpromptwait"))
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: true);
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
}.FadeIn(wait: FadeInInterval * 4, duration: FadeInDuration, alsoChildren: true);
|
||||
|
||||
if (permadeath)
|
||||
{
|
||||
if (GameMain.Client != null && GameMain.Client.ServerSettings.AllowBotTakeoverOnPermadeath)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), buttonContainerRight.RectTransform), TextManager.Get("deathprompt.takeoverbot"))
|
||||
{
|
||||
Enabled = false,
|
||||
OnAddedToGUIUpdateList = (component) =>
|
||||
{
|
||||
component.Enabled = GetAvailableBots().Any();
|
||||
},
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (takeOverBotPanel == null)
|
||||
{
|
||||
CreateTakeOverBotPanel(frame, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
takeOverBotPanel.Parent?.RemoveChild(takeOverBotPanel);
|
||||
takeOverBotPanel = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}.FadeIn(wait: FadeInInterval * 4, duration: FadeInDuration, alsoChildren: true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), buttonContainerRight.RectTransform), TextManager.Get("deathprompt.respawnnow"))
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: false);
|
||||
Close();
|
||||
return true;
|
||||
},
|
||||
Enabled = GameMain.NetworkMember is { ServerSettings.RespawnMode: RespawnMode.MidRound }
|
||||
}.FadeIn(wait: FadeInInterval * 4, duration: FadeInDuration, alsoChildren: true);
|
||||
}
|
||||
|
||||
//"info buttons" at the bottom
|
||||
//-------------------------------------------------------------------------------------------------------
|
||||
|
||||
var infoButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), content.RectTransform), childAnchor: Anchor.TopRight)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.025f
|
||||
};
|
||||
if (permadeath)
|
||||
{
|
||||
if (Level.IsLoadedFriendlyOutpost)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(0.6f, 1.0f), infoButtonContainer.RectTransform), TextManager.Get("npctitle.hrmanager"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign is { } campaign)
|
||||
{
|
||||
campaign.ShowCampaignUI = true;
|
||||
campaign.CampaignUI?.SelectTab(CampaignMode.InteractionType.Crew);
|
||||
}
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
}.FadeIn(wait: FadeInInterval * 5, duration: FadeInDuration, alsoChildren: true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(0.6f, 1.0f), infoButtonContainer.RectTransform), TextManager.Get("deathprompt.showskills"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (skillPanel == null)
|
||||
{
|
||||
CreateSkillPanel(frame, GameMain.Client?.Character?.Info ?? GameMain.Client?.CharacterInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
skillPanel.Parent?.RemoveChild(skillPanel);
|
||||
skillPanel = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}.FadeIn(wait: FadeInInterval * 5, duration: FadeInDuration, alsoChildren: true);
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.6f, 1.0f), infoButtonContainer.RectTransform), TextManager.Get("deathprompt.newcharacter"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (newCharacterPanel == null)
|
||||
{
|
||||
CreateNewCharacterPanel(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
newCharacterPanel.Parent?.RemoveChild(newCharacterPanel);
|
||||
newCharacterPanel = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}.FadeIn(wait: FadeInInterval * 5, duration: FadeInDuration, alsoChildren: true);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
/*new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), infoButtonContainer.RectTransform), "Respawn settings", style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}.FadeIn(wait: FadeInInterval * 5, duration: FadeInDuration, alsoChildren: true);*/
|
||||
|
||||
this.content = background;
|
||||
}
|
||||
|
||||
private void CreateSkillPanel(GUIComponent parent, CharacterInfo? characterInfo)
|
||||
{
|
||||
if (characterInfo == null) { return; }
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), parent.RectTransform, Anchor.CenterRight, Pivot.CenterLeft));
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.8f), frame.RectTransform, Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), content.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
var middleColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1.0f), content.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1.0f), content.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
var leftHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), leftColumn.RectTransform), TextManager.Get("Skills"), font: GUIStyle.SubHeadingFont, textColor: GUIStyle.TextColorBright);
|
||||
var middleHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), middleColumn.RectTransform), TextManager.Get("deathprompt.SkillsLostHeader"), font: GUIStyle.SubHeadingFont, textColor: GUIStyle.TextColorBright);
|
||||
var rightHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), rightColumn.RectTransform), TextManager.Get("deathprompt.respawnnow"), font: GUIStyle.SubHeadingFont, textColor: GUIStyle.TextColorBright);
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(leftHeader, middleHeader, rightHeader);
|
||||
|
||||
foreach (var skill in characterInfo.Job.GetSkills().OrderByDescending(s => s.Level))
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), leftColumn.RectTransform), skill.DisplayName);
|
||||
|
||||
int previousSkill = (int)skill.HighestLevelDuringRound;
|
||||
int reducedSkill = (int)RespawnManager.GetReducedSkill(characterInfo, skill, RespawnManager.SkillLossPercentageOnDeath);
|
||||
int reducedSkillOnImmediateRespawn = (int)RespawnManager.GetReducedSkill(characterInfo, skill, RespawnManager.SkillLossPercentageOnImmediateRespawn, currentSkillLevel: reducedSkill);
|
||||
|
||||
int skillLoss = reducedSkill - previousSkill;
|
||||
int skillLossOnImmediateRespawn = reducedSkillOnImmediateRespawn - previousSkill;
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), middleColumn.RectTransform),
|
||||
RichString.Rich($"{reducedSkill} (‖color:{XMLExtensions.ToStringHex(GUIStyle.Red)}‖{skillLoss}‖end‖)"));
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), rightColumn.RectTransform),
|
||||
RichString.Rich($"{reducedSkillOnImmediateRespawn} (‖color:{XMLExtensions.ToStringHex(GUIStyle.Red)}‖{skillLossOnImmediateRespawn}‖end‖)"));
|
||||
}
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 0.15f), leftColumn.RectTransform, Anchor.BottomLeft), TextManager.Get("Close"), style: "GUIButtonSmall")
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
frame.Parent?.RemoveChild(frame);
|
||||
skillPanel = null;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
skillPanel = frame;
|
||||
}
|
||||
|
||||
private void CreateNewCharacterPanel(GUIComponent parent)
|
||||
{
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.5f), parent.RectTransform, Anchor.CenterRight, Pivot.CenterLeft));
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: false)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(content, alwaysAllowEditing: true, createPendingText: false);
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.15f), content.RectTransform), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform, Anchor.BottomLeft), TextManager.Get("Cancel"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
frame.Parent?.RemoveChild(frame);
|
||||
newCharacterPanel = null;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform, Anchor.BottomLeft), TextManager.Get("ApplySettingsYes"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(onYes: () =>
|
||||
{
|
||||
frame.Parent?.RemoveChild(frame);
|
||||
newCharacterPanel = null;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
newCharacterPanel = frame;
|
||||
}
|
||||
|
||||
public static void CreateTakeOverBotPanel()
|
||||
{
|
||||
var panelHolder = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.3f), GUI.Canvas, Anchor.Center));
|
||||
var takeOverBotPanel = CreateTakeOverBotPanel(panelHolder, deathPrompt: null);
|
||||
if (takeOverBotPanel != null)
|
||||
{
|
||||
takeOverBotPanel.RectTransform.SetPosition(Anchor.Center);
|
||||
GUIMessageBox.MessageBoxes.Add(panelHolder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static because the "take over bot" panel can be accessed outside the death prompt too
|
||||
/// </summary>
|
||||
private static GUIComponent? CreateTakeOverBotPanel(GUIComponent parent, DeathPrompt? deathPrompt)
|
||||
{
|
||||
if (GameMain.GameSession?.CrewManager == null) { return null; }
|
||||
if (GameMain.GameSession?.Campaign is not MultiPlayerCampaign campaign) { return null; }
|
||||
|
||||
if (campaign.CampaignUI == null) { campaign.InitCampaignUI(); }
|
||||
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), parent.RectTransform, Anchor.CenterRight, Pivot.CenterLeft));
|
||||
takeOverBotPanelFrame = frame;
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: false)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
var botList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.9f), content.RectTransform));
|
||||
foreach (CharacterInfo c in GetAvailableBots())
|
||||
{
|
||||
var characterFrame = campaign.CampaignUI?.CrewManagement.CreateCharacterFrame(c, botList, hideSalary: true);
|
||||
if (characterFrame != null)
|
||||
{
|
||||
characterFrame.UserData = c;
|
||||
}
|
||||
}
|
||||
botList.UpdateScrollBarSize();
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.15f), content.RectTransform), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform, Anchor.BottomLeft), TextManager.Get("Cancel"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(frame.Parent);
|
||||
frame.Parent?.RemoveChild(frame);
|
||||
if (deathPrompt != null)
|
||||
{
|
||||
deathPrompt.takeOverBotPanel = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform, Anchor.BottomLeft), TextManager.Get("inputtype.select"), style: "GUIButtonSmall")
|
||||
{
|
||||
Enabled = false,
|
||||
OnAddedToGUIUpdateList = (component) =>
|
||||
{
|
||||
component.Enabled = botList.SelectedData is CharacterInfo;
|
||||
},
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (botList.SelectedData is CharacterInfo selectedCharacter && GameMain.Client is GameClient client)
|
||||
{
|
||||
client.SendTakeOverBotRequest(selectedCharacter);
|
||||
GUIMessageBox.MessageBoxes.Remove(frame.Parent);
|
||||
deathPrompt?.Close();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Conditions for sending bot takeover request not met");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (deathPrompt != null)
|
||||
{
|
||||
deathPrompt.takeOverBotPanel = frame;
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
private static IEnumerable<CharacterInfo> GetAvailableBots()
|
||||
{
|
||||
if (GameMain.GameSession?.CrewManager is { } crewManager)
|
||||
{
|
||||
return crewManager.GetCharacterInfos().Where(c =>
|
||||
/*either an alive bot */
|
||||
c is { Character.IsBot: true, Character.IsDead: false } ||
|
||||
/* or a newly hired bot that hasn't spawned yet */
|
||||
(c.IsNewHire && c.Character == null));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Enumerable.Empty<CharacterInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBackground(SpriteBatch spriteBatch, GUICustomComponent guiCustomComponent)
|
||||
{
|
||||
var background = GUIStyle.GetComponentStyle("DeathScreenBackground");
|
||||
if (background != null)
|
||||
{
|
||||
GUI.DrawBackgroundSprite(spriteBatch, background.GetDefaultSprite(), Color.White * (guiCustomComponent.Color.A / 255.0f));
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (GameMain.GameSession != null)
|
||||
{
|
||||
GameMain.GameSession.DeathPrompt = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CloseBotPanel()
|
||||
{
|
||||
if (takeOverBotPanelFrame is GUIComponent frame)
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(frame.Parent);
|
||||
frame.Parent?.RemoveChild(frame);
|
||||
}
|
||||
takeOverBotPanelFrame = null;
|
||||
}
|
||||
}
|
||||
@@ -717,9 +717,9 @@ namespace Barotrauma
|
||||
private static readonly Queue<GUIComponent> removals = new Queue<GUIComponent>();
|
||||
private static readonly Queue<GUIComponent> additions = new Queue<GUIComponent>();
|
||||
// A helpers list for all elements that have a draw order less than 0.
|
||||
private static readonly List<GUIComponent> first = new List<GUIComponent>();
|
||||
private static readonly List<GUIComponent> firstAdditions = new List<GUIComponent>();
|
||||
// A helper list for all elements that have a draw order greater than 0.
|
||||
private static readonly List<GUIComponent> last = new List<GUIComponent>();
|
||||
private static readonly List<GUIComponent> lastAdditions = new List<GUIComponent>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds the component on the addition queue.
|
||||
@@ -737,11 +737,11 @@ namespace Barotrauma
|
||||
if (!component.Visible) { return; }
|
||||
if (component.UpdateOrder < 0)
|
||||
{
|
||||
first.Add(component);
|
||||
firstAdditions.Add(component);
|
||||
}
|
||||
else if (component.UpdateOrder > 0)
|
||||
{
|
||||
last.Add(component);
|
||||
lastAdditions.Add(component);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -800,9 +800,9 @@ namespace Barotrauma
|
||||
RemoveFromUpdateList(component);
|
||||
}
|
||||
}
|
||||
ProcessHelperList(first);
|
||||
ProcessHelperList(firstAdditions);
|
||||
ProcessAdditions();
|
||||
ProcessHelperList(last);
|
||||
ProcessHelperList(lastAdditions);
|
||||
ProcessRemovals();
|
||||
}
|
||||
}
|
||||
@@ -897,7 +897,7 @@ namespace Barotrauma
|
||||
|
||||
public static IEnumerable<GUIComponent> GetAdditions()
|
||||
{
|
||||
return additions;
|
||||
return additions.Union(firstAdditions).Union(lastAdditions);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -2171,6 +2171,28 @@ namespace Barotrauma
|
||||
return frame;
|
||||
}
|
||||
|
||||
public static GUITextBox CreateTextBoxWithPlaceholder(RectTransform rectT, string text, LocalizedString placeholder)
|
||||
{
|
||||
var holder = new GUIFrame(rectT, style: null);
|
||||
var textBox = new GUITextBox(new RectTransform(Vector2.One, holder.RectTransform, Anchor.CenterLeft), text, createClearButton: false);
|
||||
var placeholderElement = new GUITextBlock(new RectTransform(Vector2.One, holder.RectTransform, Anchor.CenterLeft),
|
||||
textColor: Color.DarkGray * 0.6f,
|
||||
text: placeholder,
|
||||
textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
new GUICustomComponent(new RectTransform(Vector2.Zero, holder.RectTransform),
|
||||
onUpdate: delegate { placeholderElement.RectTransform.NonScaledSize = textBox.Frame.RectTransform.NonScaledSize; });
|
||||
|
||||
textBox.OnSelected += delegate { placeholderElement.Visible = false; };
|
||||
textBox.OnDeselected += delegate { placeholderElement.Visible = textBox.Text.IsNullOrWhiteSpace(); };
|
||||
|
||||
placeholderElement.Visible = string.IsNullOrWhiteSpace(text);
|
||||
return textBox;
|
||||
}
|
||||
|
||||
public static void NotifyPrompt(LocalizedString header, LocalizedString body)
|
||||
{
|
||||
GUIMessageBox msgBox = new GUIMessageBox(header, body, new[] { TextManager.Get("Ok") }, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
|
||||
|
||||
@@ -815,7 +815,8 @@ namespace Barotrauma
|
||||
protected virtual void SetAlpha(float a)
|
||||
{
|
||||
color = new Color(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, a);
|
||||
hoverColor = new Color(hoverColor.R / 255.0f, hoverColor.G / 255.0f, hoverColor.B / 255.0f, a);;
|
||||
hoverColor = new Color(hoverColor.R / 255.0f, hoverColor.G / 255.0f, hoverColor.B / 255.0f, a);
|
||||
disabledColor = new Color(disabledColor.R / 255.0f, disabledColor.G / 255.0f, disabledColor.B / 255.0f, a);
|
||||
}
|
||||
|
||||
public virtual void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectInflate = null)
|
||||
@@ -835,15 +836,29 @@ namespace Barotrauma
|
||||
flashColor = (color == null) ? GUIStyle.Red : (Color)color;
|
||||
}
|
||||
|
||||
public void FadeOut(float duration, bool removeAfter, float wait = 0.0f, Action onRemove = null)
|
||||
public void FadeOut(float duration, bool removeAfter, float wait = 0.0f, Action onRemove = null, bool alsoChildren = false)
|
||||
{
|
||||
CoroutineManager.StartCoroutine(LerpAlpha(0.0f, duration, removeAfter, wait, onRemove));
|
||||
if (alsoChildren)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.FadeOut(duration, removeAfter, wait, onRemove, alsoChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeIn(float wait, float duration)
|
||||
public void FadeIn(float wait, float duration, bool alsoChildren = false)
|
||||
{
|
||||
SetAlpha(0.0f);
|
||||
CoroutineManager.StartCoroutine(LerpAlpha(1.0f, duration, false, wait));
|
||||
if (alsoChildren)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.FadeIn(wait, duration, alsoChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SlideIn(float wait, float duration, int amount, SlideDirection direction)
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (sprite?.Texture is { IsDisposed: false })
|
||||
{
|
||||
spriteBatch.Draw(sprite.Texture, Rect.Center.ToVector2(), sourceRect, currentColor * (currentColor.A / 255.0f), Rotation, origin,
|
||||
spriteBatch.Draw(sprite.Texture, new Vector2(Rect.X + Rect.Width / 2.0f, Rect.Y + Rect.Height / 2.0f), sourceRect, currentColor * (currentColor.A / 255.0f), Rotation, origin,
|
||||
Scale, SpriteEffects, 0.0f);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace Barotrauma
|
||||
{
|
||||
public class GUIMessageBox : GUIFrame
|
||||
{
|
||||
#warning TODO: change this to List<GUIMessageBox> and fix incorrect uses of this list
|
||||
public readonly static List<GUIComponent> MessageBoxes = new List<GUIComponent>();
|
||||
private static int DefaultWidth
|
||||
{
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace Barotrauma
|
||||
}
|
||||
set
|
||||
{
|
||||
if (MathUtils.NearlyEqual(value, floatValue)) { return; }
|
||||
if (Math.Abs(value - floatValue) < 0.0001f && MathUtils.NearlyEqual(value, floatValue)) { return; }
|
||||
floatValue = value;
|
||||
ClampFloatValue();
|
||||
float newValue = floatValue;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -25,6 +26,11 @@ namespace Barotrauma
|
||||
|
||||
public delegate void OnValueChangedHandler(GUISelectionCarousel<T> carousel);
|
||||
public OnValueChangedHandler? OnValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Are there some conditions for selecting a particular element?
|
||||
/// </summary>
|
||||
public Func<T, bool>? ElementSelectionCondition { get; set; }
|
||||
|
||||
public GUITextBlock TextBlock { get; private set; }
|
||||
|
||||
@@ -89,35 +95,9 @@ namespace Barotrauma
|
||||
GUIStyle.Apply(TextBlock, "TextBlock", this);
|
||||
RightButton = new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), layoutGroup.RectTransform), style: "GUIButtonToggleRight");
|
||||
GUIStyle.Apply(RightButton, "RightButton", this);
|
||||
|
||||
RightButton.OnClicked += (btn, userData) =>
|
||||
{
|
||||
if (elements.Count < 2) { return false; }
|
||||
if (SelectedElement == null)
|
||||
{
|
||||
SelectElement(elements.First());
|
||||
}
|
||||
else
|
||||
{
|
||||
int newIndex = (elements.IndexOf(SelectedElement) + 1) % elements.Count;
|
||||
SelectElement(elements[newIndex]);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
LeftButton.OnClicked += (btn, userData) =>
|
||||
{
|
||||
if (elements.Count < 2) { return false; }
|
||||
if (SelectedElement == null)
|
||||
{
|
||||
SelectElement(elements.First());
|
||||
}
|
||||
else
|
||||
{
|
||||
int newIndex = MathUtils.PositiveModulo((elements.IndexOf(SelectedElement) - 1), elements.Count);
|
||||
SelectElement(elements[newIndex]);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
RightButton.OnClicked += (_, _) => SelectNextValidElement();
|
||||
LeftButton.OnClicked += (_, _) => SelectNextValidElement(directionLeft: true);
|
||||
|
||||
if (newElements != null && newElements.Any())
|
||||
{
|
||||
@@ -140,9 +120,11 @@ namespace Barotrauma
|
||||
SelectElement(null);
|
||||
return;
|
||||
}
|
||||
if (elements.FirstOrDefault(e => value.Equals(e.value)) is { } element)
|
||||
var matchingElement = elements.Where(e => value.Equals(e.value)) // selection is in the set of possible values
|
||||
.FirstOrDefault(e => ElementSelectionCondition == null || ElementSelectionCondition(e.value)); // selection matches extra conditions, if any
|
||||
if (matchingElement != null)
|
||||
{
|
||||
SelectElement(element);
|
||||
SelectElement(matchingElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,5 +169,42 @@ namespace Barotrauma
|
||||
SelectElement(newElement);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Refresh the current selection, for example if there are conditions for which elements are valid, and those might have changed
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
if (SelectedElement != null)
|
||||
{
|
||||
if (ElementSelectionCondition == null || ElementSelectionCondition(SelectedElement.value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SelectElement(elements.FirstOrDefault(e => ElementSelectionCondition == null || ElementSelectionCondition(e.value)));
|
||||
}
|
||||
|
||||
private bool SelectNextValidElement(bool directionLeft = false)
|
||||
{
|
||||
if (elements.Count < 2) { return false; }
|
||||
|
||||
// Try to find a valid next/previous element
|
||||
int currentIndex = SelectedElement == null ? -1 : elements.IndexOf(SelectedElement);
|
||||
int newIndex = currentIndex;
|
||||
for (int i = 0; i < elements.Count; i++)
|
||||
{
|
||||
newIndex = directionLeft ? MathUtils.PositiveModulo((newIndex - 1), elements.Count) : (newIndex + 1) % elements.Count;
|
||||
if (ElementSelectionCondition == null || ElementSelectionCondition(elements[newIndex].value))
|
||||
{
|
||||
SelectElement(elements[newIndex]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No valid elements found
|
||||
SelectElement(null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,8 +438,11 @@ namespace Barotrauma
|
||||
|
||||
protected override void SetAlpha(float a)
|
||||
{
|
||||
// base.SetAlpha(a);
|
||||
textColor = new Color(TextColor.R / 255.0f, TextColor.G / 255.0f, TextColor.B / 255.0f, a);
|
||||
textColor = new Color(TextColor, a);
|
||||
if (hoverTextColor.HasValue)
|
||||
{
|
||||
hoverTextColor = new Color(hoverTextColor.Value, a);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2108,13 +2108,13 @@ namespace Barotrauma
|
||||
deliveryPrompt.Buttons[0].OnClicked = (btn, userdata) =>
|
||||
{
|
||||
ConfirmPurchase(deliverImmediately: true);
|
||||
deliveryPrompt.Close();
|
||||
deliveryPrompt?.Close();
|
||||
return true;
|
||||
};
|
||||
deliveryPrompt.Buttons[1].OnClicked = (btn, userdata) =>
|
||||
{
|
||||
ConfirmPurchase(deliverImmediately: false);
|
||||
deliveryPrompt.Close();
|
||||
deliveryPrompt?.Close();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -742,8 +742,8 @@ namespace Barotrauma
|
||||
|
||||
private (LocalizedString header, LocalizedString body) GetItemTransferWarningText()
|
||||
{
|
||||
var header = TextManager.Get("itemtransferheader").Fallback("lowfuelheader");
|
||||
var body = TextManager.Get("itemtransferwarning").Fallback("lowfuelwarning");
|
||||
var header = TextManager.Get("itemtransferheader").Fallback("lowfuelheader", useDefaultLanguageIfFound: false);
|
||||
var body = TextManager.Get("itemtransferwarning").Fallback("lowfuelwarning", useDefaultLanguageIfFound: false);
|
||||
return (header, body);
|
||||
}
|
||||
|
||||
|
||||
@@ -320,7 +320,61 @@ namespace Barotrauma
|
||||
var reputationButton = createTabButton(InfoFrameTab.Reputation, "reputation");
|
||||
|
||||
var balanceFrame = new GUIFrame(new RectTransform(new Point(innerLayoutGroup.Rect.Width, innerLayoutGroup.Rect.Height - infoFrameHolderHeight), parent: innerLayoutGroup.RectTransform), style: "InnerFrame");
|
||||
GUITextBlock balanceText = new GUITextBlock(new RectTransform(Vector2.One, balanceFrame.RectTransform), string.Empty, textAlignment: Alignment.Right);
|
||||
GUILayoutGroup salaryFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.66f, 1f), balanceFrame.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
|
||||
GUIScrollBar salaryScrollBar = null;
|
||||
GUITextBlock salaryPercentage = null;
|
||||
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign)
|
||||
{
|
||||
float value = campaignMode.Bank.RewardDistribution;
|
||||
GUITextBlock salaryText = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1f), salaryFrame.RectTransform), TextManager.Get("defaultsalary"), textAlignment: Alignment.Center)
|
||||
{
|
||||
AutoScaleHorizontal = true
|
||||
};
|
||||
salaryScrollBar = new GUIScrollBar(new RectTransform(new Vector2(0.4f, 1f), salaryFrame.RectTransform), barSize: 0.1f, style: "GUISlider")
|
||||
{
|
||||
Range = new Vector2(0, 1),
|
||||
BarScrollValue = value / 100f,
|
||||
Step = 0.01f,
|
||||
BarSize = 0.1f,
|
||||
};
|
||||
|
||||
salaryPercentage = new GUITextBlock(new RectTransform(new Vector2(0.15f, 1f), salaryFrame.RectTransform), "0", textAlignment: Alignment.Center)
|
||||
{
|
||||
Text = ValueToPercentage(RoundRewardDistribution(salaryScrollBar.BarScroll, salaryScrollBar.Step))
|
||||
};
|
||||
|
||||
salaryScrollBar.OnMoved = (scrollBar, value) =>
|
||||
{
|
||||
salaryPercentage.Text = ValueToPercentage(RoundRewardDistribution(value, scrollBar.Step));
|
||||
return true;
|
||||
};
|
||||
salaryScrollBar.OnReleased = (bar, scroll) =>
|
||||
{
|
||||
int newRewardDistribution = RoundRewardDistribution(scroll, bar.Step);
|
||||
SetRewardDistribution(Option.None, newRewardDistribution);
|
||||
return true;
|
||||
};
|
||||
|
||||
var resetButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), salaryFrame.RectTransform), TextManager.Get("ResetSalaries"), style: "GUIButtonSmall")
|
||||
{
|
||||
TextBlock = { AutoScaleHorizontal = true },
|
||||
ToolTip = TextManager.Get("resetsalaries.tooltip"),
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
GUI.AskForConfirmation(TextManager.Get("ResetSalaries"), TextManager.Get("ResetSalaries.Warning"), onConfirm: ResetRewardDistributions);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void UpdateSliderEnabled()
|
||||
=> salaryScrollBar.Enabled = resetButton.Enabled = CampaignMode.AllowedToManageWallets();
|
||||
UpdateSliderEnabled();
|
||||
|
||||
Identifier defaultSalaryEventIdentifier = "DefaultSalarySlider".ToIdentifier();
|
||||
GameMain.Client?.OnPermissionChanged?.RegisterOverwriteExisting(defaultSalaryEventIdentifier, _ => UpdateSliderEnabled());
|
||||
}
|
||||
GUITextBlock balanceText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1f), balanceFrame.RectTransform, Anchor.TopRight), string.Empty, textAlignment: Alignment.Right);
|
||||
if (GameMain.IsMultiplayer)
|
||||
{
|
||||
balanceText.ToolTip = TextManager.Get("bankdescription");
|
||||
@@ -343,6 +397,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (!e.Owner.IsNone()) { return; }
|
||||
SetBalanceText(balanceText, e.Wallet.Balance);
|
||||
|
||||
if (salaryPercentage is not null && salaryScrollBar is not null)
|
||||
{
|
||||
float rewardDistribution = e.Wallet.RewardDistribution;
|
||||
salaryScrollBar.BarScrollValue = rewardDistribution / 100f;
|
||||
salaryPercentage.Text = ValueToPercentage(rewardDistribution);
|
||||
}
|
||||
});
|
||||
registeredEvents.Add(eventIdentifier);
|
||||
|
||||
@@ -350,6 +411,9 @@ namespace Barotrauma
|
||||
{
|
||||
text.Text = TextManager.GetWithVariable("bankbalanceformat", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", balance));
|
||||
}
|
||||
|
||||
LocalizedString ValueToPercentage(float value)
|
||||
=> TextManager.GetWithVariable("percentageformat", "[value]", $"{(int)MathF.Round(value)}");
|
||||
}
|
||||
|
||||
var submarineButton = createTabButton(InfoFrameTab.Submarine, "submarine");
|
||||
@@ -1037,11 +1101,10 @@ namespace Barotrauma
|
||||
{
|
||||
int newRewardDistribution = RoundRewardDistribution(scroll, bar.Step);
|
||||
if (newRewardDistribution == targetWallet.RewardDistribution) { return false; }
|
||||
SetRewardDistribution(character, newRewardDistribution);
|
||||
SetRewardDistribution(Option.Some(character), newRewardDistribution);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
int RoundRewardDistribution(float scroll, float step) => (int)MathUtils.RoundTowardsClosest(scroll * 100, step * 100);
|
||||
|
||||
SetRewardText(targetWallet.RewardDistribution, rewardBlock);
|
||||
|
||||
@@ -1201,6 +1264,7 @@ namespace Barotrauma
|
||||
{
|
||||
moneyBlock.Text = TextManager.FormatCurrency(e.Info.Balance);
|
||||
salarySlider.BarScrollValue = e.Info.RewardDistribution / 100f;
|
||||
SetRewardText(e.Info.RewardDistribution, rewardBlock);
|
||||
}
|
||||
|
||||
UpdateAllInputs();
|
||||
@@ -1311,20 +1375,29 @@ namespace Barotrauma
|
||||
transfer.Write(msg);
|
||||
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
static void SetRewardDistribution(Character character, int newValue)
|
||||
{
|
||||
INetSerializableStruct transfer = new NetWalletSetSalaryUpdate
|
||||
{
|
||||
Target = character.ID,
|
||||
NewRewardDistribution = newValue
|
||||
};
|
||||
IWriteMessage msg = new WriteOnlyMessage().WithHeader(ClientPacketHeader.REWARD_DISTRIBUTION);
|
||||
transfer.Write(msg);
|
||||
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
}
|
||||
|
||||
static void SetRewardDistribution(Option<Character> character, int newValue)
|
||||
{
|
||||
INetSerializableStruct transfer = new NetWalletSetSalaryUpdate
|
||||
{
|
||||
Target = character.Select(c => c.ID),
|
||||
NewRewardDistribution = newValue
|
||||
};
|
||||
IWriteMessage msg = new WriteOnlyMessage().WithHeader(ClientPacketHeader.REWARD_DISTRIBUTION);
|
||||
transfer.Write(msg);
|
||||
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
static void ResetRewardDistributions()
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage().WithHeader(ClientPacketHeader.RESET_REWARD_DISTRIBUTION);
|
||||
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
static int RoundRewardDistribution(float scroll, float step)
|
||||
=> (int)MathUtils.RoundTowardsClosest(scroll * 100, step * 100);
|
||||
|
||||
private GUIComponent CreateClientInfoFrame(GUIFrame frame, Client client, Sprite permissionIcon = null)
|
||||
{
|
||||
GUIComponent paddedFrame;
|
||||
|
||||
@@ -133,43 +133,47 @@ namespace Barotrauma
|
||||
GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null);
|
||||
GUILayoutGroup playerFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), containerFrame.RectTransform, Anchor.TopCenter));
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
|
||||
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
|
||||
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
|
||||
|
||||
// TODO: What is CampaignCharacterDiscarded and can it be relevant in permadeath mode?
|
||||
if (!GameMain.NetLobbyScreen.PermadeathMode)
|
||||
{
|
||||
IgnoreLayoutGroups = false,
|
||||
TextBlock =
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
|
||||
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
|
||||
{
|
||||
AutoScaleHorizontal = true
|
||||
}
|
||||
};
|
||||
|
||||
newCharacterBox.OnClicked = (button, o) =>
|
||||
{
|
||||
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
|
||||
IgnoreLayoutGroups = false,
|
||||
TextBlock =
|
||||
{
|
||||
newCharacterBox.Text = TextManager.Get("settings");
|
||||
if (TabMenu.PendingChangesFrame != null)
|
||||
{
|
||||
NetLobbyScreen.CreateChangesPendingFrame(TabMenu.PendingChangesFrame);
|
||||
}
|
||||
AutoScaleHorizontal = true
|
||||
}
|
||||
};
|
||||
|
||||
OpenMenu();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenMenu();
|
||||
return true;
|
||||
|
||||
void OpenMenu()
|
||||
newCharacterBox.OnClicked = (button, o) =>
|
||||
{
|
||||
characterSettingsFrame!.Visible = true;
|
||||
content.Visible = false;
|
||||
}
|
||||
};
|
||||
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
|
||||
{
|
||||
newCharacterBox.Text = TextManager.Get("settings");
|
||||
if (TabMenu.PendingChangesFrame != null)
|
||||
{
|
||||
NetLobbyScreen.CreateChangesPendingFrame(TabMenu.PendingChangesFrame);
|
||||
}
|
||||
|
||||
OpenMenu();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenMenu();
|
||||
return true;
|
||||
|
||||
void OpenMenu()
|
||||
{
|
||||
characterSettingsFrame!.Visible = true;
|
||||
content.Visible = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomCenter);
|
||||
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
|
||||
@@ -177,6 +181,7 @@ namespace Barotrauma
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
|
||||
GameMain.NetLobbyScreen.CampaignCharacterDiscarded = false;
|
||||
characterSettingsFrame.Visible = false;
|
||||
content.Visible = true;
|
||||
return true;
|
||||
|
||||
@@ -773,7 +773,7 @@ namespace Barotrauma
|
||||
subItems ??= GetSubItems();
|
||||
return subItems.Any(i =>
|
||||
i.Prefab.SwappableItem != null &&
|
||||
!i.HiddenInGame && i.AllowSwapping &&
|
||||
!i.IsHidden && i.AllowSwapping &&
|
||||
(i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) &&
|
||||
Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t)));
|
||||
}
|
||||
@@ -876,7 +876,7 @@ namespace Barotrauma
|
||||
{
|
||||
parent.Content.ClearChildren();
|
||||
currentUpgradeCategory = category;
|
||||
var entitiesOnSub = submarine.GetItems(true).Where(i => submarine.IsEntityFoundOnThisSub(i, true) && !i.HiddenInGame && i.AllowSwapping && i.Prefab.SwappableItem != null && category.ItemTags.Any(t => i.HasTag(t))).ToList();
|
||||
var entitiesOnSub = submarine.GetItems(true).Where(i => submarine.IsEntityFoundOnThisSub(i, true) && !i.IsHidden && i.AllowSwapping && i.Prefab.SwappableItem != null && category.ItemTags.Any(t => i.HasTag(t))).ToList();
|
||||
|
||||
foreach (Item item in entitiesOnSub)
|
||||
{
|
||||
|
||||
@@ -115,7 +115,6 @@ namespace Barotrauma
|
||||
if (IsMouseOver || (!RequireMouseOn && SelectedWidgets.Contains(this) && PlayerInput.PrimaryMouseButtonHeld()))
|
||||
{
|
||||
Hovered?.Invoke();
|
||||
System.Diagnostics.Debug.WriteLine("hovered");
|
||||
if (RequireMouseOn || PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
if ((multiselect && !SelectedWidgets.Contains(this)) || SelectedWidgets.None())
|
||||
|
||||
@@ -1147,19 +1147,14 @@ namespace Barotrauma
|
||||
if (save)
|
||||
{
|
||||
GUI.SetSavingIndicatorState(true);
|
||||
|
||||
GameSession.Campaign?.HandleSaveAndQuit();
|
||||
if (GameSession.Submarine != null && !GameSession.Submarine.Removed)
|
||||
{
|
||||
GameSession.SubmarineInfo = new SubmarineInfo(GameSession.Submarine);
|
||||
}
|
||||
if (GameSession.Campaign is CampaignMode campaign)
|
||||
{
|
||||
if (campaign is SinglePlayerCampaign spCampaign && Level.IsLoadedFriendlyOutpost)
|
||||
{
|
||||
spCampaign.UpdateStoreStock();
|
||||
}
|
||||
GameSession.EventManager?.RegisterEventHistory(registerFinishedOnly: true);
|
||||
campaign.End();
|
||||
}
|
||||
GameSession.Campaign?.End();
|
||||
|
||||
SaveUtil.SaveGame(GameSession.SavePath);
|
||||
}
|
||||
|
||||
|
||||
@@ -678,12 +678,12 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Adds the message to the single player chatbox.
|
||||
/// </summary>
|
||||
public void AddSinglePlayerChatMessage(LocalizedString senderName, LocalizedString text, ChatMessageType messageType, Character sender)
|
||||
public void AddSinglePlayerChatMessage(LocalizedString senderName, LocalizedString text, ChatMessageType messageType, Entity sender)
|
||||
{
|
||||
AddSinglePlayerChatMessage(senderName.Value, text.Value, messageType, sender);
|
||||
}
|
||||
|
||||
public void AddSinglePlayerChatMessage(string senderName, string text, ChatMessageType messageType, Character sender)
|
||||
public void AddSinglePlayerChatMessage(string senderName, string text, ChatMessageType messageType, Entity sender)
|
||||
{
|
||||
if (!IsSinglePlayer)
|
||||
{
|
||||
@@ -692,9 +692,13 @@ namespace Barotrauma
|
||||
}
|
||||
if (string.IsNullOrEmpty(text)) { return; }
|
||||
|
||||
if (sender != null)
|
||||
if (sender is Character character)
|
||||
{
|
||||
GameMain.GameSession.CrewManager.SetCharacterSpeaking(sender);
|
||||
GameMain.GameSession.CrewManager?.SetCharacterSpeaking(character);
|
||||
if (!character.IsBot)
|
||||
{
|
||||
character.TextChatVolume = 1f;
|
||||
}
|
||||
}
|
||||
ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender));
|
||||
}
|
||||
@@ -708,9 +712,9 @@ namespace Barotrauma
|
||||
}
|
||||
if (string.IsNullOrEmpty(message.Text)) { return; }
|
||||
|
||||
if (message.Sender != null)
|
||||
if (message.SenderCharacter != null)
|
||||
{
|
||||
GameMain.GameSession.CrewManager.SetCharacterSpeaking(message.Sender);
|
||||
GameMain.GameSession.CrewManager?.SetCharacterSpeaking(message.SenderCharacter);
|
||||
}
|
||||
ChatBox.AddMessage(message);
|
||||
}
|
||||
@@ -3688,6 +3692,9 @@ namespace Barotrauma
|
||||
crewList.ClearChildren();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current crew. Note that this is client-only code (only used in the single player campaign) - saving in multiplayer is handled in the server-side code of <see cref="MultiPlayerCampaign"/>.
|
||||
/// </summary>
|
||||
public XElement Save(XElement parentElement)
|
||||
{
|
||||
var element = new XElement("crew");
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace Barotrauma
|
||||
buttonText = TextManager.Get("map");
|
||||
}
|
||||
else if (prevCampaignUIAutoOpenType != availableTransition &&
|
||||
(availableTransition == TransitionType.ProgressToNextEmptyLocation || availableTransition == TransitionType.ReturnToPreviousEmptyLocation))
|
||||
availableTransition == TransitionType.ProgressToNextEmptyLocation)
|
||||
{
|
||||
HintManager.OnAvailableTransition(availableTransition);
|
||||
//opening the campaign map pauses the game and prevents HintManager from running -> update it manually to get the hint to show up immediately
|
||||
|
||||
+28
-15
@@ -162,7 +162,7 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
private void InitCampaignUI()
|
||||
public void InitCampaignUI()
|
||||
{
|
||||
campaignUIContainer = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
|
||||
CampaignUI = new CampaignUI(this, campaignUIContainer)
|
||||
@@ -720,7 +720,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, force: true);
|
||||
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, isNetworkMessage: true);
|
||||
}
|
||||
}
|
||||
foreach (Item item in Item.ItemList.ToList())
|
||||
@@ -906,6 +906,8 @@ namespace Barotrauma
|
||||
|
||||
public void ClientReadCrew(IReadMessage msg)
|
||||
{
|
||||
bool createNotification = msg.ReadBoolean();
|
||||
|
||||
ushort availableHireLength = msg.ReadUInt16();
|
||||
List<CharacterInfo> availableHires = new List<CharacterInfo>();
|
||||
for (int i = 0; i < availableHireLength; i++)
|
||||
@@ -916,10 +918,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
ushort pendingHireLength = msg.ReadUInt16();
|
||||
List<int> pendingHires = new List<int>();
|
||||
List<UInt16> pendingHires = new List<UInt16>();
|
||||
for (int i = 0; i < pendingHireLength; i++)
|
||||
{
|
||||
pendingHires.Add(msg.ReadInt32());
|
||||
pendingHires.Add(msg.ReadUInt16());
|
||||
}
|
||||
|
||||
ushort hiredLength = msg.ReadUInt16();
|
||||
@@ -934,30 +936,40 @@ namespace Barotrauma
|
||||
bool renameCrewMember = msg.ReadBoolean();
|
||||
if (renameCrewMember)
|
||||
{
|
||||
int renamedIdentifier = msg.ReadInt32();
|
||||
UInt16 renamedIdentifier = msg.ReadUInt16();
|
||||
string newName = msg.ReadString();
|
||||
CharacterInfo renamedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
|
||||
CharacterInfo renamedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.ID == renamedIdentifier);
|
||||
if (renamedCharacter != null) { CrewManager.RenameCharacter(renamedCharacter, newName); }
|
||||
}
|
||||
|
||||
bool fireCharacter = msg.ReadBoolean();
|
||||
if (fireCharacter)
|
||||
{
|
||||
int firedIdentifier = msg.ReadInt32();
|
||||
CharacterInfo firedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
|
||||
UInt16 firedIdentifier = msg.ReadUInt16();
|
||||
CharacterInfo firedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.ID == firedIdentifier);
|
||||
// this one might and is allowed to be null since the character is already fired on the original sender's game
|
||||
if (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); }
|
||||
}
|
||||
|
||||
if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null &&
|
||||
/*can't apply until we have the latest save file*/
|
||||
!NetIdUtils.IdMoreRecent(pendingSaveID, LastSaveID))
|
||||
if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null)
|
||||
{
|
||||
CampaignUI.CrewManagement.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters, takeMoney: false); }
|
||||
CampaignUI.CrewManagement.SetPendingHires(pendingHires, map.CurrentLocation);
|
||||
if (renameCrewMember || fireCharacter) { CampaignUI.CrewManagement.UpdateCrew(); }
|
||||
//can't apply until we have the latest save file
|
||||
if (!NetIdUtils.IdMoreRecent(pendingSaveID, LastSaveID))
|
||||
{
|
||||
CampaignUI.CrewManagement.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters, takeMoney: false, createNotification: createNotification); }
|
||||
CampaignUI.CrewManagement.SetPendingHires(pendingHires, map.CurrentLocation);
|
||||
if (renameCrewMember || fireCharacter) { CampaignUI.CrewManagement.UpdateCrew(); }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//This is pretty nasty: setting hireables is handled through CrewManagement,
|
||||
//which is part of the Campaign UI that might not exist when the client is still initializing the round.
|
||||
//If that's the case, let's force the available hires here so they're available when the UI is created
|
||||
CurrentLocation?.ForceHireableCharacters(availableHires);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void ClientReadMoney(IReadMessage inc)
|
||||
@@ -979,6 +991,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
Bank.Balance = info.Balance;
|
||||
Bank.RewardDistribution = info.RewardDistribution;
|
||||
TryInvokeEvent(Bank, transaction.ChangedData, info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using Barotrauma.Abilities;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -43,13 +45,19 @@ namespace Barotrauma
|
||||
private GUIButton crewListButton, commandButton, tabMenuButton;
|
||||
private GUIImage talentPointNotification;
|
||||
|
||||
private GUIComponent respawnInfoFrame, respawnButtonContainer;
|
||||
private GUIComponent deathChoiceInfoFrame, deathChoiceButtonContainer;
|
||||
private GUITextBlock respawnInfoText;
|
||||
private GUITickBox respawnTickBox;
|
||||
private GUITickBox deathChoiceTickBox;
|
||||
private GUIButton takeOverBotButton;
|
||||
private GUIButton hrManagerButton;
|
||||
public DeathPrompt DeathPrompt;
|
||||
|
||||
private GUIImage eventLogNotification;
|
||||
|
||||
private Point prevTopLeftButtonsResolution;
|
||||
|
||||
public bool AllowHrManagerBotTakeover => GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath, IronmanMode: false }
|
||||
&& Level.IsLoadedFriendlyOutpost;
|
||||
|
||||
private void CreateTopLeftButtons()
|
||||
{
|
||||
@@ -96,30 +104,63 @@ namespace Barotrauma
|
||||
|
||||
talentPointNotification = CreateNotificationIcon(tabMenuButton);
|
||||
eventLogNotification = CreateNotificationIcon(tabMenuButton);
|
||||
|
||||
respawnInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), parent: topLeftButtonGroup.RectTransform)
|
||||
{ MaxSize = new Point(HUDLayoutSettings.ButtonAreaTop.Width / 3, int.MaxValue) }, style: null)
|
||||
|
||||
// The visibility of the following contents of deathChoiceInfoFrame is controlled by SetRespawnInfo()
|
||||
|
||||
deathChoiceInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), parent: topLeftButtonGroup.RectTransform)
|
||||
{ MaxSize = new Point(HUDLayoutSettings.ButtonAreaTop.Width / 3, int.MaxValue) }, style: null)
|
||||
{
|
||||
Visible = false
|
||||
};
|
||||
respawnInfoText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), respawnInfoFrame.RectTransform), "", wrap: true);
|
||||
respawnButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), respawnInfoFrame.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
respawnInfoText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform), "", wrap: true);
|
||||
deathChoiceButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = HUDLayoutSettings.Padding,
|
||||
Stretch = true,
|
||||
Visible = false
|
||||
};
|
||||
respawnTickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, respawnButtonContainer.RectTransform, Anchor.Center), TextManager.Get("respawnquestionpromptrespawn"))
|
||||
|
||||
takeOverBotButton = new GUIButton(new RectTransform(Vector2.One * 0.9f, deathChoiceButtonContainer.RectTransform, Anchor.Center),
|
||||
TextManager.Get("takeoverbotquestionprompttakeoverbot"), style: "GUIButtonSmall")
|
||||
{
|
||||
ToolTip = TextManager.GetWithVariable(
|
||||
"respawnquestionprompt", "[percentage]",
|
||||
(Math.Round(Networking.RespawnManager.SkillLossPercentageOnImmediateRespawn).ToString())),
|
||||
OnSelected = (tickbox) =>
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: !tickbox.Selected);
|
||||
DeathPrompt.CreateTakeOverBotPanel();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
takeOverBotButton.TextBlock.AutoScaleHorizontal = true;
|
||||
|
||||
hrManagerButton = new GUIButton(new RectTransform(Vector2.One * 0.9f, deathChoiceButtonContainer.RectTransform, Anchor.Center),
|
||||
TextManager.Get("npctitle.hrmanager"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign is { } campaign)
|
||||
{
|
||||
campaign.ShowCampaignUI = true;
|
||||
campaign.CampaignUI?.SelectTab(CampaignMode.InteractionType.Crew);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
hrManagerButton.TextBlock.AutoScaleHorizontal = true;
|
||||
|
||||
var questionText =
|
||||
TextManager.GetWithVariable(
|
||||
"respawnquestionprompt", "[percentage]",
|
||||
((int)Math.Round(RespawnManager.SkillLossPercentageOnImmediateRespawn)).ToString());
|
||||
deathChoiceTickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, deathChoiceButtonContainer.RectTransform, Anchor.Center),
|
||||
TextManager.Get("respawnquestionpromptrespawn"))
|
||||
{
|
||||
ToolTip = questionText,
|
||||
OnSelected = (tickbox) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: !tickbox.Selected);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
prevTopLeftButtonsResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
}
|
||||
|
||||
@@ -150,6 +191,8 @@ namespace Barotrauma
|
||||
GameMain.NetLobbyScreen.CharacterAppearanceCustomizationMenu?.AddToGUIUpdateList();
|
||||
GameMain.NetLobbyScreen?.JobSelectionFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
DeathPrompt?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public static GUIImage CreateNotificationIcon(GUIComponent parent, bool offset = true)
|
||||
@@ -230,16 +273,67 @@ namespace Barotrauma
|
||||
HintManager.Update();
|
||||
ObjectiveManager.VideoPlayer.Update();
|
||||
}
|
||||
|
||||
public void SetRespawnInfo(bool visible, string text, Color textColor, bool buttonsVisible, bool waitForNextRoundRespawn)
|
||||
|
||||
/// <summary>
|
||||
/// This method controls the content and visibility logic of the respawn-related GUI elements at the top left of the game screen.
|
||||
/// </summary>
|
||||
/// <param name="waitForNextRoundRespawn">Has the player chosen to wait until next round</param>
|
||||
/// <param name="hideButtons">Hide the respawn buttons even if they would otherwise be visible</param>
|
||||
public void SetRespawnInfo(string text, Color textColor, bool waitForNextRoundRespawn, bool hideButtons = false)
|
||||
{
|
||||
if (topLeftButtonGroup == null) { return; }
|
||||
respawnInfoFrame.Visible = visible;
|
||||
if (!visible) { return; }
|
||||
|
||||
bool permadeathMode = GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath };
|
||||
bool ironmanMode = GameMain.NetworkMember is { ServerSettings: { RespawnMode: RespawnMode.Permadeath, IronmanMode: true } };
|
||||
|
||||
bool hasRespawnOptions;
|
||||
if (permadeathMode)
|
||||
{
|
||||
// In permadeath mode you can (in ironman, must) always at least wait, and possibly buy a new character from HR or take control of a bot
|
||||
hasRespawnOptions = !ironmanMode &&
|
||||
GameMain.Client is GameClient client && (client.CharacterInfo == null || client.CharacterInfo.PermanentlyDead);
|
||||
}
|
||||
else // "classic" respawn modes
|
||||
{
|
||||
//can choose between midround respawning with a penalty or waiting
|
||||
//if we're in a non-outpost level, and either don't have an existing character or have already spawned during the round
|
||||
//(otherwise, e.g. when joining a campaign in which we have an existing character, we can respawn mid-round "for free" and there's no reason to make a choice)
|
||||
hasRespawnOptions = Level.Loaded?.Type != LevelData.LevelType.Outpost &&
|
||||
(GameMain.Client is GameClient client && (client.CharacterInfo == null || client.HasSpawned));
|
||||
}
|
||||
|
||||
// Are the death choice elements shown at all, at least with the text?
|
||||
deathChoiceInfoFrame.Visible = !text.IsNullOrEmpty() || hasRespawnOptions;
|
||||
if (!deathChoiceInfoFrame.Visible) { return; }
|
||||
respawnInfoText.Text = text;
|
||||
respawnInfoText.TextColor = textColor;
|
||||
respawnButtonContainer.Visible = buttonsVisible;
|
||||
respawnTickBox.Selected = !waitForNextRoundRespawn;
|
||||
|
||||
// Determine if we even bother considering showing the buttons
|
||||
if (GameMain.GameSession.GameMode is not CampaignMode || Character.Controlled != null)
|
||||
{
|
||||
// Disable the button container in case it was left visible earlier
|
||||
deathChoiceButtonContainer.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
deathChoiceButtonContainer.Visible = hasRespawnOptions && !hideButtons;
|
||||
if (deathChoiceButtonContainer.Visible)
|
||||
{
|
||||
hrManagerButton.Visible = AllowHrManagerBotTakeover;
|
||||
|
||||
if (permadeathMode && ironmanMode)
|
||||
{
|
||||
takeOverBotButton.Visible = false;
|
||||
deathChoiceTickBox.Visible = false;
|
||||
deathChoiceTickBox.Selected = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
takeOverBotButton.Visible = permadeathMode && GameMain.NetworkMember?.ServerSettings is { AllowBotTakeoverOnPermadeath: true };
|
||||
deathChoiceTickBox.Visible = !permadeathMode;
|
||||
deathChoiceTickBox.Selected = !waitForNextRoundRespawn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -81,6 +81,22 @@ namespace Barotrauma
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
processAfflictionChangesTimer -= deltaTime;
|
||||
if (processAfflictionChangesTimer <= 0.0f)
|
||||
{
|
||||
foreach (var character in charactersWithAfflictionChanges)
|
||||
{
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
ImmutableArray<NetAffliction> afflictions = GetAllAfflictions(character.CharacterHealth);
|
||||
ui?.UpdateAfflictions(new NetCrewMember(character.Info, afflictions));
|
||||
}
|
||||
ui?.UpdateCrewPanel();
|
||||
}
|
||||
charactersWithAfflictionChanges.Clear();
|
||||
processAfflictionChangesTimer = ProcessAfflictionChangesInterval;
|
||||
}
|
||||
|
||||
DateTimeOffset now = DateTimeOffset.Now;
|
||||
UpdateQueue(afflictionRequests, now, onTimeout: static callback => { callback(new AfflictionRequest(RequestResult.Timeout, ImmutableArray<NetAffliction>.Empty)); });
|
||||
UpdateQueue(pendingHealRequests, now, onTimeout: static callback => { callback(new PendingRequest(RequestResult.Timeout, NetCollection<NetCrewMember>.Empty)); });
|
||||
|
||||
@@ -785,7 +785,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (item.Container == null || character.Inventory.FindIndex(item.Container) == -1) // Not a subinventory in the character's inventory
|
||||
{
|
||||
if (character.HeldItems.Any(i => i.OwnInventory != null && i.OwnInventory.CanBePut(item)))
|
||||
if (character.HeldItems.Any(i => i.OwnInventory != null && i.OwnInventory.CanBePut(item) && character.CanAccessInventory(i.OwnInventory)))
|
||||
{
|
||||
return QuickUseAction.PutToEquippedItem;
|
||||
}
|
||||
@@ -843,13 +843,14 @@ namespace Barotrauma
|
||||
else if (character.HeldItems.FirstOrDefault(i =>
|
||||
i.OwnInventory != null &&
|
||||
i.OwnInventory.Container.DrawInventory &&
|
||||
character.CanAccessInventory(i.OwnInventory) &&
|
||||
(i.OwnInventory.CanBePut(item) || ((i.OwnInventory.Capacity == 1 || i.OwnInventory.Container.HasSubContainers) && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))) is { } equippedContainer)
|
||||
{
|
||||
if (allowEquip)
|
||||
{
|
||||
if (!character.HasEquippedItem(item))
|
||||
{
|
||||
if (equippedContainer.GetComponent<ItemContainer>() is { QuickUseMovesItemsInside: false})
|
||||
if (equippedContainer.GetComponent<ItemContainer>() is { QuickUseMovesItemsInside: false })
|
||||
{
|
||||
//put the item in a hand slot if that hand is free
|
||||
if ((item.AllowedSlots.Contains(InvSlotType.RightHand) && character.Inventory.GetItemInLimbSlot(InvSlotType.RightHand) == null) ||
|
||||
|
||||
@@ -79,7 +79,8 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If true, the contained state indicator calculates how full the item is based on the total amount of items that can be stacked inside it, as opposed to how many of the inventory slots are occupied.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If true, the contained state indicator calculates how full the item is based on the total amount of items that can be stacked inside it, as opposed to how many of the inventory slots are occupied." +
|
||||
" Note that only items in the main container or in the subcontainer are counted, depending on which container the first containable item match is found in. The item determining this can be defined with ContainedStateIndicatorSlot")]
|
||||
public bool ShowTotalStackCapacityInContainedStateIndicator { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the inventory of this item be kept open when the item is equipped by a character.")]
|
||||
@@ -274,8 +275,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
itemsPerSlot.Sort((i1, i2) => i1.First().Name.CompareTo(i2.First().Name));
|
||||
foreach (var items in itemsPerSlot)
|
||||
var sortedItems = itemsPerSlot
|
||||
.OrderBy(i => i.First().Name)
|
||||
//if there's multiple items with the same name, sort largest stacks first
|
||||
.ThenByDescending(i => i.Count)
|
||||
//same name and stack size, sort items with most items inside first
|
||||
.ThenByDescending(i => i.First().ContainedItems.Count());
|
||||
|
||||
foreach (var items in sortedItems)
|
||||
{
|
||||
int firstFreeSlot = -1;
|
||||
for (int i = 0; i < Inventory.Capacity; i++)
|
||||
@@ -591,7 +598,8 @@ namespace Barotrauma.Items.Components
|
||||
contained.Item.Scale,
|
||||
spriteEffects,
|
||||
depth: containedSpriteDepth);
|
||||
contained.Item.DrawDecorativeSprites(spriteBatch, itemPos, flipX,flipY, (contained.Item.body == null ? 0.0f : contained.Item.body.DrawRotation), containedSpriteDepth);
|
||||
contained.Item.DrawDecorativeSprites(spriteBatch, itemPos, flipX,flipY, (contained.Item.body == null ? 0.0f : contained.Item.body.DrawRotation),
|
||||
containedSpriteDepth, overrideColor);
|
||||
|
||||
foreach (ItemContainer ic in contained.Item.GetComponents<ItemContainer>())
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
partial void SetLightSourceState(bool enabled, float brightness)
|
||||
{
|
||||
if (Light == null) { return; }
|
||||
if (item.HiddenInGame) { enabled = false; }
|
||||
if (item.IsHidden) { enabled = false; }
|
||||
Light.Enabled = enabled;
|
||||
lightColorMultiplier = brightness;
|
||||
if (enabled)
|
||||
|
||||
@@ -429,7 +429,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (it?.Submarine == null) { return false; }
|
||||
if (item.Submarine == null || !item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true)) { return false; }
|
||||
if (it.NonInteractable || it.HiddenInGame) { return false; }
|
||||
if (it.NonInteractable || it.IsHidden) { return false; }
|
||||
if (it.GetComponent<Pickable>() == null) { return false; }
|
||||
|
||||
var holdable = it.GetComponent<Holdable>();
|
||||
@@ -470,10 +470,10 @@ namespace Barotrauma.Items.Components
|
||||
scissorComponent = new GUIScissorComponent(new RectTransform(Vector2.One, submarineContainer.RectTransform, Anchor.Center));
|
||||
miniMapContainer = new GUIFrame(new RectTransform(Vector2.One, scissorComponent.Content.RectTransform, Anchor.Center), style: null) { CanBeFocused = false };
|
||||
|
||||
ImmutableHashSet<Item> hullPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && (it.GetComponent<Door>() != null || it.GetComponent<Turret>() != null)).ToImmutableHashSet();
|
||||
ImmutableHashSet<Item> hullPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.IsHidden && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && (it.GetComponent<Door>() != null || it.GetComponent<Turret>() != null)).ToImmutableHashSet();
|
||||
miniMapFrame = CreateMiniMap(item.Submarine, submarineContainer, MiniMapSettings.Default, hullPointsOfInterest, out hullStatusComponents);
|
||||
|
||||
IEnumerable<Item> electricalPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.GetComponent<Repairable>() != null);
|
||||
IEnumerable<Item> electricalPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.IsHidden && !it.NonInteractable && it.GetComponent<Repairable>() != null);
|
||||
electricalFrame = CreateMiniMap(item.Submarine, miniMapContainer, new MiniMapSettings(createHullElements: false), electricalPointsOfInterest, out electricalMapComponents);
|
||||
|
||||
Dictionary<MiniMapGUIComponent, GUIComponent> electricChildren = new Dictionary<MiniMapGUIComponent, GUIComponent>();
|
||||
@@ -566,7 +566,7 @@ namespace Barotrauma.Items.Components
|
||||
displayedSubs.Add(item.Submarine);
|
||||
displayedSubs.AddRange(item.Submarine.DockedTo.Where(s => s.TeamID == item.Submarine.TeamID));
|
||||
|
||||
subEntities = MapEntity.MapEntityList.Where(me => (item.Submarine is { } sub && sub.IsEntityFoundOnThisSub(me, includingConnectedSubs: true, allowDifferentType: false)) && !me.HiddenInGame).OrderByDescending(w => w.SpriteDepth).ToList();
|
||||
subEntities = MapEntity.MapEntityList.Where(me => (item.Submarine is { } sub && sub.IsEntityFoundOnThisSub(me, includingConnectedSubs: true, allowDifferentType: false)) && !me.IsHidden).OrderByDescending(w => w.SpriteDepth).ToList();
|
||||
|
||||
BakeSubmarine(item.Submarine, parentRect);
|
||||
elementSize = GuiFrame.Rect.Size;
|
||||
@@ -763,7 +763,7 @@ namespace Barotrauma.Items.Components
|
||||
worldBorders.Location += item.Submarine.WorldPosition.ToPoint();
|
||||
foreach (Gap gap in Gap.GapList)
|
||||
{
|
||||
if (gap.IsRoomToRoom || gap.linkedTo.Count == 0 || gap.Submarine != item.Submarine || gap.ConnectedDoor != null || gap.HiddenInGame) { continue; }
|
||||
if (gap.IsRoomToRoom || gap.linkedTo.Count == 0 || gap.Submarine != item.Submarine || gap.ConnectedDoor != null || gap.IsHidden) { continue; }
|
||||
RectangleF entityRect = ScaleRectToUI(gap, miniMapFrame.Rect, worldBorders);
|
||||
|
||||
Vector2 scale = new Vector2(entityRect.Size.X / spriteSize.X, entityRect.Size.Y / spriteSize.Y) * 2.0f;
|
||||
@@ -930,7 +930,7 @@ namespace Barotrauma.Items.Components
|
||||
if (DisplayAsSameItem(it.Prefab, searchedPrefab))
|
||||
{
|
||||
// ignore items on players and hidden inventories
|
||||
if (it.FindParentInventory(inv => inv is CharacterInventory || inv is ItemInventory { Owner: Item { HiddenInGame: true }}) is { }) { continue; }
|
||||
if (it.FindParentInventory(inv => inv is CharacterInventory || inv is ItemInventory { Owner: Item { IsHidden: true }}) is { }) { continue; }
|
||||
|
||||
if (it.FindParentInventory(inventory => inventory is ItemInventory { Owner: Item { ParentInventory: null } }) is ItemInventory parent)
|
||||
{
|
||||
@@ -1112,7 +1112,7 @@ namespace Barotrauma.Items.Components
|
||||
if (ShowHullIntegrity)
|
||||
{
|
||||
float amount = 1f + hullData.LinkedHulls.Count;
|
||||
gapOpenSum = hull.ConnectedGaps.Concat(hullData.LinkedHulls.SelectMany(h => h.ConnectedGaps)).Where(g => g.linkedTo.Count == 1 && !g.HiddenInGame).Sum(g => g.Open) / amount;
|
||||
gapOpenSum = hull.ConnectedGaps.Concat(hullData.LinkedHulls.SelectMany(h => h.ConnectedGaps)).Where(g => g.linkedTo.Count == 1 && !g.IsHidden).Sum(g => g.Open) / amount;
|
||||
borderColor = Color.Lerp(neutralColor, GUIStyle.Red, Math.Min(gapOpenSum, 1.0f));
|
||||
}
|
||||
|
||||
@@ -1557,7 +1557,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (linkedEntity is Hull linkedHull)
|
||||
{
|
||||
if (linkedHulls.Contains(linkedHull) || linkedHull.HiddenInGame) { continue; }
|
||||
if (linkedHulls.Contains(linkedHull) || linkedHull.IsHidden) { continue; }
|
||||
linkedHulls.Add(linkedHull);
|
||||
GetLinkedHulls(linkedHull, linkedHulls);
|
||||
}
|
||||
@@ -1737,7 +1737,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
bool IsPartofSub(MapEntity entity)
|
||||
{
|
||||
if (entity.Submarine != sub && !connectedSubs.Contains(entity.Submarine) || entity.HiddenInGame) { return false; }
|
||||
if (entity.Submarine != sub && !connectedSubs.Contains(entity.Submarine) || entity.IsHidden) { return false; }
|
||||
return sub.IsEntityFoundOnThisSub(entity, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1186,7 +1186,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (DockingPort dockingPort in DockingPort.List)
|
||||
{
|
||||
if (Level.Loaded != null && dockingPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
if (dockingPort.Item.HiddenInGame) { continue; }
|
||||
if (dockingPort.Item.IsHidden) { continue; }
|
||||
if (dockingPort.Item.Submarine == null) { continue; }
|
||||
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
|
||||
// docking ports should be shown even if defined as not, if the submarine is the same as the sonar's
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool ShouldDrawHUD(Character character)
|
||||
{
|
||||
if (item.HiddenInGame) { return false; }
|
||||
if (item.IsHidden) { return false; }
|
||||
if (!HasRequiredItems(character, false) || character.SelectedItem != item) { return false; }
|
||||
if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; }
|
||||
if (item.ConditionPercentageRelativeToDefaultMaxCondition < RepairThreshold) { return true; }
|
||||
@@ -224,7 +224,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
if (item.HiddenInGame) { return; }
|
||||
if (item.IsHidden) { return; }
|
||||
if (FakeBrokenTimer > 0.0f)
|
||||
{
|
||||
item.FakeBroken = true;
|
||||
@@ -397,6 +397,12 @@ namespace Barotrauma.Items.Components
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y + 20), "Condition: " + (int)item.Condition + "/" + (int)item.MaxCondition,
|
||||
GUIStyle.Orange);
|
||||
if (MaxStressDeteriorationMultiplier > 1.0f)
|
||||
{
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y + 40), "Stress multiplier: " + StressDeteriorationMultiplier.ToString("0.00"),
|
||||
GUIStyle.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -303,6 +303,17 @@ namespace Barotrauma.Items.Components
|
||||
CreateClientEvent(new CircuitBoxRenameLabelEvent(label.ID, color, header, body));
|
||||
}
|
||||
|
||||
public void SetConnectionLabelOverrides(CircuitBoxInputOutputNode node, Dictionary<string, string> newOverrides)
|
||||
{
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
node.ReplaceAllConnectionLabelOverrides(newOverrides);
|
||||
return;
|
||||
}
|
||||
|
||||
CreateClientEvent(new CircuitBoxRenameConnectionLabelsEvent(node.NodeType, newOverrides.ToNetDictionary()));
|
||||
}
|
||||
|
||||
public void ResizeNode(CircuitBoxNode node, CircuitBoxResizeDirection dir, Vector2 amount)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
@@ -528,6 +539,12 @@ namespace Barotrauma.Items.Components
|
||||
_ => node.Position
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var labelOverride in data.LabelOverrides)
|
||||
{
|
||||
RenameConnectionLabelsInternal(labelOverride.Type, labelOverride.Override.ToDictionary());
|
||||
}
|
||||
|
||||
wasInitializedByServer = true;
|
||||
break;
|
||||
}
|
||||
@@ -556,6 +573,12 @@ namespace Barotrauma.Items.Components
|
||||
ResizeLabelInternal(data.ID, data.Position, data.Size);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.RenameConnections:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxRenameConnectionLabelsEvent>(msg);
|
||||
RenameConnectionLabelsInternal(data.Type, data.Override.ToDictionary());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(header), header, "This opcode cannot be handled using entity events");
|
||||
}
|
||||
|
||||
@@ -292,8 +292,17 @@ namespace Barotrauma.Items.Components
|
||||
if (wire.HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
|
||||
|
||||
Connection recipient = wire.OtherConnection(this);
|
||||
LocalizedString label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
|
||||
if (wire.Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
|
||||
LocalizedString label;
|
||||
if (wire.Item.IsLayerHidden)
|
||||
{
|
||||
label = TextManager.Get("ConnectionLocked");
|
||||
}
|
||||
else
|
||||
{
|
||||
label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
|
||||
if (wire.Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
|
||||
}
|
||||
|
||||
DrawWire(spriteBatch, wire, position, wirePosition, equippedWire, panel, label);
|
||||
|
||||
wirePosition.Y += wireInterval;
|
||||
@@ -494,7 +503,7 @@ namespace Barotrauma.Items.Components
|
||||
ConnectionPanel.HighlightedWire = wire;
|
||||
|
||||
bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring || panel.AlwaysAllowRewiring;
|
||||
if (allowRewiring && (!wire.Locked && !panel.Locked && !panel.TemporarilyLocked || Screen.Selected == GameMain.SubEditorScreen))
|
||||
if (allowRewiring && (!wire.Locked && !wire.Item.IsLayerHidden && !panel.Locked && !panel.TemporarilyLocked || Screen.Selected == GameMain.SubEditorScreen))
|
||||
{
|
||||
//start dragging the wire
|
||||
if (PlayerInput.PrimaryMouseButtonHeld()) { DraggingConnected = wire; }
|
||||
|
||||
@@ -283,12 +283,12 @@ namespace Barotrauma.Items.Components
|
||||
texts.Add(CharacterHUD.GetCachedHudText("PlayHint", InputType.Use));
|
||||
textColors.Add(GUIStyle.Green);
|
||||
}
|
||||
if (target.CharacterHealth.UseHealthWindow && !target.DisableHealthWindow && equipper?.FocusedCharacter == target && equipper.CanInteractWith(target, 160f, false))
|
||||
if (equipper?.FocusedCharacter == target && target.CanBeHealedBy(equipper, checkFriendlyTeam: false))
|
||||
{
|
||||
texts.Add(CharacterHUD.GetCachedHudText("HealHint", InputType.Health));
|
||||
textColors.Add(GUIStyle.Green);
|
||||
}
|
||||
if (target.CanBeDragged)
|
||||
if (target.CanBeDraggedBy(Character.Controlled))
|
||||
{
|
||||
texts.Add(CharacterHUD.GetCachedHudText("GrabHint", InputType.Grab));
|
||||
textColors.Add(GUIStyle.Green);
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 particlePos = GetRelativeFiringPosition();
|
||||
foreach (ParticleEmitter emitter in particleEmitters)
|
||||
{
|
||||
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: -rotation, particleRotation: rotation);
|
||||
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: -Rotation, particleRotation: Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace Barotrauma.Items.Components
|
||||
if (crosshairSprite != null)
|
||||
{
|
||||
Vector2 itemPos = cam.WorldToScreen(new Vector2(item.WorldRect.X + transformedBarrelPos.X, item.WorldRect.Y - transformedBarrelPos.Y));
|
||||
Vector2 turretDir = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
|
||||
Vector2 turretDir = new Vector2((float)Math.Cos(Rotation), (float)Math.Sin(Rotation));
|
||||
|
||||
Vector2 mouseDiff = itemPos - PlayerInput.MousePosition;
|
||||
crosshairPos = new Vector2(
|
||||
@@ -268,7 +268,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (ParticleEmitter emitter in particleEmitterCharges)
|
||||
{
|
||||
// color is currently not connected to ammo type, should be updated when ammo is changed
|
||||
emitter.Emit(deltaTime, particlePos, hullGuess: null, angle: -rotation, particleRotation: rotation, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier);
|
||||
emitter.Emit(deltaTime, particlePos, hullGuess: null, angle: -Rotation, particleRotation: Rotation, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier);
|
||||
}
|
||||
|
||||
if (chargeSoundChannel == null || !chargeSoundChannel.IsPlaying)
|
||||
@@ -339,7 +339,7 @@ namespace Barotrauma.Items.Components
|
||||
if (crosshairSprite != null)
|
||||
{
|
||||
Vector2 itemPos = cam.WorldToScreen(item.WorldPosition);
|
||||
Vector2 turretDir = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
|
||||
Vector2 turretDir = new Vector2((float)Math.Cos(Rotation), (float)Math.Sin(Rotation));
|
||||
|
||||
Vector2 mouseDiff = itemPos - PlayerInput.MousePosition;
|
||||
crosshairPos = new Vector2(
|
||||
@@ -372,7 +372,7 @@ namespace Barotrauma.Items.Components
|
||||
recoilOffset = RecoilDistance;
|
||||
}
|
||||
}
|
||||
return new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset;
|
||||
return new Vector2((float)Math.Cos(Rotation), (float)Math.Sin(Rotation)) * recoilOffset;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1, Color? overrideColor = null)
|
||||
@@ -388,13 +388,13 @@ namespace Barotrauma.Items.Components
|
||||
railSprite?.Draw(spriteBatch,
|
||||
drawPos,
|
||||
overrideColor ?? item.SpriteColor,
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
Rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
barrelSprite?.Draw(spriteBatch,
|
||||
drawPos - GetRecoilOffset() * item.Scale,
|
||||
overrideColor ?? item.SpriteColor,
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
Rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
float chargeRatio = currentChargeTime / MaxChargeTime;
|
||||
@@ -402,9 +402,9 @@ namespace Barotrauma.Items.Components
|
||||
foreach ((Sprite chargeSprite, Vector2 position) in chargeSprites)
|
||||
{
|
||||
chargeSprite?.Draw(spriteBatch,
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(position.X * chargeRatio, position.Y * chargeRatio) * item.Scale, rotation + MathHelper.PiOver2),
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(position.X * chargeRatio, position.Y * chargeRatio) * item.Scale, Rotation + MathHelper.PiOver2),
|
||||
item.SpriteColor,
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
Rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (chargeSprite.Depth - item.Sprite.Depth));
|
||||
}
|
||||
|
||||
@@ -427,9 +427,9 @@ namespace Barotrauma.Items.Components
|
||||
float newPositionOffset = barrelPositionModifier * SpinningBarrelDistance;
|
||||
|
||||
spinningBarrel.Draw(spriteBatch,
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(newPositionOffset, 0f) * item.Scale, rotation + MathHelper.PiOver2),
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(newPositionOffset, 0f) * item.Scale, Rotation + MathHelper.PiOver2),
|
||||
Color.Lerp(overrideColor ?? item.SpriteColor, newColorModifier, 0.8f),
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
Rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, newDepth);
|
||||
}
|
||||
}
|
||||
@@ -475,9 +475,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
spriteBatch.DrawLine(drawPos, drawPos + center * circleRadius, GUIStyle.Green, thickness: lineThickness);
|
||||
}
|
||||
else if (radians > Math.PI * 2)
|
||||
else if (radians >= MathHelper.TwoPi)
|
||||
{
|
||||
spriteBatch.DrawCircle(drawPos, circleRadius, 180, GUIStyle.Red, thickness: lineThickness);
|
||||
spriteBatch.DrawCircle(drawPos, circleRadius, 180, GUIStyle.Green, thickness: lineThickness);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -510,7 +510,12 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
widget.MouseHeld += (deltaTime) =>
|
||||
{
|
||||
minRotation = GetRotationAngle(GetDrawPos());
|
||||
float newMinRotation = GetRotationAngle(GetDrawPos());
|
||||
AngleWrapAdjustment(minRotation, newMinRotation, ref maxRotation);
|
||||
|
||||
// clamp value here to keep widget movement within max range
|
||||
minRotation = MathHelper.Clamp(newMinRotation, maxRotation - MathHelper.TwoPi, maxRotation);
|
||||
|
||||
UpdateBarrel();
|
||||
MapEntity.DisableSelect = true;
|
||||
};
|
||||
@@ -554,7 +559,12 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
widget.MouseHeld += (deltaTime) =>
|
||||
{
|
||||
maxRotation = GetRotationAngle(GetDrawPos());
|
||||
float newMaxRotation = GetRotationAngle(GetDrawPos());
|
||||
AngleWrapAdjustment(maxRotation, newMaxRotation, ref minRotation);
|
||||
|
||||
// clamp value here to keep widget movement within max range
|
||||
maxRotation = MathHelper.Clamp(newMaxRotation, minRotation, minRotation + MathHelper.TwoPi);
|
||||
|
||||
UpdateBarrel();
|
||||
MapEntity.DisableSelect = true;
|
||||
};
|
||||
@@ -580,10 +590,44 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
void UpdateBarrel()
|
||||
{
|
||||
rotation = (minRotation + maxRotation) / 2;
|
||||
Rotation = (minRotation + maxRotation) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void AngleWrapAdjustment(float currentRotation, float newRotation, ref float rangeLockedRotation)
|
||||
{
|
||||
if (DetectAngleWrapAround(currentRotation, newRotation))
|
||||
{
|
||||
// if there's a wrap-around, also wrap the other rotation limit to keep range
|
||||
if (newRotation < currentRotation)
|
||||
{
|
||||
rangeLockedRotation -= MathHelper.TwoPi;
|
||||
}
|
||||
else
|
||||
{
|
||||
rangeLockedRotation += MathHelper.TwoPi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool DetectAngleWrapAround(float rotation, float newRotation)
|
||||
{
|
||||
float deltaRotation = MathF.Abs(rotation - newRotation);
|
||||
|
||||
// turret angle wraps around to 0 from -2Pi and 2Pi.
|
||||
// Detect wrap-around when dragging the widgets, where usual rotation delta is small,
|
||||
// so a large jump in rotation (here, an arbitrary big value in the range of 0 to 2Pi)
|
||||
// is considered a wrap-around for this purpose.
|
||||
// NOTE: this is not a reliable way to detect angle wrap-around in general, and is only intended for
|
||||
// the angle widgets!
|
||||
if (deltaRotation > MathHelper.TwoPi * 0.8f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Vector2 GetDrawPos()
|
||||
{
|
||||
Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y);
|
||||
@@ -764,7 +808,7 @@ namespace Barotrauma.Items.Components
|
||||
if (projectileID == 0) { return; }
|
||||
|
||||
//ID ushort.MaxValue = launched without a projectile
|
||||
if (projectileID == ushort.MaxValue)
|
||||
if (projectileID == LaunchWithoutProjectileId)
|
||||
{
|
||||
Launch(null, user);
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
string colorStr = (item.SpawnedInCurrentOutpost && !item.AllowStealing ? GUIStyle.Red : Color.White).ToStringHex();
|
||||
string colorStr = (item.Illegitimate ? GUIStyle.Red : Color.White).ToStringHex();
|
||||
|
||||
toolTip = $"‖color:{colorStr}‖{name}‖color:end‖";
|
||||
if (item.GetComponent<Quality>() != null)
|
||||
@@ -478,10 +478,11 @@ namespace Barotrauma
|
||||
{
|
||||
int row = (int)Math.Floor((double)i / slotsPerRow);
|
||||
int slotsPerThisRow = Math.Min(slotsPerRow, capacity - row * slotsPerRow);
|
||||
int slotNumberOnThisRow = i - row * slotsPerRow;
|
||||
|
||||
int rowWidth = (int)(rectSize.X * slotsPerThisRow + spacing.X * (slotsPerThisRow - 1));
|
||||
slotRect.X = (int)(center.X) - rowWidth / 2;
|
||||
slotRect.X += (int)((rectSize.X + spacing.X) * (i % slotsPerThisRow));
|
||||
slotRect.X += (int)((rectSize.X + spacing.X) * (slotNumberOnThisRow % slotsPerThisRow));
|
||||
|
||||
slotRect.Y = (int)(topLeft.Y + (rectSize.Y + spacing.Y) * row);
|
||||
visualSlots[i] = new VisualSlot(slotRect);
|
||||
@@ -1185,6 +1186,7 @@ namespace Barotrauma
|
||||
{
|
||||
DraggingItems.RemoveAll(it => !Character.Controlled.CanInteractWith(it));
|
||||
}
|
||||
|
||||
if (DraggingItems.Any() && PlayerInput.PrimaryMouseButtonReleased())
|
||||
{
|
||||
Character.Controlled.ClearInputs();
|
||||
@@ -1193,198 +1195,234 @@ namespace Barotrauma
|
||||
if (!DetermineMouseOnInventory(ignoreDraggedItem: true) &&
|
||||
(CharacterHealth.OpenHealthWindow != null || mouseOnPortrait))
|
||||
{
|
||||
bool dropSuccessful = false;
|
||||
foreach (Item item in DraggingItems)
|
||||
if (TryPortraitAndHealthDrop(mouseOnPortrait))
|
||||
{
|
||||
var inventory = item.ParentInventory;
|
||||
var indices = inventory?.FindIndices(item);
|
||||
dropSuccessful |= (CharacterHealth.OpenHealthWindow ?? Character.Controlled.CharacterHealth).OnItemDropped(item, ignoreMousePos: mouseOnPortrait);
|
||||
if (dropSuccessful)
|
||||
{
|
||||
if (indices != null && inventory.visualSlots != null)
|
||||
{
|
||||
foreach (int i in indices)
|
||||
{
|
||||
inventory.visualSlots[i]?.ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.4f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dropSuccessful)
|
||||
{
|
||||
DraggingItems.Clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedSlot == null)
|
||||
{
|
||||
if (DraggingItemToWorld &&
|
||||
Character.Controlled.FocusedItem is { OwnInventory: { } inventory } item && item.GetComponent<ItemContainer>() is { } container &&
|
||||
container.HasRequiredItems(Character.Controlled, addMessage: false) &&
|
||||
container.AllowDragAndDrop &&
|
||||
inventory.CanBePut(DraggingItems.FirstOrDefault()))
|
||||
HandleOutsideInventoryDrop();
|
||||
}
|
||||
else if (!DraggingItems.Any(it => selectedSlot.ParentInventory.slots[selectedSlot.SlotIndex].Contains(it)))
|
||||
{
|
||||
HandleInventorySlotDrop();
|
||||
}
|
||||
|
||||
DraggingItems.Clear();
|
||||
}
|
||||
|
||||
if (selectedSlot != null && !CanSelectSlot(selectedSlot))
|
||||
{
|
||||
selectedSlot = null;
|
||||
}
|
||||
|
||||
bool TryPortraitAndHealthDrop(bool mouseOnPortrait)
|
||||
{
|
||||
bool dropSuccessful = false;
|
||||
foreach (Item item in DraggingItems)
|
||||
{
|
||||
var inventory = item.ParentInventory;
|
||||
var indices = inventory?.FindIndices(item);
|
||||
dropSuccessful |= (CharacterHealth.OpenHealthWindow ?? Character.Controlled.CharacterHealth).OnItemDropped(item, ignoreMousePos: mouseOnPortrait);
|
||||
if (dropSuccessful)
|
||||
{
|
||||
bool anySuccess = false;
|
||||
foreach (Item it in DraggingItems)
|
||||
if (indices != null && inventory.visualSlots != null)
|
||||
{
|
||||
bool success = Character.Controlled.FocusedItem.OwnInventory.TryPutItem(it, Character.Controlled);
|
||||
if (!success) { break; }
|
||||
anySuccess |= success;
|
||||
}
|
||||
if (anySuccess) { SoundPlayer.PlayUISound(GUISoundType.PickItem); }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Screen.Selected is SubEditorScreen)
|
||||
{
|
||||
if (DraggingItems.First()?.ParentInventory != null)
|
||||
foreach (int i in indices)
|
||||
{
|
||||
SubEditorScreen.StoreCommand(new InventoryPlaceCommand(DraggingItems.First().ParentInventory, new List<Item>(DraggingItems), true));
|
||||
inventory.visualSlots[i]?.ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.4f);
|
||||
}
|
||||
}
|
||||
|
||||
SoundPlayer.PlayUISound(GUISoundType.DropItem);
|
||||
bool removed = false;
|
||||
if (Screen.Selected is SubEditorScreen editor)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dropSuccessful)
|
||||
{
|
||||
DraggingItems.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HandleOutsideInventoryDrop()
|
||||
{
|
||||
bool isTargetingValidContainer = Character.Controlled.FocusedItem is { OwnInventory: { } inventory } item &&
|
||||
item.GetComponent<ItemContainer>() is { } container &&
|
||||
container.HasRequiredItems(Character.Controlled, addMessage: false) &&
|
||||
container.AllowDragAndDrop &&
|
||||
inventory.CanBePut(DraggingItems.FirstOrDefault());
|
||||
|
||||
bool isTargetingValidCharacter = IsValidTargetForDragDropGive(Character.Controlled, Character.Controlled.FocusedCharacter);
|
||||
|
||||
if (DraggingItemToWorld && (isTargetingValidContainer || isTargetingValidCharacter))
|
||||
{
|
||||
bool anySuccess = false;
|
||||
foreach (Item it in DraggingItems)
|
||||
{
|
||||
bool success = false;
|
||||
if (isTargetingValidContainer)
|
||||
{
|
||||
if (editor.EntityMenu.Rect.Contains(PlayerInput.MousePosition))
|
||||
success = Character.Controlled.FocusedItem.OwnInventory.TryPutItem(it, Character.Controlled);
|
||||
}
|
||||
if (!success && isTargetingValidCharacter)
|
||||
{
|
||||
success = Character.Controlled.FocusedCharacter.Inventory.TryPutItem(it, Character.Controlled, CharacterInventory.AnySlot);
|
||||
}
|
||||
|
||||
if (!success) { break; }
|
||||
anySuccess = true;
|
||||
}
|
||||
|
||||
if (anySuccess) { SoundPlayer.PlayUISound(GUISoundType.PickItem); }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Screen.Selected is SubEditorScreen)
|
||||
{
|
||||
if (DraggingItems.First()?.ParentInventory != null)
|
||||
{
|
||||
SubEditorScreen.StoreCommand(new InventoryPlaceCommand(DraggingItems.First().ParentInventory, new List<Item>(DraggingItems), true));
|
||||
}
|
||||
}
|
||||
|
||||
SoundPlayer.PlayUISound(GUISoundType.DropItem);
|
||||
bool removed = false;
|
||||
if (Screen.Selected is SubEditorScreen editor)
|
||||
{
|
||||
if (editor.EntityMenu.Rect.Contains(PlayerInput.MousePosition))
|
||||
{
|
||||
DraggingItems.ForEachMod(it => it.Remove());
|
||||
removed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (editor.WiringMode)
|
||||
{
|
||||
DraggingItems.ForEachMod(it => it.Remove());
|
||||
removed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (editor.WiringMode)
|
||||
{
|
||||
DraggingItems.ForEachMod(it => it.Remove());
|
||||
removed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggingItems.ForEachMod(it => it.Drop(Character.Controlled));
|
||||
}
|
||||
DraggingItems.ForEachMod(it => it.Drop(Character.Controlled));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggingItems.ForEachMod(it => it.Drop(Character.Controlled));
|
||||
DraggingItems.First().CreateDroppedStack(DraggingItems, allowClientExecute: false);
|
||||
}
|
||||
SoundPlayer.PlayUISound(removed ? GUISoundType.PickItem : GUISoundType.DropItem);
|
||||
}
|
||||
}
|
||||
else if (!DraggingItems.Any(it => selectedSlot.ParentInventory.slots[selectedSlot.SlotIndex].Contains(it)))
|
||||
{
|
||||
Inventory oldInventory = DraggingItems.First().ParentInventory;
|
||||
Inventory selectedInventory = selectedSlot.ParentInventory;
|
||||
int slotIndex = selectedSlot.SlotIndex;
|
||||
int oldSlot = oldInventory == null ? 0 : Array.IndexOf(oldInventory.slots, DraggingItems);
|
||||
|
||||
//if attempting to drop into an invalid slot in the same inventory, try to move to the correct slot
|
||||
if (selectedInventory.slots[slotIndex].Empty() &&
|
||||
selectedInventory == Character.Controlled.Inventory &&
|
||||
!DraggingItems.First().AllowedSlots.Any(a => a.HasFlag(Character.Controlled.Inventory.SlotTypes[slotIndex])) &&
|
||||
DraggingItems.Any(it => selectedInventory.TryPutItem(it, Character.Controlled, it.AllowedSlots)))
|
||||
else
|
||||
{
|
||||
if (selectedInventory.visualSlots != null)
|
||||
DraggingItems.ForEachMod(it => it.Drop(Character.Controlled));
|
||||
DraggingItems.First().CreateDroppedStack(DraggingItems, allowClientExecute: false);
|
||||
}
|
||||
SoundPlayer.PlayUISound(removed ? GUISoundType.PickItem : GUISoundType.DropItem);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleInventorySlotDrop()
|
||||
{
|
||||
Inventory oldInventory = DraggingItems.First().ParentInventory;
|
||||
Inventory selectedInventory = selectedSlot.ParentInventory;
|
||||
int slotIndex = selectedSlot.SlotIndex;
|
||||
int oldSlot = oldInventory == null ? 0 : Array.IndexOf(oldInventory.slots, DraggingItems);
|
||||
|
||||
//if attempting to drop into an invalid slot in the same inventory, try to move to the correct slot
|
||||
if (selectedInventory.slots[slotIndex].Empty() &&
|
||||
selectedInventory == Character.Controlled.Inventory &&
|
||||
!DraggingItems.First().AllowedSlots.Any(a => a.HasFlag(Character.Controlled.Inventory.SlotTypes[slotIndex])) &&
|
||||
DraggingItems.Any(it => selectedInventory.TryPutItem(it, Character.Controlled, it.AllowedSlots)))
|
||||
{
|
||||
if (selectedInventory.visualSlots != null)
|
||||
{
|
||||
for (int i = 0; i < selectedInventory.visualSlots.Length; i++)
|
||||
{
|
||||
for (int i = 0; i < selectedInventory.visualSlots.Length; i++)
|
||||
if (DraggingItems.Any(it => selectedInventory.slots[i].Contains(it)))
|
||||
{
|
||||
if (DraggingItems.Any(it => selectedInventory.slots[i].Contains(it)))
|
||||
selectedInventory.visualSlots[slotIndex].ShowBorderHighlight(Color.White, 0.1f, 0.4f);
|
||||
}
|
||||
}
|
||||
selectedInventory.visualSlots[slotIndex].ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f);
|
||||
}
|
||||
SoundPlayer.PlayUISound(GUISoundType.PickItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool anySuccess = false;
|
||||
//if we're dragging a stack of partial items or trying to drag to a stack of partial items
|
||||
//(which should not normally exist, but can happen when e.g. fire damages a stack of items)
|
||||
//don't allow combining because it leads to weird behavior (stack of items of mixed quality)
|
||||
bool allowCombine = !(DraggingItems.Count(it => !it.IsFullCondition && it.Condition > 0.0f) > 1 ||
|
||||
selectedInventory.GetItemsAt(slotIndex).Count(it => !it.IsFullCondition && it.Condition > 0.0f) > 1);
|
||||
int itemCount = 0;
|
||||
foreach (Item item in DraggingItems)
|
||||
{
|
||||
if (selectedInventory.GetItemAt(slotIndex)?.OwnInventory?.Container is { } container &&
|
||||
container.Inventory.CanBePut(item))
|
||||
{
|
||||
if (!container.AllowDragAndDrop || !container.AllowAccess)
|
||||
{
|
||||
allowCombine = false;
|
||||
}
|
||||
}
|
||||
bool success = selectedInventory.TryPutItem(item, slotIndex, allowSwapping: !anySuccess, allowCombine, Character.Controlled);
|
||||
if (success)
|
||||
{
|
||||
anySuccess = true;
|
||||
itemCount++;
|
||||
}
|
||||
if (!success || itemCount >= item.Prefab.GetMaxStackSize(selectedInventory))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anySuccess)
|
||||
{
|
||||
highlightedSubInventorySlots.RemoveWhere(s => s.ParentInventory == oldInventory || s.ParentInventory == selectedInventory);
|
||||
if (SubEditorScreen.IsSubEditor())
|
||||
{
|
||||
foreach (Item draggingItem in DraggingItems)
|
||||
{
|
||||
if (selectedInventory.slots[slotIndex].Contains(draggingItem))
|
||||
{
|
||||
selectedInventory.visualSlots[slotIndex].ShowBorderHighlight(Color.White, 0.1f, 0.4f);
|
||||
SubEditorScreen.StoreCommand(new InventoryMoveCommand(oldInventory, selectedInventory, draggingItem, oldSlot, slotIndex));
|
||||
}
|
||||
}
|
||||
selectedInventory.visualSlots[slotIndex].ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f);
|
||||
}
|
||||
if (selectedInventory.visualSlots != null) { selectedInventory.visualSlots[slotIndex].ShowBorderHighlight(Color.White, 0.1f, 0.4f); }
|
||||
SoundPlayer.PlayUISound(GUISoundType.PickItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool anySuccess = false;
|
||||
bool allowCombine = true;
|
||||
//if we're dragging a stack of partial items or trying to drag to a stack of partial items
|
||||
//(which should not normally exist, but can happen when e.g. fire damages a stack of items)
|
||||
//don't allow combining because it leads to weird behavior (stack of items of mixed quality)
|
||||
if (DraggingItems.Count(it => !it.IsFullCondition && it.Condition > 0.0f) > 1 ||
|
||||
selectedInventory.GetItemsAt(slotIndex).Count(it => !it.IsFullCondition && it.Condition > 0.0f) > 1)
|
||||
{
|
||||
allowCombine = false;
|
||||
}
|
||||
int itemCount = 0;
|
||||
foreach (Item item in DraggingItems)
|
||||
{
|
||||
if (selectedInventory.GetItemAt(slotIndex)?.OwnInventory?.Container is { } container &&
|
||||
container.Inventory.CanBePut(item))
|
||||
{
|
||||
if (!container.AllowDragAndDrop || !container.AllowAccess)
|
||||
{
|
||||
allowCombine = false;
|
||||
}
|
||||
}
|
||||
bool success = selectedInventory.TryPutItem(item, slotIndex, allowSwapping: !anySuccess, allowCombine, Character.Controlled);
|
||||
if (success)
|
||||
{
|
||||
anySuccess = true;
|
||||
itemCount++;
|
||||
}
|
||||
if (!success || itemCount >= item.Prefab.GetMaxStackSize(selectedInventory))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anySuccess)
|
||||
{
|
||||
highlightedSubInventorySlots.RemoveWhere(s => s.ParentInventory == oldInventory || s.ParentInventory == selectedInventory);
|
||||
if (SubEditorScreen.IsSubEditor())
|
||||
{
|
||||
foreach (Item draggingItem in DraggingItems)
|
||||
{
|
||||
if (selectedInventory.slots[slotIndex].Contains(draggingItem))
|
||||
{
|
||||
SubEditorScreen.StoreCommand(new InventoryMoveCommand(oldInventory, selectedInventory, draggingItem, oldSlot, slotIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectedInventory.visualSlots != null) { selectedInventory.visualSlots[slotIndex].ShowBorderHighlight(Color.White, 0.1f, 0.4f); }
|
||||
SoundPlayer.PlayUISound(GUISoundType.PickItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selectedInventory.visualSlots != null){ selectedInventory.visualSlots[slotIndex].ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f); }
|
||||
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
|
||||
}
|
||||
if (selectedInventory.visualSlots != null){ selectedInventory.visualSlots[slotIndex].ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f); }
|
||||
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
|
||||
}
|
||||
|
||||
selectedInventory.HideTimer = 2.0f;
|
||||
if (selectedSlot.ParentInventory?.Owner is Item parentItem && parentItem.ParentInventory != null)
|
||||
{
|
||||
for (int i = 0; i < parentItem.ParentInventory.capacity; i++)
|
||||
{
|
||||
if (parentItem.ParentInventory.HideSlot(i)) { continue; }
|
||||
if (parentItem.ParentInventory.slots[i].FirstOrDefault() != parentItem) { continue; }
|
||||
|
||||
highlightedSubInventorySlots.Add(new SlotReference(
|
||||
parentItem.ParentInventory, parentItem.ParentInventory.visualSlots[i],
|
||||
i, false, selectedSlot.ParentInventory));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
DraggingItems.Clear();
|
||||
DraggingSlot = null;
|
||||
}
|
||||
|
||||
DraggingItems.Clear();
|
||||
}
|
||||
selectedInventory.HideTimer = 2.0f;
|
||||
if (selectedSlot.ParentInventory?.Owner is Item parentItem && parentItem.ParentInventory != null)
|
||||
{
|
||||
for (int i = 0; i < parentItem.ParentInventory.capacity; i++)
|
||||
{
|
||||
if (parentItem.ParentInventory.HideSlot(i)) { continue; }
|
||||
if (parentItem.ParentInventory.slots[i].FirstOrDefault() != parentItem) { continue; }
|
||||
|
||||
if (selectedSlot != null && !CanSelectSlot(selectedSlot))
|
||||
{
|
||||
selectedSlot = null;
|
||||
}
|
||||
highlightedSubInventorySlots.Add(new SlotReference(
|
||||
parentItem.ParentInventory, parentItem.ParentInventory.visualSlots[i],
|
||||
i, false, selectedSlot.ParentInventory));
|
||||
break;
|
||||
}
|
||||
}
|
||||
DraggingItems.Clear();
|
||||
DraggingSlot = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidTargetForDragDropGive(Character giver, Character receiver)
|
||||
{
|
||||
if (giver == null || receiver == null) { return false; }
|
||||
if (receiver == giver) { return false; }
|
||||
return receiver.IsInventoryAccessibleTo(giver, IsDragAndDropGiveAllowed ? CharacterInventory.AccessLevel.Allowed : CharacterInventory.AccessLevel.Limited);
|
||||
}
|
||||
|
||||
private static bool CanSelectSlot(SlotReference selectedSlot)
|
||||
@@ -1504,6 +1542,31 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (DraggingItems.Any())
|
||||
{
|
||||
DrawDragRelated();
|
||||
}
|
||||
|
||||
if (selectedSlot != null && selectedSlot.Item != null)
|
||||
{
|
||||
Rectangle slotRect = selectedSlot.Slot.Rect;
|
||||
slotRect.Location += selectedSlot.Slot.DrawOffset.ToPoint();
|
||||
if (selectedSlot.TooltipNeedsRefresh())
|
||||
{
|
||||
selectedSlot.RefreshTooltip();
|
||||
}
|
||||
|
||||
if (!slotIconTooltip.IsNullOrEmpty())
|
||||
{
|
||||
DrawToolTip(spriteBatch, slotIconTooltip, slotRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawToolTip(spriteBatch, selectedSlot.Tooltip, slotRect);
|
||||
}
|
||||
slotIconTooltip = string.Empty;
|
||||
}
|
||||
|
||||
void DrawDragRelated()
|
||||
{
|
||||
if (DraggingSlot == null || (!DraggingSlot.MouseOn()))
|
||||
{
|
||||
@@ -1521,10 +1584,8 @@ namespace Barotrauma
|
||||
if ((GUI.MouseOn == null || mouseOnHealthInterface) && selectedSlot == null)
|
||||
{
|
||||
var shadowSprite = GUIStyle.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0];
|
||||
LocalizedString toolTip = mouseOnHealthInterface ? TextManager.Get("QuickUseAction.UseTreatment") :
|
||||
Character.Controlled.FocusedItem != null ?
|
||||
TextManager.GetWithVariable("PutItemIn", "[itemname]", Character.Controlled.FocusedItem.Name, FormatCapitals.Yes) :
|
||||
TextManager.Get(Screen.Selected is SubEditorScreen editor && editor.EntityMenu.Rect.Contains(PlayerInput.MousePosition) ? "Delete" : "DropItem");
|
||||
|
||||
(LocalizedString toolTip, Color toolTipColor) = GetDragLabelTextAndColor(mouseOnHealthInterface);
|
||||
|
||||
Vector2 nameSize = GUIStyle.Font.MeasureString(DraggingItems.First().Name);
|
||||
Vector2 toolTipSize = GUIStyle.SmallFont.MeasureString(toolTip);
|
||||
@@ -1544,7 +1605,7 @@ namespace Barotrauma
|
||||
|
||||
GUI.DrawString(spriteBatch, textPos + new Vector2(nameSize.X * textOffset, -iconSize / 2), DraggingItems.First().Name, Color.White);
|
||||
GUI.DrawString(spriteBatch, textPos + new Vector2(toolTipSize.X * textOffset, 0), toolTip,
|
||||
color: Character.Controlled.FocusedItem == null && !mouseOnHealthInterface ? GUIStyle.Red : Color.LightGreen,
|
||||
color: toolTipColor,
|
||||
font: GUIStyle.SmallFont);
|
||||
}
|
||||
|
||||
@@ -1587,24 +1648,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedSlot != null && selectedSlot.Item != null)
|
||||
(LocalizedString, Color) GetDragLabelTextAndColor(bool mouseOnHealthInterface)
|
||||
{
|
||||
Rectangle slotRect = selectedSlot.Slot.Rect;
|
||||
slotRect.Location += selectedSlot.Slot.DrawOffset.ToPoint();
|
||||
if (selectedSlot.TooltipNeedsRefresh())
|
||||
bool useDragDropGive = IsValidTargetForDragDropGive(Character.Controlled, Character.Controlled.FocusedCharacter);
|
||||
|
||||
Color toolTipColor = Color.LightGreen;
|
||||
|
||||
LocalizedString toolTip;
|
||||
if (mouseOnHealthInterface)
|
||||
{
|
||||
selectedSlot.RefreshTooltip();
|
||||
toolTip = TextManager.Get("QuickUseAction.UseTreatment");
|
||||
}
|
||||
|
||||
if (!slotIconTooltip.IsNullOrEmpty())
|
||||
else if (Character.Controlled.FocusedItem != null)
|
||||
{
|
||||
DrawToolTip(spriteBatch, slotIconTooltip, slotRect);
|
||||
toolTip = TextManager.GetWithVariable("PutItemIn", "[itemname]", Character.Controlled.FocusedItem.Name, FormatCapitals.Yes);
|
||||
}
|
||||
else if (useDragDropGive)
|
||||
{
|
||||
toolTip = TextManager.GetWithVariable("GiveItemTo", "[character]", Character.Controlled.FocusedCharacter.Name, FormatCapitals.Yes);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawToolTip(spriteBatch, selectedSlot.Tooltip, slotRect);
|
||||
toolTipColor = GUIStyle.Red;
|
||||
toolTip = TextManager.Get(Screen.Selected is SubEditorScreen editor && editor.EntityMenu.Rect.Contains(PlayerInput.MousePosition) ? "Delete" : "DropItem");
|
||||
}
|
||||
slotIconTooltip = string.Empty;
|
||||
return (toolTip, toolTipColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1801,7 +1869,7 @@ namespace Barotrauma
|
||||
DrawSideIcon(deconstructOrder.SymbolSprite, Direction.Right, TextManager.Get("tooltip.markedfordeconstruction"), GUIStyle.Red, out bool mouseOn);
|
||||
if (mouseOn) { availableContextualOrder = (item, Tags.DontDeconstructThis); }
|
||||
}
|
||||
else if (((item.SpawnedInCurrentOutpost && !item.AllowStealing) || (inventory != null && inventory.slots[slotIndex].Items.Any(it => it.SpawnedInCurrentOutpost && !it.AllowStealing))) && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
|
||||
else if ((item.Illegitimate || (inventory != null && inventory.slots[slotIndex].Items.Any(it => it.Illegitimate))) && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
|
||||
{
|
||||
DrawSideIcon(CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand], Direction.Left, TextManager.Get("tooltip.stolenitem"), GUIStyle.Red, out _);
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ namespace Barotrauma
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Color? overrideColor = null)
|
||||
{
|
||||
if (!Visible || (!editing && HiddenInGame) || !SubEditorScreen.IsLayerVisible(this)) { return; }
|
||||
if (!Visible || (!editing && IsHidden) || !SubEditorScreen.IsLayerVisible(this)) { return; }
|
||||
|
||||
if (editing)
|
||||
{
|
||||
@@ -424,7 +424,7 @@ namespace Barotrauma
|
||||
textureScale: Vector2.One * Scale,
|
||||
depth: d);
|
||||
}
|
||||
DrawDecorativeSprites(spriteBatch, DrawPosition, flippedX && Prefab.CanSpriteFlipX, flippedY && Prefab.CanSpriteFlipY, rotation: 0, depth);
|
||||
DrawDecorativeSprites(spriteBatch, DrawPosition, flippedX && Prefab.CanSpriteFlipX, flippedY && Prefab.CanSpriteFlipY, rotation: 0, depth, overrideColor);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -445,7 +445,7 @@ namespace Barotrauma
|
||||
Prefab.DamagedInfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, Infector.HealthColor, Prefab.DamagedInfectedSprite.Origin, RotationRad, Scale, activeSprite.effects, depth - 0.002f);
|
||||
}
|
||||
|
||||
DrawDecorativeSprites(spriteBatch, DrawPosition, flippedX && Prefab.CanSpriteFlipX, flippedY && Prefab.CanSpriteFlipY, -RotationRad, depth);
|
||||
DrawDecorativeSprites(spriteBatch, DrawPosition, flippedX && Prefab.CanSpriteFlipX, flippedY && Prefab.CanSpriteFlipY, -RotationRad, depth, overrideColor);
|
||||
}
|
||||
}
|
||||
else if (body.Enabled)
|
||||
@@ -456,30 +456,49 @@ namespace Barotrauma
|
||||
//don't draw the item on hands if it's also being worn
|
||||
if (GetComponent<Wearable>() is { IsActive: true }) { return; }
|
||||
if (!back) { return; }
|
||||
float depthStep = 0.000001f;
|
||||
if (holdable.Picker.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == this)
|
||||
{
|
||||
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightArm);
|
||||
if (holdLimb?.ActiveSprite != null)
|
||||
{
|
||||
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() + depthStep * 2;
|
||||
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
|
||||
{
|
||||
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Max(wearableSprite.Sprite.Depth + depthStep, depth); }
|
||||
}
|
||||
}
|
||||
depth = GetHeldItemDepth(LimbType.RightHand, depth);
|
||||
}
|
||||
else if (holdable.Picker.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand) == this)
|
||||
{
|
||||
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftArm);
|
||||
depth = GetHeldItemDepth(LimbType.LeftHand, depth);
|
||||
}
|
||||
|
||||
float GetHeldItemDepth(LimbType limb, float depth)
|
||||
{
|
||||
//offset used to make sure the item draws just slightly behind the right hand, or slightly in front of the left hand
|
||||
float limbDepthOffset = 0.000001f;
|
||||
float depthOffset = holdable.Picker.AnimController.GetDepthOffset();
|
||||
//use the upper arm as a reference, to ensure the item gets drawn behind / in front of the whole arm (not just the forearm)
|
||||
Limb holdLimb = holdable.Picker.AnimController.GetLimb(limb == LimbType.RightHand ? LimbType.RightArm : LimbType.LeftArm);
|
||||
if (holdLimb?.ActiveSprite != null)
|
||||
{
|
||||
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() - depthStep * 2;
|
||||
depth =
|
||||
holdLimb.ActiveSprite.Depth
|
||||
+ depthOffset
|
||||
+ limbDepthOffset * 2 * (limb == LimbType.RightHand ? 1 : -1);
|
||||
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
|
||||
{
|
||||
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Min(wearableSprite.Sprite.Depth - depthStep, depth); }
|
||||
if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null)
|
||||
{
|
||||
depth =
|
||||
limb == LimbType.RightHand ?
|
||||
Math.Max(wearableSprite.Sprite.Depth + limbDepthOffset, depth) :
|
||||
Math.Min(wearableSprite.Sprite.Depth - limbDepthOffset, depth);
|
||||
}
|
||||
}
|
||||
var head = holdable.Picker.AnimController.GetLimb(LimbType.Head);
|
||||
if (head != null)
|
||||
{
|
||||
//ensure the holdable item is always drawn in front of the head no matter what the wearables or whatnot do with the sprite depths
|
||||
depth =
|
||||
limb == LimbType.RightHand ?
|
||||
Math.Min(head.Sprite.Depth + depthOffset - limbDepthOffset, depth) :
|
||||
Math.Max(head.Sprite.Depth + depthOffset + limbDepthOffset, depth);
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
Vector2 origin = GetSpriteOrigin(activeSprite);
|
||||
@@ -489,7 +508,7 @@ namespace Barotrauma
|
||||
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
|
||||
body.Draw(spriteBatch, fadeInBrokenSprite.Sprite, color * fadeInBrokenSpriteAlpha, d, Scale);
|
||||
}
|
||||
DrawDecorativeSprites(spriteBatch, body.DrawPosition, flipX: body.Dir < 0, flipY: false, rotation: body.Rotation, depth: depth);
|
||||
DrawDecorativeSprites(spriteBatch, body.DrawPosition, flipX: body.Dir < 0, flipY: false, rotation: body.Rotation, depth, overrideColor);
|
||||
}
|
||||
|
||||
foreach (var upgrade in Upgrades)
|
||||
@@ -617,11 +636,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawDecorativeSprites(SpriteBatch spriteBatch, Vector2 drawPos, bool flipX, bool flipY, float rotation, float depth)
|
||||
public void DrawDecorativeSprites(SpriteBatch spriteBatch, Vector2 drawPos, bool flipX, bool flipY, float rotation, float depth, Color? overrideColor = null)
|
||||
{
|
||||
foreach (var decorativeSprite in Prefab.DecorativeSprites)
|
||||
{
|
||||
Color decorativeSpriteColor = GetSpriteColor(decorativeSprite.Color).Multiply(GetSpriteColor(spriteColor));
|
||||
Color decorativeSpriteColor = overrideColor ?? GetSpriteColor(decorativeSprite.Color).Multiply(GetSpriteColor(spriteColor));
|
||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier,
|
||||
|
||||
@@ -215,7 +215,9 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) { return; }
|
||||
//do not emit particles unless water is flowing towards the target hull
|
||||
//(using lerpedFlowForce smooths out "flickers" when the direction of flow is rapidly changing)
|
||||
if (Math.Sign(flowTargetHull.WorldPosition.Y - WorldPosition.Y) != Math.Sign(lerpedFlowForce.Y)) { return; }
|
||||
|
||||
float particlesPerSec = Math.Max(open * rect.Width * particleAmountMultiplier, 10.0f);
|
||||
float emitInterval = 1.0f / particlesPerSec;
|
||||
|
||||
@@ -210,7 +210,9 @@ namespace Barotrauma
|
||||
{
|
||||
bool primaryMouseButtonHeld = PlayerInput.PrimaryMouseButtonHeld();
|
||||
bool secondaryMouseButtonHeld = PlayerInput.SecondaryMouseButtonHeld();
|
||||
if (!primaryMouseButtonHeld && !secondaryMouseButtonHeld) { return; }
|
||||
bool doubleClicked = PlayerInput.DoubleClicked();
|
||||
bool secondaryDoubleClicked = PlayerInput.SecondaryDoubleClicked();
|
||||
if (!primaryMouseButtonHeld && !secondaryMouseButtonHeld && !doubleClicked && !secondaryDoubleClicked) { return; }
|
||||
|
||||
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
Hull hull = FindHull(position);
|
||||
@@ -218,29 +220,67 @@ namespace Barotrauma
|
||||
if (hull == null || hull.IdFreed) { return; }
|
||||
if (EditWater)
|
||||
{
|
||||
const float waterIncrement = 100000.0f;
|
||||
if (primaryMouseButtonHeld)
|
||||
{
|
||||
ShowHulls = true;
|
||||
hull.WaterVolume += 100000.0f * deltaTime;
|
||||
hull.networkUpdatePending = true;
|
||||
hull.serverUpdateDelay = 0.5f;
|
||||
SetWaterVolume(hull.WaterVolume + waterIncrement * deltaTime);
|
||||
}
|
||||
else if (secondaryMouseButtonHeld)
|
||||
{
|
||||
hull.WaterVolume -= 100000.0f * deltaTime;
|
||||
SetWaterVolume(hull.WaterVolume - waterIncrement * deltaTime);
|
||||
}
|
||||
|
||||
if (doubleClicked)
|
||||
{
|
||||
SetWaterVolume(hull.Volume * MaxCompress);
|
||||
}
|
||||
else if (secondaryDoubleClicked)
|
||||
{
|
||||
SetWaterVolume(0f);
|
||||
}
|
||||
|
||||
void SetWaterVolume(float newVolume)
|
||||
{
|
||||
ShowHulls = true;
|
||||
hull.WaterVolume = newVolume;
|
||||
hull.networkUpdatePending = true;
|
||||
hull.serverUpdateDelay = 0.5f;
|
||||
}
|
||||
|
||||
}
|
||||
else if (EditFire)
|
||||
{
|
||||
bool networkUpdate = false;
|
||||
|
||||
if (primaryMouseButtonHeld)
|
||||
{
|
||||
new FireSource(position, hull, isNetworkMessage: true);
|
||||
networkUpdate = true;
|
||||
}
|
||||
else if (secondaryMouseButtonHeld || secondaryDoubleClicked)
|
||||
{
|
||||
for (int index = hull.FireSources.Count - 1; index >= 0; index--)
|
||||
{
|
||||
var currentFireSource = hull.FireSources[index];
|
||||
|
||||
if (secondaryMouseButtonHeld)
|
||||
{
|
||||
const float extinguishAmount = 120f;
|
||||
currentFireSource.Extinguish(deltaTime, extinguishAmount);
|
||||
networkUpdate = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFireSource.Remove();
|
||||
networkUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (networkUpdate)
|
||||
{
|
||||
hull.networkUpdatePending = true;
|
||||
hull.serverUpdateDelay = 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -70,7 +70,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
public bool LightingEnabled = true;
|
||||
|
||||
public bool ObstructVision;
|
||||
public float ObstructVisionAmount;
|
||||
|
||||
private readonly Texture2D visionCircle;
|
||||
|
||||
@@ -498,7 +498,7 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
foreach (MapEntity e in (Submarine.VisibleEntities ?? MapEntity.MapEntityList))
|
||||
{
|
||||
if (e is Item item && !item.HiddenInGame && item.GetComponent<Wire>() is Wire wire)
|
||||
if (e is Item item && !item.IsHidden && item.GetComponent<Wire>() is Wire wire)
|
||||
{
|
||||
wire.DebugDraw(spriteBatch, alpha: 0.4f);
|
||||
}
|
||||
@@ -664,7 +664,7 @@ namespace Barotrauma.Lights
|
||||
visibleHulls.Clear();
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.HiddenInGame) { continue; }
|
||||
if (hull.IsHidden) { continue; }
|
||||
var drawRect =
|
||||
hull.Submarine == null ?
|
||||
hull.Rect :
|
||||
@@ -682,12 +682,12 @@ namespace Barotrauma.Lights
|
||||
|
||||
public void UpdateObstructVision(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Vector2 lookAtPosition)
|
||||
{
|
||||
if ((!LosEnabled || LosMode == LosMode.None) && !ObstructVision) { return; }
|
||||
if ((!LosEnabled || LosMode == LosMode.None) && ObstructVisionAmount <= 0.0f) { return; }
|
||||
if (ViewTarget == null) return;
|
||||
|
||||
graphics.SetRenderTarget(LosTexture);
|
||||
|
||||
if (ObstructVision)
|
||||
if (ObstructVisionAmount > 0.0f)
|
||||
{
|
||||
graphics.Clear(Color.Black);
|
||||
Vector2 diff = lookAtPosition - ViewTarget.WorldPosition;
|
||||
@@ -697,13 +697,14 @@ namespace Barotrauma.Lights
|
||||
|
||||
//the visible area stretches to the maximum when the cursor is this far from the character
|
||||
const float MaxOffset = 256.0f;
|
||||
const float MinHorizontalScale = 2.2f;
|
||||
const float MaxHorizontalScale = 2.8f;
|
||||
const float VerticalScale = 2.5f;
|
||||
//the magic numbers here are just based on experimentation
|
||||
float MinHorizontalScale = MathHelper.Lerp(3.5f, 1.5f, ObstructVisionAmount);
|
||||
float MaxHorizontalScale = MinHorizontalScale * 1.25f;
|
||||
float VerticalScale = MathHelper.Lerp(4.0f, 1.25f, ObstructVisionAmount);
|
||||
|
||||
//Starting point and scale-based modifier that moves the point of origin closer to the edge of the texture if the player moves their mouse further away, or vice versa.
|
||||
float relativeOriginStartPosition = 0.22f; //Increasing this value moves the origin further behind the character
|
||||
float originStartPosition = visionCircle.Width * relativeOriginStartPosition;
|
||||
float relativeOriginStartPosition = 0.1f; //Increasing this value moves the origin further behind the character
|
||||
float originStartPosition = visionCircle.Width * relativeOriginStartPosition * MinHorizontalScale;
|
||||
float relativeOriginLookAtPosModifier = -0.055f; //Increase this value increases how much the vision changes by moving the mouse
|
||||
float originLookAtPosModifier = visionCircle.Width * relativeOriginLookAtPosModifier;
|
||||
|
||||
|
||||
@@ -1115,13 +1115,23 @@ namespace Barotrauma
|
||||
|
||||
float subCrushDepth = SubmarineInfo.GetSubCrushDepth(SubmarineSelection.CurrentOrPendingSubmarine(), ref pendingSubInfo);
|
||||
string crushDepthWarningIconStyle = null;
|
||||
if (connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio > subCrushDepth)
|
||||
|
||||
var levelData = connection.LevelData;
|
||||
float spawnDepth =
|
||||
levelData.InitialDepth +
|
||||
//base the warning on the start or end position of the level, whichever is deeper
|
||||
levelData.Size.Y * Math.Max(levelData.GenerationParams.StartPosition.Y, levelData.GenerationParams.EndPosition.Y);
|
||||
|
||||
//"high warning" if the sub spawns at/below crush depth
|
||||
if (spawnDepth * Physics.DisplayToRealWorldRatio > subCrushDepth)
|
||||
{
|
||||
iconCount++;
|
||||
crushDepthWarningIconStyle = "CrushDepthWarningHighIcon";
|
||||
tooltip = "crushdepthwarninghigh";
|
||||
}
|
||||
else if ((connection.LevelData.InitialDepth + connection.LevelData.Size.Y) * Physics.DisplayToRealWorldRatio > subCrushDepth)
|
||||
//"low warning" if the spawn position is less than the level's height away from crush depth
|
||||
//(i.e. the crush depth is pretty close to the spawn pos, possibly inside the level or at least close enough that many parts of the abyss are unreachable)
|
||||
else if ((spawnDepth + connection.LevelData.Size.Y) * Physics.DisplayToRealWorldRatio > subCrushDepth)
|
||||
{
|
||||
iconCount++;
|
||||
crushDepthWarningIconStyle = "CrushDepthWarningLowIcon";
|
||||
|
||||
@@ -382,7 +382,10 @@ namespace Barotrauma
|
||||
if (!HasBody && !ShowStructures) { return; }
|
||||
if (HasBody && !ShowWalls) { return; }
|
||||
}
|
||||
else if (HiddenInGame) { return; }
|
||||
else if (IsHidden)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Color color = IsIncludedInSelection && editing ? GUIStyle.Blue : IsHighlighted ? GUIStyle.Orange * Math.Max(spriteColor.A / (float) byte.MaxValue, 0.1f) : spriteColor;
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ namespace Barotrauma
|
||||
|
||||
public override bool SelectableInEditor
|
||||
{
|
||||
get { return !IsHidden(); }
|
||||
get { return ShouldDrawIcon(); }
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
|
||||
{
|
||||
if (!editing && (!GameMain.DebugDraw || Screen.Selected.Cam.Zoom < 0.1f)) { return; }
|
||||
if (IsHidden()) { return; }
|
||||
if (!ShouldDrawIcon()) { return; }
|
||||
|
||||
Vector2 drawPos = Position;
|
||||
if (Submarine != null) { drawPos += Submarine.DrawPosition; }
|
||||
@@ -59,8 +59,10 @@ namespace Barotrauma
|
||||
Color.White);
|
||||
}
|
||||
|
||||
Sprite sprite = iconSprites[SpawnType.ToString()];
|
||||
Sprite sprite2 = null;
|
||||
//there are no sprites for all possible combinations of SpawnType flags, but in the vanilla game the only possible combination is
|
||||
//SpawnType.Disabled + some other flag, in which case it's fine to just not show the icon.
|
||||
iconSprites.TryGetValue(SpawnType.ToString(), out Sprite sprite);
|
||||
if (spawnType == SpawnType.Human && AssignedJob?.Icon != null)
|
||||
{
|
||||
sprite = iconSprites["Path"];
|
||||
@@ -87,9 +89,12 @@ namespace Barotrauma
|
||||
sprite = iconSprites["Ladder"];
|
||||
}
|
||||
|
||||
float spriteScale = iconSize / (float)sprite.SourceRect.Width;
|
||||
sprite.Draw(spriteBatch, drawPos, clr, origin: sprite.size / 2, scale: spriteScale, depth: 0.001f);
|
||||
sprite2?.Draw(spriteBatch, drawPos + sprite.size * spriteScale * 0.5f, clr, origin: sprite2.size / 2, scale: spriteScale, depth: 0.001f);
|
||||
if (sprite != null)
|
||||
{
|
||||
float spriteScale = iconSize / (float)sprite.SourceRect.Width;
|
||||
sprite.Draw(spriteBatch, drawPos, clr, origin: sprite.size / 2, scale: spriteScale, depth: 0.001f);
|
||||
sprite2?.Draw(spriteBatch, drawPos + sprite.size * spriteScale * 0.5f, clr, origin: sprite2.size / 2, scale: spriteScale, depth: 0.001f);
|
||||
}
|
||||
|
||||
if (spawnType == SpawnType.Human && AssignedJob?.Icon != null)
|
||||
{
|
||||
@@ -160,22 +165,22 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsMouseOn(Vector2 position)
|
||||
{
|
||||
if (IsHidden()) { return false; }
|
||||
if (!ShouldDrawIcon()) { return false; }
|
||||
float dist = Vector2.DistanceSquared(position, WorldPosition);
|
||||
float radius = (SpawnType == SpawnType.Path ? WaypointSize : SpawnPointSize) * 0.6f;
|
||||
return dist < radius * radius;
|
||||
}
|
||||
|
||||
private bool IsHidden()
|
||||
private bool ShouldDrawIcon()
|
||||
{
|
||||
if (!SubEditorScreen.IsLayerVisible(this)) { return true; }
|
||||
if (!SubEditorScreen.IsLayerVisible(this)) { return false; }
|
||||
if (spawnType == SpawnType.Path)
|
||||
{
|
||||
return (!GameMain.DebugDraw && !ShowWayPoints);
|
||||
return GameMain.DebugDraw || ShowWayPoints;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (!GameMain.DebugDraw && !ShowSpawnPoints);
|
||||
return GameMain.DebugDraw || ShowSpawnPoints;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Barotrauma.Networking
|
||||
txt = msg.ReadString();
|
||||
|
||||
string senderName = msg.ReadString();
|
||||
Entity sender = null;
|
||||
Character senderCharacter = null;
|
||||
Client senderClient = null;
|
||||
bool hasSenderClient = msg.ReadBoolean();
|
||||
@@ -40,13 +41,14 @@ namespace Barotrauma.Networking
|
||||
=> c.SessionOrAccountIdMatches(userId));
|
||||
if (senderClient != null) { senderName = senderClient.Name; }
|
||||
}
|
||||
bool hasSenderCharacter = msg.ReadBoolean();
|
||||
if (hasSenderCharacter)
|
||||
bool hasSender = msg.ReadBoolean();
|
||||
if (hasSender)
|
||||
{
|
||||
senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
|
||||
if (senderCharacter != null)
|
||||
sender = Entity.FindEntityByID(msg.ReadUInt16());
|
||||
senderCharacter = sender as Character;
|
||||
if (sender is Character or Item)
|
||||
{
|
||||
senderName = senderCharacter.Name;
|
||||
senderName = OrderChatMessage.NameFromEntityOrNull(sender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +182,7 @@ namespace Barotrauma.Networking
|
||||
GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
|
||||
break;
|
||||
default:
|
||||
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType, textColor: textColor);
|
||||
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, sender, changeType, textColor: textColor);
|
||||
if (type == ChatMessageType.Radio && CanUseRadio(senderCharacter, out WifiComponent radio))
|
||||
{
|
||||
Signal s = new Signal(txt, sender: senderCharacter, source: radio.Item);
|
||||
|
||||
@@ -21,6 +21,10 @@ namespace Barotrauma.Networking
|
||||
|
||||
public override bool IsClient => true;
|
||||
public override bool IsServer => false;
|
||||
|
||||
#if DEBUG
|
||||
public float DebugServerVoipAmplitude;
|
||||
#endif
|
||||
|
||||
public override Voting Voting { get; }
|
||||
|
||||
@@ -112,6 +116,8 @@ namespace Barotrauma.Networking
|
||||
//has the client been given a character to control this round
|
||||
public bool HasSpawned;
|
||||
|
||||
public float EndRoundTimeRemaining { get; private set; }
|
||||
|
||||
public LocalizedString TraitorFirstObjective;
|
||||
public TraitorEventPrefab TraitorMission = null;
|
||||
|
||||
@@ -198,20 +204,6 @@ namespace Barotrauma.Networking
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
cameraFollowsSub = new GUITickBox(new RectTransform(new Vector2(0.05f, 0.05f), inGameHUD.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.CenterLeft)
|
||||
{
|
||||
AbsoluteOffset = new Point(0, HUDLayoutSettings.ButtonAreaTop.Y + HUDLayoutSettings.ButtonAreaTop.Height / 2),
|
||||
MaxSize = new Point(GUI.IntScale(25))
|
||||
}, TextManager.Get("CamFollowSubmarine"))
|
||||
{
|
||||
Selected = Camera.FollowSub,
|
||||
OnSelected = (tbox) =>
|
||||
{
|
||||
Camera.FollowSub = tbox.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
chatBox = new ChatBox(inGameHUD, isSinglePlayer: false);
|
||||
chatBox.OnEnterMessage += EnterChatMessage;
|
||||
chatBox.InputBox.OnTextChanged += TypingChatMessage;
|
||||
@@ -250,6 +242,19 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
};
|
||||
ShowLogButton.TextBlock.AutoScaleHorizontal = true;
|
||||
|
||||
cameraFollowsSub = new GUITickBox(new RectTransform(new Vector2(0.1f, 0.4f), buttonContainer.RectTransform)
|
||||
{
|
||||
MinSize = new Point(150, 0)
|
||||
}, TextManager.Get("CamFollowSubmarine"))
|
||||
{
|
||||
Selected = Camera.FollowSub,
|
||||
OnSelected = (tbox) =>
|
||||
{
|
||||
Camera.FollowSub = tbox.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GameMain.DebugDraw = false;
|
||||
Hull.EditFire = false;
|
||||
@@ -676,6 +681,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
VoipClient.Read(inc);
|
||||
break;
|
||||
#if DEBUG
|
||||
case ServerPacketHeader.VOICE_AMPLITUDE_DEBUG:
|
||||
GameMain.Client.DebugServerVoipAmplitude = inc.ReadRangedSingle(min: 0, max: 1, bitCount: 8);
|
||||
break;
|
||||
#endif
|
||||
case ServerPacketHeader.QUERY_STARTGAME:
|
||||
DebugConsole.Log("Received QUERY_STARTGAME packet.");
|
||||
string subName = inc.ReadString();
|
||||
@@ -1384,6 +1394,7 @@ namespace Barotrauma.Networking
|
||||
ServerSettings.AllowRewiring = inc.ReadBoolean();
|
||||
ServerSettings.AllowImmediateItemDelivery = inc.ReadBoolean();
|
||||
ServerSettings.AllowFriendlyFire = inc.ReadBoolean();
|
||||
ServerSettings.AllowDragAndDropGive = inc.ReadBoolean();
|
||||
ServerSettings.LockAllDefaultWires = inc.ReadBoolean();
|
||||
ServerSettings.AllowLinkingWifiToChat = inc.ReadBoolean();
|
||||
ServerSettings.MaximumMoneyTransferRequest = inc.ReadInt32();
|
||||
@@ -1814,6 +1825,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
GameStarted = inc.ReadBoolean();
|
||||
bool allowSpectating = inc.ReadBoolean();
|
||||
bool permadeathMode = inc.ReadBoolean();
|
||||
bool ironmanMode = inc.ReadBoolean();
|
||||
|
||||
ReadPermissions(inc);
|
||||
|
||||
@@ -1821,8 +1834,17 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (Screen.Selected != GameMain.GameScreen)
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("PleaseWait"), TextManager.Get(allowSpectating ? "RoundRunningSpectateEnabled" : "RoundRunningSpectateDisabled"));
|
||||
if (Screen.Selected is not ModDownloadScreen) { GameMain.NetLobbyScreen.Select(); }
|
||||
LocalizedString message;
|
||||
if (permadeathMode)
|
||||
{
|
||||
message = TextManager.Get(ironmanMode ? "RoundRunningIronman" : "RoundRunningPermadeath");
|
||||
}
|
||||
else
|
||||
{
|
||||
message = TextManager.Get(allowSpectating ? "RoundRunningSpectateEnabled" : "RoundRunningSpectateDisabled");
|
||||
}
|
||||
new GUIMessageBox(TextManager.Get("PleaseWait"), message);
|
||||
if (!(Screen.Selected is ModDownloadScreen)) { GameMain.NetLobbyScreen.Select(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2105,6 +2127,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
float sendingTime = inc.ReadSingle() - 0.0f;//TODO: reimplement inc.SenderConnection.RemoteTimeOffset;
|
||||
|
||||
EndRoundTimeRemaining = inc.ReadSingle();
|
||||
|
||||
SegmentTableReader<ServerNetSegment>.Read(inc,
|
||||
segmentDataReader: (segment, inc) =>
|
||||
{
|
||||
@@ -2375,7 +2399,16 @@ namespace Barotrauma.Networking
|
||||
WaitForNextRoundRespawn = waitForNextRoundRespawn;
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.WriteByte((byte)ClientPacketHeader.READY_TO_SPAWN);
|
||||
msg.WriteBoolean((bool)waitForNextRoundRespawn);
|
||||
msg.WriteBoolean(GameMain.NetLobbyScreen.Spectating);
|
||||
msg.WriteBoolean(waitForNextRoundRespawn);
|
||||
ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public void SendTakeOverBotRequest(CharacterInfo bot)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.WriteByte((byte)ClientPacketHeader.TAKEOVERBOT);
|
||||
msg.WriteUInt16(bot.ID);
|
||||
ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -2711,16 +2744,19 @@ namespace Barotrauma.Networking
|
||||
if (should != null && should.Value) { return; }
|
||||
|
||||
if (string.IsNullOrEmpty(message.Text)) { return; }
|
||||
if (message.Sender != null && !message.Sender.IsDead)
|
||||
if (message.SenderCharacter is { IsDead: false } sender)
|
||||
{
|
||||
if (message.Text.IsNullOrEmpty())
|
||||
{
|
||||
message.Sender.ShowTextlessSpeechBubble(2.0f, message.Color);
|
||||
|
||||
sender.ShowTextlessSpeechBubble(2.0f, message.Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
message.Sender.ShowSpeechBubble(message.Color, message.Text);
|
||||
sender.ShowSpeechBubble(message.Color, message.Text);
|
||||
if (!sender.IsBot)
|
||||
{
|
||||
sender.TextChatVolume = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameMain.NetLobbyScreen.NewChatMessage(message);
|
||||
@@ -3204,19 +3240,19 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
LocalizedString respawnText = string.Empty;
|
||||
Color textColor = Color.White;
|
||||
bool canChooseRespawn =
|
||||
GameMain.GameSession.GameMode is CampaignMode &&
|
||||
Character.Controlled == null &&
|
||||
Level.Loaded?.Type != LevelData.LevelType.Outpost &&
|
||||
(characterInfo == null || HasSpawned);
|
||||
bool hideRespawnButtons = false;
|
||||
|
||||
if (EndRoundTimeRemaining > 0)
|
||||
{
|
||||
respawnText = TextManager.GetWithVariable("endinground", "[time]", ToolBox.SecondsToReadableTime(EndRoundTimeRemaining))
|
||||
.Fallback(ToolBox.SecondsToReadableTime(EndRoundTimeRemaining), useDefaultLanguageIfFound: false);
|
||||
}
|
||||
if (RespawnManager.CurrentState == RespawnManager.State.Waiting)
|
||||
{
|
||||
if (RespawnManager.RespawnCountdownStarted)
|
||||
{
|
||||
float timeLeft = (float)(RespawnManager.RespawnTime - DateTime.Now).TotalSeconds;
|
||||
respawnText = TextManager.GetWithVariable(
|
||||
RespawnManager.UsingShuttle && !RespawnManager.ForceSpawnInMainSub ?
|
||||
"RespawnShuttleDispatching" : "RespawningIn", "[time]", ToolBox.SecondsToReadableTime(timeLeft));
|
||||
respawnText = TextManager.GetWithVariable("RespawningIn", "[time]", ToolBox.SecondsToReadableTime(timeLeft));
|
||||
}
|
||||
else if (RespawnManager.PendingRespawnCount > 0)
|
||||
{
|
||||
@@ -3239,12 +3275,12 @@ namespace Barotrauma.Networking
|
||||
//textScale = 1.0f + phase * 0.5f;
|
||||
textColor = Color.Lerp(GUIStyle.Red, Color.White, 1.0f - phase);
|
||||
}
|
||||
canChooseRespawn = false;
|
||||
hideRespawnButtons = true;
|
||||
}
|
||||
|
||||
GameMain.GameSession?.SetRespawnInfo(
|
||||
visible: !respawnText.IsNullOrEmpty() || canChooseRespawn, text: respawnText.Value, textColor: textColor,
|
||||
buttonsVisible: canChooseRespawn, waitForNextRoundRespawn: (WaitForNextRoundRespawn ?? true));
|
||||
GameMain.GameSession.SetRespawnInfo(
|
||||
text: respawnText.Value, textColor: textColor,
|
||||
waitForNextRoundRespawn: (WaitForNextRoundRespawn ?? true), hideButtons: hideRespawnButtons);
|
||||
}
|
||||
|
||||
if (!ShowNetStats) { return; }
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -23,6 +22,14 @@ namespace Barotrauma.Networking
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static void ShowDeathPromptIfNeeded(float delay = 1.0f)
|
||||
{
|
||||
if (UseDeathPrompt)
|
||||
{
|
||||
DeathPrompt.Create(delay);
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateTransportingProjSpecific(float deltaTime)
|
||||
{
|
||||
if (GameMain.Client?.Character == null || GameMain.Client.Character.Submarine != RespawnShuttle) { return; }
|
||||
@@ -37,72 +44,10 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private CoroutineHandle respawnPromptCoroutine;
|
||||
|
||||
public void ShowRespawnPromptIfNeeded(float delay = 5.0f)
|
||||
{
|
||||
if (!UseRespawnPrompt) { return; }
|
||||
if (CoroutineManager.IsCoroutineRunning(respawnPromptCoroutine) || GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "respawnquestionprompt"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
respawnPromptCoroutine = CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (Character.Controlled != null || (GameMain.GameSession is not { IsRunning: true })) { return; }
|
||||
|
||||
LocalizedString text;
|
||||
GUIMessageBox respawnPrompt;
|
||||
if (SkillLossPercentageOnImmediateRespawn > 0)
|
||||
{
|
||||
// Respawn asap with extra skill loss?
|
||||
text = TextManager.GetWithVariable("respawnquestionprompt", "[percentage]", ((int)Math.Round(SkillLossPercentageOnImmediateRespawn)).ToString());
|
||||
respawnPrompt = new GUIMessageBox(
|
||||
TextManager.Get("tutorial.tryagainheader"), text,
|
||||
new LocalizedString[] { TextManager.Get("respawnquestionpromptrespawn"), TextManager.Get("respawnquestionpromptwait") })
|
||||
{
|
||||
UserData = "respawnquestionprompt"
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Respawn asap?
|
||||
text = TextManager.Get("respawnquestionpromptnoloss");
|
||||
respawnPrompt = new GUIMessageBox(
|
||||
TextManager.Get("tutorial.tryagainheader"), text,
|
||||
new LocalizedString[] { TextManager.Get("respawnquestionpromptrespawnnoloss"), TextManager.Get("respawnquestionpromptwait") })
|
||||
{
|
||||
UserData = "respawnquestionprompt"
|
||||
};
|
||||
}
|
||||
if (SkillLossPercentageOnDeath > 0)
|
||||
{
|
||||
// You have died... etc added BEFORE the above text
|
||||
text =
|
||||
TextManager.GetWithVariable("respawnskillpenalty", "[percentage]", ((int)SkillLossPercentageOnDeath).ToString()) +
|
||||
"\n\n" + text;
|
||||
};
|
||||
|
||||
respawnPrompt.Buttons[0].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: false);
|
||||
respawnPrompt.Close();
|
||||
return true;
|
||||
};
|
||||
respawnPrompt.Buttons[1].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: true);
|
||||
respawnPrompt.Close();
|
||||
return true;
|
||||
};
|
||||
}, delay: delay);
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool respawnPromptPending = false;
|
||||
var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);
|
||||
ForceSpawnInMainSub = false;
|
||||
switch (newState)
|
||||
{
|
||||
case State.Transporting:
|
||||
@@ -122,7 +67,6 @@ namespace Barotrauma.Networking
|
||||
RequiredRespawnCount = msg.ReadUInt16();
|
||||
respawnPromptPending = msg.ReadBoolean();
|
||||
RespawnCountdownStarted = msg.ReadBoolean();
|
||||
ForceSpawnInMainSub = msg.ReadBoolean();
|
||||
ResetShuttle();
|
||||
float newRespawnTime = msg.ReadSingle();
|
||||
RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(newRespawnTime * 1000.0f));
|
||||
@@ -136,7 +80,7 @@ namespace Barotrauma.Networking
|
||||
if (respawnPromptPending)
|
||||
{
|
||||
GameMain.Client.HasSpawned = true;
|
||||
ShowRespawnPromptIfNeeded(delay: 1.0f);
|
||||
DeathPrompt.Create(delay: 1.0f);
|
||||
}
|
||||
|
||||
msg.ReadPadBits();
|
||||
|
||||
@@ -468,11 +468,28 @@ namespace Barotrauma.Networking
|
||||
};
|
||||
AssignGUIComponent(nameof(SaveServerLogs), saveLogsBox);
|
||||
|
||||
LocalizedString newCampaignDefaultSalaryLabel = TextManager.Get("ServerSettingsNewCampaignDefaultSalary");
|
||||
NetLobbyScreen.CreateLabeledSlider(listBox.Content, headerTag: "ServerSettingsNewCampaignDefaultSalary", valueLabelTag: "ServerSettingsKickVotesRequired", tooltipTag: "ServerSettingsNewCampaignDefaultSalaryToolTip",
|
||||
out var defaultSalarySlider, out var defaultSalarySliderLabel);
|
||||
defaultSalarySlider.Range = new Vector2(0, 100);
|
||||
defaultSalarySlider.StepValue = 1;
|
||||
defaultSalarySlider.OnMoved = (scrollBar, _) =>
|
||||
{
|
||||
if (scrollBar.UserData is not GUITextBlock text) { return false; }
|
||||
text.Text = TextManager.AddPunctuation(
|
||||
':',
|
||||
newCampaignDefaultSalaryLabel,
|
||||
TextManager.GetWithVariable("percentageformat", "[value]", ((int)Math.Round(scrollBar.BarScrollValue, digits: 0)).ToString()));
|
||||
return true;
|
||||
};
|
||||
AssignGUIComponent(nameof(NewCampaignDefaultSalary), defaultSalarySlider);
|
||||
defaultSalarySlider.OnMoved(defaultSalarySlider, defaultSalarySlider.BarScroll);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// game settings
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
GUILayoutGroup buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), listBox.Content.RectTransform), isHorizontal: true)
|
||||
GUILayoutGroup buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), listBox.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.BottomLeft)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
@@ -683,7 +700,7 @@ namespace Barotrauma.Networking
|
||||
// antigriefing
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
var tickBoxContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.25f), listBox.Content.RectTransform))
|
||||
var tickBoxContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.268f), listBox.Content.RectTransform))
|
||||
{
|
||||
AutoHideScrollBar = true,
|
||||
UseGridLayout = true
|
||||
@@ -693,6 +710,10 @@ namespace Barotrauma.Networking
|
||||
var allowFriendlyFire = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowFriendlyFire"));
|
||||
AssignGUIComponent(nameof(AllowFriendlyFire), allowFriendlyFire);
|
||||
|
||||
var allowDragAndDropGive = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowDragAndDropGive"));
|
||||
AssignGUIComponent(nameof(AllowDragAndDropGive), allowDragAndDropGive);
|
||||
|
||||
var killableNPCs = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsKillableNPCs"));
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
using Concentus.Enums;
|
||||
using Concentus.Structs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
static partial class VoipConfig
|
||||
{
|
||||
public const int FREQUENCY = 48000; //48Khz
|
||||
public const int BITRATE = 16000; //16Kbps
|
||||
public const int BUFFER_SIZE = (8 * MAX_COMPRESSED_SIZE * FREQUENCY) / BITRATE; //20ms window
|
||||
|
||||
public static OpusEncoder CreateEncoder()
|
||||
{
|
||||
var encoder = new OpusEncoder(FREQUENCY, 1, OpusApplication.OPUS_APPLICATION_VOIP);
|
||||
@@ -22,10 +13,5 @@ namespace Barotrauma.Networking
|
||||
encoder.SignalType = OpusSignal.OPUS_SIGNAL_VOICE;
|
||||
return encoder;
|
||||
}
|
||||
|
||||
public static OpusDecoder CreateDecoder()
|
||||
{
|
||||
return new OpusDecoder(FREQUENCY, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,8 +228,11 @@ namespace Barotrauma
|
||||
static MouseState latestMouseState; //the absolute latest state, do NOT use for player interaction
|
||||
static KeyboardState keyboardState, oldKeyboardState;
|
||||
|
||||
static double timeSinceClick;
|
||||
static Point lastClickPosition;
|
||||
static double timeSincePrimaryClick;
|
||||
static Point lastPrimaryClickPosition;
|
||||
|
||||
static double timeSinceSecondaryClick;
|
||||
static Point lastSecondaryClickPosition;
|
||||
|
||||
const float DoubleClickDelay = 0.4f;
|
||||
public static float MaxDoubleClickDistance
|
||||
@@ -237,7 +240,8 @@ namespace Barotrauma
|
||||
get { return Math.Max(15.0f * Math.Max(GameMain.GraphicsHeight / 1920.0f, GameMain.GraphicsHeight / 1080.0f), 10.0f); }
|
||||
}
|
||||
|
||||
static bool doubleClicked;
|
||||
static bool primaryDoubleClicked;
|
||||
static bool secondaryDoubleClicked;
|
||||
|
||||
static bool allowInput;
|
||||
static bool wasWindowActive;
|
||||
@@ -406,7 +410,12 @@ namespace Barotrauma
|
||||
|
||||
public static bool DoubleClicked()
|
||||
{
|
||||
return AllowInput && doubleClicked;
|
||||
return AllowInput && primaryDoubleClicked;
|
||||
}
|
||||
|
||||
public static bool SecondaryDoubleClicked()
|
||||
{
|
||||
return AllowInput && secondaryDoubleClicked;
|
||||
}
|
||||
|
||||
public static bool KeyHit(InputType inputType)
|
||||
@@ -466,7 +475,8 @@ namespace Barotrauma
|
||||
|
||||
public static void Update(double deltaTime)
|
||||
{
|
||||
timeSinceClick += deltaTime;
|
||||
timeSincePrimaryClick += deltaTime;
|
||||
timeSinceSecondaryClick += deltaTime;
|
||||
|
||||
if (!GameMain.WindowActive)
|
||||
{
|
||||
@@ -497,11 +507,33 @@ namespace Barotrauma
|
||||
MouseSpeedPerSecond = MouseSpeed / (float)deltaTime;
|
||||
|
||||
// Split into two to not accept drag & drop releasing as part of a double-click
|
||||
doubleClicked = false;
|
||||
primaryDoubleClicked = false;
|
||||
if (PrimaryMouseButtonClicked())
|
||||
{
|
||||
float dist = (mouseState.Position - lastClickPosition).ToVector2().Length();
|
||||
primaryDoubleClicked = UpdateDoubleClicking(ref lastPrimaryClickPosition, ref timeSincePrimaryClick);
|
||||
}
|
||||
|
||||
if (PrimaryMouseButtonDown())
|
||||
{
|
||||
lastPrimaryClickPosition = mouseState.Position;
|
||||
}
|
||||
|
||||
secondaryDoubleClicked = false;
|
||||
if (SecondaryMouseButtonClicked())
|
||||
{
|
||||
secondaryDoubleClicked = UpdateDoubleClicking(ref lastSecondaryClickPosition, ref timeSinceSecondaryClick);
|
||||
}
|
||||
|
||||
if (SecondaryMouseButtonDown())
|
||||
{
|
||||
lastSecondaryClickPosition = mouseState.Position;
|
||||
}
|
||||
|
||||
bool UpdateDoubleClicking(ref Point lastClickPosition, ref double timeSinceClick)
|
||||
{
|
||||
bool doubleClicked = false;
|
||||
float dist = (mouseState.Position - lastClickPosition).ToVector2().Length();
|
||||
|
||||
if (timeSinceClick < DoubleClickDelay && dist < MaxDoubleClickDistance)
|
||||
{
|
||||
doubleClicked = true;
|
||||
@@ -515,11 +547,8 @@ namespace Barotrauma
|
||||
{
|
||||
timeSinceClick = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (PrimaryMouseButtonDown())
|
||||
{
|
||||
lastClickPosition = mouseState.Position;
|
||||
|
||||
return doubleClicked;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -781,7 +781,7 @@ namespace Barotrauma.CharacterEditor
|
||||
// Lightmaps
|
||||
if (GameMain.LightManager.LightingEnabled && Character.Controlled != null)
|
||||
{
|
||||
GameMain.LightManager.ObstructVision = Character.Controlled.ObstructVision;
|
||||
GameMain.LightManager.ObstructVisionAmount = Character.Controlled.ObstructVisionAmount;
|
||||
GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam);
|
||||
GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Lights;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
@@ -178,10 +178,15 @@ namespace Barotrauma
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
GameMain.LightManager.ObstructVision =
|
||||
Character.Controlled != null &&
|
||||
Character.Controlled.ObstructVision &&
|
||||
(Character.Controlled.ViewTarget == Character.Controlled || Character.Controlled.ViewTarget == null);
|
||||
if (Character.Controlled != null &&
|
||||
(Character.Controlled.ViewTarget == Character.Controlled || Character.Controlled.ViewTarget == null))
|
||||
{
|
||||
GameMain.LightManager.ObstructVisionAmount = Character.Controlled.ObstructVisionAmount;
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.LightManager.ObstructVisionAmount = 0.0f;
|
||||
}
|
||||
|
||||
GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled?.CursorWorldPosition ?? Vector2.Zero);
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Barotrauma
|
||||
currentLevelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
|
||||
editorContainer.ClearChildren();
|
||||
SortLevelObjectsList(currentLevelData);
|
||||
new SerializableEntityEditor(editorContainer.Content.RectTransform, selectedParams, false, true, elementHeight: 20);
|
||||
new SerializableEntityEditor(editorContainer.Content.RectTransform, selectedParams, inGame: false, showName: true, elementHeight: 20, titleFont: GUIStyle.LargeFont);
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -996,7 +996,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item == null || item.HiddenInGame) { continue; }
|
||||
if (item == null || item.IsHidden) { continue; }
|
||||
foreach (var light in item.GetComponents<Items.Components.LightComponent>())
|
||||
{
|
||||
light.Update((float)deltaTime, Cam);
|
||||
|
||||
@@ -63,6 +63,9 @@ namespace Barotrauma
|
||||
private GUITickBox spectateBox;
|
||||
public bool Spectating => spectateBox is { Selected: true, Visible: true };
|
||||
|
||||
public bool PermadeathMode => GameMain.Client?.ServerSettings?.RespawnMode == RespawnMode.Permadeath;
|
||||
public bool PermanentlyDead => campaignCharacterInfo?.PermanentlyDead ?? false;
|
||||
|
||||
private GUILayoutGroup playerInfoContent;
|
||||
private GUIComponent changesPendingText;
|
||||
private bool createPendingChangesText = true;
|
||||
@@ -87,7 +90,14 @@ namespace Barotrauma
|
||||
private GUIFrame characterInfoFrame;
|
||||
private GUIFrame appearanceFrame;
|
||||
|
||||
private readonly List<GUIComponent> respawnSettingsElements = new List<GUIComponent>();
|
||||
private GUISelectionCarousel<RespawnMode> respawnModeSelection;
|
||||
private GUITextBlock respawnModeLabel;
|
||||
private GUIComponent respawnIntervalElement;
|
||||
|
||||
private readonly List<GUIComponent> midRoundRespawnSettings = new List<GUIComponent>();
|
||||
private readonly List<GUIComponent> permadeathEnabledRespawnSettings = new List<GUIComponent>();
|
||||
private readonly List<GUIComponent> permadeathDisabledRespawnSettings = new List<GUIComponent>();
|
||||
private readonly List<GUIComponent> ironmanDisabledRespawnSettings = new List<GUIComponent>();
|
||||
private readonly List<GUIComponent> campaignDisabledElements = new List<GUIComponent>();
|
||||
|
||||
public CharacterInfo.AppearanceCustomizationMenu CharacterAppearanceCustomizationMenu { get; set; }
|
||||
@@ -191,7 +201,7 @@ namespace Barotrauma
|
||||
|
||||
public bool UsingShuttle
|
||||
{
|
||||
get { return shuttleTickBox.Selected; }
|
||||
get { return shuttleTickBox.Selected && !PermadeathMode; }
|
||||
set { shuttleTickBox.Selected = value; }
|
||||
}
|
||||
|
||||
@@ -955,19 +965,17 @@ namespace Barotrauma
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
var respawnBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), settingsContent.RectTransform) { AbsoluteOffset = new Point((int)respawnSettingsHeader.Padding.X, 0) },
|
||||
TextManager.Get("ServerSettingsAllowRespawning"))
|
||||
var respawnModeHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), settingsContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
respawnModeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.0f), respawnModeHolder.RectTransform), TextManager.Get("RespawnMode"), wrap: true);
|
||||
respawnModeSelection = new GUISelectionCarousel<RespawnMode>(new RectTransform(new Vector2(0.6f, 1.0f), respawnModeHolder.RectTransform));
|
||||
foreach (var respawnMode in Enum.GetValues(typeof(RespawnMode)).Cast<RespawnMode>())
|
||||
{
|
||||
ToolTip = TextManager.Get("RespawnExplanation"),
|
||||
OnSelected = (tickbox) =>
|
||||
{
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Properties);
|
||||
RefreshEnabledElements();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
AssignComponentToServerSetting(respawnBox, nameof(ServerSettings.AllowRespawn));
|
||||
clientDisabledElements.Add(respawnBox);
|
||||
respawnModeSelection.AddElement(respawnMode, TextManager.Get($"respawnmode.{respawnMode}"), TextManager.Get($"respawnmode.{respawnMode}.tooltip"));
|
||||
}
|
||||
|
||||
respawnModeSelection.ElementSelectionCondition += (value) => value != RespawnMode.Permadeath || SelectedMode == GameModePreset.MultiPlayerCampaign;
|
||||
respawnModeSelection.OnValueChanged += (_) => GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Properties);
|
||||
AssignComponentToServerSetting(respawnModeSelection, nameof(ServerSettings.RespawnMode));
|
||||
|
||||
GUILayoutGroup shuttleHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), settingsContent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
@@ -977,7 +985,7 @@ namespace Barotrauma
|
||||
shuttleTickBox = new GUITickBox(new RectTransform(Vector2.One, shuttleHolder.RectTransform), TextManager.Get("RespawnShuttle"))
|
||||
{
|
||||
ToolTip = TextManager.Get("RespawnShuttleExplanation"),
|
||||
Selected = true,
|
||||
Selected = !PermadeathMode,
|
||||
OnSelected = (GUITickBox box) =>
|
||||
{
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Properties);
|
||||
@@ -985,7 +993,7 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
AssignComponentToServerSetting(shuttleTickBox, nameof(ServerSettings.UseRespawnShuttle));
|
||||
respawnSettingsElements.Add(shuttleTickBox);
|
||||
midRoundRespawnSettings.Add(shuttleTickBox);
|
||||
|
||||
shuttleTickBox.TextBlock.RectTransform.SizeChanged += () =>
|
||||
{
|
||||
@@ -1008,9 +1016,9 @@ namespace Barotrauma
|
||||
};
|
||||
ShuttleList.ListBox.RectTransform.MinSize = new Point(250, 0);
|
||||
shuttleHolder.RectTransform.MinSize = new Point(0, ShuttleList.RectTransform.Children.Max(c => c.MinSize.Y));
|
||||
respawnSettingsElements.Add(ShuttleList);
|
||||
midRoundRespawnSettings.Add(ShuttleList);
|
||||
|
||||
var respawnIntervalElement = CreateLabeledSlider(settingsContent, "ServerSettingsRespawnInterval", "", "", out var respawnIntervalSlider, out var respawnIntervalSliderLabel,
|
||||
respawnIntervalElement = CreateLabeledSlider(settingsContent, "ServerSettingsRespawnInterval", "", "", out var respawnIntervalSlider, out var respawnIntervalSliderLabel,
|
||||
range: new Vector2(10.0f, 600.0f));
|
||||
LocalizedString intervalLabel = respawnIntervalSliderLabel.Text;
|
||||
respawnIntervalSlider.StepValue = 10.0f;
|
||||
@@ -1026,7 +1034,6 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
respawnIntervalSlider.OnMoved(respawnIntervalSlider, respawnIntervalSlider.BarScroll);
|
||||
respawnSettingsElements.AddRange(respawnIntervalElement.GetAllChildren());
|
||||
AssignComponentToServerSetting(respawnIntervalSlider, nameof(ServerSettings.RespawnInterval));
|
||||
|
||||
var minRespawnElement = CreateLabeledSlider(settingsContent, "ServerSettingsMinRespawn", "", "ServerSettingsMinRespawnToolTip", out var minRespawnSlider, out var minRespawnSliderLabel,
|
||||
@@ -1043,7 +1050,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
minRespawnSlider.OnMoved(minRespawnSlider, minRespawnSlider.BarScroll);
|
||||
respawnSettingsElements.AddRange(minRespawnElement.GetAllChildren());
|
||||
midRoundRespawnSettings.AddRange(minRespawnElement.GetAllChildren());
|
||||
AssignComponentToServerSetting(minRespawnSlider, nameof(ServerSettings.MinRespawnRatio));
|
||||
|
||||
var respawnDurationElement = CreateLabeledSlider(settingsContent, "ServerSettingsRespawnDuration", "", "ServerSettingsRespawnDurationTooltip", out var respawnDurationSlider, out var respawnDurationSliderLabel,
|
||||
@@ -1068,7 +1075,7 @@ namespace Barotrauma
|
||||
return value <= 0.0f ? 1.0f : (value - scrollBar.Range.X) / (scrollBar.Range.Y - scrollBar.Range.X);
|
||||
};
|
||||
respawnDurationSlider.OnMoved(respawnDurationSlider, respawnDurationSlider.BarScroll);
|
||||
respawnSettingsElements.AddRange(respawnDurationElement.GetAllChildren());
|
||||
midRoundRespawnSettings.AddRange(respawnDurationElement.GetAllChildren());
|
||||
AssignComponentToServerSetting(respawnDurationSlider, nameof(ServerSettings.MaxTransportTime));
|
||||
|
||||
var skillLossElement = CreateLabeledSlider(settingsContent, "ServerSettingsSkillLossPercentageOnDeath", "", "ServerSettingsSkillLossPercentageOnDeathToolTip",
|
||||
@@ -1085,7 +1092,8 @@ namespace Barotrauma
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Properties);
|
||||
return true;
|
||||
};
|
||||
respawnSettingsElements.AddRange(skillLossElement.GetAllChildren());
|
||||
permadeathDisabledRespawnSettings.AddRange(skillLossElement.GetAllChildren());
|
||||
clientDisabledElements.AddRange(skillLossElement.GetAllChildren());
|
||||
AssignComponentToServerSetting(skillLossSlider, nameof(ServerSettings.SkillLossPercentageOnDeath));
|
||||
skillLossSlider.OnMoved(skillLossSlider, skillLossSlider.BarScroll);
|
||||
|
||||
@@ -1103,11 +1111,41 @@ namespace Barotrauma
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Properties);
|
||||
return true;
|
||||
};
|
||||
respawnSettingsElements.AddRange(skillLossImmediateRespawnElement.GetAllChildren());
|
||||
midRoundRespawnSettings.AddRange(skillLossImmediateRespawnElement.GetAllChildren());
|
||||
permadeathDisabledRespawnSettings.AddRange(skillLossImmediateRespawnElement.GetAllChildren());
|
||||
AssignComponentToServerSetting(skillLossImmediateRespawnSlider, nameof(ServerSettings.SkillLossPercentageOnImmediateRespawn));
|
||||
skillLossImmediateRespawnSlider.OnMoved(skillLossImmediateRespawnSlider, skillLossImmediateRespawnSlider.BarScroll);
|
||||
|
||||
foreach (var respawnElement in respawnSettingsElements)
|
||||
var allowBotTakeoverTickbox = new GUITickBox(new RectTransform(Vector2.One, settingsContent.RectTransform), TextManager.Get("AllowBotTakeover"))
|
||||
{
|
||||
ToolTip = TextManager.Get("AllowBotTakeover.Tooltip"),
|
||||
Selected = GameMain.Client != null && GameMain.Client.ServerSettings.AllowBotTakeoverOnPermadeath,
|
||||
OnSelected = (GUITickBox box) =>
|
||||
{
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Properties);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
AssignComponentToServerSetting(allowBotTakeoverTickbox, nameof(ServerSettings.AllowBotTakeoverOnPermadeath));
|
||||
permadeathEnabledRespawnSettings.Add(allowBotTakeoverTickbox);
|
||||
ironmanDisabledRespawnSettings.Add(allowBotTakeoverTickbox);
|
||||
clientDisabledElements.Add(allowBotTakeoverTickbox);
|
||||
|
||||
var ironmanTickbox = new GUITickBox(new RectTransform(Vector2.One, settingsContent.RectTransform), TextManager.Get("IronmanMode").ToUpper())
|
||||
{
|
||||
ToolTip = TextManager.Get("IronmanMode.Tooltip"),
|
||||
Selected = GameMain.Client != null && GameMain.Client.ServerSettings.IronmanMode,
|
||||
OnSelected = (GUITickBox box) =>
|
||||
{
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Properties);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
AssignComponentToServerSetting(ironmanTickbox, nameof(ServerSettings.IronmanMode));
|
||||
permadeathEnabledRespawnSettings.Add(ironmanTickbox);
|
||||
clientDisabledElements.Add(ironmanTickbox);
|
||||
|
||||
foreach (var respawnElement in midRoundRespawnSettings)
|
||||
{
|
||||
if (!clientDisabledElements.Contains(respawnElement))
|
||||
{
|
||||
@@ -1650,19 +1688,31 @@ namespace Barotrauma
|
||||
bool campaignStarted = CampaignFrame.Visible;
|
||||
bool gameStarted = client != null && client.GameStarted;
|
||||
|
||||
//disable elements the client doesn't have access to
|
||||
// First, enable or disable elements based on client permissions
|
||||
foreach (var element in clientDisabledElements)
|
||||
{
|
||||
element.Enabled = manageSettings;
|
||||
}
|
||||
|
||||
// Then disable elements depending on other conditions
|
||||
traitorElements.ForEach(e => e.Enabled &= settings.TraitorProbability > 0);
|
||||
SetTraitorDangerIndicators(settings.TraitorDangerLevel);
|
||||
respawnSettingsElements.ForEach(e => e.Enabled &= settings.AllowRespawn);
|
||||
respawnModeSelection.Enabled = respawnModeLabel.Enabled = manageSettings && !gameStarted;
|
||||
midRoundRespawnSettings.ForEach(e => e.Enabled &= settings.RespawnMode == RespawnMode.MidRound);
|
||||
permadeathDisabledRespawnSettings.ForEach(e => e.Enabled &= settings.RespawnMode != RespawnMode.Permadeath);
|
||||
permadeathEnabledRespawnSettings.ForEach(e => e.Enabled &= settings.RespawnMode == RespawnMode.Permadeath && !gameStarted);
|
||||
ironmanDisabledRespawnSettings.ForEach(e => e.Enabled &= !settings.IronmanMode);
|
||||
|
||||
// The respawn interval is used even if the shuttle is not
|
||||
respawnIntervalElement.GetAllChildren().ForEach(e => e.Enabled = settings.RespawnMode != RespawnMode.BetweenRounds && manageSettings);
|
||||
|
||||
//go through the individual elements that are only enabled in a specific context
|
||||
shuttleTickBox.Enabled &= !gameStarted;
|
||||
if (ShuttleList != null)
|
||||
{
|
||||
ShuttleList.Enabled = ShuttleList.ButtonEnabled = HasPermission(ClientPermissions.SelectSub) && !gameStarted && settings.AllowRespawn;
|
||||
// Shuttle list depends on shuttle tickbox
|
||||
ShuttleList.Enabled &= shuttleTickBox.Enabled && HasPermission(ClientPermissions.SelectSub);
|
||||
ShuttleList.ButtonEnabled = ShuttleList.Enabled;
|
||||
}
|
||||
if (SubList != null)
|
||||
{
|
||||
@@ -1672,7 +1722,6 @@ namespace Barotrauma
|
||||
{
|
||||
ModeList.Enabled = !gameStarted && (settings.AllowModeVoting || HasPermission(ClientPermissions.SelectMode));
|
||||
}
|
||||
shuttleTickBox.Enabled &= !gameStarted;
|
||||
|
||||
RefreshStartButtonVisibility();
|
||||
|
||||
@@ -1750,6 +1799,10 @@ namespace Barotrauma
|
||||
private void UpdatePlayerFrame(CharacterInfo characterInfo, bool allowEditing, GUIComponent parent, bool createPendingText = true)
|
||||
{
|
||||
if (GameMain.Client == null) { return; }
|
||||
|
||||
// When permanently dead and still characterless, spectating is the only option
|
||||
spectateBox.Enabled = !PermanentlyDead;
|
||||
|
||||
createPendingChangesText = createPendingText;
|
||||
if (characterInfo == null || CampaignCharacterDiscarded)
|
||||
{
|
||||
@@ -1780,41 +1833,57 @@ namespace Barotrauma
|
||||
MaxTextLength = Client.MaxNameLength,
|
||||
OverflowClip = true
|
||||
};
|
||||
|
||||
CharacterNameBox.OnEnterPressed += (tb, text) => { CharacterNameBox.Deselect(); return true; };
|
||||
CharacterNameBox.OnDeselected += (tb, key) =>
|
||||
|
||||
if (PermanentlyDead)
|
||||
{
|
||||
if (GameMain.Client == null) { return; }
|
||||
string newName = Client.SanitizeName(tb.Text);
|
||||
if (newName == GameMain.Client.Name) return;
|
||||
if (string.IsNullOrWhiteSpace(newName))
|
||||
CharacterNameBox.Readonly = true;
|
||||
CharacterNameBox.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterNameBox.OnEnterPressed += (tb, text) =>
|
||||
{
|
||||
tb.Text = GameMain.Client.Name;
|
||||
}
|
||||
else
|
||||
CharacterNameBox.Deselect();
|
||||
return true;
|
||||
};
|
||||
CharacterNameBox.OnDeselected += (tb, key) =>
|
||||
{
|
||||
if (isGameRunning)
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
GameMain.Client.PendingName = tb.Text;
|
||||
TabMenu.PendingChanges = true;
|
||||
if (createPendingText)
|
||||
{
|
||||
CreateChangesPendingText();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
string newName = Client.SanitizeName(tb.Text);
|
||||
if (newName == GameMain.Client.Name) { return; }
|
||||
if (string.IsNullOrWhiteSpace(newName))
|
||||
{
|
||||
tb.Text = GameMain.Client.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadyToStartBox.Selected = false;
|
||||
if (isGameRunning)
|
||||
{
|
||||
GameMain.Client.PendingName = tb.Text;
|
||||
TabMenu.PendingChanges = true;
|
||||
if (createPendingText)
|
||||
{
|
||||
CreateChangesPendingText();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadyToStartBox.Selected = false;
|
||||
}
|
||||
|
||||
GameMain.Client.SetName(tb.Text);
|
||||
}
|
||||
|
||||
GameMain.Client.SetName(tb.Text);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.006f), parent.RectTransform), style: null);
|
||||
|
||||
if (allowEditing)
|
||||
if (allowEditing && (!PermadeathMode || !isGameRunning))
|
||||
{
|
||||
GUILayoutGroup characterInfoTabs = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), parent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
@@ -1892,37 +1961,70 @@ namespace Barotrauma
|
||||
{
|
||||
characterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.16f), parent.RectTransform, Anchor.TopCenter));
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform), characterInfo.Job.Name, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont, wrap: true)
|
||||
if (PermanentlyDead)
|
||||
{
|
||||
HoverColor = Color.Transparent,
|
||||
SelectedColor = Color.Transparent
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform), TextManager.Get("Skills"), font: GUIStyle.SubHeadingFont);
|
||||
foreach (Skill skill in characterInfo.Job.GetSkills())
|
||||
new GUITextBlock(
|
||||
new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform),
|
||||
TextManager.Get("deceased"),
|
||||
textAlignment: Alignment.Center, font: GUIStyle.LargeFont);
|
||||
|
||||
if (GameMain.Client?.ServerSettings is { IronmanMode: true })
|
||||
{
|
||||
new GUITextBlock(
|
||||
new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform),
|
||||
TextManager.Get("lobby.ironmaninfo"),
|
||||
textAlignment: Alignment.Center, wrap: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUITextBlock(
|
||||
new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform),
|
||||
TextManager.Get("lobby.permadeathinfo"),
|
||||
textAlignment: Alignment.Center, wrap: true);
|
||||
new GUITextBlock(
|
||||
new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform),
|
||||
TextManager.Get("lobby.permadeathoptionsexplanation"),
|
||||
textAlignment: Alignment.Center, wrap: true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Color textColor = Color.White * (0.5f + skill.Level / 200.0f);
|
||||
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform),
|
||||
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + skill.Identifier), ((int)skill.Level).ToString()),
|
||||
textColor,
|
||||
font: GUIStyle.SmallFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform), characterInfo.Job.Name, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont, wrap: true)
|
||||
{
|
||||
HoverColor = Color.Transparent,
|
||||
SelectedColor = Color.Transparent
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform), TextManager.Get("Skills"), font: GUIStyle.SubHeadingFont);
|
||||
foreach (Skill skill in characterInfo.Job.GetSkills())
|
||||
{
|
||||
Color textColor = Color.White * (0.5f + skill.Level / 200.0f);
|
||||
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform),
|
||||
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + skill.Identifier), ((int)skill.Level).ToString()),
|
||||
textColor,
|
||||
font: GUIStyle.SmallFont);
|
||||
}
|
||||
}
|
||||
|
||||
// Spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), parent.RectTransform), style: null);
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.8f, 0.1f), parent.RectTransform, Anchor.BottomCenter), TextManager.Get("CreateNew"))
|
||||
if (GameMain.Client?.ServerSettings?.RespawnMode != RespawnMode.Permadeath)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
OnClicked = (btn, userdata) =>
|
||||
// Button to create new character
|
||||
new GUIButton(new RectTransform(new Vector2(0.8f, 0.1f), parent.RectTransform, Anchor.BottomCenter), TextManager.Get("CreateNew"))
|
||||
{
|
||||
TryDiscardCampaignCharacter(() =>
|
||||
IgnoreLayoutGroups = true,
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
UpdatePlayerFrame(null, true, parent);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
TryDiscardCampaignCharacter(() =>
|
||||
{
|
||||
UpdatePlayerFrame(null, true, parent);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
TeamPreferenceListBox = null;
|
||||
@@ -2095,14 +2197,20 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.Client == null) { return; }
|
||||
spectateBox.Selected = spectate;
|
||||
|
||||
if (spectate)
|
||||
{
|
||||
playerInfoContent.ClearChildren();
|
||||
|
||||
GameMain.Client.CharacterInfo?.Remove();
|
||||
GameMain.Client.CharacterInfo = null;
|
||||
GameMain.Client.Character?.Remove();
|
||||
GameMain.Client.Character = null;
|
||||
// TODO: The following lines are ancient, unexplained, and they cause a client spectating because of permadeath
|
||||
// to get kicked from the server at round transition because the server expects to be in control of
|
||||
// removing Characters and the client to still have one. Commenting these lines out for now, but
|
||||
// if no side-effects occur, they can just be deleted.
|
||||
//GameMain.Client.Character?.Remove();
|
||||
//GameMain.Client.Character = null;
|
||||
|
||||
playerInfoContent.ClearChildren();
|
||||
|
||||
new GUITextBlock(new RectTransform(Vector2.One, playerInfoContent.RectTransform, Anchor.Center),
|
||||
TextManager.Get("PlayingAsSpectator"),
|
||||
textAlignment: Alignment.Center);
|
||||
@@ -2118,6 +2226,10 @@ namespace Barotrauma
|
||||
// Server owner is allowed to spectate regardless of the server settings
|
||||
if (GameMain.Client != null && GameMain.Client.IsServerOwner) { return; }
|
||||
|
||||
// A client whose character has faced permadeath and hasn't chosen a new
|
||||
// character yet has no choice but to spectate
|
||||
if (campaignCharacterInfo != null && campaignCharacterInfo.PermanentlyDead) { return; }
|
||||
|
||||
// Show the player config menu if spectating is not allowed
|
||||
if (spectateBox.Selected && !allowSpectating) { spectateBox.Selected = false; }
|
||||
|
||||
@@ -3609,6 +3721,7 @@ namespace Barotrauma
|
||||
GameMain.GameSession = null;
|
||||
}
|
||||
|
||||
respawnModeSelection.Refresh(); // not all respawn modes are compatible with all game modes
|
||||
RefreshGameModeContent();
|
||||
RefreshEnabledElements();
|
||||
}
|
||||
|
||||
@@ -27,38 +27,8 @@ namespace Barotrauma
|
||||
get => Submarine.MainSub;
|
||||
set => Submarine.MainSub = value;
|
||||
}
|
||||
|
||||
private enum LayerVisibility
|
||||
{
|
||||
Visible,
|
||||
Invisible
|
||||
}
|
||||
|
||||
private enum LayerLinkage
|
||||
{
|
||||
Unlinked,
|
||||
Linked
|
||||
}
|
||||
|
||||
private readonly struct LayerData
|
||||
{
|
||||
public readonly LayerVisibility Visible;
|
||||
public readonly LayerLinkage Linkage;
|
||||
|
||||
public static readonly LayerData Default = new LayerData(LayerVisibility.Visible, LayerLinkage.Unlinked);
|
||||
|
||||
public LayerData(LayerVisibility visible, LayerLinkage linkage)
|
||||
{
|
||||
Visible = visible;
|
||||
Linkage = linkage;
|
||||
}
|
||||
|
||||
public void Deconstruct(out LayerVisibility isvisible, out LayerLinkage islinked)
|
||||
{
|
||||
isvisible = Visible;
|
||||
islinked = Linkage;
|
||||
}
|
||||
}
|
||||
private readonly record struct LayerData(bool IsVisible = true, bool IsGrouped = false);
|
||||
|
||||
public enum Mode
|
||||
{
|
||||
@@ -1105,12 +1075,22 @@ namespace Barotrauma
|
||||
|
||||
GameSession gameSession = new GameSession(backedUpSubInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null);
|
||||
gameSession.StartRound(null, false);
|
||||
|
||||
(gameSession.GameMode as TestGameMode).OnRoundEnd = () =>
|
||||
|
||||
foreach ((string layerName, LayerData layerData) in Layers)
|
||||
{
|
||||
Submarine.Unload();
|
||||
GameMain.SubEditorScreen.Select();
|
||||
};
|
||||
Identifier identifier = layerName.ToIdentifier();
|
||||
bool enabled = layerData.IsVisible;
|
||||
MainSub.SetLayerEnabled(identifier, enabled);
|
||||
}
|
||||
|
||||
if (gameSession.GameMode is TestGameMode testGameMode)
|
||||
{
|
||||
testGameMode.OnRoundEnd = () =>
|
||||
{
|
||||
Submarine.Unload();
|
||||
GameMain.SubEditorScreen.Select();
|
||||
};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1472,6 +1452,7 @@ namespace Barotrauma
|
||||
{
|
||||
var subInfo = new SubmarineInfo();
|
||||
MainSub = new Submarine(subInfo, showErrorMessages: false);
|
||||
ReconstructLayers();
|
||||
}
|
||||
|
||||
MainSub.UpdateTransform(interpolate: false);
|
||||
@@ -1507,7 +1488,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
ImageManager.OnEditorSelected();
|
||||
ReconstructLayers();
|
||||
if (Layers.None())
|
||||
{
|
||||
ReconstructLayers();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnFileDropped(string filePath, string extension)
|
||||
@@ -1664,7 +1648,6 @@ namespace Barotrauma
|
||||
});
|
||||
|
||||
ClearFilter();
|
||||
ClearLayers();
|
||||
}
|
||||
|
||||
private void CreateDummyCharacter()
|
||||
@@ -2168,32 +2151,32 @@ namespace Barotrauma
|
||||
if (Layers.Any())
|
||||
{
|
||||
var layerVisibilityGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.01f), leftColumn.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
var visibleLayers = Layers.Where(l => !MainSub.Info.LayersHiddenByDefault.Contains(l.Key.ToIdentifier()));
|
||||
LocalizedString visibleLayersString = LocalizedString.Join(", ", visibleLayers.Select(l => TextManager.Capitalize(l.Key)) ?? ((LocalizedString)"None").ToEnumerable());
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), layerVisibilityGroup.RectTransform), TextManager.Get("editor.layer.visiblebydefault"), textAlignment: Alignment.CenterLeft);
|
||||
var layerVisibilityDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), layerVisibilityGroup.RectTransform),
|
||||
text: LocalizedString.Join(", ", Layers.Where(l => !Submarine.MainSub?.Info?.LayersHiddenByDefault?.Contains(l.ToIdentifier()) ?? false).Select(lt => TextManager.Capitalize(lt.Key)) ?? ((LocalizedString)"None").ToEnumerable()), selectMultiple: true);
|
||||
foreach (string layerName in Layers.Keys)
|
||||
var layerVisibilityDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), layerVisibilityGroup.RectTransform), text: visibleLayersString, selectMultiple: true);
|
||||
foreach (var layer in Layers)
|
||||
{
|
||||
string layerName = layer.Key;
|
||||
layerVisibilityDropDown.AddItem(TextManager.Capitalize(layerName), layerName);
|
||||
if (MainSub?.Info == null) { continue; }
|
||||
if (!MainSub.Info.LayersHiddenByDefault.Contains(layerName.ToIdentifier()))
|
||||
if (visibleLayers.Contains(layer))
|
||||
{
|
||||
layerVisibilityDropDown.SelectItem(layerName);
|
||||
}
|
||||
}
|
||||
layerVisibilityDropDown.OnSelected += (_, __) =>
|
||||
layerVisibilityDropDown.OnSelected += (button, obj) =>
|
||||
{
|
||||
if (MainSub.Info == null) { return false; }
|
||||
MainSub.Info.LayersHiddenByDefault.Clear();
|
||||
foreach (string layerName in Layers.Keys)
|
||||
string layerName = (string)obj;
|
||||
bool isVisible = layerVisibilityDropDown.SelectedDataMultiple.Contains(obj);
|
||||
if (isVisible)
|
||||
{
|
||||
//selected as visible = not hidden
|
||||
if (layerVisibilityDropDown.SelectedDataMultiple.Any(o => o as string == layerName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
MainSub.Info.LayersHiddenByDefault.Add(layerName.ToIdentifier());
|
||||
MainSub.Info.LayersHiddenByDefault.Remove(layerName.ToIdentifier());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
MainSub.Info.LayersHiddenByDefault.Add(layerName.ToIdentifier());
|
||||
}
|
||||
UpdateLayerPanel();
|
||||
layerVisibilityDropDown.Text = ToolBox.LimitString(layerVisibilityDropDown.Text.Value, layerVisibilityDropDown.Font, layerVisibilityDropDown.Rect.Width);
|
||||
return true;
|
||||
};
|
||||
@@ -2511,6 +2494,15 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
};
|
||||
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.25f), beaconSettingsContainer.RectTransform), TextManager.Get("allowdamageddevices"))
|
||||
{
|
||||
Selected = MainSub?.Info?.BeaconStationInfo?.AllowDamagedDevices ?? true,
|
||||
OnSelected = (tb) =>
|
||||
{
|
||||
MainSub.Info.BeaconStationInfo.AllowDamagedDevices = tb.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.25f), beaconSettingsContainer.RectTransform), TextManager.Get("allowdisconnectedwires"))
|
||||
{
|
||||
Selected = MainSub?.Info?.BeaconStationInfo?.AllowDisconnectedWires ?? true,
|
||||
@@ -3935,15 +3927,14 @@ namespace Barotrauma
|
||||
MapEntity.HighlightedEntities.ToList() :
|
||||
new List<MapEntity>(MapEntity.SelectedList);
|
||||
|
||||
Item target = null;
|
||||
|
||||
var single = targets.Count == 1 ? targets.Single() : null;
|
||||
if (single is Item item && item.Components.Any(static ic => ic is not ConnectionPanel && ic is not Repairable && ic.GuiFrame != null))
|
||||
{
|
||||
// Do not offer the ability to open the inventory if the inventory should never be drawn
|
||||
var containers = item.GetComponents<ItemContainer>();
|
||||
if (containers.Any(static c => c.DrawInventory) || item.GetComponent<CircuitBox>() is not null) { target = item; }
|
||||
}
|
||||
bool allowOpening = false;
|
||||
var targetItem = (targets.Count == 1 ? targets.Single() : null) as Item;
|
||||
// Do not offer the ability to open the inventory if the inventory should never be drawn
|
||||
allowOpening = targetItem is not null && targetItem.Components.Any(static ic =>
|
||||
ic is not ConnectionPanel &&
|
||||
ic is not Repairable &&
|
||||
ic is not ItemContainer { DrawInventory: false } &&
|
||||
ic.GuiFrame != null);
|
||||
|
||||
bool hasTargets = targets.Count > 0;
|
||||
|
||||
@@ -3987,7 +3978,6 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
List<ContextMenuOption> availableLayers = new List<ContextMenuOption>
|
||||
{
|
||||
new ContextMenuOption("editor.layer.nolayer", true, onSelected: () => { MoveToLayer(null, targets); })
|
||||
@@ -3995,7 +3985,8 @@ namespace Barotrauma
|
||||
availableLayers.AddRange(Layers.Select(layer => new ContextMenuOption(layer.Key, true, onSelected: () => { MoveToLayer(layer.Key, targets); })));
|
||||
|
||||
List<ContextMenuOption> availableLayerOptions = new List<ContextMenuOption>
|
||||
{ new ContextMenuOption("editor.layer.movetolayer", isEnabled: hasTargets, availableLayers.ToArray()),
|
||||
{
|
||||
new ContextMenuOption("editor.layer.movetolayer", isEnabled: hasTargets, availableLayers.ToArray()),
|
||||
new ContextMenuOption("editor.layer.createlayer", isEnabled: hasTargets, onSelected: () => { CreateNewLayer(null, targets); }),
|
||||
new ContextMenuOption("editor.layer.selectall", isEnabled: hasTargets, onSelected: () =>
|
||||
{
|
||||
@@ -4009,7 +4000,7 @@ namespace Barotrauma
|
||||
availableLayerOptions.AddRange(Layers.Select(layer => new ContextMenuOption(layer.Key, true, onSelected: () => { MoveToLayer(layer.Key, targets); })));
|
||||
|
||||
GUIContextMenu.CreateContextMenu(
|
||||
new ContextMenuOption("label.openlabel", isEnabled: target != null, onSelected: () => OpenItem(target)),
|
||||
new ContextMenuOption("label.openlabel", isEnabled: allowOpening, onSelected: () => OpenItem(targetItem)),
|
||||
new ContextMenuOption("editor.cut", isEnabled: hasTargets, onSelected: () => MapEntity.Cut(targets)),
|
||||
new ContextMenuOption("editor.copytoclipboard", isEnabled: hasTargets, onSelected: () => MapEntity.Copy(targets)),
|
||||
new ContextMenuOption("editor.paste", isEnabled: MapEntity.CopiedList.Any(), onSelected: () => MapEntity.Paste(cam.ScreenToWorld(PlayerInput.MousePosition))),
|
||||
@@ -4064,13 +4055,13 @@ namespace Barotrauma
|
||||
MoveToLayer(name, content);
|
||||
}
|
||||
|
||||
Layers.Add(name, LayerData.Default);
|
||||
Layers.Add(name, new LayerData());
|
||||
UpdateLayerPanel();
|
||||
}
|
||||
|
||||
private void RenameLayer(string original, string newName)
|
||||
{
|
||||
Layers.Remove(original);
|
||||
Layers.Remove(original, out LayerData originalData);
|
||||
|
||||
foreach (MapEntity entity in MapEntity.MapEntityList.Where(entity => entity.Layer == original))
|
||||
{
|
||||
@@ -4079,7 +4070,7 @@ namespace Barotrauma
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(newName))
|
||||
{
|
||||
Layers.TryAdd(newName, LayerData.Default);
|
||||
Layers.TryAdd(newName, originalData);
|
||||
}
|
||||
UpdateLayerPanel();
|
||||
}
|
||||
@@ -4091,7 +4082,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(entity.Layer))
|
||||
{
|
||||
Layers.TryAdd(entity.Layer, LayerData.Default);
|
||||
Layers.TryAdd(entity.Layer, new LayerData(!entity.IsLayerHidden));
|
||||
}
|
||||
}
|
||||
UpdateLayerPanel();
|
||||
@@ -4102,6 +4093,18 @@ namespace Barotrauma
|
||||
Layers.Clear();
|
||||
UpdateLayerPanel();
|
||||
}
|
||||
|
||||
private static void SetLayerVisibility(string layerName, bool isVisible)
|
||||
{
|
||||
if (Layers.Remove(layerName, out LayerData layerData))
|
||||
{
|
||||
Layers.Add(layerName, layerData with { IsVisible = isVisible });
|
||||
}
|
||||
else
|
||||
{
|
||||
Layers.Add(layerName, new LayerData(isVisible));
|
||||
}
|
||||
}
|
||||
|
||||
private void PasteAssembly(string text = null, Vector2? pos = null)
|
||||
{
|
||||
@@ -4495,39 +4498,39 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to open an item container in the submarine editor using the dummy character
|
||||
/// Tries to open an item in the submarine editor using the dummy character
|
||||
/// </summary>
|
||||
/// <param name="itemContainer">The item we want to open</param>
|
||||
private void OpenItem(Item itemContainer)
|
||||
/// <param name="item">The item we want to open</param>
|
||||
private void OpenItem(Item item)
|
||||
{
|
||||
if (dummyCharacter == null || itemContainer == null) { return; }
|
||||
if (dummyCharacter == null || item == null) { return; }
|
||||
|
||||
if ((itemContainer.GetComponent<Holdable>() is { Attached: false } || itemContainer.GetComponent<Wearable>() != null) && itemContainer.GetComponent<ItemContainer>() != null)
|
||||
if ((item.GetComponent<Holdable>() is { Attached: false } || item.GetComponent<Wearable>() != null) && item.GetComponent<ItemContainer>() != null)
|
||||
{
|
||||
// We teleport our dummy character to the item so it appears as the entity stays still when in reality the dummy is holding it
|
||||
oldItemPosition = itemContainer.SimPosition;
|
||||
oldItemPosition = item.SimPosition;
|
||||
TeleportDummyCharacter(oldItemPosition);
|
||||
|
||||
// Override this so we can be sure the container opens
|
||||
var container = itemContainer.GetComponent<ItemContainer>();
|
||||
var container = item.GetComponent<ItemContainer>();
|
||||
if (container != null) { container.KeepOpenWhenEquipped = true; }
|
||||
|
||||
// We accept any slots except "Any" since that would take priority
|
||||
List<InvSlotType> allowedSlots = new List<InvSlotType>();
|
||||
itemContainer.AllowedSlots.ForEach(type =>
|
||||
item.AllowedSlots.ForEach(type =>
|
||||
{
|
||||
if (type != InvSlotType.Any) { allowedSlots.Add(type); }
|
||||
});
|
||||
|
||||
// Try to place the item in the dummy character's inventory
|
||||
bool success = dummyCharacter.Inventory.TryPutItem(itemContainer, dummyCharacter, allowedSlots);
|
||||
if (success) { OpenedItem = itemContainer; }
|
||||
bool success = dummyCharacter.Inventory.TryPutItem(item, dummyCharacter, allowedSlots);
|
||||
if (success) { OpenedItem = item; }
|
||||
else { return; }
|
||||
}
|
||||
MapEntity.SelectedList.Clear();
|
||||
MapEntity.FilteredSelectedList.Clear();
|
||||
MapEntity.SelectEntity(itemContainer);
|
||||
dummyCharacter.SelectedItem = itemContainer;
|
||||
MapEntity.SelectEntity(item);
|
||||
dummyCharacter.SelectedItem = item;
|
||||
FilterEntities(entityFilterBox.Text);
|
||||
MapEntity.StopSelection();
|
||||
}
|
||||
@@ -5179,7 +5182,7 @@ namespace Barotrauma
|
||||
};
|
||||
new GUIButton(new RectTransform(new Vector2(0.6f, 1f), buttonHeaders.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes };
|
||||
|
||||
foreach (var (layer, (visibility, linkage)) in Layers)
|
||||
foreach ((string layer, (bool isVisible, bool isGrouped)) in Layers)
|
||||
{
|
||||
GUIFrame parent = new GUIFrame(new RectTransform(new Vector2(1f, 0.1f), layerList.Content.RectTransform), style: "ListBoxElement")
|
||||
{
|
||||
@@ -5191,7 +5194,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup layerVisibilityLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1f), layerGroup.RectTransform), childAnchor: Anchor.Center);
|
||||
GUITickBox layerVisibleButton = new GUITickBox(new RectTransform(Vector2.One, layerVisibilityLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), string.Empty)
|
||||
{
|
||||
Selected = visibility == LayerVisibility.Visible,
|
||||
Selected = isVisible,
|
||||
OnSelected = box =>
|
||||
{
|
||||
if (!Layers.TryGetValue(layer, out LayerData data))
|
||||
@@ -5199,12 +5202,15 @@ namespace Barotrauma
|
||||
UpdateLayerPanel();
|
||||
return false;
|
||||
}
|
||||
//hiding a layer automatically deselects it (can't edit a hidden layer)
|
||||
if (!box.Selected && layerList.SelectedData as string == layer)
|
||||
{
|
||||
layerList.Deselect();
|
||||
//hiding a layer automatically deselects it (can't edit a hidden layer)
|
||||
if (!box.Selected)
|
||||
{
|
||||
layerList.Deselect();
|
||||
}
|
||||
}
|
||||
Layers[layer] = new LayerData(box.Selected ? LayerVisibility.Visible : LayerVisibility.Invisible, data.Linkage);
|
||||
Layers[layer] = data with { IsVisible = box.Selected };
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -5212,7 +5218,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup layerChainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.15f, 1f), layerGroup.RectTransform), childAnchor: Anchor.Center);
|
||||
GUITickBox layerChainButton = new GUITickBox(new RectTransform(Vector2.One, layerChainLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), string.Empty)
|
||||
{
|
||||
Selected = linkage == LayerLinkage.Linked,
|
||||
Selected = isGrouped,
|
||||
OnSelected = box =>
|
||||
{
|
||||
if (!Layers.TryGetValue(layer, out LayerData data))
|
||||
@@ -5221,7 +5227,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
Layers[layer] = new LayerData(data.Visible, box.Selected ? LayerLinkage.Linked : LayerLinkage.Unlinked);
|
||||
Layers[layer] = data with { IsGrouped = box.Selected };
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -5252,7 +5258,6 @@ namespace Barotrauma
|
||||
btn.ToolTip = originalBtnText;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void UpdateUndoHistoryPanel()
|
||||
@@ -6306,11 +6311,11 @@ namespace Barotrauma
|
||||
|
||||
if (!Layers.TryGetValue(entity.Layer, out LayerData data))
|
||||
{
|
||||
Layers.TryAdd(entity.Layer, LayerData.Default);
|
||||
Layers.TryAdd(entity.Layer, new LayerData(!entity.IsLayerHidden));
|
||||
return true;
|
||||
}
|
||||
|
||||
return data.Visible == LayerVisibility.Visible;
|
||||
return data.IsVisible;
|
||||
}
|
||||
|
||||
public static bool IsLayerLinked(MapEntity entity)
|
||||
@@ -6319,11 +6324,11 @@ namespace Barotrauma
|
||||
|
||||
if (!Layers.TryGetValue(entity.Layer, out LayerData data))
|
||||
{
|
||||
Layers.TryAdd(entity.Layer, LayerData.Default);
|
||||
Layers.TryAdd(entity.Layer, new LayerData(!entity.IsLayerHidden));
|
||||
return true;
|
||||
}
|
||||
|
||||
return data.Linkage == LayerLinkage.Linked;
|
||||
return data.IsGrouped;
|
||||
}
|
||||
|
||||
public static ImmutableHashSet<MapEntity> GetEntitiesInSameLayer(MapEntity entity)
|
||||
|
||||
@@ -326,7 +326,7 @@ namespace Barotrauma
|
||||
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, IEnumerable<SerializableProperty> properties, bool showName, string style = "", int elementHeight = 24, GUIFont titleFont = null)
|
||||
: base(style, new RectTransform(Vector2.One, parent))
|
||||
{
|
||||
this.elementHeight = (int)(elementHeight * GUI.Scale);
|
||||
elementHeight = (int)(elementHeight * GUI.Scale);
|
||||
var tickBoxStyle = GUIStyle.GetComponentStyle("GUITickBox");
|
||||
var textBoxStyle = GUIStyle.GetComponentStyle("GUITextBox");
|
||||
var numberInputStyle = GUIStyle.GetComponentStyle("GUINumberInput");
|
||||
@@ -343,7 +343,50 @@ namespace Barotrauma
|
||||
Color = Color.Black
|
||||
};
|
||||
}
|
||||
properties.ForEach(ep => CreateNewField(ep, entity));
|
||||
|
||||
List<Header> headers = new List<Header>()
|
||||
{
|
||||
//"no header" comes first = properties under no header are listed first
|
||||
null
|
||||
};
|
||||
//check which header each property is under
|
||||
Dictionary<SerializableProperty, Header> propertyHeaders = new Dictionary<SerializableProperty, Header>();
|
||||
Header prevHeader = null;
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var header = property.GetAttribute<Header>();
|
||||
if (header != null)
|
||||
{
|
||||
prevHeader = header;
|
||||
//Attribute.Equals is based on the equality of the fields,
|
||||
//so in practice we treat identical headers split into different files/classes as the same header
|
||||
if (!headers.Contains(header))
|
||||
{
|
||||
//collect headers into a list in the order they're encountered in
|
||||
//(to keep them in the same order as they're defined in the code, as the dictionary is not in any particular order)
|
||||
headers.Add(header);
|
||||
}
|
||||
}
|
||||
propertyHeaders[property] = prevHeader;
|
||||
}
|
||||
|
||||
prevHeader = null;
|
||||
foreach (Header header in headers)
|
||||
{
|
||||
//go through all the properties that belong under this header
|
||||
foreach (var property in properties)
|
||||
{
|
||||
if (!Equals(propertyHeaders[property], header)) { continue; }
|
||||
//don't create a header if the previous header has the same text as this one (= if we already created this header before)
|
||||
if (header != null && !Equals(header, prevHeader))
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Point(Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize: true),
|
||||
header.Text, textColor: GUIStyle.TextColorBright, font: GUIStyle.SubHeadingFont);
|
||||
prevHeader = header;
|
||||
}
|
||||
CreateNewField(property, entity);
|
||||
}
|
||||
}
|
||||
|
||||
//scale the size of this component and the layout group to fit the children
|
||||
Recalculate();
|
||||
|
||||
@@ -137,22 +137,10 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
outBuffer[i] = FloatToShort(inBuffer[i]);
|
||||
outBuffer[i] = ToolBox.FloatToShortAudioSample(inBuffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static protected short FloatToShort(float fVal)
|
||||
{
|
||||
int temp = (int)(32767 * fVal);
|
||||
if (temp > short.MaxValue) temp = short.MaxValue;
|
||||
else if (temp < short.MinValue) temp = short.MinValue;
|
||||
return (short)temp;
|
||||
}
|
||||
static protected float ShortToFloat(short shortVal)
|
||||
{
|
||||
return shortVal / 32767f;
|
||||
}
|
||||
|
||||
public abstract int FillStreamBuffer(int samplePos, short[] buffer);
|
||||
|
||||
public abstract float GetAmplitudeAtPlaybackPos(int playbackPos);
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Barotrauma.Sounds
|
||||
float finalGain = gain * GameSettings.CurrentConfig.Audio.VoiceChatVolume * client.VoiceVolume;
|
||||
for (int i = 0; i < readSamples; i++)
|
||||
{
|
||||
float fVal = ShortToFloat(buffer[i]);
|
||||
float fVal = ToolBox.ShortAudioSampleToFloat(buffer[i]);
|
||||
|
||||
if (finalGain > 1.0f) //TODO: take distance into account?
|
||||
{
|
||||
@@ -128,7 +128,7 @@ namespace Barotrauma.Sounds
|
||||
fVal = Math.Clamp(filter.Process(fVal) * PostRadioFilterBoost, -1f, 1f);
|
||||
}
|
||||
}
|
||||
buffer[i] = FloatToShort(fVal);
|
||||
buffer[i] = ToolBox.FloatToShortAudioSample(fVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,10 +99,10 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
currentLobby?.SetData("EosEndpoint", puids[0].Value);
|
||||
}
|
||||
|
||||
|
||||
DebugConsole.Log("Lobby updated!");
|
||||
}
|
||||
|
||||
|
||||
private static void SetServerListInfo(Identifier key, object value)
|
||||
{
|
||||
switch (value)
|
||||
@@ -115,7 +115,7 @@ namespace Barotrauma.Steam
|
||||
.JoinEscaped(','));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
currentLobby?.SetData(key.Value.ToLowerInvariant(), value.ToString());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user