v1.4.4.1 (Blood in the Water Update)
This commit is contained in:
@@ -277,7 +277,7 @@ namespace Barotrauma
|
||||
velocity = Vector2.Lerp(velocity, moveInput, deltaTime * 10.0f);
|
||||
moveCam = velocity * moveSpeed * deltaTime * FreeCamMoveSpeed * 60.0f;
|
||||
|
||||
if (Screen.Selected == GameMain.GameScreen && (followSub ?? FollowSub))
|
||||
if (Screen.Selected == GameMain.GameScreen && (followSub ?? FollowSub) && GameMain.Instance is not { Paused: true })
|
||||
{
|
||||
var closestSub = Submarine.FindClosest(WorldViewCenter);
|
||||
if (closestSub != null)
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
|
||||
float errorTolerance = character.CanMove && !character.IsRagdolled ? 0.01f : 0.2f;
|
||||
float errorTolerance = character.CanMove && (!character.IsRagdolled || character.AnimController.IsHangingWithRope) ? 0.01f : 0.2f;
|
||||
if (distSqrd > errorTolerance)
|
||||
{
|
||||
if (distSqrd > 10.0f || !character.CanMove)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Particles;
|
||||
@@ -9,7 +10,6 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -42,6 +42,8 @@ namespace Barotrauma
|
||||
private set;
|
||||
} = true;
|
||||
|
||||
public bool ShowInteractionLabels { get; private set; }
|
||||
|
||||
//the Character that the player is currently controlling
|
||||
private static Character controlled;
|
||||
|
||||
@@ -230,14 +232,51 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private float pressureEffectTimer;
|
||||
|
||||
private readonly List<ObjectiveEntity> activeObjectiveEntities = new List<ObjectiveEntity>();
|
||||
public IEnumerable<ObjectiveEntity> ActiveObjectiveEntities
|
||||
{
|
||||
get { return activeObjectiveEntities; }
|
||||
}
|
||||
|
||||
private static readonly List<SpeechBubble> speechBubbles = new List<SpeechBubble>();
|
||||
|
||||
private SpeechBubble textlessSpeechBubble;
|
||||
|
||||
sealed class SpeechBubble
|
||||
{
|
||||
public float LifeTime;
|
||||
public Vector2 PrevPosition;
|
||||
public Vector2 Position;
|
||||
public Vector2 DrawPosition;
|
||||
public float MoveUpAmount;
|
||||
public readonly string Text;
|
||||
public readonly Character Character;
|
||||
public readonly Submarine Submarine;
|
||||
public readonly Vector2 TextSize;
|
||||
|
||||
public Color Color;
|
||||
public bool Moving;
|
||||
|
||||
public SpeechBubble(Character character, float lifeTime, Color color, string text = "")
|
||||
{
|
||||
Text = ToolBox.WrapText(text, GUI.IntScale(300), GUIStyle.SmallFont.GetFontForStr(text));
|
||||
TextSize = GUIStyle.SmallFont.MeasureString(Text);
|
||||
|
||||
Character = character;
|
||||
Position = GetDesiredPosition();
|
||||
Submarine = character.Submarine;
|
||||
LifeTime = lifeTime;
|
||||
Color = color;
|
||||
}
|
||||
|
||||
public Vector2 GetDesiredPosition()
|
||||
{
|
||||
return Character.Position + Vector2.UnitY * 100;
|
||||
}
|
||||
}
|
||||
|
||||
private float pressureEffectTimer;
|
||||
|
||||
partial void InitProjSpecific(ContentXElement mainElement)
|
||||
{
|
||||
soundTimer = Rand.Range(0.0f, Params.SoundInterval);
|
||||
@@ -272,6 +311,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Item> previousInteractablesInRange = new();
|
||||
private readonly List<Item> interactablesInRange = new();
|
||||
|
||||
private bool wasFiring;
|
||||
|
||||
/// <summary>
|
||||
@@ -279,6 +321,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
|
||||
{
|
||||
|
||||
if (DisableControls || GUI.InputBlockingMenuOpen)
|
||||
{
|
||||
foreach (Key key in keys)
|
||||
@@ -316,6 +359,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
ShowInteractionLabels = keys[(int)InputType.ShowInteractionLabels].Held;
|
||||
|
||||
if (ShowInteractionLabels)
|
||||
{
|
||||
focusedItem = InteractionLabelManager.HoveredItem;
|
||||
}
|
||||
|
||||
//if we were firing (= pressing the aim and shoot keys at the same time)
|
||||
//and the fire key is the same as Select or Use, reset the key to prevent accidentally selecting/using items
|
||||
if (wasFiring && !keys[(int)InputType.Shoot].Held)
|
||||
@@ -549,19 +599,61 @@ namespace Barotrauma
|
||||
if (Lights.LightManager.ViewTarget == this) { Lights.LightManager.ViewTarget = null; }
|
||||
}
|
||||
|
||||
private void UpdateInteractablesInRange()
|
||||
{
|
||||
// keep two lists to detect changes to the current state of interactables in range
|
||||
previousInteractablesInRange.Clear();
|
||||
previousInteractablesInRange.AddRange(interactablesInRange);
|
||||
|
||||
interactablesInRange.Clear();
|
||||
|
||||
//use the list of visible entities if it exists
|
||||
var entityList = Submarine.VisibleEntities ?? Item.ItemList;
|
||||
|
||||
foreach (MapEntity entity in entityList)
|
||||
{
|
||||
if (entity is not Item item) { continue; }
|
||||
|
||||
if (item.body != null && !item.body.Enabled) { continue; }
|
||||
|
||||
if (item.ParentInventory != null) { continue; }
|
||||
|
||||
if (item.Prefab.RequireCampaignInteract &&
|
||||
item.CampaignInteractionType == CampaignMode.InteractionType.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Screen.Selected is SubEditorScreen { WiringMode: true } &&
|
||||
item.GetComponent<ConnectionPanel>() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CanInteractWith(item))
|
||||
{
|
||||
interactablesInRange.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (!interactablesInRange.SequenceEqual(previousInteractablesInRange))
|
||||
{
|
||||
InteractionLabelManager.RefreshInteractablesInRange(interactablesInRange);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Item> debugInteractablesInRange = new List<Item>();
|
||||
private readonly List<Item> debugInteractablesAtCursor = new List<Item>();
|
||||
private readonly List<(Item item, float dist)> debugInteractablesNearCursor = new List<(Item item, float dist)>();
|
||||
|
||||
/// <summary>
|
||||
/// Finds the front (lowest depth) interactable item at a position. "Interactable" in this case means that the character can "reach" the item.
|
||||
/// Finds the front (lowest depth) interactable item at a position. "Interactable" in this case means that the character can "reach" the item.
|
||||
/// </summary>
|
||||
/// <param name="character">The Character who is looking for the interactable item, only items that are close enough to this character are returned</param>
|
||||
/// <param name="simPosition">The item at the simPosition, with the lowest depth, is returned</param>
|
||||
/// <param name="allowFindingNearestItem">If this is true and an item cannot be found at simPosition then a nearest item will be returned if possible</param>
|
||||
/// <param name="hull">If a hull is specified, only items within that hull are returned</param>
|
||||
public Item FindItemAtPosition(Vector2 simPosition, float aimAssistModifier = 0.0f, Item[] ignoredItems = null)
|
||||
/// <param name="itemCollection">Item collection to look in</param>
|
||||
/// <param name="simPosition">sim position for distance comparison (such as mouse position)</param>
|
||||
/// <param name="aimAssistModifier">aim assist modifier</param>
|
||||
/// <returns></returns>
|
||||
public Item FindClosestItem(List<Item> itemCollection, Vector2 simPosition, float aimAssistModifier = 0.0f)
|
||||
{
|
||||
if (Submarine != null)
|
||||
{
|
||||
@@ -580,24 +672,11 @@ namespace Barotrauma
|
||||
float aimAssistAmount = SelectedItem == null ? 100.0f * aimAssistModifier : 1.0f;
|
||||
|
||||
Vector2 displayPosition = ConvertUnits.ToDisplayUnits(simPosition);
|
||||
|
||||
//use the list of visible entities if it exists
|
||||
var entityList = Submarine.VisibleEntities ?? Item.ItemList;
|
||||
|
||||
|
||||
Item closestItem = null;
|
||||
float closestItemDistance = Math.Max(aimAssistAmount, 2.0f);
|
||||
foreach (MapEntity entity in entityList)
|
||||
foreach (var item in itemCollection)
|
||||
{
|
||||
if (entity is not Item item)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.body != null && !item.body.Enabled) { continue; }
|
||||
if (item.ParentInventory != null) { continue; }
|
||||
if (ignoredItems != null && ignoredItems.Contains(item)) { continue; }
|
||||
if (item.Prefab.RequireCampaignInteract && item.CampaignInteractionType == CampaignMode.InteractionType.None) { continue; }
|
||||
if (Screen.Selected is SubEditorScreen editor && editor.WiringMode && item.GetComponent<ConnectionPanel>() == null) { continue; }
|
||||
|
||||
if (draggingItemToWorld)
|
||||
{
|
||||
if (item.OwnInventory == null ||
|
||||
@@ -644,7 +723,7 @@ namespace Barotrauma
|
||||
distanceToItem = 2.0f + Vector2.Distance(rectIntersectionPoint, displayPosition);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (distanceToItem > closestItemDistance) { continue; }
|
||||
if (!CanInteractWith(item)) { continue; }
|
||||
|
||||
@@ -661,7 +740,7 @@ namespace Barotrauma
|
||||
Character closestCharacter = null;
|
||||
|
||||
maxDist = ConvertUnits.ToSimUnits(maxDist);
|
||||
float closestDist = maxDist * maxDist;
|
||||
float closestDist = maxDist;
|
||||
foreach (Character c in CharacterList)
|
||||
{
|
||||
if (!CanInteractWith(c, checkVisibility: false) || (c.AnimController?.SimplePhysicsEnabled ?? true)) { continue; }
|
||||
@@ -704,6 +783,12 @@ namespace Barotrauma
|
||||
}
|
||||
guiMessages.RemoveAll(m => m.Timer >= m.Lifetime);
|
||||
|
||||
if (textlessSpeechBubble != null)
|
||||
{
|
||||
textlessSpeechBubble.LifeTime -= deltaTime;
|
||||
if (textlessSpeechBubble.LifeTime <= 0) { textlessSpeechBubble = null; }
|
||||
}
|
||||
|
||||
if (!enabled) { return; }
|
||||
|
||||
if (!IsIncapacitated)
|
||||
@@ -890,13 +975,6 @@ namespace Barotrauma
|
||||
|
||||
pos.Y = -pos.Y;
|
||||
|
||||
if (speechBubbleTimer > 0.0f)
|
||||
{
|
||||
GUIStyle.SpeechBubbleIcon.Value.Sprite.Draw(spriteBatch, pos - Vector2.UnitY * 5,
|
||||
speechBubbleColor * Math.Min(speechBubbleTimer, 1.0f), 0.0f,
|
||||
Math.Min(speechBubbleTimer, 1.0f));
|
||||
}
|
||||
|
||||
if (this == controlled)
|
||||
{
|
||||
if (DebugDrawInteract)
|
||||
@@ -921,113 +999,232 @@ namespace Barotrauma
|
||||
ToolBox.GradientLerp(dist, GUIStyle.Red, GUIStyle.Orange, GUIStyle.Green), width: 2);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float hoverRange = 300.0f;
|
||||
float fadeOutRange = 200.0f;
|
||||
float cursorDist = Vector2.Distance(WorldPosition, cam.ScreenToWorld(PlayerInput.MousePosition));
|
||||
float hudInfoAlpha =
|
||||
CampaignInteractionType == CampaignMode.InteractionType.None ?
|
||||
MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f) :
|
||||
1.0f;
|
||||
|
||||
if (!GUI.DisableCharacterNames && hudInfoVisible &&
|
||||
(controlled == null || this != controlled.FocusedCharacter || IsPet) && cam.Zoom > 0.4f)
|
||||
else
|
||||
{
|
||||
if (info != null)
|
||||
|
||||
float hoverRange = 300.0f;
|
||||
float fadeOutRange = 200.0f;
|
||||
float cursorDist = Vector2.Distance(WorldPosition, cam.ScreenToWorld(PlayerInput.MousePosition));
|
||||
float hudInfoAlpha =
|
||||
CampaignInteractionType == CampaignMode.InteractionType.None ?
|
||||
MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f) :
|
||||
1.0f;
|
||||
|
||||
if (!GUI.DisableCharacterNames && hudInfoVisible &&
|
||||
(controlled == null || this != controlled.FocusedCharacter || IsPet) && cam.Zoom > 0.4f)
|
||||
{
|
||||
LocalizedString name = Info.DisplayName;
|
||||
if (controlled == null && name != Info.Name)
|
||||
{
|
||||
name += " " + TextManager.Get("Disguised");
|
||||
}
|
||||
else if (Info.Title != null && TeamID != CharacterTeamType.Team1)
|
||||
if (info != null)
|
||||
{
|
||||
name += '\n' + Info.Title;
|
||||
LocalizedString name = Info.DisplayName;
|
||||
if (controlled == null && name != Info.Name)
|
||||
{
|
||||
name += " " + TextManager.Get("Disguised");
|
||||
}
|
||||
else if (Info.Title != null && TeamID != CharacterTeamType.Team1)
|
||||
{
|
||||
name += '\n' + Info.Title;
|
||||
}
|
||||
|
||||
Vector2 nameSize = GUIStyle.Font.MeasureString(name);
|
||||
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
|
||||
Color nameColor = GetNameColor();
|
||||
|
||||
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
|
||||
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
|
||||
namePos *= screenSize / viewportSize;
|
||||
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
|
||||
namePos *= viewportSize / screenSize;
|
||||
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
|
||||
|
||||
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
|
||||
{
|
||||
var iconStyle = GUIStyle.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
|
||||
if (iconStyle != null)
|
||||
{
|
||||
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
|
||||
Vector2 iconPos = headPos;
|
||||
iconPos.Y = -iconPos.Y;
|
||||
nameColor = iconStyle.Color;
|
||||
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
|
||||
float iconScale = (30.0f / icon.Sprite.size.X / cam.Zoom) * GUI.Scale;
|
||||
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
|
||||
}
|
||||
}
|
||||
|
||||
GUIStyle.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
|
||||
GUIStyle.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
GUIStyle.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 nameSize = GUIStyle.Font.MeasureString(name);
|
||||
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
|
||||
Color nameColor = GetNameColor();
|
||||
|
||||
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
|
||||
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
|
||||
namePos *= screenSize / viewportSize;
|
||||
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
|
||||
namePos *= viewportSize / screenSize;
|
||||
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
|
||||
|
||||
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
|
||||
var petBehavior = (AIController as EnemyAIController)?.PetBehavior;
|
||||
if (petBehavior != null && !IsDead && !IsUnconscious)
|
||||
{
|
||||
var iconStyle = GUIStyle.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
|
||||
var petStatus = petBehavior.GetCurrentStatusIndicatorType();
|
||||
var iconStyle = GUIStyle.GetComponentStyle("PetIcon." + petStatus);
|
||||
if (iconStyle != null)
|
||||
{
|
||||
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
|
||||
Vector2 iconPos = headPos;
|
||||
iconPos.Y = -iconPos.Y;
|
||||
nameColor = iconStyle.Color;
|
||||
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
|
||||
float iconScale = (30.0f / icon.Sprite.size.X / cam.Zoom) * GUI.Scale;
|
||||
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
|
||||
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUIStyle.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
|
||||
GUIStyle.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
|
||||
if (GameMain.DebugDraw)
|
||||
if (IsDead) { return; }
|
||||
|
||||
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
|
||||
if (healthBarMode != EnemyHealthBarMode.ShowAll)
|
||||
{
|
||||
if (Controlled == null)
|
||||
{
|
||||
GUIStyle.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
|
||||
if (!IsOnPlayerTeam) { return; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!HumanAIController.IsFriendly(Controlled, this) ||
|
||||
(AIController is HumanAIController humanAi && humanAi.ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective && HumanAIController.IsFriendly(Controlled, combatObjective.Enemy)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var petBehavior = (AIController as EnemyAIController)?.PetBehavior;
|
||||
if (petBehavior != null && !IsDead && !IsUnconscious)
|
||||
|
||||
if (Params.ShowHealthBar && CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
|
||||
{
|
||||
var petStatus = petBehavior.GetCurrentStatusIndicatorType();
|
||||
var iconStyle = GUIStyle.GetComponentStyle("PetIcon." + petStatus);
|
||||
if (iconStyle != null)
|
||||
{
|
||||
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
|
||||
Vector2 iconPos = headPos;
|
||||
iconPos.Y = -iconPos.Y;
|
||||
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
|
||||
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
|
||||
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
|
||||
}
|
||||
hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f));
|
||||
|
||||
Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y);
|
||||
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f),
|
||||
CharacterHealth.DisplayedVitality / MaxVitality,
|
||||
Color.Lerp(GUIStyle.Red, GUIStyle.Green, CharacterHealth.DisplayedVitality / MaxVitality) * 0.8f * hudInfoAlpha,
|
||||
new Color(0.5f, 0.57f, 0.6f, 1.0f) * hudInfoAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsDead) { return; }
|
||||
|
||||
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
|
||||
if (healthBarMode != EnemyHealthBarMode.ShowAll)
|
||||
if (textlessSpeechBubble != null)
|
||||
{
|
||||
if (Controlled == null)
|
||||
Vector2 iconPos = pos - Vector2.UnitY * 5;
|
||||
|
||||
GUIStyle.SpeechBubbleIcon.Value.Sprite.Draw(spriteBatch, iconPos,
|
||||
textlessSpeechBubble.Color * Math.Min(textlessSpeechBubble.LifeTime, 1.0f), 0.0f,
|
||||
Math.Min(textlessSpeechBubble.LifeTime, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowSpeechBubble(Color color, string text)
|
||||
{
|
||||
if (!GameSettings.CurrentConfig.ChatSpeechBubbles)
|
||||
{
|
||||
ShowTextlessSpeechBubble(1.0f, color);
|
||||
return;
|
||||
}
|
||||
float duration = MathHelper.Lerp(1.0f, 8.0f, Math.Min(text.Length / 100.0f, 1.0f));
|
||||
speechBubbles.Add(new SpeechBubble(this, duration, color, text));
|
||||
textlessSpeechBubble = null;
|
||||
}
|
||||
|
||||
public void ShowTextlessSpeechBubble(float duration, Color color)
|
||||
{
|
||||
if (speechBubbles.Any(sb => sb.Character == this)) { return; }
|
||||
if (textlessSpeechBubble == null)
|
||||
{
|
||||
textlessSpeechBubble = new SpeechBubble(this, duration, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
textlessSpeechBubble.Color = color;
|
||||
textlessSpeechBubble.LifeTime = Math.Max(textlessSpeechBubble.LifeTime, duration);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawSpeechBubbles(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform);
|
||||
foreach (var bubble in speechBubbles)
|
||||
{
|
||||
Vector2 iconPos = Timing.Interpolate(bubble.PrevPosition, bubble.Position);
|
||||
iconPos += Vector2.UnitY * bubble.MoveUpAmount;
|
||||
if (bubble.Submarine != null)
|
||||
{
|
||||
if (!IsOnPlayerTeam) { return; }
|
||||
iconPos += bubble.Submarine.DrawPosition;
|
||||
}
|
||||
else
|
||||
|
||||
float alpha = 1.0f;
|
||||
float mouseDist = Vector2.Distance(cam.WorldToScreen(iconPos), PlayerInput.MousePosition);
|
||||
//treat the size of the bubble from corner to corner as the
|
||||
float textSize = bubble.TextSize.Length();
|
||||
if (mouseDist < textSize)
|
||||
{
|
||||
if (!HumanAIController.IsFriendly(Controlled, this) ||
|
||||
(AIController is HumanAIController humanAi && humanAi.ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective && HumanAIController.IsFriendly(Controlled, combatObjective.Enemy)))
|
||||
{
|
||||
return;
|
||||
alpha *= Math.Max(mouseDist / textSize, 0.5f);
|
||||
}
|
||||
|
||||
iconPos.Y = -iconPos.Y;
|
||||
if (GUIStyle.SpeechBubbleIconSliced.Value is { } speechBubbleIconSliced)
|
||||
{
|
||||
Vector2 bubbleSize = bubble.TextSize + Vector2.One * GUI.IntScale(15);
|
||||
speechBubbleIconSliced.Draw(spriteBatch, new RectangleF(iconPos - bubbleSize / 2, bubbleSize), bubble.Color * Math.Min(bubble.LifeTime, 1.0f) * alpha);
|
||||
}
|
||||
GUI.DrawString(spriteBatch, iconPos - bubble.TextSize / 2, bubble.Text, bubble.Color * Math.Min(bubble.LifeTime, 1.0f) * alpha, font: GUIStyle.SmallFont);
|
||||
}
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
static partial void UpdateSpeechBubbles(float deltaTime)
|
||||
{
|
||||
for (int i = speechBubbles.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var bubble = speechBubbles[i];
|
||||
bubble.LifeTime -= deltaTime;
|
||||
if (bubble.LifeTime <= 0 || bubble.Character is { Removed: true })
|
||||
{
|
||||
speechBubbles.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
bubble.PrevPosition = bubble.Position;
|
||||
|
||||
Vector2 desiredPos = bubble.GetDesiredPosition();
|
||||
Vector2 diff = desiredPos - bubble.Position;
|
||||
float dist = diff.Length();
|
||||
//how far the bubble needs to be from the desired position to start moving
|
||||
const float MoveThreshold = 100.0f;
|
||||
const float MaxSpeed = 1000.0f;
|
||||
if (dist < 1)
|
||||
{
|
||||
bubble.Moving = false;
|
||||
}
|
||||
else if (dist > MoveThreshold || bubble.Moving)
|
||||
{
|
||||
Vector2 moveAmount = diff / dist * MathHelper.Clamp(dist * 5, 0, MaxSpeed) * deltaTime;
|
||||
//slower vertical movement (don't want to interfere too much with the bubbles floating up
|
||||
//and the overlap prevention which works vertically)
|
||||
moveAmount.Y *= 0.1f;
|
||||
bubble.Position += moveAmount;
|
||||
bubble.Moving = true;
|
||||
}
|
||||
|
||||
bubble.MoveUpAmount += deltaTime * 5.0f;
|
||||
//go through the newer bubbles, move this one out of the way if one is overlapping
|
||||
for (int j = i + 1; j < speechBubbles.Count; j++)
|
||||
{
|
||||
var otherBubble = speechBubbles[j];
|
||||
{
|
||||
if (Math.Abs(bubble.Position.X - otherBubble.Position.X) < (bubble.TextSize.X + otherBubble.TextSize.X) / 2 &&
|
||||
Math.Abs(bubble.Position.Y - otherBubble.Position.Y) < (bubble.TextSize.Y + otherBubble.TextSize.Y) / 2 + 10)
|
||||
{
|
||||
bubble.Position += Vector2.UnitY * deltaTime * 50.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Params.ShowHealthBar && CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
|
||||
{
|
||||
hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f));
|
||||
|
||||
Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y);
|
||||
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f),
|
||||
CharacterHealth.DisplayedVitality / MaxVitality,
|
||||
Color.Lerp(GUIStyle.Red, GUIStyle.Green, CharacterHealth.DisplayedVitality / MaxVitality) * 0.8f * hudInfoAlpha,
|
||||
new Color(0.5f, 0.57f, 0.6f, 1.0f) * hudInfoAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
public Color GetNameColor()
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Barotrauma
|
||||
{
|
||||
cachedHudTexts.Clear();
|
||||
}
|
||||
Identifier key = (textTag + keyBind).ToIdentifier();
|
||||
Identifier key = (textTag + keyBind + GameSettings.CurrentConfig.KeyMap.KeyBindText(keyBind)).ToIdentifier();
|
||||
if (cachedHudTexts.TryGetValue(key, out LocalizedString text)) { return text; }
|
||||
text = TextManager.GetWithVariable(textTag, "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(keyBind)).Value;
|
||||
cachedHudTexts.Add(key, text);
|
||||
@@ -246,6 +246,7 @@ namespace Barotrauma
|
||||
|
||||
public static void Update(float deltaTime, Character character, Camera cam)
|
||||
{
|
||||
|
||||
UpdateBossProgressBars(deltaTime);
|
||||
|
||||
if (GUI.DisableHUD)
|
||||
@@ -256,6 +257,11 @@ namespace Barotrauma
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (character.ShowInteractionLabels && character.ViewTarget == null)
|
||||
{
|
||||
InteractionLabelManager.Update(character, cam);
|
||||
}
|
||||
|
||||
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen)
|
||||
{
|
||||
@@ -375,7 +381,7 @@ namespace Barotrauma
|
||||
|
||||
static bool DrawIcon(Order o) =>
|
||||
o != null &&
|
||||
(!(o.TargetEntity is Item i) ||
|
||||
(o.TargetEntity is not Item i ||
|
||||
o.DrawIconWhenContained ||
|
||||
i.GetRootInventoryOwner() == i);
|
||||
}
|
||||
@@ -405,90 +411,115 @@ namespace Barotrauma
|
||||
if (!brokenItem.IsInteractable(character)) { continue; }
|
||||
float alpha = GetDistanceBasedIconAlpha(brokenItem);
|
||||
if (alpha <= 0.0f) { continue; }
|
||||
GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUIStyle.BrokenIcon.Value.Sprite,
|
||||
GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUIStyle.BrokenIcon.Value.Sprite,
|
||||
Color.Lerp(GUIStyle.Red, GUIStyle.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
|
||||
}
|
||||
|
||||
if (OrderPrefab.Prefabs.TryGet(Tags.DeconstructThis, out OrderPrefab deconstructOrder))
|
||||
{
|
||||
foreach (Item deconstructItem in Item.DeconstructItems)
|
||||
{
|
||||
if (deconstructItem.ParentInventory != null) { continue; }
|
||||
if (deconstructItem.OrderedToBeIgnored) { continue; }
|
||||
if (deconstructItem.Submarine != character.Submarine || !deconstructItem.IsInteractable(character)) { continue; }
|
||||
float alpha = GetDistanceBasedIconAlpha(deconstructItem, maxDistance: 450) * 0.7f;
|
||||
if (alpha <= 0.0f) { continue; }
|
||||
GUI.DrawIndicator(spriteBatch, deconstructItem.DrawPosition, cam, 100.0f, deconstructOrder.SymbolSprite,
|
||||
GUIStyle.Red, scaleMultiplier: 0.5f, overrideAlpha: alpha);
|
||||
}
|
||||
}
|
||||
|
||||
float GetDistanceBasedIconAlpha(ISpatialEntity target, float maxDistance = 1000.0f)
|
||||
{
|
||||
float dist = Vector2.Distance(character.WorldPosition, target.WorldPosition);
|
||||
return Math.Min((maxDistance - dist) / maxDistance * 2.0f, 1.0f);
|
||||
}
|
||||
|
||||
|
||||
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.HeldItems.None(it => it?.GetComponent<Sprayer>() != null)))
|
||||
{
|
||||
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
|
||||
if (!character.ShowInteractionLabels)
|
||||
{
|
||||
DrawCharacterHoverTexts(spriteBatch, cam, character);
|
||||
}
|
||||
|
||||
if (character.FocusedItem != null)
|
||||
{
|
||||
if (focusedItem != character.FocusedItem)
|
||||
if (character.FocusedCharacter is { CanBeSelected: true })
|
||||
{
|
||||
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
|
||||
RecreateHudTexts = true;
|
||||
DrawCharacterHoverTexts(spriteBatch, cam, character);
|
||||
}
|
||||
focusedItem = character.FocusedItem;
|
||||
}
|
||||
|
||||
if (focusedItem != null && focusedItemOverlayTimer > ItemOverlayDelay)
|
||||
{
|
||||
Vector2 circlePos = cam.WorldToScreen(focusedItem.DrawPosition);
|
||||
float circleSize = Math.Max(focusedItem.Rect.Width, focusedItem.Rect.Height) * 1.5f;
|
||||
circleSize = MathHelper.Clamp(circleSize, 45.0f, 100.0f) * Math.Min((focusedItemOverlayTimer - 1.0f) * 5.0f, 1.0f);
|
||||
if (circleSize > 0.0f)
|
||||
|
||||
if (character.FocusedItem != null)
|
||||
{
|
||||
Vector2 scale = new Vector2(circleSize / GUIStyle.FocusIndicator.FrameSize.X);
|
||||
GUIStyle.FocusIndicator.Draw(spriteBatch,
|
||||
(int)((focusedItemOverlayTimer - 1.0f) * GUIStyle.FocusIndicator.FrameCount * 3.0f),
|
||||
circlePos,
|
||||
Color.LightBlue * 0.3f,
|
||||
origin: GUIStyle.FocusIndicator.FrameSize.ToVector2() / 2,
|
||||
rotate: (float)Timing.TotalTime,
|
||||
scale: scale);
|
||||
}
|
||||
|
||||
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
|
||||
{
|
||||
bool hudTextsContextual = PlayerInput.IsShiftDown();
|
||||
if (RecreateHudTexts || lastHudTextsContextual != hudTextsContextual)
|
||||
if (focusedItem != character.FocusedItem)
|
||||
{
|
||||
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
|
||||
RecreateHudTexts = true;
|
||||
lastHudTextsContextual = hudTextsContextual;
|
||||
}
|
||||
var hudTexts = focusedItem.GetHUDTexts(character, RecreateHudTexts);
|
||||
RecreateHudTexts = false;
|
||||
|
||||
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
|
||||
|
||||
Vector2 textSize = GUIStyle.Font.MeasureString(hudTexts.First().Text);
|
||||
Vector2 largeTextSize = GUIStyle.SubHeadingFont.MeasureString(hudTexts.First().Text);
|
||||
|
||||
Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition);
|
||||
startPos.Y -= (hudTexts.Count + 1) * textSize.Y;
|
||||
if (focusedItem.Sprite != null)
|
||||
focusedItem = character.FocusedItem;
|
||||
}
|
||||
|
||||
if (focusedItem != null && focusedItemOverlayTimer > ItemOverlayDelay)
|
||||
{
|
||||
Vector2 circlePos = cam.WorldToScreen(focusedItem.DrawPosition);
|
||||
float circleSize = Math.Max(focusedItem.Rect.Width, focusedItem.Rect.Height) * 1.5f;
|
||||
circleSize = MathHelper.Clamp(circleSize, 45.0f, 100.0f) * Math.Min((focusedItemOverlayTimer - 1.0f) * 5.0f, 1.0f);
|
||||
if (circleSize > 0.0f)
|
||||
{
|
||||
startPos.X += (int)(circleSize * 0.4f * dir);
|
||||
startPos.Y -= (int)(circleSize * 0.4f);
|
||||
Vector2 scale = new Vector2(circleSize / GUIStyle.FocusIndicator.FrameSize.X);
|
||||
GUIStyle.FocusIndicator.Draw(spriteBatch,
|
||||
(int)((focusedItemOverlayTimer - 1.0f) * GUIStyle.FocusIndicator.FrameCount * 3.0f),
|
||||
circlePos,
|
||||
Color.LightBlue * 0.3f,
|
||||
origin: GUIStyle.FocusIndicator.FrameSize.ToVector2() / 2,
|
||||
rotate: (float)Timing.TotalTime,
|
||||
scale: scale);
|
||||
}
|
||||
|
||||
Vector2 textPos = startPos;
|
||||
if (dir == -1) { textPos.X -= largeTextSize.X; }
|
||||
|
||||
float alpha = MathHelper.Clamp((focusedItemOverlayTimer - ItemOverlayDelay) * 2.0f, 0.0f, 1.0f);
|
||||
|
||||
GUI.DrawString(spriteBatch, textPos, hudTexts.First().Text, hudTexts.First().Color * alpha, Color.Black * alpha * 0.7f, 2, font: GUIStyle.SubHeadingFont, ForceUpperCase.No);
|
||||
startPos.X += dir * 10.0f * GUI.Scale;
|
||||
textPos.X += dir * 10.0f * GUI.Scale;
|
||||
textPos.Y += largeTextSize.Y;
|
||||
foreach (ColoredText coloredText in hudTexts.Skip(1))
|
||||
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
|
||||
{
|
||||
if (dir == -1) textPos.X = (int)(startPos.X - GUIStyle.SmallFont.MeasureString(coloredText.Text).X);
|
||||
GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUIStyle.SmallFont);
|
||||
textPos.Y += textSize.Y;
|
||||
}
|
||||
}
|
||||
bool hudTextsContextual = PlayerInput.KeyDown(InputType.ContextualCommand);
|
||||
if (RecreateHudTexts || lastHudTextsContextual != hudTextsContextual)
|
||||
{
|
||||
RecreateHudTexts = true;
|
||||
lastHudTextsContextual = hudTextsContextual;
|
||||
}
|
||||
var hudTexts = focusedItem.GetHUDTexts(character, RecreateHudTexts);
|
||||
RecreateHudTexts = false;
|
||||
|
||||
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
|
||||
|
||||
Vector2 textSize = GUIStyle.Font.MeasureString(hudTexts.First().Text);
|
||||
Vector2 largeTextSize = GUIStyle.SubHeadingFont.MeasureString(hudTexts.First().Text);
|
||||
|
||||
Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition);
|
||||
startPos.Y -= (hudTexts.Count + 1) * textSize.Y;
|
||||
if (focusedItem.Sprite != null)
|
||||
{
|
||||
startPos.X += (int)(circleSize * 0.4f * dir);
|
||||
startPos.Y -= (int)(circleSize * 0.4f);
|
||||
}
|
||||
|
||||
Vector2 textPos = startPos;
|
||||
if (dir == -1) { textPos.X -= largeTextSize.X; }
|
||||
|
||||
float alpha = MathHelper.Clamp((focusedItemOverlayTimer - ItemOverlayDelay) * 2.0f, 0.0f, 1.0f);
|
||||
|
||||
GUI.DrawString(spriteBatch, textPos, hudTexts.First().Text, hudTexts.First().Color * alpha, Color.Black * alpha * 0.7f, 2, font: GUIStyle.SubHeadingFont, ForceUpperCase.No);
|
||||
startPos.X += dir * 10.0f * GUI.Scale;
|
||||
textPos.X += dir * 10.0f * GUI.Scale;
|
||||
textPos.Y += largeTextSize.Y;
|
||||
foreach (ColoredText coloredText in hudTexts.Skip(1))
|
||||
{
|
||||
if (dir == -1)
|
||||
{
|
||||
textPos.X = (int)(startPos.X - GUIStyle.SmallFont.MeasureString(coloredText.Text).X);
|
||||
}
|
||||
GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUIStyle.SmallFont);
|
||||
textPos.Y += textSize.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (character.ShowInteractionLabels && character.ViewTarget == null)
|
||||
{
|
||||
InteractionLabelManager.DrawLabels(spriteBatch, cam, character);
|
||||
}
|
||||
|
||||
foreach (HUDProgressBar progressBar in character.HUDProgressBars.Values)
|
||||
@@ -673,6 +704,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static bool MouseOnCharacterPortrait()
|
||||
{
|
||||
if (Character.Controlled == null) { return false; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -46,6 +46,7 @@ namespace Barotrauma
|
||||
ContentPath tintMaskPath = maskElement.GetAttributeContentPath("texture");
|
||||
if (!tintMaskPath.IsNullOrEmpty())
|
||||
{
|
||||
VerifySpriteTagsLoaded();
|
||||
tintMask = new Sprite(maskElement, file: Limb.GetSpritePath(tintMaskPath, this));
|
||||
tintHighlightThreshold = maskElement.GetAttributeFloat("highlightthreshold", 0.6f);
|
||||
tintHighlightMultiplier = maskElement.GetAttributeFloat("highlightmultiplier", 0.8f);
|
||||
@@ -61,7 +62,7 @@ namespace Barotrauma
|
||||
//Stretch = true
|
||||
};
|
||||
|
||||
var headerArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.322f), paddedFrame.RectTransform), isHorizontal: true);
|
||||
var headerArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.4f), paddedFrame.RectTransform), isHorizontal: true);
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.425f, 1.0f), headerArea.RectTransform),
|
||||
onDraw: (sb, component) => DrawInfoFrameCharacterIcon(sb, component.Rect));
|
||||
@@ -185,7 +186,7 @@ namespace Barotrauma
|
||||
|
||||
private void DrawInfoFrameCharacterIcon(SpriteBatch sb, Rectangle componentRect)
|
||||
{
|
||||
if (_headSprite == null) { return; }
|
||||
if (HeadSprite == null) { return; }
|
||||
Vector2 targetAreaSize = componentRect.Size.ToVector2();
|
||||
float scale = Math.Min(targetAreaSize.X / _headSprite.size.X, targetAreaSize.Y / _headSprite.size.Y);
|
||||
DrawIcon(sb, componentRect.Location.ToVector2() + _headSprite.size / 2 * scale, targetAreaSize);
|
||||
@@ -537,8 +538,7 @@ namespace Barotrauma
|
||||
Color skinColor = inc.ReadColorR8G8B8();
|
||||
Color hairColor = inc.ReadColorR8G8B8();
|
||||
Color facialHairColor = inc.ReadColorR8G8B8();
|
||||
|
||||
string ragdollFile = inc.ReadString();
|
||||
|
||||
Identifier npcId = inc.ReadIdentifier();
|
||||
|
||||
Identifier factionId = inc.ReadIdentifier();
|
||||
@@ -560,18 +560,15 @@ namespace Barotrauma
|
||||
throw new Exception($"Error while reading {nameof(CharacterInfo)} received from the server: could not find a job prefab with the identifier \"{jobIdentifier}\".");
|
||||
}
|
||||
byte skillCount = inc.ReadByte();
|
||||
List<SkillPrefab> jobSkills = jobPrefab?.Skills.OrderBy(s => s.Identifier).ToList();
|
||||
for (int i = 0; i < skillCount; i++)
|
||||
{
|
||||
Identifier skillIdentifier = inc.ReadIdentifier();
|
||||
float skillLevel = inc.ReadSingle();
|
||||
if (jobSkills != null && i < jobSkills.Count)
|
||||
{
|
||||
skillLevels.Add(jobSkills[i].Identifier, skillLevel);
|
||||
}
|
||||
skillLevels.Add(skillIdentifier, skillLevel);
|
||||
}
|
||||
}
|
||||
|
||||
CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, ragdollFile, variant, npcIdentifier: npcId)
|
||||
CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, variant, npcIdentifier: npcId)
|
||||
{
|
||||
ID = infoID,
|
||||
MinReputationToHire = (factionId, minReputationToHire)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
@@ -532,6 +533,47 @@ namespace Barotrauma
|
||||
bool removeOnDeath = msg.ReadBoolean();
|
||||
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
|
||||
}
|
||||
break;
|
||||
case EventType.LatchOntoTarget:
|
||||
bool attached = msg.ReadBoolean();
|
||||
if (attached)
|
||||
{
|
||||
Vector2 characterSimPos = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
Vector2 attachSurfaceNormal = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
Vector2 attachPos = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
int attachWallIndex = msg.ReadInt32();
|
||||
UInt16 attachTargetId = msg.ReadUInt16();
|
||||
|
||||
if (AIController is EnemyAIController { LatchOntoAI: { } latchOntoAi })
|
||||
{
|
||||
var attachTargetEntity = FindEntityByID(attachTargetId);
|
||||
switch (attachTargetEntity)
|
||||
{
|
||||
case Character attachTargetCharacter:
|
||||
latchOntoAi.SetAttachTarget(attachTargetCharacter);
|
||||
break;
|
||||
case Structure attachTargetStructure:
|
||||
latchOntoAi.SetAttachTarget(attachTargetStructure, attachPos, attachSurfaceNormal);
|
||||
break;
|
||||
default:
|
||||
var allLevelWalls = Level.Loaded.GetAllCells();
|
||||
if (attachWallIndex >= 0 && attachWallIndex <= allLevelWalls.Count)
|
||||
{
|
||||
latchOntoAi.SetAttachTarget(allLevelWalls[attachWallIndex]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
latchOntoAi.AttachToBody(attachPos, attachSurfaceNormal, characterSimPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AIController is EnemyAIController { LatchOntoAI: { } latchOntoAi })
|
||||
{
|
||||
latchOntoAi.DeattachFromBody(reset: false);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
|
||||
@@ -858,8 +858,8 @@ namespace Barotrauma
|
||||
foreach (GUIComponent component in recommendedTreatmentContainer.Content.Children)
|
||||
{
|
||||
var treatmentButton = component.GetChild<GUIButton>();
|
||||
if (!(treatmentButton?.UserData is ItemPrefab itemPrefab)) { continue; }
|
||||
var matchingItem = Character.Controlled.Inventory.FindItem(it => it.Prefab == itemPrefab, recursive: true);
|
||||
if (treatmentButton?.UserData is not ItemPrefab itemPrefab) { continue; }
|
||||
var matchingItem = AIObjectiveRescue.FindMedicalItem(Character.Controlled.Inventory, itemPrefab.Identifier);
|
||||
treatmentButton.Enabled = matchingItem != null;
|
||||
if (treatmentButton.Enabled && treatmentButton.State == GUIComponent.ComponentState.Hover)
|
||||
{
|
||||
@@ -1387,7 +1387,6 @@ namespace Barotrauma
|
||||
//float = suitability
|
||||
Dictionary<Identifier, float> treatmentSuitability = new Dictionary<Identifier, float>();
|
||||
GetSuitableTreatments(treatmentSuitability,
|
||||
normalize: true,
|
||||
user: Character.Controlled,
|
||||
ignoreHiddenAfflictions: true,
|
||||
limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex));
|
||||
@@ -1421,9 +1420,12 @@ namespace Barotrauma
|
||||
int count = 0;
|
||||
foreach (KeyValuePair<Identifier, float> treatment in treatmentSuitabilities)
|
||||
{
|
||||
//don't list negative treatments
|
||||
if (treatment.Value < 0) { continue; }
|
||||
|
||||
count++;
|
||||
if (count > 5) { break; }
|
||||
if (!(MapEntityPrefab.Find(name: null, identifier: treatment.Key, showErrorMessages: false) is ItemPrefab item)) { continue; }
|
||||
if (MapEntityPrefab.FindByIdentifier(treatment.Key) is not ItemPrefab item) { continue; }
|
||||
|
||||
var itemSlot = new GUIFrame(new RectTransform(new Vector2(1.0f / 6.0f, 1.0f), recommendedTreatmentContainer.Content.RectTransform, Anchor.TopLeft),
|
||||
style: null)
|
||||
@@ -1439,7 +1441,7 @@ namespace Barotrauma
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (userdata is not ItemPrefab itemPrefab) { return false; }
|
||||
var item = Character.Controlled.Inventory.FindItem(it => it.Prefab == itemPrefab, recursive: true);
|
||||
var item = AIObjectiveRescue.FindMedicalItem(Character.Controlled.Inventory, it => it.Prefab == itemPrefab);
|
||||
if (item == null) { return false; }
|
||||
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex);
|
||||
item.ApplyTreatment(Character.Controlled, Character, targetLimb);
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
#nullable enable
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
public static class InteractionLabelManager
|
||||
{
|
||||
|
||||
private class LabelData
|
||||
{
|
||||
private readonly Camera drawCamera;
|
||||
public readonly Item Item;
|
||||
|
||||
public RectangleF TextRect { get; set; }
|
||||
|
||||
public readonly Vector2 OriginalItemPosition;
|
||||
|
||||
public bool OverlapPreventionDone;
|
||||
|
||||
public LabelData(Item item, RectangleF textRect, Camera drawCamera)
|
||||
{
|
||||
Item = item;
|
||||
TextRect = textRect;
|
||||
OriginalItemPosition = item.Position;
|
||||
this.drawCamera = drawCamera;
|
||||
}
|
||||
|
||||
public RectangleF GetScreenDrawRect(Camera cam)
|
||||
{
|
||||
float scale = cam.Zoom;
|
||||
RectangleF screenDrawRect = TextRect;
|
||||
screenDrawRect.Location = drawCamera
|
||||
.WorldToScreen(screenDrawRect.Location + (Item.Submarine?.DrawPosition ?? Vector2.Zero));
|
||||
|
||||
return new RectangleF(
|
||||
screenDrawRect.X,
|
||||
screenDrawRect.Y,
|
||||
screenDrawRect.Width * scale,
|
||||
screenDrawRect.Height * scale);
|
||||
}
|
||||
|
||||
public Vector2 GetInteractableDrawPositionScreen()
|
||||
{
|
||||
return drawCamera.WorldToScreen(Item.DrawPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<LabelData> labels = new();
|
||||
|
||||
private const int TextBoxMarginPx = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier on the scale of the labels. Ad-hoc formula: since the zoom affects the size of the labels,
|
||||
/// and high resolutions are more zoomed in to keep the view range the same, let's scale down the labels on large resolutions to compensate.
|
||||
/// </summary>
|
||||
private static float LabelScale => 1.0f / GUI.Scale;
|
||||
|
||||
private static InteractionLabelDisplayMode displayMode;
|
||||
|
||||
private static int graphicsWidth, graphicsHeight;
|
||||
|
||||
private static bool shouldRecalculate;
|
||||
private static bool recalculateEverything;
|
||||
|
||||
private static readonly List<Item> interactablesInRange = new();
|
||||
|
||||
internal static Item? HoveredItem { get; private set; }
|
||||
|
||||
internal static void RefreshInteractablesInRange(List<Item> interactables)
|
||||
{
|
||||
interactablesInRange.Clear();
|
||||
interactablesInRange.AddRange(interactables);
|
||||
|
||||
shouldRecalculate = true;
|
||||
}
|
||||
|
||||
private static void RecalculateLabelPositions(Camera cam, Character character)
|
||||
{
|
||||
if (recalculateEverything)
|
||||
{
|
||||
labels.Clear();
|
||||
recalculateEverything = false;
|
||||
}
|
||||
|
||||
labels.RemoveAll(l => !interactablesInRange.Contains(l.Item));
|
||||
|
||||
// for every interactable, create a label data object with relevant info for real-time drawing
|
||||
foreach (var interactableInRange in interactablesInRange)
|
||||
{
|
||||
// this removes the hidden vents from the list
|
||||
if (interactableInRange.HasTag(Tags.HiddenItemContainer)) { continue; }
|
||||
|
||||
// filter out items depending on visibility filter setting
|
||||
switch (displayMode)
|
||||
{
|
||||
case InteractionLabelDisplayMode.InteractionAvailable when !interactableInRange.HasVisibleInteraction(character):
|
||||
case InteractionLabelDisplayMode.LooseItems when !IsLooseItem(interactableInRange):
|
||||
continue;
|
||||
}
|
||||
|
||||
RectangleF textRect = GetLabelRect(interactableInRange, cam);
|
||||
|
||||
if (labels.None(l => l.Item == interactableInRange))
|
||||
{
|
||||
var labelData = new LabelData(interactableInRange, textRect, cam);
|
||||
labels.Add(labelData);
|
||||
}
|
||||
}
|
||||
|
||||
PreventInteractionLabelOverlap(centerPos: character.Position);
|
||||
}
|
||||
|
||||
private static bool IsLooseItem(Item item)
|
||||
{
|
||||
bool hasActivePhysics = item.body is { Enabled: true };
|
||||
bool hasPickableComponent = item.GetComponent<Pickable>() != null;
|
||||
return hasActivePhysics && hasPickableComponent;
|
||||
}
|
||||
|
||||
private static RectangleF GetLabelRect(Item item, Camera cam)
|
||||
{
|
||||
// create rectangle for overlap prevention
|
||||
Vector2 itemTextSizeScreen = GUIStyle.SubHeadingFont.MeasureString(item.Name) * LabelScale;
|
||||
Vector2 interactablePosScreen = cam.WorldToScreen(item.Position);
|
||||
RectangleF textRect = new RectangleF(interactablePosScreen.X, interactablePosScreen.Y, itemTextSizeScreen.X, itemTextSizeScreen.Y);
|
||||
// center the rectangle on the item
|
||||
textRect.X -= textRect.Width / 2;
|
||||
textRect.Y += textRect.Height / 2;
|
||||
|
||||
// inflate by a bit, because the text is drawn with padding
|
||||
textRect.Inflate(TextBoxMarginPx * LabelScale, TextBoxMarginPx * LabelScale);
|
||||
|
||||
// the rect has screen space size, and sub-relative position
|
||||
textRect.Location = cam.ScreenToWorld(textRect.Location);
|
||||
return textRect;
|
||||
}
|
||||
|
||||
private static void PreventInteractionLabelOverlap(Vector2 centerPos)
|
||||
{
|
||||
//sort by distance from "centerPos": moving labels further away from the character (or whatever the center is) is preferred
|
||||
labels.Sort((l1, l2) =>
|
||||
Vector2.DistanceSquared(l1.TextRect.Center, centerPos).CompareTo(
|
||||
Vector2.DistanceSquared(l2.TextRect.Center, centerPos)));
|
||||
|
||||
const float MoveStep = 10.0f;
|
||||
bool intersections = true;
|
||||
int iterations = 0;
|
||||
int maxIterations = System.Math.Max(labels.Count * labels.Count, 100);
|
||||
|
||||
while (intersections && iterations < maxIterations)
|
||||
{
|
||||
intersections = false;
|
||||
foreach (var label in labels)
|
||||
{
|
||||
if (label.OverlapPreventionDone) { continue; }
|
||||
foreach (var otherLabel in labels)
|
||||
{
|
||||
if (label == otherLabel) { continue; }
|
||||
|
||||
//allow labels to overlap if there's multiple instances of the same item at (roughly) the same position
|
||||
if (label.Item.Prefab == otherLabel.Item.Prefab &&
|
||||
Vector2.DistanceSquared(label.Item.WorldPosition, otherLabel.Item.WorldPosition) < 1.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!label.TextRect.Intersects(otherLabel.TextRect))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
intersections = true;
|
||||
Vector2 moveAmount = Vector2.Normalize(label.TextRect.Center - centerPos) * MoveStep;
|
||||
label.TextRect = new RectangleF(label.TextRect.Location + moveAmount, label.TextRect.Size);
|
||||
}
|
||||
if (intersections) { break; }
|
||||
}
|
||||
iterations++;
|
||||
}
|
||||
|
||||
foreach (var labelData in labels)
|
||||
{
|
||||
labelData.OverlapPreventionDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetMouseHoveredLabelIndex(Camera cam)
|
||||
{
|
||||
for (int i = 0; i < labels.Count; i++)
|
||||
{
|
||||
var labelData = labels[i];
|
||||
var drawRect = labelData.GetScreenDrawRect(cam);
|
||||
if (drawRect.Contains(PlayerInput.MousePosition))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static bool RefreshSettings()
|
||||
{
|
||||
bool settingsChanged = false;
|
||||
|
||||
if (GameSettings.CurrentConfig.InteractionLabelDisplayMode != displayMode)
|
||||
{
|
||||
displayMode = GameSettings.CurrentConfig.InteractionLabelDisplayMode;
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
if (GameMain.GraphicsWidth != graphicsWidth || GameMain.GraphicsHeight != graphicsHeight)
|
||||
{
|
||||
graphicsWidth = GameMain.GraphicsWidth;
|
||||
graphicsHeight = GameMain.GraphicsHeight;
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
return settingsChanged;
|
||||
}
|
||||
|
||||
internal static void Update(Character character, Camera cam)
|
||||
{
|
||||
if (RefreshSettings()) { shouldRecalculate = true; recalculateEverything = true; }
|
||||
|
||||
if (shouldRecalculate)
|
||||
{
|
||||
RecalculateLabelPositions(cam, character);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void DrawLabels(SpriteBatch spriteBatch, Camera cam, Character character)
|
||||
{
|
||||
//if any item changes subs or moves significantly, we need to recalculate the label position
|
||||
foreach (var label in labels)
|
||||
{
|
||||
const float MoveThreshold = 150.0f;
|
||||
if (Vector2.DistanceSquared(label.OriginalItemPosition, label.Item.Position) > MoveThreshold * MoveThreshold)
|
||||
{
|
||||
label.TextRect = GetLabelRect(label.Item, cam);
|
||||
}
|
||||
}
|
||||
|
||||
// find out if mouse is on top of any of the labels
|
||||
int mouseOnLabelIndex = GetMouseHoveredLabelIndex(cam);
|
||||
bool isMouseOnLabel = mouseOnLabelIndex >= 0;
|
||||
|
||||
const float LineAlpha = 0.5f;
|
||||
|
||||
if (!isMouseOnLabel)
|
||||
{
|
||||
HoveredItem = null;
|
||||
}
|
||||
|
||||
// draw order: draw lines for labels first
|
||||
for (int i = 0; i < labels.Count; i++)
|
||||
{
|
||||
// Skip the box that the mouse is on, it will be drawn last
|
||||
if (i == mouseOnLabelIndex) { continue; }
|
||||
|
||||
DrawLineForLabel(spriteBatch, cam, labels[i], GUIStyle.InteractionLabelColor * LineAlpha);
|
||||
}
|
||||
|
||||
// Then draw labels
|
||||
for (int i = 0; i < labels.Count; i++)
|
||||
{
|
||||
// Skip the box that the mouse is on, it will be drawn last
|
||||
if (i == mouseOnLabelIndex) { continue; }
|
||||
|
||||
DrawLabelForItem(spriteBatch, cam, labels[i], GUIStyle.InteractionLabelColor);
|
||||
}
|
||||
|
||||
// Draw the label and line that the mouse is on last (for draw order)
|
||||
if (isMouseOnLabel)
|
||||
{
|
||||
var labelData = labels[mouseOnLabelIndex];
|
||||
|
||||
HoveredItem = labelData.Item;
|
||||
|
||||
DrawLineForLabel(spriteBatch, cam, labelData, GUIStyle.InteractionLabelHoverColor * LineAlpha);
|
||||
DrawLabelForItem(spriteBatch, cam,labelData, GUIStyle.InteractionLabelHoverColor);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawLineForLabel(SpriteBatch spriteBatch, Camera cam, LabelData labelData, Color color)
|
||||
{
|
||||
var drawRect = labelData.GetScreenDrawRect(cam);
|
||||
// deflate by one pixel to avoid gap between line and box graphic edge
|
||||
const int lineAnchorInsetPx = 1;
|
||||
var deflateAmount = lineAnchorInsetPx * GUI.Scale;
|
||||
deflateAmount = MathHelper.Max(deflateAmount * Screen.Selected.Cam.Zoom, lineAnchorInsetPx);
|
||||
drawRect.Inflate(-deflateAmount, -deflateAmount);
|
||||
|
||||
var itemDrawPosScreen = labelData.GetInteractableDrawPositionScreen();
|
||||
|
||||
// if item position is inside the box, don't draw a line
|
||||
if (drawRect.Contains(itemDrawPosScreen)) { return; }
|
||||
|
||||
// find the point on the box edge that is closest to the item
|
||||
Vector2 textLineAnchorScreenPos = new Vector2(
|
||||
MathHelper.Clamp(itemDrawPosScreen.X, drawRect.Left, drawRect.Right),
|
||||
MathHelper.Clamp(itemDrawPosScreen.Y, drawRect.Top, drawRect.Bottom));
|
||||
|
||||
// draw line from label to item in the world
|
||||
GUI.DrawLine(spriteBatch, textLineAnchorScreenPos, itemDrawPosScreen, color, depth: 0f, width: 2f);
|
||||
}
|
||||
|
||||
private static void DrawLabelForItem(SpriteBatch spriteBatch, Camera cam, LabelData labelData, Color color)
|
||||
{
|
||||
float scale = Screen.Selected.Cam.Zoom * LabelScale;
|
||||
|
||||
var textDrawRect = labelData.GetScreenDrawRect(cam);
|
||||
RectangleF backgroundRect = textDrawRect;
|
||||
|
||||
// remove margin from the box the text is drawn in
|
||||
textDrawRect.Inflate(-TextBoxMarginPx * scale, -TextBoxMarginPx * scale);
|
||||
Vector2 textDrawPosScreen = new Vector2(textDrawRect.X, textDrawRect.Y);
|
||||
|
||||
GUIStyle.InteractionLabelBackground.Draw(spriteBatch, backgroundRect, color * 0.7f);
|
||||
|
||||
GUIStyle.SubHeadingFont.DrawString(spriteBatch,
|
||||
labelData.Item.Name,
|
||||
textDrawPosScreen, color, rotation: 0, origin: Vector2.Zero, scale, spriteEffects: SpriteEffects.None, layerDepth: 0.0f,
|
||||
forceUpperCase: ForceUpperCase.No);
|
||||
}
|
||||
}
|
||||
@@ -483,9 +483,11 @@ namespace Barotrauma
|
||||
//2. check if the base prefab defines the texture
|
||||
if (texturePath.IsNullOrEmpty() && !character.Prefab.VariantOf.IsEmpty)
|
||||
{
|
||||
Identifier speciesName = character.GetBaseCharacterSpeciesName();
|
||||
RagdollParams parentRagdollParams = character.IsHumanoid ?
|
||||
RagdollParams.GetRagdollParams<HumanRagdollParams>(character.Prefab.VariantOf) :
|
||||
RagdollParams.GetRagdollParams<FishRagdollParams>(character.Prefab.VariantOf);
|
||||
RagdollParams.GetDefaultRagdollParams<HumanRagdollParams>(speciesName, character.Params, character.Prefab.ContentPackage) :
|
||||
RagdollParams.GetDefaultRagdollParams<FishRagdollParams>(speciesName, character.Params, character.Prefab.ContentPackage);
|
||||
|
||||
texturePath = parentRagdollParams.OriginalElement?.GetAttributeContentPath("texture");
|
||||
}
|
||||
//3. "default case", get the texture from this character's XML
|
||||
@@ -514,8 +516,8 @@ namespace Barotrauma
|
||||
if (characterInfo != null)
|
||||
{
|
||||
spritePath = characterInfo.ReplaceVars(spritePath);
|
||||
|
||||
if (characterInfo.HeadSprite != null && characterInfo.SpriteTags.Any())
|
||||
characterInfo.VerifySpriteTagsLoaded();
|
||||
if (characterInfo.SpriteTags.Any())
|
||||
{
|
||||
string tags = "";
|
||||
characterInfo.SpriteTags.ForEach(tag => tags += $"[{tag}]");
|
||||
@@ -695,7 +697,7 @@ namespace Barotrauma
|
||||
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null, bool disableDeformations = false)
|
||||
{
|
||||
var spriteParams = Params.GetSprite();
|
||||
if (spriteParams == null) { return; }
|
||||
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;
|
||||
@@ -724,6 +726,8 @@ namespace Barotrauma
|
||||
|
||||
color = overrideColor ?? color;
|
||||
blankColor = overrideColor ?? blankColor;
|
||||
color *= Alpha;
|
||||
blankColor *= Alpha;
|
||||
|
||||
if (isSevered)
|
||||
{
|
||||
@@ -779,7 +783,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
bool useTintMask = TintMask != null && spriteBatch.GetCurrentEffect() is null;
|
||||
if (useTintMask)
|
||||
if (useTintMask && Sprite?.Texture != null && TintMask?.Texture != null)
|
||||
{
|
||||
tintEffectParams.Effect ??= GameMain.GameScreen.ThresholdTintEffect;
|
||||
tintEffectParams.Params ??= new Dictionary<string, object>();
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Barotrauma
|
||||
bool isEditor = Screen.Selected is { IsEditor: true };
|
||||
|
||||
GUILayoutGroup titleHolder = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), listBox.Content.RectTransform));
|
||||
new GUITextBlock(new RectTransform(Vector2.One, titleHolder.RectTransform), Item.Name, font: GUIStyle.LargeFont)
|
||||
new GUITextBlock(new RectTransform(Vector2.One, titleHolder.RectTransform), Item.Prefab.Name, font: GUIStyle.LargeFont)
|
||||
{
|
||||
TextColor = Color.White,
|
||||
Color = Color.Black
|
||||
@@ -84,7 +84,10 @@ namespace Barotrauma
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listBox.Content.RectTransform), style: "HorizontalLine");
|
||||
|
||||
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame: !isEditor, showName: false, titleFont: GUIStyle.SubHeadingFont);
|
||||
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame: !isEditor, showName: false, titleFont: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
Readonly = CircuitBox.Locked
|
||||
};
|
||||
fieldCount += componentEditor.Fields.Count;
|
||||
|
||||
ic.CreateEditingHUD(componentEditor);
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal sealed partial class CircuitBoxLabelNode
|
||||
{
|
||||
private CircuitBoxLabel headerLabel;
|
||||
private readonly GUITextBlock bodyLabel;
|
||||
private const string PromptUserData = "LabelEditPrompt";
|
||||
|
||||
public override void DrawHeader(SpriteBatch spriteBatch, RectangleF rect, Color color)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, new Vector2(rect.X + CircuitBoxSizes.NodeHeaderTextPadding, rect.Center.Y - headerLabel.Size.Y / 2f), headerLabel.Value, GUIStyle.TextColorNormal, font: GUIStyle.LargeFont);
|
||||
}
|
||||
|
||||
public override void DrawBody(SpriteBatch spriteBatch, RectangleF rect, Color color)
|
||||
{
|
||||
bodyLabel.TextOffset = rect.Location - bodyLabel.Rect.Location.ToVector2() + new Vector2(CircuitBoxSizes.NodeBodyTextPadding);
|
||||
bodyLabel.DrawManually(spriteBatch);
|
||||
}
|
||||
|
||||
public override void OnResized(RectangleF rect)
|
||||
=> UpdateTextSizes(rect);
|
||||
|
||||
private void UpdateTextSizes(RectangleF rect)
|
||||
{
|
||||
var size = new Point((int)rect.Width - CircuitBoxSizes.NodeBodyTextPadding * 2, (int)rect.Height - CircuitBoxSizes.NodeBodyTextPadding * 2);
|
||||
bodyLabel.RectTransform.NonScaledSize = size;
|
||||
bodyLabel.Text = GetLocalizedText(BodyText);
|
||||
if (bodyLabel.Font != null)
|
||||
{
|
||||
bodyLabel.Text = ToolBox.LimitStringHeight(bodyLabel.WrappedText.Value, bodyLabel.Font!, size.Y);
|
||||
}
|
||||
headerLabel = new CircuitBoxLabel(ToolBox.LimitString(GetLocalizedText(HeaderText), GUIStyle.LargeFont, size.X), GUIStyle.LargeFont);
|
||||
|
||||
static LocalizedString GetLocalizedText(NetLimitedString text) => TextManager.Get(text.Value).Fallback(text.Value);
|
||||
}
|
||||
|
||||
public void PromptEditText(GUIComponent parent)
|
||||
{
|
||||
Color newColor = Color;
|
||||
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.3f, 0.8f), backgroundBlocker.RectTransform, Anchor.Center), isHorizontal: false, childAnchor: Anchor.TopCenter);
|
||||
|
||||
GUILayoutGroup colorLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), mainLayout.RectTransform));
|
||||
new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), colorLayout.RectTransform)) { IgnoreLayoutGroups = true };
|
||||
GUILayoutGroup colorArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), colorLayout.RectTransform), isHorizontal: true);
|
||||
|
||||
GUIFrame labelArea = new(new RectTransform(new Vector2(1f, 0.65f), mainLayout.RectTransform, Anchor.Center));
|
||||
|
||||
GUIFrame header = new GUIFrame(new RectTransform(new Vector2(1f, 0.15f), labelArea.RectTransform, Anchor.TopLeft), style: "CircuitBoxTop");
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Vector2(1f, 0.86f), labelArea.RectTransform, Anchor.BottomLeft), style: "CircuitBoxFrame");
|
||||
header.Color = frame.Color = Color;
|
||||
|
||||
GUITextBox headerTextBox = new GUITextBox(new RectTransform(Vector2.One, header.RectTransform, Anchor.Center), text: HeaderText.Value, font: headerLabel.Font, style: "GUITextBoxNoStyle")
|
||||
{
|
||||
MaxTextLength = NetLimitedString.MaxLength,
|
||||
Text = HeaderText.Value
|
||||
};
|
||||
|
||||
GUITextBox bodyTextBox = new GUITextBox(new RectTransform(ToolBox.PaddingSizeParentRelative(frame.RectTransform, 0.95f), frame.RectTransform, Anchor.Center), text: BodyText.Value, font: GUIStyle.Font, style: "GUITextBoxNoStyle", textAlignment: Alignment.TopLeft, wrap: true)
|
||||
{
|
||||
MaxTextLength = NetLimitedString.MaxLength
|
||||
};
|
||||
|
||||
bodyTextBox.OnEnterPressed += (textBox, text) =>
|
||||
{
|
||||
int caretIndex = textBox.CaretIndex;
|
||||
textBox.Text = $"{text[..caretIndex]}\n{text[caretIndex..]}";
|
||||
textBox.CaretIndex = caretIndex + 1;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
static void UpdateLabelColor(GUITextBox box)
|
||||
{
|
||||
bool found = TextManager.ContainsTag(box.Text);
|
||||
box.TextColor = found
|
||||
? GUIStyle.Orange
|
||||
: GUIStyle.TextColorNormal;
|
||||
|
||||
if (found)
|
||||
{
|
||||
box.ToolTip = TextManager.GetWithVariable("StringPropertyTranslate", "[translation]", TextManager.Get(box.Text));
|
||||
}
|
||||
else
|
||||
{
|
||||
box.ToolTip = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
bodyTextBox.OnDeselected += (textBox, _) => UpdateLabelColor(textBox);
|
||||
headerTextBox.OnDeselected += (textBox, _) => UpdateLabelColor(textBox);
|
||||
UpdateLabelColor(bodyTextBox);
|
||||
UpdateLabelColor(headerTextBox);
|
||||
|
||||
mainLayout.Recalculate();
|
||||
headerTextBox.ForceUpdate();
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), mainLayout.RectTransform), text: TextManager.Get("confirm"))
|
||||
{
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
CircuitBox.RenameLabel(this, newColor, new NetLimitedString(headerTextBox.Text), new NetLimitedString(bodyTextBox.Text));
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
LocalizedString[] colorComponentLabels =
|
||||
{
|
||||
TextManager.Get("spriteeditor.colorcomponentr"),
|
||||
TextManager.Get("spriteeditor.colorcomponentg"),
|
||||
TextManager.Get("spriteeditor.colorcomponentb")
|
||||
};
|
||||
for (int i = 0; i <= 2; i++)
|
||||
{
|
||||
var element = new GUIFrame(new RectTransform(new Vector2(0.33f, 1), colorArea.RectTransform), style: null);
|
||||
|
||||
var colorLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), colorComponentLabels[i],
|
||||
font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
|
||||
|
||||
var numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight), NumberType.Int)
|
||||
{
|
||||
Font = GUIStyle.SubHeadingFont,
|
||||
MinValueInt = 0,
|
||||
MaxValueInt = 255
|
||||
};
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
colorLabel.TextColor = GUIStyle.Red;
|
||||
numberInput.IntValue = Color.R;
|
||||
numberInput.OnValueChanged += numInput =>
|
||||
{
|
||||
newColor.R = (byte)numInput.IntValue;
|
||||
header.Color = frame.Color = newColor;
|
||||
};
|
||||
break;
|
||||
case 1:
|
||||
colorLabel.TextColor = GUIStyle.Green;
|
||||
numberInput.IntValue = Color.G;
|
||||
numberInput.OnValueChanged += numInput =>
|
||||
{
|
||||
newColor.G = (byte)numInput.IntValue;
|
||||
header.Color = frame.Color = newColor;
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
colorLabel.TextColor = GUIStyle.Blue;
|
||||
numberInput.IntValue = Color.B;
|
||||
numberInput.OnValueChanged += numInput =>
|
||||
{
|
||||
newColor.B = (byte)numInput.IntValue;
|
||||
header.Color = frame.Color = newColor;
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
@@ -15,7 +16,17 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
internal sealed class CircuitBoxMouseDragSnapshotHandler
|
||||
{
|
||||
public IEnumerable<CircuitBoxNode> Nodes => circuitBoxUi.CircuitBox.Components.Union<CircuitBoxNode>(circuitBoxUi.CircuitBox.InputOutputNodes);
|
||||
public IEnumerable<CircuitBoxNode> Nodes
|
||||
{
|
||||
get
|
||||
{
|
||||
var cb = circuitBoxUi.CircuitBox;
|
||||
|
||||
foreach (var label in cb.Labels) { yield return label; }
|
||||
foreach (var component in cb.Components) { yield return component; }
|
||||
foreach (var node in cb.InputOutputNodes) { yield return node; }
|
||||
}
|
||||
}
|
||||
|
||||
private IReadOnlyList<CircuitBoxWire> Wires => circuitBoxUi.CircuitBox.Wires;
|
||||
|
||||
@@ -29,6 +40,8 @@ namespace Barotrauma
|
||||
// Nodes that should be moved when dragging
|
||||
moveAffectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty;
|
||||
|
||||
public Option<(CircuitBoxResizeDirection, CircuitBoxNode)> LastResizeAffectedNode = Option.None;
|
||||
|
||||
public ImmutableHashSet<CircuitBoxNode> GetLastComponentsUnderCursor() => lastNodesUnderCursor;
|
||||
public ImmutableHashSet<CircuitBoxNode> GetMoveAffectedComponents() => moveAffectedComponents;
|
||||
|
||||
@@ -45,6 +58,11 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public bool IsWiring { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the user grabbed a side of a node and is resizing a node
|
||||
/// </summary>
|
||||
public bool IsResizing { get; private set; }
|
||||
|
||||
private Vector2 startClick = Vector2.Zero;
|
||||
private readonly CircuitBoxUI circuitBoxUi;
|
||||
|
||||
@@ -147,6 +165,41 @@ namespace Barotrauma
|
||||
lastNodesUnderCursor = FindNodesUnderCursor(cursorPos);
|
||||
LastConnectorUnderCursor = FindConnectorUnderCursor(cursorPos);
|
||||
LastWireUnderCursor = FindWireUnderCursor(cursorPos);
|
||||
LastResizeAffectedNode = FindResizeBorderUnderCursor(lastNodesUnderCursor, cursorPos);
|
||||
}
|
||||
|
||||
private static Option<(CircuitBoxResizeDirection, CircuitBoxNode)> FindResizeBorderUnderCursor(ImmutableHashSet<CircuitBoxNode> nodes, Vector2 cursorPos)
|
||||
{
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -193,6 +246,7 @@ namespace Barotrauma
|
||||
startClick = Vector2.Zero;
|
||||
IsDragging = false;
|
||||
IsWiring = false;
|
||||
IsResizing = false;
|
||||
lastNodesUnderCursor = ImmutableHashSet<CircuitBoxNode>.Empty;
|
||||
}
|
||||
|
||||
@@ -210,23 +264,34 @@ namespace Barotrauma
|
||||
IsDragging = false;
|
||||
}
|
||||
|
||||
if (LastResizeAffectedNode.IsNone())
|
||||
{
|
||||
IsResizing = false;
|
||||
}
|
||||
|
||||
// startClick is set to zero when the user releases the mouse button, so we should be neither dragging nor wiring in this state
|
||||
if (startClick == Vector2.Zero)
|
||||
{
|
||||
IsDragging = false;
|
||||
IsWiring = false;
|
||||
IsResizing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool isDragTresholdExceeded = Vector2.DistanceSquared(startClick, cursorPos) > dragTreshold * dragTreshold;
|
||||
if (circuitBoxUi.Locked) { return; }
|
||||
bool isDragThresholdExceeded = Vector2.DistanceSquared(startClick, cursorPos) > dragTreshold * dragTreshold;
|
||||
|
||||
if (LastConnectorUnderCursor.IsNone())
|
||||
if (LastResizeAffectedNode.IsSome())
|
||||
{
|
||||
IsDragging |= isDragTresholdExceeded;
|
||||
IsResizing |= isDragThresholdExceeded;
|
||||
}
|
||||
else if (LastConnectorUnderCursor.IsSome())
|
||||
{
|
||||
IsWiring |= isDragThresholdExceeded;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsWiring |= isDragTresholdExceeded;
|
||||
IsDragging |= isDragThresholdExceeded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxNode
|
||||
{
|
||||
private RectangleF DrawRect,
|
||||
TopDrawRect;
|
||||
public RectangleF DrawRect;
|
||||
private RectangleF TopDrawRect;
|
||||
|
||||
private void UpdateDrawRects()
|
||||
protected void UpdateDrawRects()
|
||||
{
|
||||
var drawRect = new RectangleF(Position - Size / 2f, Size);
|
||||
drawRect.Y = -drawRect.Y;
|
||||
@@ -26,6 +26,8 @@ namespace Barotrauma
|
||||
UpdatePositions();
|
||||
}
|
||||
|
||||
public virtual void OnResized(RectangleF drawRect) { }
|
||||
|
||||
public void DrawBackground(SpriteBatch spriteBatch, RectangleF drawRect, RectangleF topDrawRect, Color color)
|
||||
{
|
||||
CircuitBox.NodeFrameSprite?.Draw(spriteBatch, drawRect, color);
|
||||
@@ -39,6 +41,7 @@ namespace Barotrauma
|
||||
|
||||
DrawBackground(spriteBatch, drawRect, topDrawRect, color);
|
||||
DrawHeader(spriteBatch, topDrawRect, color);
|
||||
DrawBody(spriteBatch, drawRect, color);
|
||||
|
||||
DrawConnectors(spriteBatch, drawPos);
|
||||
}
|
||||
@@ -52,6 +55,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public virtual void DrawHeader(SpriteBatch spriteBatch, RectangleF rect, Color color) { }
|
||||
public virtual void DrawBody(SpriteBatch spriteBatch, RectangleF rect, Color color) { }
|
||||
|
||||
public void DrawConnectors(SpriteBatch spriteBatch, Vector2 drawPos)
|
||||
{
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace Barotrauma
|
||||
|
||||
public List<CircuitBoxWireRenderer> VirtualWires = new();
|
||||
|
||||
public bool Locked => CircuitBox.Locked;
|
||||
|
||||
public CircuitBoxUI(CircuitBox box)
|
||||
{
|
||||
camera = new Camera
|
||||
@@ -47,7 +49,7 @@ namespace Barotrauma
|
||||
MouseSnapshotHandler = new CircuitBoxMouseDragSnapshotHandler(this);
|
||||
}
|
||||
|
||||
#region UI
|
||||
#region UI
|
||||
|
||||
public void CreateGUI(GUIFrame parent)
|
||||
{
|
||||
@@ -63,7 +65,7 @@ namespace Barotrauma
|
||||
spriteBatch.End();
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
DrawHUD(spriteBatch);
|
||||
DrawHUD(spriteBatch, component.Rect);
|
||||
spriteBatch.End();
|
||||
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
@@ -82,6 +84,8 @@ namespace Barotrauma
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
componentMenuOpen = !componentMenuOpen;
|
||||
if (Locked) { componentMenuOpen = false; }
|
||||
|
||||
foreach (GUIComponent child in btn.Children)
|
||||
{
|
||||
child.SpriteEffects = componentMenuOpen ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
@@ -139,65 +143,68 @@ namespace Barotrauma
|
||||
};
|
||||
int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f);
|
||||
var settingsIcon = new GUIButton(new RectTransform(new Point(buttonHeight), parent.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4), MinSize = new Point(buttonHeight) },
|
||||
style: "GUIButtonSettings")
|
||||
style: "GUIButtonSettings")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(
|
||||
new ContextMenuOption("circuitboxsetting.resetview", isEnabled: true, onSelected: ResetCamera)
|
||||
GUIContextMenu.CreateContextMenu(
|
||||
new ContextMenuOption("circuitboxsetting.resetview", isEnabled: true, onSelected: ResetCamera)
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.resetview")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.find", isEnabled: true,
|
||||
new ContextMenuOption("circuitboxsetting.focusinput", isEnabled: true, onSelected: () => FindInputOutput(CircuitBoxInputOutputNode.Type.Input))
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.resetview")
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focusinput")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.find", isEnabled: true,
|
||||
new ContextMenuOption("circuitboxsetting.focusinput", isEnabled: true, onSelected: () => FindInputOuput(CircuitBoxInputOutputNode.Type.Input))
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focusinput")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.focusoutput", isEnabled: true, onSelected: () => FindInputOuput(CircuitBoxInputOutputNode.Type.Output))
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focusoutput")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.focuscircuits", isEnabled: CircuitBox.Components.Any(), onSelected: FindCircuit)
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focuscircuits")
|
||||
}));
|
||||
new ContextMenuOption("circuitboxsetting.focusoutput", isEnabled: true, onSelected: () => FindInputOutput(CircuitBoxInputOutputNode.Type.Output))
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focusoutput")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.focuscircuits", isEnabled: CircuitBox.Components.Any(), onSelected: FindCircuit)
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focuscircuits")
|
||||
}));
|
||||
|
||||
|
||||
void ResetCamera()
|
||||
{
|
||||
// Vector2.One because Vector2.Zero means no value
|
||||
camera.TargetPos = Vector2.One;
|
||||
}
|
||||
|
||||
void FindInputOuput(CircuitBoxInputOutputNode.Type type)
|
||||
{
|
||||
var input = CircuitBox.InputOutputNodes.FirstOrDefault(n => n.NodeType == type);
|
||||
if (input is null) { return; }
|
||||
|
||||
camera.TargetPos = input.Position;
|
||||
}
|
||||
|
||||
void FindCircuit()
|
||||
{
|
||||
var closestComponent = CircuitBox.Components.MinBy(c => Vector2.DistanceSquared(c.Position, camera.Position));
|
||||
if (closestComponent is null) { return; }
|
||||
|
||||
camera.TargetPos = closestComponent.Position;
|
||||
}
|
||||
return true;
|
||||
void ResetCamera()
|
||||
{
|
||||
// Vector2.One because Vector2.Zero means no value
|
||||
camera.TargetPos = Vector2.One;
|
||||
}
|
||||
};
|
||||
|
||||
void FindInputOutput(CircuitBoxInputOutputNode.Type type)
|
||||
{
|
||||
var input = CircuitBox.InputOutputNodes.FirstOrDefault(n => n.NodeType == type);
|
||||
if (input is null) { return; }
|
||||
|
||||
camera.TargetPos = input.Position;
|
||||
}
|
||||
|
||||
void FindCircuit()
|
||||
{
|
||||
var closestComponent = CircuitBox.Components.MinBy(c => Vector2.DistanceSquared(c.Position, camera.Position));
|
||||
if (closestComponent is null) { return; }
|
||||
|
||||
camera.TargetPos = closestComponent.Position;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
MouseSnapshotHandler.UpdateConnections();
|
||||
|
||||
// update scales of everything
|
||||
foreach (var node in CircuitBox.Components) { node.OnUICreated(); }
|
||||
|
||||
foreach (var node in CircuitBox.InputOutputNodes) { node.OnUICreated(); }
|
||||
|
||||
foreach (var wire in CircuitBox.Wires) { wire.Update(); }
|
||||
}
|
||||
|
||||
private string GetInventoryText()
|
||||
=> CircuitBox.ComponentContainer is { } container
|
||||
private string GetInventoryText() =>
|
||||
CircuitBox.ComponentContainer is { } container
|
||||
? $"{container.Inventory.AllItems.Count()}/{container.Capacity}"
|
||||
: "0/0";
|
||||
|
||||
@@ -209,7 +216,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (componentList is null) { return; }
|
||||
var playerInventory = CircuitBox.GetSortedCircuitBoxSortedItemsFromPlayer(Character.Controlled);
|
||||
|
||||
var playerInventory = CircuitBox.GetSortedCircuitBoxItemsFromPlayer(Character.Controlled);
|
||||
|
||||
foreach (GUIComponent child in componentList.Content.Children)
|
||||
{
|
||||
@@ -304,9 +312,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
private void DrawHUD(SpriteBatch spriteBatch)
|
||||
private void DrawHUD(SpriteBatch spriteBatch, Rectangle screenRect)
|
||||
{
|
||||
float scale = GUI.Scale / 1.5f;
|
||||
Vector2 offset = new Vector2(20, 40) * scale;
|
||||
@@ -352,6 +360,16 @@ namespace Barotrauma
|
||||
{
|
||||
n.DrawHUD(spriteBatch, camera);
|
||||
}
|
||||
|
||||
if (Locked)
|
||||
{
|
||||
LocalizedString lockedText = TextManager.Get("CircuitBoxLocked")
|
||||
.Fallback(TextManager.Get("ConnectionLocked"));
|
||||
|
||||
Vector2 size = GUIStyle.LargeFont.MeasureString(lockedText);
|
||||
Vector2 pos = new Vector2(screenRect.Center.X - size.X / 2, screenRect.Top + screenRect.Height * 0.05f);
|
||||
GUI.DrawString(spriteBatch, pos, lockedText, Color.Red, Color.Black, 8, GUIStyle.LargeFont);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSelection(SpriteBatch spriteBatch, Vector2 pos1, Vector2 pos2, Color color)
|
||||
@@ -367,6 +385,12 @@ namespace Barotrauma
|
||||
private static float lineWidth;
|
||||
|
||||
public static void DrawRectangleWithBorder(SpriteBatch spriteBatch, RectangleF rect, Color fillColor, Color borderColor)
|
||||
{
|
||||
GUI.DrawFilledRectangle(spriteBatch, rect, fillColor);
|
||||
DrawRectangleOnlyBorder(spriteBatch, rect, borderColor);
|
||||
}
|
||||
|
||||
private static void DrawRectangleOnlyBorder(SpriteBatch spriteBatch, RectangleF rect, Color borderColor)
|
||||
{
|
||||
Vector2 topRight = new Vector2(rect.Right, rect.Top),
|
||||
topLeft = new Vector2(rect.Left, rect.Top),
|
||||
@@ -375,8 +399,6 @@ namespace Barotrauma
|
||||
|
||||
Vector2 offset = new Vector2(0f, lineWidth / 2f);
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, rect, fillColor);
|
||||
|
||||
spriteBatch.DrawLine(topRight, topLeft, borderColor, thickness: lineWidth);
|
||||
spriteBatch.DrawLine(topLeft - offset, bottomLeft + offset, borderColor, thickness: lineWidth);
|
||||
spriteBatch.DrawLine(bottomLeft, bottomRight, borderColor, thickness: lineWidth);
|
||||
@@ -393,6 +415,16 @@ namespace Barotrauma
|
||||
Vector2 mousePos = GetCursorPosition();
|
||||
mousePos.Y = -mousePos.Y;
|
||||
|
||||
foreach (var label in CircuitBox.Labels)
|
||||
{
|
||||
if (label.IsSelected)
|
||||
{
|
||||
label.DrawSelection(spriteBatch, GetSelectionColor(label));
|
||||
}
|
||||
|
||||
label.Draw(spriteBatch, label.Position, label.Color);
|
||||
}
|
||||
|
||||
foreach (CircuitBoxWire wire in CircuitBox.Wires)
|
||||
{
|
||||
wire.Renderer.Draw(spriteBatch, GetSelectionColor(wire));
|
||||
@@ -428,6 +460,7 @@ namespace Barotrauma
|
||||
Color color = moveable switch
|
||||
{
|
||||
CircuitBoxComponent node => node.Item.Prefab.SignalComponentColor,
|
||||
CircuitBoxLabelNode label => label.Color,
|
||||
CircuitBoxInputOutputNode ioNode => ioNode.NodeType is CircuitBoxInputOutputNode.Type.Input ? GUIStyle.Green : GUIStyle.Red,
|
||||
_ => Color.White
|
||||
};
|
||||
@@ -435,17 +468,49 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (MouseSnapshotHandler.IsResizing && MouseSnapshotHandler.LastResizeAffectedNode.TryUnwrap(out var resize))
|
||||
{
|
||||
var (dir, node) = resize;
|
||||
Vector2 dragOffset = MouseSnapshotHandler.GetDragAmount(GetCursorPosition());
|
||||
|
||||
var rect = node.Rect;
|
||||
rect.Y = -rect.Y;
|
||||
rect.Y -= rect.Height;
|
||||
|
||||
if (dir.HasFlag(CircuitBoxResizeDirection.Down))
|
||||
{
|
||||
rect.Height -= dragOffset.Y;
|
||||
rect.Height = Math.Max(rect.Height, CircuitBoxLabelNode.MinSize.Y + CircuitBoxSizes.NodeHeaderHeight);
|
||||
}
|
||||
|
||||
if (dir.HasFlag(CircuitBoxResizeDirection.Right))
|
||||
{
|
||||
rect.Width += dragOffset.X;
|
||||
rect.Width = Math.Max(rect.Width, CircuitBoxLabelNode.MinSize.X);
|
||||
}
|
||||
|
||||
if (dir.HasFlag(CircuitBoxResizeDirection.Left))
|
||||
{
|
||||
float oldWidth = rect.Width;
|
||||
rect.Width -= dragOffset.X;
|
||||
rect.Width = Math.Max(rect.Width, CircuitBoxLabelNode.MinSize.X);
|
||||
|
||||
float actualResize = rect.Width - oldWidth;
|
||||
rect.X -= actualResize;
|
||||
}
|
||||
|
||||
DrawRectangleOnlyBorder(spriteBatch, rect, GUIStyle.Yellow);
|
||||
}
|
||||
|
||||
if (DraggedWire.TryUnwrap(out CircuitBoxWireRenderer? draggedWire))
|
||||
{
|
||||
draggedWire.Draw(spriteBatch, GUIStyle.Yellow);
|
||||
}
|
||||
}
|
||||
|
||||
private Color GetSelectionColor(CircuitBoxNode node)
|
||||
=> GetSelectionColor(node.SelectedBy, node.IsSelectedByMe);
|
||||
private Color GetSelectionColor(CircuitBoxNode node) => GetSelectionColor(node.SelectedBy, node.IsSelectedByMe);
|
||||
|
||||
private Color GetSelectionColor(CircuitBoxWire wire)
|
||||
=> GetSelectionColor(wire.SelectedBy, wire.IsSelectedByMe);
|
||||
private Color GetSelectionColor(CircuitBoxWire wire) => GetSelectionColor(wire.SelectedBy, wire.IsSelectedByMe);
|
||||
|
||||
private Color GetSelectionColor(ushort selectedBy, bool isSelectedByMe)
|
||||
{
|
||||
@@ -489,6 +554,7 @@ namespace Barotrauma
|
||||
{
|
||||
node.UpdateEditing(circuitComponent.RectTransform);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -503,6 +569,7 @@ namespace Barotrauma
|
||||
{
|
||||
Character.DisableControls = true;
|
||||
}
|
||||
|
||||
camera.MoveCamera(deltaTime, allowMove: true, allowZoom: isMouseOn, allowInput: isMouseOn, followSub: false);
|
||||
|
||||
if (camera.TargetPos != Vector2.Zero && MathUtils.NearlyEqual(camera.Position, camera.TargetPos, 0.01f))
|
||||
@@ -547,7 +614,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggedWire = Option.Some(new CircuitBoxWireRenderer(Option.None,start, end, GUIStyle.Red, CircuitBox.WireSprite));
|
||||
DraggedWire = Option.Some(new CircuitBoxWireRenderer(Option.None, start, end, GUIStyle.Red, CircuitBox.WireSprite));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -562,6 +629,12 @@ namespace Barotrauma
|
||||
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
if (MouseSnapshotHandler.IsResizing && MouseSnapshotHandler.LastResizeAffectedNode.TryUnwrap(out var r))
|
||||
{
|
||||
var (dir, node) = r;
|
||||
CircuitBox.ResizeNode(node, dir, MouseSnapshotHandler.GetDragAmount(cursorPos));
|
||||
}
|
||||
|
||||
if (CircuitBox.HeldComponent.TryUnwrap(out ItemPrefab? prefab))
|
||||
{
|
||||
CircuitBox.AddComponent(prefab, cursorPos);
|
||||
@@ -604,22 +677,32 @@ namespace Barotrauma
|
||||
{
|
||||
CircuitBox.RemoveComponents(CircuitBox.Components.Where(static node => node.IsSelectedByMe).ToArray());
|
||||
CircuitBox.RemoveWires(CircuitBox.Wires.Where(static wire => wire.IsSelectedByMe).ToImmutableArray());
|
||||
CircuitBox.RemoveLabel(CircuitBox.Labels.Where(static label => label.IsSelectedByMe).ToImmutableArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (componentMenu is { } menu && toggleMenuButton is { } button)
|
||||
{
|
||||
componentMenuOpenState = componentMenuOpen ? Math.Min(componentMenuOpenState + deltaTime * 5.0f, 1.0f) : Math.Max(componentMenuOpenState - deltaTime * 5.0f, 0.0f);
|
||||
button.Enabled = !Locked;
|
||||
componentMenuOpenState = componentMenuOpen && !Locked ? Math.Min(componentMenuOpenState + deltaTime * 5.0f, 1.0f) : Math.Max(componentMenuOpenState - deltaTime * 5.0f, 0.0f);
|
||||
|
||||
menu.RectTransform.ScreenSpaceOffset = Vector2.Lerp(new Vector2(0.0f, menu.Rect.Height - 10), Vector2.Zero, componentMenuOpenState).ToPoint();
|
||||
button.RectTransform.AbsoluteOffset = new Point(menu.Rect.X + ((menu.Rect.Width / 2) - (button.Rect.Width / 2)), menu.Rect.Y - button.Rect.Height);
|
||||
}
|
||||
|
||||
if (selectedWireFrame is { } wireFrame)
|
||||
{
|
||||
wireFrame.Visible = !Locked;
|
||||
}
|
||||
|
||||
camera.Position = Vector2.Clamp(camera.Position,
|
||||
new Vector2(-CircuitBoxSizes.PlayableAreaSize / 2f),
|
||||
new Vector2(CircuitBoxSizes.PlayableAreaSize / 2f));
|
||||
}
|
||||
|
||||
public void SetMenuVisibility(bool state)
|
||||
=> componentMenuOpen = state;
|
||||
|
||||
private void UpdateSelection()
|
||||
{
|
||||
if (!PlayerInput.IsAltDown() && PlayerInput.PrimaryMouseButtonDown())
|
||||
@@ -662,35 +745,55 @@ namespace Barotrauma
|
||||
var wireSelection = CircuitBox.Wires.Where(static w => w.IsSelectedByMe).ToImmutableArray();
|
||||
var nodeOption = GetTopmostNode(MouseSnapshotHandler.FindNodesUnderCursor(cursorPos));
|
||||
var nodeSelection = CircuitBox.Components.Where(static n => n.IsSelectedByMe).ToImmutableArray();
|
||||
var labels = CircuitBox.Labels.Where(static l => l.IsSelectedByMe).ToImmutableArray();
|
||||
|
||||
var option = new ContextMenuOption(TextManager.Get("delete"), isEnabled: wireOption.IsSome() || nodeOption is CircuitBoxComponent, () =>
|
||||
var option = new ContextMenuOption(TextManager.Get("delete"), isEnabled: (wireOption.IsSome() || nodeOption is CircuitBoxComponent or CircuitBoxLabelNode) && !Locked, () =>
|
||||
{
|
||||
if (wireOption.TryUnwrap(out var wire))
|
||||
{
|
||||
CircuitBox.RemoveWires(wire.IsSelected ? wireSelection : ImmutableArray.Create(wire));
|
||||
}
|
||||
|
||||
if (nodeOption is CircuitBoxComponent node)
|
||||
switch (nodeOption)
|
||||
{
|
||||
CircuitBox.RemoveComponents(node.IsSelected ? nodeSelection : ImmutableArray.Create(node));
|
||||
case CircuitBoxComponent node:
|
||||
CircuitBox.RemoveComponents(node.IsSelected ? nodeSelection : ImmutableArray.Create(node));
|
||||
break;
|
||||
case CircuitBoxLabelNode label:
|
||||
CircuitBox.RemoveLabel(label.IsSelected ? labels : ImmutableArray.Create(label));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
var editLabel = new ContextMenuOption(TextManager.Get("circuitboxeditlabel"), isEnabled: nodeOption is CircuitBoxLabelNode && !Locked, () =>
|
||||
{
|
||||
if (nodeOption is not CircuitBoxLabelNode label || circuitComponent is null) { return; }
|
||||
|
||||
label.PromptEditText(circuitComponent);
|
||||
});
|
||||
|
||||
var addLabelOption = new ContextMenuOption(TextManager.Get("circuitboxaddlabel"), isEnabled: !Locked, () =>
|
||||
{
|
||||
CircuitBox.AddLabel(cursorPos);
|
||||
});
|
||||
|
||||
ContextMenuOption[] allOptions = { addLabelOption, editLabel, option };
|
||||
|
||||
// show component name in the header to better indicate what is about to be deleted
|
||||
if (nodeOption is CircuitBoxComponent comp)
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(PlayerInput.MousePosition, comp.Item.Name, comp.Item.Prefab.SignalComponentColor, option);
|
||||
GUIContextMenu.CreateContextMenu(PlayerInput.MousePosition, comp.Item.Name, comp.Item.Prefab.SignalComponentColor, allOptions);
|
||||
return;
|
||||
}
|
||||
|
||||
// also check if a wire is being deleted
|
||||
if (wireOption.TryUnwrap(out var foundWire))
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(PlayerInput.MousePosition, foundWire.UsedItemPrefab.Name, foundWire.Color, option);
|
||||
GUIContextMenu.CreateContextMenu(PlayerInput.MousePosition, foundWire.UsedItemPrefab.Name, foundWire.Color, allOptions);
|
||||
return;
|
||||
}
|
||||
|
||||
GUIContextMenu.CreateContextMenu(option);
|
||||
GUIContextMenu.CreateContextMenu(allOptions);
|
||||
}
|
||||
|
||||
public CircuitBoxNode? GetTopmostNode(ImmutableHashSet<CircuitBoxNode> nodes)
|
||||
|
||||
@@ -2434,6 +2434,29 @@ namespace Barotrauma
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("converttowreck", "", (string[] args) =>
|
||||
{
|
||||
if (Screen.Selected is not SubEditorScreen)
|
||||
{
|
||||
ThrowError("The command can only be used in the submarine editor.");
|
||||
return;
|
||||
}
|
||||
if (Submarine.MainSub == null)
|
||||
{
|
||||
ThrowError("Load a submarine first to convert it to a wreck.");
|
||||
return;
|
||||
}
|
||||
if (Submarine.MainSub.Info.SubmarineElement == null)
|
||||
{
|
||||
ThrowError("The submarine must be saved before you can convert it to a wreck.");
|
||||
return;
|
||||
}
|
||||
var wreckedSubmarineInfo = new SubmarineInfo(filePath: string.Empty, element: WreckConverter.ConvertToWreck(Submarine.MainSub.Info.SubmarineElement));
|
||||
wreckedSubmarineInfo.Name += "_Wrecked";
|
||||
wreckedSubmarineInfo.Type = SubmarineType.Wreck;
|
||||
GameMain.SubEditorScreen.LoadSub(wreckedSubmarineInfo);
|
||||
}));
|
||||
|
||||
commands.Add(new Command("camerasettings", "camerasettings [defaultzoom] [zoomsmoothness] [movesmoothness] [minzoom] [maxzoom]: debug command for testing camera settings. The values default to 1.1, 8.0, 8.0, 0.1 and 2.0.", (string[] args) =>
|
||||
{
|
||||
float defaultZoom = Screen.Selected.Cam.DefaultZoom;
|
||||
@@ -2485,6 +2508,49 @@ namespace Barotrauma
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("listcontainertags", "Lists all container tags on the submarine.", (string[] args) =>
|
||||
{
|
||||
if (Screen.Selected != GameMain.SubEditorScreen)
|
||||
{
|
||||
ThrowError("This command can only be used in the sub editor.");
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<Identifier> allContainerTagsInTheGame = new();
|
||||
|
||||
foreach (var itemPrefab in ItemPrefab.Prefabs)
|
||||
{
|
||||
foreach (var pc in itemPrefab.PreferredContainers)
|
||||
{
|
||||
foreach (Identifier identifier in Enumerable.Union(pc.Primary, pc.Secondary))
|
||||
{
|
||||
allContainerTagsInTheGame.Add(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<Identifier, float> prefab = new();
|
||||
|
||||
foreach (Item it in Item.ItemList)
|
||||
{
|
||||
foreach (var tag in allContainerTagsInTheGame)
|
||||
{
|
||||
if (it.GetTags().All(t => tag != t)) { continue; }
|
||||
|
||||
prefab.TryAdd(tag, 0.0f);
|
||||
prefab[tag]++;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new();
|
||||
foreach (var (tag, amount) in prefab.OrderByDescending(kvp => kvp.Value))
|
||||
{
|
||||
sb.AppendLine($"{tag}: {amount}");
|
||||
}
|
||||
|
||||
NewMessage(sb.ToString());
|
||||
}, isCheat: false));
|
||||
|
||||
commands.Add(new Command("refreshrect", "Updates the dimensions of the selected items to match the ones defined in the prefab. Applied only in the subeditor.", (string[] args) =>
|
||||
{
|
||||
//TODO: maybe do this automatically during loading when possible?
|
||||
@@ -3015,6 +3081,46 @@ namespace Barotrauma
|
||||
ContentPackageManager.EnabledPackages.ReloadCore();
|
||||
}));
|
||||
|
||||
commands.Add(new Command("reloadpackage", "reloapackage [name]: reloads a content package.", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
ThrowError("Please specify the name of the package to reload.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length < 2)
|
||||
{
|
||||
if (Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
ThrowError("Reloading the package while in GameScreen may break things; to do it anyway, type 'reloadcorepackage [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'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
ThrowError("Cannot change content packages while playing online");
|
||||
return;
|
||||
}
|
||||
|
||||
var package = ContentPackageManager.RegularPackages.FirstOrDefault(p => p.Name == args[0]);
|
||||
if (package == null)
|
||||
{
|
||||
ThrowError($"Could not find the package {args[0]}!");
|
||||
return;
|
||||
}
|
||||
ContentPackageManager.EnabledPackages.ReloadPackage(package);
|
||||
}, getValidArgs: () => new[]
|
||||
{
|
||||
ContentPackageManager.RegularPackages.Select(p => p.Name).ToArray()
|
||||
}));
|
||||
|
||||
#if WINDOWS
|
||||
commands.Add(new Command("startdedicatedserver", "", (string[] args) =>
|
||||
{
|
||||
@@ -3395,6 +3501,29 @@ namespace Barotrauma
|
||||
}
|
||||
character.AnimController.ResetRagdoll(forceReload: true);
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("loadanimation", "Loads an animation variation by name for the controlled character. The animation file has to be in the correct animations folder. Note: the changes are not saved!", (string[] args) =>
|
||||
{
|
||||
var character = Character.Controlled;
|
||||
if (character == null)
|
||||
{
|
||||
ThrowError("Not controlling any character!");
|
||||
return;
|
||||
}
|
||||
if (args.Length < 2)
|
||||
{
|
||||
ThrowError("Insufficient parameters: Have to pass the type of animation (Walk, Run, SwimSlow, SwimFast, or Crouch) and the filename!");
|
||||
return;
|
||||
}
|
||||
string type = args[0];
|
||||
if (!Enum.TryParse(type, ignoreCase: true, out AnimationType animationType))
|
||||
{
|
||||
ThrowError($"Failed to parse animation type from {type}. Supported types are Walk, Run, SwimSlow, SwimFast, and Crouch!");
|
||||
return;
|
||||
}
|
||||
string fileName = args[1];
|
||||
character.AnimController.TryLoadAnimation(animationType, Path.GetFileNameWithoutExtension(fileName), out _, throwErrors: true);
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("reloadwearables", "Reloads the sprites of all limbs and wearable sprites (clothing) of the controlled character. Provide id or name if you want to target another character.", args =>
|
||||
{
|
||||
@@ -3545,7 +3674,10 @@ namespace Barotrauma
|
||||
}
|
||||
try
|
||||
{
|
||||
var subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.DisplayName.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
var subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s =>
|
||||
//accept both the localized and the non-localized name of the sub
|
||||
s.DisplayName.Equals(args[0], StringComparison.OrdinalIgnoreCase) ||
|
||||
s.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
if (subInfo == null)
|
||||
{
|
||||
ThrowError($"Could not find a submarine with the name \"{args[0]}\".");
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using Barotrauma.Tutorials;
|
||||
using Segment = Barotrauma.ObjectiveManager.Segment;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
/// <summary>
|
||||
/// Checks the state of an Objective created using <see cref="EventObjectiveAction"/>.
|
||||
/// </summary>
|
||||
partial class CheckObjectiveAction : BinaryOptionAction
|
||||
{
|
||||
public enum CheckType
|
||||
@@ -12,10 +14,10 @@ partial class CheckObjectiveAction : BinaryOptionAction
|
||||
Incomplete
|
||||
}
|
||||
|
||||
[Serialize(CheckType.Completed, IsPropertySaveable.Yes)]
|
||||
[Serialize(CheckType.Completed, IsPropertySaveable.Yes, description: "The objective must be in this state for the check to succeed.")]
|
||||
public CheckType Type { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "The identifier of the objective to check.")]
|
||||
public Identifier Identifier { get; set; }
|
||||
|
||||
partial void DetermineSuccessProjSpecific(ref bool success)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Barotrauma.Tutorials;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Barotrauma
|
||||
{
|
||||
LocalizedString rewardText = GetRewardAmountText(sub);
|
||||
LocalizedString retVal;
|
||||
if (rewardPerCrate.HasValue)
|
||||
if (rewardPerCrate.HasValue) // If every crate has the same value
|
||||
{
|
||||
LocalizedString rewardPerCrateText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", rewardPerCrate.Value));
|
||||
retVal = TextManager.GetWithVariables("missionrewardcargopercrate",
|
||||
@@ -21,7 +21,7 @@ namespace Barotrauma
|
||||
("[maxitemcount]", maxItemCount.ToString()),
|
||||
("[totalreward]", $"‖color:gui.orange‖{rewardText}‖end‖"));
|
||||
}
|
||||
else
|
||||
else // Crates have differing values, so only show the total value
|
||||
{
|
||||
retVal = TextManager.GetWithVariables("missionrewardcargo",
|
||||
("[totalreward]", $"‖color:gui.orange‖{rewardText}‖end‖"),
|
||||
|
||||
@@ -151,10 +151,10 @@ namespace Barotrauma
|
||||
message = ModifyMessage(message);
|
||||
}
|
||||
|
||||
CoroutineManager.StartCoroutine(ShowMessageBoxAfterRoundSummary(header, message));
|
||||
CoroutineManager.StartCoroutine(ShowMessageBoxWhenRoundSummaryIsNotActive(header, message));
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> ShowMessageBoxAfterRoundSummary(LocalizedString header, LocalizedString message)
|
||||
private IEnumerable<CoroutineStatus> ShowMessageBoxWhenRoundSummaryIsNotActive(LocalizedString header, LocalizedString message)
|
||||
{
|
||||
while (GUIMessageBox.VisibleBox?.UserData is RoundSummary)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,22 @@ namespace Barotrauma
|
||||
public override bool DisplayAsCompleted => false;
|
||||
public override bool DisplayAsFailed => false;
|
||||
|
||||
private void TryShowRetrievedMessage()
|
||||
{
|
||||
if (DetermineCompleted())
|
||||
{
|
||||
if (!allRetrievedMessage.IsNullOrEmpty()) { CreateMessageBox(string.Empty, allRetrievedMessage); }
|
||||
//no need to show this again, clear it
|
||||
allRetrievedMessage = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!partiallyRetrievedMessage.IsNullOrEmpty()) { CreateMessageBox(string.Empty, partiallyRetrievedMessage); }
|
||||
//no need to show this again, clear it
|
||||
partiallyRetrievedMessage = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
@@ -30,6 +46,15 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
target.Item = Item.ReadSpawnData(msg);
|
||||
target.Item.HighlightColor = GUIStyle.Orange;
|
||||
target.Item.ExternalHighlight = true;
|
||||
|
||||
ushort parentTargetId = msg.ReadUInt16();
|
||||
if (parentTargetId != Entity.NullEntityID)
|
||||
{
|
||||
target.OriginalContainer = Entity.FindEntityByID(parentTargetId) as Item;
|
||||
}
|
||||
|
||||
if (target.Item == null)
|
||||
{
|
||||
throw new System.Exception("Error in SalvageMission.ClientReadInitial: spawned item was null (mission: " + Prefab.Identifier + ")");
|
||||
@@ -45,7 +70,7 @@ namespace Barotrauma
|
||||
target.Item.ApplyStatusEffect(selectedEffect, selectedEffect.type, deltaTime: 1.0f, worldPosition: target.Item.Position);
|
||||
}
|
||||
|
||||
if (target.Item.body != null)
|
||||
if (target.Item.body != null && target.Item.CurrentHull == null)
|
||||
{
|
||||
target.Item.body.FarseerBody.BodyType = BodyType.Kinematic;
|
||||
}
|
||||
@@ -55,15 +80,25 @@ namespace Barotrauma
|
||||
public override void ClientRead(IReadMessage msg)
|
||||
{
|
||||
base.ClientRead(msg);
|
||||
bool atLeastOneTargetWasRetrieved = false;
|
||||
int targetCount = msg.ReadByte();
|
||||
for (int i = 0; i < targetCount; i++)
|
||||
{
|
||||
var state = (Target.RetrievalState)msg.ReadByte();
|
||||
if (i < targets.Count)
|
||||
{
|
||||
bool wasRetrieved = targets[i].Retrieved;
|
||||
targets[i].State = state;
|
||||
if (!wasRetrieved && targets[i].Retrieved)
|
||||
{
|
||||
atLeastOneTargetWasRetrieved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (atLeastOneTargetWasRetrieved)
|
||||
{
|
||||
TryShowRetrievedMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,11 @@ namespace Barotrauma
|
||||
{
|
||||
case "sprite":
|
||||
UISprite newSprite = new UISprite(subElement);
|
||||
Rectangle sourceRect = newSprite.Sprite.SourceRect;
|
||||
if ((sourceRect.Width <= 1 || sourceRect.Height <= 1) && newSprite.Tile)
|
||||
{
|
||||
DebugConsole.AddWarning($"Sprite \"{subElement.GetAttributeString("name", Name)}\" has a size of 1 or less which may cause performance problems.", contentPackage: element.ContentPackage);
|
||||
}
|
||||
|
||||
GUIComponent.ComponentState spriteState = GUIComponent.ComponentState.None;
|
||||
if (subElement.GetAttribute("state") != null)
|
||||
|
||||
@@ -187,7 +187,16 @@ namespace Barotrauma
|
||||
Spacing = 1
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUIStyle.SubHeadingFont);
|
||||
var crewHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), crewHeader.RectTransform, Anchor.CenterRight), string.Empty, textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
TextGetter = () =>
|
||||
{
|
||||
int crewSize = campaign?.CrewManager?.GetCharacterInfos()?.Count() ?? 0;
|
||||
return $"{crewSize}/{CrewManager.MaxCrewSize}";
|
||||
}
|
||||
};
|
||||
crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, 8 * height), pendingAndCrewGroup.RectTransform))
|
||||
{
|
||||
Spacing = 1
|
||||
@@ -207,7 +216,7 @@ namespace Barotrauma
|
||||
{
|
||||
ClickSound = GUISoundType.ConfirmTransaction,
|
||||
ForceUpperCase = ForceUpperCase.Yes,
|
||||
OnClicked = (b, o) => ValidateHires(PendingHires, true)
|
||||
OnClicked = (b, o) => ValidateHires(PendingHires, createNetworkEvent: true)
|
||||
};
|
||||
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
|
||||
{
|
||||
@@ -647,7 +656,7 @@ namespace Barotrauma
|
||||
|
||||
private bool AddPendingHire(CharacterInfo characterInfo, bool createNetworkMessage = true)
|
||||
{
|
||||
if (PendingHires.Count + campaign.CrewManager.GetCharacters().Count() >= CrewManager.MaxCrewSize)
|
||||
if (PendingHires.Count + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -703,7 +712,7 @@ namespace Barotrauma
|
||||
validateHiresButton.Enabled = enoughMoney && HasPermission && pendingList.Content.RectTransform.Children.Any();
|
||||
}
|
||||
|
||||
public bool ValidateHires(List<CharacterInfo> hires, bool createNetworkEvent = false)
|
||||
public bool ValidateHires(List<CharacterInfo> hires, bool takeMoney = true, bool createNetworkEvent = false)
|
||||
{
|
||||
if (hires == null || hires.None()) { return false; }
|
||||
|
||||
@@ -718,14 +727,16 @@ namespace Barotrauma
|
||||
|
||||
if (nonDuplicateHires.None()) { return false; }
|
||||
|
||||
int total = HireManager.GetSalaryFor(nonDuplicateHires);
|
||||
|
||||
if (!campaign.CanAfford(total)) { return false; }
|
||||
if (takeMoney)
|
||||
{
|
||||
int total = HireManager.GetSalaryFor(nonDuplicateHires);
|
||||
if (!campaign.CanAfford(total)) { return false; }
|
||||
}
|
||||
|
||||
bool atLeastOneHired = false;
|
||||
foreach (CharacterInfo ci in nonDuplicateHires)
|
||||
{
|
||||
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci, Character.Controlled))
|
||||
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci, takeMoney: takeMoney))
|
||||
{
|
||||
atLeastOneHired = true;
|
||||
}
|
||||
@@ -951,8 +962,8 @@ namespace Barotrauma
|
||||
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifierUsingOriginalName() == identifier);
|
||||
if (match != null)
|
||||
{
|
||||
PendingHires.Add(match);
|
||||
AddPendingHire(match, createNetworkMessage: false);
|
||||
System.Diagnostics.Debug.Assert(PendingHires.Contains(match));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Barotrauma.IO;
|
||||
@@ -641,19 +641,9 @@ namespace Barotrauma
|
||||
sprite?.Draw(spriteBatch, PlayerInput.MousePosition, scale: Math.Min(64 / sprite.size.X, 64 / sprite.size.Y) * Scale);
|
||||
break;
|
||||
}
|
||||
case ItemAssemblyPrefab iPrefab:
|
||||
case ItemAssemblyPrefab itemAssemblyPrefab:
|
||||
{
|
||||
var (x, y) = PlayerInput.MousePosition;
|
||||
foreach (var pair in iPrefab.DisplayEntities)
|
||||
{
|
||||
Rectangle dRect = pair.Item2;
|
||||
dRect = new Rectangle(x: (int)(dRect.X * iPrefab.Scale + x),
|
||||
y: (int)(dRect.Y * iPrefab.Scale - y),
|
||||
width: (int)(dRect.Width * iPrefab.Scale),
|
||||
height: (int)(dRect.Height * iPrefab.Scale));
|
||||
MapEntityPrefab prefab = MapEntityPrefab.Find("", pair.Item1);
|
||||
prefab.DrawPlacing(spriteBatch, dRect, prefab.Scale * iPrefab.Scale);
|
||||
}
|
||||
itemAssemblyPrefab.Draw(spriteBatch, PlayerInput.MousePosition.FlipY());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -716,7 +706,7 @@ namespace Barotrauma
|
||||
|
||||
spriteBatch.Draw(backgroundSprite.Texture,
|
||||
area.Center.ToVector2() + pos,
|
||||
null, color, 0.0f, backgroundSprite.size / 2,
|
||||
backgroundSprite.SourceRect, color, 0.0f, backgroundSprite.size / 2,
|
||||
scale, spriteEffects, 0.0f);
|
||||
}
|
||||
|
||||
@@ -1050,12 +1040,31 @@ namespace Barotrauma
|
||||
{
|
||||
return dragHandle.Dragging ? CursorState.Dragging : CursorState.Hand;
|
||||
}
|
||||
//do not show the hover cursor when the cursor is on a listbox (on the listbox itself, not any of elements inside it!)
|
||||
if (c is GUIListBox && (parent == null || parent == c))
|
||||
{
|
||||
return CursorState.Default;
|
||||
}
|
||||
// Some parent elements take priority
|
||||
// but not when the child is a GUIButton or GUITickBox
|
||||
if (!(parent is GUIButton) && !(parent is GUIListBox) ||
|
||||
if (parent is not GUIButton && parent is not GUIListBox ||
|
||||
(c is GUIButton) || (c is GUITickBox))
|
||||
{
|
||||
if (!c.Rect.Equals(monitorRect)) { return c.HoverCursor; }
|
||||
if (!c.Rect.Equals(monitorRect))
|
||||
{
|
||||
if (c is GUITickBox)
|
||||
{
|
||||
//tickboxes have some special logic: not all of the component is hoverable (just the box and the text area)
|
||||
if (c.State is GUIComponent.ComponentState.Hover or GUIComponent.ComponentState.HoverSelected)
|
||||
{
|
||||
return c.HoverCursor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return c.HoverCursor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2399,30 +2408,31 @@ namespace Barotrauma
|
||||
}
|
||||
iterations++;
|
||||
}
|
||||
|
||||
static Vector2 ClampMoveAmount(Rectangle Rect, Rectangle clampTo, Vector2 moveAmount)
|
||||
{
|
||||
if (Rect.Y < clampTo.Y)
|
||||
{
|
||||
moveAmount.Y = Math.Max(moveAmount.Y, 0.0f);
|
||||
}
|
||||
else if (Rect.Bottom > clampTo.Bottom)
|
||||
{
|
||||
moveAmount.Y = Math.Min(moveAmount.Y, 0.0f);
|
||||
}
|
||||
if (Rect.X < clampTo.X)
|
||||
{
|
||||
moveAmount.X = Math.Max(moveAmount.X, 0.0f);
|
||||
}
|
||||
else if (Rect.Right > clampTo.Right)
|
||||
{
|
||||
moveAmount.X = Math.Min(moveAmount.X, 0.0f);
|
||||
}
|
||||
return moveAmount;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
private static Vector2 ClampMoveAmount(Rectangle rect, Rectangle clampTo, Vector2 moveAmount)
|
||||
{
|
||||
if (rect.Y < clampTo.Y)
|
||||
{
|
||||
moveAmount.Y = Math.Max(moveAmount.Y, 0.0f);
|
||||
}
|
||||
else if (rect.Bottom > clampTo.Bottom)
|
||||
{
|
||||
moveAmount.Y = Math.Min(moveAmount.Y, 0.0f);
|
||||
}
|
||||
if (rect.X < clampTo.X)
|
||||
{
|
||||
moveAmount.X = Math.Max(moveAmount.X, 0.0f);
|
||||
}
|
||||
else if (rect.Right > clampTo.Right)
|
||||
{
|
||||
moveAmount.X = Math.Min(moveAmount.X, 0.0f);
|
||||
}
|
||||
return moveAmount;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Misc
|
||||
public static void TogglePauseMenu()
|
||||
@@ -2477,7 +2487,7 @@ namespace Barotrauma
|
||||
GameMain.GameSession.LoadPreviousSave();
|
||||
});
|
||||
|
||||
if (IsFriendlyOutpostLevel())
|
||||
if (IsFriendlyOutpostLevel() && !spMode.CrewDead)
|
||||
{
|
||||
CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToMainMenuVerification", action: () =>
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -734,7 +734,7 @@ namespace Barotrauma
|
||||
DrawToolTip(spriteBatch, ToolTip, Rect);
|
||||
}
|
||||
|
||||
public static void DrawToolTip(SpriteBatch spriteBatch, RichString toolTip, Vector2 pos)
|
||||
public static void DrawToolTip(SpriteBatch spriteBatch, RichString toolTip, Vector2 pos, Color? textColor = null, Color? backgroundColor = null)
|
||||
{
|
||||
if (ObjectiveManager.ContentRunning) { return; }
|
||||
|
||||
@@ -745,6 +745,8 @@ namespace Barotrauma
|
||||
if (toolTipBlock == null || (RichString)toolTipBlock.UserData != toolTip)
|
||||
{
|
||||
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), toolTip, font: GUIStyle.SmallFont, wrap: true, style: "GUIToolTip");
|
||||
if (textColor != null) { toolTipBlock.TextColor = textColor.Value; }
|
||||
if (backgroundColor != null) { toolTipBlock.Color = backgroundColor.Value; }
|
||||
toolTipBlock.RectTransform.NonScaledSize = new Point(
|
||||
(int)(GUIStyle.SmallFont.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
|
||||
(int)(GUIStyle.SmallFont.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
|
||||
@@ -754,6 +756,15 @@ namespace Barotrauma
|
||||
toolTipBlock.RectTransform.AbsoluteOffset = pos.ToPoint();
|
||||
toolTipBlock.SetTextPos();
|
||||
|
||||
if (toolTipBlock.Rect.Right > GameMain.GraphicsWidth - 10)
|
||||
{
|
||||
toolTipBlock.RectTransform.AbsoluteOffset -= new Point(toolTipBlock.Rect.Width,0);
|
||||
}
|
||||
if (toolTipBlock.Rect.Bottom > GameMain.GraphicsHeight - 10)
|
||||
{
|
||||
toolTipBlock.RectTransform.AbsoluteOffset -= new Point(0, toolTipBlock.Rect.Height);
|
||||
}
|
||||
|
||||
toolTipBlock.DrawManually(spriteBatch);
|
||||
}
|
||||
|
||||
@@ -982,6 +993,24 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the minimum height of the transfrom to equal to the sum of the minimum heights of the children
|
||||
/// (i.e. makes the element at least large enough to fit all the children vertically)
|
||||
/// </summary>
|
||||
public void InheritTotalChildrenMinHeight()
|
||||
{
|
||||
RectTransform.InheritTotalChildrenMinHeight();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the minimum height of the transfrom to equal to the sum of the heights of the children
|
||||
/// (i.e. makes the element at least large enough to fit all the children vertically)
|
||||
/// </summary>
|
||||
public void InheritTotalChildrenHeight()
|
||||
{
|
||||
RectTransform.InheritTotalChildrenHeight();
|
||||
}
|
||||
|
||||
public static GUIComponent FromXML(ContentXElement element, RectTransform parent)
|
||||
{
|
||||
GUIComponent component = null;
|
||||
|
||||
@@ -15,20 +15,73 @@ namespace Barotrauma
|
||||
|
||||
public GUITextBox TextBox { get; private set; }
|
||||
|
||||
public override RichString ToolTip
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.ToolTip;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.ToolTip = value;
|
||||
TextBox.ToolTip = value;
|
||||
}
|
||||
}
|
||||
|
||||
public GUIButton PlusButton { get; private set; }
|
||||
public GUIButton MinusButton { get; private set; }
|
||||
|
||||
public enum ButtonVisibility { Automatic, Manual, ForceVisible, ForceHidden }
|
||||
private ButtonVisibility _plusMinusButtonVisibility;
|
||||
/// <summary>
|
||||
/// Whether or not the default +- buttons should be shown. Defaults to Automatic,
|
||||
/// which enables it for all integers and for those floats that have a defined
|
||||
/// range, because for these it is implicitly more obvious how to increment them.
|
||||
/// </summary>
|
||||
public ButtonVisibility PlusMinusButtonVisibility
|
||||
{
|
||||
get { return _plusMinusButtonVisibility; }
|
||||
set
|
||||
{
|
||||
if (_plusMinusButtonVisibility != value)
|
||||
{
|
||||
_plusMinusButtonVisibility = value;
|
||||
UpdatePlusMinusButtonVisibility();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePlusMinusButtonVisibility()
|
||||
{
|
||||
if (ForceShowPlusMinusButtons
|
||||
|| inputType == NumberType.Int
|
||||
|| (inputType == NumberType.Float && MinValueFloat > float.MinValue && MaxValueFloat < float.MaxValue))
|
||||
switch (PlusMinusButtonVisibility)
|
||||
{
|
||||
ShowPlusMinusButtons();
|
||||
}
|
||||
else
|
||||
{
|
||||
HidePlusMinusButtons();
|
||||
case ButtonVisibility.ForceHidden:
|
||||
{
|
||||
HidePlusMinusButtons();
|
||||
break;
|
||||
}
|
||||
case ButtonVisibility.ForceVisible:
|
||||
{
|
||||
ShowPlusMinusButtons();
|
||||
break;
|
||||
}
|
||||
case ButtonVisibility.Automatic:
|
||||
{
|
||||
if (inputType == NumberType.Int
|
||||
|| (inputType == NumberType.Float
|
||||
&& MinValueFloat > float.MinValue
|
||||
&& MaxValueFloat < float.MaxValue))
|
||||
{
|
||||
ShowPlusMinusButtons();
|
||||
}
|
||||
else
|
||||
{
|
||||
HidePlusMinusButtons();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ButtonVisibility.Manual:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,19 +139,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool forceShowPlusMinusButtons;
|
||||
|
||||
public bool ForceShowPlusMinusButtons
|
||||
{
|
||||
get { return forceShowPlusMinusButtons; }
|
||||
set
|
||||
{
|
||||
if (forceShowPlusMinusButtons == value) { return; }
|
||||
forceShowPlusMinusButtons = value;
|
||||
UpdatePlusMinusButtonVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private int decimalsToDisplay = 1;
|
||||
public int DecimalsToDisplay
|
||||
{
|
||||
@@ -162,6 +202,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Readonly
|
||||
{
|
||||
get { return TextBox.Readonly; }
|
||||
set
|
||||
{
|
||||
TextBox.Readonly = value;
|
||||
PlusButton.Enabled = !value;
|
||||
MinusButton.Enabled = !value;
|
||||
}
|
||||
}
|
||||
|
||||
public override GUIFont Font
|
||||
{
|
||||
@@ -189,11 +240,19 @@ namespace Barotrauma
|
||||
|
||||
public float ValueStep;
|
||||
|
||||
// Enable holding to scroll through values faster
|
||||
private float pressedTimer;
|
||||
private readonly float pressedDelay = 0.5f;
|
||||
private bool IsPressedTimerRunning { get { return pressedTimer > 0; } }
|
||||
|
||||
public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center, float? relativeButtonAreaWidth = null, bool hidePlusMinusButtons = false) : base(style, rectT)
|
||||
public GUINumberInput(
|
||||
RectTransform rectT,
|
||||
NumberType inputType,
|
||||
string style = "",
|
||||
Alignment textAlignment = Alignment.Center,
|
||||
float? relativeButtonAreaWidth = null,
|
||||
ButtonVisibility buttonVisibility = ButtonVisibility.Automatic,
|
||||
(GUIButton PlusButton, GUIButton MinusButton)? customPlusMinusButtons = null) : base(style, rectT)
|
||||
{
|
||||
LayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, rectT), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
|
||||
@@ -233,9 +292,23 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
|
||||
var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null);
|
||||
PlusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null);
|
||||
GUIStyle.Apply(PlusButton, "PlusButton", this);
|
||||
if (customPlusMinusButtons.HasValue)
|
||||
{
|
||||
PlusButton = customPlusMinusButtons.Value.PlusButton;
|
||||
MinusButton = customPlusMinusButtons.Value.MinusButton;
|
||||
}
|
||||
else // generate the default +- buttons
|
||||
{
|
||||
var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null);
|
||||
|
||||
PlusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null);
|
||||
GUIStyle.Apply(PlusButton, "PlusButton", this);
|
||||
|
||||
MinusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform, Anchor.BottomRight), style: null);
|
||||
GUIStyle.Apply(MinusButton, "MinusButton", this);
|
||||
}
|
||||
|
||||
// Set up default and custom +- buttons the same way to ensure uniform functionality
|
||||
PlusButton.ClickSound = GUISoundType.Increase;
|
||||
PlusButton.OnButtonDown += () =>
|
||||
{
|
||||
@@ -255,9 +328,6 @@ namespace Barotrauma
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
MinusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform, Anchor.BottomRight), style: null);
|
||||
GUIStyle.Apply(MinusButton, "MinusButton", this);
|
||||
MinusButton.ClickSound = GUISoundType.Decrease;
|
||||
MinusButton.OnButtonDown += () =>
|
||||
{
|
||||
@@ -278,10 +348,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
|
||||
if (inputType != NumberType.Int || hidePlusMinusButtons)
|
||||
{
|
||||
HidePlusMinusButtons();
|
||||
}
|
||||
PlusMinusButtonVisibility = buttonVisibility;
|
||||
|
||||
if (inputType == NumberType.Int)
|
||||
{
|
||||
@@ -324,16 +391,16 @@ namespace Barotrauma
|
||||
|
||||
private void HidePlusMinusButtons()
|
||||
{
|
||||
PlusButton.Parent.Visible = false;
|
||||
PlusButton.Parent.IgnoreLayoutGroups = true;
|
||||
PlusButton.Parent.Visible = MinusButton.Parent.Visible = false;
|
||||
PlusButton.Parent.IgnoreLayoutGroups = MinusButton.Parent.IgnoreLayoutGroups = true;
|
||||
TextBox.RectTransform.RelativeSize = Vector2.One;
|
||||
LayoutGroup.Recalculate();
|
||||
}
|
||||
|
||||
private void ShowPlusMinusButtons()
|
||||
{
|
||||
PlusButton.Parent.Visible = true;
|
||||
PlusButton.Parent.IgnoreLayoutGroups = false;
|
||||
PlusButton.Parent.Visible = MinusButton.Parent.Visible = true;
|
||||
PlusButton.Parent.IgnoreLayoutGroups = MinusButton.Parent.IgnoreLayoutGroups = false;
|
||||
TextBox.RectTransform.RelativeSize = new Vector2(1.0f - PlusButton.Parent.RectTransform.RelativeSize.X, 1.0f);
|
||||
LayoutGroup.Recalculate();
|
||||
}
|
||||
@@ -427,6 +494,11 @@ namespace Barotrauma
|
||||
Math.Min(floatValue, MaxValueFloat.Value);
|
||||
PlusButton.Enabled = WrapAround || floatValue < MaxValueFloat;
|
||||
}
|
||||
|
||||
if (Readonly)
|
||||
{
|
||||
PlusButton.Enabled = MinusButton.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClampIntValue()
|
||||
@@ -441,8 +513,16 @@ namespace Barotrauma
|
||||
intValue = WrapAround && MinValueInt.HasValue ? MinValueInt.Value : Math.Min(intValue, MaxValueInt.Value);
|
||||
UpdateText();
|
||||
}
|
||||
PlusButton.Enabled = WrapAround || MaxValueInt == null || intValue < MaxValueInt;
|
||||
MinusButton.Enabled = WrapAround || MinValueInt == null || intValue > MinValueInt;
|
||||
|
||||
if (Readonly)
|
||||
{
|
||||
PlusButton.Enabled = MinusButton.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlusButton.Enabled = WrapAround || MaxValueInt == null || intValue < MaxValueInt;
|
||||
MinusButton.Enabled = WrapAround || MinValueInt == null || intValue > MinValueInt;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateText()
|
||||
|
||||
@@ -135,7 +135,8 @@ namespace Barotrauma
|
||||
if (subElement.NameAsIdentifier() != "override") { continue; }
|
||||
if (ScalableFont.ExtractShccFromXElement(subElement).HasFlag(flag))
|
||||
{
|
||||
return new ScalableFont(subElement, font?.Size ?? 14, GameMain.Instance.GraphicsDevice);
|
||||
uint overrideFontSize = GetFontSize(subElement, defaultSize: font?.Size ?? 14);
|
||||
return new ScalableFont(subElement, overrideFontSize, GameMain.Instance.GraphicsDevice);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,6 +381,11 @@ namespace Barotrauma
|
||||
|
||||
public static implicit operator UISprite?(GUISprite reference) => reference.Value;
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, RectangleF rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None)
|
||||
{
|
||||
Value?.Draw(spriteBatch, rect, color, spriteEffects);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None)
|
||||
{
|
||||
Value?.Draw(spriteBatch, rect, color, spriteEffects);
|
||||
|
||||
@@ -214,7 +214,7 @@ namespace Barotrauma
|
||||
Bar.HoverCursor = CursorState.Default;
|
||||
break;
|
||||
case "GUISlider":
|
||||
HoverCursor = CursorState.Default;
|
||||
HoverCursor = CursorState.Hand;
|
||||
Bar.HoverCursor = CursorState.Hand;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows accessing the element selected in the carousel in contexts where the type of T isn't known.
|
||||
/// Pretty hacky, but I could not think of a better way to do this (<see cref="Networking.ServerSettings.NetPropertyData"/> in which this is used).
|
||||
/// </summary>
|
||||
public interface IGUISelectionCarouselAccessor
|
||||
{
|
||||
object? GetSelectedElement();
|
||||
void SelectElement(object? value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An UI element that allows toggling through a set of options with buttons to the left and right
|
||||
/// </summary>
|
||||
public class GUISelectionCarousel<T> : GUIComponent, IGUISelectionCarouselAccessor
|
||||
{
|
||||
public record class Element(T value, LocalizedString text, LocalizedString toolTip);
|
||||
|
||||
public delegate void OnValueChangedHandler(GUISelectionCarousel<T> carousel);
|
||||
public OnValueChangedHandler? OnValueChanged;
|
||||
|
||||
public GUITextBlock TextBlock { get; private set; }
|
||||
|
||||
public GUIButton RightButton { get; private set; }
|
||||
public GUIButton LeftButton { get; private set; }
|
||||
|
||||
private readonly List<Element> elements = new List<Element>();
|
||||
|
||||
private readonly GUILayoutGroup layoutGroup;
|
||||
|
||||
public Element? SelectedElement { get; private set; }
|
||||
public T? SelectedValue => SelectedElement == null ? default : SelectedElement.value;
|
||||
public LocalizedString SelectedText => SelectedElement?.text ?? string.Empty;
|
||||
|
||||
public override bool Enabled
|
||||
{
|
||||
get => base.Enabled;
|
||||
set
|
||||
{
|
||||
base.Enabled = RightButton.Enabled = LeftButton.Enabled = TextBlock.Enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override Color Color
|
||||
{
|
||||
get { return color; }
|
||||
set
|
||||
{
|
||||
color = value;
|
||||
TextBlock.Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
public Color TextColor
|
||||
{
|
||||
get { return TextBlock.TextColor; }
|
||||
set { TextBlock.TextColor = value; }
|
||||
}
|
||||
|
||||
public override Color HoverColor
|
||||
{
|
||||
get => base.HoverColor;
|
||||
set
|
||||
{
|
||||
base.HoverColor = value;
|
||||
TextBlock.HoverColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
public GUISelectionCarousel(RectTransform rectT, string style = "", params (T value, LocalizedString text)[] newElements) : base(style, rectT)
|
||||
{
|
||||
layoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, rectT), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
LeftButton = new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), layoutGroup.RectTransform), style: "GUIButtonToggleLeft");
|
||||
GUIStyle.Apply(LeftButton, "LeftButton", this);
|
||||
TextBlock = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), layoutGroup.RectTransform), "", textAlignment: Alignment.Center, style: "GUITextBox");
|
||||
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;
|
||||
};
|
||||
|
||||
if (newElements != null && newElements.Any())
|
||||
{
|
||||
SetElements(newElements);
|
||||
}
|
||||
}
|
||||
|
||||
public object? GetSelectedElement()
|
||||
{
|
||||
return SelectedValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select the element whose value matches the specified value. If null, deselects the currently selected element.
|
||||
/// </summary>
|
||||
public void SelectElement(object? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
SelectElement(null);
|
||||
return;
|
||||
}
|
||||
if (elements.FirstOrDefault(e => value.Equals(e.value)) is { } element)
|
||||
{
|
||||
SelectElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectElement(Element? element)
|
||||
{
|
||||
SelectedElement = element;
|
||||
TextBlock.Text = element?.text ?? string.Empty;
|
||||
TextBlock.ToolTip = element?.toolTip ?? string.Empty;
|
||||
OnValueChanged?.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all existing elements from the carousels and adds the specified new elements to it
|
||||
/// </summary>
|
||||
public void SetElements(params (T value, LocalizedString text)[] elements)
|
||||
{
|
||||
this.elements.Clear();
|
||||
foreach ((T value, LocalizedString text) in elements)
|
||||
{
|
||||
AddElement(value, text);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all existing elements from the carousels and adds the specified new elements to it
|
||||
/// </summary>
|
||||
public void SetElements(params (T value, LocalizedString text, LocalizedString toolTip)[] elements)
|
||||
{
|
||||
this.elements.Clear();
|
||||
foreach ((T value, LocalizedString text, LocalizedString toolTip) in elements)
|
||||
{
|
||||
AddElement(value, text, toolTip);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddElement(T value, LocalizedString text, LocalizedString? tooltip = null)
|
||||
{
|
||||
var newElement = new Element(value, text, tooltip ?? string.Empty);
|
||||
elements.Add(newElement);
|
||||
if (SelectedElement == null)
|
||||
{
|
||||
SelectElement(newElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -47,6 +47,8 @@ namespace Barotrauma
|
||||
public readonly static GUISprite SubmarineLocationIcon = new GUISprite("SubmarineLocationIcon");
|
||||
public readonly static GUISprite Arrow = new GUISprite("Arrow");
|
||||
public readonly static GUISprite SpeechBubbleIcon = new GUISprite("SpeechBubbleIcon");
|
||||
public readonly static GUISprite SpeechBubbleIconSliced = new GUISprite("SpeechBubbleIconSliced");
|
||||
public readonly static GUISprite InteractionLabelBackground = new GUISprite("InteractionLabelBackground");
|
||||
public readonly static GUISprite BrokenIcon = new GUISprite("BrokenIcon");
|
||||
public readonly static GUISprite YouAreHereCircle = new GUISprite("YouAreHereCircle");
|
||||
|
||||
@@ -124,6 +126,9 @@ namespace Barotrauma
|
||||
public readonly static GUIColor ColorReputationNeutral = new GUIColor("ColorReputationNeutral", new Color(228, 217, 167, 255));
|
||||
public readonly static GUIColor ColorReputationHigh = new GUIColor("ColorReputationHigh", new Color(51, 152, 64, 255));
|
||||
public readonly static GUIColor ColorReputationVeryHigh = new GUIColor("ColorReputationVeryHigh", new Color(71, 160, 164, 255));
|
||||
|
||||
public readonly static GUIColor InteractionLabelColor = new GUIColor("InteractionLabelColor", new Color(255, 255, 255, 255));
|
||||
public readonly static GUIColor InteractionLabelHoverColor = new GUIColor("InteractionLabelHoverColor", new Color(0, 255, 255, 255));
|
||||
|
||||
// Inventory
|
||||
public readonly static GUIColor EquipmentSlotIconColor = new GUIColor("EquipmentSlotIconColor", new Color(99, 70, 64, 255));
|
||||
@@ -195,8 +200,7 @@ namespace Barotrauma
|
||||
|
||||
if (parentStyle == null)
|
||||
{
|
||||
Identifier parentStyleName = parent.GetType().Name.ToIdentifier();
|
||||
|
||||
Identifier parentStyleName = ReflectionUtils.GetTypeNameWithoutGenericArity(parent.GetType());
|
||||
if (!ComponentStyles.ContainsKey(parentStyleName))
|
||||
{
|
||||
DebugConsole.ThrowError($"Couldn't find a GUI style \"{parentStyleName}\"");
|
||||
@@ -204,7 +208,7 @@ namespace Barotrauma
|
||||
}
|
||||
parentStyle = ComponentStyles[parentStyleName];
|
||||
}
|
||||
Identifier childStyleName = styleName.IsEmpty ? targetComponent.GetType().Name.ToIdentifier() : styleName;
|
||||
Identifier childStyleName = styleName.IsEmpty ? ReflectionUtils.GetTypeNameWithoutGenericArity(targetComponent.GetType()) : styleName;
|
||||
parentStyle.ChildStyles.TryGetValue(childStyleName, out componentStyle);
|
||||
}
|
||||
else
|
||||
@@ -212,7 +216,7 @@ namespace Barotrauma
|
||||
Identifier styleIdentifier = styleName.ToIdentifier();
|
||||
if (styleIdentifier == Identifier.Empty)
|
||||
{
|
||||
styleIdentifier = targetComponent.GetType().Name.ToIdentifier();
|
||||
styleIdentifier = ReflectionUtils.GetTypeNameWithoutGenericArity(targetComponent.GetType());
|
||||
}
|
||||
if (!ComponentStyles.ContainsKey(styleIdentifier))
|
||||
{
|
||||
|
||||
@@ -143,6 +143,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, clips the left side of the text if it's too long to fit in the box (i.e. allows you to enter longer texts without the text overflowing from the box).
|
||||
/// </summary>
|
||||
public bool OverflowClip
|
||||
{
|
||||
get { return textBlock.OverflowClip; }
|
||||
@@ -325,7 +328,7 @@ namespace Barotrauma
|
||||
textBlock.Text = text;
|
||||
ClearSelection();
|
||||
if (Text == null) textBlock.Text = "";
|
||||
if (Text != "" && !Wrap)
|
||||
if (Text != "")
|
||||
{
|
||||
if (maxTextLength != null)
|
||||
{
|
||||
@@ -334,7 +337,7 @@ namespace Barotrauma
|
||||
textBlock.Text = Text.Substring(0, (int)maxTextLength);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!Wrap)
|
||||
{
|
||||
while (ClampText && textBlock.Text.Length > 0 && Font.MeasureString(textBlock.Text).X * TextBlock.TextScale > (int)(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z))
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -198,7 +198,9 @@ namespace Barotrauma
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (GUI.MouseOn == this && Enabled)
|
||||
if (GUI.MouseOn == this && Enabled &&
|
||||
//allow clicking on the text area, but not further to the right (the dimensions of the component itself can extend further than the text)
|
||||
PlayerInput.MousePosition.X < Rect.X + ContentWidth)
|
||||
{
|
||||
State = Selected ?
|
||||
ComponentState.HoverSelected :
|
||||
|
||||
@@ -779,6 +779,24 @@ namespace Barotrauma
|
||||
NonScaledSize = targetSize;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the minimum height of the transfrom to equal to the sum of the minimum heights of the children
|
||||
/// (i.e. makes the rect at least large enough to fit all the children vertically)
|
||||
/// </summary>
|
||||
public void InheritTotalChildrenMinHeight()
|
||||
{
|
||||
MinSize = new Point(MinSize.X, children.Sum(c => c.MinSize.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the minimum height of the transfrom to equal to the sum of the heights of the children
|
||||
/// (i.e. makes the rect at least large enough to fit all the children vertically)
|
||||
/// </summary>
|
||||
public void InheritTotalChildrenHeight()
|
||||
{
|
||||
MinSize = new Point(MinSize.X, children.Sum(c => c.Rect.Height));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static methods
|
||||
|
||||
@@ -1733,6 +1733,9 @@ namespace Barotrauma
|
||||
if (!subItem.Components.All(c => c is not Holdable h || !h.Attachable || !h.Attached)) { continue; }
|
||||
if (!subItem.Components.All(c => c is not Wire w || w.Connections.All(c => c == null))) { continue; }
|
||||
if (!ItemAndAllContainersInteractable(subItem)) { continue; }
|
||||
//don't list items in a character inventory (the ones in a crew member's inventory are counted below)
|
||||
var rootInventoryOwner = subItem.GetRootInventoryOwner();
|
||||
if (rootInventoryOwner != null) { continue; }
|
||||
AddOwnedItem(subItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1066,7 +1066,7 @@ namespace Barotrauma
|
||||
GUIButton centerButton = new GUIButton(new RectTransform(new Vector2(1f), centerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight, anchor: Anchor.Center), style: "GUIButtonTransferArrow");
|
||||
|
||||
GUILayoutGroup inputLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), paddedTransferMenuLayout.RectTransform), childAnchor: Anchor.Center);
|
||||
GUINumberInput transferAmountInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), inputLayout.RectTransform), NumberType.Int, hidePlusMinusButtons: true)
|
||||
GUINumberInput transferAmountInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), inputLayout.RectTransform), NumberType.Int, buttonVisibility: GUINumberInput.ButtonVisibility.ForceHidden)
|
||||
{
|
||||
MinValueInt = 0
|
||||
};
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace Barotrauma
|
||||
GUIFrame characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null) { Visible = false };
|
||||
GUILayoutGroup characterLayout = new GUILayoutGroup(new RectTransform(Vector2.One, characterSettingsFrame.RectTransform));
|
||||
GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null);
|
||||
GUIFrame playerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), containerFrame.RectTransform, Anchor.Center), 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),
|
||||
@@ -423,7 +423,7 @@ namespace Barotrauma
|
||||
GUIFrame croppedTalentFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: null);
|
||||
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, croppedTalentFrame.RectTransform, anchor: Anchor.Center), style: null)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖" + "\n\n" + ToolBox.ExtendColorToPercentageSigns(talent.Description.Value)),
|
||||
ToolTip = CreateTooltip(talent, characterInfo),
|
||||
UserData = talent.Identifier,
|
||||
PressedColor = pressedColor,
|
||||
Enabled = info.Character != null,
|
||||
@@ -489,6 +489,24 @@ namespace Barotrauma
|
||||
},
|
||||
};
|
||||
|
||||
static RichString CreateTooltip(TalentPrefab talent, CharacterInfo? character)
|
||||
{
|
||||
LocalizedString progress = string.Empty;
|
||||
|
||||
if (character is not null && talent.TrackedStat.TryUnwrap(out var stat))
|
||||
{
|
||||
var statValue = character.GetSavedStatValue(StatTypes.None, stat.PermanentStatIdentifier);
|
||||
var intValue = (int)MathF.Round(statValue);
|
||||
progress = "\n\n";
|
||||
progress += statValue < stat.Max
|
||||
? TextManager.GetWithVariables("talentprogress", ("[amount]", intValue.ToString()), ("[max]", stat.Max.ToString()))
|
||||
: TextManager.Get("talentprogresscompleted");
|
||||
}
|
||||
|
||||
RichString tooltip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖\n\n{ToolBox.ExtendColorToPercentageSigns(talent.Description.Value)}{progress}");
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = talentButton.DisabledColor = Color.Transparent;
|
||||
|
||||
GUIComponent iconImage;
|
||||
|
||||
@@ -1305,7 +1305,7 @@ namespace Barotrauma
|
||||
const int maxUpgrades = 4;
|
||||
|
||||
Item? item = entity as Item;
|
||||
itemName.Text = item?.Name ?? TextManager.Get("upgradecategory.walls");
|
||||
itemName.Text = item?.Prefab.Name ?? TextManager.Get("upgradecategory.walls");
|
||||
if (slotIndex > -1)
|
||||
{
|
||||
itemName.Text = TextManager.GetWithVariables("weaponslotwithname", ("[number]", slotIndex.ToString()), ("[weaponname]", itemName.Text));
|
||||
|
||||
@@ -7,46 +7,46 @@ using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class Widget
|
||||
public enum WidgetShape
|
||||
{
|
||||
public enum Shape
|
||||
{
|
||||
Rectangle,
|
||||
Circle,
|
||||
Cross
|
||||
}
|
||||
Rectangle,
|
||||
Circle,
|
||||
Cross
|
||||
}
|
||||
|
||||
public Shape shape;
|
||||
public LocalizedString tooltip;
|
||||
public bool showTooltip = true;
|
||||
public Rectangle DrawRect => new Rectangle((int)(DrawPos.X - (float)size / 2), (int)(DrawPos.Y - (float)size / 2), size, size);
|
||||
internal class Widget
|
||||
{
|
||||
public WidgetShape Shape;
|
||||
public LocalizedString Tooltip;
|
||||
public bool ShowTooltip = true;
|
||||
public Rectangle DrawRect => new Rectangle((int)(DrawPos.X - (float)Size / 2), (int)(DrawPos.Y - (float)Size / 2), Size, Size);
|
||||
public Rectangle InputRect
|
||||
{
|
||||
get
|
||||
{
|
||||
var inputRect = DrawRect;
|
||||
inputRect.Inflate(inputAreaMargin, inputAreaMargin);
|
||||
inputRect.Inflate(InputAreaMargin, InputAreaMargin);
|
||||
return inputRect;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 DrawPos { get; set; }
|
||||
public int size = 10;
|
||||
public float thickness = 1f;
|
||||
public int Size = 10;
|
||||
public float Thickness = 1f;
|
||||
/// <summary>
|
||||
/// Used only for circles.
|
||||
/// </summary>
|
||||
public int sides = 40;
|
||||
public int Sides = 40;
|
||||
/// <summary>
|
||||
/// Currently used only for rectangles.
|
||||
/// </summary>
|
||||
public bool isFilled;
|
||||
public int inputAreaMargin;
|
||||
public Color color = GUIStyle.Red;
|
||||
public Color? secondaryColor;
|
||||
public Color textColor = Color.White;
|
||||
public Color textBackgroundColor = Color.Black * 0.5f;
|
||||
public readonly string id;
|
||||
public bool IsFilled;
|
||||
public int InputAreaMargin;
|
||||
public Color Color = GUIStyle.Red;
|
||||
public Color? SecondaryColor;
|
||||
public Color TextColor = Color.White;
|
||||
public Color TextBackgroundColor = Color.Black * 0.5f;
|
||||
public readonly string Id;
|
||||
|
||||
public event Action Selected;
|
||||
public event Action Deselected;
|
||||
@@ -61,11 +61,11 @@ namespace Barotrauma
|
||||
|
||||
public bool RequireMouseOn = true;
|
||||
|
||||
public Action refresh;
|
||||
public Action Refresh;
|
||||
|
||||
public object data;
|
||||
public object Data;
|
||||
|
||||
public bool IsSelected => enabled && selectedWidgets.Contains(this);
|
||||
public bool IsSelected => enabled && SelectedWidgets.Contains(this);
|
||||
public bool IsControlled => IsSelected && PlayerInput.PrimaryMouseButtonHeld();
|
||||
public bool IsMouseOver => GUI.MouseOn == null && InputRect.Contains(PlayerInput.MousePosition);
|
||||
private bool enabled = true;
|
||||
@@ -75,9 +75,9 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
enabled = value;
|
||||
if (!enabled && selectedWidgets.Contains(this))
|
||||
if (!enabled && SelectedWidgets.Contains(this))
|
||||
{
|
||||
selectedWidgets.Remove(this);
|
||||
SelectedWidgets.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,46 +89,46 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
multiselect = value;
|
||||
if (!multiselect && selectedWidgets.Multiple())
|
||||
if (!multiselect && SelectedWidgets.Multiple())
|
||||
{
|
||||
selectedWidgets = selectedWidgets.Take(1).ToList();
|
||||
SelectedWidgets = SelectedWidgets.Take(1).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
public Vector2? tooltipOffset;
|
||||
public Vector2? TooltipOffset;
|
||||
|
||||
public Widget linkedWidget;
|
||||
public Widget LinkedWidget;
|
||||
|
||||
public static List<Widget> selectedWidgets = new List<Widget>();
|
||||
public static List<Widget> SelectedWidgets = new List<Widget>();
|
||||
|
||||
public Widget(string id, int size, Shape shape)
|
||||
public Widget(string id, int size, WidgetShape shape)
|
||||
{
|
||||
this.id = id;
|
||||
this.size = size;
|
||||
this.shape = shape;
|
||||
Id = id;
|
||||
Size = size;
|
||||
Shape = shape;
|
||||
}
|
||||
|
||||
public virtual void Update(float deltaTime)
|
||||
{
|
||||
PreUpdate?.Invoke(deltaTime);
|
||||
if (!enabled) { return; }
|
||||
if (IsMouseOver || (!RequireMouseOn && selectedWidgets.Contains(this) && PlayerInput.PrimaryMouseButtonHeld()))
|
||||
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())
|
||||
if ((multiselect && !SelectedWidgets.Contains(this)) || SelectedWidgets.None())
|
||||
{
|
||||
selectedWidgets.Add(this);
|
||||
SelectedWidgets.Add(this);
|
||||
Selected?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (selectedWidgets.Contains(this))
|
||||
else if (SelectedWidgets.Contains(this))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("selectedWidgets.Contains(this) -> remove");
|
||||
selectedWidgets.Remove(this);
|
||||
SelectedWidgets.Remove(this);
|
||||
Deselected?.Invoke();
|
||||
}
|
||||
if (IsSelected)
|
||||
@@ -153,40 +153,40 @@ namespace Barotrauma
|
||||
{
|
||||
PreDraw?.Invoke(spriteBatch, deltaTime);
|
||||
var drawRect = DrawRect;
|
||||
switch (shape)
|
||||
switch (Shape)
|
||||
{
|
||||
case Shape.Rectangle:
|
||||
if (secondaryColor.HasValue)
|
||||
case WidgetShape.Rectangle:
|
||||
if (SecondaryColor.HasValue)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, drawRect, secondaryColor.Value, isFilled, thickness: 2);
|
||||
GUI.DrawRectangle(spriteBatch, drawRect, SecondaryColor.Value, IsFilled, thickness: 2);
|
||||
}
|
||||
GUI.DrawRectangle(spriteBatch, drawRect, color, isFilled, thickness: IsSelected ? (int)(thickness * 3) : (int)thickness);
|
||||
GUI.DrawRectangle(spriteBatch, drawRect, Color, IsFilled, thickness: IsSelected ? (int)(Thickness * 3) : (int)Thickness);
|
||||
break;
|
||||
case Shape.Circle:
|
||||
if (secondaryColor.HasValue)
|
||||
case WidgetShape.Circle:
|
||||
if (SecondaryColor.HasValue)
|
||||
{
|
||||
ShapeExtensions.DrawCircle(spriteBatch, DrawPos, size / 2, sides, secondaryColor.Value, thickness: 2);
|
||||
ShapeExtensions.DrawCircle(spriteBatch, DrawPos, Size / 2, Sides, SecondaryColor.Value, thickness: 2);
|
||||
}
|
||||
ShapeExtensions.DrawCircle(spriteBatch, DrawPos, size / 2, sides, color, thickness: IsSelected ? 3 : 1);
|
||||
ShapeExtensions.DrawCircle(spriteBatch, DrawPos, Size / 2, Sides, Color, thickness: IsSelected ? 3 : 1);
|
||||
break;
|
||||
case Shape.Cross:
|
||||
float halfSize = size / 2;
|
||||
if (secondaryColor.HasValue)
|
||||
case WidgetShape.Cross:
|
||||
float halfSize = Size / 2;
|
||||
if (SecondaryColor.HasValue)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch, DrawPos + Vector2.UnitY * halfSize, DrawPos - Vector2.UnitY * halfSize, secondaryColor.Value, width: 2);
|
||||
GUI.DrawLine(spriteBatch, DrawPos + Vector2.UnitX * halfSize, DrawPos - Vector2.UnitX * halfSize, secondaryColor.Value, width: 2);
|
||||
GUI.DrawLine(spriteBatch, DrawPos + Vector2.UnitY * halfSize, DrawPos - Vector2.UnitY * halfSize, SecondaryColor.Value, width: 2);
|
||||
GUI.DrawLine(spriteBatch, DrawPos + Vector2.UnitX * halfSize, DrawPos - Vector2.UnitX * halfSize, SecondaryColor.Value, width: 2);
|
||||
}
|
||||
GUI.DrawLine(spriteBatch, DrawPos + Vector2.UnitY * halfSize, DrawPos - Vector2.UnitY * halfSize, color, width: IsSelected ? 3 : 1);
|
||||
GUI.DrawLine(spriteBatch, DrawPos + Vector2.UnitX * halfSize, DrawPos - Vector2.UnitX * halfSize, color, width: IsSelected ? 3 : 1);
|
||||
GUI.DrawLine(spriteBatch, DrawPos + Vector2.UnitY * halfSize, DrawPos - Vector2.UnitY * halfSize, Color, width: IsSelected ? 3 : 1);
|
||||
GUI.DrawLine(spriteBatch, DrawPos + Vector2.UnitX * halfSize, DrawPos - Vector2.UnitX * halfSize, Color, width: IsSelected ? 3 : 1);
|
||||
break;
|
||||
default: throw new NotImplementedException(shape.ToString());
|
||||
default: throw new NotImplementedException(Shape.ToString());
|
||||
}
|
||||
if (IsSelected)
|
||||
{
|
||||
if (showTooltip && !tooltip.IsNullOrEmpty())
|
||||
if (ShowTooltip && !Tooltip.IsNullOrEmpty())
|
||||
{
|
||||
var offset = tooltipOffset ?? new Vector2(size, -size / 2f);
|
||||
GUI.DrawString(spriteBatch, DrawPos + offset, tooltip, textColor, textBackgroundColor);
|
||||
var offset = TooltipOffset ?? new Vector2(Size, -Size / 2f);
|
||||
GUIComponent.DrawToolTip(spriteBatch, Tooltip, DrawPos + offset, TextColor, TextBackgroundColor);
|
||||
}
|
||||
}
|
||||
PostDraw?.Invoke(spriteBatch, deltaTime);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Media;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Particles;
|
||||
@@ -131,7 +131,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public event Action ResolutionChanged;
|
||||
|
||||
private bool exiting;
|
||||
public static bool IsExiting { get; private set; }
|
||||
|
||||
public static bool IsFirstLaunch
|
||||
{
|
||||
@@ -175,7 +175,7 @@ namespace Barotrauma
|
||||
{
|
||||
try
|
||||
{
|
||||
return Instance != null && !Instance.exiting && Instance.IsActive;
|
||||
return Instance != null && !IsExiting && Instance.IsActive;
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
@@ -462,7 +462,15 @@ namespace Barotrauma
|
||||
{
|
||||
Thread.Sleep((int)(Timing.Step * 1000));
|
||||
}
|
||||
LanguageIdentifier selectedLanguage = GameSettings.CurrentConfig.Language;
|
||||
//unload text files at this point - we only loaded for the purposes of the language selection screen,
|
||||
//they will be loaded "normally" with the rest of the files later
|
||||
ContentPackageManager.VanillaCorePackage.UnloadFilesOfType<TextFile>();
|
||||
//the selected language got unloaded, need to reselect it
|
||||
var config = GameSettings.CurrentConfig;
|
||||
config.Language = selectedLanguage;
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
GameSettings.SaveCurrentConfig();
|
||||
}
|
||||
|
||||
SoundManager = new Sounds.SoundManager();
|
||||
@@ -485,8 +493,10 @@ namespace Barotrauma
|
||||
.Select(p => p.Result).Successes())
|
||||
{
|
||||
const float min = 1f, max = 70f;
|
||||
if (IsExiting) { break; }
|
||||
TitleScreen.LoadState = MathHelper.Lerp(min, max, progress);
|
||||
}
|
||||
if (IsExiting) { return; }
|
||||
|
||||
var corePackage = ContentPackageManager.EnabledPackages.Core;
|
||||
if (corePackage.EnableError.TryUnwrap(out var error))
|
||||
@@ -578,6 +588,8 @@ namespace Barotrauma
|
||||
|
||||
MainMenuScreen.Select();
|
||||
|
||||
ContainerTagPrefab.CheckForContainerTagErrors();
|
||||
|
||||
foreach (Identifier steamError in SteamManager.InitializationErrors)
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get(steamError));
|
||||
@@ -1205,7 +1217,7 @@ namespace Barotrauma
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), linkHolder.RectTransform), TextManager.Get("bugreportgithubform"), style: "MainMenuGUIButton", textAlignment: Alignment.Left)
|
||||
{
|
||||
UserData = "https://github.com/Regalis11/Barotrauma/issues/new/choose",
|
||||
UserData = "https://github.com/FakeFishGames/Barotrauma/discussions/new?category=bug-reports",
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
ShowOpenUriPrompt(userdata as string);
|
||||
@@ -1229,7 +1241,7 @@ namespace Barotrauma
|
||||
|
||||
protected override void OnExiting(object sender, EventArgs args)
|
||||
{
|
||||
exiting = true;
|
||||
IsExiting = true;
|
||||
CreatureMetrics.Save();
|
||||
DebugConsole.NewMessage("Exiting...");
|
||||
Client?.Quit();
|
||||
|
||||
@@ -157,6 +157,7 @@ namespace Barotrauma
|
||||
Character.Controlled.Info.Name,
|
||||
msg, messageType,
|
||||
Character.Controlled);
|
||||
Character.Controlled.ShowSpeechBubble(ChatMessage.MessageColor[(int)messageType], text);
|
||||
if (messageType == ChatMessageType.Radio && headset != null)
|
||||
{
|
||||
Signal s = new Signal(msg, sender: Character.Controlled, source: headset.Item);
|
||||
@@ -620,7 +621,7 @@ namespace Barotrauma
|
||||
private void OnCrewListRearranged(GUIListBox crewList, object draggedElementData)
|
||||
{
|
||||
if (crewList != this.crewList) { return; }
|
||||
if (!(draggedElementData is Character)) { return; }
|
||||
if (draggedElementData is not Character) { return; }
|
||||
if (!IsSinglePlayer) { return; }
|
||||
if (crewList.HasDraggedElementIndexChanged)
|
||||
{
|
||||
@@ -645,7 +646,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < crewList.Content.CountChildren; i++)
|
||||
{
|
||||
var characterComponent = crewList.Content.GetChild(i);
|
||||
if (!(characterComponent?.UserData is Character c)) { continue; }
|
||||
if (characterComponent?.UserData is not Character c) { continue; }
|
||||
if (c.Info == null) { continue; }
|
||||
c.Info.CrewListIndex = i;
|
||||
}
|
||||
@@ -681,6 +682,7 @@ namespace Barotrauma
|
||||
{
|
||||
AddSinglePlayerChatMessage(senderName.Value, text.Value, messageType, sender);
|
||||
}
|
||||
|
||||
public void AddSinglePlayerChatMessage(string senderName, string text, ChatMessageType messageType, Character sender)
|
||||
{
|
||||
if (!IsSinglePlayer)
|
||||
@@ -798,12 +800,27 @@ namespace Barotrauma
|
||||
hull ??= order.OrderGiver.CurrentHull;
|
||||
AddOrder(order.WithTargetEntity(hull), order.FadeOutTime);
|
||||
}
|
||||
if (order.IsDeconstructOrder)
|
||||
{
|
||||
if (order.TargetEntity is Item item)
|
||||
{
|
||||
if (order.Identifier == Tags.DeconstructThis)
|
||||
{
|
||||
Item.DeconstructItems.Add(item);
|
||||
HintManager.OnItemMarkedForDeconstruction(order.OrderGiver);
|
||||
}
|
||||
else
|
||||
{
|
||||
Item.DeconstructItems.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (order.IsIgnoreOrder)
|
||||
{
|
||||
WallSection ws = null;
|
||||
if (order.TargetType == Order.OrderTargetType.Entity && order.TargetEntity is IIgnorable ignorable)
|
||||
{
|
||||
ignorable.OrderedToBeIgnored = order.Identifier == "ignorethis";
|
||||
ignorable.OrderedToBeIgnored = order.Identifier == Tags.IgnoreThis;
|
||||
AddOrder(order.Clone(), null);
|
||||
}
|
||||
else if (order.TargetType == Order.OrderTargetType.WallSection && order.TargetEntity is Structure s)
|
||||
@@ -812,7 +829,7 @@ namespace Barotrauma
|
||||
ws = s.GetSection(wallSectionIndex);
|
||||
if (ws != null)
|
||||
{
|
||||
ws.OrderedToBeIgnored = order.Identifier == "ignorethis";
|
||||
ws.OrderedToBeIgnored = order.Identifier == Tags.IgnoreThis;
|
||||
AddOrder(order.WithWallSection(s, wallSectionIndex), null);
|
||||
}
|
||||
}
|
||||
@@ -835,7 +852,9 @@ namespace Barotrauma
|
||||
}
|
||||
if (IsSinglePlayer)
|
||||
{
|
||||
order.OrderGiver?.Speak(order.GetChatMessage("", hull?.DisplayName?.Value, givingOrderToSelf: character == order.OrderGiver, isNewOrder: isNewOrder), ChatMessageType.Order);
|
||||
order.OrderGiver?.Speak(
|
||||
order.GetChatMessage("", hull?.DisplayName?.Value, givingOrderToSelf: character == order.OrderGiver, isNewOrder: isNewOrder),
|
||||
ChatMessageType.Order);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1404,9 +1423,10 @@ namespace Barotrauma
|
||||
if (PlayerInput.KeyDown(InputType.Command) &&
|
||||
(GUI.KeyboardDispatcher.Subscriber == null || (GUI.KeyboardDispatcher.Subscriber is GUIComponent component && (component == crewList || component.IsChildOf(crewList)))) &&
|
||||
commandFrame == null && !clicklessSelectionActive && CanIssueOrders && !(GameMain.GameSession?.Campaign?.ShowCampaignUI ?? false) &&
|
||||
Character.Controlled?.SelectedItem?.Prefab is not { DisableCommandMenuWhenSelected: true })
|
||||
Character.Controlled?.SelectedItem?.Prefab is not { DisableCommandMenuWhenSelected: true } &&
|
||||
!Inventory.IsMouseOnInventory)
|
||||
{
|
||||
if (PlayerInput.IsShiftDown())
|
||||
if (PlayerInput.KeyDown(InputType.ContextualCommand))
|
||||
{
|
||||
CreateCommandUI(FindEntityContext(), true);
|
||||
}
|
||||
@@ -1535,14 +1555,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (node.Keys != Keys.None && PlayerInput.KeyHit(node.Keys))
|
||||
{
|
||||
var b = node.Button as GUIButton;
|
||||
if (PlayerInput.IsShiftDown() && b?.OnSecondaryClicked != null)
|
||||
var button = node.Button;
|
||||
if (PlayerInput.IsShiftDown() && button?.OnSecondaryClicked != null)
|
||||
{
|
||||
b.OnSecondaryClicked.Invoke(node.Button as GUIButton, node.Button.UserData);
|
||||
button.OnSecondaryClicked.Invoke(button, button.UserData);
|
||||
}
|
||||
else
|
||||
{
|
||||
b?.OnClicked?.Invoke(node.Button as GUIButton, node.Button.UserData);
|
||||
button?.OnClicked?.Invoke(button, button.UserData);
|
||||
}
|
||||
ResetNodeSelection();
|
||||
hotkeyHit = true;
|
||||
@@ -1612,7 +1632,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var orderIcon in currentOrderIconList.Content.Children)
|
||||
{
|
||||
if (!(orderIcon.UserData is Order order)) { continue; }
|
||||
if (orderIcon.UserData is not Order order) { continue; }
|
||||
if (order.ColoredWhenControllingGiver && order.OrderGiver != Character.Controlled)
|
||||
{
|
||||
orderIcon.Color = AIObjective.ObjectiveIconColor;
|
||||
@@ -1701,7 +1721,7 @@ namespace Barotrauma
|
||||
bool foundMatch = false;
|
||||
foreach (var orderIcon in currentOrderIconList.Content.Children)
|
||||
{
|
||||
if (!(orderIcon.GetChildByUserData("glow") is GUIComponent glowComponent)) { continue; }
|
||||
if (orderIcon.GetChildByUserData("glow") is not GUIComponent glowComponent) { continue; }
|
||||
glowComponent.Color = orderIcon.Color;
|
||||
if (foundMatch)
|
||||
{
|
||||
@@ -1946,6 +1966,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenCommandUI(Entity entityContext = null, bool forceContextual = false)
|
||||
{
|
||||
CreateCommandUI(entityContext, forceContextual);
|
||||
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
|
||||
clicklessSelectionActive = isOpeningClick = true;
|
||||
}
|
||||
|
||||
private void CreateCommandUI(Entity entityContext = null, bool forceContextual = false)
|
||||
{
|
||||
if (commandFrame != null) { DisableCommandUI(); }
|
||||
@@ -2018,7 +2045,7 @@ namespace Barotrauma
|
||||
new RectTransform(Vector2.One, startNode.RectTransform, anchor: Anchor.Center),
|
||||
(spriteBatch, _) =>
|
||||
{
|
||||
if (!(entityContext is Character character) || character?.Info == null) { return; }
|
||||
if (entityContext is not Character character || character?.Info == null) { return; }
|
||||
var node = startNode;
|
||||
character.Info.DrawJobIcon(spriteBatch,
|
||||
new Rectangle((int)(node.Rect.X + node.Rect.Width * 0.5f), (int)(node.Rect.Y + node.Rect.Height * 0.1f), (int)(node.Rect.Width * 0.6f), (int)(node.Rect.Height * 0.8f)));
|
||||
@@ -2082,7 +2109,7 @@ namespace Barotrauma
|
||||
returnNodeMargin = returnNodeSize.X * 0.5f;
|
||||
|
||||
nodeDistance = (int)(150 * GUI.Scale);
|
||||
shorcutCenterNodeOffset = new Point(0, (int)(1.25f * nodeDistance));
|
||||
shorcutCenterNodeOffset = new Point(0, (int)(1.35f * nodeDistance));
|
||||
}
|
||||
|
||||
private List<OrderCategory> GetAvailableCategories()
|
||||
@@ -2115,7 +2142,7 @@ namespace Barotrauma
|
||||
if (centerNode == null || optionNodes == null) { return; }
|
||||
var startNodePos = centerNode.Rect.Center.ToVector2();
|
||||
// Don't draw connectors for assignment nodes
|
||||
if (!(optionNodes.FirstOrDefault()?.Button.UserData is Character))
|
||||
if (optionNodes.FirstOrDefault()?.Button.UserData is not Character)
|
||||
{
|
||||
// Regular option nodes
|
||||
if (targetFrame == null || !targetFrame.Visible)
|
||||
@@ -2194,7 +2221,7 @@ namespace Barotrauma
|
||||
private bool NavigateForward(GUIButton node, object userData)
|
||||
{
|
||||
if (commandFrame == null) { return false; }
|
||||
if (!(optionNodes.Find(n => n.Button == node) is OptionNode optionNode) || !optionNodes.Remove(optionNode))
|
||||
if (optionNodes.Find(n => n.Button == node) is not OptionNode optionNode || !optionNodes.Remove(optionNode))
|
||||
{
|
||||
shortcutNodes.Remove(node);
|
||||
};
|
||||
@@ -2352,7 +2379,7 @@ namespace Barotrauma
|
||||
matchingItems = nodeOrder.GetMatchingItems(submarine, true, interactableFor: characterContext ?? Character.Controlled);
|
||||
}
|
||||
//more than one target item -> create a minimap-like selection with a pic of the sub
|
||||
if (itemContext == null && !(nodeOrder.TargetEntity is Item) && matchingItems != null && matchingItems.Count > 1)
|
||||
if (itemContext == null && nodeOrder.TargetEntity is not Item && matchingItems != null && matchingItems.Count > 1)
|
||||
{
|
||||
CreateMinimapNodes(nodeOrder, submarine, matchingItems);
|
||||
}
|
||||
@@ -2417,7 +2444,7 @@ namespace Barotrauma
|
||||
|
||||
node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration);
|
||||
var icon = OrderCategoryIcon.OrderCategoryIcons.FirstOrDefault(ic => ic.Category == category);
|
||||
if (!(icon is null))
|
||||
if (icon is not null)
|
||||
{
|
||||
var tooltip = TextManager.Get($"ordercategorytitle.{category}");
|
||||
var categoryDescription = TextManager.Get($"ordercategorydescription.{category}");
|
||||
@@ -2484,7 +2511,7 @@ namespace Barotrauma
|
||||
// TODO: Doesn't work for player issued reports, because they don't have a target.
|
||||
bool useSpecificRepairOrder = false;
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder("repairelectrical") &&
|
||||
ActiveOrders.Any(o => o.Order.Prefab == reportBrokenDevices && o.Order.TargetItemComponent is Repairable r && r.requiredSkills.Any(s => s.Identifier == "electrical")))
|
||||
ActiveOrders.Any(o => o.Order.Prefab == reportBrokenDevices && o.Order.TargetItemComponent is Repairable r && r.RequiredSkills.Any(s => s.Identifier == "electrical")))
|
||||
{
|
||||
if (IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["repairelectrical"]))
|
||||
{
|
||||
@@ -2493,7 +2520,7 @@ namespace Barotrauma
|
||||
useSpecificRepairOrder = true;
|
||||
}
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder("repairmechanical") &&
|
||||
ActiveOrders.Any(o => o.Order.Prefab == reportBrokenDevices && o.Order.TargetItemComponent is Repairable r && r.requiredSkills.Any(s => s.Identifier == "mechanical")))
|
||||
ActiveOrders.Any(o => o.Order.Prefab == reportBrokenDevices && o.Order.TargetItemComponent is Repairable r && r.RequiredSkills.Any(s => s.Identifier == "mechanical")))
|
||||
{
|
||||
if (IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["repairmechanical"]))
|
||||
{
|
||||
@@ -2565,7 +2592,7 @@ namespace Barotrauma
|
||||
static bool ShouldDelegateOrder(string orderIdentifier) => ShouldDelegateOrderId(orderIdentifier.ToIdentifier());
|
||||
static bool ShouldDelegateOrderId(Identifier orderIdentifier)
|
||||
{
|
||||
return !(Character.Controlled is Character c) || !(c?.Info?.Job != null && c.Info.Job.Prefab.AppropriateOrders.Contains(orderIdentifier));
|
||||
return Character.Controlled is not Character c || !(c?.Info?.Job != null && c.Info.Job.Prefab.AppropriateOrders.Contains(orderIdentifier));
|
||||
}
|
||||
bool IsNonDuplicateOrder(Order order) => IsNonDuplicateOrderPrefab(order.Prefab, order.Option);
|
||||
bool IsNonDuplicateOrderPrefab(OrderPrefab orderPrefab, Identifier option = default)
|
||||
@@ -2642,6 +2669,7 @@ namespace Barotrauma
|
||||
new Order(p, itemContext, targetComponent));
|
||||
}
|
||||
}
|
||||
|
||||
// If targeting a periscope connected to a turret, show the 'operateweapons' order
|
||||
var operateWeaponsPrefab = OrderPrefab.Prefabs["operateweapons"];
|
||||
if (contextualOrders.None(o => o.Identifier == "operateweapons") && itemContext.Components.Any(c => c is Controller))
|
||||
@@ -2656,11 +2684,11 @@ namespace Barotrauma
|
||||
// If targeting a repairable item with condition below the repair threshold, show the 'repairsystems' order
|
||||
if (contextualOrders.None(order => order.Identifier == "repairsystems") && itemContext.Repairables.Any(r => r.IsBelowRepairThreshold))
|
||||
{
|
||||
if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical"))))
|
||||
if (itemContext.Repairables.Any(r => r != null && r.RequiredSkills.Any(s => s != null && s.Identifier.Equals("electrical"))))
|
||||
{
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["repairelectrical"], itemContext, targetItem: null));
|
||||
}
|
||||
else if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("mechanical"))))
|
||||
else if (itemContext.Repairables.Any(r => r != null && r.RequiredSkills.Any(s => s != null && s.Identifier.Equals("mechanical"))))
|
||||
{
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["repairmechanical"], itemContext, targetItem: null));
|
||||
}
|
||||
@@ -2694,14 +2722,14 @@ namespace Barotrauma
|
||||
}
|
||||
void AddIgnoreOrder(IIgnorable target)
|
||||
{
|
||||
var orderIdentifier = "ignorethis";
|
||||
var orderIdentifier = Tags.IgnoreThis;
|
||||
if (!target.OrderedToBeIgnored && contextualOrders.None(order => order.Identifier == orderIdentifier))
|
||||
{
|
||||
AddOrder();
|
||||
}
|
||||
else
|
||||
{
|
||||
orderIdentifier = "unignorethis";
|
||||
orderIdentifier = Tags.UnignoreThis;
|
||||
if (target.OrderedToBeIgnored && contextualOrders.None(order => order.Identifier == orderIdentifier))
|
||||
{
|
||||
AddOrder();
|
||||
@@ -2753,20 +2781,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: there's duplicate logic here and above -> would be better to refactor so that the conditions are only defined in one place
|
||||
public static bool DoesItemHaveContextualOrders(Item item)
|
||||
{
|
||||
if (OrderPrefab.Prefabs.Any(o => o.TargetItemsMatchItem(item))) { return true; }
|
||||
if (OrderPrefab.Prefabs.Any(o => o.TryGetTargetItemComponent(item, out _))) { return true; }
|
||||
if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled, checkInventory: false)) { return true; }
|
||||
if (AIObjectiveCleanupItems.IsValidContainer(item, Character.Controlled)) { return true; }
|
||||
if (OrderPrefab.Prefabs.TryGet("loaditems", out OrderPrefab loadItemsPrefab) && AIObjectiveLoadItems.IsValidTarget(item, Character.Controlled, targetContainerTags: loadItemsPrefab.GetTargetItems())) { return true; }
|
||||
if (item.Repairables.Any(r => r.IsBelowRepairThreshold)) { return true; }
|
||||
return OrderPrefab.Prefabs.TryGet("operateweapons", out OrderPrefab operateWeaponsPrefab) && item.Components.Any(c => c is Controller) &&
|
||||
(item.GetConnectedComponents<Turret>().Any(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item)) ||
|
||||
item.GetConnectedComponents<Turret>(recursive: true).Any(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item)));
|
||||
}
|
||||
|
||||
/// <param name="hotkey">Use a negative value (e.g. -1) if there should be no hotkey associated with the node</param>
|
||||
private GUIButton CreateOrderNode(Point size, RectTransform parent, Point offset, Order order, int hotkey, bool disableNode = false, bool checkIfOrderCanBeHeard = true)
|
||||
{
|
||||
@@ -3719,16 +3733,16 @@ namespace Barotrauma
|
||||
switch (order.TargetType)
|
||||
{
|
||||
case Order.OrderTargetType.Entity:
|
||||
if (!(order.TargetEntity is IIgnorable ignorableEntity)) { break; }
|
||||
ignorableEntity.OrderedToBeIgnored = order.Identifier == "ignorethis";
|
||||
if (order.TargetEntity is not IIgnorable ignorableEntity) { break; }
|
||||
ignorableEntity.OrderedToBeIgnored = order.Identifier == Tags.IgnoreThis;
|
||||
break;
|
||||
case Order.OrderTargetType.Position:
|
||||
throw new NotImplementedException();
|
||||
case Order.OrderTargetType.WallSection:
|
||||
if (!order.WallSectionIndex.HasValue) { break; }
|
||||
if (!(order.TargetEntity is Structure s)) { break; }
|
||||
if (!(s.GetSection(order.WallSectionIndex.Value) is IIgnorable ignorableWall)) { break; }
|
||||
ignorableWall.OrderedToBeIgnored = order.Identifier == "ignorethis";
|
||||
if (order.TargetEntity is not Structure s) { break; }
|
||||
if (s.GetSection(order.WallSectionIndex.Value) is not IIgnorable ignorableWall) { break; }
|
||||
ignorableWall.OrderedToBeIgnored = order.Identifier == Tags.IgnoreThis;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
abstract partial class CampaignMode : GameMode
|
||||
internal abstract partial class CampaignMode : GameMode
|
||||
{
|
||||
protected bool crewDead;
|
||||
public bool CrewDead
|
||||
{
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
protected Color overlayColor;
|
||||
protected Sprite overlaySprite;
|
||||
|
||||
@@ -67,10 +67,11 @@ namespace Barotrauma
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), layout.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.1f) }, isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
|
||||
var campaignContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), layout.RectTransform, Anchor.BottomLeft), style: "InnerFrame")
|
||||
var campaignContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), layout.RectTransform, Anchor.BottomLeft), style: "GUIFrameListBox")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
@@ -95,6 +96,7 @@ namespace Barotrauma
|
||||
loadCampaignButton.Selected = false;
|
||||
newCampaignContainer.Visible = true;
|
||||
loadCampaignContainer.Visible = false;
|
||||
GameMain.NetLobbyScreen?.RefreshStartButtonVisibility();
|
||||
return true;
|
||||
};
|
||||
loadCampaignButton.OnClicked = (btn, obj) =>
|
||||
@@ -103,6 +105,7 @@ namespace Barotrauma
|
||||
loadCampaignButton.Selected = true;
|
||||
newCampaignContainer.Visible = false;
|
||||
loadCampaignContainer.Visible = true;
|
||||
GameMain.NetLobbyScreen?.RefreshStartButtonVisibility();
|
||||
return true;
|
||||
};
|
||||
loadCampaignContainer.Visible = false;
|
||||
@@ -297,7 +300,7 @@ namespace Barotrauma
|
||||
Level prevLevel = Level.Loaded;
|
||||
|
||||
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
|
||||
crewDead = false;
|
||||
CrewDead = false;
|
||||
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
if (continueButton != null)
|
||||
@@ -480,7 +483,6 @@ namespace Barotrauma
|
||||
GameMain.CampaignEndScreen.OnFinished = () =>
|
||||
{
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
if (GameMain.NetLobbyScreen.ContinueCampaignButton != null) { GameMain.NetLobbyScreen.ContinueCampaignButton.Enabled = false; }
|
||||
if (GameMain.NetLobbyScreen.QuitCampaignButton != null) { GameMain.NetLobbyScreen.QuitCampaignButton.Enabled = false; }
|
||||
};
|
||||
}
|
||||
@@ -934,7 +936,7 @@ namespace Barotrauma
|
||||
{
|
||||
int renamedIdentifier = msg.ReadInt32();
|
||||
string newName = msg.ReadString();
|
||||
CharacterInfo renamedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
|
||||
CharacterInfo renamedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
|
||||
if (renamedCharacter != null) { CrewManager.RenameCharacter(renamedCharacter, newName); }
|
||||
}
|
||||
|
||||
@@ -942,7 +944,7 @@ namespace Barotrauma
|
||||
if (fireCharacter)
|
||||
{
|
||||
int firedIdentifier = msg.ReadInt32();
|
||||
CharacterInfo firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
|
||||
CharacterInfo firedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.GetIdentifier() == 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); }
|
||||
}
|
||||
@@ -952,7 +954,7 @@ namespace Barotrauma
|
||||
!NetIdUtils.IdMoreRecent(pendingSaveID, LastSaveID))
|
||||
{
|
||||
CampaignUI.CrewManagement.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters); }
|
||||
if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters, takeMoney: false); }
|
||||
CampaignUI.CrewManagement.SetPendingHires(pendingHires, map.CurrentLocation);
|
||||
if (renameCrewMember || fireCharacter) { CampaignUI.CrewManagement.UpdateCrew(); }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -243,7 +244,7 @@ namespace Barotrauma
|
||||
savedOnStart = true;
|
||||
}
|
||||
|
||||
crewDead = false;
|
||||
CrewDead = false;
|
||||
endTimer = 5.0f;
|
||||
CrewManager.InitSinglePlayerRound();
|
||||
LoadPets();
|
||||
@@ -373,7 +374,7 @@ namespace Barotrauma
|
||||
SoundPlayer.OverrideMusicType = (success ? "endround" : "crewdead").ToIdentifier();
|
||||
SoundPlayer.OverrideMusicDuration = 18.0f;
|
||||
GUI.SetSavingIndicatorState(success);
|
||||
crewDead = false;
|
||||
CrewDead = false;
|
||||
|
||||
if (success)
|
||||
{
|
||||
@@ -582,9 +583,12 @@ namespace Barotrauma
|
||||
HintManager.OnAvailableTransition(transitionType);
|
||||
}
|
||||
|
||||
if (!crewDead)
|
||||
if (!CrewDead)
|
||||
{
|
||||
if (!CrewManager.GetCharacters().Any(c => !c.IsDead)) { crewDead = true; }
|
||||
if (CrewManager.GetCharacters().None(c => !c.IsDead && !CrewManager.IsFired(c)))
|
||||
{
|
||||
CrewDead = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -42,6 +42,15 @@ namespace Barotrauma
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
submarine.NeutralizeBallast();
|
||||
//normally the body would be made static during level generation,
|
||||
//but in the test mode we load the outpost/wreck/beacon as if it was a normal sub and need to do this manually
|
||||
if (submarine.Info.Type == SubmarineType.Outpost ||
|
||||
submarine.Info.Type == SubmarineType.OutpostModule ||
|
||||
submarine.Info.Type == SubmarineType.Wreck ||
|
||||
submarine.Info.Type == SubmarineType.BeaconStation)
|
||||
{
|
||||
submarine.PhysicsBody.BodyType = FarseerPhysics.BodyType.Static;
|
||||
}
|
||||
}
|
||||
|
||||
if (SpawnOutpost)
|
||||
@@ -51,14 +60,14 @@ namespace Barotrauma
|
||||
|
||||
if (TriggeredEvent != null)
|
||||
{
|
||||
scriptedEvent = new List<Event> { TriggeredEvent.CreateInstance() };
|
||||
scriptedEvent = new List<Event> { TriggeredEvent.CreateInstance(GameMain.GameSession.EventManager.RandomSeed) };
|
||||
GameMain.GameSession.EventManager.PinnedEvent = scriptedEvent.Last();
|
||||
|
||||
createEventButton = new GUIButton(new RectTransform(new Point(128, 64), GUI.Canvas, Anchor.TopCenter) { ScreenSpaceOffset = new Point(0, 32) }, TextManager.Get("create"))
|
||||
{
|
||||
OnClicked = delegate
|
||||
{
|
||||
scriptedEvent.Add(TriggeredEvent.CreateInstance());
|
||||
scriptedEvent.Add(TriggeredEvent.CreateInstance(GameMain.GameSession.EventManager.RandomSeed));
|
||||
GameMain.GameSession.EventManager.PinnedEvent = scriptedEvent.Last();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -188,9 +188,9 @@ namespace Barotrauma.Tutorials
|
||||
var door = item.GetComponent<Door>();
|
||||
if (door != null)
|
||||
{
|
||||
if (door.requiredItems.Values.None(ris => ris.None(ri => ri.Identifiers.None(i => i == "locked"))))
|
||||
if (door.RequiredItems.Values.None(ris => ris.None(ri => ri.Identifiers.None(i => i == "locked"))))
|
||||
{
|
||||
door.requiredItems.Clear();
|
||||
door.RequiredItems.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,7 +262,7 @@ namespace Barotrauma.Tutorials
|
||||
yield return CoroutineStatus.Failure;
|
||||
}
|
||||
|
||||
if (eventPrefab.CreateInstance() is Event eventInstance)
|
||||
if (eventPrefab.CreateInstance(GameMain.GameSession.EventManager.RandomSeed) is Event eventInstance)
|
||||
{
|
||||
GameMain.GameSession.EventManager.QueuedEvents.Enqueue(eventInstance);
|
||||
while (!eventInstance.IsFinished)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -110,7 +111,9 @@ namespace Barotrauma
|
||||
};
|
||||
respawnTickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, respawnButtonContainer.RectTransform, Anchor.Center), TextManager.Get("respawnquestionpromptrespawn"))
|
||||
{
|
||||
ToolTip = TextManager.Get("respawnquestionprompt"),
|
||||
ToolTip = TextManager.GetWithVariable(
|
||||
"respawnquestionprompt", "[percentage]",
|
||||
(Math.Round(Networking.RespawnManager.SkillLossPercentageOnImmediateRespawn).ToString())),
|
||||
OnSelected = (tickbox) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: !tickbox.Selected);
|
||||
|
||||
@@ -185,6 +185,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnItemMarkedForRelocation()
|
||||
{
|
||||
DisplayHint($"onitemmarkedforrelocation".ToIdentifier());
|
||||
}
|
||||
|
||||
public static void OnItemMarkedForDeconstruction(Character character)
|
||||
{
|
||||
if (character == Character.Controlled)
|
||||
{
|
||||
DisplayHint($"onitemmarkedfordeconstruction".ToIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckIsInteracting()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
@@ -582,6 +595,24 @@ namespace Barotrauma
|
||||
{
|
||||
DisplayHint("onballastflorainfected".ToIdentifier());
|
||||
}
|
||||
if (order.Identifier == "deconstructitems" &&
|
||||
Item.DeconstructItems.None())
|
||||
{
|
||||
DisplayHint("ondeconstructorder".ToIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnSetOrder(Character character, Order order)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character == null || order == null) { return; }
|
||||
|
||||
if (order.OrderGiver == Character.Controlled &&
|
||||
order.Identifier == "deconstructitems" &&
|
||||
Item.DeconstructItems.None())
|
||||
{
|
||||
DisplayHint("ondeconstructorder".ToIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckIfDivingGearOutOfOxygen()
|
||||
@@ -719,7 +750,7 @@ namespace Barotrauma
|
||||
|
||||
HintsIgnoredThisRound.Add(hintIdentifier);
|
||||
|
||||
ActiveHintMessageBox = new GUIMessageBox(hintIdentifier, text, icon);
|
||||
ActiveHintMessageBox = new GUIMessageBox(hintIdentifier, TextManager.ParseInputTypes(text), icon);
|
||||
if (iconColor.HasValue) { ActiveHintMessageBox.IconColor = iconColor.Value; }
|
||||
OnUpdate = onUpdate;
|
||||
|
||||
|
||||
@@ -822,7 +822,7 @@ namespace Barotrauma
|
||||
factionTextContent.Recalculate();
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform),
|
||||
onDraw: (sb, customComponent) => DrawReputationBar(sb, customComponent.Rect, reputation.NormalizedValue));
|
||||
onDraw: (sb, customComponent) => DrawReputationBar(sb, customComponent.Rect, reputation.NormalizedValue, reputation.MinReputation, reputation.MaxReputation));
|
||||
|
||||
var reputationText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
|
||||
string.Empty, textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont);
|
||||
@@ -871,7 +871,7 @@ namespace Barotrauma
|
||||
return factionFrame;
|
||||
}
|
||||
|
||||
public static void DrawReputationBar(SpriteBatch sb, Rectangle rect, float normalizedReputation)
|
||||
public static void DrawReputationBar(SpriteBatch sb, Rectangle rect, float normalizedReputation, float minReputation, float maxReputation)
|
||||
{
|
||||
int segmentWidth = rect.Width / 5;
|
||||
rect.Width = segmentWidth * 5;
|
||||
@@ -885,9 +885,10 @@ namespace Barotrauma
|
||||
GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUIStyle.ColorInventoryBackground, scale: GUI.Scale, spriteEffect: SpriteEffects.FlipVertically);
|
||||
GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUIStyle.TextColorNormal, scale: GUI.Scale * 0.8f, spriteEffect: SpriteEffects.FlipVertically);
|
||||
|
||||
GUI.DrawString(sb, new Vector2(rect.X, rect.Bottom), "-100", GUIStyle.TextColorNormal, font: GUIStyle.SmallFont);
|
||||
Vector2 textSize = GUIStyle.SmallFont.MeasureString("100");
|
||||
GUI.DrawString(sb, new Vector2(rect.Right - textSize.X, rect.Bottom), "100", GUIStyle.TextColorNormal, font: GUIStyle.SmallFont);
|
||||
GUI.DrawString(sb, new Vector2(rect.X, rect.Bottom), ((int)minReputation).ToString(), GUIStyle.TextColorNormal, font: GUIStyle.SmallFont);
|
||||
string maxRepText = ((int)maxReputation).ToString();
|
||||
Vector2 textSize = GUIStyle.SmallFont.MeasureString(maxRepText);
|
||||
GUI.DrawString(sb, new Vector2(rect.Right - textSize.X, rect.Bottom), maxRepText, GUIStyle.TextColorNormal, font: GUIStyle.SmallFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -13,7 +12,7 @@ namespace Barotrauma
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
Speak(character);
|
||||
character.Speak(text, ChatMessageType.Default);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -21,23 +20,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (npc.CampaignInteractionType == CampaignMode.InteractionType.Upgrade)
|
||||
{
|
||||
Speak(npc);
|
||||
npc.Speak(text, ChatMessageType.Default);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Speak(Character npc)
|
||||
{
|
||||
ChatMessage message = ChatMessage.Create(npc.Name, text, ChatMessageType.Default, npc);
|
||||
if (!isSinglePlayer)
|
||||
{
|
||||
GameMain.Client?.AddChatMessage(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.AddSinglePlayerChatMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -771,7 +771,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
if (item.NonInteractable || item.NonPlayerTeamInteractable)
|
||||
if (!item.IsInteractable(Character.Controlled))
|
||||
{
|
||||
return QuickUseAction.None;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace Barotrauma.Items.Components
|
||||
private void UpdateConvexHulls()
|
||||
{
|
||||
if (item.Removed) { return; }
|
||||
if (doorSprite == null) { return; }
|
||||
|
||||
doorRect = new Rectangle(
|
||||
item.Rect.Center.X - (int)(doorSprite.size.X / 2 * item.Scale),
|
||||
@@ -83,7 +84,7 @@ namespace Barotrauma.Items.Components
|
||||
(int)(doorSprite.size.Y * item.Scale));
|
||||
|
||||
Rectangle rect = doorRect;
|
||||
if (IsHorizontal)
|
||||
if (IsConvexHullHorizontal)
|
||||
{
|
||||
rect.Width = (int)(rect.Width * (1.0f - openState));
|
||||
}
|
||||
@@ -94,7 +95,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (Window.Height > 0 && Window.Width > 0)
|
||||
{
|
||||
if (IsHorizontal)
|
||||
if (IsConvexHullHorizontal)
|
||||
{
|
||||
rect.Width = (int)(Window.X * item.Scale);
|
||||
rect.X -= (int)(doorRect.Width * openState);
|
||||
@@ -163,8 +164,8 @@ namespace Barotrauma.Items.Components
|
||||
var verts = GetConvexHullCorners(rect);
|
||||
Vector2 center = (verts[0] + verts[2]) / 2;
|
||||
convexHull.SetVertices(
|
||||
verts,
|
||||
IsHorizontal ?
|
||||
verts,
|
||||
IsConvexHullHorizontal ?
|
||||
new Vector2[] { new Vector2(verts[0].X, center.Y), new Vector2(verts[2].X, center.Y) } :
|
||||
new Vector2[] { new Vector2(center.X, verts[0].Y), new Vector2(center.X, verts[2].Y) });
|
||||
convexHull.MaxMergeLosVerticesDist = 35.0f;
|
||||
@@ -304,6 +305,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
bool stateChanged = open != isOpen;
|
||||
isOpen = open;
|
||||
if (!isNetworkMessage || open != PredictedState)
|
||||
{
|
||||
@@ -314,6 +316,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (isOpen) { stuck = MathHelper.Clamp(stuck - StuckReductionOnOpen, 0.0f, 100.0f); }
|
||||
}
|
||||
if (stateChanged)
|
||||
{
|
||||
ActionType actionType = open ? ActionType.OnOpen : ActionType.OnClose;
|
||||
item.ApplyStatusEffects(actionType, deltaTime: 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayInteractionSound()
|
||||
|
||||
@@ -64,7 +64,8 @@ namespace Barotrauma.Items.Components
|
||||
spriteBatch,
|
||||
new Vector2(attachPos.X, -attachPos.Y),
|
||||
item.SpriteColor * 0.5f,
|
||||
0.0f, item.Scale, SpriteEffects.None, 0.0f);
|
||||
item.RotationRad,
|
||||
item.Scale, SpriteEffects.None, 0.0f);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(attachPos.X - 2, -attachPos.Y - 2), Vector2.One * 5, GUIStyle.Red, thickness: 3);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,31 @@ namespace Barotrauma.Items.Components
|
||||
protected float currentCrossHairScale, currentCrossHairPointerScale;
|
||||
|
||||
private RoundSound chargeSound;
|
||||
|
||||
private SoundChannel chargeSoundChannel;
|
||||
|
||||
[Serialize(defaultValue: "0.5, 1.5", IsPropertySaveable.No, description: "Pitch slides from X to Y over the charge time")]
|
||||
public Vector2 ChargeSoundWindupPitchSlide
|
||||
{
|
||||
get => _chargeSoundWindupPitchSlide;
|
||||
set
|
||||
{
|
||||
_chargeSoundWindupPitchSlide = new Vector2(
|
||||
Math.Max(value.X, SoundChannel.MinFrequencyMultiplier),
|
||||
Math.Min(value.Y, SoundChannel.MaxFrequencyMultiplier));
|
||||
}
|
||||
}
|
||||
private Vector2 _chargeSoundWindupPitchSlide;
|
||||
|
||||
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||
private readonly List<ParticleEmitter> particleEmitterCharges = new List<ParticleEmitter>();
|
||||
|
||||
/// <summary>
|
||||
/// The orientation of the item is briefly wrong after the character holding it flips and before the holding logic forces it to the correct position.
|
||||
/// We disable the crosshair briefly during that time to prevent it from momentarily jumping to an incorrect position.
|
||||
/// </summary>
|
||||
private float crossHairPosDirtyTimer;
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.No, description: "The scale of the crosshair sprite (if there is one).")]
|
||||
public float CrossHairScale
|
||||
{
|
||||
@@ -30,9 +50,9 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
partial void InitProjSpecific(ContentXElement rangedWeaponElement)
|
||||
{
|
||||
foreach (var subElement in element.Elements())
|
||||
foreach (var subElement in rangedWeaponElement.Elements())
|
||||
{
|
||||
string textureDir = GetTextureDirectory(subElement);
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -62,6 +82,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
crossHairPosDirtyTimer -= deltaTime;
|
||||
currentCrossHairScale = currentCrossHairPointerScale = cam == null ? 1.0f : cam.Zoom;
|
||||
if (crosshairSprite != null)
|
||||
{
|
||||
@@ -92,6 +113,15 @@ namespace Barotrauma.Items.Components
|
||||
crosshairPointerPos = PlayerInput.MousePosition;
|
||||
}
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
crossHairPosDirtyTimer = 0.02f;
|
||||
}
|
||||
public override void FlipY(bool relativeToSub)
|
||||
{
|
||||
crossHairPosDirtyTimer = 0.02f;
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
float chargeRatio = currentChargeTime / MaxChargeTime;
|
||||
@@ -117,7 +147,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (chargeSoundChannel != null)
|
||||
{
|
||||
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(0.5f, 1.5f, chargeRatio);
|
||||
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(ChargeSoundWindupPitchSlide.X, ChargeSoundWindupPitchSlide.Y, chargeRatio);
|
||||
chargeSoundChannel.Position = new Vector3(item.WorldPosition, 0.0f);
|
||||
}
|
||||
break;
|
||||
@@ -143,15 +173,19 @@ namespace Barotrauma.Items.Components
|
||||
if (character == null || !character.IsKeyDown(InputType.Aim) || !character.CanAim) { return; }
|
||||
|
||||
//camera focused on some other item/device, don't draw the crosshair
|
||||
if (character.ViewTarget != null && (character.ViewTarget is Item viewTargetItem) && viewTargetItem.Prefab.FocusOnSelected) { return; }
|
||||
if (character.ViewTarget is Item viewTargetItem && viewTargetItem.Prefab.FocusOnSelected) { return; }
|
||||
//don't draw the crosshair if the item is in some other type of equip slot than hands (e.g. assault rifle in the bag slot)
|
||||
if (!character.HeldItems.Contains(item)) { return; }
|
||||
|
||||
GUI.HideCursor = (crosshairSprite != null || crosshairPointerSprite != null) &&
|
||||
GUI.MouseOn == null && !Inventory.IsMouseOnInventory && !GameMain.Instance.Paused;
|
||||
if (GUI.HideCursor)
|
||||
|
||||
if (GUI.HideCursor && !character.AnimController.IsHoldingToRope)
|
||||
{
|
||||
crosshairSprite?.Draw(spriteBatch, crosshairPos, ReloadTimer <= 0.0f ? Color.White : Color.White * 0.2f, 0, currentCrossHairScale);
|
||||
if (crossHairPosDirtyTimer <= 0.0f)
|
||||
{
|
||||
crosshairSprite?.Draw(spriteBatch, crosshairPos, ReloadTimer <= 0.0f ? Color.White : Color.White * 0.2f, 0, currentCrossHairScale);
|
||||
}
|
||||
crosshairPointerSprite?.Draw(spriteBatch, crosshairPointerPos, 0, currentCrossHairPointerScale);
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace Barotrauma.Items.Components
|
||||
int buttonSize = GUIStyle.ItemFrameTopBarHeight;
|
||||
Point margin = new Point(buttonSize / 4, buttonSize / 6);
|
||||
|
||||
GUILayoutGroup buttonArea = new GUILayoutGroup(new RectTransform(new Point(GuiFrame.Rect.Width - margin.X * 2, buttonSize - margin.Y * 2), GuiFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, margin.Y) },
|
||||
GUILayoutGroup buttonArea = new GUILayoutGroup(new RectTransform(new Point(content.Rect.Width, buttonSize - margin.Y * 2), content.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(0, margin.Y) },
|
||||
isHorizontal: true, childAnchor: Anchor.TopRight)
|
||||
{
|
||||
AbsoluteSpacing = margin.X / 2
|
||||
@@ -334,7 +334,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
return item?.Name;
|
||||
return item?.Prefab.Name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
partial void SetLightSourceState(bool enabled, float brightness)
|
||||
{
|
||||
if (Light == null) { return; }
|
||||
if (item.HiddenInGame) { enabled = false; }
|
||||
Light.Enabled = enabled;
|
||||
lightColorMultiplier = brightness;
|
||||
if (enabled)
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Barotrauma.Items.Components
|
||||
RelativeSpacing = 0.08f
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1f, 0.07f), paddedFrame.RectTransform) { MinSize = new Point(0, GUI.IntScale(25)) }, item.Name, font: GUIStyle.SubHeadingFont)
|
||||
new GUITextBlock(new RectTransform(new Vector2(1f, 0.07f), paddedFrame.RectTransform) { MinSize = new Point(0, GUI.IntScale(25)) }, item.Prefab.Name, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
AutoScaleHorizontal = true
|
||||
@@ -341,7 +341,7 @@ namespace Barotrauma.Items.Components
|
||||
GUIFrame itemFrame = new GUIFrame(new RectTransform(new Vector2(0.1f, 1f), parent.RectTransform), style: null)
|
||||
{
|
||||
UserData = identifier,
|
||||
ToolTip = GetTooltip(prefab)
|
||||
ToolTip = prefab.CreateTooltipText()
|
||||
};
|
||||
|
||||
Sprite icon = prefab.InventoryIcon ?? prefab.Sprite;
|
||||
@@ -372,21 +372,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
textBlock.Text = TextManager.GetWithVariable("campaignstore.quantity", "[amount]", count.ToString());
|
||||
}
|
||||
|
||||
static RichString GetTooltip(ItemPrefab prefab)
|
||||
{
|
||||
LocalizedString toolTip = $"‖color:{Color.White.ToStringHex()}‖{prefab.Name}‖color:end‖";
|
||||
|
||||
LocalizedString description = prefab.Description;
|
||||
if (!description.IsNullOrEmpty()) { toolTip += '\n' + description; }
|
||||
|
||||
if (prefab.ContentPackage != GameMain.VanillaContent && prefab.ContentPackage != null)
|
||||
{
|
||||
toolTip += $"\n‖color:{Color.MediumPurple.ToStringHex()}‖{prefab.ContentPackage.Name}‖color:end‖";
|
||||
}
|
||||
|
||||
return RichString.Rich(toolTip);
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnItemLoadedProjSpecific()
|
||||
|
||||
@@ -134,11 +134,11 @@ namespace Barotrauma.Items.Components
|
||||
spriteIndex += (force / 100.0f) * AnimSpeed * deltaTime;
|
||||
if (spriteIndex < 0)
|
||||
{
|
||||
spriteIndex = propellerSprite.FrameCount;
|
||||
spriteIndex = propellerSprite.FrameCount - Math.Abs(spriteIndex) % propellerSprite.FrameCount;
|
||||
}
|
||||
if (spriteIndex >= propellerSprite.FrameCount)
|
||||
else
|
||||
{
|
||||
spriteIndex = 0.0f;
|
||||
spriteIndex = spriteIndex % propellerSprite.FrameCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Barotrauma.Items.Components
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter);
|
||||
|
||||
// === LABEL === //
|
||||
new GUITextBlock(new RectTransform(new Vector2(1f, 0.05f), paddedFrame.RectTransform), item.Name, font: GUIStyle.SubHeadingFont)
|
||||
new GUITextBlock(new RectTransform(new Vector2(1f, 0.05f), paddedFrame.RectTransform), item.Prefab.Name, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
AutoScaleVertical = true
|
||||
@@ -326,7 +326,7 @@ namespace Barotrauma.Items.Components
|
||||
UserData = fi,
|
||||
HoverColor = Color.Gold * 0.2f,
|
||||
SelectedColor = Color.Gold * 0.5f,
|
||||
ToolTip = fi.TargetItem.Description
|
||||
ToolTip = RichString.Rich(fi.TargetItem.Description)
|
||||
};
|
||||
|
||||
var container = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform),
|
||||
@@ -339,7 +339,7 @@ namespace Barotrauma.Items.Components
|
||||
itemIcon, scaleToFit: true)
|
||||
{
|
||||
Color = fi.TargetItem.InventoryIconColor,
|
||||
ToolTip = fi.TargetItem.Description
|
||||
ToolTip = RichString.Rich(fi.TargetItem.Description)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
AutoScaleVertical = true,
|
||||
ToolTip = fi.TargetItem.Description
|
||||
ToolTip = RichString.Rich(fi.TargetItem.Description)
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), frame.RectTransform, Anchor.BottomRight),
|
||||
@@ -925,8 +925,6 @@ namespace Barotrauma.Items.Components
|
||||
nameBlock.Padding = new Vector4(0, nameBlock.Padding.Y, GUI.IntScale(5), nameBlock.Padding.W);
|
||||
if (nameBlock.TextScale < 0.7f)
|
||||
{
|
||||
nameBlock.SetRichText(TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", itemName)
|
||||
.Fallback(TextManager.GetWithVariable("itemname.quality3", "[itemname]", itemName)));
|
||||
nameBlock.AutoScaleHorizontal = false;
|
||||
nameBlock.TextScale = 0.7f;
|
||||
nameBlock.Wrap = true;
|
||||
@@ -937,7 +935,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!selectedItem.TargetItem.Description.IsNullOrEmpty())
|
||||
{
|
||||
var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
|
||||
selectedItem.TargetItem.Description,
|
||||
RichString.Rich(selectedItem.TargetItem.Description),
|
||||
font: GUIStyle.SmallFont, wrap: true);
|
||||
description.Padding = new Vector4(0, description.Padding.Y, description.Padding.Z, description.Padding.W);
|
||||
|
||||
|
||||
@@ -1090,7 +1090,12 @@ namespace Barotrauma.Items.Components
|
||||
float totalVolume = 0.0f;
|
||||
foreach (Hull linkedHull in hullData.LinkedHulls)
|
||||
{
|
||||
waterVolume += linkedHull.WaterVolume;
|
||||
//water detector ignores very small amounts of water,
|
||||
//do it here too so the nav terminal doesn't display the water
|
||||
if (WaterDetector.GetWaterPercentage(linkedHull) > 0.0f)
|
||||
{
|
||||
waterVolume += linkedHull.WaterVolume;
|
||||
}
|
||||
totalVolume += linkedHull.Volume;
|
||||
}
|
||||
hullData.HullWaterAmount =
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign == null)
|
||||
if (GameMain.GameSession?.Campaign == null || !Level.IsLoadedFriendlyOutpost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1446,7 +1446,7 @@ namespace Barotrauma.Items.Components
|
||||
//only relevant in the end levels or maybe custom subs with some kind of non-hulled parts
|
||||
Rectangle worldBorders = submarine.GetDockedBorders();
|
||||
worldBorders.Location += submarine.WorldPosition.ToPoint();
|
||||
if (Submarine.RectContains(worldBorders, pingSource))
|
||||
if (Submarine.RectContains(worldBorders, pingSource) || submarine.Info.OutpostGenerationParams is { AlwaysShowStructuresOnSonar: true })
|
||||
{
|
||||
CreateBlipsForSubmarineWalls(submarine, pingSource, transducerPos, pingRadius, prevPingRadius, range, passive);
|
||||
continue;
|
||||
@@ -1502,7 +1502,9 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Voronoi2.GraphEdge edge in cell.Edges)
|
||||
{
|
||||
if (!edge.IsSolid) { continue; }
|
||||
float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center);
|
||||
|
||||
//the normal of the edge must be pointing towards the ping source to be visible
|
||||
float cellDot = Vector2.Dot((edge.Center + cell.Translation) - pingSource, edge.GetNormal(cell));
|
||||
if (cellDot > 0) { continue; }
|
||||
|
||||
float facingDot = Vector2.Dot(
|
||||
@@ -1543,6 +1545,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (c.AnimController.CurrentHull != null || !c.Enabled) { continue; }
|
||||
if (!c.IsUnconscious && c.Params.HideInSonar) { continue; }
|
||||
if (c.InDetectable) { continue; }
|
||||
if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) { continue; }
|
||||
|
||||
if (c.AnimController.SimplePhysicsEnabled)
|
||||
|
||||
@@ -316,28 +316,28 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
case 0:
|
||||
leftText = TextManager.Get("DescentVelocity");
|
||||
centerText = $"({TextManager.Get("KilometersPerHour")})";
|
||||
centerText = TextManager.Get("KilometersPerHour");
|
||||
rightTextGetter = () =>
|
||||
{
|
||||
Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
|
||||
var realWorldVel = ConvertUnits.ToDisplayUnits(vel.Y * Physics.DisplayToRealWorldRatio) * 3.6f;
|
||||
return ((int)(-realWorldVel)).ToString();
|
||||
return (-realWorldVel).ToString("0.0");
|
||||
};
|
||||
break;
|
||||
case 1:
|
||||
leftText = TextManager.Get("Velocity");
|
||||
centerText = $"({TextManager.Get("KilometersPerHour")})";
|
||||
centerText = TextManager.Get("KilometersPerHour");
|
||||
rightTextGetter = () =>
|
||||
{
|
||||
Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
|
||||
var realWorldVel = ConvertUnits.ToDisplayUnits(vel.X * Physics.DisplayToRealWorldRatio) * 3.6f;
|
||||
if (controlledSub != null && controlledSub.FlippedX) { realWorldVel *= -1; }
|
||||
return ((int)realWorldVel).ToString();
|
||||
return realWorldVel.ToString("0.0");
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
leftText = TextManager.Get("Depth");
|
||||
centerText = $"({TextManager.Get("Meter")})";
|
||||
centerText = TextManager.Get("Meter");
|
||||
rightTextGetter = () =>
|
||||
{
|
||||
if (Level.Loaded is { IsEndBiome: true })
|
||||
@@ -789,9 +789,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
pressureWarningText.Visible = item.Submarine != null && Timing.TotalTime % 1.0f < 0.8f;
|
||||
float depthEffectThreshold = 500.0f;
|
||||
if (Level.Loaded != null && pressureWarningText.Visible &&
|
||||
item.Submarine.RealWorldDepth > Level.Loaded.RealWorldCrushDepth - depthEffectThreshold && item.Submarine.RealWorldDepth > item.Submarine.RealWorldCrushDepth - depthEffectThreshold)
|
||||
item.Submarine.RealWorldDepth > Level.Loaded.RealWorldCrushDepth - PressureWarningThreshold &&
|
||||
item.Submarine.RealWorldDepth > item.Submarine.RealWorldCrushDepth - PressureWarningThreshold)
|
||||
{
|
||||
pressureWarningText.Visible = true;
|
||||
pressureWarningText.Text =
|
||||
|
||||
@@ -148,11 +148,29 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 particlePos = item.WorldPosition;
|
||||
float rotation = -item.body.Rotation;
|
||||
if (item.body.Dir < 0.0f) { rotation += MathHelper.Pi; }
|
||||
|
||||
//if the position is in a sub's local coordinates, convert to world coordinates
|
||||
particlePos = ConvertToWorldCoordinates(particlePos);
|
||||
//if the start location is in a sub's local coordinates, convert to world coordinates
|
||||
startLocation = ConvertToWorldCoordinates(startLocation);
|
||||
//same for end location
|
||||
endLocation = ConvertToWorldCoordinates(endLocation);
|
||||
|
||||
Tuple<Vector2, Vector2> tracerPoints = new Tuple<Vector2, Vector2>(startLocation, endLocation);
|
||||
foreach (ParticleEmitter emitter in particleEmitters)
|
||||
{
|
||||
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: rotation, particleRotation: rotation, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier, tracerPoints: tracerPoints);
|
||||
}
|
||||
|
||||
static Vector2 ConvertToWorldCoordinates(Vector2 position)
|
||||
{
|
||||
Submarine containing = Submarine.FindContainingInLocalCoordinates(position);
|
||||
if (containing != null)
|
||||
{
|
||||
position += containing.Position;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
|
||||
@@ -63,10 +63,7 @@ namespace Barotrauma.Items.Components
|
||||
if (item.HiddenInGame) { return false; }
|
||||
if (!HasRequiredItems(character, false) || character.SelectedItem != item) { return false; }
|
||||
if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; }
|
||||
|
||||
float defaultMaxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier;
|
||||
|
||||
if (MathUtils.Percentage(item.Condition, defaultMaxCondition) < RepairThreshold) { return true; }
|
||||
if (item.ConditionPercentageRelativeToDefaultMaxCondition < RepairThreshold) { return true; }
|
||||
|
||||
if (CurrentFixer == character)
|
||||
{
|
||||
@@ -135,13 +132,13 @@ namespace Barotrauma.Items.Components
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
|
||||
TextManager.Get("RequiredRepairSkills"), font: GUIStyle.SubHeadingFont);
|
||||
skillTextContainer = paddedFrame;
|
||||
for (int i = 0; i < requiredSkills.Count; i++)
|
||||
for (int i = 0; i < RequiredSkills.Count; i++)
|
||||
{
|
||||
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillTextContainer.RectTransform),
|
||||
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + requiredSkills[i].Identifier), ((int) Math.Round(requiredSkills[i].Level * SkillRequirementMultiplier)).ToString()),
|
||||
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + RequiredSkills[i].Identifier), ((int) Math.Round(RequiredSkills[i].Level * SkillRequirementMultiplier)).ToString()),
|
||||
font: GUIStyle.SmallFont)
|
||||
{
|
||||
UserData = requiredSkills[i]
|
||||
UserData = RequiredSkills[i]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -262,7 +259,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
float conditionPercentage = item.Condition / (item.MaxCondition / item.MaxRepairConditionMultiplier) * 100f;
|
||||
float conditionPercentage = item.ConditionPercentageRelativeToDefaultMaxCondition;
|
||||
|
||||
for (int i = 0; i < particleEmitters.Count; i++)
|
||||
{
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Sounds;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Rope : ItemComponent, IDrawableComponent
|
||||
{
|
||||
private Sprite sprite, startSprite, endSprite;
|
||||
|
||||
private RoundSound snapSound, reelSound;
|
||||
private SoundChannel reelSoundChannel;
|
||||
|
||||
[Serialize(5, IsPropertySaveable.No)]
|
||||
public int SpriteWidth
|
||||
@@ -54,6 +57,19 @@ namespace Barotrauma.Items.Components
|
||||
Math.Abs(target.DrawPosition.Y - sourcePos.Y)) * 1.5f;
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize("1.0, 1.0", IsPropertySaveable.No, description: "When reeling in, the pitch slides from X to Y, depending on the length of the rope.")]
|
||||
public Vector2 ReelSoundPitchSlide
|
||||
{
|
||||
get => _reelSoundPitchSlide;
|
||||
set
|
||||
{
|
||||
_reelSoundPitchSlide = new Vector2(
|
||||
Math.Max(value.X, SoundChannel.MinFrequencyMultiplier),
|
||||
Math.Min(value.Y, SoundChannel.MaxFrequencyMultiplier));
|
||||
}
|
||||
}
|
||||
private Vector2 _reelSoundPitchSlide;
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
{
|
||||
@@ -70,9 +86,28 @@ namespace Barotrauma.Items.Components
|
||||
case "endsprite":
|
||||
endSprite = new Sprite(subElement);
|
||||
break;
|
||||
case "snapsound":
|
||||
snapSound = RoundSound.Load(subElement);
|
||||
break;
|
||||
case "reelsound":
|
||||
reelSound = RoundSound.Load(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific()
|
||||
{
|
||||
if (isReelingIn && !Snapped)
|
||||
{
|
||||
PlaySound(reelSound, source.WorldPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
reelSoundChannel?.FadeOutAndDispose();
|
||||
reelSoundChannel = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
@@ -184,6 +219,32 @@ namespace Barotrauma.Items.Components
|
||||
overrideColor ?? SpriteColor, depth: depth, width: width);
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaySound(RoundSound sound, Vector2 position)
|
||||
{
|
||||
if (sound == null) { return; }
|
||||
if (sound == reelSound)
|
||||
{
|
||||
if (reelSoundChannel is not { IsPlaying: true })
|
||||
{
|
||||
reelSoundChannel = SoundPlayer.PlaySound(sound.Sound, position, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling, freqMult: sound.GetRandomFrequencyMultiplier());
|
||||
if (reelSoundChannel != null)
|
||||
{
|
||||
reelSoundChannel.Looping = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reelSoundChannel.Position = new Vector3(position, 0);
|
||||
reelSoundChannel.Gain = MathHelper.Lerp(0, 1.0f, MathUtils.InverseLerp(MinPullDistance, MaxLength, MathUtils.Pow(currentRopeLength, 1.5f)));
|
||||
reelSoundChannel.FrequencyMultiplier = MathHelper.Lerp(ReelSoundPitchSlide.X, ReelSoundPitchSlide.Y, MathUtils.InverseLerp(MinPullDistance, MaxLength, currentRopeLength));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SoundPlayer.PlaySound(sound.Sound, position, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling, freqMult: sound.GetRandomFrequencyMultiplier());
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
@@ -191,30 +252,38 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!snapped)
|
||||
{
|
||||
UInt16 targetId = msg.ReadUInt16();
|
||||
UInt16 sourceId = msg.ReadUInt16();
|
||||
ushort targetId = msg.ReadUInt16();
|
||||
ushort sourceId = msg.ReadUInt16();
|
||||
byte limbIndex = msg.ReadByte();
|
||||
|
||||
Item target = Entity.FindEntityByID(targetId) as Item;
|
||||
if (target == null) { return; }
|
||||
if (Entity.FindEntityByID(targetId) is not Item target) { return; }
|
||||
var source = Entity.FindEntityByID(sourceId);
|
||||
if (source is Character sourceCharacter && limbIndex >= 0 && limbIndex < sourceCharacter.AnimController.Limbs.Length)
|
||||
switch (source)
|
||||
{
|
||||
Limb sourceLimb = sourceCharacter.AnimController.Limbs[limbIndex];
|
||||
Attach(sourceLimb, target);
|
||||
}
|
||||
else if (source is ISpatialEntity spatialEntity)
|
||||
{
|
||||
Attach(spatialEntity, target);
|
||||
case Character sourceCharacter when limbIndex >= 0 && limbIndex < sourceCharacter.AnimController.Limbs.Length:
|
||||
{
|
||||
Limb sourceLimb = sourceCharacter.AnimController.Limbs[limbIndex];
|
||||
Attach(sourceLimb, target);
|
||||
sourceCharacter.AnimController.DragWithRope();
|
||||
break;
|
||||
}
|
||||
case ISpatialEntity spatialEntity:
|
||||
Attach(spatialEntity, target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
sprite?.Remove(); sprite = null;
|
||||
startSprite?.Remove(); startSprite = null;
|
||||
endSprite?.Remove(); endSprite = null;
|
||||
sprite?.Remove();
|
||||
sprite = null;
|
||||
startSprite?.Remove();
|
||||
startSprite = null;
|
||||
endSprite?.Remove();
|
||||
endSprite = null;
|
||||
reelSoundChannel?.FadeOutAndDispose();
|
||||
reelSoundChannel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void RemoveComponents(IReadOnlyCollection<CircuitBoxComponent> node)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
var ids = node.Select(static n => n.ID).ToImmutableArray();
|
||||
|
||||
if (GameMain.NetworkMember is null)
|
||||
@@ -145,6 +146,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void AddWire(CircuitBoxConnection one, CircuitBoxConnection two)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
Connect(one, two, static delegate { }, CircuitBoxWire.SelectedWirePrefab);
|
||||
@@ -158,6 +160,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void RemoveWires(IReadOnlyCollection<CircuitBoxWire> wires)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
var ids = wires.Select(static w => w.ID).ToImmutableArray();
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
@@ -175,6 +178,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
var ids = ImmutableArray.CreateBuilder<ushort>();
|
||||
var ios = ImmutableArray.CreateBuilder<CircuitBoxInputOutputNode.Type>();
|
||||
var labelIds = ImmutableArray.CreateBuilder<ushort>();
|
||||
|
||||
foreach (var moveable in moveables)
|
||||
{
|
||||
@@ -188,6 +192,9 @@ namespace Barotrauma.Items.Components
|
||||
case CircuitBoxInputOutputNode io:
|
||||
ios.Add(io.NodeType);
|
||||
break;
|
||||
case CircuitBoxLabelNode label:
|
||||
labelIds.Add(label.ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,12 +202,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
SelectComponentsInternal(ids, controlledId, overwrite);
|
||||
SelectInputOutputInternal(ios, controlledId, overwrite);
|
||||
SelectLabelsInternal(labelIds, controlledId, overwrite);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!ids.Any() && !ios.Any()) && !overwrite) { return; }
|
||||
if (!ids.Any() && !ios.Any() && !labelIds.Any() && !overwrite) { return; }
|
||||
|
||||
CreateClientEvent(new CircuitBoxSelectNodesEvent(ids.ToImmutable(), ios.ToImmutable(), overwrite, controlledId));
|
||||
CreateClientEvent(new CircuitBoxSelectNodesEvent(ids.ToImmutable(), ios.ToImmutable(), labelIds.ToImmutable(), overwrite, controlledId));
|
||||
}
|
||||
|
||||
public void SelectWires(IReadOnlyCollection<CircuitBoxWire> wires, bool overwrite)
|
||||
@@ -222,8 +230,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void MoveComponent(Vector2 moveAmount, IReadOnlyCollection<CircuitBoxNode> moveables)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
var ids = ImmutableArray.CreateBuilder<ushort>();
|
||||
var ios = ImmutableArray.CreateBuilder<CircuitBoxInputOutputNode.Type>();
|
||||
var labelIds = ImmutableArray.CreateBuilder<ushort>();
|
||||
|
||||
foreach (CircuitBoxNode move in moveables)
|
||||
{
|
||||
@@ -235,23 +245,27 @@ namespace Barotrauma.Items.Components
|
||||
case CircuitBoxInputOutputNode io:
|
||||
ios.Add(io.NodeType);
|
||||
break;
|
||||
case CircuitBoxLabelNode label:
|
||||
labelIds.Add(label.ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
MoveNodesInternal(ids, ios, moveAmount);
|
||||
MoveNodesInternal(ids, ios, labelIds, moveAmount);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ids.Any() && !ios.Any()) { return; }
|
||||
if (!ids.Any() && !ios.Any() && !labelIds.Any()) { return; }
|
||||
|
||||
|
||||
CreateClientEvent(new CircuitBoxMoveComponentEvent(ids.ToImmutable(), ios.ToImmutable(), moveAmount));
|
||||
CreateClientEvent(new CircuitBoxMoveComponentEvent(ids.ToImmutable(), ios.ToImmutable(), labelIds.ToImmutable(), moveAmount));
|
||||
}
|
||||
|
||||
public void AddComponent(ItemPrefab prefab, Vector2 pos)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
ItemPrefab resource;
|
||||
@@ -276,6 +290,72 @@ namespace Barotrauma.Items.Components
|
||||
CreateClientEvent(new CircuitBoxAddComponentEvent(prefab.UintIdentifier, pos));
|
||||
}
|
||||
|
||||
public void RenameLabel(CircuitBoxLabelNode label, Color color, NetLimitedString header, NetLimitedString body)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
label.EditText(header, body);
|
||||
label.Color = color;
|
||||
return;
|
||||
}
|
||||
|
||||
CreateClientEvent(new CircuitBoxRenameLabelEvent(label.ID, color, header, body));
|
||||
}
|
||||
|
||||
public void ResizeNode(CircuitBoxNode node, CircuitBoxResizeDirection dir, Vector2 amount)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
var resize = node.ResizeBy(dir, amount);
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
node.ApplyResize(resize.Size, resize.Pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO this needs to be refactored at some point, probably not now
|
||||
// the problem here is that the circuit box supports resizing all nodes
|
||||
// but we limit the resizing to only labels on the client
|
||||
// and on the server we only have a network message that targets labels
|
||||
// so if we ever want the ability to resize other nodes (could be useful) the network message
|
||||
// needs to know what type of ID it's targeting
|
||||
if (node is not ICircuitBoxIdentifiable identifiable)
|
||||
{
|
||||
DebugConsole.ThrowError("Tried to resize a node that doesn't have an ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
CreateClientEvent(new CircuitBoxResizeLabelEvent(identifiable.ID, resize.Pos, resize.Size));
|
||||
}
|
||||
|
||||
public void AddLabel(Vector2 pos)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
AddLabelInternal(ICircuitBoxIdentifiable.FindFreeID(Labels), GUIStyle.Blue, pos, CircuitBoxLabelNode.DefaultHeaderText, NetLimitedString.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
CreateClientEvent(new CircuitBoxAddLabelEvent(pos, GUIStyle.Blue, CircuitBoxLabelNode.DefaultHeaderText, NetLimitedString.Empty));
|
||||
}
|
||||
|
||||
public void RemoveLabel(IReadOnlyCollection<CircuitBoxLabelNode> labels)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (!labels.Any()) { return; }
|
||||
|
||||
var ids = labels.Select(static n => n.ID).ToImmutableArray();
|
||||
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
RemoveLabelInternal(ids);
|
||||
return;
|
||||
}
|
||||
|
||||
CreateClientEvent(new CircuitBoxRemoveLabelEvent(ids));
|
||||
}
|
||||
|
||||
public partial void OnViewUpdateProjSpecific()
|
||||
{
|
||||
UI?.MouseSnapshotHandler.UpdateConnections();
|
||||
@@ -396,7 +476,7 @@ namespace Barotrauma.Items.Components
|
||||
case CircuitBoxOpcode.MoveComponent:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxMoveComponentEvent>(msg);
|
||||
MoveNodesInternal(data.TargetIDs, data.IOs, data.MoveAmount);
|
||||
MoveNodesInternal(data.TargetIDs, data.IOs, data.LabelIDs, data.MoveAmount);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.UpdateSelection:
|
||||
@@ -406,8 +486,9 @@ namespace Barotrauma.Items.Components
|
||||
var nodeDict = data.ComponentIds.ToImmutableDictionary(static s => s.ID, static s => s.SelectedBy);
|
||||
var wireDict = data.WireIds.ToImmutableDictionary(static s => s.ID, static s => s.SelectedBy);
|
||||
var ioDict = data.InputOutputs.ToImmutableDictionary(static s => s.Type, static s => s.SelectedBy);
|
||||
var labelDict = data.LabelIds.ToImmutableDictionary(static s => s.ID, static s => s.SelectedBy);
|
||||
|
||||
UpdateSelections(nodeDict, wireDict, ioDict);
|
||||
UpdateSelections(nodeDict, wireDict, ioDict, labelDict);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.AddWire:
|
||||
@@ -426,11 +507,18 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Components.Clear();
|
||||
Wires.Clear();
|
||||
Labels.Clear();
|
||||
|
||||
var data = INetSerializableStruct.Read<CircuitBoxInitializeStateFromServerEvent>(msg);
|
||||
foreach (var compData in data.Components) { AddComponentFromData(compData); }
|
||||
foreach (var wireData in data.Wires) { AddWireFromData(wireData); }
|
||||
|
||||
foreach (var labelData in data.Labels)
|
||||
{
|
||||
AddLabelInternal(labelData.ID, labelData.Color, labelData.Position, labelData.Header, labelData.Body);
|
||||
ResizeLabelInternal(labelData.ID, labelData.Position, labelData.Size);
|
||||
}
|
||||
|
||||
foreach (var node in InputOutputNodes)
|
||||
{
|
||||
node.Position = node.NodeType switch
|
||||
@@ -443,6 +531,31 @@ namespace Barotrauma.Items.Components
|
||||
wasInitializedByServer = true;
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.RenameLabel:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxRenameLabelEvent>(msg);
|
||||
RenameLabelInternal(data.LabelId, data.Color, data.NewHeader, data.NewBody);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.AddLabel:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxServerAddLabelEvent>(msg);
|
||||
AddLabelInternal(data.ID, data.Color, data.Position, data.Header, data.Body);
|
||||
ResizeLabelInternal(data.ID, data.Position, data.Size);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.RemoveLabel:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxRemoveLabelEvent>(msg);
|
||||
RemoveLabelInternal(data.TargetIDs);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.ResizeLabel:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxResizeLabelEvent>(msg);
|
||||
ResizeLabelInternal(data.ID, data.Position, data.Size);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(header), header, "This opcode cannot be handled using entity events");
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (!editing || !MapEntity.SelectedList.Contains(item)) { return; }
|
||||
|
||||
Vector2 pos = item.WorldPosition + TransformedDetectOffset;
|
||||
pos.Y = -pos.Y;
|
||||
GUI.DrawRectangle(spriteBatch, pos - new Vector2(rangeX, rangeY), new Vector2(rangeX, rangeY) * 2.0f, Color.Cyan * 0.5f, isFilled: false, thickness: 2);
|
||||
if ((editing && MapEntity.SelectedList.Contains(item)) ||
|
||||
(ConnectionPanel.ShouldDebugDrawWiring && Character.Controlled?.SelectedItem == item))
|
||||
{
|
||||
Vector2 pos = item.WorldPosition + TransformedDetectOffset;
|
||||
pos.Y = -pos.Y;
|
||||
GUI.DrawRectangle(spriteBatch, pos - new Vector2(rangeX, rangeY), new Vector2(rangeX, rangeY) * 2.0f, Color.Cyan * 0.5f, isFilled: false, thickness: 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +137,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (c == equipper || !c.Enabled || c.Removed) { continue; }
|
||||
if (!ShowDeadCharacters && c.IsDead) { continue; }
|
||||
if (c.InDetectable) { continue; }
|
||||
|
||||
float dist = Vector2.DistanceSquared(refEntity.WorldPosition, c.WorldPosition);
|
||||
if (dist < Range * Range)
|
||||
|
||||
@@ -118,6 +118,19 @@ namespace Barotrauma.Items.Components
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(defaultValue: "0.5, 1.5", IsPropertySaveable.No, description: "Pitch slides from X to Y over the charge time")]
|
||||
public Vector2 ChargeSoundWindupPitchSlide
|
||||
{
|
||||
get => _chargeSoundWindupPitchSlide;
|
||||
set
|
||||
{
|
||||
_chargeSoundWindupPitchSlide = new Vector2(
|
||||
Math.Max(value.X, SoundChannel.MinFrequencyMultiplier),
|
||||
Math.Min(value.Y, SoundChannel.MaxFrequencyMultiplier));
|
||||
}
|
||||
}
|
||||
private Vector2 _chargeSoundWindupPitchSlide;
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
{
|
||||
@@ -220,9 +233,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (moveSound != null)
|
||||
{
|
||||
moveSoundChannel.FadeOutAndDispose();
|
||||
moveSoundChannel?.FadeOutAndDispose();
|
||||
moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, item.WorldPosition, moveSound.Volume, moveSound.Range, ignoreMuffling: moveSound.IgnoreMuffling, freqMult: moveSound.GetRandomFrequencyMultiplier());
|
||||
if (moveSoundChannel != null) moveSoundChannel.Looping = true;
|
||||
if (moveSoundChannel != null) { moveSoundChannel.Looping = true;}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,7 +281,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (chargeSoundChannel != null)
|
||||
{
|
||||
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(0.5f, 1.5f, chargeRatio);
|
||||
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(ChargeSoundWindupPitchSlide.X, ChargeSoundWindupPitchSlide.Y, chargeRatio);
|
||||
chargeSoundChannel.Position = new Vector3(item.WorldPosition, 0.0f);
|
||||
}
|
||||
break;
|
||||
@@ -482,12 +495,12 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
widget.MouseDown += () =>
|
||||
{
|
||||
widget.color = GUIStyle.Green;
|
||||
widget.Color = GUIStyle.Green;
|
||||
prevAngle = minRotation;
|
||||
};
|
||||
widget.Deselected += () =>
|
||||
{
|
||||
widget.color = Color.Yellow;
|
||||
widget.Color = Color.Yellow;
|
||||
item.CreateEditingHUD();
|
||||
RotationLimits = RotationLimits;
|
||||
if (SubEditorScreen.IsSubEditor())
|
||||
@@ -513,7 +526,7 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
widget.PreDraw += (sprtBtch, deltaTime) =>
|
||||
{
|
||||
widget.tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
|
||||
widget.Tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
|
||||
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
|
||||
};
|
||||
});
|
||||
@@ -526,12 +539,12 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
widget.MouseDown += () =>
|
||||
{
|
||||
widget.color = GUIStyle.Green;
|
||||
widget.Color = GUIStyle.Green;
|
||||
prevAngle = maxRotation;
|
||||
};
|
||||
widget.Deselected += () =>
|
||||
{
|
||||
widget.color = Color.Yellow;
|
||||
widget.Color = Color.Yellow;
|
||||
item.CreateEditingHUD();
|
||||
RotationLimits = RotationLimits;
|
||||
if (SubEditorScreen.IsSubEditor())
|
||||
@@ -557,7 +570,7 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
widget.PreDraw += (sprtBtch, deltaTime) =>
|
||||
{
|
||||
widget.tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation);
|
||||
widget.Tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation);
|
||||
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
|
||||
widget.Update(deltaTime);
|
||||
};
|
||||
@@ -584,20 +597,20 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 offset = new Vector2(size / 2 + 5, -10);
|
||||
if (!widgets.TryGetValue(id, out Widget widget))
|
||||
{
|
||||
widget = new Widget(id, size, Widget.Shape.Rectangle)
|
||||
widget = new Widget(id, size, WidgetShape.Rectangle)
|
||||
{
|
||||
color = Color.Yellow,
|
||||
tooltipOffset = offset,
|
||||
inputAreaMargin = 20,
|
||||
Color = Color.Yellow,
|
||||
TooltipOffset = offset,
|
||||
InputAreaMargin = 20,
|
||||
RequireMouseOn = false
|
||||
};
|
||||
widgets.Add(id, widget);
|
||||
initMethod?.Invoke(widget);
|
||||
}
|
||||
|
||||
widget.size = size;
|
||||
widget.tooltipOffset = offset;
|
||||
widget.thickness = thickness;
|
||||
widget.Size = size;
|
||||
widget.TooltipOffset = offset;
|
||||
widget.Thickness = thickness;
|
||||
return widget;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,11 +168,14 @@ namespace Barotrauma
|
||||
//TODO: define this in xml
|
||||
slotSpriteSmall = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(10, 6, 119, 120), null, 0);
|
||||
// Adjustment to match the old size of 75,71
|
||||
SlotSpriteSmall.size = new Vector2(SlotSpriteSmall.SourceRect.Width * 0.575f, SlotSpriteSmall.SourceRect.Height * 0.575f);
|
||||
SlotSpriteSmall.size = new Vector2(SlotSpriteSmall.SourceRect.Width * SlotSpriteSmallScale, SlotSpriteSmall.SourceRect.Height * SlotSpriteSmallScale);
|
||||
}
|
||||
return slotSpriteSmall;
|
||||
}
|
||||
}
|
||||
|
||||
public const float SlotSpriteSmallScale = 0.575f;
|
||||
|
||||
public static Sprite DraggableIndicator;
|
||||
public static Sprite UnequippedIndicator, UnequippedHoverIndicator, UnequippedClickedIndicator, EquippedIndicator, EquippedHoverIndicator, EquippedClickedIndicator;
|
||||
|
||||
@@ -211,6 +214,7 @@ namespace Barotrauma
|
||||
public RichString Tooltip { get; private set; }
|
||||
|
||||
public int tooltipDisplayedCondition;
|
||||
public bool tooltipShowedContextualOptions;
|
||||
|
||||
public bool ForceTooltipRefresh;
|
||||
|
||||
@@ -230,6 +234,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (ForceTooltipRefresh) { return true; }
|
||||
if (Item == null) { return false; }
|
||||
if (PlayerInput.KeyDown(InputType.ContextualCommand) != tooltipShowedContextualOptions) { return true; }
|
||||
return (int)Item.ConditionPercentage != tooltipDisplayedCondition;
|
||||
}
|
||||
|
||||
@@ -244,6 +249,7 @@ namespace Barotrauma
|
||||
}
|
||||
Tooltip = GetTooltip(Item, itemsInSlot, Character.Controlled);
|
||||
tooltipDisplayedCondition = (int)Item.ConditionPercentage;
|
||||
tooltipShowedContextualOptions = PlayerInput.KeyDown(InputType.ContextualCommand);
|
||||
}
|
||||
|
||||
private static RichString GetTooltip(Item item, IEnumerable<Item> itemsInSlot, Character character)
|
||||
@@ -323,19 +329,19 @@ namespace Barotrauma
|
||||
.TrimStart();
|
||||
}
|
||||
|
||||
if (itemsInSlot.All(it => it.NonInteractable || it.NonPlayerTeamInteractable))
|
||||
if (itemsInSlot.All(it => !it.IsInteractable(Character.Controlled)))
|
||||
{
|
||||
toolTip += " " + TextManager.Get("connectionlocked");
|
||||
}
|
||||
if (!item.IsFullCondition && !item.Prefab.HideConditionInTooltip)
|
||||
{
|
||||
string conditionColorStr = XMLExtensions.ColorToString(ToolBox.GradientLerp(item.Condition / item.MaxCondition, GUIStyle.ColorInventoryEmpty, GUIStyle.ColorInventoryHalf, GUIStyle.ColorInventoryFull));
|
||||
string conditionColorStr = XMLExtensions.ToStringHex(ToolBox.GradientLerp(item.Condition / item.MaxCondition, GUIStyle.ColorInventoryEmpty, GUIStyle.ColorInventoryHalf, GUIStyle.ColorInventoryFull));
|
||||
toolTip += $"‖color:{conditionColorStr}‖ ({(int)item.ConditionPercentage} %)‖color:end‖";
|
||||
}
|
||||
if (!description.IsNullOrEmpty()) { toolTip += '\n' + description; }
|
||||
if (item.Prefab.ContentPackage != GameMain.VanillaContent && item.Prefab.ContentPackage != null)
|
||||
{
|
||||
colorStr = XMLExtensions.ColorToString(Color.MediumPurple);
|
||||
colorStr = XMLExtensions.ToStringHex(Color.MediumPurple);
|
||||
toolTip += $"\n‖color:{colorStr}‖{item.Prefab.ContentPackage.Name}‖color:end‖";
|
||||
}
|
||||
}
|
||||
@@ -350,7 +356,17 @@ namespace Barotrauma
|
||||
}
|
||||
#if DEBUG
|
||||
toolTip += $" ({item.Prefab.Identifier})";
|
||||
#endif
|
||||
#endif
|
||||
if (PlayerInput.KeyDown(InputType.ContextualCommand))
|
||||
{
|
||||
toolTip += $"\n‖color:gui.blue‖{TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders"))}‖color:end‖";
|
||||
}
|
||||
else
|
||||
{
|
||||
var colorStr = XMLExtensions.ToStringHex(Color.LightGray * 0.7f);
|
||||
toolTip += $"\n‖color:{colorStr}‖{TextManager.Get("itemmsg.morreoptionsavailable")}‖color:end‖";
|
||||
}
|
||||
|
||||
return RichString.Rich(toolTip);
|
||||
}
|
||||
}
|
||||
@@ -613,10 +629,7 @@ namespace Barotrauma
|
||||
slot.State = GUIComponent.ComponentState.None;
|
||||
|
||||
if (mouseOn && (DraggingItems.Any() || selectedSlot == null || selectedSlot.Slot == slot) && DraggingInventory == null)
|
||||
// &&
|
||||
//(highlightedSubInventories.Count == 0 || highlightedSubInventories.Contains(this) || highlightedSubInventorySlot?.Slot == slot || highlightedSubInventory.Owner == item))
|
||||
{
|
||||
|
||||
{
|
||||
slot.State = GUIComponent.ComponentState.Hover;
|
||||
|
||||
if (selectedSlot == null || (!selectedSlot.IsSubSlot && isSubSlot))
|
||||
@@ -631,27 +644,47 @@ namespace Barotrauma
|
||||
|
||||
if (!DraggingItems.Any())
|
||||
{
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ? slots[slotIndex].Items.Where(it => !it.NonInteractable && !it.NonPlayerTeamInteractable) : slots[slotIndex].Items;
|
||||
if (PlayerInput.PrimaryMouseButtonDown() && interactableItems.Any())
|
||||
{
|
||||
if (PlayerInput.KeyDown(InputType.TakeHalfFromInventorySlot))
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ? slots[slotIndex].Items.Where(it => it.IsInteractable(Character.Controlled)) : slots[slotIndex].Items;
|
||||
if (interactableItems.Any())
|
||||
{
|
||||
if (availableContextualOrder.target != null)
|
||||
{
|
||||
DraggingItems.AddRange(interactableItems.Skip(interactableItems.Count() / 2));
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
GameMain.GameSession.CrewManager.SetCharacterOrder(character: null,
|
||||
new Order(OrderPrefab.Prefabs[availableContextualOrder.orderIdentifier], availableContextualOrder.target, targetItem: null, orderGiver: Character.Controlled));
|
||||
}
|
||||
availableContextualOrder = default;
|
||||
}
|
||||
else if (PlayerInput.KeyDown(InputType.TakeOneFromInventorySlot))
|
||||
else if (PlayerInput.KeyDown(InputType.Command) &&
|
||||
PlayerInput.KeyDown(InputType.ContextualCommand) &&
|
||||
GameMain.GameSession?.CrewManager != null)
|
||||
{
|
||||
DraggingItems.Add(interactableItems.First());
|
||||
GameMain.GameSession.CrewManager.OpenCommandUI(interactableItems.FirstOrDefault(), forceContextual: true);
|
||||
}
|
||||
else
|
||||
else if (PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
DraggingItems.AddRange(interactableItems);
|
||||
if (PlayerInput.KeyDown(InputType.TakeHalfFromInventorySlot))
|
||||
{
|
||||
DraggingItems.AddRange(interactableItems.Skip(interactableItems.Count() / 2));
|
||||
}
|
||||
else if (PlayerInput.KeyDown(InputType.TakeOneFromInventorySlot))
|
||||
{
|
||||
DraggingItems.Add(interactableItems.First());
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggingItems.AddRange(interactableItems);
|
||||
}
|
||||
DraggingSlot = slot;
|
||||
}
|
||||
DraggingSlot = slot;
|
||||
}
|
||||
}
|
||||
else if (PlayerInput.PrimaryMouseButtonReleased())
|
||||
{
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ? slots[slotIndex].Items.Where(it => !it.NonInteractable && !it.NonPlayerTeamInteractable) : slots[slotIndex].Items;
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ?
|
||||
slots[slotIndex].Items.Where(it => it.IsInteractable(Character.Controlled)) :
|
||||
slots[slotIndex].Items;
|
||||
if (PlayerInput.DoubleClicked() && interactableItems.Any())
|
||||
{
|
||||
doubleClickedItems.Clear();
|
||||
@@ -1286,7 +1319,7 @@ namespace Barotrauma
|
||||
if (selectedInventory.GetItemAt(slotIndex)?.OwnInventory?.Container is { } container &&
|
||||
container.Inventory.CanBePut(item))
|
||||
{
|
||||
if (!container.AllowDragAndDrop || !container.DrawInventory)
|
||||
if (!container.AllowDragAndDrop || !container.AllowAccess)
|
||||
{
|
||||
allowCombine = false;
|
||||
}
|
||||
@@ -1562,10 +1595,22 @@ namespace Barotrauma
|
||||
{
|
||||
selectedSlot.RefreshTooltip();
|
||||
}
|
||||
DrawToolTip(spriteBatch, selectedSlot.Tooltip, slotRect);
|
||||
|
||||
if (!slotIconTooltip.IsNullOrEmpty())
|
||||
{
|
||||
DrawToolTip(spriteBatch, slotIconTooltip, slotRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawToolTip(spriteBatch, selectedSlot.Tooltip, slotRect);
|
||||
}
|
||||
slotIconTooltip = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static (Item target, Identifier orderIdentifier) availableContextualOrder;
|
||||
private static LocalizedString slotIconTooltip;
|
||||
|
||||
public static void DrawSlot(SpriteBatch spriteBatch, Inventory inventory, VisualSlot slot, Item item, int slotIndex, bool drawItem = true, InvSlotType type = InvSlotType.Any)
|
||||
{
|
||||
Rectangle rect = slot.Rect;
|
||||
@@ -1730,7 +1775,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Color spriteColor = sprite == item.Sprite ? item.GetSpriteColor() : item.GetInventoryIconColor();
|
||||
if (inventory != null && (inventory.Locked || inventory.slots[slotIndex].Items.All(it => it.NonInteractable || it.NonPlayerTeamInteractable))) { spriteColor *= 0.5f; }
|
||||
if (inventory != null && (inventory.Locked || inventory.slots[slotIndex].Items.All(it => !it.IsInteractable(Character.Controlled)))) { spriteColor *= 0.5f; }
|
||||
if (CharacterHealth.OpenHealthWindow != null && !item.UseInHealthInterface && !item.AllowedSlots.Contains(InvSlotType.HealthInterface) && item.GetComponent<GeneticMaterial>() == null)
|
||||
{
|
||||
spriteColor = Color.Lerp(spriteColor, Color.TransparentBlack, 0.5f);
|
||||
@@ -1741,15 +1786,24 @@ namespace Barotrauma
|
||||
}
|
||||
sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale);
|
||||
|
||||
if (((item.SpawnedInCurrentOutpost && !item.AllowStealing) || (inventory != null && inventory.slots[slotIndex].Items.Any(it => it.SpawnedInCurrentOutpost && !it.AllowStealing))) && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
|
||||
if (item.OrderedToBeIgnored)
|
||||
{
|
||||
var stealIcon = CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand];
|
||||
Vector2 iconSize = new Vector2(25 * GUI.Scale);
|
||||
stealIcon.Draw(
|
||||
spriteBatch,
|
||||
new Vector2(rect.X + iconSize.X * 0.2f, rect.Bottom - iconSize.Y * 1.2f),
|
||||
color: GUIStyle.Red,
|
||||
scale: iconSize.X / stealIcon.size.X);
|
||||
if (OrderPrefab.Prefabs.TryGet(Tags.IgnoreThis, out OrderPrefab ignoreOrder))
|
||||
{
|
||||
DrawSideIcon(ignoreOrder.SymbolSprite, Direction.Right, TextManager.Get("tooltip.ignored"), ignoreOrder.Color, out bool mouseOn);
|
||||
if (mouseOn) { availableContextualOrder = (item, Tags.UnignoreThis); }
|
||||
|
||||
}
|
||||
}
|
||||
else if (Item.DeconstructItems.Contains(item) &&
|
||||
OrderPrefab.Prefabs.TryGet(Tags.DeconstructThis, out OrderPrefab deconstructOrder))
|
||||
{
|
||||
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))
|
||||
{
|
||||
DrawSideIcon(CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand], Direction.Left, TextManager.Get("tooltip.stolenitem"), GUIStyle.Red, out _);
|
||||
}
|
||||
int maxStackSize = item.Prefab.GetMaxStackSize(inventory);
|
||||
if (inventory is ItemInventory itemInventory)
|
||||
@@ -1798,6 +1852,22 @@ namespace Barotrauma
|
||||
SpriteEffects.None,
|
||||
layerDepth: 0.0f);
|
||||
}
|
||||
|
||||
void DrawSideIcon(Sprite icon, Direction side, LocalizedString tooltip, Color color, out bool mouseOn)
|
||||
{
|
||||
Vector2 iconSize = new Vector2(25 * GUI.Scale);
|
||||
float margin = 0.2f;
|
||||
Vector2 pos = new Vector2(
|
||||
side == Direction.Left ? rect.X + iconSize.X * margin : rect.Right - iconSize.X * margin,
|
||||
rect.Bottom - iconSize.Y * 1.2f);
|
||||
mouseOn = Vector2.Distance(PlayerInput.MousePosition, pos) < iconSize.X / 2;
|
||||
if (mouseOn)
|
||||
{
|
||||
slotIconTooltip = tooltip;
|
||||
color = Color.Lerp(color, Color.White, 0.5f);
|
||||
}
|
||||
icon.Draw(spriteBatch, pos, color: color, scale: iconSize.X / icon.size.X);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -216,7 +219,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentageRelativeToDefaultMaxCondition;
|
||||
for (int i = 0; i < Prefab.BrokenSprites.Length;i++)
|
||||
{
|
||||
if (Prefab.BrokenSprites[i].FadeIn) { continue; }
|
||||
@@ -340,9 +343,28 @@ namespace Barotrauma
|
||||
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
|
||||
if (renderTransparent) { color *= 0.15f; }
|
||||
|
||||
if (Character.Controlled != null && Character.DebugDrawInteract)
|
||||
{
|
||||
color = Color.Red;
|
||||
foreach (var ic in components)
|
||||
{
|
||||
var interactionType = GetComponentInteractionVisibility(Character.Controlled, ic);
|
||||
if (interactionType == InteractionVisibility.MissingRequirement)
|
||||
{
|
||||
color = Color.Orange;
|
||||
}
|
||||
else if (interactionType == InteractionVisibility.Visible)
|
||||
{
|
||||
color = Color.LightGreen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BrokenItemSprite fadeInBrokenSprite = null;
|
||||
float fadeInBrokenSpriteAlpha = 0.0f;
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
|
||||
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentageRelativeToDefaultMaxCondition;
|
||||
Vector2 drawOffset = GetCollapseEffectOffset();
|
||||
drawOffset.Y = -drawOffset.Y;
|
||||
|
||||
@@ -849,27 +871,6 @@ namespace Barotrauma
|
||||
itemEditor.Children.First().Color = Color.Black * 0.7f;
|
||||
if (!inGame)
|
||||
{
|
||||
//create a tag picker for item containers to make it easier to pick relevant tags for PreferredContainers
|
||||
var itemContainer = GetComponent<ItemContainer>();
|
||||
if (itemContainer != null)
|
||||
{
|
||||
var tagsField = itemEditor.Fields["Tags".ToIdentifier()].First().Parent;
|
||||
|
||||
//find all the items that can be put inside the container and add their PreferredContainer identifiers/tags to the available tags
|
||||
ImmutableHashSet<Identifier> availableTags = ItemPrefab.Prefabs
|
||||
.Where(ip => itemContainer.CanBeContained(ip))
|
||||
.SelectMany(ip => ip.PreferredContainers.SelectMany(pc => pc.Primary.Union(pc.Secondary)))
|
||||
//remove identifiers from the available container tags
|
||||
//(otherwise the list will include many irrelevant options,
|
||||
//e.g. "weldingtool" because a welding fuel tank can be placed inside the container, etc)
|
||||
.Where(t => !ItemPrefab.Prefabs.ContainsKey(t))
|
||||
.ToImmutableHashSet();
|
||||
new GUIButton(new RectTransform(new Vector2(0.1f, 1), tagsField.RectTransform, Anchor.TopRight), "...")
|
||||
{
|
||||
OnClicked = (bt, userData) => { CreateTagPicker(tagsField.GetChild<GUITextBox>(), availableTags); return true; }
|
||||
};
|
||||
}
|
||||
|
||||
if (Linkable)
|
||||
{
|
||||
var linkText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, heightScaled), isFixedSize: true), TextManager.Get("HoldToLink"), font: GUIStyle.SmallFont);
|
||||
@@ -881,6 +882,33 @@ namespace Barotrauma
|
||||
linkText.TextColor = GUIStyle.Orange;
|
||||
itemsText.TextColor = GUIStyle.Orange;
|
||||
}
|
||||
|
||||
//create a tag picker for item containers to make it easier to pick relevant tags for PreferredContainers
|
||||
var itemContainer = GetComponent<ItemContainer>();
|
||||
if (itemContainer != null)
|
||||
{
|
||||
var tagBox = itemEditor.Fields["Tags".ToIdentifier()].First() as GUITextBox;
|
||||
var tagsField = tagBox?.Parent;
|
||||
|
||||
var containerTagLayout = new GUILayoutGroup(new RectTransform(new Point(editingHUD.Rect.Width, heightScaled), isFixedSize: true), isHorizontal: true);
|
||||
var containerTagButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1), containerTagLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterRight);
|
||||
new GUIButton(new RectTransform(new Vector2(0.95f, 1), containerTagButtonLayout.RectTransform), text: TextManager.Get("containertaguibutton"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (_, _) => { CreateContainerTagPicker(tagBox); return true; },
|
||||
TextBlock = { AutoScaleHorizontal = true }
|
||||
};
|
||||
var containerTagText = new GUITextBlock(new RectTransform(new Vector2(0.8f, 1), containerTagLayout.RectTransform), TextManager.Get("containertaguibuttondescription"), font: GUIStyle.SmallFont)
|
||||
{
|
||||
TextColor = GUIStyle.Orange
|
||||
};
|
||||
var limitedString = ToolBox.LimitString(containerTagText.Text, containerTagText.Font, itemEditor.Rect.Width - containerTagButtonLayout.Rect.Width);
|
||||
if (limitedString != containerTagText.Text)
|
||||
{
|
||||
containerTagText.ToolTip = containerTagText.Text;
|
||||
containerTagText.Text = limitedString;
|
||||
}
|
||||
itemEditor.AddCustomContent(containerTagLayout, 3);
|
||||
}
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)), isHorizontal: true)
|
||||
{
|
||||
@@ -972,6 +1000,12 @@ namespace Barotrauma
|
||||
};
|
||||
itemEditor.AddCustomContent(tickBox, 1);
|
||||
}
|
||||
|
||||
if (!Layer.IsNullOrEmpty())
|
||||
{
|
||||
var layerText = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)) { MinSize = new Point(0, heightScaled) }, TextManager.AddPunctuation(':', TextManager.Get("editor.layer"), Layer));
|
||||
itemEditor.AddCustomContent(layerText, 1);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ItemComponent ic in components)
|
||||
@@ -987,7 +1021,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ic.requiredItems.Count == 0 && ic.DisabledRequiredItems.Count == 0 && SerializableProperty.GetProperties<Editable>(ic).Count == 0) { continue; }
|
||||
if (ic.RequiredItems.Count == 0 && ic.DisabledRequiredItems.Count == 0 && SerializableProperty.GetProperties<Editable>(ic).Count == 0) { continue; }
|
||||
}
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listBox.Content.RectTransform), style: "HorizontalLine");
|
||||
@@ -1004,7 +1038,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
List<RelatedItem> requiredItems = new List<RelatedItem>();
|
||||
foreach (var kvp in ic.requiredItems)
|
||||
foreach (var kvp in ic.RequiredItems)
|
||||
{
|
||||
foreach (RelatedItem relatedItem in kvp.Value)
|
||||
{
|
||||
@@ -1089,34 +1123,389 @@ namespace Barotrauma
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CreateTagPicker(GUITextBox textBox, IEnumerable<Identifier> availableTags)
|
||||
public void CreateContainerTagPicker([MaybeNull] GUITextBox tagTextBox)
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", "", new LocalizedString[] { TextManager.Get("Cancel") }, new Vector2(0.2f, 0.5f), new Point(300, 400));
|
||||
var msgBox = new GUIMessageBox(string.Empty, string.Empty, new[] { TextManager.Get("Ok") }, new Vector2(0.35f, 0.6f), new Point(400, 400));
|
||||
msgBox.Buttons[0].OnClicked = msgBox.Close;
|
||||
|
||||
var textList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), msgBox.Content.RectTransform, Anchor.TopCenter))
|
||||
var infoIcon = new GUIImage(new RectTransform(new Vector2(0.066f), msgBox.InnerFrame.RectTransform)
|
||||
{
|
||||
PlaySoundOnSelect = true,
|
||||
OnSelected = (component, userData) =>
|
||||
RelativeOffset = new Vector2(0.015f)
|
||||
}, style: "GUIButtonInfo")
|
||||
{
|
||||
ToolTip = TextManager.Get("containertagui.tutorial"),
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
var layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.85f), msgBox.Content.RectTransform));
|
||||
|
||||
var list = new GUIListBox(new RectTransform(new Vector2(1f, 1f), layout.RectTransform));
|
||||
|
||||
const float NameSize = 0.4f;
|
||||
const float ItemSize = 0.5f;
|
||||
const float CountSize = 0.1f;
|
||||
|
||||
var headerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.075f), list.Content.RectTransform), isHorizontal: true);
|
||||
new GUIButton(new RectTransform(new Vector2(NameSize, 1f), headerLayout.RectTransform), TextManager.Get("tagheader.tag"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
new GUIButton(new RectTransform(new Vector2(ItemSize, 1f), headerLayout.RectTransform), TextManager.Get("tagheader.items"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
new GUIButton(new RectTransform(new Vector2(CountSize, 1f), headerLayout.RectTransform), TextManager.Get("tagheader.count"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
|
||||
var itemsByTag =
|
||||
ContainerTagPrefab.Prefabs
|
||||
.ToImmutableDictionary(
|
||||
ct => ct,
|
||||
ct => ct.GetItemsAndSpawnProbabilities());
|
||||
|
||||
// Group the prefabs by category and turn them into a dictionary where the key is the category and value is the list of identifiers of the prefabs.
|
||||
// LINQ GroupBy returns GroupedEnumerable where the enumerable is the list of prefabs and key is what we grouped by.
|
||||
var tagCategories = ContainerTagPrefab.Prefabs
|
||||
.GroupBy(ct => ct.Category)
|
||||
.ToImmutableDictionary(
|
||||
g => g.Key,
|
||||
g => g.Select(ct => ct.Identifier).ToImmutableArray());
|
||||
|
||||
foreach (var (category, categoryTags) in tagCategories)
|
||||
{
|
||||
var categoryButton = new GUIButton(new RectTransform(new Vector2(1f, 0.075f), list.Content.RectTransform), style: "GUIButtonSmallFreeScale");
|
||||
categoryButton.Color *= 0.66f;
|
||||
var categoryLayout = new GUILayoutGroup(new RectTransform(Vector2.One, categoryButton.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
var categoryText = new GUITextBlock(new RectTransform(Vector2.One, categoryLayout.RectTransform), TextManager.Get($"tagcategory.{category}"), font: GUIStyle.SubHeadingFont);
|
||||
var arrowImage = new GUIImage(new RectTransform(new Vector2(1f, 0.5f), categoryLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIButtonVerticalArrowFreeScale");
|
||||
var arrowPadding = new GUIFrame(new RectTransform(new Vector2(0.025f, 1f), categoryLayout.RectTransform), style: null);
|
||||
|
||||
bool hasHiddenCategories = false;
|
||||
foreach (var categoryTag in categoryTags.OrderBy(t => t.Value))
|
||||
{
|
||||
if (!(userData is Identifier)) { return true; }
|
||||
AddTag((Identifier)userData);
|
||||
textBox.Text = Tags;
|
||||
msgBox.Close();
|
||||
var found = itemsByTag.FirstOrNull(kvp => kvp.Key.Identifier == categoryTag);
|
||||
if (found is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find tag with identifier {categoryTag} in itemsByTag");
|
||||
continue;
|
||||
}
|
||||
|
||||
var (tag, prefabsAndProbabilities) = found.Value;
|
||||
|
||||
bool isCorrectSubType = tag.IsRecommendedForSub(Submarine);
|
||||
|
||||
var tagLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), list.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
UserData = category,
|
||||
Visible = isCorrectSubType
|
||||
};
|
||||
|
||||
if (!isCorrectSubType)
|
||||
{
|
||||
hasHiddenCategories = true;
|
||||
}
|
||||
|
||||
var checkBoxLayout = new GUILayoutGroup(new RectTransform(new Vector2(NameSize, 1f), tagLayout.RectTransform), childAnchor: Anchor.Center);
|
||||
var enabledCheckBox = new GUITickBox(new RectTransform(Vector2.One, checkBoxLayout.RectTransform, Anchor.Center), tag.Name, font: GUIStyle.SmallFont)
|
||||
{
|
||||
Selected = tags.Contains(tag.Identifier),
|
||||
ToolTip = tag.Description
|
||||
};
|
||||
|
||||
var tickBoxText = enabledCheckBox.TextBlock;
|
||||
tickBoxText.Text = ToolBox.LimitString(tickBoxText.Text, tickBoxText.Font, tickBoxText.Rect.Width);
|
||||
|
||||
var itemLayout = new GUILayoutGroup(new RectTransform(new Vector2(ItemSize, 1f), tagLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
var itemLayoutScissor = new GUIScissorComponent(new RectTransform(new Vector2(0.8f, 1f), itemLayout.RectTransform)) { CanBeFocused = false };
|
||||
var itemLayoutButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1), itemLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.Center);
|
||||
var itemLayoutButton = new GUIButton(new RectTransform(new Vector2(0.8f), itemLayoutButtonLayout.RectTransform), text: "...", style: "GUICharacterInfoButton")
|
||||
{
|
||||
UserData = tag,
|
||||
ToolTip = TextManager.Get("containertagui.viewprobabilities")
|
||||
};
|
||||
|
||||
itemLayoutButtonLayout.Recalculate();
|
||||
|
||||
float scroll = 0f;
|
||||
float localScroll = 0f;
|
||||
int lastSkippedItems = 0;
|
||||
int skippedItems = 0;
|
||||
var itemLayoutDraw = new GUICustomComponent(new RectTransform(new Vector2(1f, 0.9f), itemLayoutScissor.Content.RectTransform, Anchor.CenterLeft), onDraw: (spriteBatch, component) =>
|
||||
{
|
||||
component.ToolTip = string.Empty;
|
||||
|
||||
const float padding = 8f;
|
||||
float offset = 0f;
|
||||
float size = component.Rect.Height;
|
||||
int start = (int)Math.Floor(scroll);
|
||||
int amountToDraw = (int)Math.Ceiling(component.Rect.Width / size) + 1; // +1 just to be on the safe side
|
||||
bool shouldIncrementOnSkip = true;
|
||||
float toDrawWidth = prefabsAndProbabilities.Length * (size + padding);
|
||||
|
||||
// if the width is less than the component width we need to limit how many items we draw or it looks weird
|
||||
if (toDrawWidth < component.Rect.Width)
|
||||
{
|
||||
shouldIncrementOnSkip = false;
|
||||
amountToDraw = prefabsAndProbabilities.Length;
|
||||
}
|
||||
|
||||
for (int i = start; i < start + amountToDraw; i++)
|
||||
{
|
||||
var (ip, probability, _) = prefabsAndProbabilities[i % prefabsAndProbabilities.Length];
|
||||
var sprite = ip.InventoryIcon ?? ip.Sprite;
|
||||
|
||||
if (sprite is null)
|
||||
{
|
||||
// I don't think this should happen but just in case
|
||||
if (shouldIncrementOnSkip)
|
||||
{
|
||||
amountToDraw++;
|
||||
skippedItems++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ShouldHideItemPrefab(ip, probability))
|
||||
{
|
||||
if (shouldIncrementOnSkip)
|
||||
{
|
||||
skippedItems++;
|
||||
amountToDraw++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
float partialScroll = localScroll * (size + padding);
|
||||
var drawRect = new RectangleF(itemLayoutScissor.Rect.X + offset - partialScroll, component.Rect.Y, size, size);
|
||||
|
||||
var isMouseOver = drawRect.Contains(PlayerInput.MousePosition);
|
||||
if (isMouseOver)
|
||||
{
|
||||
component.ToolTip = ip.CreateTooltipText();
|
||||
}
|
||||
|
||||
var slotSprite = Inventory.SlotSpriteSmall;
|
||||
slotSprite?.Draw(spriteBatch, drawRect.Location, Color.White, origin: Vector2.Zero, rotate: 0f, scale: size / slotSprite.size.X * Inventory.SlotSpriteSmallScale);
|
||||
|
||||
float iconScale = Math.Min(drawRect.Width / sprite.size.X, drawRect.Height / sprite.size.Y) * 0.9f;
|
||||
|
||||
Color drawColor = ip.InventoryIconColor;
|
||||
|
||||
sprite.Draw(spriteBatch, drawRect.Center, drawColor, origin: sprite.Origin, scale: iconScale);
|
||||
offset += size + padding;
|
||||
}
|
||||
|
||||
// we need to compensate for the skipped items so that the scroll doesn't jump around
|
||||
if (skippedItems < lastSkippedItems)
|
||||
{
|
||||
scroll += lastSkippedItems - skippedItems;
|
||||
}
|
||||
|
||||
lastSkippedItems = skippedItems;
|
||||
skippedItems = 0;
|
||||
}, onUpdate: (deltaTime, component) =>
|
||||
{
|
||||
if (GUI.MouseOn != component && MathUtils.NearlyEqual(localScroll, 0, deltaTime * 2))
|
||||
{
|
||||
localScroll = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
float totalWidth = prefabsAndProbabilities.Length * (component.Rect.Height + 8f);
|
||||
if (totalWidth < component.Rect.Width) { return; }
|
||||
scroll += deltaTime;
|
||||
localScroll = scroll % 1f;
|
||||
})
|
||||
{
|
||||
HoverCursor = CursorState.Default,
|
||||
AlwaysOverrideCursor = true
|
||||
};
|
||||
|
||||
var tooltip = TextManager.Get(tag.WarnIfLess ? "ContainerTagUI.RecommendedAmount" : "ContainerTagUI.SuggestedAmount");
|
||||
|
||||
var countBlock = new GUITextBlock(new RectTransform(new Vector2(CountSize, 1f), tagLayout.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
ToolTip = tooltip
|
||||
};
|
||||
UpdateCountBlock(countBlock, tag);
|
||||
|
||||
enabledCheckBox.OnSelected += tickBox =>
|
||||
{
|
||||
if (tickBox.Selected)
|
||||
{
|
||||
AddTag(tag.Identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveTag(tag.Identifier);
|
||||
}
|
||||
|
||||
if (tagTextBox is not null)
|
||||
{
|
||||
tagTextBox.Text = string.Join(',', tags.Where(t => !Prefab.Tags.Contains(t)));
|
||||
}
|
||||
UpdateCountBlock(countBlock, tag);
|
||||
return true;
|
||||
};
|
||||
|
||||
itemLayoutButton.OnClicked = (button, _) =>
|
||||
{
|
||||
CreateContainerTagItemListPopup(tag, button.Rect.Center, layout, prefabsAndProbabilities);
|
||||
return true;
|
||||
};
|
||||
|
||||
void UpdateCountBlock(GUITextBlock textBlock, ContainerTagPrefab containerTag)
|
||||
{
|
||||
if (textBlock is null) { return; }
|
||||
|
||||
var tagCount = Submarine.GetItems(alsoFromConnectedSubs: true).Count(i => i.HasTag(containerTag.Identifier));
|
||||
textBlock.Text = $"{tagCount} ({containerTag.RecommendedAmount})";
|
||||
|
||||
if (!isCorrectSubType || !containerTag.WarnIfLess || containerTag.RecommendedAmount <= 0) { return; }
|
||||
|
||||
if (tagCount < containerTag.RecommendedAmount)
|
||||
{
|
||||
textBlock.TextColor = GUIStyle.Red;
|
||||
textBlock.Text += "*";
|
||||
textBlock.ToolTip = RichString.Rich($"{tooltip}\n\n‖color:gui.red‖{TextManager.Get("ContainerTagUI.RecommendedAmountWarning")}‖color:end‖");
|
||||
}
|
||||
else if (tagCount >= containerTag.RecommendedAmount)
|
||||
{
|
||||
textBlock.TextColor = GUIStyle.Green;
|
||||
textBlock.ToolTip = tooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arrowImage.SpriteEffects = hasHiddenCategories ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
categoryButton.OnClicked = (_, _) =>
|
||||
{
|
||||
arrowImage.SpriteEffects ^= SpriteEffects.FlipVertically;
|
||||
|
||||
foreach (var child in list.Content.Children)
|
||||
{
|
||||
if (child.UserData is Identifier id && id == category)
|
||||
{
|
||||
child.Visible = !child.Visible;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateContainerTagItemListPopup(ContainerTagPrefab tag, Point location, GUIComponent popupParent, ImmutableArray<ContainerTagPrefab.ItemAndProbability> prefabAndProbabilities)
|
||||
{
|
||||
const string TooltipUserData = "tooltip";
|
||||
const string ProbabilityUserData = "probability";
|
||||
|
||||
if (popupParent.GetChildByUserData(TooltipUserData) is { } existingTooltip)
|
||||
{
|
||||
popupParent.RemoveChild(existingTooltip);
|
||||
}
|
||||
|
||||
var tooltip = new GUIFrame(new RectTransform(new Point(popupParent.Rect.Height), popupParent.RectTransform)
|
||||
{
|
||||
AbsoluteOffset = location - popupParent.Rect.Location
|
||||
})
|
||||
{
|
||||
UserData = TooltipUserData,
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
if (tooltip.Rect.Bottom > GameMain.GraphicsHeight)
|
||||
{
|
||||
int diffY = tooltip.Rect.Bottom - GameMain.GraphicsHeight;
|
||||
tooltip.RectTransform.AbsoluteOffset -= new Point(0, diffY);
|
||||
}
|
||||
|
||||
if (tooltip.Rect.Right > GameMain.GraphicsWidth)
|
||||
{
|
||||
int diffX = tooltip.Rect.Right - GameMain.GraphicsWidth;
|
||||
tooltip.RectTransform.AbsoluteOffset -= new Point(diffX, 0);
|
||||
}
|
||||
|
||||
var tooltipLayout = new GUILayoutGroup(new RectTransform(ToolBox.PaddingSizeParentRelative(tooltip.RectTransform, 0.9f), tooltip.RectTransform, Anchor.Center));
|
||||
|
||||
var tooltipHeader = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), tooltipLayout.RectTransform), tag.Name, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont);
|
||||
var tooltipList = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), tooltipLayout.RectTransform));
|
||||
|
||||
var tooltipHeaderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), tooltipList.Content.RectTransform), isHorizontal: true);
|
||||
new GUIButton(new RectTransform(new Vector2(0.66f, 1f), tooltipHeaderLayout.RectTransform), TextManager.Get("tagheader.item"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
new GUIButton(new RectTransform(new Vector2(0.33f, 1f), tooltipHeaderLayout.RectTransform), TextManager.Get("tagheader.probability"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
|
||||
foreach (var itemAndProbability in prefabAndProbabilities.OrderByDescending(p => p.Probability))
|
||||
{
|
||||
var (ip, probability, campaignOnlyProbability) = itemAndProbability;
|
||||
if (ShouldHideItemPrefab(ip, probability)) { continue; }
|
||||
|
||||
var itemLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), tooltipList.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
UserData = itemAndProbability
|
||||
};
|
||||
|
||||
var itemNameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.66f, 1f), itemLayout.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var itemIcon = new GUIImage(new RectTransform(Vector2.One, itemNameLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), ip.InventoryIcon ?? ip.Sprite, scaleToFit: true)
|
||||
{
|
||||
Color = ip.InventoryIconColor
|
||||
};
|
||||
|
||||
var itemName = new GUITextBlock(new RectTransform(Vector2.One, itemNameLayout.RectTransform), ip.Name);
|
||||
itemName.Text = ToolBox.LimitString(ip.Name, itemName.Font, itemName.Rect.Width);
|
||||
|
||||
var toolTipContainer = new GUIFrame(new RectTransform(Vector2.One, itemNameLayout.RectTransform), style: null)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
ToolTip = ip.CreateTooltipText()
|
||||
};
|
||||
|
||||
var probabilityText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1f), itemLayout.RectTransform), ProbabilityToPercentage(campaignOnlyProbability), textAlignment: Alignment.Right)
|
||||
{
|
||||
UserData = ProbabilityUserData
|
||||
};
|
||||
if (MathUtils.NearlyEqual(campaignOnlyProbability, 0f)) { probabilityText.TextColor = GUIStyle.Red; }
|
||||
}
|
||||
|
||||
var campaignCheckbox = new GUITickBox(new RectTransform(new Vector2(1f, 0.1f), tooltipLayout.RectTransform), label: TextManager.Get("containertagui.campaignonly"))
|
||||
{
|
||||
ToolTip = TextManager.Get("containertagui.campaignonlytooltip"),
|
||||
Selected = true,
|
||||
OnSelected = box =>
|
||||
{
|
||||
foreach (var child in tooltipList.Content.Children)
|
||||
{
|
||||
if (child.UserData is not ContainerTagPrefab.ItemAndProbability data) { continue; }
|
||||
|
||||
if (child.GetChildByUserData(ProbabilityUserData) is not GUITextBlock text) { continue; }
|
||||
|
||||
float probability = box.Selected
|
||||
? data.CampaignProbability
|
||||
: data.Probability;
|
||||
text.Text = ProbabilityToPercentage(probability);
|
||||
|
||||
text.TextColor = MathUtils.NearlyEqual(probability, 0f)
|
||||
? GUIStyle.Red
|
||||
: GUIStyle.TextColorNormal;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var availableTag in availableTags.ToList().OrderBy(t => t))
|
||||
var tooltipClose = new GUIButton(new RectTransform(new Vector2(1f, 0.1f), tooltipLayout.RectTransform), TextManager.Get("Close"))
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), textList.Content.RectTransform) { MinSize = new Point(0, 20) },
|
||||
ToolBox.LimitString(availableTag.Value, GUIStyle.Font, textList.Content.Rect.Width))
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
UserData = availableTag
|
||||
};
|
||||
}
|
||||
popupParent.RemoveChild(tooltip);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static LocalizedString ProbabilityToPercentage(float probability)
|
||||
=> TextManager.GetWithVariable("percentageformat", "[value]", MathF.Round((probability * 100f), 1).ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
}
|
||||
|
||||
private static bool ShouldHideItemPrefab(ItemPrefab ip, float probability)
|
||||
=> ip.HideInMenus && MathUtils.NearlyEqual(probability, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Reposition currently active item interfaces to make sure they don't overlap with each other
|
||||
/// </summary>
|
||||
@@ -1213,10 +1602,21 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
activeHUDs.Clear();
|
||||
maxPriorityHUDs.Clear();
|
||||
bool DrawHud(ItemComponent ic)
|
||||
{
|
||||
if (!ic.ShouldDrawHUD(character)) { return false; }
|
||||
if (character.HasEquippedItem(this))
|
||||
{
|
||||
return ic.DrawHudWhenEquipped;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ic.CanBeSelected && ic.HasRequiredItems(character, addMessage: false);
|
||||
}
|
||||
}
|
||||
//the HUD of the component with the highest priority will be drawn
|
||||
//if all components have a priority of 0, all of them are drawn
|
||||
maxPriorityHUDs.Clear();
|
||||
bool DrawHud(ItemComponent ic) => ic.ShouldDrawHUD(character) && (ic.CanBeSelected && ic.HasRequiredItems(character, addMessage: false) || (character.HasEquippedItem(this) && ic.DrawHudWhenEquipped));
|
||||
foreach (ItemComponent ic in activeComponents)
|
||||
{
|
||||
if (ic.HudPriority > 0 && DrawHud(ic) && (maxPriorityHUDs.Count == 0 || ic.HudPriority >= maxPriorityHUDs[0].HudPriority))
|
||||
@@ -1317,7 +1717,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
readonly List<ColoredText> texts = new List<ColoredText>();
|
||||
readonly List<ColoredText> texts = new();
|
||||
public List<ColoredText> GetHUDTexts(Character character, bool recreateHudTexts = true)
|
||||
{
|
||||
// Always create the texts if they have not yet been created
|
||||
@@ -1348,42 +1748,91 @@ namespace Barotrauma
|
||||
nameText += $" x{DroppedStack.Count()}";
|
||||
}
|
||||
|
||||
texts.Add(new ColoredText(nameText, GUIStyle.TextColorNormal, false, false));
|
||||
texts.Add(new ColoredText(nameText, GUIStyle.TextColorNormal, isCommand: false, isError: false));
|
||||
|
||||
if (CampaignMode.BlocksInteraction(CampaignInteractionType))
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Use)).Value, Color.Cyan, false, false));
|
||||
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Use)).Value, Color.Cyan, isCommand: false, isError: false));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ItemComponent ic in components)
|
||||
foreach (ItemComponent itemComponent in components)
|
||||
{
|
||||
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
|
||||
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
|
||||
if (ic is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { continue; }
|
||||
Color color = Color.Gray;
|
||||
if (ic.HasRequiredItems(character, false))
|
||||
{
|
||||
if (ic is Repairable r)
|
||||
{
|
||||
if (r.IsBelowRepairThreshold) { color = Color.Cyan; }
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.Cyan;
|
||||
}
|
||||
}
|
||||
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
|
||||
texts.Add(new ColoredText(ic.DisplayMsg.Value, color, false, false));
|
||||
var interactionVisibility = GetComponentInteractionVisibility(character, itemComponent);
|
||||
if (interactionVisibility == InteractionVisibility.None) { continue; }
|
||||
if (itemComponent.DisplayMsg.IsNullOrEmpty()) { continue; }
|
||||
|
||||
Color color = interactionVisibility == InteractionVisibility.MissingRequirement ? Color.Gray : Color.Cyan;
|
||||
texts.Add(new ColoredText(itemComponent.DisplayMsg.Value, color, isCommand: false, isError: false));
|
||||
}
|
||||
}
|
||||
if (PlayerInput.IsShiftDown() && CrewManager.DoesItemHaveContextualOrders(this))
|
||||
if (PlayerInput.KeyDown(InputType.ContextualCommand))
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")).Value, Color.Cyan, false, false));
|
||||
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")).Value, Color.Cyan, isCommand: false, isError: false));
|
||||
}
|
||||
else
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.Get("itemmsg.morreoptionsavailable").Value, Color.LightGray * 0.7f, isCommand: false, isError: false));
|
||||
}
|
||||
return texts;
|
||||
}
|
||||
|
||||
private enum InteractionVisibility
|
||||
{
|
||||
None,
|
||||
MissingRequirement,
|
||||
Visible
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine, for UI display purposes, the type of interaction visibility for an item component.
|
||||
///
|
||||
/// Example:
|
||||
/// Visible -> Display cyan "click to interact" type text on item hover.
|
||||
/// MissingRequirement -> Display gray "need tool" type text on item hover.
|
||||
/// None -> Hide from item hover texts.
|
||||
/// </summary>
|
||||
/// <param name="character">Character, for tool requirement purposes.</param>
|
||||
/// <param name="itemComponent">The item component to inspect.</param>
|
||||
/// <returns>The interaction visibility state for this component.</returns>
|
||||
private static InteractionVisibility GetComponentInteractionVisibility(Character character, ItemComponent itemComponent)
|
||||
{
|
||||
if (!itemComponent.CanBePicked && !itemComponent.CanBeSelected) { return InteractionVisibility.None; }
|
||||
if (itemComponent is Holdable holdable && !holdable.CanBeDeattached()) { return InteractionVisibility.None; }
|
||||
if (itemComponent is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { return InteractionVisibility.None; }
|
||||
|
||||
InteractionVisibility interactionVisibility = InteractionVisibility.MissingRequirement;
|
||||
if (itemComponent.HasRequiredItems(character, addMessage: false))
|
||||
{
|
||||
if (itemComponent is Repairable repairable)
|
||||
{
|
||||
if (repairable.IsBelowRepairThreshold)
|
||||
{
|
||||
interactionVisibility = InteractionVisibility.Visible;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
interactionVisibility = InteractionVisibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
return interactionVisibility;
|
||||
}
|
||||
|
||||
public bool HasVisibleInteraction(Character character)
|
||||
{
|
||||
foreach (var component in components)
|
||||
{
|
||||
if (GetComponentInteractionVisibility(character, component) == InteractionVisibility.Visible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ForceHUDLayoutUpdate(bool ignoreLocking = false)
|
||||
{
|
||||
foreach (ItemComponent ic in activeHUDs)
|
||||
|
||||
@@ -331,7 +331,6 @@ namespace Barotrauma
|
||||
}
|
||||
private GUIComponent CreateEditingHUD(bool inGame = false)
|
||||
{
|
||||
|
||||
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.15f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) })
|
||||
{
|
||||
UserData = this
|
||||
|
||||
@@ -170,16 +170,18 @@ namespace Barotrauma
|
||||
{
|
||||
if (!pendingSectionUpdates.Any() && !pendingDecalUpdates.Any())
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new StatusEventData());
|
||||
//these are used to modify the amount water/fire in the hull with console commands
|
||||
//they should be usable even when not controlling a character
|
||||
GameMain.Client?.CreateEntityEvent(this, new StatusEventData(), requireControlledCharacter: false);
|
||||
}
|
||||
foreach (Decal decal in pendingDecalUpdates)
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new DecalEventData(decal));
|
||||
GameMain.Client?.CreateEntityEvent(this, new DecalEventData(decal));
|
||||
}
|
||||
pendingDecalUpdates.Clear();
|
||||
foreach (int pendingSectionUpdate in pendingSectionUpdates)
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new BackgroundSectionsEventData(pendingSectionUpdate));
|
||||
GameMain.Client?.CreateEntityEvent(this, new BackgroundSectionsEventData(pendingSectionUpdate));
|
||||
}
|
||||
pendingSectionUpdates.Clear();
|
||||
networkUpdatePending = false;
|
||||
|
||||
@@ -16,27 +16,34 @@ namespace Barotrauma
|
||||
|
||||
float scale = Math.Min(drawArea.Width / (float)Bounds.Width, drawArea.Height / (float)Bounds.Height) * 0.9f;
|
||||
|
||||
foreach ((Identifier identifier, Rectangle rect) in DisplayEntities)
|
||||
foreach (var displayEntity in DisplayEntities)
|
||||
{
|
||||
var entityPrefab = FindByIdentifier(identifier);
|
||||
var entityPrefab = FindByIdentifier(displayEntity.Identifier);
|
||||
if (entityPrefab is CoreEntityPrefab || entityPrefab == null) { continue; }
|
||||
var drawRect = new Rectangle(
|
||||
(int)(rect.X * scale) + drawArea.Center.X, (int)((rect.Y) * scale) - drawArea.Center.Y,
|
||||
(int)(rect.Width * scale), (int)(rect.Height * scale));
|
||||
entityPrefab.DrawPlacing(spriteBatch, drawRect, entityPrefab.Scale * scale);
|
||||
(int)(displayEntity.Rect.X * scale) + drawArea.Center.X, (int)((displayEntity.Rect.Y) * scale) - drawArea.Center.Y,
|
||||
(int)(displayEntity.Rect.Width * scale), (int)(displayEntity.Rect.Height * scale));
|
||||
entityPrefab.DrawPlacing(spriteBatch, drawRect, entityPrefab.Scale * scale, rotation: displayEntity.RotationRad);
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawPlacing(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
base.DrawPlacing(spriteBatch, cam);
|
||||
foreach ((Identifier identifier, Rectangle rect) in DisplayEntities)
|
||||
Draw(
|
||||
spriteBatch,
|
||||
placePosition != Vector2.Zero ? placePosition : Submarine.MouseToWorldGrid(cam, Submarine.MainSub));
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 pos)
|
||||
{
|
||||
foreach (var displayEntity in DisplayEntities)
|
||||
{
|
||||
var entityPrefab = FindByIdentifier(identifier);
|
||||
var entityPrefab = FindByIdentifier(displayEntity.Identifier);
|
||||
if (entityPrefab == null) { continue; }
|
||||
Rectangle drawRect = rect;
|
||||
drawRect.Location += placePosition != Vector2.Zero ? placePosition.ToPoint() : Submarine.MouseToWorldGrid(cam, Submarine.MainSub).ToPoint();
|
||||
entityPrefab.DrawPlacing(spriteBatch, drawRect, entityPrefab.Scale);
|
||||
Rectangle drawRect = displayEntity.Rect;
|
||||
drawRect.Location += pos.ToPoint();
|
||||
entityPrefab.DrawPlacing(spriteBatch, drawRect, entityPrefab.Scale, rotation: displayEntity.RotationRad);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,9 +54,12 @@ namespace Barotrauma
|
||||
new XAttribute("description", description),
|
||||
new XAttribute("hideinmenus", hideInMenus));
|
||||
|
||||
|
||||
//move the entities so that their "center of mass" is at {0,0}
|
||||
var assemblyEntities = MapEntity.CopyEntities(entities);
|
||||
for (int i = 0; i < assemblyEntities.Count && i < entities.Count; i++)
|
||||
{
|
||||
assemblyEntities[i].Layer = entities[i].Layer;
|
||||
}
|
||||
|
||||
//find wires and items that are contained inside another item
|
||||
//place them at {0,0} to prevent them from messing up the origin of the prefab and to hide them in preview
|
||||
@@ -86,7 +96,7 @@ namespace Barotrauma
|
||||
MathUtils.RoundTowardsClosest(center.Y, Submarine.GridSize.Y) - center.Y - Submarine.GridSize.Y / 2);
|
||||
|
||||
MapEntity.SelectedList.Clear();
|
||||
entities.ForEach(e => MapEntity.AddSelection(e));
|
||||
assemblyEntities.ForEach(e => MapEntity.AddSelection(e));
|
||||
|
||||
foreach (MapEntity mapEntity in assemblyEntities)
|
||||
{
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (damage <= 0.0f) { return; }
|
||||
Vector2 particlePos = worldPosition;
|
||||
Vector2 particleDir = particlePos - WorldPosition;
|
||||
if (particleDir.LengthSquared() > 0.0001f) { particleDir = Vector2.Normalize(particleDir); }
|
||||
if (!Cells.Any(c => c.IsPointInside(particlePos)))
|
||||
{
|
||||
bool intersectionFound = false;
|
||||
@@ -31,6 +33,7 @@ namespace Barotrauma
|
||||
{
|
||||
intersectionFound = true;
|
||||
particlePos = intersection;
|
||||
particleDir = edge.GetNormal(cell);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -38,14 +41,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 particleDir = particlePos - WorldPosition;
|
||||
if (particleDir.LengthSquared() > 0.0001f) { particleDir = Vector2.Normalize(particleDir); }
|
||||
int particleAmount = MathHelper.Clamp((int)damage, 1, 10);
|
||||
for (int i = 0; i < particleAmount; i++)
|
||||
{
|
||||
var particle = GameMain.ParticleManager.CreateParticle("iceshards",
|
||||
var particle = GameMain.ParticleManager.CreateParticle("iceexplosionsmall",
|
||||
particlePos + Rand.Vector(5.0f),
|
||||
particleDir * Rand.Range(200.0f, 500.0f) + Rand.Vector(100.0f));
|
||||
particleDir * Rand.Range(30.0f, 500.0f) + Rand.Vector(20.0f));
|
||||
GameMain.ParticleManager.CreateParticle("iceshards",
|
||||
particlePos + Rand.Vector(5.0f),
|
||||
particleDir * Rand.Range(100.0f, 500.0f) + Rand.Vector(100.0f));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var rects in blockedRects.Values)
|
||||
{
|
||||
foreach (var rect in rects)
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Barotrauma
|
||||
|
||||
//Maximum number of visible objects drawn at once. Should be large enough to not have an effect during normal gameplay,
|
||||
//but small enough to prevent wrecking performance when zooming out very far
|
||||
const int MaxVisibleObjects = 500;
|
||||
const int MaxVisibleObjects = 600;
|
||||
|
||||
private Rectangle currentGridIndices;
|
||||
|
||||
|
||||
@@ -362,6 +362,13 @@ namespace Barotrauma
|
||||
GUI.DrawLine(spriteBatch, new Vector2(edge.Point1.X + cell.Translation.X, -(edge.Point1.Y + cell.Translation.Y)),
|
||||
new Vector2(edge.Point2.X + cell.Translation.X, -(edge.Point2.Y + cell.Translation.Y)), edge.NextToCave ? Color.Red : (cell.Body == null ? Color.Cyan * 0.5f : (edge.IsSolid ? Color.White : Color.Gray)),
|
||||
width: edge.NextToCave ? 8 : 1);
|
||||
|
||||
Vector2 normal = edge.GetNormal(cell);
|
||||
GUI.DrawLine(spriteBatch,
|
||||
(edge.Center + cell.Translation).FlipY(),
|
||||
(edge.Center + cell.Translation + normal * 32).FlipY(),
|
||||
Color.Red * 0.5f,
|
||||
width: 3);
|
||||
}
|
||||
|
||||
foreach (Vector2 point in cell.BodyVertices)
|
||||
|
||||
@@ -156,17 +156,7 @@ namespace Barotrauma.Lights
|
||||
BoundingBox = rect;
|
||||
|
||||
this.isHorizontal = isHorizontal;
|
||||
if (ParentEntity is Structure structure)
|
||||
{
|
||||
Debug.Assert(!structure.Removed);
|
||||
isHorizontal = structure.IsHorizontal;
|
||||
}
|
||||
else if (ParentEntity is Item item)
|
||||
{
|
||||
Debug.Assert(!item.Removed);
|
||||
var door = item.GetComponent<Door>();
|
||||
if (door != null) { isHorizontal = door.IsHorizontal; }
|
||||
}
|
||||
Debug.Assert(!ParentEntity.Removed);
|
||||
|
||||
Vector2[] verts = new Vector2[]
|
||||
{
|
||||
|
||||
@@ -268,8 +268,7 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
if (!light.Enabled) { continue; }
|
||||
if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; }
|
||||
//above the top boundary of the level (in an inactive respawn shuttle?)
|
||||
if (Level.Loaded != null && light.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
|
||||
if (light.ParentBody != null)
|
||||
{
|
||||
light.ParentBody.UpdateDrawPosition();
|
||||
@@ -696,12 +695,24 @@ namespace Barotrauma.Lights
|
||||
if (diff.LengthSquared() > 20.0f * 20.0f) { losOffset = diff; }
|
||||
float rotation = MathUtils.VectorToAngle(losOffset);
|
||||
|
||||
//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;
|
||||
|
||||
//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 relativeOriginLookAtPosModifier = -0.055f; //Increase this value increases how much the vision changes by moving the mouse
|
||||
float originLookAtPosModifier = visionCircle.Width * relativeOriginLookAtPosModifier;
|
||||
|
||||
Vector2 scale = new Vector2(
|
||||
MathHelper.Clamp(losOffset.Length() / 256.0f, 4.0f, 5.0f), 3.0f);
|
||||
MathHelper.Clamp(losOffset.Length() / MaxOffset, MinHorizontalScale, MaxHorizontalScale), VerticalScale);
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cam.Transform * Matrix.CreateScale(new Vector3(GameSettings.CurrentConfig.Graphics.LightMapScale, GameSettings.CurrentConfig.Graphics.LightMapScale, 1.0f)));
|
||||
spriteBatch.Draw(visionCircle, new Vector2(ViewTarget.WorldPosition.X, -ViewTarget.WorldPosition.Y), null, Color.White, rotation,
|
||||
new Vector2(visionCircle.Width * 0.2f, visionCircle.Height / 2), scale, SpriteEffects.None, 0.0f);
|
||||
new Vector2(originStartPosition + (scale.X * originLookAtPosModifier), visionCircle.Height / 2), scale, SpriteEffects.None, 0.0f);
|
||||
spriteBatch.End();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -461,7 +461,9 @@ namespace Barotrauma
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.6f, 1.0f), repBarHolder.RectTransform), onDraw: (sb, component) =>
|
||||
{
|
||||
if (location.Reputation == null) { return; }
|
||||
RoundSummary.DrawReputationBar(sb, component.Rect, location.Reputation.NormalizedValue);
|
||||
RoundSummary.DrawReputationBar(sb, component.Rect,
|
||||
location.Reputation.NormalizedValue,
|
||||
location.Reputation.MinReputation, location.Reputation.MaxReputation);
|
||||
});
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.4f, 1.0f), repBarHolder.RectTransform),
|
||||
@@ -1128,8 +1130,11 @@ namespace Barotrauma
|
||||
|
||||
if (connection.LevelData.HasBeaconStation)
|
||||
{
|
||||
var beaconStationIconStyle = connection.LevelData.IsBeaconActive ? "BeaconStationActive" : "BeaconStationInactive";
|
||||
DrawIcon(beaconStationIconStyle, (int)(28 * zoom), connection.LevelData.IsBeaconActive ? beaconStationActiveText : beaconStationInactiveText);
|
||||
bool beaconActive =
|
||||
connection.LevelData.IsBeaconActive ||
|
||||
(Level.Loaded?.LevelData == connection.LevelData && Level.Loaded.CheckBeaconActive());
|
||||
var beaconStationIconStyle = beaconActive ? "BeaconStationActive" : "BeaconStationInactive";
|
||||
DrawIcon(beaconStationIconStyle, (int)(28 * zoom), beaconActive ? beaconStationActiveText : beaconStationInactiveText);
|
||||
}
|
||||
|
||||
if (connection.Locked)
|
||||
|
||||
@@ -808,8 +808,16 @@ namespace Barotrauma
|
||||
{
|
||||
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
|
||||
if (structure.FlippedY && structure.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
|
||||
spriteRotation = MathHelper.ToRadians(structure.Rotation);
|
||||
rectangleRotation = spriteRotation;
|
||||
rectangleRotation = MathHelper.ToRadians(structure.Rotation);
|
||||
|
||||
spriteRotation = rectangleRotation;
|
||||
bool spriteIsFlippedHorizontally = structure.Sprite.effects.HasFlag(SpriteEffects.FlipHorizontally);
|
||||
bool spriteIsFlippedVertically = structure.Sprite.effects.HasFlag(SpriteEffects.FlipVertically);
|
||||
if (spriteIsFlippedHorizontally != spriteIsFlippedVertically)
|
||||
{
|
||||
spriteRotation = -spriteRotation;
|
||||
}
|
||||
|
||||
if (structure.FlippedX != structure.FlippedY) { rectangleRotation = -rectangleRotation; }
|
||||
break;
|
||||
}
|
||||
@@ -978,6 +986,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void ResetEditingHUD()
|
||||
{
|
||||
editingHUD = null;
|
||||
}
|
||||
|
||||
public static void DrawEditor(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
if (SelectedList.Count == 1)
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
abstract partial class MapEntityPrefab : PrefabWithUintIdentifier
|
||||
{
|
||||
public RichString CreateTooltipText()
|
||||
{
|
||||
LocalizedString name = Category.HasFlag(MapEntityCategory.Legacy) ? TextManager.GetWithVariable("legacyitemformat", "[name]", Name) : Name;
|
||||
LocalizedString tooltip = $"‖color:{XMLExtensions.ToStringHex(GUIStyle.TextColorBright)}‖{name}‖color:end‖";
|
||||
|
||||
if (!Description.IsNullOrEmpty())
|
||||
{
|
||||
tooltip += '\n' + Description;
|
||||
}
|
||||
|
||||
if (IsModded)
|
||||
{
|
||||
tooltip = $"{tooltip}\n‖color:{Color.MediumPurple.ToStringHex()}‖{ContentPackage?.Name}‖color:end‖";
|
||||
}
|
||||
|
||||
return RichString.Rich(tooltip);
|
||||
}
|
||||
|
||||
public bool IsModded
|
||||
=> ContentPackage != GameMain.VanillaContent && ContentPackage != null;
|
||||
|
||||
public virtual void UpdatePlacing(Camera cam)
|
||||
{
|
||||
if (PlayerInput.SecondaryMouseButtonClicked())
|
||||
|
||||
@@ -25,8 +25,10 @@ namespace Barotrauma
|
||||
Stream = sound.Stream;
|
||||
Range = element.GetAttributeFloat("range", 1000.0f);
|
||||
Volume = element.GetAttributeFloat("volume", 1.0f);
|
||||
IgnoreMuffling = element.GetAttributeBool("dontmuffle", false);
|
||||
|
||||
FrequencyMultiplierRange = new Vector2(1.0f);
|
||||
string freqMultAttr = element.GetAttributeString("frequencymultiplier", element.GetAttributeString("frequency", "1.0"))!;
|
||||
string freqMultAttr = element.GetAttributeString("frequencymultiplier", element.GetAttributeString("frequency", "1.0"));
|
||||
if (!freqMultAttr.Contains(','))
|
||||
{
|
||||
if (float.TryParse(freqMultAttr, NumberStyles.Any, CultureInfo.InvariantCulture, out float freqMult))
|
||||
@@ -47,7 +49,6 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"Loaded frequency range exceeds max value: {FrequencyMultiplierRange} (original string was \"{freqMultAttr}\")",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
IgnoreMuffling = element.GetAttributeBool("dontmuffle", false);
|
||||
}
|
||||
|
||||
public float GetRandomFrequencyMultiplier()
|
||||
|
||||
@@ -221,6 +221,12 @@ namespace Barotrauma
|
||||
editor.AddCustomContent(tickBox, 1);
|
||||
}
|
||||
|
||||
if (!Layer.IsNullOrEmpty())
|
||||
{
|
||||
var layerText = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)) { MinSize = new Point(0, heightScaled) }, TextManager.AddPunctuation(':', TextManager.Get("editor.layer"), Layer));
|
||||
editor.AddCustomContent(layerText, 1);
|
||||
}
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
@@ -435,13 +441,12 @@ namespace Barotrauma
|
||||
dropShadowOffset.Y = -dropShadowOffset.Y;
|
||||
}
|
||||
|
||||
SpriteEffects oldEffects = Prefab.BackgroundSprite.effects;
|
||||
Prefab.BackgroundSprite.effects ^= SpriteEffects;
|
||||
|
||||
Vector2 backGroundOffset = new Vector2(
|
||||
MathUtils.PositiveModulo(-textureOffset.X, Prefab.BackgroundSprite.SourceRect.Width * TextureScale.X * Scale),
|
||||
MathUtils.PositiveModulo(-textureOffset.Y, Prefab.BackgroundSprite.SourceRect.Height * TextureScale.Y * Scale));
|
||||
|
||||
float rotationRad = rotationForSprite(this.rotationRad, Prefab.BackgroundSprite);
|
||||
|
||||
Prefab.BackgroundSprite.DrawTiled(
|
||||
spriteBatch,
|
||||
new Vector2(rect.X + rect.Width / 2 + drawOffset.X, -(rect.Y - rect.Height / 2 + drawOffset.Y)),
|
||||
@@ -451,7 +456,8 @@ namespace Barotrauma
|
||||
color: Prefab.BackgroundSpriteColor,
|
||||
textureScale: TextureScale * Scale,
|
||||
startOffset: backGroundOffset,
|
||||
depth: Math.Max(GetDrawDepth(Prefab.BackgroundSprite.Depth, Prefab.BackgroundSprite), depth + 0.000001f));
|
||||
depth: Math.Max(GetDrawDepth(Prefab.BackgroundSprite.Depth, Prefab.BackgroundSprite), depth + 0.000001f),
|
||||
spriteEffects: Prefab.BackgroundSprite.effects ^ SpriteEffects);
|
||||
|
||||
if (UseDropShadow)
|
||||
{
|
||||
@@ -464,18 +470,14 @@ namespace Barotrauma
|
||||
color: Color.Black * 0.5f,
|
||||
textureScale: TextureScale * Scale,
|
||||
startOffset: backGroundOffset,
|
||||
depth: (depth + Prefab.BackgroundSprite.Depth) / 2.0f);
|
||||
depth: (depth + Prefab.BackgroundSprite.Depth) / 2.0f,
|
||||
spriteEffects: Prefab.BackgroundSprite.effects ^ SpriteEffects);
|
||||
}
|
||||
|
||||
Prefab.BackgroundSprite.effects = oldEffects;
|
||||
}
|
||||
}
|
||||
|
||||
if (back == GetRealDepth() > 0.5f)
|
||||
{
|
||||
SpriteEffects oldEffects = Prefab.Sprite.effects;
|
||||
Prefab.Sprite.effects ^= SpriteEffects;
|
||||
|
||||
Vector2 advanceX = MathUtils.RotatedUnitXRadians(this.rotationRad).FlipY();
|
||||
Vector2 advanceY = advanceX.YX().FlipX();
|
||||
if (FlippedX != FlippedY)
|
||||
@@ -483,6 +485,9 @@ namespace Barotrauma
|
||||
advanceX = advanceX.FlipY();
|
||||
advanceY = advanceY.FlipX();
|
||||
}
|
||||
|
||||
float sectionSpriteRotationRad = rotationForSprite(this.rotationRad, Prefab.Sprite);
|
||||
|
||||
for (int i = 0; i < Sections.Length; i++)
|
||||
{
|
||||
Rectangle drawSection = Sections[i].rect;
|
||||
@@ -528,22 +533,17 @@ namespace Barotrauma
|
||||
pos += rect.Location.ToVector2();
|
||||
pos = new Vector2(pos.X + rect.Width / 2 + drawOffset.X, -(pos.Y - rect.Height / 2 + drawOffset.Y));
|
||||
|
||||
Vector2 pos = new Vector2(drawSection.X, drawSection.Y);
|
||||
pos -= rect.Location.ToVector2();
|
||||
pos = advanceX * pos.X + advanceY * pos.Y;
|
||||
pos += rect.Location.ToVector2();
|
||||
pos = new Vector2(pos.X + rect.Width / 2 + drawOffset.X, -(pos.Y - rect.Height / 2 + drawOffset.Y));
|
||||
|
||||
Prefab.Sprite.DrawTiled(
|
||||
spriteBatch,
|
||||
pos,
|
||||
new Vector2(drawSection.Width, drawSection.Height),
|
||||
rotation: rotationRad,
|
||||
rotation: sectionSpriteRotationRad,
|
||||
origin: rect.Size.ToVector2() * new Vector2(0.5f, 0.5f),
|
||||
color: color,
|
||||
startOffset: sectionOffset,
|
||||
depth: depth,
|
||||
textureScale: TextureScale * Scale);
|
||||
textureScale: TextureScale * Scale,
|
||||
spriteEffects: Prefab.Sprite.effects ^ SpriteEffects);
|
||||
}
|
||||
|
||||
foreach (var decorativeSprite in Prefab.DecorativeSprites)
|
||||
@@ -551,27 +551,42 @@ namespace Barotrauma
|
||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor) + this.rotationRad;
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier) * Scale;
|
||||
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
|
||||
rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, Prefab.Sprite.effects,
|
||||
Vector2 drawPos = DrawPosition + MathUtils.RotatePoint(offset, -this.rotationRad);
|
||||
decorativeSprite.Sprite.Draw(
|
||||
spriteBatch: spriteBatch,
|
||||
pos: drawPos.FlipY(),
|
||||
color: color,
|
||||
rotate: rotation,
|
||||
scale: decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale,
|
||||
spriteEffect: Prefab.Sprite.effects ^ SpriteEffects,
|
||||
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - Prefab.Sprite.Depth), 0.999f));
|
||||
}
|
||||
Prefab.Sprite.effects = oldEffects;
|
||||
}
|
||||
|
||||
static float rotationForSprite(float rotationRad, Sprite sprite)
|
||||
{
|
||||
if (sprite.effects.HasFlag(SpriteEffects.FlipHorizontally) != sprite.effects.HasFlag(SpriteEffects.FlipVertically))
|
||||
{
|
||||
rotationRad = -rotationRad;
|
||||
}
|
||||
return rotationRad;
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.5f)
|
||||
{
|
||||
if (Bodies != null)
|
||||
{
|
||||
for (int i = 0; i < Bodies.Count; i++)
|
||||
foreach (var body in Bodies)
|
||||
{
|
||||
Vector2 pos = FarseerPhysics.ConvertUnits.ToDisplayUnits(Bodies[i].Position);
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(body.Position);
|
||||
if (Submarine != null) { pos += Submarine.DrawPosition; }
|
||||
pos.Y = -pos.Y;
|
||||
var dimensions = bodyDimensions[body];
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
pos,
|
||||
FarseerPhysics.ConvertUnits.ToDisplayUnits(bodyDebugDimensions[i].X),
|
||||
FarseerPhysics.ConvertUnits.ToDisplayUnits(bodyDebugDimensions[i].Y),
|
||||
-Bodies[i].Rotation, Color.White);
|
||||
ConvertUnits.ToDisplayUnits(dimensions.X),
|
||||
ConvertUnits.ToDisplayUnits(dimensions.Y),
|
||||
-body.Rotation, Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -506,6 +507,47 @@ namespace Barotrauma
|
||||
Hull.ShowHulls = true;
|
||||
}
|
||||
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NotEnoughContainers))
|
||||
{
|
||||
HashSet<ContainerTagPrefab> missingContainerTags = new();
|
||||
foreach (var prefab in ContainerTagPrefab.Prefabs)
|
||||
{
|
||||
if (!prefab.IsRecommendedForSub(this) || !prefab.WarnIfLess) { continue; }
|
||||
|
||||
int count = Item.ItemList.Count(i => i.HasTag(prefab.Identifier));
|
||||
if (count < prefab.RecommendedAmount)
|
||||
{
|
||||
missingContainerTags.Add(prefab);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingContainerTags.Any())
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
int count = 0;
|
||||
foreach (var tag in missingContainerTags)
|
||||
{
|
||||
sb.AppendLine($"- {tag.Name}");
|
||||
count++;
|
||||
if (missingContainerTags.Count > count && count >= 3)
|
||||
{
|
||||
var moreIndicator = TextManager.GetWithVariable(
|
||||
"upgradeuitooltip.moreindicator",
|
||||
"[amount]",
|
||||
(missingContainerTags.Count - count).ToString()).Value;
|
||||
sb.AppendLine(moreIndicator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
errorMsgs.Add(TextManager.GetWithVariable(
|
||||
"ContainerTagUI.CountWarning",
|
||||
"[tags]",
|
||||
sb.ToString()).Value);
|
||||
warnings.Add(SubEditorScreen.WarningType.NotEnoughContainers);
|
||||
}
|
||||
}
|
||||
|
||||
if (Info.Type == SubmarineType.Player)
|
||||
{
|
||||
foreach (Item item in Item.ItemList)
|
||||
@@ -521,7 +563,40 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.GetComponent<OxygenGenerator>() is not OxygenGenerator oxygenGenerator) { continue; }
|
||||
|
||||
Dictionary<Hull, float> hullOxygenFlow = new Dictionary<Hull, float>();
|
||||
|
||||
foreach (var linkedTo in item.linkedTo)
|
||||
{
|
||||
if (linkedTo is not Item linkedItem || linkedItem.GetComponent<Vent>() is not Vent vent) { continue; }
|
||||
if (vent.Item.CurrentHull == null)
|
||||
{
|
||||
vent.Item.FindHull();
|
||||
if (vent.Item.CurrentHull == null) { continue; }
|
||||
}
|
||||
float oxygenFlow = oxygenGenerator.GetVentOxygenFlow(vent);
|
||||
if (!hullOxygenFlow.ContainsKey(vent.Item.CurrentHull))
|
||||
{
|
||||
hullOxygenFlow[vent.Item.CurrentHull] = oxygenFlow;
|
||||
}
|
||||
else
|
||||
{
|
||||
hullOxygenFlow[vent.Item.CurrentHull] += oxygenFlow;
|
||||
}
|
||||
}
|
||||
foreach ((Hull hull, float oxygenFlow) in hullOxygenFlow)
|
||||
{
|
||||
if (oxygenFlow < Hull.OxygenConsumptionSpeed)
|
||||
{
|
||||
errorMsgs.Add(TextManager.GetWithVariable("LowOxygenOutputWarning", "[roomname]",
|
||||
hull.DisplayName).Value);
|
||||
warnings.Add(SubEditorScreen.WarningType.LowOxygenOutputWarning);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Human))
|
||||
{
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoHumanSpawnpoints))
|
||||
@@ -554,6 +629,15 @@ namespace Barotrauma
|
||||
warnings.Add(SubEditorScreen.WarningType.NoHiddenContainers);
|
||||
}
|
||||
}
|
||||
if (Info.Dimensions.X * Physics.DisplayToRealWorldRatio > 80 ||
|
||||
Info.Dimensions.Y * Physics.DisplayToRealWorldRatio > 32)
|
||||
{
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.TooLargeForEndGame))
|
||||
{
|
||||
errorMsgs.Add(TextManager.Get("TooLargeForEndGameWarning").Value);
|
||||
warnings.Add(SubEditorScreen.WarningType.TooLargeForEndGame);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Info.Type == SubmarineType.OutpostModule)
|
||||
{
|
||||
@@ -749,7 +833,9 @@ namespace Barotrauma
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
throw new Exception($"Error while reading a network event for the submarine \"{Info.Name} ({ID})\". Submarines are not even supposed to receive events!");
|
||||
Identifier layerIdentifier = msg.ReadIdentifier();
|
||||
bool enabled = msg.ReadBoolean();
|
||||
SetLayerEnabled(layerIdentifier, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,52 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Voronoi2;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class SubmarineBody
|
||||
{
|
||||
|
||||
partial void HandleLevelCollisionProjSpecific(Impact impact)
|
||||
{
|
||||
float wallImpact = Vector2.Dot(impact.Velocity, -impact.Normal);
|
||||
int particleAmount = (int)Math.Min(wallImpact, 10);
|
||||
|
||||
const float BurstParticleThreshold = 5.0f;
|
||||
|
||||
float velocityFactor = MathHelper.Clamp(wallImpact / 10.0f, 0.0f, 1.0f);
|
||||
for (int i = 0; i < particleAmount * 5; i++)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("iceshards",
|
||||
ConvertUnits.ToDisplayUnits(impact.ImpactPos) + Rand.Vector(Rand.Range(1.0f, 50.0f)),
|
||||
(Rand.Vector(0.9f) + impact.Normal) * Rand.Range(100.0f, 10000) * velocityFactor);
|
||||
}
|
||||
for (int i = 0; i < particleAmount; i++)
|
||||
{
|
||||
float particleVelocityMultiplier = Rand.Range(0.0f, 1);
|
||||
var p = GameMain.ParticleManager.CreateParticle("iceexplosion",
|
||||
ConvertUnits.ToDisplayUnits(impact.ImpactPos) + Rand.Vector(Rand.Range(1.0f, 50.0f)),
|
||||
(Rand.Vector(0.5f) + impact.Normal) * particleVelocityMultiplier * 500 * velocityFactor);
|
||||
if (p != null)
|
||||
{
|
||||
p.VelocityChangeMultiplier = particleVelocityMultiplier * Rand.Range(0.0f, 1.0f);
|
||||
p.Size *= Math.Max(particleVelocityMultiplier, 0);
|
||||
}
|
||||
}
|
||||
if (wallImpact > BurstParticleThreshold)
|
||||
{
|
||||
for (int i = 0; i < particleAmount; i++)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("iceburst",
|
||||
ConvertUnits.ToDisplayUnits(impact.ImpactPos) + Rand.Vector(Rand.Range(1.0f, 50.0f)),
|
||||
angle: MathUtils.VectorToAngle(impact.Normal.FlipY() + Rand.Vector(0.25f)), speed: 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void ClientUpdatePosition(float deltaTime)
|
||||
{
|
||||
if (GameMain.Client == null) { return; }
|
||||
|
||||
@@ -168,7 +168,7 @@ namespace Barotrauma
|
||||
|
||||
private bool IsHidden()
|
||||
{
|
||||
if (!SubEditorScreen.IsLayerVisible(this)) { return false; }
|
||||
if (!SubEditorScreen.IsLayerVisible(this)) { return true; }
|
||||
if (spawnType == SpawnType.Path)
|
||||
{
|
||||
return (!GameMain.DebugDraw && !ShowWayPoints);
|
||||
@@ -316,6 +316,11 @@ namespace Barotrauma
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), TextManager.Get("Spawnpoint"), font: GUIStyle.LargeFont);
|
||||
|
||||
if (!Layer.IsNullOrEmpty())
|
||||
{
|
||||
var layerText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform), TextManager.AddPunctuation(':', TextManager.Get("editor.layer"), Layer));
|
||||
}
|
||||
|
||||
var spawnTypeContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (localRemovedBans.Contains(bannedPlayer.UniqueIdentifier)) { continue; }
|
||||
|
||||
var playerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), ((GUIListBox)BanFrame).Content.RectTransform) { MinSize = new Point(0, 70) })
|
||||
var playerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), ((GUIListBox)BanFrame).Content.RectTransform) { MinSize = new Point(0, 70) }, style: "InnerFrame")
|
||||
{
|
||||
UserData = BanFrame
|
||||
};
|
||||
@@ -80,18 +80,22 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
CanBeFocused = true
|
||||
};
|
||||
textBlock.RectTransform.MinSize = new Point(
|
||||
(int)textBlock.Font.MeasureString(textBlock.Text.SanitizedValue).X, 0);
|
||||
textBlock.RectTransform.MinSize = new Point(0, (int)textBlock.Font.MeasureString(textBlock.Text.SanitizedValue).Y);
|
||||
|
||||
var removeButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.4f), topArea.RectTransform),
|
||||
var removeButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.4f), topArea.RectTransform, Anchor.CenterRight),
|
||||
TextManager.Get("BanListRemove"), style: "GUIButtonSmall")
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
UserData = bannedPlayer,
|
||||
OnClicked = RemoveBan
|
||||
OnClicked = RemoveBan,
|
||||
Enabled = false
|
||||
};
|
||||
topArea.RectTransform.MinSize = new Point(0, (int)(removeButton.Rect.Height * 1.25f));
|
||||
|
||||
topArea.ForceLayoutRecalculation();
|
||||
removeButton.OnAddedToGUIUpdateList += (component) =>
|
||||
{
|
||||
component.Enabled = GameMain.Client?.HasPermission(ClientPermissions.Unban) ?? false;
|
||||
};
|
||||
topArea.RectTransform.MinSize = new Point(0, Math.Max(textBlock.RectTransform.MinSize.Y, removeButton.RectTransform.MinSize.Y));
|
||||
topArea.RectTransform.IsFixedSize = true;
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedPlayerFrame.RectTransform),
|
||||
bannedPlayer.ExpirationTime.TryUnwrap(out var expirationTime)
|
||||
|
||||
@@ -14,6 +14,17 @@ namespace Barotrauma.Networking
|
||||
set;
|
||||
}
|
||||
|
||||
// Players can boost per-user volume by 200%
|
||||
public const float MaxVoiceChatBoost = 2.0f;
|
||||
|
||||
private float voiceVolume = 1f;
|
||||
|
||||
public float VoiceVolume
|
||||
{
|
||||
get => voiceVolume;
|
||||
set => voiceVolume = Math.Clamp(value, 0f, MaxVoiceChatBoost);
|
||||
}
|
||||
|
||||
private SoundChannel radioNoiseChannel;
|
||||
private float radioNoise;
|
||||
|
||||
@@ -23,7 +34,6 @@ namespace Barotrauma.Networking
|
||||
set { radioNoise = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
}
|
||||
|
||||
|
||||
private bool mutedLocally;
|
||||
public bool MutedLocally
|
||||
{
|
||||
@@ -86,6 +96,17 @@ namespace Barotrauma.Networking
|
||||
float dist = Vector3.Distance(new Vector3(character.WorldPosition, 0.0f), GameMain.SoundManager.ListenerPosition);
|
||||
gain = 1.0f - MathUtils.InverseLerp(VoipSound.Near, VoipSound.Far, dist);
|
||||
}
|
||||
if (!VoipSound.UsingRadio)
|
||||
{
|
||||
//emulate the "garbling" of the text chat
|
||||
//this in a sense means the volume diminishes exponentially when close to the maximum range of the sound
|
||||
//(diminished by both the garbling and the distance attenuation)
|
||||
|
||||
//which is good, because we want the voice chat to become unintelligible close to the max range,
|
||||
//and we need to heavily reduce the volume to do that (otherwise it's just quiet, but still intelligible)
|
||||
float garbleAmount = ChatMessage.GetGarbleAmount(Character.Controlled, character, ChatMessage.SpeakRangeVOIP);
|
||||
gain *= 1.0f - garbleAmount;
|
||||
}
|
||||
if (RadioNoise > 0.0f)
|
||||
{
|
||||
noiseGain = gain * RadioNoise;
|
||||
|
||||
@@ -21,7 +21,12 @@ namespace Barotrauma
|
||||
DebugConsole.Log($"Received entity removal message for \"{entity}\".");
|
||||
if (entity is Item item && item.Container?.GetComponent<Deconstructor>() != null)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none".ToIdentifier()) + ":" + item.Prefab.Identifier);
|
||||
if (item.Prefab.ContentPackage == ContentPackageManager.VanillaCorePackage &&
|
||||
/* we don't need info of every deconstructed item, we can get a good sample size just by logging 5% */
|
||||
Rand.Range(0.0f, 1.0f) < 0.05f)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none".ToIdentifier()) + ":" + item.Prefab.Identifier);
|
||||
}
|
||||
}
|
||||
entity.Remove();
|
||||
}
|
||||
@@ -45,7 +50,12 @@ namespace Barotrauma
|
||||
{
|
||||
if (newItem.Container?.GetComponent<Fabricator>() != null)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none".ToIdentifier()) + ":" + newItem.Prefab.Identifier);
|
||||
if (newItem.Prefab.ContentPackage == ContentPackageManager.VanillaCorePackage &&
|
||||
/* we don't need info of every fabricated item, we can get a good sample size just by logging 5% */
|
||||
Rand.Range(0.0f, 1.0f) < 0.05f)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none".ToIdentifier()) + ":" + newItem.Prefab.Identifier);
|
||||
}
|
||||
}
|
||||
receivedEvents.Add((newItem, false));
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public readonly List<SubmarineInfo> ServerSubmarines = new List<SubmarineInfo>();
|
||||
|
||||
public string ServerName { get; private set; }
|
||||
public string ServerName => ServerSettings.ServerName;
|
||||
|
||||
private bool canStart;
|
||||
|
||||
@@ -270,11 +270,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
otherClients = new List<Client>();
|
||||
|
||||
ServerSettings = new ServerSettings(this, "Server", 0, 0, 0, false, false);
|
||||
ServerSettings = new ServerSettings(this, serverName, 0, 0, 0, false, false);
|
||||
Voting = new Voting();
|
||||
|
||||
serverEndpoints = endpoints;
|
||||
InitiateServerJoin(serverName);
|
||||
InitiateServerJoin();
|
||||
|
||||
//ServerLog = new ServerLog("");
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace Barotrauma.Networking
|
||||
return serverInfo;
|
||||
}
|
||||
|
||||
private void InitiateServerJoin(string hostName)
|
||||
private void InitiateServerJoin()
|
||||
{
|
||||
LastClientListUpdateID = 0;
|
||||
|
||||
@@ -306,8 +306,6 @@ namespace Barotrauma.Networking
|
||||
GameMain.NetLobbyScreen.ChatInput.Enabled = false;
|
||||
}
|
||||
|
||||
ServerName = hostName;
|
||||
|
||||
myCharacter = Character.Controlled;
|
||||
ChatMessage.LastID = 0;
|
||||
|
||||
@@ -318,9 +316,8 @@ namespace Barotrauma.Networking
|
||||
CoroutineManager.StartCoroutine(WaitForStartingInfo(), "WaitForStartingInfo");
|
||||
}
|
||||
|
||||
public void SetLobbyPublic(bool isPublic)
|
||||
public static void SetLobbyPublic(bool isPublic)
|
||||
{
|
||||
GameMain.NetLobbyScreen.SetPublic(isPublic);
|
||||
SteamManager.SetLobbyPublic(isPublic);
|
||||
}
|
||||
|
||||
@@ -723,7 +720,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (readyToStart && !CoroutineManager.IsCoroutineRunning("WaitForStartRound"))
|
||||
{
|
||||
CoroutineManager.StartCoroutine(GameMain.NetLobbyScreen.WaitForStartRound(startButton: null), "WaitForStartRound");
|
||||
CoroutineManager.StartCoroutine(NetLobbyScreen.WaitForStartRound(startButton: null), "WaitForStartRound");
|
||||
}
|
||||
break;
|
||||
case ServerPacketHeader.STARTGAME:
|
||||
@@ -1096,7 +1093,7 @@ namespace Barotrauma.Networking
|
||||
var prevContentPackages = ClientPeer.ServerContentPackages;
|
||||
//decrement lobby update ID to make sure we update the lobby when we reconnect
|
||||
GameMain.NetLobbyScreen.LastUpdateID--;
|
||||
InitiateServerJoin(ServerName);
|
||||
InitiateServerJoin();
|
||||
if (ClientPeer != null)
|
||||
{
|
||||
//restore the previous list of content packages so we can reconnect immediately without having to recheck that the packages match
|
||||
@@ -1189,7 +1186,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (!CoroutineManager.IsCoroutineRunning("WaitForStartingInfo"))
|
||||
{
|
||||
InitiateServerJoin(ServerName);
|
||||
InitiateServerJoin();
|
||||
yield return new WaitForSeconds(5.0f);
|
||||
}
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
@@ -1253,7 +1250,7 @@ namespace Barotrauma.Networking
|
||||
if (!(this.permittedConsoleCommands.Any(c => !permittedConsoleCommands.Contains(c)) ||
|
||||
permittedConsoleCommands.Any(c => !this.permittedConsoleCommands.Contains(c))))
|
||||
{
|
||||
if (newPermissions == permissions) return;
|
||||
if (newPermissions == permissions) { return; }
|
||||
}
|
||||
|
||||
bool refreshCampaignUI = permissions.HasFlag(ClientPermissions.ManageCampaign) != newPermissions.HasFlag(ClientPermissions.ManageCampaign) ||
|
||||
@@ -1335,6 +1332,8 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen.RefreshEnabledElements();
|
||||
//close settings menu in case it was open
|
||||
ServerSettings.Close();
|
||||
OnPermissionChanged.Invoke(new PermissionChangedEvent(permissions, this.permittedConsoleCommands));
|
||||
}
|
||||
|
||||
@@ -2025,18 +2024,15 @@ namespace Barotrauma.Networking
|
||||
GameMain.NetLobbyScreen.LastUpdateID = updateID;
|
||||
|
||||
ServerSettings.ServerLog.ServerName = ServerSettings.ServerName;
|
||||
|
||||
if (!GameMain.NetLobbyScreen.ServerName.Selected) { GameMain.NetLobbyScreen.ServerName.Text = ServerSettings.ServerName; }
|
||||
if (!GameMain.NetLobbyScreen.ServerMessage.Selected) { GameMain.NetLobbyScreen.ServerMessage.Text = ServerSettings.ServerMessageText; }
|
||||
GameMain.NetLobbyScreen.UsingShuttle = usingShuttle;
|
||||
|
||||
if (!allowSubVoting) { GameMain.NetLobbyScreen.TrySelectSub(selectSubName, selectSubHash, GameMain.NetLobbyScreen.SubList); }
|
||||
if (!allowSubVoting || GameMain.NetLobbyScreen.SelectedSub == null) { GameMain.NetLobbyScreen.TrySelectSub(selectSubName, selectSubHash, GameMain.NetLobbyScreen.SubList); }
|
||||
GameMain.NetLobbyScreen.TrySelectSub(selectShuttleName, selectShuttleHash, GameMain.NetLobbyScreen.ShuttleList.ListBox);
|
||||
|
||||
GameMain.NetLobbyScreen.SetTraitorProbability(traitorProbability);
|
||||
GameMain.NetLobbyScreen.SetTraitorDangerLevel(traitorDangerLevel);
|
||||
|
||||
GameMain.NetLobbyScreen.SetMissionType(missionType);
|
||||
GameMain.NetLobbyScreen.LevelSeed = levelSeed;
|
||||
|
||||
GameMain.NetLobbyScreen.SelectMode(modeIndex);
|
||||
if (isInitialUpdate && GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
|
||||
@@ -2053,7 +2049,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating);
|
||||
GameMain.NetLobbyScreen.LevelSeed = levelSeed;
|
||||
GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty);
|
||||
GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode);
|
||||
GameMain.NetLobbyScreen.SetBotCount(botCount);
|
||||
@@ -2574,12 +2569,17 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
public override void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
CreateEntityEvent(entity, extraData, requireControlledCharacter: true);
|
||||
}
|
||||
|
||||
public void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData, bool requireControlledCharacter)
|
||||
{
|
||||
if (entity is not IClientSerializable clientSerializable)
|
||||
{
|
||||
throw new InvalidCastException($"Entity is not {nameof(IClientSerializable)}");
|
||||
}
|
||||
EntityEventManager.CreateEvent(clientSerializable, extraData);
|
||||
EntityEventManager.CreateEvent(clientSerializable, extraData, requireControlledCharacter);
|
||||
}
|
||||
|
||||
public bool HasPermission(ClientPermissions permission)
|
||||
@@ -2703,9 +2703,19 @@ namespace Barotrauma.Networking
|
||||
|
||||
public override void AddChatMessage(ChatMessage message)
|
||||
{
|
||||
base.AddChatMessage(message);
|
||||
|
||||
if (string.IsNullOrEmpty(message.Text)) { return; }
|
||||
if (message.Sender != null && !message.Sender.IsDead)
|
||||
{
|
||||
if (message.Text.IsNullOrEmpty())
|
||||
{
|
||||
message.Sender.ShowTextlessSpeechBubble(2.0f, message.Color);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
message.Sender.ShowSpeechBubble(message.Color, message.Text);
|
||||
}
|
||||
}
|
||||
GameMain.NetLobbyScreen.NewChatMessage(message);
|
||||
chatBox.AddMessage(message);
|
||||
}
|
||||
@@ -2894,7 +2904,7 @@ namespace Barotrauma.Networking
|
||||
ClientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public bool SpectateClicked(GUIButton button, object _)
|
||||
public bool JoinOnGoingClicked(GUIButton button, object _)
|
||||
{
|
||||
MultiPlayerCampaign campaign =
|
||||
GameMain.NetLobbyScreen.SelectedMode == GameMain.GameSession?.GameMode.Preset ?
|
||||
@@ -3283,17 +3293,36 @@ namespace Barotrauma.Networking
|
||||
|
||||
private void CreateSelectionRelatedButtons(Client client, GUIComponent frame)
|
||||
{
|
||||
var content = new GUIFrame(new RectTransform(new Vector2(1f, 1.0f - frame.RectTransform.RelativeSize.Y), frame.RectTransform, Anchor.BottomCenter, Pivot.TopCenter),
|
||||
style: null);
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1.0f - frame.RectTransform.RelativeSize.Y), frame.RectTransform, Anchor.BottomCenter, Pivot.TopCenter), childAnchor: Anchor.TopCenter);
|
||||
|
||||
var mute = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform, Anchor.TopCenter),
|
||||
var mute = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.2f), content.RectTransform, Anchor.TopCenter),
|
||||
TextManager.Get("Mute"))
|
||||
{
|
||||
Selected = client.MutedLocally,
|
||||
OnSelected = (tickBox) => { client.MutedLocally = tickBox.Selected; return true; }
|
||||
};
|
||||
|
||||
var volumeLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.45f), content.RectTransform, Anchor.TopCenter), isHorizontal: false);
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.35f), content.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomLeft)
|
||||
var volumeTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), volumeLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
var label = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), volumeTextLayout.RectTransform), TextManager.Get("VoiceChatVolume"));
|
||||
var percentageText = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1f), volumeTextLayout.RectTransform), ToolBox.GetFormattedPercentage(client.VoiceVolume), textAlignment: Alignment.Right);
|
||||
|
||||
var volumeSlider = new GUIScrollBar(new RectTransform(new Vector2(1f, 0.5f), volumeLayout.RectTransform), barSize: 0.1f, style: "GUISlider")
|
||||
{
|
||||
Range = new Vector2(0f, 1f),
|
||||
BarScroll = client.VoiceVolume / Client.MaxVoiceChatBoost,
|
||||
OnMoved = (_, barScroll) =>
|
||||
{
|
||||
float newVolume = barScroll * Client.MaxVoiceChatBoost;
|
||||
|
||||
client.VoiceVolume = newVolume;
|
||||
percentageText.Text = ToolBox.GetFormattedPercentage(newVolume);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.35f), content.RectTransform), isHorizontal: true, childAnchor: Anchor.BottomLeft)
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
@@ -3327,7 +3356,7 @@ namespace Barotrauma.Networking
|
||||
TextManager.Get("Ban"), style: "GUIButtonSmall")
|
||||
{
|
||||
UserData = client,
|
||||
OnClicked = (btn, userdata) => { GameMain.NetLobbyScreen.BanPlayer(client); return false; }
|
||||
OnClicked = (btn, userdata) => { NetLobbyScreen.BanPlayer(client); return false; }
|
||||
};
|
||||
}
|
||||
if (HasPermission(ClientPermissions.Kick) && client.AllowKicking)
|
||||
@@ -3336,7 +3365,7 @@ namespace Barotrauma.Networking
|
||||
TextManager.Get("Kick"), style: "GUIButtonSmall")
|
||||
{
|
||||
UserData = client,
|
||||
OnClicked = (btn, userdata) => { GameMain.NetLobbyScreen.KickPlayer(client); return false; }
|
||||
OnClicked = (btn, userdata) => { NetLobbyScreen.KickPlayer(client); return false; }
|
||||
};
|
||||
}
|
||||
else if (ServerSettings.AllowVoteKick && client.AllowKicking)
|
||||
|
||||
@@ -42,16 +42,17 @@ namespace Barotrauma.Networking
|
||||
thisClient = client;
|
||||
}
|
||||
|
||||
public void CreateEvent(IClientSerializable entity, NetEntityEvent.IData extraData = null)
|
||||
public void CreateEvent(IClientSerializable entity, NetEntityEvent.IData extraData = null, bool requireControlledCharacter = true)
|
||||
{
|
||||
if (GameMain.Client?.Character == null) { return; }
|
||||
if (GameMain.Client == null) { return; }
|
||||
if (requireControlledCharacter && GameMain.Client.Character == null) { return; }
|
||||
|
||||
if (!ValidateEntity(entity)) { return; }
|
||||
|
||||
var newEvent = new ClientEntityEvent(
|
||||
entity,
|
||||
eventId: (UInt16)(ID + 1),
|
||||
characterStateId: GameMain.Client.Character.LastNetworkUpdateID);
|
||||
characterStateId: GameMain.Client.Character?.LastNetworkUpdateID ?? Entity.NullEntityID);
|
||||
if (extraData != null) { newEvent.SetData(extraData); }
|
||||
|
||||
for (int i = events.Count - 1; i >= 0; i--)
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace Barotrauma.Networking
|
||||
public ImmutableArray<ServerContentPackage> ServerContentPackages { get; set; } =
|
||||
ImmutableArray<ServerContentPackage>.Empty;
|
||||
|
||||
public bool AllowModDownloads { get; private set; } = true;
|
||||
|
||||
public readonly record struct Callbacks(
|
||||
Callbacks.MessageCallback OnMessageReceived,
|
||||
Callbacks.DisconnectCallback OnDisconnect,
|
||||
@@ -151,6 +153,7 @@ namespace Barotrauma.Networking
|
||||
if (!ContentPackageOrderReceived)
|
||||
{
|
||||
ServerContentPackages = orderPacket.ContentPackages;
|
||||
AllowModDownloads = orderPacket.AllowModDownloads;
|
||||
if (ServerContentPackages.Length == 0)
|
||||
{
|
||||
string errorMsg = "Error in ContentPackageOrder message: list of content packages enabled on the server was empty.";
|
||||
|
||||
@@ -51,20 +51,38 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (Character.Controlled != null || (GameMain.GameSession is not { IsRunning: true })) { return; }
|
||||
|
||||
LocalizedString text = TextManager.Get("respawnquestionprompt");
|
||||
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;
|
||||
};
|
||||
|
||||
var respawnPrompt = new GUIMessageBox(
|
||||
TextManager.Get("tutorial.tryagainheader"), text,
|
||||
new LocalizedString[] { TextManager.Get("respawnquestionpromptrespawn"), TextManager.Get("respawnquestionpromptwait") })
|
||||
{
|
||||
UserData = "respawnquestionprompt"
|
||||
};
|
||||
respawnPrompt.Buttons[0].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: false);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,924 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class ServerSettings : ISerializableEntity
|
||||
{
|
||||
//GUI stuff
|
||||
private GUIFrame settingsFrame;
|
||||
private readonly Dictionary<SettingsTab, GUIComponent> settingsTabs = new Dictionary<SettingsTab, GUIComponent>();
|
||||
private readonly Dictionary<SettingsTab, GUIButton> tabButtons = new Dictionary<SettingsTab, GUIButton>();
|
||||
private SettingsTab selectedTab;
|
||||
|
||||
//UI elements relating to karma, hidden when karma is disabled
|
||||
private readonly List<GUIComponent> karmaElements = new List<GUIComponent>();
|
||||
private GUIDropDown karmaPresetDD;
|
||||
private GUIListBox karmaSettingsList;
|
||||
|
||||
private GUIComponent extraCargoPanel, monstersEnabledPanel;
|
||||
private GUIButton extraCargoButton, monstersEnabledButton;
|
||||
|
||||
enum SettingsTab
|
||||
{
|
||||
ServerIdentity,
|
||||
General,
|
||||
Antigriefing,
|
||||
Banlist
|
||||
}
|
||||
|
||||
public void AssignGUIComponent(string propertyName, GUIComponent component)
|
||||
{
|
||||
GetPropertyData(propertyName).AssignGUIComponent(component);
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
if (GUI.DisableHUD) { return; }
|
||||
|
||||
settingsFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
private void CreateSettingsFrame()
|
||||
{
|
||||
foreach (NetPropertyData prop in netProperties.Values)
|
||||
{
|
||||
prop.TempValue = prop.Value;
|
||||
}
|
||||
|
||||
//background frame
|
||||
settingsFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
|
||||
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, settingsFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null).OnClicked += (btn, userData) =>
|
||||
{
|
||||
if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) { ToggleSettingsFrame(btn, userData); }
|
||||
return true;
|
||||
};
|
||||
|
||||
new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null)
|
||||
{
|
||||
OnClicked = ToggleSettingsFrame
|
||||
};
|
||||
|
||||
//center frames
|
||||
GUIFrame innerFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.85f), settingsFrame.RectTransform, Anchor.Center) { MinSize = new Point(400, 430) });
|
||||
GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(innerFrame.Rect.Size - new Point(GUI.IntScale(20)), innerFrame.RectTransform, Anchor.Center),
|
||||
childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform), TextManager.Get("serversettingsbutton"), font: GUIStyle.LargeFont);
|
||||
|
||||
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), paddedFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.01f
|
||||
};
|
||||
|
||||
var tabContent = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.85f), paddedFrame.RectTransform), style: "InnerFrame");
|
||||
|
||||
//tabs
|
||||
var settingsTabTypes = Enum.GetValues(typeof(SettingsTab)).Cast<SettingsTab>();
|
||||
foreach (var settingsTab in settingsTabTypes)
|
||||
{
|
||||
settingsTabs[settingsTab] = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), tabContent.RectTransform, Anchor.Center));
|
||||
tabButtons[settingsTab] = new GUIButton(new RectTransform(new Vector2(0.2f, 1.2f), buttonArea.RectTransform), TextManager.Get($"ServerSettings{settingsTab}Tab"), style: "GUITabButton")
|
||||
{
|
||||
UserData = settingsTab,
|
||||
OnClicked = SelectSettingsTab
|
||||
};
|
||||
}
|
||||
GUITextBlock.AutoScaleAndNormalize(tabButtons.Values.Select(b => b.TextBlock));
|
||||
SelectSettingsTab(tabButtons[0], 0);
|
||||
tabButtons[SettingsTab.Banlist].Enabled =
|
||||
GameMain.Client.HasPermission(Networking.ClientPermissions.Ban) ||
|
||||
GameMain.Client.HasPermission(Networking.ClientPermissions.Unban);
|
||||
|
||||
//"Close"
|
||||
var buttonContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.05f), paddedFrame.RectTransform), style: null);
|
||||
var closeButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform, Anchor.BottomRight), TextManager.Get("Close"))
|
||||
{
|
||||
OnClicked = ToggleSettingsFrame
|
||||
};
|
||||
|
||||
CreateServerIdentityTab(settingsTabs[SettingsTab.ServerIdentity]);
|
||||
CreateGeneralTab(settingsTabs[SettingsTab.General]);
|
||||
CreateAntigriefingTab(settingsTabs[SettingsTab.Antigriefing]);
|
||||
CreateBanlistTab(settingsTabs[SettingsTab.Banlist]);
|
||||
|
||||
if (GameMain.Client == null ||
|
||||
!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings))
|
||||
{
|
||||
//block all settings if the client doesn't have permission to edit them
|
||||
foreach (var settingsTab in settingsTabs)
|
||||
{
|
||||
SetElementInteractability(settingsTab.Value, false);
|
||||
}
|
||||
}
|
||||
//keep these enabled, so clients can open the panels and see what's enabled even if they can't edit them
|
||||
extraCargoButton.Enabled = monstersEnabledButton.Enabled = true;
|
||||
}
|
||||
|
||||
private void SetElementInteractability(GUIComponent parent, bool interactable)
|
||||
{
|
||||
foreach (var child in parent.GetAllChildren<GUIComponent>())
|
||||
{
|
||||
child.Enabled = interactable;
|
||||
//make the disabled color slightly less dim (these should be readable, despite being non-interactable)
|
||||
child.DisabledColor = new Color(child.Color, child.Color.A / 255.0f * 0.8f);
|
||||
if (child is GUITextBlock textBlock)
|
||||
{
|
||||
textBlock.DisabledTextColor = new Color(textBlock.TextColor, textBlock.TextColor.A / 255.0f * 0.8f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateServerIdentityTab(GUIComponent parent)
|
||||
{
|
||||
//changing server visibility on the fly is not supported in dedicated servers
|
||||
if (GameMain.Client?.ClientPeer is not LidgrenClientPeer)
|
||||
{
|
||||
var isPublic = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), parent.RectTransform),
|
||||
TextManager.Get("publicserver"))
|
||||
{
|
||||
ToolTip = TextManager.Get("publicservertooltip")
|
||||
};
|
||||
AssignGUIComponent(nameof(IsPublic), isPublic);
|
||||
}
|
||||
|
||||
var serverNameLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), parent.RectTransform), TextManager.Get("ServerName"));
|
||||
var serverNameBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), serverNameLabel.RectTransform, Anchor.CenterRight),
|
||||
GameMain.Client.ServerSettings.ServerName)
|
||||
{
|
||||
OverflowClip = true,
|
||||
MaxTextLength = NetConfig.ServerNameMaxLength
|
||||
};
|
||||
serverNameBox.OnDeselected += (textBox, key) =>
|
||||
{
|
||||
if (textBox.Text.IsNullOrWhiteSpace())
|
||||
{
|
||||
textBox.Flash(GUIStyle.Red);
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
textBox.Text = GameMain.Client.ServerSettings.ServerName;
|
||||
}
|
||||
}
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(NetFlags.Properties);
|
||||
};
|
||||
AssignGUIComponent(nameof(ServerName), serverNameBox);
|
||||
|
||||
// server message *************************************************************************
|
||||
|
||||
var motdHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), parent.RectTransform), TextManager.Get("ServerMOTD"));
|
||||
var motdCharacterCount = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), motdHeader.RectTransform, Anchor.CenterRight), string.Empty, textAlignment: Alignment.CenterRight);
|
||||
var serverMessageContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), parent.RectTransform))
|
||||
{
|
||||
Visible = true
|
||||
};
|
||||
var serverMessageBox = new GUITextBox(new RectTransform(Vector2.One, serverMessageContainer.Content.RectTransform),
|
||||
style: "GUITextBoxNoBorder", wrap: true, textAlignment: Alignment.TopLeft)
|
||||
{
|
||||
MaxTextLength = NetConfig.ServerMessageMaxLength
|
||||
};
|
||||
var serverMessageHint = new GUITextBlock(new RectTransform(Vector2.One, serverMessageBox.RectTransform),
|
||||
textColor: Color.DarkGray * 0.6f, textAlignment: Alignment.TopLeft, font: GUIStyle.Font, text: TextManager.Get("ClickToWriteServerMessage"));
|
||||
AssignGUIComponent(nameof(ServerMessageText), serverMessageBox);
|
||||
|
||||
void updateServerMessageScrollBasedOnCaret()
|
||||
{
|
||||
float caretY = serverMessageBox.CaretScreenPos.Y;
|
||||
float bottomCaretExtent = serverMessageBox.Font.LineHeight * 1.5f;
|
||||
float topCaretExtent = -serverMessageBox.Font.LineHeight * 0.5f;
|
||||
if (caretY + bottomCaretExtent > serverMessageContainer.Rect.Bottom)
|
||||
{
|
||||
serverMessageContainer.ScrollBar.BarScroll
|
||||
= (caretY - serverMessageBox.Rect.Top - serverMessageContainer.Rect.Height + bottomCaretExtent)
|
||||
/ (serverMessageBox.Rect.Height - serverMessageContainer.Rect.Height);
|
||||
}
|
||||
else if (caretY + topCaretExtent < serverMessageContainer.Rect.Top)
|
||||
{
|
||||
serverMessageContainer.ScrollBar.BarScroll
|
||||
= (caretY - serverMessageBox.Rect.Top + topCaretExtent)
|
||||
/ (serverMessageBox.Rect.Height - serverMessageContainer.Rect.Height);
|
||||
}
|
||||
}
|
||||
serverMessageBox.OnSelected += (textBox, key) =>
|
||||
{
|
||||
serverMessageHint.Visible = false;
|
||||
updateServerMessageScrollBasedOnCaret();
|
||||
};
|
||||
serverMessageBox.OnTextChanged += (textBox, text) =>
|
||||
{
|
||||
serverMessageHint.Visible = !textBox.Selected && !textBox.Readonly && string.IsNullOrWhiteSpace(textBox.Text);
|
||||
RefreshServerInfoSize();
|
||||
return true;
|
||||
};
|
||||
serverMessageBox.RectTransform.SizeChanged += RefreshServerInfoSize;
|
||||
motdCharacterCount.TextGetter += () => { return serverMessageBox.Text.Length + " / " + NetConfig.ServerMessageMaxLength; };
|
||||
|
||||
void RefreshServerInfoSize()
|
||||
{
|
||||
serverMessageHint.Visible = !serverMessageBox.Selected && !serverMessageBox.Readonly && string.IsNullOrWhiteSpace(serverMessageBox.Text);
|
||||
Vector2 textSize = serverMessageBox.Font.MeasureString(serverMessageBox.WrappedText);
|
||||
serverMessageBox.RectTransform.NonScaledSize = new Point(serverMessageBox.RectTransform.NonScaledSize.X, Math.Max(serverMessageContainer.Content.Rect.Height, (int)textSize.Y + 10));
|
||||
serverMessageContainer.UpdateScrollBarSize();
|
||||
}
|
||||
|
||||
serverMessageBox.OnEnterPressed += (textBox, text) =>
|
||||
{
|
||||
string str = textBox.Text;
|
||||
int caretIndex = textBox.CaretIndex;
|
||||
textBox.Text = $"{str[..caretIndex]}\n{str[caretIndex..]}";
|
||||
textBox.CaretIndex = caretIndex + 1;
|
||||
|
||||
return true;
|
||||
};
|
||||
serverMessageBox.OnDeselected += (textBox, key) =>
|
||||
{
|
||||
if (!textBox.Readonly)
|
||||
{
|
||||
GameMain.Client?.ServerSettings?.ClientAdminWrite(NetFlags.Properties);
|
||||
}
|
||||
serverMessageHint.Visible = !textBox.Readonly && string.IsNullOrWhiteSpace(textBox.Text);
|
||||
};
|
||||
serverMessageBox.OnKeyHit += (sender, key) => updateServerMessageScrollBasedOnCaret();
|
||||
|
||||
// *************************************************************************
|
||||
|
||||
var playStyleLayoutLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), parent.RectTransform),
|
||||
TextManager.Get("ServerSettingsPlayStyle"));
|
||||
var playStyleSelection = new GUISelectionCarousel<PlayStyle>(new RectTransform(new Vector2(0.5f, 1.0f), playStyleLayoutLabel.RectTransform, Anchor.CenterRight));
|
||||
foreach (PlayStyle playStyle in Enum.GetValues(typeof(PlayStyle)))
|
||||
{
|
||||
playStyleSelection.AddElement(playStyle, TextManager.Get("servertag." + playStyle), TextManager.Get("servertagdescription." + playStyle));
|
||||
}
|
||||
AssignGUIComponent(nameof(PlayStyle), playStyleSelection);
|
||||
|
||||
var passwordLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), parent.RectTransform), TextManager.Get("Password"));
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), passwordLabel.RectTransform, Anchor.CenterRight),
|
||||
TextManager.Get("ServerSettingsSetPassword"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) => { CreateChangePasswordPrompt(); return true; }
|
||||
};
|
||||
|
||||
var wrongPasswordBanBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), parent.RectTransform), TextManager.Get("ServerSettingsBanAfterWrongPassword"));
|
||||
AssignGUIComponent(nameof(BanAfterWrongPassword), wrongPasswordBanBox);
|
||||
var allowedPasswordRetries = NetLobbyScreen.CreateLabeledNumberInput(parent, "ServerSettingsPasswordRetriesBeforeBan", 0, 10);
|
||||
AssignGUIComponent(nameof(MaxPasswordRetriesBeforeBan), allowedPasswordRetries);
|
||||
|
||||
var maxPlayers = NetLobbyScreen.CreateLabeledNumberInput(parent, "MaxPlayers", 0, NetConfig.MaxPlayers);
|
||||
AssignGUIComponent(nameof(MaxPlayers), maxPlayers);
|
||||
|
||||
// Language
|
||||
var languageLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform), TextManager.Get("Language"));
|
||||
var languageDD = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1.0f), languageLabel.RectTransform, Anchor.CenterRight));
|
||||
foreach (var language in ServerLanguageOptions.Options)
|
||||
{
|
||||
languageDD.AddItem(language.Label, language.Identifier);
|
||||
}
|
||||
languageLabel.InheritTotalChildrenMinHeight();
|
||||
AssignGUIComponent(nameof(Language), languageDD);
|
||||
|
||||
}
|
||||
|
||||
private static void CreateChangePasswordPrompt()
|
||||
{
|
||||
var passwordMsgBox = new GUIMessageBox(
|
||||
TextManager.Get("ServerSettingsSetPassword"),
|
||||
"", new LocalizedString[] { TextManager.Get("OK"), TextManager.Get("Cancel") },
|
||||
relativeSize: new Vector2(0.25f, 0.1f), minSize: new Point(400, GUI.IntScale(170)));
|
||||
var passwordHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), passwordMsgBox.Content.RectTransform), childAnchor: Anchor.TopCenter);
|
||||
var passwordBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1f), passwordHolder.RectTransform))
|
||||
{
|
||||
Censor = true
|
||||
};
|
||||
|
||||
passwordMsgBox.Content.Recalculate();
|
||||
passwordMsgBox.Content.InheritTotalChildrenHeight();
|
||||
passwordMsgBox.Content.Parent.RectTransform.MinSize = new Point(0, (int)(passwordMsgBox.Content.RectTransform.MinSize.Y / passwordMsgBox.Content.RectTransform.RelativeSize.Y));
|
||||
|
||||
var okButton = passwordMsgBox.Buttons[0];
|
||||
okButton.OnClicked += (_, __) =>
|
||||
{
|
||||
DebugConsole.ExecuteCommand($"setpassword \"{passwordBox.Text}\"");
|
||||
return true;
|
||||
};
|
||||
okButton.OnClicked += passwordMsgBox.Close;
|
||||
|
||||
var cancelButton = passwordMsgBox.Buttons[1];
|
||||
cancelButton.OnClicked = (_, __) =>
|
||||
{
|
||||
passwordMsgBox?.Close();
|
||||
passwordMsgBox = null;
|
||||
return true;
|
||||
};
|
||||
passwordBox.OnEnterPressed += (_, __) =>
|
||||
{
|
||||
okButton.OnClicked.Invoke(okButton, okButton.UserData);
|
||||
return true;
|
||||
};
|
||||
|
||||
passwordBox.Select();
|
||||
}
|
||||
|
||||
private void CreateGeneralTab(GUIComponent parent)
|
||||
{
|
||||
var listBox = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform), style: "GUIListBoxNoBorder")
|
||||
{
|
||||
AutoHideScrollBar = true,
|
||||
CurrentSelectMode = GUIListBox.SelectMode.None
|
||||
};
|
||||
|
||||
NetLobbyScreen.CreateSubHeader("serversettingscategory.roundmanagement", listBox.Content);
|
||||
|
||||
var endVoteBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsEndRoundVoting"));
|
||||
AssignGUIComponent(nameof(AllowEndVoting), endVoteBox);
|
||||
|
||||
NetLobbyScreen.CreateLabeledSlider(listBox.Content, headerTag: string.Empty, valueLabelTag: "ServerSettingsEndRoundVotesRequired", tooltipTag: string.Empty, out var slider, out var sliderLabel);
|
||||
|
||||
LocalizedString endRoundLabel = sliderLabel.Text;
|
||||
slider.Step = 0.2f;
|
||||
slider.Range = new Vector2(0.5f, 1.0f);
|
||||
AssignGUIComponent(nameof(EndVoteRequiredRatio), slider);
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
((GUITextBlock)scrollBar.UserData).Text = endRoundLabel + " " + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %";
|
||||
return true;
|
||||
};
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
//***********************************************
|
||||
|
||||
// Sub Selection
|
||||
|
||||
var subSelectionLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsSubSelection"));
|
||||
var subSelection = new GUISelectionCarousel<SelectionMode>(new RectTransform(new Vector2(0.5f, 1.0f), subSelectionLabel.RectTransform, Anchor.CenterRight));
|
||||
foreach (SelectionMode selectionMode in Enum.GetValues(typeof(SelectionMode)))
|
||||
{
|
||||
subSelection.AddElement(selectionMode, TextManager.Get(selectionMode.ToString()));
|
||||
}
|
||||
AssignGUIComponent(nameof(SubSelectionMode), subSelection);
|
||||
|
||||
// Mode Selection
|
||||
var gameModeSelectionLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsModeSelection"));
|
||||
var gameModeSelection = new GUISelectionCarousel<SelectionMode>(new RectTransform(new Vector2(0.5f, 1.0f), gameModeSelectionLabel.RectTransform, Anchor.CenterRight));
|
||||
foreach (SelectionMode selectionMode in Enum.GetValues(typeof(SelectionMode)))
|
||||
{
|
||||
gameModeSelection.AddElement(selectionMode, TextManager.Get(selectionMode.ToString()));
|
||||
}
|
||||
AssignGUIComponent(nameof(ModeSelectionMode), gameModeSelection);
|
||||
|
||||
//***********************************************
|
||||
|
||||
LocalizedString autoRestartDelayLabel = TextManager.Get("ServerSettingsAutoRestartDelay") + " ";
|
||||
|
||||
var autorestartBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("AutoRestart"));
|
||||
AssignGUIComponent(nameof(AutoRestart), autorestartBox);
|
||||
NetLobbyScreen.CreateLabeledSlider(listBox.Content, headerTag: string.Empty, valueLabelTag: string.Empty, tooltipTag: string.Empty,
|
||||
out var startIntervalSlider, out var startIntervalSliderLabel, range: new Vector2(10.0f, 300.0f));
|
||||
startIntervalSlider.StepValue = 10.0f;
|
||||
startIntervalSlider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
GUITextBlock text = scrollBar.UserData as GUITextBlock;
|
||||
text.Text = autoRestartDelayLabel + ToolBox.SecondsToReadableTime(scrollBar.BarScrollValue);
|
||||
return true;
|
||||
};
|
||||
AssignGUIComponent(nameof(AutoRestartInterval), startIntervalSlider);
|
||||
startIntervalSlider.OnMoved(startIntervalSlider, startIntervalSlider.BarScroll);
|
||||
|
||||
//***********************************************
|
||||
|
||||
var startWhenClientsReady = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsStartWhenClientsReady"));
|
||||
AssignGUIComponent(nameof(StartWhenClientsReady), startWhenClientsReady);
|
||||
|
||||
NetLobbyScreen.CreateLabeledSlider(listBox.Content, headerTag: string.Empty, valueLabelTag: "ServerSettingsStartWhenClientsReadyRatio", tooltipTag: string.Empty,
|
||||
out slider, out sliderLabel);
|
||||
LocalizedString clientsReadyRequiredLabel = sliderLabel.Text;
|
||||
slider.Step = 0.2f;
|
||||
slider.Range = new Vector2(0.5f, 1.0f);
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
((GUITextBlock)scrollBar.UserData).Text = clientsReadyRequiredLabel.Replace("[percentage]", ((int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f)).ToString());
|
||||
return true;
|
||||
};
|
||||
AssignGUIComponent(nameof(StartWhenClientsReadyRatio), slider);
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
var randomizeLevelBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsRandomizeSeed"));
|
||||
AssignGUIComponent(nameof(RandomizeSeed), randomizeLevelBox);
|
||||
|
||||
//***********************************************
|
||||
|
||||
NetLobbyScreen.CreateSubHeader("serversettingsroundstab", listBox.Content);
|
||||
|
||||
var voiceChatEnabled = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsVoiceChatEnabled"));
|
||||
AssignGUIComponent(nameof(VoiceChatEnabled), voiceChatEnabled);
|
||||
|
||||
var allowSpecBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsAllowSpectating"));
|
||||
AssignGUIComponent(nameof(AllowSpectating), allowSpecBox);
|
||||
|
||||
var losModeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("LosEffect"));
|
||||
var losModeSelection = new GUISelectionCarousel<LosMode>(new RectTransform(new Vector2(0.5f, 0.6f), losModeLabel.RectTransform, Anchor.CenterRight));
|
||||
foreach (var losMode in Enum.GetValues(typeof(LosMode)).Cast<LosMode>())
|
||||
{
|
||||
losModeSelection.AddElement(losMode, TextManager.Get($"LosMode{losMode}"), TextManager.Get($"LosMode{losMode}.tooltip"));
|
||||
}
|
||||
AssignGUIComponent(nameof(LosMode), losModeSelection);
|
||||
|
||||
var healthBarModeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("ShowEnemyHealthBars"));
|
||||
var healthBarModeSelection = new GUISelectionCarousel<EnemyHealthBarMode>(new RectTransform(new Vector2(0.5f, 0.6f), healthBarModeLabel.RectTransform, Anchor.CenterRight));
|
||||
foreach (var healthBarMode in Enum.GetValues(typeof(EnemyHealthBarMode)).Cast<EnemyHealthBarMode>())
|
||||
{
|
||||
healthBarModeSelection.AddElement(healthBarMode, TextManager.Get($"ShowEnemyHealthBars.{healthBarMode}"));
|
||||
}
|
||||
AssignGUIComponent(nameof(ShowEnemyHealthBars), healthBarModeSelection);
|
||||
|
||||
var disableBotConversationsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsDisableBotConversations"));
|
||||
AssignGUIComponent(nameof(DisableBotConversations), disableBotConversationsBox);
|
||||
|
||||
//***********************************************
|
||||
|
||||
NetLobbyScreen.CreateSubHeader("serversettingscategory.misc", listBox.Content);
|
||||
|
||||
var shareSubsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsShareSubFiles"));
|
||||
AssignGUIComponent(nameof(AllowFileTransfers), shareSubsBox);
|
||||
|
||||
var saveLogsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsSaveLogs"))
|
||||
{
|
||||
OnSelected = (GUITickBox) =>
|
||||
{
|
||||
//TODO: fix?
|
||||
//showLogButton.Visible = SaveServerLogs;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
AssignGUIComponent(nameof(SaveServerLogs), saveLogsBox);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// game settings
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
GUILayoutGroup buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), listBox.Content.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
const string MonstersEnabledUserdata = "monstersenabled";
|
||||
const string ExtraCargoUserdata = "extracargo";
|
||||
|
||||
monstersEnabledButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonHolder.RectTransform),
|
||||
TextManager.Get("ServerSettingsMonsterSpawns"), style: "GUIButtonSmall")
|
||||
{
|
||||
Enabled = !GameMain.NetworkMember.GameStarted
|
||||
};
|
||||
monstersEnabledPanel = CreateMonstersEnabledPanel();
|
||||
monstersEnabledButton.UserData = MonstersEnabledUserdata;
|
||||
monstersEnabledButton.OnClicked = ExtraSettingsButtonClicked;
|
||||
|
||||
extraCargoButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonHolder.RectTransform),
|
||||
TextManager.Get("ServerSettingsAdditionalCargo"), style: "GUIButtonSmall")
|
||||
{
|
||||
Enabled = !GameMain.NetworkMember.GameStarted
|
||||
};
|
||||
|
||||
extraCargoPanel = CreateExtraCargoPanel();
|
||||
extraCargoButton.UserData = ExtraCargoUserdata;
|
||||
extraCargoButton.OnClicked = ExtraSettingsButtonClicked;
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(buttonHolder.Children.Select(c => ((GUIButton)c).TextBlock));
|
||||
|
||||
bool ExtraSettingsButtonClicked(GUIButton button, object obj)
|
||||
{
|
||||
//the extra settings buttons (monsters enabled, cargo) hold a reference to the panel they're supposed to toggle
|
||||
GUIComponent panel;
|
||||
switch (obj as string)
|
||||
{
|
||||
case MonstersEnabledUserdata:
|
||||
panel = monstersEnabledPanel;
|
||||
break;
|
||||
case ExtraCargoUserdata:
|
||||
panel = extraCargoPanel;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unrecognized extra settings button");
|
||||
}
|
||||
if (GameMain.NetworkMember.GameStarted)
|
||||
{
|
||||
panel.Visible = false;
|
||||
button.Enabled = false;
|
||||
return true;
|
||||
}
|
||||
panel.Visible = !panel.Visible;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private GUIComponent CreateMonstersEnabledPanel()
|
||||
{
|
||||
var monsterFrame = new GUIListBox(new RectTransform(new Vector2(0.5f, 0.7f), settingsTabs[SettingsTab.General].RectTransform, Anchor.BottomLeft, Pivot.BottomRight))
|
||||
{
|
||||
Visible = false,
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
InitMonstersEnabled();
|
||||
List<Identifier> monsterNames = MonsterEnabled.Keys.ToList();
|
||||
tempMonsterEnabled = new Dictionary<Identifier, bool>(MonsterEnabled);
|
||||
foreach (Identifier s in monsterNames)
|
||||
{
|
||||
LocalizedString translatedLabel = TextManager.Get($"Character.{s}").Fallback(s.Value);
|
||||
var monsterEnabledBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), monsterFrame.Content.RectTransform) { MinSize = new Point(0, 25) },
|
||||
label: translatedLabel)
|
||||
{
|
||||
Selected = tempMonsterEnabled[s],
|
||||
OnSelected = (GUITickBox tb) =>
|
||||
{
|
||||
tempMonsterEnabled[s] = tb.Selected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
monsterFrame.Content.RectTransform.SortChildren((c1, c2) =>
|
||||
{
|
||||
var name1 = (c1.GUIComponent as GUITickBox)?.Text ?? string.Empty;
|
||||
var name2 = (c2.GUIComponent as GUITickBox)?.Text ?? string.Empty;
|
||||
return name1.CompareTo(name2);
|
||||
});
|
||||
|
||||
if (GameMain.Client == null ||
|
||||
!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings))
|
||||
{
|
||||
SetElementInteractability(monsterFrame.Content, false);
|
||||
}
|
||||
|
||||
return monsterFrame;
|
||||
}
|
||||
|
||||
private GUIComponent CreateExtraCargoPanel()
|
||||
{
|
||||
var cargoFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.7f), settingsTabs[SettingsTab.General].RectTransform, Anchor.BottomRight, Pivot.BottomLeft))
|
||||
{
|
||||
Visible = false,
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
var cargoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), cargoFrame.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var filterText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), cargoContent.RectTransform), TextManager.Get("serverlog.filter"), font: GUIStyle.SubHeadingFont);
|
||||
var entityFilterBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), filterText.RectTransform, Anchor.CenterRight), font: GUIStyle.Font, createClearButton: true);
|
||||
filterText.RectTransform.MinSize = new Point(0, entityFilterBox.RectTransform.MinSize.Y);
|
||||
var cargoList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), cargoContent.RectTransform));
|
||||
entityFilterBox.OnTextChanged += (textBox, text) =>
|
||||
{
|
||||
foreach (var child in cargoList.Content.Children)
|
||||
{
|
||||
if (child.UserData is not ItemPrefab itemPrefab) { continue; }
|
||||
child.Visible = string.IsNullOrEmpty(text) || itemPrefab.Name.Contains(text, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
foreach (ItemPrefab ip in ItemPrefab.Prefabs.OrderBy(ip => ip.Name))
|
||||
{
|
||||
if (ip.AllowAsExtraCargo.HasValue)
|
||||
{
|
||||
if (!ip.AllowAsExtraCargo.Value) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ip.CanBeBought) { continue; }
|
||||
}
|
||||
|
||||
var itemFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), cargoList.Content.RectTransform) { MinSize = new Point(0, 30) }, isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
UserData = ip,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
if (ip.InventoryIcon != null || ip.Sprite != null)
|
||||
{
|
||||
GUIImage img = new GUIImage(new RectTransform(new Point(itemFrame.Rect.Height), itemFrame.RectTransform),
|
||||
ip.InventoryIcon ?? ip.Sprite, scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
img.Color = img.Sprite == ip.InventoryIcon ? ip.InventoryIconColor : ip.SpriteColor;
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.75f, 1.0f), itemFrame.RectTransform),
|
||||
ip.Name, font: GUIStyle.SmallFont)
|
||||
{
|
||||
Wrap = true,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
ExtraCargo.TryGetValue(ip, out int cargoVal);
|
||||
var amountInput = new GUINumberInput(new RectTransform(new Vector2(0.35f, 1.0f), itemFrame.RectTransform),
|
||||
NumberType.Int, textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
MinValueInt = 0,
|
||||
MaxValueInt = MaxExtraCargoItemsOfType,
|
||||
IntValue = cargoVal
|
||||
};
|
||||
amountInput.OnValueChanged += (numberInput) =>
|
||||
{
|
||||
if (ExtraCargo.ContainsKey(ip))
|
||||
{
|
||||
ExtraCargo[ip] = numberInput.IntValue;
|
||||
if (numberInput.IntValue <= 0) { ExtraCargo.Remove(ip); }
|
||||
}
|
||||
else if (ExtraCargo.Keys.Count < MaxExtraCargoItemTypes)
|
||||
{
|
||||
ExtraCargo.Add(ip, numberInput.IntValue);
|
||||
}
|
||||
numberInput.IntValue = ExtraCargo.ContainsKey(ip) ? ExtraCargo[ip] : 0;
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
foreach (var child in cargoList.Content.GetAllChildren())
|
||||
{
|
||||
if (child.GetChild<GUINumberInput>() is GUINumberInput otherNumberInput)
|
||||
{
|
||||
otherNumberInput.PlusButton.Enabled = ExtraCargo.Keys.Count < MaxExtraCargoItemTypes && otherNumberInput.IntValue < otherNumberInput.MaxValueInt;
|
||||
}
|
||||
}
|
||||
}, 0.0f);
|
||||
};
|
||||
}
|
||||
if (GameMain.Client == null ||
|
||||
!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings))
|
||||
{
|
||||
SetElementInteractability(cargoList.Content, false);
|
||||
}
|
||||
|
||||
return cargoFrame;
|
||||
}
|
||||
|
||||
private void CreateAntigriefingTab(GUIComponent parent)
|
||||
{
|
||||
var listBox = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform), style: "GUIListBoxNoBorder")
|
||||
{
|
||||
AutoHideScrollBar = true,
|
||||
CurrentSelectMode = GUIListBox.SelectMode.None
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// antigriefing
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
var tickBoxContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.25f), listBox.Content.RectTransform))
|
||||
{
|
||||
AutoHideScrollBar = true,
|
||||
UseGridLayout = true
|
||||
};
|
||||
tickBoxContainer.Padding *= 2.0f;
|
||||
|
||||
var allowFriendlyFire = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowFriendlyFire"));
|
||||
AssignGUIComponent(nameof(AllowFriendlyFire), allowFriendlyFire);
|
||||
|
||||
var killableNPCs = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsKillableNPCs"));
|
||||
AssignGUIComponent(nameof(KillableNPCs), killableNPCs);
|
||||
|
||||
var destructibleOutposts = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsDestructibleOutposts"));
|
||||
AssignGUIComponent(nameof(DestructibleOutposts), destructibleOutposts);
|
||||
|
||||
var lockAllDefaultWires = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsLockAllDefaultWires"));
|
||||
AssignGUIComponent(nameof(LockAllDefaultWires), lockAllDefaultWires);
|
||||
|
||||
var allowRewiring = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowRewiring"));
|
||||
AssignGUIComponent(nameof(AllowRewiring), allowRewiring);
|
||||
|
||||
var allowWifiChatter = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowWifiChat"));
|
||||
AssignGUIComponent(nameof(AllowLinkingWifiToChat), allowWifiChatter);
|
||||
|
||||
var allowDisguises = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowDisguises"));
|
||||
AssignGUIComponent(nameof(AllowDisguises), allowDisguises);
|
||||
|
||||
var allowImmediateItemDeliveryBox = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsImmediateItemDelivery"));
|
||||
AssignGUIComponent(nameof(AllowImmediateItemDelivery), allowImmediateItemDeliveryBox);
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(tickBoxContainer.Content.Children.Select(c => ((GUITickBox)c).TextBlock));
|
||||
|
||||
tickBoxContainer.RectTransform.MinSize = new Point(0, (int)(tickBoxContainer.Content.Children.First().Rect.Height * 2.0f + tickBoxContainer.Padding.Y + tickBoxContainer.Padding.W));
|
||||
|
||||
tickBoxContainer.RectTransform.MinSize = new Point(0, (int)(tickBoxContainer.Content.Children.First().Rect.Height * 2.0f + tickBoxContainer.Padding.Y + tickBoxContainer.Padding.W));
|
||||
|
||||
var voteKickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowVoteKick"));
|
||||
AssignGUIComponent(nameof(AllowVoteKick), voteKickBox);
|
||||
|
||||
NetLobbyScreen.CreateLabeledSlider(listBox.Content, headerTag: string.Empty, valueLabelTag: "ServerSettingsKickVotesRequired", tooltipTag: string.Empty, out var slider, out var sliderLabel);
|
||||
LocalizedString votesRequiredLabel = sliderLabel.Text + " ";
|
||||
slider.Step = 0.2f;
|
||||
slider.Range = new Vector2(0.5f, 1.0f);
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
((GUITextBlock)scrollBar.UserData).Text = votesRequiredLabel + (int)MathUtils.Round(scrollBar.BarScrollValue * 100.0f, 10.0f) + " %";
|
||||
return true;
|
||||
};
|
||||
AssignGUIComponent(nameof(KickVoteRequiredRatio), slider);
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
NetLobbyScreen.CreateLabeledSlider(listBox.Content, headerTag: string.Empty, valueLabelTag: "ServerSettingsAutobanTime", tooltipTag: "ServerSettingsAutobanTime.Tooltip", out slider, out sliderLabel);
|
||||
LocalizedString autobanLabel = sliderLabel.Text + " ";
|
||||
slider.Range = new Vector2(0.0f, MaxAutoBanTime);
|
||||
slider.StepValue = 60.0f * 15.0f; //15 minutes
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
((GUITextBlock)scrollBar.UserData).Text = autobanLabel + ToolBox.SecondsToReadableTime(scrollBar.BarScrollValue);
|
||||
return true;
|
||||
};
|
||||
AssignGUIComponent(nameof(AutoBanTime), slider);
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
var maximumTransferAmount = NetLobbyScreen.CreateLabeledNumberInput(listBox.Content, "serversettingsmaximumtransferrequest", 0, CampaignMode.MaxMoney, "serversettingsmaximumtransferrequesttooltip");
|
||||
AssignGUIComponent(nameof(MaximumMoneyTransferRequest), maximumTransferAmount);
|
||||
|
||||
var lootedMoneyDestination = NetLobbyScreen.CreateLabeledDropdown(listBox.Content, "serversettingslootedmoneydestination", numElements: 2, "serversettingslootedmoneydestinationtooltip");
|
||||
lootedMoneyDestination.AddItem(TextManager.Get("lootedmoneydestination.bank"), LootedMoneyDestination.Bank);
|
||||
lootedMoneyDestination.AddItem(TextManager.Get("lootedmoneydestination.wallet"), LootedMoneyDestination.Wallet);
|
||||
AssignGUIComponent(nameof(LootedMoneyDestination), lootedMoneyDestination);
|
||||
|
||||
var enableDosProtection = new GUITickBox(new RectTransform(new Vector2(0.5f, 0.0f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsEnableDoSProtection"))
|
||||
{
|
||||
ToolTip = TextManager.Get("ServerSettingsEnableDoSProtectionTooltip")
|
||||
};
|
||||
AssignGUIComponent(nameof(EnableDoSProtection), enableDosProtection);
|
||||
|
||||
NetLobbyScreen.CreateLabeledSlider(listBox.Content, headerTag: string.Empty, valueLabelTag: "ServerSettingsMaxPacketAmount", tooltipTag: string.Empty, out GUIScrollBar maxPacketSlider, out GUITextBlock maxPacketSliderLabel);
|
||||
LocalizedString maxPacketCountLabel = maxPacketSliderLabel.Text;
|
||||
maxPacketSlider.Step = 0.001f;
|
||||
maxPacketSlider.Range = new Vector2(PacketLimitMin, PacketLimitMax);
|
||||
maxPacketSlider.ToolTip = packetAmountTooltip;
|
||||
maxPacketSlider.OnMoved = (scrollBar, _) =>
|
||||
{
|
||||
GUITextBlock textBlock = (GUITextBlock)scrollBar.UserData;
|
||||
int value = (int)MathF.Floor(scrollBar.BarScrollValue);
|
||||
|
||||
LocalizedString valueText = value > PacketLimitMin
|
||||
? value.ToString()
|
||||
: TextManager.Get("ServerSettingsNoLimit");
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case <= PacketLimitMin:
|
||||
textBlock.TextColor = GUIStyle.Green;
|
||||
scrollBar.ToolTip = packetAmountTooltip;
|
||||
break;
|
||||
case < PacketLimitWarning:
|
||||
textBlock.TextColor = GUIStyle.Red;
|
||||
scrollBar.ToolTip = packetAmountTooltipWarning;
|
||||
break;
|
||||
default:
|
||||
textBlock.TextColor = GUIStyle.TextColorNormal;
|
||||
scrollBar.ToolTip = packetAmountTooltip;
|
||||
break;
|
||||
}
|
||||
|
||||
textBlock.Text = $"{maxPacketCountLabel} {valueText}";
|
||||
return true;
|
||||
};
|
||||
AssignGUIComponent(nameof(MaxPacketAmount), maxPacketSlider);
|
||||
maxPacketSlider.OnMoved(maxPacketSlider, maxPacketSlider.BarScroll);
|
||||
|
||||
// karma --------------------------------------------------------------------------
|
||||
|
||||
NetLobbyScreen.CreateSubHeader("Karma", listBox.Content, toolTipTag: "KarmaExplanation");
|
||||
|
||||
var karmaBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1f), listBox.Content.RectTransform), TextManager.Get("ServerSettingsUseKarma"))
|
||||
{
|
||||
ToolTip = TextManager.Get("KarmaExplanation")
|
||||
};
|
||||
AssignGUIComponent(nameof(KarmaEnabled), karmaBox);
|
||||
|
||||
karmaPresetDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), listBox.Content.RectTransform));
|
||||
foreach (string karmaPreset in GameMain.NetworkMember.KarmaManager.Presets.Keys)
|
||||
{
|
||||
karmaPresetDD.AddItem(TextManager.Get("KarmaPreset." + karmaPreset), karmaPreset);
|
||||
}
|
||||
karmaElements.Add(karmaPresetDD);
|
||||
|
||||
var karmaSettingsContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), listBox.Content.RectTransform), style: null);
|
||||
karmaElements.Add(karmaSettingsContainer);
|
||||
karmaSettingsList = new GUIListBox(new RectTransform(Vector2.One, karmaSettingsContainer.RectTransform))
|
||||
{
|
||||
Spacing = (int)(8 * GUI.Scale)
|
||||
};
|
||||
karmaSettingsList.Padding *= 2.0f;
|
||||
|
||||
karmaPresetDD.SelectItem(KarmaPreset);
|
||||
SetElementInteractability(karmaSettingsList.Content, !karmaBox.Selected || KarmaPreset != "custom");
|
||||
GameMain.NetworkMember.KarmaManager.CreateSettingsFrame(karmaSettingsList.Content);
|
||||
karmaPresetDD.OnSelected = (selected, obj) =>
|
||||
{
|
||||
string newKarmaPreset = obj as string;
|
||||
if (newKarmaPreset == KarmaPreset) { return true; }
|
||||
|
||||
List<NetPropertyData> properties = netProperties.Values.ToList();
|
||||
List<object> prevValues = new List<object>();
|
||||
foreach (NetPropertyData prop in netProperties.Values)
|
||||
{
|
||||
prevValues.Add(prop.TempValue);
|
||||
if (prop.GUIComponent != null) { prop.Value = prop.GUIComponentValue; }
|
||||
}
|
||||
if (KarmaPreset == "custom")
|
||||
{
|
||||
GameMain.NetworkMember?.KarmaManager?.SaveCustomPreset();
|
||||
GameMain.NetworkMember?.KarmaManager?.Save();
|
||||
}
|
||||
KarmaPreset = newKarmaPreset;
|
||||
GameMain.NetworkMember.KarmaManager.SelectPreset(KarmaPreset);
|
||||
karmaSettingsList.Content.ClearChildren();
|
||||
GameMain.NetworkMember.KarmaManager.CreateSettingsFrame(karmaSettingsList.Content);
|
||||
SetElementInteractability(karmaSettingsList.Content, !karmaBox.Selected || KarmaPreset != "custom");
|
||||
for (int i = 0; i < netProperties.Count; i++)
|
||||
{
|
||||
properties[i].TempValue = prevValues[i];
|
||||
}
|
||||
return true;
|
||||
};
|
||||
AssignGUIComponent(nameof(KarmaPreset), karmaPresetDD);
|
||||
karmaBox.OnSelected = (tb) =>
|
||||
{
|
||||
SetElementInteractability(karmaSettingsList.Content, !karmaBox.Selected || KarmaPreset != "custom");
|
||||
karmaElements.ForEach(e => e.Visible = tb.Selected);
|
||||
return true;
|
||||
};
|
||||
karmaElements.ForEach(e => e.Visible = KarmaEnabled);
|
||||
|
||||
listBox.Content.InheritTotalChildrenMinHeight();
|
||||
}
|
||||
|
||||
private void CreateBanlistTab(GUIComponent parent)
|
||||
{
|
||||
BanList.CreateBanFrame(parent);
|
||||
}
|
||||
|
||||
private bool SelectSettingsTab(GUIButton button, object obj)
|
||||
{
|
||||
selectedTab = (SettingsTab)obj;
|
||||
foreach (var key in settingsTabs.Keys)
|
||||
{
|
||||
settingsTabs[key].Visible = key == selectedTab;
|
||||
tabButtons[key].Selected = key == selectedTab;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (KarmaPreset == "custom")
|
||||
{
|
||||
GameMain.NetworkMember?.KarmaManager?.SaveCustomPreset();
|
||||
GameMain.NetworkMember?.KarmaManager?.Save();
|
||||
}
|
||||
ClientAdminWrite(NetFlags.Properties);
|
||||
foreach (NetPropertyData prop in netProperties.Values)
|
||||
{
|
||||
prop.GUIComponent = null;
|
||||
}
|
||||
settingsFrame = null;
|
||||
//give control of server settings back to elements in the lobby
|
||||
GameMain.NetLobbyScreen.AssignComponentsToServerSettings();
|
||||
}
|
||||
|
||||
public bool ToggleSettingsFrame(GUIButton button, object obj)
|
||||
{
|
||||
if (GameMain.NetworkMember == null) { return false; }
|
||||
if (settingsFrame == null)
|
||||
{
|
||||
CreateSettingsFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
Close();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,7 +275,7 @@ namespace Barotrauma.Networking
|
||||
var messageType = !ForceLocal && ChatMessage.CanUseRadio(GameMain.Client.Character, out _) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||
if (GameMain.Client.Character.IsDead) { messageType = ChatMessageType.Dead; }
|
||||
|
||||
GameMain.Client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
GameMain.Client.Character.ShowTextlessSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
}
|
||||
//encode audio and enqueue it
|
||||
lock (buffers)
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Barotrauma.Networking
|
||||
if (client.VoipSound == null)
|
||||
{
|
||||
DebugConsole.Log("Recreating voipsound " + queueId);
|
||||
client.VoipSound = new VoipSound(client.Name, GameMain.SoundManager, client.VoipQueue);
|
||||
client.VoipSound = new VoipSound(client, GameMain.SoundManager, client.VoipQueue);
|
||||
}
|
||||
GameMain.SoundManager.ForceStreamUpdate();
|
||||
client.RadioNoise = 0.0f;
|
||||
@@ -122,12 +122,13 @@ namespace Barotrauma.Networking
|
||||
ChatMessage.CanUseRadio(Character.Controlled, out var recipientRadio) &&
|
||||
senderRadio.CanReceive(recipientRadio) ?
|
||||
ChatMessageType.Radio : ChatMessageType.Default;
|
||||
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
client.Character.ShowTextlessSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
|
||||
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
|
||||
client.RadioNoise = 0.0f;
|
||||
if (messageType == ChatMessageType.Radio)
|
||||
{
|
||||
client.VoipSound.UsingRadio = true;
|
||||
client.VoipSound.SetRange(senderRadio.Range * RangeNear * speechImpedimentMultiplier * rangeMultiplier, senderRadio.Range * speechImpedimentMultiplier * rangeMultiplier);
|
||||
if (distanceFactor > RangeNear && !spectating)
|
||||
{
|
||||
@@ -137,11 +138,12 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
client.VoipSound.SetRange(ChatMessage.SpeakRange * RangeNear * speechImpedimentMultiplier * rangeMultiplier, ChatMessage.SpeakRange * speechImpedimentMultiplier * rangeMultiplier);
|
||||
client.VoipSound.UsingRadio = false;
|
||||
client.VoipSound.SetRange(ChatMessage.SpeakRangeVOIP * RangeNear * speechImpedimentMultiplier * rangeMultiplier, ChatMessage.SpeakRangeVOIP * speechImpedimentMultiplier * rangeMultiplier);
|
||||
}
|
||||
client.VoipSound.UseMuffleFilter =
|
||||
messageType != ChatMessageType.Radio && Character.Controlled != null && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters &&
|
||||
SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRange, client.Character.CurrentHull);
|
||||
SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRangeVOIP, client.Character.CurrentHull);
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen?.SetPlayerSpeaking(client);
|
||||
|
||||
@@ -70,6 +70,8 @@ namespace Barotrauma.Particles
|
||||
|
||||
public Vector4 ColorMultiplier;
|
||||
|
||||
public float VelocityChangeMultiplier;
|
||||
|
||||
public bool DrawOnTop { get; private set; }
|
||||
|
||||
public ParticlePrefab.DrawTargetType DrawTarget
|
||||
@@ -121,8 +123,8 @@ namespace Barotrauma.Particles
|
||||
|
||||
animState = 0;
|
||||
animFrame = 0;
|
||||
|
||||
currentHull = Hull.FindHull(position, hullGuess);
|
||||
|
||||
currentHull = prefab.CanEnterSubs ? Hull.FindHull(position, hullGuess) : null;
|
||||
|
||||
size = prefab.StartSizeMin + (prefab.StartSizeMax - prefab.StartSizeMin) * Rand.Range(0.0f, 1.0f);
|
||||
|
||||
@@ -178,6 +180,8 @@ namespace Barotrauma.Particles
|
||||
|
||||
HighQualityCollisionDetection = false;
|
||||
|
||||
VelocityChangeMultiplier = 1.0f;
|
||||
|
||||
OnChangeHull = null;
|
||||
OnCollision = null;
|
||||
|
||||
@@ -247,8 +251,8 @@ namespace Barotrauma.Particles
|
||||
bool inWater = (currentHull == null || (currentHull.Submarine != null && position.Y - currentHull.Submarine.DrawPosition.Y < currentHull.Surface));
|
||||
if (inWater)
|
||||
{
|
||||
velocity.X += velocityChangeWater.X * deltaTime;
|
||||
velocity.Y += velocityChangeWater.Y * deltaTime;
|
||||
velocity.X += velocityChangeWater.X * VelocityChangeMultiplier * deltaTime;
|
||||
velocity.Y += velocityChangeWater.Y * VelocityChangeMultiplier * deltaTime;
|
||||
if (prefab.WaterDrag > 0.0f)
|
||||
{
|
||||
ApplyDrag(prefab.WaterDrag, deltaTime);
|
||||
@@ -256,8 +260,8 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
else
|
||||
{
|
||||
velocity.X += velocityChange.X * deltaTime;
|
||||
velocity.Y += velocityChange.Y * deltaTime;
|
||||
velocity.X += velocityChange.X * VelocityChangeMultiplier * deltaTime;
|
||||
velocity.Y += velocityChange.Y * VelocityChangeMultiplier * deltaTime;
|
||||
if (prefab.Drag > 0.0f)
|
||||
{
|
||||
ApplyDrag(prefab.Drag, deltaTime);
|
||||
|
||||
@@ -132,6 +132,9 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, IsPropertySaveable.No, description: "Is the particle considered to be inside a submarine if it spawns at a position inside a hull (causing it to move with the sub)?")]
|
||||
public bool CanEnterSubs { get; private set; }
|
||||
|
||||
[Editable(0.0f, 10000.0f), Serialize(0.0f, IsPropertySaveable.No, description: "Radius of the particle's collider. Only has an effect if UseCollision is set to true.")]
|
||||
public float CollisionRadius { get; private set; }
|
||||
|
||||
@@ -153,10 +156,10 @@ namespace Barotrauma.Particles
|
||||
|
||||
//size -----------------------------------------
|
||||
|
||||
[Editable, Serialize("1.0,1.0", IsPropertySaveable.No, description: "The minimum initial size of the particle.")]
|
||||
[Editable(DecimalCount = 3), Serialize("1.0,1.0", IsPropertySaveable.No, description: "The minimum initial size of the particle.")]
|
||||
public Vector2 StartSizeMin { get; private set; }
|
||||
|
||||
[Editable, Serialize("1.0,1.0", IsPropertySaveable.No, description: "The maximum initial size of the particle.")]
|
||||
[Editable(DecimalCount = 3), Serialize("1.0,1.0", IsPropertySaveable.No, description: "The maximum initial size of the particle.")]
|
||||
public Vector2 StartSizeMax { get; private set; }
|
||||
|
||||
[Editable, Serialize("0.0,0.0", IsPropertySaveable.No, description: "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user