Unstable 1.8.4.0
This commit is contained in:
@@ -63,6 +63,12 @@ namespace Barotrauma
|
||||
private float prevZoom;
|
||||
|
||||
public float Shake;
|
||||
|
||||
/// <summary>
|
||||
/// Should the camera's transform matrices be automatically updated to match the screen resolution?
|
||||
/// </summary>
|
||||
public bool AutoUpdateToScreenResolution = true;
|
||||
|
||||
public Vector2 ShakePosition { get; private set; }
|
||||
private float shakeTimer;
|
||||
|
||||
@@ -198,10 +204,13 @@ namespace Barotrauma
|
||||
|
||||
public void UpdateTransform(bool interpolate = true, bool updateListener = true)
|
||||
{
|
||||
if (GameMain.GraphicsWidth != Resolution.X ||
|
||||
GameMain.GraphicsHeight != Resolution.Y)
|
||||
if (AutoUpdateToScreenResolution)
|
||||
{
|
||||
CreateMatrices();
|
||||
if (GameMain.GraphicsWidth != Resolution.X ||
|
||||
GameMain.GraphicsHeight != Resolution.Y)
|
||||
{
|
||||
CreateMatrices();
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 interpolatedPosition = interpolate ? Timing.Interpolate(prevPosition, position) : position;
|
||||
|
||||
@@ -42,13 +42,13 @@ namespace Barotrauma
|
||||
if (wallTarget != null && !IsCoolDownRunning)
|
||||
{
|
||||
Vector2 wallTargetPos = wallTarget.Position;
|
||||
if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.Position; }
|
||||
if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.DrawPosition; }
|
||||
wallTargetPos.Y = -wallTargetPos.Y;
|
||||
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false);
|
||||
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
|
||||
}
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity}", GUIStyle.Red, Color.Black);
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"{targetValue.FormatZeroDecimal()} (M: {SelectedTargetMemory?.Priority.FormatZeroDecimal()}, P: {SelectedTargetingParams?.Priority.FormatZeroDecimal()})", GUIStyle.Red, Color.Black);
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"{targetValue.FormatZeroDecimal()} (M: {CurrentTargetMemory?.Priority.FormatZeroDecimal()}, P: {CurrentTargetingParams?.Priority.FormatZeroDecimal()})", GUIStyle.Red, Color.Black);
|
||||
}
|
||||
|
||||
/*GUIStyle.Font.DrawString(spriteBatch, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, GUIStyle.Red);
|
||||
@@ -73,7 +73,7 @@ namespace Barotrauma
|
||||
}
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black);
|
||||
|
||||
if (State == AIState.Attack && selectedTargetingParams != null && selectedTargetingParams.AttackPattern == AttackPattern.Circle)
|
||||
if (State == AIState.Attack && currentTargetingParams != null && currentTargetingParams.AttackPattern == AttackPattern.Circle)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 100.0f, CirclePhase.ToString(), stateColor, Color.Black);
|
||||
}
|
||||
@@ -134,8 +134,8 @@ namespace Barotrauma
|
||||
//GUI.DrawLine(spriteBatch, pos, ConvertUnits.ToDisplayUnits(steeringManager.AvoidLookAheadPos.X, -steeringManager.AvoidLookAheadPos.Y), Color.Orange, width: 4);
|
||||
}
|
||||
}
|
||||
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 4);
|
||||
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2);
|
||||
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using FarseerPhysics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -11,7 +12,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Character == Character.Controlled) { return; }
|
||||
if (!DebugAI) { return; }
|
||||
Vector2 pos = Character.WorldPosition;
|
||||
Vector2 pos = Character.DrawPosition;
|
||||
pos.Y = -pos.Y;
|
||||
Vector2 textOffset = new Vector2(-40, -160);
|
||||
textOffset.Y -= Math.Max(ObjectiveManager.CurrentOrders.Count - 1, 0) * 20;
|
||||
@@ -63,6 +64,25 @@ namespace Barotrauma
|
||||
stringDrawPos += new Vector2(0, 20);
|
||||
GUI.DrawString(spriteBatch, stringDrawPos, $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
if (currentObjective is AIObjectiveCombat
|
||||
{
|
||||
Weapon: Item weapon,
|
||||
BlockedPositions: List<Vector2> blockedPositions
|
||||
})
|
||||
{
|
||||
Vector2 weaponPos = weapon.DrawPosition;
|
||||
weaponPos.Y = -weaponPos.Y;
|
||||
foreach (Vector2 blockedPosition in blockedPositions)
|
||||
{
|
||||
Vector2 blockedPos = blockedPosition;
|
||||
if (Character.Submarine != null)
|
||||
{
|
||||
blockedPos += Character.Submarine.DrawPosition;
|
||||
}
|
||||
blockedPos.Y = -blockedPos.Y;
|
||||
GUI.DrawLine(spriteBatch, weaponPos, blockedPos, Color.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 objectiveStringDrawPos = stringDrawPos + new Vector2(120, 40);
|
||||
|
||||
@@ -23,13 +23,13 @@ namespace Barotrauma
|
||||
|
||||
partial void UpdateNetPlayerPositionProjSpecific(float deltaTime, float lowestSubPos)
|
||||
{
|
||||
if (character != GameMain.Client.Character || !character.CanMove)
|
||||
if (character != GameMain.Client.Character)
|
||||
{
|
||||
//remove states without a timestamp (there may still be ID-based states
|
||||
//in the list when the controlled character switches to timestamp-based interpolation)
|
||||
character.MemState.RemoveAll(m => m.Timestamp == 0.0f);
|
||||
|
||||
//use simple interpolation for other players' characters and characters that can't move
|
||||
//use simple interpolation for other players' characters
|
||||
if (character.MemState.Count > 0)
|
||||
{
|
||||
CharacterStateInfo serverPos = character.MemState.Last();
|
||||
@@ -93,6 +93,9 @@ namespace Barotrauma
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
}
|
||||
|
||||
character.AnimController.IgnorePlatforms = character.MemState[0].IgnorePlatforms;
|
||||
character.AnimController.overrideTargetMovement = character.MemState[0].TargetMovement;
|
||||
|
||||
Vector2 newVelocity = Collider.LinearVelocity;
|
||||
Vector2 newPosition = Collider.SimPosition;
|
||||
float newRotation = Collider.Rotation;
|
||||
@@ -103,16 +106,17 @@ namespace Barotrauma
|
||||
{
|
||||
newVelocity = newVelocity.ClampLength(100.0f);
|
||||
if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; }
|
||||
overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero;
|
||||
Collider.LinearVelocity = newVelocity;
|
||||
Collider.AngularVelocity = newAngularVelocity;
|
||||
}
|
||||
|
||||
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
|
||||
float errorTolerance = character.CanMove && (!character.IsRagdolled || character.AnimController.IsHangingWithRope) ? 0.01f : 0.2f;
|
||||
float errorTolerance =
|
||||
ColliderControlsMovement && (!character.IsRagdolled || character.AnimController.IsHangingWithRope) ? 0.01f : 0.2f;
|
||||
if (distSqrd > errorTolerance)
|
||||
{
|
||||
if (distSqrd > 10.0f || !character.CanMove)
|
||||
character.AnimController.BodyInRest = false;
|
||||
if (distSqrd > 10.0f)
|
||||
{
|
||||
Collider.TargetRotation = newRotation;
|
||||
if (distSqrd > 10.0f)
|
||||
@@ -126,30 +130,35 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false);
|
||||
//make sure ragdoll isn't stuck at the wrong side of a platform if the movement is controlled by the ragdoll, and the ragdoll has come to rest server-side
|
||||
if (!ColliderControlsMovement && newVelocity.LengthSquared() < 0.01f) { TryPlatformCorrection(newPosition); }
|
||||
}
|
||||
else
|
||||
else if (ColliderControlsMovement)
|
||||
{
|
||||
Collider.TargetRotation = newRotation;
|
||||
Collider.TargetPosition = newPosition;
|
||||
Collider.MoveToTargetPosition(true);
|
||||
}
|
||||
}
|
||||
|
||||
//immobilized characters can't correct their position using AnimController movement
|
||||
// -> we need to correct it manually
|
||||
if (!character.CanMove)
|
||||
{
|
||||
float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition);
|
||||
float mainLimbErrorTolerance = 0.1f;
|
||||
//if the main limb is roughly at the correct position and the collider isn't moving (much at least),
|
||||
//don't attempt to correct the position.
|
||||
if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f)
|
||||
else
|
||||
{
|
||||
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
|
||||
MainLimb.PullJointEnabled = true;
|
||||
float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, newPosition);
|
||||
float mainLimbErrorTolerance = character == GameMain.Client.Character ? 0.25f : 0.1f;
|
||||
MainLimb.body.LinearVelocity = newVelocity;
|
||||
//if the main limb is roughly at the correct position and the collider isn't moving (much at least),
|
||||
//don't attempt to correct the position.
|
||||
if (mainLimbDistSqrd > mainLimbErrorTolerance)
|
||||
{
|
||||
MainLimb.PullJointWorldAnchorB = newPosition;
|
||||
MainLimb.PullJointEnabled = true;
|
||||
if (!ColliderControlsMovement && newVelocity.LengthSquared() < 0.01f) { TryPlatformCorrection(newPosition); }
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!ColliderControlsMovement)
|
||||
{
|
||||
//correct velocity regardless of the positional error
|
||||
MainLimb.body.LinearVelocity = newVelocity;
|
||||
}
|
||||
}
|
||||
character.MemLocalState.Clear();
|
||||
}
|
||||
@@ -179,12 +188,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (character.MemState.Count < 1) return;
|
||||
if (character.MemState.Count < 1) { return; }
|
||||
|
||||
overrideTargetMovement = Vector2.Zero;
|
||||
overrideTargetMovement = null;
|
||||
|
||||
CharacterStateInfo serverPos = character.MemState.Last();
|
||||
|
||||
Collider.LastServerState = serverPos;
|
||||
|
||||
if (!character.isSynced)
|
||||
{
|
||||
SetPosition(serverPos.Position, lerp: false);
|
||||
@@ -282,19 +293,66 @@ namespace Barotrauma
|
||||
}
|
||||
else if (errorMagnitude > 0.01f)
|
||||
{
|
||||
Collider.TargetPosition = Collider.SimPosition + positionError;
|
||||
Collider.TargetRotation = Collider.Rotation + rotationError;
|
||||
Collider.MoveToTargetPosition(lerp: true);
|
||||
if (ColliderControlsMovement)
|
||||
{
|
||||
Collider.TargetPosition = Collider.SimPosition + positionError;
|
||||
Collider.TargetRotation = Collider.Rotation + rotationError;
|
||||
Collider.MoveToTargetPosition(lerp: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
float mainLimbErrorTolerance = character == GameMain.Client.Character ? 0.25f : 0.1f;
|
||||
//if the main limb is roughly at the correct position and the collider isn't moving (much at least),
|
||||
//don't attempt to correct the position.
|
||||
if (errorMagnitude > mainLimbErrorTolerance)
|
||||
{
|
||||
MainLimb.PullJointWorldAnchorB = MainLimb.SimPosition + positionError;
|
||||
MainLimb.PullJointEnabled = true;
|
||||
if (serverPos.LinearVelocity.LengthSquared() < 0.01f) { TryPlatformCorrection(MainLimb.SimPosition + positionError); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (character.MemLocalState.Count > 120) character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120);
|
||||
if (character.MemLocalState.Count > 120) { character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120); }
|
||||
character.MemState.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to correct the ragdoll to the correct side of a platform if the server position is above the platform and some of the ragdoll's limbs below it client-side, or vice versa.
|
||||
/// </summary>
|
||||
private void TryPlatformCorrection(Vector2 serverPos)
|
||||
{
|
||||
float highestPos = limbs.Where(static l => !l.IsSevered).Max(static l => l.SimPosition.Y);
|
||||
highestPos = Math.Max(serverPos.Y, highestPos);
|
||||
float lowestPos = limbs.Where(static l => !l.IsSevered).Min(static l => l.SimPosition.Y);
|
||||
lowestPos = Math.Min(serverPos.Y, lowestPos);
|
||||
|
||||
var platform = Submarine.PickBody(new Vector2(serverPos.X, highestPos), new Vector2(serverPos.X, lowestPos), collisionCategory: Physics.CollisionPlatform, allowInsideFixture: true);
|
||||
if (platform == null) { return; }
|
||||
|
||||
int serverDir = Math.Sign(serverPos.Y - platform.Position.Y);
|
||||
foreach (var limb in limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
int limbDir = Math.Sign(limb.SimPosition.Y - platform.Position.Y);
|
||||
|
||||
const float Margin = 0.01f;
|
||||
|
||||
if (limbDir != serverDir)
|
||||
{
|
||||
limb.body.SetTransformIgnoreContacts(
|
||||
new Vector2(
|
||||
limb.SimPosition.X,
|
||||
serverDir > 0 ? Math.Max(serverPos.Y + Margin + limb.body.GetMaxExtent(), limb.SimPosition.Y) : Math.Min(serverPos.Y - Margin - limb.body.GetMaxExtent(), limb.SimPosition.Y)),
|
||||
limb.Rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void ImpactProjSpecific(float impact, Body body)
|
||||
{
|
||||
float volume = MathHelper.Clamp(impact - 3.0f, 0.5f, 1.0f);
|
||||
@@ -563,15 +621,19 @@ namespace Barotrauma
|
||||
|
||||
void AdjustDepthOffset(Item item)
|
||||
{
|
||||
if (item?.GetComponent<Controller>() is { ControlCharacterPose: true, UserInCorrectPosition: true } controller && controller.User == character)
|
||||
if (item == null) { return; }
|
||||
foreach (var controller in item.GetComponents<Controller>())
|
||||
{
|
||||
if (controller.Item.SpriteDepth <= maxDepth || controller.DrawUserBehind)
|
||||
if (controller is { ControlCharacterPose: true, UserInCorrectPosition: true } && controller.User == character)
|
||||
{
|
||||
depthOffset = Math.Max(controller.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
depthOffset = Math.Max(controller.Item.GetDrawDepth() - 0.0001f - maxDepth, 0.0f);
|
||||
if (controller.Item.SpriteDepth <= maxDepth || controller.DrawUserBehind)
|
||||
{
|
||||
depthOffset = Math.Max(controller.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
depthOffset = Math.Max(controller.Item.GetDrawDepth() - 0.0001f - maxDepth, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Barotrauma
|
||||
|
||||
if (sound != null)
|
||||
{
|
||||
SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling, freqMult: sound.GetRandomFrequencyMultiplier());
|
||||
SoundPlayer.PlaySound(sound, worldPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -27,7 +28,8 @@ namespace Barotrauma
|
||||
|
||||
protected float lastRecvPositionUpdateTime;
|
||||
|
||||
private float hudInfoHeight = 100.0f;
|
||||
private const float DefaultHudInfoHeight = 78.0f;
|
||||
private float hudInfoHeight = DefaultHudInfoHeight;
|
||||
|
||||
private List<CharacterSound> sounds;
|
||||
|
||||
@@ -249,7 +251,9 @@ namespace Barotrauma
|
||||
public Vector2 Position;
|
||||
public Vector2 DrawPosition;
|
||||
public float MoveUpAmount;
|
||||
public readonly string Text;
|
||||
public readonly RichString Text;
|
||||
public ImmutableArray<RichTextData>? RichTextData { get; private set; }
|
||||
|
||||
public readonly Character Character;
|
||||
public readonly Submarine Submarine;
|
||||
public readonly Vector2 TextSize;
|
||||
@@ -259,8 +263,10 @@ namespace Barotrauma
|
||||
|
||||
public SpeechBubble(Character character, float lifeTime, Color color, string text = "")
|
||||
{
|
||||
Text = ToolBox.WrapText(text, GUI.IntScale(300), GUIStyle.SmallFont.GetFontForStr(text));
|
||||
var richStr = RichString.Rich(text);
|
||||
Text = ToolBox.WrapText(richStr.SanitizedValue, GUI.IntScale(300), GUIStyle.SmallFont.GetFontForStr(text));
|
||||
TextSize = GUIStyle.SmallFont.MeasureString(Text);
|
||||
RichTextData = richStr.RichTextData;
|
||||
|
||||
Character = character;
|
||||
Position = GetDesiredPosition();
|
||||
@@ -321,7 +327,6 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
|
||||
{
|
||||
|
||||
if (DisableControls || GUI.InputBlockingMenuOpen)
|
||||
{
|
||||
foreach (Key key in keys)
|
||||
@@ -329,7 +334,7 @@ namespace Barotrauma
|
||||
if (key == null) { continue; }
|
||||
key.Reset();
|
||||
}
|
||||
if (GUI.InputBlockingMenuOpen)
|
||||
if (GUI.InputBlockingMenuOpen || ConversationAction.IsDialogOpen)
|
||||
{
|
||||
cursorPosition =
|
||||
Position + PlayerInput.MouseSpeed.ClampLength(10.0f); //apply a little bit of movement to the cursor pos to prevent AFK kicking
|
||||
@@ -416,6 +421,11 @@ namespace Barotrauma
|
||||
|
||||
UpdateLocalCursor(cam);
|
||||
|
||||
if (IsKeyHit(InputType.ToggleRun))
|
||||
{
|
||||
ToggleRun = !ToggleRun;
|
||||
}
|
||||
|
||||
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
|
||||
if (GUI.PauseMenuOpen)
|
||||
{
|
||||
@@ -471,7 +481,7 @@ namespace Barotrauma
|
||||
if (!GUI.InputBlockingMenuOpen)
|
||||
{
|
||||
if (SelectedItem != null &&
|
||||
(SelectedItem.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)) ||
|
||||
(SelectedItem.ActiveHUDs.Any(ic => ic.GuiFrame != null && ic.CloseByClickingOutsideGUIFrame && HUD.CloseHUD(ic.GuiFrame.Rect)) ||
|
||||
((ViewTarget as Item)?.Prefab.FocusOnSelected ?? false) && PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)))
|
||||
{
|
||||
if (GameMain.Client != null)
|
||||
@@ -543,7 +553,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (attackResult.Damage <= 1.0f) { return; }
|
||||
}
|
||||
PlaySound(CharacterSound.SoundType.Damage, maxInterval: 2);
|
||||
if (AIState != AIState.PlayDead)
|
||||
{
|
||||
PlaySound(CharacterSound.SoundType.Damage, maxInterval: 2);
|
||||
}
|
||||
}
|
||||
|
||||
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log)
|
||||
@@ -588,7 +601,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
sounds.ForEach(s => s.Sound?.Dispose());
|
||||
sounds.Clear();
|
||||
|
||||
if (GameMain.GameSession?.CrewManager != null &&
|
||||
@@ -814,9 +826,12 @@ namespace Barotrauma
|
||||
PlaySound(CharacterSound.SoundType.Idle);
|
||||
}
|
||||
break;
|
||||
case AIState.PlayDead:
|
||||
case AIState.Freeze:
|
||||
case AIState.Hiding:
|
||||
break;
|
||||
default:
|
||||
var petBehavior = enemyAI.PetBehavior;
|
||||
if (petBehavior != null &&
|
||||
if (enemyAI.PetBehavior is PetBehavior petBehavior &&
|
||||
(petBehavior.Happiness < petBehavior.UnhappyThreshold || petBehavior.Hunger > petBehavior.HungryThreshold))
|
||||
{
|
||||
PlaySound(CharacterSound.SoundType.Unhappy);
|
||||
@@ -948,7 +963,9 @@ namespace Barotrauma
|
||||
Controlled != this &&
|
||||
Submarine != null &&
|
||||
Controlled.Submarine == Submarine &&
|
||||
GameSettings.CurrentConfig.Graphics.LosMode != LosMode.None)
|
||||
GameSettings.CurrentConfig.Graphics.LosMode != LosMode.None &&
|
||||
//less restrictions on name tag visibility in PvP mode (always show them if the character is visible)
|
||||
GameMain.GameSession?.GameMode is not PvPMode)
|
||||
{
|
||||
float yPos = Controlled.AnimController.FloorY - 1.5f;
|
||||
|
||||
@@ -965,15 +982,16 @@ namespace Barotrauma
|
||||
Vector2 pos = DrawPosition;
|
||||
pos.Y += hudInfoHeight;
|
||||
|
||||
if (CurrentHull != null && DrawPosition.Y > CurrentHull.WorldRect.Y - 130.0f)
|
||||
float paddingBelowCeiling = 30.0f;
|
||||
if (CurrentHull != null && DrawPosition.Y + DefaultHudInfoHeight > CurrentHull.WorldRect.Y - paddingBelowCeiling)
|
||||
{
|
||||
float lowerAmount = DrawPosition.Y - (CurrentHull.WorldRect.Y - 130.0f);
|
||||
hudInfoHeight = MathHelper.Lerp(hudInfoHeight, 100.0f - lowerAmount, 0.1f);
|
||||
float lowerAmount = (DrawPosition.Y + DefaultHudInfoHeight) - (CurrentHull.WorldRect.Y - paddingBelowCeiling);
|
||||
hudInfoHeight = MathHelper.Lerp(hudInfoHeight, DefaultHudInfoHeight - lowerAmount, 0.1f);
|
||||
hudInfoHeight = Math.Max(hudInfoHeight, 20.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
hudInfoHeight = MathHelper.Lerp(hudInfoHeight, 100.0f, 0.1f);
|
||||
hudInfoHeight = MathHelper.Lerp(hudInfoHeight, DefaultHudInfoHeight, 0.1f);
|
||||
}
|
||||
|
||||
pos.Y = -pos.Y;
|
||||
@@ -1013,6 +1031,8 @@ namespace Barotrauma
|
||||
CampaignInteractionType == CampaignMode.InteractionType.None ?
|
||||
MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f) :
|
||||
1.0f;
|
||||
//full name tag visibility in PvP mode to make it easier to tell who's an enemy
|
||||
float nameTextAlpha = GameMain.GameSession?.GameMode is PvPMode ? 1.0f : hudInfoAlpha;
|
||||
|
||||
if (!GUI.DisableCharacterNames && hudInfoVisible &&
|
||||
(controlled == null || this != controlled.FocusedCharacter || IsPet) && cam.Zoom > 0.4f)
|
||||
@@ -1030,7 +1050,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
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;
|
||||
Vector2 namePos = new Vector2(pos.X, pos.Y - 5.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
|
||||
Color nameColor = GetNameColor();
|
||||
|
||||
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
@@ -1057,7 +1077,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
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);
|
||||
GUIStyle.Font.DrawString(spriteBatch, name, namePos, nameColor * nameTextAlpha, 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);
|
||||
@@ -1068,15 +1088,18 @@ namespace Barotrauma
|
||||
if (petBehavior != null && !IsDead && !IsUnconscious)
|
||||
{
|
||||
var petStatus = petBehavior.GetCurrentStatusIndicatorType();
|
||||
var iconStyle = GUIStyle.GetComponentStyle("PetIcon." + petStatus);
|
||||
if (iconStyle != null)
|
||||
if (petStatus != PetBehavior.StatusIndicatorType.None)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1100,7 +1123,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (Params.ShowHealthBar && CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
|
||||
if (Params.ShowHealthBar && CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible && AIState != AIState.PlayDead && AIState != AIState.Hiding)
|
||||
{
|
||||
hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f));
|
||||
|
||||
@@ -1175,7 +1198,7 @@ namespace Barotrauma
|
||||
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);
|
||||
GUI.DrawStringWithColors(spriteBatch, iconPos - bubble.TextSize / 2, bubble.Text.SanitizedValue, bubble.Color * Math.Min(bubble.LifeTime, 1.0f) * alpha, bubble.RichTextData, font: GUIStyle.SmallFont);
|
||||
}
|
||||
spriteBatch.End();
|
||||
}
|
||||
@@ -1233,7 +1256,7 @@ namespace Barotrauma
|
||||
public Color GetNameColor()
|
||||
{
|
||||
CharacterTeamType team = teamID;
|
||||
if (Info?.IsDisguisedAsAnother != null)
|
||||
if (Info is { IsDisguisedAsAnother: true })
|
||||
{
|
||||
var idCard = Inventory.GetItemInLimbSlot(InvSlotType.Card)?.GetComponent<IdCard>();
|
||||
if (idCard != null)
|
||||
@@ -1249,18 +1272,22 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
CharacterTeamType myTeam =
|
||||
Controlled?.TeamID ??
|
||||
GameMain.Client?.MyClient?.TeamID ??
|
||||
CharacterTeamType.Team1;
|
||||
|
||||
Color nameColor = GUIStyle.TextColorNormal;
|
||||
if (Controlled != null && team != Controlled.TeamID)
|
||||
if (TeamID == CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
if (TeamID == CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
nameColor = UniqueNameColor ?? Color.SkyBlue;
|
||||
}
|
||||
else
|
||||
{
|
||||
nameColor = GUIStyle.Red;
|
||||
}
|
||||
nameColor = UniqueNameColor ?? Color.SkyBlue;
|
||||
}
|
||||
else if (team != myTeam)
|
||||
{
|
||||
//opposing team is red when controlling a character
|
||||
nameColor = GUIStyle.Red;
|
||||
}
|
||||
|
||||
return nameColor;
|
||||
}
|
||||
|
||||
@@ -1417,7 +1444,10 @@ namespace Barotrauma
|
||||
|
||||
partial void OnTalentGiven(TalentPrefab talentPrefab)
|
||||
{
|
||||
AddMessage(TextManager.Get("talentname." + talentPrefab.Identifier).Value, GUIStyle.Yellow, playSound: this == Controlled);
|
||||
if (!talentPrefab.IsHiddenExtraTalent)
|
||||
{
|
||||
AddMessage(TextManager.Get("talentname." + talentPrefab.Identifier).Value, GUIStyle.Yellow, playSound: this == Controlled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,17 +11,15 @@ namespace Barotrauma
|
||||
{
|
||||
partial class CharacterHUD
|
||||
{
|
||||
const float BossHealthBarDuration = 120.0f;
|
||||
|
||||
abstract class BossProgressBar
|
||||
abstract class ProgressBar
|
||||
{
|
||||
public float FadeTimer;
|
||||
|
||||
public readonly GUIComponent TopContainer;
|
||||
public readonly GUIComponent SideContainer;
|
||||
|
||||
public readonly GUIProgressBar TopHealthBar;
|
||||
public readonly GUIProgressBar SideHealthBar;
|
||||
public readonly GUIProgressBar TopBar;
|
||||
public readonly GUIProgressBar SideBar;
|
||||
|
||||
public abstract bool Completed { get; }
|
||||
|
||||
@@ -33,9 +31,9 @@ namespace Barotrauma
|
||||
|
||||
public abstract Color Color { get; }
|
||||
|
||||
public BossProgressBar(LocalizedString label)
|
||||
public ProgressBar(LocalizedString label, float fadeTimer = 120.0f)
|
||||
{
|
||||
FadeTimer = BossHealthBarDuration;
|
||||
FadeTimer = fadeTimer;
|
||||
|
||||
TopContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.18f, 0.03f), HUDFrame.RectTransform, Anchor.TopCenter)
|
||||
{
|
||||
@@ -43,25 +41,25 @@ namespace Barotrauma
|
||||
RelativeOffset = new Vector2(0.0f, 0.01f)
|
||||
}, isHorizontal: false, childAnchor: Anchor.TopCenter);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), TopContainer.RectTransform), label, textAlignment: Alignment.Center, textColor: GUIStyle.Red);
|
||||
TopHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.6f), TopContainer.RectTransform)
|
||||
TopBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.6f), TopContainer.RectTransform)
|
||||
{
|
||||
MinSize = new Point(100, HUDLayoutSettings.HealthBarArea.Size.Y)
|
||||
}, barSize: 0.0f, style: "CharacterHealthBarCentered")
|
||||
{
|
||||
Color = GUIStyle.Red
|
||||
};
|
||||
CreateNumberText(TopHealthBar);
|
||||
CreateNumberText(TopBar);
|
||||
|
||||
SideContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), bossHealthContainer.RectTransform)
|
||||
{
|
||||
MinSize = new Point(80, 60)
|
||||
}, isHorizontal: false, childAnchor: Anchor.TopRight);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), SideContainer.RectTransform), label, textAlignment: Alignment.CenterRight, textColor: GUIStyle.Red);
|
||||
SideHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.7f), SideContainer.RectTransform), barSize: 0.0f, style: "CharacterHealthBar")
|
||||
SideBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.7f), SideContainer.RectTransform), barSize: 0.0f, style: "CharacterHealthBar")
|
||||
{
|
||||
Color = GUIStyle.Red
|
||||
};
|
||||
CreateNumberText(SideHealthBar);
|
||||
CreateNumberText(SideBar);
|
||||
|
||||
TopContainer.Visible = SideContainer.Visible = false;
|
||||
TopContainer.CanBeFocused = false;
|
||||
@@ -88,7 +86,7 @@ namespace Barotrauma
|
||||
public abstract bool IsDuplicate(object targetObject);
|
||||
}
|
||||
|
||||
class BossHealthBar : BossProgressBar
|
||||
class HealthBar : ProgressBar
|
||||
{
|
||||
public readonly Character Character;
|
||||
|
||||
@@ -104,7 +102,7 @@ namespace Barotrauma
|
||||
|
||||
public override string NumberToDisplay => string.Empty;
|
||||
|
||||
public BossHealthBar(Character character) : base(character.DisplayName)
|
||||
public HealthBar(Character character) : base(character.DisplayName)
|
||||
{
|
||||
Character = character;
|
||||
}
|
||||
@@ -115,7 +113,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
class MissionProgressBar : BossProgressBar
|
||||
class MissionProgressBar : ProgressBar
|
||||
{
|
||||
public readonly Mission Mission;
|
||||
|
||||
@@ -125,13 +123,13 @@ namespace Barotrauma
|
||||
|
||||
public override bool Interrupted => Mission.Failed || GameMain.GameSession?.Missions == null || !GameMain.GameSession.Missions.Contains(Mission);
|
||||
|
||||
public override Color Color => GUIStyle.Red;
|
||||
public override Color Color => Mission.Prefab.ProgressBarColor;
|
||||
|
||||
public override string NumberToDisplay => Mission.Prefab.ShowProgressInNumbers ?
|
||||
$"{Mission.State}/{Mission.Prefab.MaxProgressState}" :
|
||||
string.Empty;
|
||||
|
||||
public MissionProgressBar(Mission mission) : base(mission.Prefab.ProgressBarLabel)
|
||||
public MissionProgressBar(Mission mission) : base(mission.Prefab.ProgressBarLabel, fadeTimer: float.PositiveInfinity)
|
||||
{
|
||||
Mission = mission;
|
||||
}
|
||||
@@ -150,7 +148,7 @@ namespace Barotrauma
|
||||
private static readonly List<Item> brokenItems = new List<Item>();
|
||||
private static float brokenItemsCheckTimer;
|
||||
|
||||
private static readonly List<BossProgressBar> bossProgressBars = new List<BossProgressBar>();
|
||||
private static readonly List<ProgressBar> bossProgressBars = new List<ProgressBar>();
|
||||
|
||||
private static readonly Dictionary<Identifier, LocalizedString> cachedHudTexts = new Dictionary<Identifier, LocalizedString>();
|
||||
private static LanguageIdentifier cachedHudTextLanguage = LanguageIdentifier.None;
|
||||
@@ -394,6 +392,7 @@ namespace Barotrauma
|
||||
foreach (var target in mission.HudIconTargets)
|
||||
{
|
||||
if (target.Submarine != character.Submarine) { continue; }
|
||||
if (target.Removed) { continue; }
|
||||
float alpha = GetDistanceBasedIconAlpha(target, maxDistance: mission.Prefab.HudIconMaxDistance);
|
||||
if (alpha <= 0.0f) { continue; }
|
||||
GUI.DrawIndicator(spriteBatch, target.DrawPosition, cam, 100.0f, mission.Prefab.HudIcon, mission.Prefab.HudIconColor * alpha);
|
||||
@@ -564,7 +563,7 @@ namespace Barotrauma
|
||||
float alpha = MathHelper.Lerp(0.3f, 1.0f, distFactor);
|
||||
GUI.DrawIndicator(
|
||||
spriteBatch,
|
||||
entity.WorldPosition,
|
||||
entity.DrawPosition,
|
||||
cam,
|
||||
visibleRange,
|
||||
style.GetDefaultSprite(),
|
||||
@@ -592,7 +591,7 @@ namespace Barotrauma
|
||||
if (Vector2.DistanceSquared(character.Position, item.Position) > 500f * 500f) { continue; }
|
||||
var body = Submarine.CheckVisibility(character.SimPosition, item.SimPosition, ignoreLevel: true);
|
||||
if (body != null && body.UserData as Item != item) { continue; }
|
||||
GUI.DrawIndicator(spriteBatch, item.WorldPosition + new Vector2(0f, item.RectHeight * 0.65f), cam, new Range<float>(-100f, 500.0f), item.IconStyle.GetDefaultSprite(), item.IconStyle.Color, createOffset: false);
|
||||
GUI.DrawIndicator(spriteBatch, item.DrawPosition + new Vector2(0f, item.RectHeight * 0.65f), cam, new Range<float>(-100f, 500.0f), item.IconStyle.GetDefaultSprite(), item.IconStyle.Color, createOffset: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -752,7 +751,8 @@ namespace Barotrauma
|
||||
}
|
||||
textPos.X += 10.0f * GUI.Scale;
|
||||
|
||||
if (!character.FocusedCharacter.IsIncapacitated && character.FocusedCharacter.IsPet)
|
||||
if (!character.FocusedCharacter.IsIncapacitated && character.FocusedCharacter.IsPet &&
|
||||
character.FocusedCharacter.AIController is EnemyAIController enemyAI && enemyAI.PetBehavior.CanPlayWith(character))
|
||||
{
|
||||
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("PlayHint", InputType.Use),
|
||||
GUIStyle.Green, Color.Black, 2, GUIStyle.SmallFont);
|
||||
@@ -773,7 +773,7 @@ namespace Barotrauma
|
||||
GUIStyle.Green, Color.Black, 2, GUIStyle.SmallFont);
|
||||
textPos.Y += textSize.Y;
|
||||
}
|
||||
if (!character.FocusedCharacter.CustomInteractHUDText.IsNullOrEmpty() && character.FocusedCharacter.AllowCustomInteract)
|
||||
if (character.FocusedCharacter.ShouldShowCustomInteractText)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.CustomInteractHUDText, GUIStyle.Green, Color.Black, 2, GUIStyle.SmallFont);
|
||||
textPos.Y += textSize.Y;
|
||||
@@ -784,7 +784,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character == null || character.IsDead || character.Removed) { return; }
|
||||
if (bossProgressBars.Any(b => b.IsDuplicate(character))) { return; }
|
||||
AddBossProgressBar(new BossHealthBar(character));
|
||||
AddBossProgressBar(new HealthBar(character));
|
||||
}
|
||||
|
||||
public static void ShowMissionProgressBar(Mission mission)
|
||||
@@ -803,26 +803,26 @@ namespace Barotrauma
|
||||
bossProgressBars.Clear();
|
||||
}
|
||||
|
||||
private static void RemoveBossProgressBar(BossProgressBar progressBar)
|
||||
private static void RemoveBossProgressBar(ProgressBar progressBar)
|
||||
{
|
||||
progressBar.SideContainer.Parent?.RemoveChild(progressBar.SideContainer);
|
||||
progressBar.TopContainer.Parent?.RemoveChild(progressBar.TopContainer);
|
||||
bossProgressBars.Remove(progressBar);
|
||||
}
|
||||
|
||||
private static void AddBossProgressBar(BossProgressBar progressBar)
|
||||
private static void AddBossProgressBar(ProgressBar progressBar)
|
||||
{
|
||||
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
|
||||
if (healthBarMode == EnemyHealthBarMode.HideAll)
|
||||
if (healthBarMode == EnemyHealthBarMode.HideAll && progressBar is not MissionProgressBar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (bossProgressBars.Count > 5)
|
||||
{
|
||||
BossProgressBar oldestHealthBar = bossProgressBars.First();
|
||||
ProgressBar oldestHealthBar = bossProgressBars.First();
|
||||
foreach (var bar in bossProgressBars)
|
||||
{
|
||||
if (bar.TopHealthBar.BarSize < oldestHealthBar.TopHealthBar.BarSize)
|
||||
if (bar.TopBar.BarSize < oldestHealthBar.TopBar.BarSize)
|
||||
{
|
||||
oldestHealthBar = bar;
|
||||
}
|
||||
@@ -850,7 +850,7 @@ namespace Barotrauma
|
||||
bossHealthBar.TopContainer.Visible = showTopBar;
|
||||
bossHealthBar.SideContainer.Visible = !bossHealthBar.TopContainer.Visible;
|
||||
|
||||
bossHealthBar.TopHealthBar.BarSize = bossHealthBar.SideHealthBar.BarSize = bossHealthBar.State;
|
||||
bossHealthBar.TopBar.BarSize = bossHealthBar.SideBar.BarSize = bossHealthBar.State;
|
||||
float alpha = Math.Min(bossHealthBar.FadeTimer, 1.0f);
|
||||
|
||||
if (bossHealthBar.TopContainer.Visible)
|
||||
@@ -862,7 +862,7 @@ namespace Barotrauma
|
||||
SetColor(bossHealthBar, bossHealthBar.SideContainer, alpha);
|
||||
}
|
||||
|
||||
static void SetColor(BossProgressBar bossHealthBar, GUIComponent container, float alpha)
|
||||
static void SetColor(ProgressBar bossHealthBar, GUIComponent container, float alpha)
|
||||
{
|
||||
foreach (var component in container.GetAllChildren())
|
||||
{
|
||||
@@ -887,7 +887,7 @@ namespace Barotrauma
|
||||
for (int i = bossProgressBars.Count - 1; i >= 0 ; i--)
|
||||
{
|
||||
var bossHealthBar = bossProgressBars[i];
|
||||
if (bossHealthBar.FadeTimer <= 0 || healthBarMode == EnemyHealthBarMode.HideAll)
|
||||
if (bossHealthBar.FadeTimer <= 0 || (healthBarMode == EnemyHealthBarMode.HideAll && bossHealthBar is not MissionProgressBar))
|
||||
{
|
||||
bossHealthBar.SideContainer.Parent?.RemoveChild(bossHealthBar.SideContainer);
|
||||
bossHealthBar.TopContainer.Parent?.RemoveChild(bossHealthBar.TopContainer);
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Barotrauma
|
||||
private static Sprite infoAreaPortraitBG;
|
||||
|
||||
public bool LastControlled;
|
||||
public int CrewListIndex { get; set; } = -1;
|
||||
|
||||
public int CrewListIndex { get; set; } = int.MaxValue; //default to the bottom of the list
|
||||
|
||||
private Sprite disguisedPortrait;
|
||||
private List<WearableSprite> disguisedAttachmentSprites;
|
||||
@@ -32,6 +33,8 @@ namespace Barotrauma
|
||||
private float tintHighlightThreshold;
|
||||
private float tintHighlightMultiplier;
|
||||
|
||||
public bool ShowTalentResetPopupOnOpen = true;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
infoAreaPortraitBG = GUIStyle.GetComponentStyle("InfoAreaPortraitBG")?.GetDefaultSprite();
|
||||
@@ -208,7 +211,7 @@ namespace Barotrauma
|
||||
return frame;
|
||||
}
|
||||
|
||||
partial void OnSkillChanged(Identifier skillIdentifier, float prevLevel, float newLevel)
|
||||
partial void OnSkillChanged(Identifier skillIdentifier, float prevLevel, float newLevel, bool forceNotification)
|
||||
{
|
||||
if (TeamID == CharacterTeamType.FriendlyNPC) { return; }
|
||||
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
|
||||
@@ -226,6 +229,18 @@ namespace Barotrauma
|
||||
specialIncrease ? GUIStyle.Orange : GUIStyle.Green,
|
||||
playSound: Character == Character.Controlled, skillIdentifier, increase);
|
||||
}
|
||||
else if (forceNotification)
|
||||
{
|
||||
float change = newLevel - prevLevel;
|
||||
if (Math.Abs(change) > 0.01f)
|
||||
{
|
||||
string sign = change > 0 ? "+" : "-";
|
||||
Character?.AddMessage(
|
||||
$"{sign}{Math.Round(change, 2)} {TextManager.Get("SkillName." + skillIdentifier).Value}",
|
||||
specialIncrease ? GUIStyle.Orange : GUIStyle.Green,
|
||||
playSound: Character == Character.Controlled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnExperienceChanged(int prevAmount, int newAmount)
|
||||
@@ -511,6 +526,17 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
origin = attachment.Sprite.Origin;
|
||||
if (spriteEffects.HasFlag(SpriteEffects.FlipHorizontally))
|
||||
{
|
||||
origin.X = attachment.Sprite.size.X - origin.X;
|
||||
}
|
||||
if (spriteEffects.HasFlag(SpriteEffects.FlipVertically))
|
||||
{
|
||||
origin.Y = attachment.Sprite.size.Y - origin.Y;
|
||||
}
|
||||
//the portrait's origin is forced to 0,0 (presumably for easier drawing on the UI?), see LoadHeadElement
|
||||
//we need to take that into account here and draw the attachment at where the origin of the "actual" head sprite would be
|
||||
drawPos += HeadSprite.Origin * scale;
|
||||
}
|
||||
float depth = attachment.Sprite.Depth;
|
||||
if (attachment.InheritLimbDepth)
|
||||
@@ -526,6 +552,8 @@ namespace Barotrauma
|
||||
string newName = inc.ReadString();
|
||||
string originalName = inc.ReadString();
|
||||
bool renamingEnabled = inc.ReadBoolean();
|
||||
BotStatus botStatus = (BotStatus)inc.ReadByte();
|
||||
int salary = inc.ReadInt32();
|
||||
int tagCount = inc.ReadByte();
|
||||
HashSet<Identifier> tagSet = new HashSet<Identifier>();
|
||||
for (int i = 0; i < tagCount; i++)
|
||||
@@ -576,6 +604,8 @@ namespace Barotrauma
|
||||
MinReputationToHire = (factionId, minReputationToHire),
|
||||
RenamingEnabled = renamingEnabled
|
||||
};
|
||||
ch.BotStatus = botStatus;
|
||||
ch.Salary = salary;
|
||||
ch.RecreateHead(tagSet.ToImmutableHashSet(), hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
|
||||
ch.Head.SkinColor = skinColor;
|
||||
ch.Head.HairColor = hairColor;
|
||||
@@ -586,6 +616,8 @@ namespace Barotrauma
|
||||
ch.ExperiencePoints = inc.ReadInt32();
|
||||
ch.AdditionalTalentPoints = inc.ReadRangedInteger(0, MaxAdditionalTalentPoints);
|
||||
ch.PermanentlyDead = inc.ReadBoolean();
|
||||
ch.TalentRefundPoints = inc.ReadInt32();
|
||||
ch.TalentResetCount = inc.ReadInt32();
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace Barotrauma
|
||||
SelectedCharacter,
|
||||
SelectedItem,
|
||||
SelectedSecondaryItem,
|
||||
AnimController.TargetMovement,
|
||||
AnimController.Anim);
|
||||
|
||||
memLocalState.Add(posInfo);
|
||||
@@ -56,7 +57,7 @@ namespace Barotrauma
|
||||
if (IsKeyDown(InputType.Right)) newInput |= InputNetFlags.Right;
|
||||
if (IsKeyDown(InputType.Up)) newInput |= InputNetFlags.Up;
|
||||
if (IsKeyDown(InputType.Down)) newInput |= InputNetFlags.Down;
|
||||
if (IsKeyDown(InputType.Run)) newInput |= InputNetFlags.Run;
|
||||
if (IsKeyDown(InputType.Run) || ToggleRun) newInput |= InputNetFlags.Run;
|
||||
if (IsKeyDown(InputType.Crouch)) newInput |= InputNetFlags.Crouch;
|
||||
if (IsKeyHit(InputType.Select)) newInput |= InputNetFlags.Select; //TODO: clean up the way this input is registered
|
||||
if (IsKeyHit(InputType.Deselect)) newInput |= InputNetFlags.Deselect;
|
||||
@@ -68,7 +69,7 @@ namespace Barotrauma
|
||||
if (IsKeyDown(InputType.Attack)) newInput |= InputNetFlags.Attack;
|
||||
if (IsKeyDown(InputType.Ragdoll)) newInput |= InputNetFlags.Ragdoll;
|
||||
|
||||
if (AnimController.TargetDir == Direction.Left) newInput |= InputNetFlags.FacingLeft;
|
||||
if (AnimController.Dir < 0) newInput |= InputNetFlags.FacingLeft;
|
||||
|
||||
Vector2 relativeCursorPos = cursorPosition - AimRefPosition;
|
||||
relativeCursorPos.Normalize();
|
||||
@@ -154,6 +155,9 @@ namespace Barotrauma
|
||||
case TreatmentEventData _:
|
||||
msg.WriteBoolean(AnimController.Anim == AnimController.Animation.CPR);
|
||||
break;
|
||||
case ConfirmRefundEventData _:
|
||||
//do nothing
|
||||
break;
|
||||
case CharacterStatusEventData _:
|
||||
//do nothing
|
||||
break;
|
||||
@@ -202,12 +206,16 @@ namespace Barotrauma
|
||||
keys[(int)InputType.Use].Held = useInput;
|
||||
keys[(int)InputType.Use].SetState(false, useInput);
|
||||
|
||||
bool crouching = msg.ReadBoolean();
|
||||
if (AnimController is HumanoidAnimController)
|
||||
{
|
||||
bool crouching = msg.ReadBoolean();
|
||||
keys[(int)InputType.Crouch].Held = crouching;
|
||||
keys[(int)InputType.Crouch].SetState(false, crouching);
|
||||
}
|
||||
else if (AnimController is FishAnimController fishAnim)
|
||||
{
|
||||
fishAnim.Reverse = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
bool attackInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Attack].Held = attackInput;
|
||||
@@ -255,17 +263,22 @@ namespace Barotrauma
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
|
||||
linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);
|
||||
|
||||
Vector2 targetMovement = new Vector2(
|
||||
msg.ReadRangedSingle(-Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12),
|
||||
msg.ReadRangedSingle(-Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12));
|
||||
targetMovement = NetConfig.Quantize(targetMovement, -Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12);
|
||||
|
||||
bool fixedRotation = msg.ReadBoolean();
|
||||
float? rotation = null;
|
||||
float? angularVelocity = null;
|
||||
if (!fixedRotation)
|
||||
{
|
||||
rotation = msg.ReadSingle();
|
||||
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
|
||||
angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
|
||||
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
|
||||
angularVelocity = msg.ReadSingle();
|
||||
}
|
||||
|
||||
bool ignorePlatforms = msg.ReadBoolean();
|
||||
|
||||
bool readStatus = msg.ReadBoolean();
|
||||
if (readStatus)
|
||||
{
|
||||
@@ -287,7 +300,7 @@ namespace Barotrauma
|
||||
{
|
||||
byte happiness = msg.ReadByte();
|
||||
byte hunger = msg.ReadByte();
|
||||
if ((AIController as EnemyAIController)?.PetBehavior is PetBehavior petBehavior)
|
||||
if (AIController is EnemyAIController { PetBehavior: PetBehavior petBehavior })
|
||||
{
|
||||
petBehavior.Happiness = (float)happiness / byte.MaxValue * petBehavior.MaxHappiness;
|
||||
petBehavior.Hunger = (float)hunger / byte.MaxValue * petBehavior.MaxHunger;
|
||||
@@ -303,13 +316,13 @@ namespace Barotrauma
|
||||
msg.ReadPadBits();
|
||||
|
||||
int index = 0;
|
||||
if (GameMain.Client.Character == this && CanMove)
|
||||
if (GameMain.Client.Character == this)
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
networkUpdateID,
|
||||
facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, selectedSecondaryItem, animation);
|
||||
selectedCharacter, selectedItem, selectedSecondaryItem, targetMovement, animation, ignorePlatforms);
|
||||
|
||||
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
|
||||
index++;
|
||||
@@ -321,7 +334,7 @@ namespace Barotrauma
|
||||
pos, rotation,
|
||||
linearVelocity, angularVelocity,
|
||||
sendingTime, facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, selectedSecondaryItem, animation);
|
||||
selectedCharacter, selectedItem, selectedSecondaryItem, targetMovement, animation, ignorePlatforms);
|
||||
|
||||
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
|
||||
index++;
|
||||
@@ -371,6 +384,9 @@ namespace Barotrauma
|
||||
GameMain.Client.HasSpawned = true;
|
||||
GameMain.Client.Character = this;
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
#if DEBUG
|
||||
GameMain.LightManager.LosEnabled = !GameMain.DevMode;
|
||||
#endif
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
GameMain.Client.WaitForNextRoundRespawn = null;
|
||||
}
|
||||
@@ -393,14 +409,15 @@ namespace Barotrauma
|
||||
break;
|
||||
case EventType.Status:
|
||||
ReadStatus(msg);
|
||||
GodMode = msg.ReadBoolean();
|
||||
break;
|
||||
case EventType.UpdateSkills:
|
||||
int skillCount = msg.ReadByte();
|
||||
for (int i = 0; i < skillCount; i++)
|
||||
Identifier skillIdentifier = msg.ReadIdentifier();
|
||||
if (!skillIdentifier.IsEmpty)
|
||||
{
|
||||
Identifier skillIdentifier = msg.ReadIdentifier();
|
||||
bool forceNotification = msg.ReadBoolean();
|
||||
float skillLevel = msg.ReadSingle();
|
||||
info?.SetSkillLevel(skillIdentifier, skillLevel);
|
||||
info?.SetSkillLevel(skillIdentifier, skillLevel, forceNotification: forceNotification);
|
||||
}
|
||||
break;
|
||||
case EventType.SetAttackTarget:
|
||||
@@ -512,7 +529,12 @@ namespace Barotrauma
|
||||
break;
|
||||
case EventType.UpdateExperience:
|
||||
int experienceAmount = msg.ReadInt32();
|
||||
info?.SetExperience(experienceAmount);
|
||||
int additionalTalentPoints = msg.ReadInt32();
|
||||
if (info != null)
|
||||
{
|
||||
info.SetExperience(experienceAmount);
|
||||
info.AdditionalTalentPoints = additionalTalentPoints;
|
||||
}
|
||||
break;
|
||||
case EventType.UpdateTalents:
|
||||
ushort talentCount = msg.ReadUInt16();
|
||||
@@ -527,6 +549,20 @@ namespace Barotrauma
|
||||
int moneyAmount = msg.ReadInt32();
|
||||
SetMoney(moneyAmount);
|
||||
break;
|
||||
case EventType.UpdateTalentRefundPoints:
|
||||
int refundPoints = msg.ReadInt32();
|
||||
if (info != null)
|
||||
{
|
||||
if (refundPoints > info.TalentRefundPoints)
|
||||
{
|
||||
info.ShowTalentResetPopupOnOpen = true;
|
||||
}
|
||||
info.TalentRefundPoints = refundPoints;
|
||||
}
|
||||
break;
|
||||
case EventType.ConfirmTalentRefund:
|
||||
Info?.RefundTalents();
|
||||
break;
|
||||
case EventType.UpdatePermanentStats:
|
||||
byte savedStatValueCount = msg.ReadByte();
|
||||
StatTypes statType = (StatTypes)msg.ReadByte();
|
||||
@@ -730,7 +766,7 @@ namespace Barotrauma
|
||||
|
||||
if (character.IsHuman && character.TeamID != CharacterTeamType.FriendlyNPC && character.TeamID != CharacterTeamType.None)
|
||||
{
|
||||
CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID);
|
||||
CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos(includeReserveBench: true).FirstOrDefault(c => c.ID == info.ID);
|
||||
GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo);
|
||||
if (character.isDead)
|
||||
{
|
||||
@@ -750,6 +786,9 @@ namespace Barotrauma
|
||||
if (!character.IsDead) { Controlled = character; }
|
||||
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
#if DEBUG
|
||||
GameMain.LightManager.LosEnabled = !GameMain.DevMode;
|
||||
#endif
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
|
||||
GameMain.NetLobbyScreen.CampaignCharacterDiscarded = false;
|
||||
@@ -817,6 +856,7 @@ namespace Barotrauma
|
||||
if (IsDead) { Revive(); }
|
||||
CharacterHealth.ClientRead(msg);
|
||||
}
|
||||
|
||||
byte severedLimbCount = msg.ReadByte();
|
||||
for (int i = 0; i < severedLimbCount; i++)
|
||||
{
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace Barotrauma
|
||||
fakeBrokenTimer -= deltaTime;
|
||||
if (fakeBrokenTimer > 0.0f) { return; }
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
foreach (Item item in Item.RepairableItems)
|
||||
{
|
||||
var repairable = item.GetComponent<Repairable>();
|
||||
if (repairable == null) { continue; }
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace Barotrauma
|
||||
|
||||
public static Sprite DamageOverlay => DamageOverlayPrefab.Prefabs.ActivePrefab.DamageOverlay;
|
||||
|
||||
|
||||
private Point screenResolution;
|
||||
|
||||
private float uiScale, inventoryScale;
|
||||
@@ -105,6 +104,12 @@ namespace Barotrauma
|
||||
private GUILayoutGroup treatmentLayout;
|
||||
private GUIListBox recommendedTreatmentContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Timer for updating visuals (limb tints and overlays) caused by the affliction
|
||||
/// </summary>
|
||||
private float updateVisualsTimer = Rand.Range(0.0f, UpdateVisualsInterval);
|
||||
const float UpdateVisualsInterval = 0.5f;
|
||||
|
||||
private float distortTimer;
|
||||
|
||||
// 0-1
|
||||
@@ -461,15 +466,17 @@ namespace Barotrauma
|
||||
private void OnAttacked(Character attacker, AttackResult attackResult)
|
||||
{
|
||||
if (Math.Abs(attackResult.Damage) < 0.01f) { return; }
|
||||
DamageOverlayTimer = MathHelper.Clamp(attackResult.Damage / MaxVitality, DamageOverlayTimer, 1.0f);
|
||||
if (healthShadowDelay <= 0.0f) { healthShadowDelay = 1.0f; }
|
||||
|
||||
if (ShowDamageOverlay)
|
||||
{
|
||||
DamageOverlayTimer = MathHelper.Clamp(attackResult.Damage / MaxVitality, DamageOverlayTimer, 1.0f);
|
||||
float additionalIntensity = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, 0.1f, attackResult.Damage / MaxVitality));
|
||||
damageIntensity = MathHelper.Clamp(damageIntensity + additionalIntensity, 0, 1);
|
||||
}
|
||||
|
||||
if (healthShadowDelay <= 0.0f) { healthShadowDelay = 1.0f; }
|
||||
if (healthBarPulsateTimer <= 0.0f) { healthBarPulsatePhase = 0.0f; }
|
||||
healthBarPulsateTimer = 1.0f;
|
||||
|
||||
float additionalIntensity = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, 0.1f, attackResult.Damage / MaxVitality));
|
||||
damageIntensity = MathHelper.Clamp(damageIntensity + additionalIntensity, 0, 1);
|
||||
|
||||
DisplayVitalityDelay = 0.5f;
|
||||
}
|
||||
|
||||
@@ -1036,6 +1043,12 @@ namespace Barotrauma
|
||||
var affliction = kvp.Key;
|
||||
affliction.Prefab.AfflictionOverlay?.Draw(spriteBatch, Vector2.Zero, Color.White * affliction.GetAfflictionOverlayMultiplier(), Vector2.Zero, 0.0f,
|
||||
new Vector2(GameMain.GraphicsWidth / DamageOverlay.size.X, GameMain.GraphicsHeight / DamageOverlay.size.Y));
|
||||
|
||||
var activeEffect = affliction.GetActiveEffect();
|
||||
if (activeEffect is { ThermalOverlayRange: > 0.0f })
|
||||
{
|
||||
StatusHUD.DrawThermalOverlay(spriteBatch, Character, Character, activeEffect.ThermalOverlayColor, activeEffect.ThermalOverlayRange, effectState: (float)Timing.TotalTimeUnpaused, showDeadCharacters: false);
|
||||
}
|
||||
}
|
||||
|
||||
float damageOverlayAlpha = DamageOverlayTimer;
|
||||
@@ -1130,6 +1143,8 @@ namespace Barotrauma
|
||||
if (!statusIconVisibleTime.ContainsKey(afflictionPrefab)) { statusIconVisibleTime.Add(afflictionPrefab, 0.0f); }
|
||||
statusIconVisibleTime[afflictionPrefab] += deltaTime;
|
||||
|
||||
Color color = GetAfflictionIconColor(afflictionPrefab, affliction);
|
||||
|
||||
var matchingIcon =
|
||||
afflictionIconContainer.GetChildByUserData(afflictionPrefab) ??
|
||||
hiddenAfflictionIconContainer.GetChildByUserData(afflictionPrefab);
|
||||
@@ -1138,9 +1153,13 @@ namespace Barotrauma
|
||||
matchingIcon = new GUIButton(new RectTransform(new Point(afflictionIconContainer.Rect.Height), afflictionIconContainer.RectTransform), style: null)
|
||||
{
|
||||
UserData = afflictionPrefab,
|
||||
ToolTip = affliction.Prefab.Name,
|
||||
ToolTip = $"‖color:{color.ToStringHex()}‖{affliction.Prefab.Name}‖color:end‖",
|
||||
CanBeSelected = false
|
||||
};
|
||||
if (affliction.Prefab.ShowDescriptionInTooltip)
|
||||
{
|
||||
matchingIcon.ToolTip = matchingIcon.ToolTip + "\n" + affliction.Prefab.GetDescription(affliction.Strength, AfflictionPrefab.Description.TargetType.Self);
|
||||
}
|
||||
if (affliction == pressureAffliction)
|
||||
{
|
||||
matchingIcon.ToolTip = TextManager.Get("PressureHUDWarning");
|
||||
@@ -1149,6 +1168,8 @@ namespace Barotrauma
|
||||
{
|
||||
matchingIcon.ToolTip = TextManager.Get("OxygenHUDWarning");
|
||||
}
|
||||
matchingIcon.ToolTip = RichString.Rich(matchingIcon.ToolTip);
|
||||
|
||||
new GUIImage(new RectTransform(Vector2.One, matchingIcon.RectTransform, Anchor.BottomCenter), afflictionPrefab.Icon, scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
@@ -1159,7 +1180,7 @@ namespace Barotrauma
|
||||
matchingIcon.RectTransform.Parent = hiddenAfflictionIconContainer.RectTransform;
|
||||
}
|
||||
var image = matchingIcon.GetChild<GUIImage>();
|
||||
image.Color = GetAfflictionIconColor(afflictionPrefab, affliction);
|
||||
image.Color = color;
|
||||
image.HoverColor = Color.Lerp(image.Color, Color.White, 0.5f);
|
||||
|
||||
if (affliction.DamagePerSecond > 1.0f && matchingIcon.FlashTimer <= 0.0f)
|
||||
@@ -1380,7 +1401,7 @@ namespace Barotrauma
|
||||
|
||||
recommendedTreatmentContainer.Content.ClearChildren();
|
||||
|
||||
float characterSkillLevel = Character.Controlled == null ? 0.0f : Character.Controlled.GetSkillLevel("medical");
|
||||
float characterSkillLevel = Character.Controlled == null ? 0.0f : Character.Controlled.GetSkillLevel(Tags.MedicalSkill);
|
||||
|
||||
//key = item identifier
|
||||
//float = suitability
|
||||
@@ -1388,7 +1409,9 @@ namespace Barotrauma
|
||||
GetSuitableTreatments(treatmentSuitability,
|
||||
user: Character.Controlled,
|
||||
ignoreHiddenAfflictions: true,
|
||||
limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex));
|
||||
limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex),
|
||||
checkTreatmentSuggestionThreshold: true,
|
||||
checkTreatmentThreshold: false);
|
||||
|
||||
foreach (Identifier treatment in treatmentSuitability.Keys.ToList())
|
||||
{
|
||||
@@ -1949,7 +1972,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool ShouldDisplayAfflictionOnLimb(KeyValuePair<Affliction, LimbHealth> kvp, LimbHealth limbHealth)
|
||||
{
|
||||
if (!kvp.Key.ShouldShowIcon(Character)) { return false; }
|
||||
@@ -2058,23 +2080,23 @@ namespace Barotrauma
|
||||
newAfflictions.Add((limbHealths[limbIndex], afflictionPrefab, afflictionStrength));
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
foreach ((Affliction affliction, LimbHealth limbHealth) in afflictions)
|
||||
{
|
||||
//deactivate afflictions that weren't included in the network message
|
||||
if (!newAfflictions.Any(a => kvp.Key.Prefab == a.afflictionPrefab && kvp.Value == a.limb))
|
||||
if (newAfflictions.None(a => affliction.Prefab == a.afflictionPrefab && limbHealth == a.limb))
|
||||
{
|
||||
kvp.Key.Strength = 0.0f;
|
||||
affliction.Strength = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (limb, afflictionPrefab, strength) in newAfflictions)
|
||||
{
|
||||
Affliction existingAffliction = null;
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
foreach ((Affliction affliction, LimbHealth limbHealth) in afflictions)
|
||||
{
|
||||
if (kvp.Key.Prefab == afflictionPrefab && kvp.Value == limb)
|
||||
if (affliction.Prefab == afflictionPrefab && limbHealth == limb)
|
||||
{
|
||||
existingAffliction = kvp.Key;
|
||||
existingAffliction = affliction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2123,9 +2145,8 @@ namespace Barotrauma
|
||||
|
||||
if (!Character.Params.Health.ApplyAfflictionColors) { return; }
|
||||
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
foreach ((Affliction affliction, LimbHealth _) in afflictions)
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
Color faceTint = affliction.GetFaceTint();
|
||||
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||
Color bodyTint = affliction.GetBodyTint();
|
||||
@@ -2137,17 +2158,23 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Limb limb in Character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count) { continue; }
|
||||
limb.BurnOverlayStrength = 0.0f;
|
||||
limb.DamageOverlayStrength = 0.0f;
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
}
|
||||
|
||||
foreach ((Affliction affliction, LimbHealth limbHealth) in afflictions)
|
||||
{
|
||||
if (affliction.Prefab.BurnOverlayAlpha <= 0.0f && affliction.Prefab.DamageOverlayAlpha <= 0.0f) { continue; }
|
||||
|
||||
float burnStrength = affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.BurnOverlayAlpha;
|
||||
float damageOverlayStrength = affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.DamageOverlayAlpha;
|
||||
foreach (Limb limb in Character.AnimController.Limbs)
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
float burnStrength = affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.BurnOverlayAlpha;
|
||||
if (kvp.Value == limbHealths[limb.HealthIndex] || !affliction.Prefab.LimbSpecific)
|
||||
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count) { continue; }
|
||||
if (limbHealth == limbHealths[limb.HealthIndex] || !affliction.Prefab.LimbSpecific)
|
||||
{
|
||||
limb.BurnOverlayStrength += burnStrength;
|
||||
limb.DamageOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.DamageOverlayAlpha;
|
||||
limb.DamageOverlayStrength += damageOverlayStrength;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -17,13 +17,16 @@ public static class InteractionLabelManager
|
||||
|
||||
public RectangleF TextRect { get; set; }
|
||||
|
||||
public RichString Text;
|
||||
|
||||
public readonly Vector2 OriginalItemPosition;
|
||||
|
||||
public bool OverlapPreventionDone;
|
||||
|
||||
public LabelData(Item item, RectangleF textRect, Camera drawCamera)
|
||||
public LabelData(Item item, RectangleF textRect, RichString text, Camera drawCamera)
|
||||
{
|
||||
Item = item;
|
||||
Text = text;
|
||||
TextRect = textRect;
|
||||
OriginalItemPosition = item.Position;
|
||||
this.drawCamera = drawCamera;
|
||||
@@ -106,7 +109,7 @@ public static class InteractionLabelManager
|
||||
|
||||
if (labels.None(l => l.Item == interactableInRange))
|
||||
{
|
||||
var labelData = new LabelData(interactableInRange, textRect, cam);
|
||||
var labelData = new LabelData(interactableInRange, textRect, RichString.Rich(interactableInRange.Prefab.Name), cam);
|
||||
labels.Add(labelData);
|
||||
}
|
||||
}
|
||||
@@ -124,7 +127,7 @@ public static class InteractionLabelManager
|
||||
private static RectangleF GetLabelRect(Item item, Camera cam)
|
||||
{
|
||||
// create rectangle for overlap prevention
|
||||
Vector2 itemTextSizeScreen = GUIStyle.SubHeadingFont.MeasureString(item.Name) * LabelScale;
|
||||
Vector2 itemTextSizeScreen = GUIStyle.SubHeadingFont.MeasureString(RichString.Rich(item.Prefab.Name).SanitizedValue) * 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
|
||||
@@ -320,9 +323,11 @@ public static class InteractionLabelManager
|
||||
|
||||
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,
|
||||
GUIStyle.SubHeadingFont.DrawStringWithColors(spriteBatch,
|
||||
labelData.Text.SanitizedValue,
|
||||
textDrawPosScreen, color, rotation: 0, origin: Vector2.Zero, scale, spriteEffects: SpriteEffects.None,
|
||||
layerDepth: 0.0f,
|
||||
richTextData: labelData.Text.RichTextData,
|
||||
forceUpperCase: ForceUpperCase.No);
|
||||
}
|
||||
}
|
||||
@@ -1,85 +1,75 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class JobPrefab : PrefabWithUintIdentifier
|
||||
{
|
||||
public GUIButton CreateInfoFrame(out GUIComponent buttonContainer)
|
||||
public GUIButton CreateInfoFrame(bool isPvP, out GUIComponent buttonContainer)
|
||||
{
|
||||
int width = 500, height = 400;
|
||||
int windowPixelWidth = 500, windowPixelHeight = 400;
|
||||
Point absoluteWindowSize = new Point((int)(windowPixelWidth * GUI.xScale), (int)(windowPixelHeight * GUI.yScale));
|
||||
|
||||
GUIButton frameHolder = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
|
||||
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, frameHolder.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, height), frameHolder.RectTransform, Anchor.Center));
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(absoluteWindowSize, frameHolder.RectTransform, Anchor.Center));
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), Name, font: GUIStyle.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), Name, font: GUIStyle.LargeFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
var descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.15f) },
|
||||
Description, font: GUIStyle.SmallFont, wrap: true);
|
||||
var contentList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.75f), paddedFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.1f) })
|
||||
{
|
||||
ScrollBarVisible = true,
|
||||
AutoHideScrollBar = true,
|
||||
CurrentSelectMode = GUIListBox.SelectMode.None,
|
||||
Padding = new Vector4(0, GUI.Scale * 10, 0, 0),
|
||||
Spacing = (int)(GUI.Scale * 5)
|
||||
};
|
||||
|
||||
var descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), contentList.Content.RectTransform),
|
||||
Description, font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.TopLeft)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), contentList.Content.RectTransform),
|
||||
TextManager.Get("Skills"), font: GUIStyle.LargeFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
var skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform)
|
||||
{ RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) });
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillContainer.RectTransform),
|
||||
TextManager.Get("Skills"), font: GUIStyle.LargeFont);
|
||||
foreach (SkillPrefab skill in Skills)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillContainer.RectTransform),
|
||||
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + skill.Identifier), (int)skill.LevelRange.Start + " - " + (int)skill.LevelRange.End),
|
||||
font: GUIStyle.SmallFont);
|
||||
var levelRange = skill.GetLevelRange(isPvP);
|
||||
|
||||
string levelStr =
|
||||
levelRange.End > levelRange.Start ?
|
||||
(int)levelRange.Start + " - " + (int)levelRange.End :
|
||||
((int)levelRange.Start).ToString();
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), contentList.Content.RectTransform),
|
||||
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + skill.Identifier), levelStr),
|
||||
font: GUIStyle.SmallFont, wrap: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
|
||||
buttonContainer = paddedFrame;
|
||||
|
||||
/*if (!ItemIdentifiers.TryGetValue(variant, out var itemIdentifiers)) { return backFrame; }
|
||||
var itemContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform, Anchor.TopRight)
|
||||
{ RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) })
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
|
||||
TextManager.Get("Items", "mapentitycategory.equipment"), font: GUIStyle.LargeFont);
|
||||
foreach (string identifier in itemIdentifiers.Distinct())
|
||||
{
|
||||
if (!(MapEntityPrefab.Find(name: null, identifier: identifier) is ItemPrefab itemPrefab)) { continue; }
|
||||
int count = itemIdentifiers.Count(i => i == identifier);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
|
||||
" - " + (count == 1 ? itemPrefab.Name : itemPrefab.Name + " x" + count),
|
||||
font: GUIStyle.SmallFont);
|
||||
}*/
|
||||
|
||||
return frameHolder;
|
||||
}
|
||||
|
||||
public class OutfitPreview
|
||||
public IEnumerable<Sprite> GetJobOutfitSprites(CharacterTeamType team, bool isPvPMode)
|
||||
{
|
||||
public readonly List<(Sprite sprite, Vector2 drawOffset)> Sprites;
|
||||
|
||||
public Vector2 Dimensions;
|
||||
|
||||
public OutfitPreview()
|
||||
{
|
||||
Sprites = new List<(Sprite sprite, Vector2 drawOffset)>();
|
||||
Dimensions = Vector2.One;
|
||||
}
|
||||
|
||||
public void AddSprite(Sprite sprite, Vector2 drawOffset)
|
||||
{
|
||||
Sprites.Add((sprite, drawOffset));
|
||||
}
|
||||
}
|
||||
|
||||
public List<OutfitPreview> GetJobOutfitSprites(CharacterInfoPrefab charInfoPrefab, bool useInventoryIcon, out Vector2 maxDimensions)
|
||||
{
|
||||
List<OutfitPreview> outfitPreviews = new List<OutfitPreview>();
|
||||
maxDimensions = Vector2.One;
|
||||
|
||||
var equipIdentifiers = Element.GetChildElements("ItemSet").Elements().Where(e => e.GetAttributeBool("outfit", false)).Select(e => e.GetAttributeIdentifier("identifier", ""));
|
||||
var equipIdentifiers = JobItems
|
||||
.SelectMany(kvp => kvp.Value)
|
||||
.Where(j => j.Outfit)
|
||||
.Select(j => j.GetItemIdentifier(team, isPvPMode));
|
||||
|
||||
List<ItemPrefab> outfitPrefabs = new List<ItemPrefab>();
|
||||
foreach (var equipIdentifier in equipIdentifiers)
|
||||
@@ -88,45 +78,9 @@ namespace Barotrauma
|
||||
if (itemPrefab != null) { outfitPrefabs.Add(itemPrefab); }
|
||||
}
|
||||
|
||||
if (!outfitPrefabs.Any()) { return null; }
|
||||
if (!outfitPrefabs.Any()) { return Enumerable.Empty<Sprite>(); }
|
||||
|
||||
for (int i = 0; i < outfitPrefabs.Count; i++)
|
||||
{
|
||||
var outfitPreview = new OutfitPreview();
|
||||
|
||||
if (!ItemSets.TryGetValue(i, out var itemSetElement)) { continue; }
|
||||
var previewElement = itemSetElement.GetChildElement("PreviewSprites");
|
||||
if (previewElement == null || useInventoryIcon)
|
||||
{
|
||||
if (outfitPrefabs[i] is ItemPrefab prefab && prefab.InventoryIcon != null)
|
||||
{
|
||||
outfitPreview.AddSprite(prefab.InventoryIcon, Vector2.Zero);
|
||||
outfitPreview.Dimensions = prefab.InventoryIcon.SourceRect.Size.ToVector2();
|
||||
maxDimensions.X = MathHelper.Max(maxDimensions.X, outfitPreview.Dimensions.X);
|
||||
maxDimensions.Y = MathHelper.Max(maxDimensions.Y, outfitPreview.Dimensions.Y);
|
||||
}
|
||||
outfitPreviews.Add(outfitPreview);
|
||||
continue;
|
||||
}
|
||||
|
||||
var children = previewElement.Elements().ToList();
|
||||
for (int n = 0; n < children.Count; n++)
|
||||
{
|
||||
var spriteElement = children[n];
|
||||
string spriteTexture = charInfoPrefab.ReplaceVars(spriteElement.GetAttributeString("texture", ""), charInfoPrefab.Heads.First());
|
||||
var sprite = new Sprite(spriteElement, file: spriteTexture);
|
||||
sprite.size = new Vector2(sprite.SourceRect.Width, sprite.SourceRect.Height);
|
||||
outfitPreview.AddSprite(sprite, children[n].GetAttributeVector2("offset", Vector2.Zero));
|
||||
}
|
||||
|
||||
outfitPreview.Dimensions = previewElement.GetAttributeVector2("dims", Vector2.One);
|
||||
maxDimensions.X = MathHelper.Max(maxDimensions.X, outfitPreview.Dimensions.X);
|
||||
maxDimensions.Y = MathHelper.Max(maxDimensions.Y, outfitPreview.Dimensions.Y);
|
||||
|
||||
outfitPreviews.Add(outfitPreview);
|
||||
}
|
||||
|
||||
return outfitPreviews;
|
||||
return outfitPrefabs.Select(p => p.InventoryIcon ?? p.Sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,16 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
var conditionalSprite = ConditionalSprites.FirstOrDefault(c => c.Exclusive && c.IsActive && c.DeformableSprite != null);
|
||||
// Performance-sensitive, hence implemented without Linq.
|
||||
ConditionalSprite conditionalSprite = null;
|
||||
foreach (ConditionalSprite cs in ConditionalSprites)
|
||||
{
|
||||
if (cs.Exclusive && cs.IsActive && cs.DeformableSprite != null)
|
||||
{
|
||||
conditionalSprite = cs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conditionalSprite != null)
|
||||
{
|
||||
return conditionalSprite.DeformableSprite;
|
||||
@@ -155,7 +164,16 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
var conditionalSprite = ConditionalSprites.FirstOrDefault(c => c.Exclusive && c.IsActive && c.ActiveSprite != null);
|
||||
// Performance-sensitive, hence implemented without Linq.
|
||||
ConditionalSprite conditionalSprite = null;
|
||||
foreach (ConditionalSprite cs in ConditionalSprites)
|
||||
{
|
||||
if (cs.Exclusive && cs.IsActive && cs.ActiveSprite != null)
|
||||
{
|
||||
conditionalSprite = cs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conditionalSprite != null)
|
||||
{
|
||||
return conditionalSprite.ActiveSprite;
|
||||
@@ -184,12 +202,6 @@ namespace Barotrauma
|
||||
|
||||
public Sprite DamagedSprite { get; private set; }
|
||||
|
||||
public bool Hide
|
||||
{
|
||||
get => Params.Hide;
|
||||
set => Params.Hide = value;
|
||||
}
|
||||
|
||||
public List<ConditionalSprite> ConditionalSprites { get; private set; } = new List<ConditionalSprite>();
|
||||
private Dictionary<DecorativeSprite, SpriteState> spriteAnimState = new Dictionary<DecorativeSprite, SpriteState>();
|
||||
private Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
|
||||
@@ -198,6 +210,7 @@ namespace Barotrauma
|
||||
{
|
||||
public float RotationState;
|
||||
public float OffsetState;
|
||||
public float ScaleState;
|
||||
public Vector2 RandomOffsetMultiplier = new Vector2(Rand.Range(-1.0f, 1.0f), Rand.Range(-1.0f, 1.0f));
|
||||
public float RandomRotationFactor = Rand.Range(0.0f, 1.0f);
|
||||
public float RandomScaleFactor = Rand.Range(0.0f, 1.0f);
|
||||
@@ -301,7 +314,16 @@ namespace Barotrauma
|
||||
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams, ref _damagedTexturePath), sourceRectScale: sourceRectScale);
|
||||
break;
|
||||
case "conditionalsprite":
|
||||
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null, ref _texturePath), sourceRectScale: sourceRectScale);
|
||||
string conditionalSpritePath = string.Empty;
|
||||
GetSpritePath(subElement.GetChildElement("sprite") ?? subElement.GetChildElement("deformablesprite") ?? subElement, null, ref conditionalSpritePath);
|
||||
if (conditionalSpritePath.IsNullOrEmpty())
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find a sprite path in the conditional sprite defined in {character.SpeciesName}, limb {type}.",
|
||||
contentPackage: subElement.ContentPackage);
|
||||
}
|
||||
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(),
|
||||
file: conditionalSpritePath,
|
||||
sourceRectScale: sourceRectScale);
|
||||
ConditionalSprites.Add(conditionalSprite);
|
||||
if (conditionalSprite.DeformableSprite != null)
|
||||
{
|
||||
@@ -475,13 +497,24 @@ namespace Barotrauma
|
||||
private string _damagedTexturePath;
|
||||
private string GetSpritePath(ContentXElement element, SpriteParams spriteParams, ref string path)
|
||||
{
|
||||
if (path == null)
|
||||
if (path.IsNullOrEmpty())
|
||||
{
|
||||
if (spriteParams != null)
|
||||
{
|
||||
//1. check if the variant file redefines the texture
|
||||
ContentPath texturePath = character.Params.VariantFile?.Root?.GetAttributeContentPath("texture", character.Prefab.ContentPackage);
|
||||
//2. check if the base prefab defines the texture
|
||||
ContentPath texturePath;
|
||||
//1. check if the limb defines the texture directly
|
||||
var definedTexturePath = element?.GetAttributeContentPath("texture");
|
||||
if (!definedTexturePath.IsNullOrEmpty())
|
||||
{
|
||||
texturePath = definedTexturePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
//2. check if the character file defines the texture directly
|
||||
texturePath = character.Params.VariantFile?.GetRootExcludingOverride()?.GetAttributeContentPath("texture", character.Prefab.ContentPackage);
|
||||
}
|
||||
|
||||
//3. check if the base prefab defines the texture
|
||||
if (texturePath.IsNullOrEmpty() && !character.Prefab.VariantOf.IsEmpty)
|
||||
{
|
||||
Identifier speciesName = character.GetBaseCharacterSpeciesName();
|
||||
@@ -491,7 +524,7 @@ namespace Barotrauma
|
||||
|
||||
texturePath = parentRagdollParams.OriginalElement?.GetAttributeContentPath("texture");
|
||||
}
|
||||
//3. "default case", get the texture from this character's XML
|
||||
//4. "default case", get the texture from this character's XML
|
||||
texturePath ??= ContentPath.FromRaw(spriteParams.Element.ContentPackage ?? character.Prefab.ContentPackage, spriteParams.GetTexturePath());
|
||||
path = GetSpritePath(texturePath);
|
||||
}
|
||||
@@ -749,9 +782,29 @@ namespace Barotrauma
|
||||
|
||||
float herpesStrength = character.CharacterHealth.GetAfflictionStrengthByType(AfflictionPrefab.SpaceHerpesType);
|
||||
|
||||
bool hideLimb = Hide ||
|
||||
OtherWearables.Any(w => w.HideLimb) ||
|
||||
WearingItems.Any(w => w.HideLimb);
|
||||
bool hideLimb = ShouldHideLimb(this);
|
||||
if (!hideLimb && Params.InheritHiding != LimbType.None)
|
||||
{
|
||||
if (character.AnimController.GetLimb(Params.InheritHiding) is Limb otherLimb)
|
||||
{
|
||||
hideLimb = ShouldHideLimb(otherLimb);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ShouldHideLimb(Limb limb)
|
||||
{
|
||||
if (limb.Hide) { return true; }
|
||||
// Performance-sensitive code -> implemented without Linq
|
||||
foreach (var wearable in limb.OtherWearables)
|
||||
{
|
||||
if (wearable.HideLimb) { return true; }
|
||||
}
|
||||
foreach (var wearable in limb.WearingItems)
|
||||
{
|
||||
if (wearable.HideLimb) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool drawHuskSprite = HuskSprite != null && !wearableTypesToHide.Contains(WearableType.Husk);
|
||||
|
||||
@@ -771,11 +824,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (ActiveDeformations.Any())
|
||||
{
|
||||
var deformation = SpriteDeformation.GetDeformation(ActiveDeformations, deformSprite.Size);
|
||||
var deformation = SpriteDeformation.GetDeformation(ActiveDeformations, deformSprite.Size, flippedHorizontally: IsFlipped, false);
|
||||
deformSprite.Deform(deformation);
|
||||
if (LightSource != null && LightSource.DeformableLightSprite != null)
|
||||
{
|
||||
deformation = SpriteDeformation.GetDeformation(ActiveDeformations, deformSprite.Size, dir == Direction.Left);
|
||||
//apparently inversing on the y-axis is only necessary for light sprites (see 345a65ca6)
|
||||
//it's a mystery why this is the case, something to do with sprite flipping being handled differently in light rendering?
|
||||
deformation = SpriteDeformation.GetDeformation(ActiveDeformations, deformSprite.Size, flippedHorizontally: IsFlipped, inverseY: dir == Direction.Left);
|
||||
LightSource.DeformableLightSprite.Deform(deformation);
|
||||
}
|
||||
}
|
||||
@@ -826,7 +881,7 @@ namespace Barotrauma
|
||||
var defSprite = conditionalSprite.DeformableSprite;
|
||||
if (ActiveDeformations.Any())
|
||||
{
|
||||
var deformation = SpriteDeformation.GetDeformation(ActiveDeformations, defSprite.Size);
|
||||
var deformation = SpriteDeformation.GetDeformation(ActiveDeformations, defSprite.Size, flippedHorizontally: IsFlipped);
|
||||
defSprite.Deform(deformation);
|
||||
}
|
||||
else
|
||||
@@ -883,28 +938,28 @@ namespace Barotrauma
|
||||
}
|
||||
depthStep += step;
|
||||
}
|
||||
foreach (WearableSprite wearable in OtherWearables)
|
||||
if (!hideLimb)
|
||||
{
|
||||
if (wearable.Type == WearableType.Husk) { continue; }
|
||||
if (wearableTypesToHide.Contains(wearable.Type))
|
||||
foreach (WearableSprite wearable in OtherWearables)
|
||||
{
|
||||
if (wearable.Type == WearableType.Hair)
|
||||
if (wearable.Type == WearableType.Husk) { continue; }
|
||||
if (wearableTypesToHide.Contains(wearable.Type))
|
||||
{
|
||||
if (HairWithHatSprite != null && !hideLimb)
|
||||
// Draws the short hair
|
||||
if (wearable.Type == WearableType.Hair)
|
||||
{
|
||||
DrawWearable(HairWithHatSprite, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
||||
depthStep += step;
|
||||
continue;
|
||||
if (HairWithHatSprite != null)
|
||||
{
|
||||
DrawWearable(HairWithHatSprite, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
||||
depthStep += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
DrawWearable(wearable, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
||||
//if there are multiple sprites on this limb, make the successive ones be drawn in front
|
||||
depthStep += step;
|
||||
}
|
||||
DrawWearable(wearable, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
||||
//if there are multiple sprites on this limb, make the successive ones be drawn in front
|
||||
depthStep += step;
|
||||
}
|
||||
}
|
||||
foreach (WearableSprite wearable in WearingItems)
|
||||
@@ -952,8 +1007,8 @@ namespace Barotrauma
|
||||
var ca = (float)Math.Cos(-body.Rotation);
|
||||
var sa = (float)Math.Sin(-body.Rotation);
|
||||
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
|
||||
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c,
|
||||
-body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
|
||||
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c, decorativeSprite.Sprite.Origin,
|
||||
-body.Rotation + rotation, decorativeSprite.GetScale(ref spriteAnimState[decorativeSprite].ScaleState, spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
|
||||
depth: activeSprite.Depth - depthStep);
|
||||
depthStep += step;
|
||||
}
|
||||
@@ -963,7 +1018,7 @@ namespace Barotrauma
|
||||
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
|
||||
colorWithoutTint * damageOverlayStrength, activeSprite.Origin,
|
||||
-body.DrawRotation,
|
||||
Scale, spriteEffect, activeSprite.Depth - depthStep * Math.Max(1, WearingItems.Count * 2)); // Multiply by 2 to get rid of z-fighting with some clothing combos
|
||||
Scale * TextureScale, spriteEffect, activeSprite.Depth - depthStep * Math.Max(1, WearingItems.Count * 2)); // Multiply by 2 to get rid of z-fighting with some clothing combos
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Barotrauma
|
||||
|
||||
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame: !isEditor, showName: false, titleFont: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
Readonly = CircuitBox.Locked
|
||||
Readonly = CircuitBox.IsLocked()
|
||||
};
|
||||
fieldCount += componentEditor.Fields.Count;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Barotrauma
|
||||
|
||||
public List<CircuitBoxWireRenderer> VirtualWires = new();
|
||||
|
||||
public bool Locked => CircuitBox.Locked;
|
||||
public bool Locked => CircuitBox.IsLocked();
|
||||
|
||||
public CircuitBoxUI(CircuitBox box)
|
||||
{
|
||||
@@ -786,6 +786,7 @@ namespace Barotrauma
|
||||
if (wireOption.TryUnwrap(out var wire))
|
||||
{
|
||||
CircuitBox.RemoveWires(wire.IsSelected ? wireSelection : ImmutableArray.Create(wire));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (nodeOption)
|
||||
|
||||
@@ -169,9 +169,9 @@ namespace Barotrauma
|
||||
return doc;
|
||||
}
|
||||
|
||||
public void Save(string path)
|
||||
public void Save(string path, bool catchUnauthorizedAccessExceptions = true)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!, catchUnauthorizedAccessExceptions);
|
||||
ToXDocument().SaveSafe(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ namespace Barotrauma.Transition
|
||||
subs = getFiles(oldSubsPath, "*.sub");
|
||||
itemAssemblies = getFiles(oldItemAssembliesPath, "*.xml");
|
||||
|
||||
string[] allOldMods = Directory.GetDirectories(oldModsPath, "*", System.IO.SearchOption.TopDirectoryOnly);
|
||||
string[] allOldMods = Directory.GetDirectories(oldModsPath, "*");
|
||||
|
||||
var publishedItems = await SteamManager.Workshop.GetPublishedItems();
|
||||
foreach (var modDir in allOldMods)
|
||||
|
||||
@@ -588,6 +588,39 @@ namespace Barotrauma
|
||||
GameMain.CharacterEditorScreen.Select();
|
||||
}));
|
||||
|
||||
commands.Add(new Command("settainted", "settainted [true/false]: Sets tainted effect on hovered genetic material.",
|
||||
onExecute: (string[] args) =>
|
||||
{
|
||||
|
||||
if (Character.Controlled == null)
|
||||
{
|
||||
NewMessage("No controlled character!", Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
Item focusedItem = Character.Controlled?.FocusedItem ?? Inventory.SelectedSlot?.Item;
|
||||
|
||||
if (focusedItem == null)
|
||||
{
|
||||
NewMessage("No focused item, hover on something!", Color.Red);
|
||||
return;
|
||||
}
|
||||
|
||||
var geneticMaterial = focusedItem.GetComponent<GeneticMaterial>();
|
||||
|
||||
if (geneticMaterial == null)
|
||||
{
|
||||
NewMessage("Not hovering on a genetic material!", Color.Red);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool newValue = args.None(arg => string.Equals(arg, "false", StringComparison.InvariantCultureIgnoreCase));
|
||||
geneticMaterial.SetTainted(newValue);
|
||||
NewMessage($"Set tainted to {newValue} for {focusedItem.Name}", Color.Yellow);
|
||||
}
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("quickstart", "Starts a singleplayer sandbox", (string[] args) =>
|
||||
{
|
||||
if (Screen.Selected != GameMain.MainMenuScreen)
|
||||
@@ -626,6 +659,113 @@ namespace Barotrauma
|
||||
|
||||
}, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().OrderBy(s => s).ToArray() }));
|
||||
|
||||
commands.Add(new Command("forcewreck", "forcewreck [wreckname] (optional, ThalamusSpawn)[Random/Forced/Disabled]: When generating levels, ensures a specific wreck is generated. Second optional parameter to control thalamus spawning.", (string[] args) =>
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
var submarineFile = GetSubmarineFile<WreckFile>(args[0]);
|
||||
|
||||
if (submarineFile != null)
|
||||
{
|
||||
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(i => i.FilePath == submarineFile.Path.Value);
|
||||
if (matchingSub != null)
|
||||
{
|
||||
NewMessage($"Setting ForceWreck to: {matchingSub.Name}, {submarineFile.Path}", color: Color.Yellow);
|
||||
|
||||
LevelData.ConsoleForceWreck = matchingSub;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage($"Can't find: {args[0]}", color: Color.Red);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Length > 1)
|
||||
{
|
||||
string forceThalamusArg = args[1];
|
||||
if (Enum.TryParse(forceThalamusArg, ignoreCase: true, out LevelData.ThalamusSpawn result))
|
||||
{
|
||||
NewMessage($"Setting ThalamusSpawn to: {result}", color: Color.Yellow);
|
||||
LevelData.ForceThalamus = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage($"Can't parse argument: {forceThalamusArg}", color: Color.Red);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage($"Setting ThalamusSpawn to: {LevelData.ThalamusSpawn.Random}", color: Color.Yellow);
|
||||
LevelData.ForceThalamus = LevelData.ThalamusSpawn.Random;
|
||||
}
|
||||
},
|
||||
() =>
|
||||
{
|
||||
return new string[][]
|
||||
{
|
||||
ListSubmarineFileNames<WreckFile>(),
|
||||
new string[] { LevelData.ThalamusSpawn.Random.ToString(), LevelData.ThalamusSpawn.Forced.ToString(), LevelData.ThalamusSpawn.Disabled.ToString() }
|
||||
};
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("forcebeaconstation|forcebeacon", "forcebeaconstation [station name]: When generating levels, ensures a specific beacon station is generated.", (string[] args) =>
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
var submarineFile = GetSubmarineFile<BeaconStationFile>(args[0]);
|
||||
|
||||
if (submarineFile != null)
|
||||
{
|
||||
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(i => i.FilePath == submarineFile.Path.Value);
|
||||
if (matchingSub != null)
|
||||
{
|
||||
NewMessage($"Setting ForceBeaconStation to: {matchingSub.Name}, {submarineFile.Path}", color: Color.Yellow);
|
||||
|
||||
LevelData.ConsoleForceBeaconStation = matchingSub;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage($"Can't find: {args[0]}", color: Color.Red);
|
||||
}
|
||||
}
|
||||
},
|
||||
() =>
|
||||
{
|
||||
return new string[][]
|
||||
{
|
||||
ListSubmarineFileNames<BeaconStationFile>()
|
||||
};
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("reloadcontentfile", "reloadcontentfile [filepath]: Reloads a specific content xml file during runtime.", (string[] args) =>
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
string pathArgument = args[0];
|
||||
var contentFile = GetContentFile(pathArgument);
|
||||
|
||||
if (contentFile != null)
|
||||
{
|
||||
NewMessage($"Reloading content file: {pathArgument}", Color.Yellow);
|
||||
contentFile.UnloadFile();
|
||||
contentFile.LoadFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage($"Can't find {args[0]} to reload", color:Color.Red);
|
||||
}
|
||||
}
|
||||
},
|
||||
() =>
|
||||
{
|
||||
return new string[][]
|
||||
{
|
||||
ListContentFilePaths()
|
||||
};
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks networking debug logging.", (string[] args) =>
|
||||
{
|
||||
SteamManager.SetSteamworksNetworkingDebugLog(!SteamManager.NetworkingDebugLog);
|
||||
@@ -783,6 +923,7 @@ namespace Barotrauma
|
||||
AssignRelayToServer("spreadsheetexport", false);
|
||||
#if DEBUG
|
||||
AssignRelayToServer("listspamfilters", false);
|
||||
AssignRelayToServer("showitemxml", false);
|
||||
AssignRelayToServer("crash", false);
|
||||
AssignRelayToServer("showballastflorasprite", false);
|
||||
AssignRelayToServer("simulatedlatency", false);
|
||||
@@ -858,14 +999,8 @@ namespace Barotrauma
|
||||
TeleportCharacter(cursorWorldPos, Character.Controlled, args);
|
||||
});
|
||||
|
||||
AssignOnExecute("spawn|spawncharacter", (string[] args) =>
|
||||
{
|
||||
SpawnCharacter(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), out string errorMsg);
|
||||
if (!string.IsNullOrWhiteSpace(errorMsg))
|
||||
{
|
||||
ThrowError(errorMsg);
|
||||
}
|
||||
});
|
||||
AssignOnExecute("spawn|spawncharacter", args => SpawnCharacter(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition)));
|
||||
AssignOnExecute("spawnnpc", args => SpawnCharacter(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), true));
|
||||
|
||||
AssignOnExecute("los", (string[] args) =>
|
||||
{
|
||||
@@ -1924,7 +2059,7 @@ namespace Barotrauma
|
||||
addIfMissing($"missionname.{missionId}".ToIdentifier(), language);
|
||||
}
|
||||
|
||||
if (missionPrefab.Type == MissionType.Combat)
|
||||
if (missionPrefab.Type == Tags.MissionTypeCombat)
|
||||
{
|
||||
addIfMissing($"MissionDescriptionNeutral.{missionId}".ToIdentifier(), language);
|
||||
addIfMissing($"MissionDescription1.{missionId}".ToIdentifier(), language);
|
||||
@@ -2360,7 +2495,47 @@ namespace Barotrauma
|
||||
GameMain.SubEditorScreen.LoadSub(wreckedSubmarineInfo);
|
||||
}));
|
||||
|
||||
commands.Add(new Command("showitemxml", "showitemxml [item]: Shows the XML configuration of an item in the console and copies it to the clipboard. Useful for debugging variants that partially override the XML of the base item for example.", (string[] args) =>
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
ThrowError("Please specify the name or identifier of the item.");
|
||||
return;
|
||||
}
|
||||
|
||||
string itemNameOrId = args[0].ToLowerInvariant();
|
||||
ItemPrefab itemPrefab =
|
||||
(MapEntityPrefab.FindByName(itemNameOrId) ??
|
||||
MapEntityPrefab.FindByIdentifier(itemNameOrId.ToIdentifier())) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
ThrowError("Item \"{itemNameOrId}\" not found!");
|
||||
return;
|
||||
}
|
||||
string xmlStr = itemPrefab.ConfigElement.Element.ToString();
|
||||
NewMessage(xmlStr);
|
||||
Clipboard.SetText(xmlStr);
|
||||
}, getValidArgs: () =>
|
||||
{
|
||||
return new string[][]
|
||||
{
|
||||
GetItemNameOrIdParams().ToArray()
|
||||
};
|
||||
}));
|
||||
|
||||
#if DEBUG
|
||||
|
||||
commands.Add(new Command("unlockachievement", "unlockachievement [identifier]: Unlocks the specified achievement.", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
ThrowError("Please specify the achievement to unlock.");
|
||||
return;
|
||||
}
|
||||
NewMessage($"Unlocked \"{args[0]}\".");
|
||||
AchievementManager.UnlockAchievement(args[0].ToIdentifier());
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("deathprompt", "Shows the death prompt for testing purposes.", (string[] args) =>
|
||||
{
|
||||
DeathPrompt.Create(delay: 1.0f);
|
||||
@@ -2665,7 +2840,7 @@ namespace Barotrauma
|
||||
string[] lines;
|
||||
try
|
||||
{
|
||||
lines = File.ReadAllLines(sourcePath);
|
||||
lines = File.ReadAllLines(sourcePath, catchUnauthorizedAccessExceptions: false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -2746,34 +2921,60 @@ namespace Barotrauma
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
}));
|
||||
|
||||
commands.Add(new Command("dumpeventtexts", "dumpeventtexts [filepath]: gets the texts from event files and and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EventTexts.txt", (string[] args) =>
|
||||
commands.Add(new Command("dumpeventtexts", "dumpeventtexts [sourcepath] [destinationpath]: gets the texts from event files and writes them into a file along with xml tags that can be used in translation files. If the filepath arguments are omitted, all event files are gone through and written to Content/Texts/EventTexts.txt", (string[] args) =>
|
||||
{
|
||||
string filePath = args.Length > 0 ? args[0] : "Content/Texts/EventTexts.txt";
|
||||
string sourcePath = args.Length > 0 ? Path.GetFullPath(args[0]) : string.Empty;
|
||||
string destinationPath = args.Length > 1 ? args[1] : "Content/Texts/EventTexts.txt";
|
||||
List<string> lines = new List<string>();
|
||||
HashSet<XDocument> docs = new HashSet<XDocument>();
|
||||
HashSet<string> textIds = new HashSet<string>();
|
||||
|
||||
Dictionary<string, string> existingTexts = new Dictionary<string, string>();
|
||||
|
||||
foreach (EventPrefab eventPrefab in EventSet.GetAllEventPrefabs())
|
||||
{
|
||||
if (eventPrefab is not TraitorEventPrefab) { continue; }
|
||||
if (eventPrefab.Identifier.IsEmpty)
|
||||
string dir = Path.GetDirectoryName(eventPrefab.FilePath.FullPath);
|
||||
if (!sourcePath.IsNullOrEmpty() &&
|
||||
Path.GetFullPath(eventPrefab.FilePath.FullPath) != sourcePath &&
|
||||
Path.GetDirectoryName(eventPrefab.FilePath.FullPath) != sourcePath)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (eventPrefab.Identifier.IsEmpty) { continue; }
|
||||
docs.Add(eventPrefab.ConfigElement.Document);
|
||||
getTextsFromElement(eventPrefab.ConfigElement, lines, eventPrefab.Identifier.Value);
|
||||
NewMessage($"Collecting event texts from event \"{eventPrefab.Identifier}\"...", Color.Cyan);
|
||||
}
|
||||
|
||||
if (lines.None())
|
||||
{
|
||||
if (sourcePath.IsNullOrEmpty())
|
||||
{
|
||||
ThrowError("Could not find any event texts. Have all the texts already been moved from the event files to the text files?");
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowError($"Could not find any event texts from \"{sourcePath}\". Are you sure the path is to a valid event xml file or a directory that contains event xml files?");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(filePath, lines);
|
||||
try
|
||||
{
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
File.WriteAllLines(destinationPath, lines);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ThrowError($"Failed to open the file \"{filePath}\".", e);
|
||||
ThrowError($"Failed to write to the file \"{destinationPath}\".", e);
|
||||
}
|
||||
try
|
||||
{
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(destinationPath));
|
||||
NewMessage($"Wrote the event texts to a text file in \"{destinationPath}\".", Color.Cyan);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ThrowError($"Failed to open the file \"{destinationPath}\".", e);
|
||||
}
|
||||
|
||||
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
|
||||
@@ -2783,10 +2984,12 @@ namespace Barotrauma
|
||||
};
|
||||
foreach (XDocument doc in docs)
|
||||
{
|
||||
using (var writer = XmlWriter.Create(new System.Uri(doc.BaseUri).LocalPath, settings))
|
||||
string filePath = new System.Uri(doc.BaseUri).LocalPath;
|
||||
using (var writer = XmlWriter.Create(filePath, settings))
|
||||
{
|
||||
doc.WriteTo(writer);
|
||||
writer.Flush();
|
||||
NewMessage($"Updated the event file \"{filePath}\".", Color.Cyan);
|
||||
}
|
||||
}
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
@@ -2805,14 +3008,10 @@ namespace Barotrauma
|
||||
text = subTextElement?.GetAttributeString(textAttribute, null);
|
||||
textElement = subTextElement;
|
||||
}
|
||||
if (text == null)
|
||||
{
|
||||
AddWarning("Failed to find text from the element " + element.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
string textId = $"EventText.{parentName}";
|
||||
if (!string.IsNullOrEmpty(text) && !text.Contains("EventText.", StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrEmpty(text) && !text.StartsWith("EventText.", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("Tutorial.", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (existingTexts.TryGetValue(text, out string existingTextId))
|
||||
{
|
||||
@@ -2982,9 +3181,18 @@ namespace Barotrauma
|
||||
}));
|
||||
|
||||
#if DEBUG
|
||||
commands.Add(new Command("playovervc", "Plays a sound over voice chat.", (args) =>
|
||||
{
|
||||
VoipCapture.Instance?.SetOverrideSound(args.Length > 0 ? args[0] : null);
|
||||
}));
|
||||
|
||||
commands.Add(new Command("checkduplicates", "Checks the given language for duplicate translation keys and writes to file.", (string[] args) =>
|
||||
{
|
||||
if (args.Length != 1) { return; }
|
||||
if (args.Length != 1)
|
||||
{
|
||||
ThrowError("Please specify a language to check.");
|
||||
return;
|
||||
}
|
||||
TextManager.CheckForDuplicates(args[0].ToIdentifier().ToLanguageIdentifier());
|
||||
}));
|
||||
|
||||
@@ -3443,9 +3651,7 @@ namespace Barotrauma
|
||||
}
|
||||
RagdollParams ragdollParams = character.AnimController.RagdollParams;
|
||||
ragdollParams.LimbScale = MathHelper.Clamp(value, RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE);
|
||||
var pos = character.WorldPosition;
|
||||
character.AnimController.Recreate();
|
||||
character.TeleportTo(pos);
|
||||
character.AnimController.RecreateAndRespawn();
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("jointscale", "Define the jointscale for the controlled character. Provide id or name if you want to target another character. Note: the changes are not saved!", (string[] args) =>
|
||||
@@ -3468,9 +3674,7 @@ namespace Barotrauma
|
||||
}
|
||||
RagdollParams ragdollParams = character.AnimController.RagdollParams;
|
||||
ragdollParams.JointScale = MathHelper.Clamp(value, RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE);
|
||||
var pos = character.WorldPosition;
|
||||
character.AnimController.Recreate();
|
||||
character.TeleportTo(pos);
|
||||
character.AnimController.RecreateAndRespawn();
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("ragdollscale", "Rescale the ragdoll of the controlled character. Provide id or name if you want to target another character. Note: the changes are not saved!", (string[] args) =>
|
||||
@@ -3494,9 +3698,7 @@ namespace Barotrauma
|
||||
RagdollParams ragdollParams = character.AnimController.RagdollParams;
|
||||
ragdollParams.LimbScale = MathHelper.Clamp(value, RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE);
|
||||
ragdollParams.JointScale = MathHelper.Clamp(value, RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE);
|
||||
var pos = character.WorldPosition;
|
||||
character.AnimController.Recreate();
|
||||
character.TeleportTo(pos);
|
||||
character.AnimController.RecreateAndRespawn();
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("recreateragdoll", "Recreate the ragdoll of the controlled character. Provide id or name if you want to target another character.", (string[] args) =>
|
||||
@@ -3507,21 +3709,43 @@ namespace Barotrauma
|
||||
ThrowError("Not controlling any character!");
|
||||
return;
|
||||
}
|
||||
var pos = character.WorldPosition;
|
||||
character.AnimController.Recreate();
|
||||
character.TeleportTo(pos);
|
||||
}, isCheat: true));
|
||||
character.AnimController.RecreateAndRespawn();
|
||||
}, isCheat: true,
|
||||
getValidArgs: () => new[] { GetSpawnedSpeciesNames() }));
|
||||
|
||||
commands.Add(new Command("resetragdoll", "Reset the ragdoll of the controlled character. Provide id or name if you want to target another character.", (string[] args) =>
|
||||
commands.Add(new Command("resetragdoll", "Reset the ragdoll of the controlled character (and all of the same species). Provide species name if you want to target another character.", (string[] args) =>
|
||||
{
|
||||
var character = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, true);
|
||||
if (character == null)
|
||||
IEnumerable<Character> characters;
|
||||
if (args.Length == 0)
|
||||
{
|
||||
ThrowError("Not controlling any character!");
|
||||
if (Character.Controlled == null)
|
||||
{
|
||||
ThrowError("Invalid species name! Press [TAB] to get valid options in this context.");
|
||||
return;
|
||||
}
|
||||
// Reset all characters of the same species, because the same params affect them too.
|
||||
characters = FindMatchingSpecies(Character.Controlled.SpeciesName.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
characters = FindMatchingSpecies(args);
|
||||
}
|
||||
if (characters.None())
|
||||
{
|
||||
ThrowError("Invalid species name!");
|
||||
return;
|
||||
}
|
||||
character.AnimController.ResetRagdoll(forceReload: true);
|
||||
}, isCheat: true));
|
||||
characters.ForEach(c => c.AnimController.ResetRagdoll());
|
||||
foreach (Character character in characters)
|
||||
{
|
||||
// Variant scale multiplier doesn't work without recreating the ragdoll.
|
||||
if (!character.VariantOf.IsEmpty)
|
||||
{
|
||||
character.AnimController.RecreateAndRespawn();
|
||||
}
|
||||
}
|
||||
}, isCheat: true,
|
||||
getValidArgs: () => new[] { GetSpawnedSpeciesNames() }));
|
||||
|
||||
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) =>
|
||||
{
|
||||
@@ -3546,6 +3770,29 @@ namespace Barotrauma
|
||||
character.AnimController.TryLoadAnimation(animationType, Path.GetFileNameWithoutExtension(fileName), out _, throwErrors: true);
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("startlocalmptestsession", "startlocalmptestsession [(optional) number of clients, defaults to 2]: starts a new mp test session with multiple clients connected to local dedicated server", (string[] args) =>
|
||||
{
|
||||
// if we are not in main menu, exit out
|
||||
if (Screen.Selected != GameMain.MainMenuScreen)
|
||||
{
|
||||
ThrowError("Must be in main menu to start.");
|
||||
return;
|
||||
}
|
||||
|
||||
// try to parse the number of clients
|
||||
int numClients = 2;
|
||||
if (args.Length > 0)
|
||||
{
|
||||
if (!int.TryParse(args[0], out numClients))
|
||||
{
|
||||
ThrowError("Failed to parse the number of clients.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
StartLocalMPSession(numClients);
|
||||
}));
|
||||
|
||||
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 =>
|
||||
{
|
||||
var character = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, true);
|
||||
@@ -3634,7 +3881,7 @@ namespace Barotrauma
|
||||
ThrowError("Cannot use the flipx command while playing online.");
|
||||
return;
|
||||
}
|
||||
if (Submarine.MainSub.SubBody != null) { Submarine.MainSub?.FlipX(); }
|
||||
if (Submarine.MainSub?.SubBody != null) { Submarine.MainSub.FlipX(); }
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("head", "Load the head sprite and the wearables (hair etc). Required argument: head id. Optional arguments: hair index, beard index, moustache index, face attachment index.", args =>
|
||||
@@ -3997,5 +4244,44 @@ namespace Barotrauma
|
||||
componentCost += itemPrefab.DefaultPrice.Price;
|
||||
}
|
||||
}
|
||||
|
||||
public static void StartLocalMPSession(int numClients = 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Process.GetProcessesByName("DedicatedServer").Length == 0)
|
||||
{
|
||||
#if WINDOWS
|
||||
Process.Start("DedicatedServer.exe", arguments: "-multiclienttestmode");
|
||||
#else
|
||||
Process.Start("./DedicatedServer", arguments: "-multiclienttestmode");
|
||||
#endif
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
GameMain.Client = new GameClient("client1",
|
||||
new LidgrenEndpoint(System.Net.IPAddress.Loopback, NetConfig.DefaultPort), "localhost", Option<int>.None());
|
||||
|
||||
numClients = MathHelper.Clamp(numClients, 1, 4);
|
||||
|
||||
if (numClients > 1)
|
||||
{
|
||||
for (int i = 2; i <= numClients; i++)
|
||||
{
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
#if WINDOWS
|
||||
Process.Start("Barotrauma.exe", arguments: "-connect server localhost -username client" + i);
|
||||
#else
|
||||
Process.Start("./Barotrauma", arguments: "-connect server localhost -username client" + i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to start the local MP test session", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace Barotrauma
|
||||
{
|
||||
return
|
||||
lastActiveAction != null &&
|
||||
!lastActiveAction.ParentEvent.IsFinished &&
|
||||
lastActiveAction.ParentEvent != ParentEvent &&
|
||||
Timing.TotalTime < lastActiveAction.lastActiveTime + duration;
|
||||
}
|
||||
@@ -101,6 +102,7 @@ namespace Barotrauma
|
||||
conversationList.BarScroll = (prevSize - conversationList.Content.Rect.Height) / (conversationList.TotalSize - conversationList.Content.Rect.Height);
|
||||
conversationList.ScrollToEnd(duration: 0.5f);
|
||||
lastMessageBox.SetBackgroundIcon(eventSprite);
|
||||
MarkMessageBoxAsLastAction(lastMessageBox);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -123,16 +125,7 @@ namespace Barotrauma
|
||||
messageBox.AutoClose = false;
|
||||
GUIStyle.Apply(messageBox.InnerFrame, "DialogBox");
|
||||
|
||||
if (actionInstance != null)
|
||||
{
|
||||
lastActiveAction = actionInstance;
|
||||
actionInstance.lastActiveTime = Timing.TotalTime;
|
||||
actionInstance.dialogBox = messageBox;
|
||||
}
|
||||
else
|
||||
{
|
||||
messageBox.UserData = new Pair<string, UInt16>("ConversationAction", actionId.Value);
|
||||
}
|
||||
MarkMessageBoxAsLastAction(messageBox);
|
||||
|
||||
int padding = GUI.IntScale(16);
|
||||
|
||||
@@ -155,6 +148,20 @@ namespace Barotrauma
|
||||
};
|
||||
shadow.SetAsFirstChild();
|
||||
|
||||
void MarkMessageBoxAsLastAction(GUIMessageBox messageBox)
|
||||
{
|
||||
if (actionInstance != null)
|
||||
{
|
||||
lastActiveAction = actionInstance;
|
||||
actionInstance.lastActiveTime = Timing.TotalTime;
|
||||
actionInstance.dialogBox = messageBox;
|
||||
}
|
||||
else
|
||||
{
|
||||
messageBox.UserData = new Pair<string, UInt16>("ConversationAction", actionId.Value);
|
||||
}
|
||||
}
|
||||
|
||||
static void RecalculateLastMessage(GUIListBox conversationList, bool append)
|
||||
{
|
||||
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
|
||||
|
||||
@@ -48,6 +48,7 @@ partial class EventLog
|
||||
textContent,
|
||||
difficultyIconCount,
|
||||
icon, GUIStyle.Red,
|
||||
difficultyTooltipText: null,
|
||||
out GUIImage missionIcon);
|
||||
|
||||
if (traitorResults != null &&
|
||||
|
||||
@@ -549,6 +549,33 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public void ClientRead(IReadMessage msg)
|
||||
{
|
||||
if (GameMain.GameSession.IsRunning && !GameMain.Instance.LoadingScreenOpen)
|
||||
{
|
||||
ClientApplyNetworkMessage(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if the game session is not currently running (round still loading),
|
||||
//we need to wait because the entities the status effect / conversation / etc targets may not exist yet
|
||||
CoroutineManager.StartCoroutine(ApplyNetworkMessageWhenRoundLoaded(msg));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<CoroutineStatus> ApplyNetworkMessageWhenRoundLoaded(IReadMessage msg)
|
||||
{
|
||||
while (GameMain.GameSession is { IsRunning: false } || GameMain.Instance.LoadingScreenOpen)
|
||||
{
|
||||
yield return new WaitForSeconds(1.0f);
|
||||
}
|
||||
if (GameMain.GameSession != null && GameMain.Client != null)
|
||||
{
|
||||
ClientApplyNetworkMessage(msg);
|
||||
}
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void ClientApplyNetworkMessage(IReadMessage msg)
|
||||
{
|
||||
NetworkEventType eventType = (NetworkEventType)msg.ReadByte();
|
||||
switch (eventType)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -21,7 +21,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override bool DisplayAsCompleted => State > 0 && requireRescue.None();
|
||||
public override bool DisplayAsCompleted =>
|
||||
!DisplayAsFailed &&
|
||||
State > 0 &&
|
||||
//don't display as completed mid-round if there's NPCs to rescue (mission isn't guaranteed to complete yet)
|
||||
requireRescue.None();
|
||||
|
||||
public override bool DisplayAsFailed => State == HostagesKilledState;
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
@@ -47,7 +52,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (allowOrderingRescuees)
|
||||
{
|
||||
GameMain.GameSession.CrewManager.AddCharacterToCrewList(character);
|
||||
GameMain.GameSession.CrewManager?.AddCharacterToCrewList(character);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -59,10 +64,13 @@ namespace Barotrauma
|
||||
if (character.Submarine != null && character.AIController is EnemyAIController enemyAi)
|
||||
{
|
||||
enemyAi.UnattackableSubmarines.Add(character.Submarine);
|
||||
enemyAi.UnattackableSubmarines.Add(Submarine.MainSub);
|
||||
foreach (Submarine sub in Submarine.MainSub.DockedTo)
|
||||
if (Submarine.MainSub != null)
|
||||
{
|
||||
enemyAi.UnattackableSubmarines.Add(sub);
|
||||
enemyAi.UnattackableSubmarines.Add(Submarine.MainSub);
|
||||
foreach (Submarine sub in Submarine.MainSub.DockedTo)
|
||||
{
|
||||
enemyAi.UnattackableSubmarines.Add(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
namespace Barotrauma
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class CombatMission : Mission
|
||||
{
|
||||
private readonly Dictionary<byte, int> clientKills = new Dictionary<byte, int>();
|
||||
private readonly Dictionary<byte, int> clientDeaths = new Dictionary<byte, int>();
|
||||
|
||||
private readonly Dictionary<ushort, int> botKills = new Dictionary<ushort, int>();
|
||||
private readonly Dictionary<ushort, int> botDeaths = new Dictionary<ushort, int>();
|
||||
|
||||
public override LocalizedString Description
|
||||
{
|
||||
get
|
||||
@@ -21,5 +31,93 @@
|
||||
|
||||
public override bool DisplayAsCompleted => false;
|
||||
public override bool DisplayAsFailed => false;
|
||||
|
||||
public override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (targetSubmarine == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return (targetSubmarineSonarLabel is { Loaded: true } ? targetSubmarineSonarLabel : targetSubmarine.Info.DisplayName, targetSubmarine.WorldPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color GetTeamColor(CharacterTeamType teamID)
|
||||
{
|
||||
if (teamID == CharacterTeamType.Team1)
|
||||
{
|
||||
return GUIStyle.GetComponentStyle("CoalitionIcon")?.Color ?? GUIStyle.Blue;
|
||||
}
|
||||
else if (teamID == CharacterTeamType.Team2)
|
||||
{
|
||||
return GUIStyle.GetComponentStyle("SeparatistIcon")?.Color ?? GUIStyle.Orange;
|
||||
}
|
||||
return Color.White;
|
||||
}
|
||||
|
||||
public int GetClientKillCount(Client client)
|
||||
{
|
||||
if (clientKills.TryGetValue(client.SessionId, out int kills))
|
||||
{
|
||||
return kills;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int GetClientDeathCount(Client client)
|
||||
{
|
||||
if (clientDeaths.TryGetValue(client.SessionId, out int deaths))
|
||||
{
|
||||
return deaths;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int GetBotKillCount(CharacterInfo botInfo)
|
||||
{
|
||||
if (botKills.TryGetValue(botInfo.ID, out int kills))
|
||||
{
|
||||
return kills;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int GetBotDeathCount(CharacterInfo botInfo)
|
||||
{
|
||||
if (botDeaths.TryGetValue(botInfo.ID, out int deaths))
|
||||
{
|
||||
return deaths;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override void ClientRead(IReadMessage msg)
|
||||
{
|
||||
base.ClientRead(msg);
|
||||
Scores[0] = msg.ReadUInt16();
|
||||
Scores[1] = msg.ReadUInt16();
|
||||
|
||||
uint clientCount = msg.ReadVariableUInt32();
|
||||
for (int i = 0; i < clientCount; i++)
|
||||
{
|
||||
byte clientId = msg.ReadByte();
|
||||
clientDeaths[clientId] = (int)msg.ReadVariableUInt32();
|
||||
clientKills[clientId] = (int)msg.ReadVariableUInt32();
|
||||
}
|
||||
|
||||
uint botCount = msg.ReadVariableUInt32();
|
||||
for (int i = 0; i < botCount; i++)
|
||||
{
|
||||
ushort botId = msg.ReadUInt16();
|
||||
botDeaths[botId] = (int)msg.ReadVariableUInt32();
|
||||
botKills[botId] = (int)msg.ReadVariableUInt32();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using static Barotrauma.MissionPrefab;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -61,6 +60,16 @@ namespace Barotrauma
|
||||
return RichString.Rich(TextManager.GetWithVariable("missionreward", "[reward]", "‖color:gui.orange‖" + rewardText + "‖end‖"));
|
||||
}
|
||||
|
||||
public RichString GetDifficultyToolTipText()
|
||||
{
|
||||
// 2 skulls give +10% XP, 3 skulls +20% XP and 4 skulls give +30% XP.
|
||||
float xpBonusMultiplier = CalculateDifficultyXPMultiplier();
|
||||
float xpBonusPercentage = (xpBonusMultiplier - 1f) * 100f;
|
||||
int bonusRounded = (int)Math.Round(xpBonusPercentage);
|
||||
LocalizedString tooltipText = TextManager.GetWithVariable(tag: "missiondifficultyxpbonustooltip", varName: "[bonus]", value: bonusRounded.ToString());
|
||||
return RichString.Rich(tooltipText);
|
||||
}
|
||||
|
||||
public RichString GetReputationRewardText()
|
||||
{
|
||||
List<LocalizedString> reputationRewardTexts = new List<LocalizedString>();
|
||||
@@ -117,6 +126,7 @@ namespace Barotrauma
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
partial void DistributeExperienceToCrew(IEnumerable<Character> crew, int experienceGain)
|
||||
{
|
||||
foreach (Character character in crew)
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace Barotrauma
|
||||
return hudIconColor ?? IconColor;
|
||||
}
|
||||
}
|
||||
public Color ProgressBarColor { get; private set; }
|
||||
|
||||
private Sprite hudIcon;
|
||||
private Color? hudIconColor;
|
||||
@@ -90,6 +91,7 @@ namespace Barotrauma
|
||||
}
|
||||
this.portraits = portraits.ToImmutableArray();
|
||||
overrideMusicOnState = overrideMusic.ToImmutableDictionary();
|
||||
ProgressBarColor = element.GetAttributeColor(nameof(ProgressBarColor), GUIStyle.Blue);
|
||||
}
|
||||
|
||||
public Identifier GetOverrideMusicType(int state)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -8,26 +10,51 @@ namespace Barotrauma
|
||||
public override bool DisplayAsCompleted => false;
|
||||
public override bool DisplayAsFailed => false;
|
||||
|
||||
private void TryShowPickedUpMessage() => HandleMessage(ref pickedUpMessage);
|
||||
|
||||
private void TryShowRetrievedMessage()
|
||||
{
|
||||
if (DetermineCompleted())
|
||||
{
|
||||
if (!allRetrievedMessage.IsNullOrEmpty()) { CreateMessageBox(string.Empty, allRetrievedMessage); }
|
||||
//no need to show this again, clear it
|
||||
allRetrievedMessage = string.Empty;
|
||||
HandleMessage(ref allRetrievedMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!partiallyRetrievedMessage.IsNullOrEmpty()) { CreateMessageBox(string.Empty, partiallyRetrievedMessage); }
|
||||
//no need to show this again, clear it
|
||||
partiallyRetrievedMessage = string.Empty;
|
||||
HandleMessage(ref partiallyRetrievedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMessage(ref LocalizedString message)
|
||||
{
|
||||
if (!message.IsNullOrEmpty()) { CreateMessageBox(string.Empty, message); }
|
||||
//no need to show this again, clear it
|
||||
message = string.Empty;
|
||||
}
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
|
||||
byte characterCount = msg.ReadByte();
|
||||
for (int i = 0; i < characterCount; i++)
|
||||
{
|
||||
Character character = Character.ReadSpawnData(msg);
|
||||
characters.Add(character);
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int j = 0; j < itemCount; j++)
|
||||
{
|
||||
Item.ReadSpawnData(msg);
|
||||
}
|
||||
}
|
||||
if (characters.Contains(null))
|
||||
{
|
||||
throw new System.Exception("Error in SalvageMission.ClientReadInitial: character list contains null (mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
if (characters.Count != characterCount)
|
||||
{
|
||||
throw new System.Exception("Error in SalvageMission.ClientReadInitial: character count does not match the server count (" + characters + " != " + characters.Count + "mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
bool targetFound = msg.ReadBoolean();
|
||||
@@ -81,24 +108,37 @@ namespace Barotrauma
|
||||
{
|
||||
base.ClientRead(msg);
|
||||
bool atLeastOneTargetWasRetrieved = false;
|
||||
bool showPickedUpMsg = 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;
|
||||
Target target = targets[i];
|
||||
bool wasRetrieved = target.Retrieved;
|
||||
bool wasPickedUp = target.State == Target.RetrievalState.PickedUp;
|
||||
targets[i].State = state;
|
||||
if (!wasRetrieved && targets[i].Retrieved)
|
||||
if (!wasRetrieved && target.Retrieved)
|
||||
{
|
||||
atLeastOneTargetWasRetrieved = true;
|
||||
}
|
||||
else if (!wasPickedUp && target.State == Target.RetrievalState.PickedUp)
|
||||
{
|
||||
showPickedUpMsg = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (atLeastOneTargetWasRetrieved)
|
||||
{
|
||||
TryShowRetrievedMessage();
|
||||
}
|
||||
if (showPickedUpMsg)
|
||||
{
|
||||
TryShowPickedUpMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<Entity> HudIconTargets => targets.Where(static t => !t.Retrieved && t.Item.GetRootInventoryOwner() is not Character { IsLocalPlayer: true }).Select(static t => t.Item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,20 +7,7 @@ namespace Barotrauma
|
||||
{
|
||||
partial class ScanMission : Mission
|
||||
{
|
||||
public override IEnumerable<Entity> HudIconTargets
|
||||
{
|
||||
get
|
||||
{
|
||||
if (State == 0)
|
||||
{
|
||||
return scanTargets.Where(kvp => !kvp.Value).Select(kvp => kvp.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Enumerable.Empty<Entity>();
|
||||
}
|
||||
}
|
||||
}
|
||||
public override IEnumerable<Entity> HudIconTargets => scanTargets.Where(kvp => !kvp.Value).Select(kvp => kvp.Key);
|
||||
|
||||
public override bool DisplayAsCompleted => false;
|
||||
public override bool DisplayAsFailed => false;
|
||||
@@ -62,7 +49,7 @@ namespace Barotrauma
|
||||
ushort id = msg.ReadUInt16();
|
||||
bool scanned = msg.ReadBoolean();
|
||||
Entity entity = Entity.FindEntityByID(id);
|
||||
if (!(entity is WayPoint wayPoint))
|
||||
if (entity is not WayPoint wayPoint)
|
||||
{
|
||||
string errorMsg = $"Failed to find a waypoint in ScanMission.ClientReadScanTargetStatus. Entity {id} was {(entity?.ToString() ?? null)}";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
|
||||
@@ -420,7 +420,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor this further
|
||||
private void HandleNewLineAndAlignment(
|
||||
string text,
|
||||
in Vector2 advanceUnit,
|
||||
@@ -435,23 +434,29 @@ namespace Barotrauma
|
||||
out uint charIndex,
|
||||
out bool shouldContinue)
|
||||
{
|
||||
if ((alignment.HasFlag(Alignment.CenterX) || alignment.HasFlag(Alignment.Right)) && (lineWidth < 0.0f || text[i] == '\n'))
|
||||
if (lineWidth < 0.0f || text[i] == '\n')
|
||||
{
|
||||
int startIndex = lineWidth < 0.0f ? i : (i + 1);
|
||||
lineWidth = 0.0f;
|
||||
for (int j = startIndex; j < text.Length; j++)
|
||||
// Use bitwise operations instead of HasFlag or HasAnyFlag to avoid boxing, as this is performance-sensitive code.
|
||||
bool isHorizontallyCentered = (alignment & Alignment.CenterX) == Alignment.CenterX;
|
||||
bool isAlignedToRight = (alignment & Alignment.Right) == Alignment.Right;
|
||||
if (isHorizontallyCentered || isAlignedToRight)
|
||||
{
|
||||
if (text[j] == '\n') { break; }
|
||||
uint chrIndex = text[j];
|
||||
int startIndex = lineWidth < 0.0f ? i : (i + 1);
|
||||
lineWidth = 0.0f;
|
||||
for (int j = startIndex; j < text.Length; j++)
|
||||
{
|
||||
if (text[j] == '\n') { break; }
|
||||
uint chrIndex = text[j];
|
||||
|
||||
var gd2 = GetGlyphData(chrIndex);
|
||||
lineWidth += gd2.Advance;
|
||||
var gd2 = GetGlyphData(chrIndex);
|
||||
lineWidth += gd2.Advance;
|
||||
}
|
||||
currentLineOffset = -lineWidth * advanceUnit * scale.X;
|
||||
if (isHorizontallyCentered) { currentLineOffset *= 0.5f; }
|
||||
|
||||
currentLineOffset.X = MathF.Round(currentLineOffset.X);
|
||||
currentLineOffset.Y = MathF.Round(currentLineOffset.Y);
|
||||
}
|
||||
currentLineOffset = -lineWidth * advanceUnit * scale.X;
|
||||
if (alignment.HasFlag(Alignment.CenterX)) { currentLineOffset *= 0.5f; }
|
||||
|
||||
currentLineOffset.X = MathF.Round(currentLineOffset.X);
|
||||
currentLineOffset.Y = MathF.Round(currentLineOffset.Y);
|
||||
}
|
||||
if (text[i] == '\n')
|
||||
{
|
||||
@@ -493,7 +498,7 @@ namespace Barotrauma
|
||||
|
||||
int lineNum = 0;
|
||||
Vector2 currentPos = position;
|
||||
Vector2 advanceUnit = rotation == 0.0f ? Vector2.UnitX : new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
|
||||
Vector2 advanceUnit = rotation == 0.0f ? Vector2.UnitX : new Vector2(MathF.Cos(rotation), MathF.Sin(rotation));
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
HandleNewLineAndAlignment(text, advanceUnit, position, scale, alignment, i,
|
||||
@@ -504,7 +509,7 @@ namespace Barotrauma
|
||||
GlyphData gd = GetGlyphData(charIndex);
|
||||
if (gd.TexIndex >= 0)
|
||||
{
|
||||
if (gd.TexIndex < 0 || gd.TexIndex >= textures.Count)
|
||||
if (gd.TexIndex >= textures.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"Error while rendering text. Texture index was out of range. Text: {text}, char: {charIndex} index: {gd.TexIndex}, texture count: {textures.Count}");
|
||||
}
|
||||
@@ -542,6 +547,11 @@ namespace Barotrauma
|
||||
DynamicRenderAtlas(graphicsDevice, text);
|
||||
}
|
||||
|
||||
quadVertices[0].Color = color;
|
||||
quadVertices[1].Color = color;
|
||||
quadVertices[2].Color = color;
|
||||
quadVertices[3].Color = color;
|
||||
|
||||
Vector2 currentPos = position;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
@@ -558,26 +568,33 @@ namespace Barotrauma
|
||||
if (gd.TexIndex >= 0)
|
||||
{
|
||||
float halfCharHeight = gd.TexCoords.Height * 0.5f;
|
||||
float slantStrength = 0.35f;
|
||||
float topItalicOffset = italics ? ((halfCharHeight - gd.DrawOffset.Y) * slantStrength) + baseHeight * 0.18f : 0.0f;
|
||||
float bottomItalicOffset = italics ? ((-halfCharHeight - gd.DrawOffset.Y) * slantStrength) + baseHeight * 0.18f : 0.0f;
|
||||
const float slantStrength = 0.35f;
|
||||
float topItalicOffset = 0.0f;
|
||||
float bottomItalicOffset = 0.0f;
|
||||
if (italics)
|
||||
{
|
||||
topItalicOffset = ((halfCharHeight - gd.DrawOffset.Y) * slantStrength) + baseHeight * 0.18f;
|
||||
bottomItalicOffset = ((-halfCharHeight - gd.DrawOffset.Y) * slantStrength) + baseHeight * 0.18f;
|
||||
}
|
||||
|
||||
Texture2D tex = textures[gd.TexIndex];
|
||||
|
||||
float left = (float)gd.TexCoords.Left / tex.Width;
|
||||
float bottom = (float)gd.TexCoords.Bottom / tex.Height;
|
||||
float top = (float)gd.TexCoords.Top / tex.Height;
|
||||
float right = (float)gd.TexCoords.Right / tex.Width;
|
||||
|
||||
quadVertices[0].Position = new Vector3(currentPos + gd.DrawOffset + (bottomItalicOffset, gd.TexCoords.Height), 0.0f);
|
||||
quadVertices[0].TextureCoordinate = ((float)gd.TexCoords.Left / tex.Width, (float)gd.TexCoords.Bottom / tex.Height);
|
||||
quadVertices[0].Color = color;
|
||||
quadVertices[0].TextureCoordinate = new Vector2(left, bottom);
|
||||
|
||||
quadVertices[1].Position = new Vector3(currentPos + gd.DrawOffset + (topItalicOffset, 0.0f), 0.0f);
|
||||
quadVertices[1].TextureCoordinate = ((float)gd.TexCoords.Left / tex.Width, (float)gd.TexCoords.Top / tex.Height);
|
||||
quadVertices[1].Color = color;
|
||||
quadVertices[1].TextureCoordinate = new Vector2(left, top);
|
||||
|
||||
quadVertices[2].Position = new Vector3(currentPos + gd.DrawOffset + (gd.TexCoords.Width + bottomItalicOffset, gd.TexCoords.Height), 0.0f);
|
||||
quadVertices[2].TextureCoordinate = ((float)gd.TexCoords.Right / tex.Width, (float)gd.TexCoords.Bottom / tex.Height);
|
||||
quadVertices[2].Color = color;
|
||||
quadVertices[2].TextureCoordinate = new Vector2(right, bottom);
|
||||
|
||||
quadVertices[3].Position = new Vector3(currentPos + gd.DrawOffset + (gd.TexCoords.Width + topItalicOffset, 0.0f), 0.0f);
|
||||
quadVertices[3].TextureCoordinate = ((float)gd.TexCoords.Right / tex.Width, (float)gd.TexCoords.Top / tex.Height);
|
||||
quadVertices[3].Color = color;
|
||||
quadVertices[3].TextureCoordinate = new Vector2(right, top);
|
||||
|
||||
sb.Draw(tex, quadVertices, 0.0f);
|
||||
}
|
||||
|
||||
@@ -22,13 +22,36 @@ namespace Barotrauma
|
||||
private bool _toggleOpen = true;
|
||||
public bool ToggleOpen
|
||||
{
|
||||
get { return _toggleOpen; }
|
||||
set
|
||||
{
|
||||
_toggleOpen = PreferChatBoxOpen = value;
|
||||
if (value) { hideableElements.Visible = true; }
|
||||
}
|
||||
get => _toggleOpen;
|
||||
set => SetToggleOpenState(value, setPreference: true);
|
||||
}
|
||||
|
||||
public static ChatBox GetChatBox()
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is not GameMode gameMode) { return null; }
|
||||
return gameMode.IsSinglePlayer ? GameMain.GameSession.CrewManager?.ChatBox : GameMain.Client?.ChatBox;
|
||||
}
|
||||
|
||||
public static void AutoHideChatBox() => SetChatBoxOpen(false);
|
||||
|
||||
private void SetToggleOpenState(bool value, bool setPreference = true)
|
||||
{
|
||||
_toggleOpen = value;
|
||||
if (setPreference)
|
||||
{
|
||||
PreferChatBoxOpen = value;
|
||||
}
|
||||
if (value) { hideableElements.Visible = true; }
|
||||
}
|
||||
|
||||
public static void ResetChatBoxOpenState() => GetChatBox()?.ResetOpenState();
|
||||
|
||||
public void ResetOpenState() => SetOpen(PreferChatBoxOpen);
|
||||
|
||||
private static void SetChatBoxOpen(bool isOpen) => GetChatBox()?.SetOpen(isOpen);
|
||||
|
||||
private void SetOpen(bool value) => SetToggleOpenState(value, setPreference: false);
|
||||
|
||||
private float openState;
|
||||
|
||||
public static bool PreferChatBoxOpen = true;
|
||||
@@ -199,7 +222,7 @@ namespace Barotrauma
|
||||
if (channelMemPending)
|
||||
{
|
||||
int.TryParse(channelText.Text, out int newChannel);
|
||||
radio.SetChannelMemory(index, newChannel);
|
||||
SetChannelMemory(index, newChannel);
|
||||
btn.ToolTip = TextManager.GetWithVariables("radiochannelpreset",
|
||||
("[index]", index.ToString()),
|
||||
("[channel]", radio.GetChannelMemory(index).ToString()));
|
||||
@@ -330,7 +353,7 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
showNewMessagesButton.Visible = false;
|
||||
ToggleOpen = PreferChatBoxOpen = GameSettings.CurrentConfig.ChatOpen;
|
||||
SetToggleOpenState(GameSettings.CurrentConfig.ChatOpen, setPreference: true);
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
@@ -797,6 +820,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void SetChannelMemory(int index, int channel)
|
||||
{
|
||||
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
|
||||
{
|
||||
radio.SetChannelMemory(index, channel);
|
||||
radio.Item.CreateClientEvent(radio);
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplySelectionInputs() => ApplySelectionInputs(InputBox, true, ChatKeyStates.GetChatKeyStates());
|
||||
|
||||
public struct ChatKeyStates
|
||||
|
||||
@@ -11,13 +11,14 @@ internal class DeathPrompt
|
||||
{
|
||||
private static CoroutineHandle? createPromptCoroutine;
|
||||
|
||||
private GUIFrame? deathPromptFrame;
|
||||
private GUIComponent? skillPanel;
|
||||
private GUIComponent? newCharacterPanel;
|
||||
private GUIComponent? takeOverBotPanel;
|
||||
|
||||
private GUIComponent? content;
|
||||
|
||||
public static GUIComponent? takeOverBotPanelFrame;
|
||||
private static GUIComponent? takeOverBotPanelFrame;
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor, because these should only be created using the Show method
|
||||
@@ -58,7 +59,7 @@ internal class DeathPrompt
|
||||
const float FadeInDuration = 1.0f;
|
||||
|
||||
bool permadeath = GameMain.NetworkMember is { ServerSettings.RespawnMode: RespawnMode.Permadeath };
|
||||
bool ironman = GameMain.NetworkMember is { ServerSettings: { RespawnMode: RespawnMode.Permadeath, IronmanMode: true } };
|
||||
bool ironman = GameMain.NetworkMember is { ServerSettings.IronmanModeActive: true };
|
||||
|
||||
var background = new GUICustomComponent(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), onDraw: DrawBackground)
|
||||
{
|
||||
@@ -73,11 +74,11 @@ internal class DeathPrompt
|
||||
foreground.FadeIn(wait: 0, duration: 5.0f);
|
||||
foreground.Pulsate(startScale: Vector2.One, Vector2.One * 0.8f, duration: 25.0f);
|
||||
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.3f), background.RectTransform, Anchor.Center))
|
||||
deathPromptFrame = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.3f), background.RectTransform, Anchor.Center))
|
||||
{
|
||||
UserData = this
|
||||
};
|
||||
frame.FadeIn(wait: 0, duration: FadeInDuration);
|
||||
deathPromptFrame.FadeIn(wait: 0, duration: FadeInDuration);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.1f), background.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.2f) }, string.Empty, font: GUIStyle.LargeFont, textAlignment: Alignment.TopCenter)
|
||||
{
|
||||
@@ -90,7 +91,7 @@ internal class DeathPrompt
|
||||
}
|
||||
};
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), frame.RectTransform, Anchor.Center))
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), deathPromptFrame.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
@@ -188,7 +189,7 @@ internal class DeathPrompt
|
||||
{
|
||||
if (takeOverBotPanel == null)
|
||||
{
|
||||
CreateTakeOverBotPanel(frame, this);
|
||||
CreateTakeOverBotPanel(deathPromptFrame, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -202,7 +203,7 @@ internal class DeathPrompt
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), buttonContainerRight.RectTransform), TextManager.Get("deathprompt.respawnnow"))
|
||||
var respawnNowButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), buttonContainerRight.RectTransform), TextManager.Get("deathprompt.respawnnow"))
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
@@ -211,7 +212,13 @@ internal class DeathPrompt
|
||||
return true;
|
||||
},
|
||||
Enabled = GameMain.NetworkMember is { ServerSettings.RespawnMode: RespawnMode.MidRound }
|
||||
}.FadeIn(wait: FadeInInterval * 4, duration: FadeInDuration, alsoChildren: true);
|
||||
};
|
||||
if (GameMain.NetworkMember is { ServerSettings.RespawnMode: RespawnMode.BetweenRounds })
|
||||
{
|
||||
respawnNowButton.ToolTip = TextManager.Get("respawnnotavailable.respawnmode.betweenrounds");
|
||||
}
|
||||
|
||||
respawnNowButton.FadeIn(wait: FadeInInterval * 4, duration: FadeInDuration, alsoChildren: true);
|
||||
}
|
||||
|
||||
//"info buttons" at the bottom
|
||||
@@ -249,7 +256,7 @@ internal class DeathPrompt
|
||||
{
|
||||
if (skillPanel == null)
|
||||
{
|
||||
CreateSkillPanel(frame, GameMain.Client?.Character?.Info ?? GameMain.Client?.CharacterInfo);
|
||||
CreateSkillPanel(deathPromptFrame, GameMain.Client?.Character?.Info ?? GameMain.Client?.CharacterInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -266,7 +273,7 @@ internal class DeathPrompt
|
||||
{
|
||||
if (newCharacterPanel == null)
|
||||
{
|
||||
CreateNewCharacterPanel(frame);
|
||||
CreateNewCharacterPanel(deathPromptFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -279,15 +286,6 @@ internal class DeathPrompt
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
/*new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), infoButtonContainer.RectTransform), "Respawn settings", style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}.FadeIn(wait: FadeInInterval * 5, duration: FadeInDuration, alsoChildren: true);*/
|
||||
|
||||
this.content = background;
|
||||
}
|
||||
|
||||
@@ -384,6 +382,8 @@ internal class DeathPrompt
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(onYes: () =>
|
||||
{
|
||||
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
|
||||
GameMain.NetLobbyScreen.CampaignCharacterDiscarded = false;
|
||||
frame.Parent?.RemoveChild(frame);
|
||||
newCharacterPanel = null;
|
||||
});
|
||||
@@ -465,6 +465,11 @@ internal class DeathPrompt
|
||||
{
|
||||
if (botList.SelectedData is CharacterInfo selectedCharacter && GameMain.Client is GameClient client)
|
||||
{
|
||||
if (!GetAvailableBots().Contains(selectedCharacter)) // Someone may have taken over the bot while the list was open, etc
|
||||
{
|
||||
CreateTakeOverBotPanel(frame, deathPrompt); // Update
|
||||
return true;
|
||||
}
|
||||
client.SendTakeOverBotRequest(selectedCharacter);
|
||||
GUIMessageBox.MessageBoxes.Remove(frame.Parent);
|
||||
deathPrompt?.Close();
|
||||
@@ -484,15 +489,26 @@ internal class DeathPrompt
|
||||
return frame;
|
||||
}
|
||||
|
||||
public void UpdateBotList()
|
||||
{
|
||||
if (deathPromptFrame != null && takeOverBotPanelFrame != null)
|
||||
{
|
||||
CloseBotPanel();
|
||||
CreateTakeOverBotPanel(deathPromptFrame, deathPrompt: this);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<CharacterInfo> GetAvailableBots()
|
||||
{
|
||||
if (GameMain.GameSession?.CrewManager is { } crewManager)
|
||||
{
|
||||
return crewManager.GetCharacterInfos().Where(c =>
|
||||
/*either an alive bot */
|
||||
c is { Character.IsBot: true, Character.IsDead: false } ||
|
||||
/* or a newly hired bot that hasn't spawned yet */
|
||||
(c.IsNewHire && c.Character == null));
|
||||
return crewManager.GetCharacterInfos(includeReserveBench: true).Where(c =>
|
||||
// a bot on reserve bench
|
||||
c.IsOnReserveBench ||
|
||||
// an alive bot
|
||||
(c.Character != null && c.Character is { IsBot: true, IsDead: false }) ||
|
||||
// a newly hired bot that hasn't spawned yet
|
||||
(c.Character == null && c.IsNewHire));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -84,16 +83,31 @@ namespace Barotrauma
|
||||
{
|
||||
currentDirectory += "/";
|
||||
}
|
||||
fileSystemWatcher?.Dispose();
|
||||
fileSystemWatcher = new System.IO.FileSystemWatcher(currentDirectory)
|
||||
try
|
||||
{
|
||||
Filter = "*",
|
||||
NotifyFilter = System.IO.NotifyFilters.LastWrite | System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName
|
||||
};
|
||||
fileSystemWatcher.Created += OnFileSystemChanges;
|
||||
fileSystemWatcher.Deleted += OnFileSystemChanges;
|
||||
fileSystemWatcher.Renamed += OnFileSystemChanges;
|
||||
fileSystemWatcher.EnableRaisingEvents = true;
|
||||
fileSystemWatcher?.Dispose();
|
||||
fileSystemWatcher = new System.IO.FileSystemWatcher(currentDirectory)
|
||||
{
|
||||
Filter = "*",
|
||||
NotifyFilter = System.IO.NotifyFilters.LastWrite | System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName
|
||||
};
|
||||
fileSystemWatcher.Created += OnFileSystemChanges;
|
||||
fileSystemWatcher.Deleted += OnFileSystemChanges;
|
||||
fileSystemWatcher.Renamed += OnFileSystemChanges;
|
||||
fileSystemWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
catch (System.IO.FileNotFoundException exception)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the current directory, possibly due to insufficient access permissions.", exception);
|
||||
}
|
||||
catch (ArgumentException exception)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the current directory, possibly because it was deleted.", exception);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the current directory for an unknown reason.", exception);
|
||||
}
|
||||
RefreshFileList();
|
||||
}
|
||||
}
|
||||
@@ -218,6 +232,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (Directory.Exists(txt))
|
||||
{
|
||||
var attributes = System.IO.File.GetAttributes(txt);
|
||||
if (attributes.HasAnyFlag(System.IO.FileAttributes.System) || attributes.HasAnyFlag(System.IO.FileAttributes.Hidden))
|
||||
{
|
||||
// System and hidden folders should be filtered out when populating the options, but the user can still write or copy-paste the path in the text field,
|
||||
// which will throw a file not found exception when the file system watcher starts. Therefore, this extra check.
|
||||
tb.Text = CurrentDirectory;
|
||||
return false;
|
||||
}
|
||||
CurrentDirectory = txt;
|
||||
return true;
|
||||
}
|
||||
@@ -354,20 +376,19 @@ namespace Barotrauma
|
||||
var directories = Directory.EnumerateDirectories(currentDirectory, "*" + filterBox!.Text + "*");
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
string txt = directory;
|
||||
if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
|
||||
if (!txt.EndsWith("/")) { txt += "/"; }
|
||||
//get directory info
|
||||
DirectoryInfo dirInfo = new DirectoryInfo(directory);
|
||||
try
|
||||
{
|
||||
//this will throw an exception if the directory can't be opened
|
||||
Directory.GetDirectories(directory);
|
||||
//this will intentionally throw an exception if the directory can't be opened
|
||||
System.IO.Directory.GetDirectories(directory);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
// Skip the folders that can't be accessed.
|
||||
continue;
|
||||
}
|
||||
string txt = directory;
|
||||
if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
|
||||
if (!txt.EndsWith("/")) { txt += "/"; }
|
||||
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
|
||||
{
|
||||
UserData = ItemIsDirectory.Yes
|
||||
|
||||
@@ -106,6 +106,27 @@ namespace Barotrauma
|
||||
public static float VerticalAspectRatio => GameMain.GraphicsHeight / (float)GameMain.GraphicsWidth;
|
||||
public static float RelativeHorizontalAspectRatio => HorizontalAspectRatio / (ReferenceResolution.X / ReferenceResolution.Y);
|
||||
public static float RelativeVerticalAspectRatio => VerticalAspectRatio / (ReferenceResolution.Y / ReferenceResolution.X);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the difference of the current aspect ratio to the reference aspect ratio (16:9).
|
||||
/// E.g. if the aspect ratio is 16:9, returns 0; if it's 4:3, returns 0.444; if the aspect ratio is 12:5, returns -0.623.
|
||||
/// </summary>
|
||||
public static float AspectRatioDifference
|
||||
{
|
||||
get
|
||||
{
|
||||
// ~ 1.777
|
||||
float referenceAspectRatio = ReferenceResolution.X / ReferenceResolution.Y;
|
||||
float aspectRatioDifference = referenceAspectRatio - HorizontalAspectRatio;
|
||||
if (MathUtils.NearlyEqual(aspectRatioDifference, 0))
|
||||
{
|
||||
// Handle possible rounding errors, so that we can trust that this returns 0 when the aspect ratio matches the reference aspect ratio.
|
||||
return 0;
|
||||
}
|
||||
return aspectRatioDifference;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A horizontal scaling factor for low aspect ratios (small width relative to height)
|
||||
/// </summary>
|
||||
@@ -113,6 +134,8 @@ namespace Barotrauma
|
||||
|
||||
public static bool IsUltrawide => HorizontalAspectRatio > 2.3f;
|
||||
|
||||
public static bool IsHUDScaled => GameSettings.CurrentConfig.Graphics.HUDScale > 1 || GameSettings.CurrentConfig.Graphics.InventoryScale > 1;
|
||||
|
||||
public static int UIWidth
|
||||
{
|
||||
get
|
||||
@@ -469,7 +492,7 @@ namespace Barotrauma
|
||||
"Loaded sounds: " + GameMain.SoundManager.LoadedSoundCount + " (" + GameMain.SoundManager.UniqueLoadedSoundCount + " unique)", Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
|
||||
soundTextY += yStep;
|
||||
|
||||
for (int i = 0; i < SoundManager.SOURCE_COUNT; i++)
|
||||
for (int i = 0; i < SoundManager.SourceCount; i++)
|
||||
{
|
||||
Color clr = Color.White;
|
||||
string soundStr = i + ": ";
|
||||
@@ -625,9 +648,13 @@ namespace Barotrauma
|
||||
|
||||
DrawMessages(spriteBatch, cam);
|
||||
|
||||
if (MouseOn != null && !MouseOn.ToolTip.IsNullOrWhiteSpace())
|
||||
if (MouseOn != null)
|
||||
{
|
||||
MouseOn.DrawToolTip(spriteBatch);
|
||||
if (!MouseOn.ToolTip.IsNullOrWhiteSpace())
|
||||
{
|
||||
MouseOn.DrawToolTip(spriteBatch);
|
||||
}
|
||||
MouseOn.OnDrawToolTip?.Invoke(MouseOn);
|
||||
}
|
||||
|
||||
if (SubEditorScreen.IsSubEditor())
|
||||
@@ -1546,7 +1573,7 @@ namespace Barotrauma
|
||||
private static readonly VertexPositionColorTexture[] donutVerts = new VertexPositionColorTexture[DonutSegments * 4];
|
||||
|
||||
public static void DrawDonutSection(
|
||||
SpriteBatch sb, Vector2 center, Range<float> radii, float sectionRad, Color clr, float depth = 0.0f)
|
||||
SpriteBatch sb, Vector2 center, Range<float> radii, float sectionRad, Color clr, float depth = 0.0f, float rotationRad = 0.0f)
|
||||
{
|
||||
float getRadius(int vertexIndex)
|
||||
=> (vertexIndex % 4) switch
|
||||
@@ -1589,7 +1616,7 @@ namespace Barotrauma
|
||||
for (int vertexIndex = 0; vertexIndex < maxDirectionIndex * 4; vertexIndex++)
|
||||
{
|
||||
donutVerts[vertexIndex].Color = clr;
|
||||
donutVerts[vertexIndex].Position = new Vector3(center + getDirection(vertexIndex) * getRadius(vertexIndex), 0.0f);
|
||||
donutVerts[vertexIndex].Position = new Vector3(center + Vector2.Transform(getDirection(vertexIndex) * getRadius(vertexIndex), Matrix.CreateRotationZ(rotationRad)), 0.0f);
|
||||
}
|
||||
sb.Draw(solidWhiteTexture, donutVerts, depth, count: maxDirectionIndex);
|
||||
}
|
||||
@@ -1856,9 +1883,16 @@ namespace Barotrauma
|
||||
Vector2 pos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) - new Vector2(HUDLayoutSettings.Padding) - 2 * Scale * sheet.FrameSize.ToVector2();
|
||||
sheet.Draw(spriteBatch, (int)Math.Floor(savingIndicatorSpriteIndex), pos, savingIndicatorColor, origin: Vector2.Zero, rotate: 0.0f, scale: new Vector2(Scale));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Element creation
|
||||
public static void DrawCapsule(SpriteBatch sb, Vector2 origin, float length, float radius, float rotation, Color clr, float depth = 0, float thickness = 1)
|
||||
{
|
||||
DrawDonutSection(sb, origin + Vector2.Transform(-new Vector2(length / 2, 0), Matrix.CreateRotationZ(rotation)), new Range<float>(radius - thickness / 2, radius + thickness / 2), MathHelper.Pi, clr, depth, rotation - MathHelper.Pi);
|
||||
DrawRectangle(sb, origin, new Vector2(length, radius * 2), new Vector2(length / 2, radius), rotation, clr, depth, thickness);
|
||||
DrawDonutSection(sb, origin + Vector2.Transform(new Vector2(length / 2, 0), Matrix.CreateRotationZ(rotation)), new Range<float>(radius - thickness / 2, radius + thickness / 2), MathHelper.Pi, clr, depth, rotation);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Element creation
|
||||
|
||||
public static Texture2D CreateCircle(int radius, bool filled = false)
|
||||
{
|
||||
@@ -2488,7 +2522,12 @@ namespace Barotrauma
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
ToolTip = TextManager.Get("bugreportbutton") + $" (v{GameMain.Version})",
|
||||
OnClicked = (btn, userdata) => { GameMain.Instance.ShowBugReporter(); return true; }
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (PauseMenuOpen) { TogglePauseMenu(); }
|
||||
GameMain.Instance.ShowBugReporter();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CreateButton("PauseMenuResume", buttonContainer, null);
|
||||
@@ -2524,23 +2563,37 @@ namespace Barotrauma
|
||||
GameMain.GameSession?.EndRound("");
|
||||
});
|
||||
}
|
||||
else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ManageRound))
|
||||
else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null)
|
||||
{
|
||||
bool canSave = GameMain.GameSession.GameMode is CampaignMode && IsFriendlyOutpostLevel();
|
||||
if (canSave)
|
||||
//server owner (host) can't return to the lobby without ending the round for everyone
|
||||
if (!GameMain.Client.IsServerOwner)
|
||||
{
|
||||
CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToServerLobbyVerification", action: () =>
|
||||
{
|
||||
GameMain.Client?.RequestRoundEnd(save: true);
|
||||
});
|
||||
CreateButton("ReturnToServerlobby", buttonContainer,
|
||||
verificationTextTag: "PauseMenuReturnToServerLobbyVerificationSelf",
|
||||
action: () =>
|
||||
{
|
||||
GameMain.Client?.EndRoundForSelf();
|
||||
});
|
||||
}
|
||||
|
||||
CreateButton(GameMain.GameSession.GameMode is CampaignMode ? "ReturnToServerlobby" : "EndRound", buttonContainer,
|
||||
verificationTextTag: GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
|
||||
action: () =>
|
||||
if (GameMain.Client.HasPermission(ClientPermissions.ManageRound))
|
||||
{
|
||||
bool canSave = GameMain.GameSession.GameMode is CampaignMode && IsFriendlyOutpostLevel();
|
||||
if (canSave)
|
||||
{
|
||||
GameMain.Client?.RequestRoundEnd(save: false);
|
||||
});
|
||||
CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToServerLobbyVerification", action: () =>
|
||||
{
|
||||
GameMain.Client?.RequestEndRound(save: true);
|
||||
}, color: GUIStyle.Red);
|
||||
}
|
||||
|
||||
CreateButton("EndRound", buttonContainer,
|
||||
verificationTextTag: GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
|
||||
action: () =>
|
||||
{
|
||||
GameMain.Client?.RequestEndRound(save: false);
|
||||
}, color: GUIStyle.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2568,9 +2621,9 @@ namespace Barotrauma
|
||||
|
||||
}
|
||||
|
||||
void CreateButton(string textTag, GUIComponent parent, Action action, string verificationTextTag = null)
|
||||
void CreateButton(string textTag, GUIComponent parent, Action action, string verificationTextTag = null, Color? color = null)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), TextManager.Get(textTag))
|
||||
var button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), TextManager.Get(textTag))
|
||||
{
|
||||
OnClicked = (btn, userData) =>
|
||||
{
|
||||
@@ -2586,25 +2639,29 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
};
|
||||
if (color.HasValue)
|
||||
{
|
||||
button.Color = color.Value;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateVerificationPrompt(string textTag, Action confirmAction)
|
||||
}
|
||||
public static void CreateVerificationPrompt(string textTag, Action confirmAction)
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", TextManager.Get(textTag),
|
||||
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") })
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", TextManager.Get(textTag),
|
||||
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") })
|
||||
{
|
||||
UserData = "verificationprompt",
|
||||
DrawOnTop = true
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (_, __) =>
|
||||
{
|
||||
PauseMenuOpen = false;
|
||||
confirmAction?.Invoke();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
||||
}
|
||||
UserData = "verificationprompt",
|
||||
DrawOnTop = true
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (_, __) =>
|
||||
{
|
||||
PauseMenuOpen = false;
|
||||
confirmAction?.Invoke();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
||||
}
|
||||
|
||||
private static bool TogglePauseMenu(GUIButton button, object obj)
|
||||
@@ -2690,12 +2747,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsFourByThree()
|
||||
{
|
||||
float aspectRatio = HorizontalAspectRatio;
|
||||
return aspectRatio > 1.3f && aspectRatio < 1.4f;
|
||||
}
|
||||
|
||||
public static void SetSavingIndicatorState(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
|
||||
@@ -159,6 +159,34 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private GUIComponent holdOverlay;
|
||||
|
||||
private bool requireHold;
|
||||
public bool RequireHold
|
||||
{
|
||||
get => requireHold;
|
||||
set
|
||||
{
|
||||
requireHold = value;
|
||||
if (value)
|
||||
{
|
||||
holdOverlay ??= new GUIFrame(new RectTransform(new Vector2(0.5f, 1f), Frame.RectTransform, Anchor.CenterLeft), style: null)
|
||||
{
|
||||
Color = GUIStyle.Yellow * 0.33f,
|
||||
CanBeFocused = false,
|
||||
IgnoreLayoutGroups = true,
|
||||
Visible = true
|
||||
};
|
||||
}
|
||||
else if (holdOverlay != null)
|
||||
{
|
||||
holdOverlay.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public float HoldDurationSeconds { get; set; } = 5f;
|
||||
private float holdTimer;
|
||||
|
||||
public bool Pulse { get; set; }
|
||||
private float pulseTimer;
|
||||
private float pulseExpand;
|
||||
@@ -240,6 +268,11 @@ namespace Barotrauma
|
||||
}
|
||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
if (RequireHold)
|
||||
{
|
||||
holdTimer += deltaTime;
|
||||
}
|
||||
|
||||
if (OnPressed != null)
|
||||
{
|
||||
if (OnPressed())
|
||||
@@ -254,25 +287,34 @@ namespace Barotrauma
|
||||
}
|
||||
else if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
if (PlaySoundOnSelect)
|
||||
if (!RequireHold || holdTimer > HoldDurationSeconds)
|
||||
{
|
||||
SoundPlayer.PlayUISound(ClickSound);
|
||||
}
|
||||
if (OnClicked != null)
|
||||
{
|
||||
if (OnClicked(this, UserData))
|
||||
if (PlaySoundOnSelect)
|
||||
{
|
||||
State = ComponentState.Selected;
|
||||
SoundPlayer.PlayUISound(ClickSound);
|
||||
}
|
||||
|
||||
if (OnClicked != null)
|
||||
{
|
||||
if (OnClicked(this, UserData))
|
||||
{
|
||||
State = ComponentState.Selected;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Selected = !Selected;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Selected = !Selected;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
holdTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
holdTimer = 0.0f;
|
||||
if (!ExternalHighlight)
|
||||
{
|
||||
State = Selected ? ComponentState.Selected : ComponentState.None;
|
||||
@@ -283,6 +325,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (RequireHold)
|
||||
{
|
||||
float width = MathHelper.Clamp(holdTimer / HoldDurationSeconds, 0f, 1f);
|
||||
if (!MathUtils.NearlyEqual(width, holdOverlay.RectTransform.RelativeSize.X))
|
||||
{
|
||||
holdOverlay.RectTransform.RelativeSize = new Vector2(width, 1f);
|
||||
}
|
||||
|
||||
holdOverlay.Color =
|
||||
holdTimer >= HoldDurationSeconds
|
||||
? Color.Green * 0.33f
|
||||
: Color.Red * 0.33f;
|
||||
}
|
||||
|
||||
foreach (GUIComponent child in Children)
|
||||
{
|
||||
child.State = State;
|
||||
|
||||
@@ -9,6 +9,7 @@ using Barotrauma.IO;
|
||||
using RestSharp;
|
||||
using System.Net;
|
||||
using Barotrauma.Steam;
|
||||
using Steamworks;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -158,6 +159,12 @@ namespace Barotrauma
|
||||
|
||||
public Action<GUIComponent> OnAddedToGUIUpdateList;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers when a tooltip should be draw on the component.
|
||||
/// Note that the callback triggers even if the item has no tooltip (which can be useful for e.g. only contructing the tooltip when needed).
|
||||
/// </summary>
|
||||
public Action<GUIComponent> OnDrawToolTip;
|
||||
|
||||
public enum ComponentState { None, Hover, Pressed, Selected, HoverSelected };
|
||||
|
||||
protected Alignment alignment;
|
||||
@@ -247,6 +254,57 @@ namespace Barotrauma
|
||||
get { return new Vector2(Rect.Center.X, Rect.Center.Y); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the component's rect position to the specified area. Does not resize the component.
|
||||
/// </summary>
|
||||
/// <param name="clampArea">Area to contain the Rect of this component to</param>
|
||||
public void ClampToArea(Rectangle clampArea)
|
||||
{
|
||||
Rectangle componentRect = Rect;
|
||||
|
||||
int x = componentRect.X;
|
||||
int y = componentRect.Y;
|
||||
|
||||
// Adjust the X position
|
||||
if (componentRect.Width <= clampArea.Width)
|
||||
{
|
||||
if (componentRect.Left < clampArea.Left)
|
||||
{
|
||||
x = clampArea.Left;
|
||||
}
|
||||
else if (componentRect.Right > clampArea.Right)
|
||||
{
|
||||
x = clampArea.Right - componentRect.Width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Component is wider than clamp area, osition it to overlap as much as possible
|
||||
x = clampArea.Left - (componentRect.Width - clampArea.Width) / 2;
|
||||
}
|
||||
|
||||
// Adjust the Y position
|
||||
if (componentRect.Height <= clampArea.Height)
|
||||
{
|
||||
if (componentRect.Top < clampArea.Top)
|
||||
{
|
||||
y = clampArea.Top;
|
||||
}
|
||||
else if (componentRect.Bottom > clampArea.Bottom)
|
||||
{
|
||||
y = clampArea.Bottom - componentRect.Height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Component is taller than clamp area, osition it to overlap as much as possible
|
||||
y = clampArea.Top - (componentRect.Height - clampArea.Height) / 2;
|
||||
}
|
||||
|
||||
Point moveAmount = new Point(x - componentRect.X, y - componentRect.Y);
|
||||
RectTransform.ScreenSpaceOffset += moveAmount;
|
||||
}
|
||||
|
||||
protected Rectangle ClampRect(Rectangle r)
|
||||
{
|
||||
if (Parent is null) { return r; }
|
||||
@@ -1087,6 +1145,8 @@ namespace Barotrauma
|
||||
FromXML(subElement, component is GUIListBox listBox ? listBox.Content.RectTransform : component.RectTransform);
|
||||
}
|
||||
|
||||
component.toolTip = element.GetAttributeString("tooltip", string.Empty);
|
||||
|
||||
if (element.GetAttributeBool("resizetofitchildren", false))
|
||||
{
|
||||
Vector2 relativeResizeScale = element.GetAttributeVector2("relativeresizescale", Vector2.One);
|
||||
@@ -1129,7 +1189,8 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (XAttribute attribute in element.Attributes())
|
||||
{
|
||||
switch (attribute.Name.ToString().ToLowerInvariant())
|
||||
string conditionName = attribute.Name.ToString().ToLowerInvariant();
|
||||
switch (conditionName)
|
||||
{
|
||||
case "language":
|
||||
var languages = element.GetAttributeIdentifierArray(attribute.Name.ToString(), Array.Empty<Identifier>())
|
||||
@@ -1171,6 +1232,20 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
case "mingamelaunches":
|
||||
if (int.TryParse(attribute.Value, out int minLaunches))
|
||||
{
|
||||
return SteamManager.GetStatInt(AchievementStat.GameLaunchCount) > minLaunches;
|
||||
}
|
||||
return false;
|
||||
case "appsubscribed":
|
||||
case "appnotsubscribed":
|
||||
if (SteamManager.IsInitialized &&
|
||||
int.TryParse(attribute.Value, out int appId))
|
||||
{
|
||||
return SteamApps.IsSubscribedToApp(appId) == (conditionName == "appsubscribed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,21 @@ namespace Barotrauma
|
||||
{
|
||||
public class GUIDropDown : GUIComponent, IKeyboardSubscriber
|
||||
{
|
||||
/// <param name="selected">The component that was selected from the dropdown.</param>
|
||||
/// <param name="obj"><see cref="GUIComponent.UserData"/> of the component selected from the dropdown.</param>
|
||||
public delegate bool OnSelectedHandler(GUIComponent selected, object obj = null);
|
||||
/// <summary>
|
||||
/// Triggers when some item is cliecked from the dropdown.
|
||||
/// Note that <see cref="SelectedData"/> is not set yet when this callback triggers, and returning false from the callback disallows selecting it.
|
||||
/// If you want to access the new value, use the obj argument.
|
||||
/// </summary>
|
||||
public OnSelectedHandler OnSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers after an item has been selected from the dropdown, all validation has been done and the new value has been set.
|
||||
/// </summary>
|
||||
public OnSelectedHandler AfterSelected;
|
||||
|
||||
public OnSelectedHandler OnDropped;
|
||||
|
||||
private readonly GUIButton button;
|
||||
@@ -166,7 +179,7 @@ namespace Barotrauma
|
||||
|
||||
public Vector4 Padding => button.TextBlock.Padding;
|
||||
|
||||
public GUIDropDown(RectTransform rectT, LocalizedString text = null, int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false, Alignment textAlignment = Alignment.CenterLeft) : base(style, rectT)
|
||||
public GUIDropDown(RectTransform rectT, LocalizedString text = null, int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false, Alignment textAlignment = Alignment.CenterLeft, float listBoxScale = 1) : base(style, rectT)
|
||||
{
|
||||
text ??= LocalizedString.EmptyString;
|
||||
|
||||
@@ -185,13 +198,21 @@ namespace Barotrauma
|
||||
|
||||
Anchor listAnchor = dropAbove ? Anchor.TopCenter : Anchor.BottomCenter;
|
||||
Pivot listPivot = dropAbove ? Pivot.BottomCenter : Pivot.TopCenter;
|
||||
listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot)
|
||||
listBox = new GUIListBox(new RectTransform(new Point((int)(Rect.Width * listBoxScale), Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot)
|
||||
{ IsFixedSize = false }, style: null)
|
||||
{
|
||||
Enabled = !selectMultiple,
|
||||
PlaySoundOnSelect = true,
|
||||
};
|
||||
if (!selectMultiple) { listBox.OnSelected = SelectItem; }
|
||||
if (!selectMultiple)
|
||||
{
|
||||
listBox.AfterSelected = (component, obj) =>
|
||||
{
|
||||
SelectItem(component, obj);
|
||||
AfterSelected?.Invoke(component, obj);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
GUIStyle.Apply(listBox, "GUIListBox", this);
|
||||
GUIStyle.Apply(listBox.ContentBackground, "GUIListBox", this);
|
||||
|
||||
@@ -199,6 +220,8 @@ namespace Barotrauma
|
||||
{
|
||||
icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), button.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5, 0) }, null, scaleToFit: true);
|
||||
icon.ApplyStyle(button.Style.ChildStyles["dropdownicon".ToIdentifier()]);
|
||||
//move the text away from the icon
|
||||
button.TextBlock.Padding += new Vector4(0, 0, icon.Rect.Width, 0);
|
||||
}
|
||||
|
||||
currentHighestParent = FindHighestParent();
|
||||
@@ -249,12 +272,12 @@ namespace Barotrauma
|
||||
toolTip ??= "";
|
||||
if (selectMultiple)
|
||||
{
|
||||
var frame = new GUIFrame(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, style: "ListBoxElement", color: color)
|
||||
var frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, style: "ListBoxElement", color: color)
|
||||
{
|
||||
UserData = userData,
|
||||
ToolTip = toolTip
|
||||
};
|
||||
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.8f), frame.RectTransform, anchor: Anchor.CenterLeft) { MaxSize = new Point(int.MaxValue, (int)(button.Rect.Height * 0.8f)) }, text)
|
||||
var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.8f), frame.RectTransform, anchor: Anchor.CenterLeft) { MaxSize = new Point(int.MaxValue, (int)(button.Rect.Height * 0.8f)) }, text)
|
||||
{
|
||||
UserData = userData,
|
||||
ToolTip = toolTip,
|
||||
@@ -266,6 +289,11 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OnSelected != null && !OnSelected.Invoke(tb.Parent, tb.Parent.UserData))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<LocalizedString> texts = new List<LocalizedString>();
|
||||
selectedDataMultiple.Clear();
|
||||
selectedIndexMultiple.Clear();
|
||||
@@ -282,8 +310,7 @@ namespace Barotrauma
|
||||
i++;
|
||||
}
|
||||
button.Text = LocalizedString.Join(", ", texts);
|
||||
// TODO: The callback is called at least twice, remove this?
|
||||
OnSelected?.Invoke(tb.Parent, tb.Parent.UserData);
|
||||
AfterSelected?.Invoke(tb.Parent, SelectedData);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -291,7 +318,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
return new GUITextBlock(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, text, style: "ListBoxElement", color: color, textColor: textColor)
|
||||
return new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, text, style: "ListBoxElement", color: color, textColor: textColor)
|
||||
{
|
||||
UserData = userData,
|
||||
ToolTip = toolTip
|
||||
@@ -328,9 +355,8 @@ namespace Barotrauma
|
||||
}
|
||||
button.Text = textBlock?.Text ?? "";
|
||||
}
|
||||
OnSelected?.Invoke(component, obj);
|
||||
Dropped = false;
|
||||
// TODO: OnSelected can be called multiple times and when it shouldn't be called -> turn into an event so that nobody else can call it.
|
||||
OnSelected?.Invoke(component, component.UserData);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -344,6 +370,7 @@ namespace Barotrauma
|
||||
{
|
||||
listBox.Select(userData);
|
||||
}
|
||||
AfterSelected?.Invoke(SelectedComponent, SelectedData);
|
||||
}
|
||||
|
||||
public void Select(int index)
|
||||
@@ -360,6 +387,7 @@ namespace Barotrauma
|
||||
{
|
||||
listBox.Select(index);
|
||||
}
|
||||
AfterSelected?.Invoke(this, SelectedData);
|
||||
}
|
||||
|
||||
private bool wasOpened;
|
||||
|
||||
@@ -14,8 +14,17 @@ namespace Barotrauma
|
||||
protected List<GUIComponent> selected;
|
||||
|
||||
public delegate bool OnSelectedHandler(GUIComponent component, object obj);
|
||||
/// <summary>
|
||||
/// Triggers when some element is clicked on the listbox.
|
||||
/// Note that <see cref="SelectedData"/> is not set yet when this callback triggers, and returning false from the callback disallows selecting it.
|
||||
/// </summary>
|
||||
public OnSelectedHandler OnSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers after some element has been selected from the listbox.
|
||||
/// </summary>
|
||||
public OnSelectedHandler AfterSelected;
|
||||
|
||||
public delegate object CheckSelectedHandler();
|
||||
public CheckSelectedHandler CheckSelected;
|
||||
|
||||
@@ -1021,7 +1030,7 @@ namespace Barotrauma
|
||||
while (index < Content.CountChildren)
|
||||
{
|
||||
GUIComponent child = Content.GetChild(index);
|
||||
if (child.Visible)
|
||||
if (child.Visible && child.CanBeFocused)
|
||||
{
|
||||
Select(index, force, GetAutoScroll(!SmoothScroll && autoScroll == AutoScroll.Enabled), takeKeyBoardFocus, playSelectSound);
|
||||
if (SmoothScroll)
|
||||
@@ -1040,7 +1049,7 @@ namespace Barotrauma
|
||||
while (index >= 0)
|
||||
{
|
||||
GUIComponent child = Content.GetChild(index);
|
||||
if (child.Visible)
|
||||
if (child.Visible && child.CanBeFocused)
|
||||
{
|
||||
Select(index, force, GetAutoScroll(!SmoothScroll && autoScroll == AutoScroll.Enabled), takeKeyBoardFocus, playSelectSound);
|
||||
if (SmoothScroll)
|
||||
@@ -1151,6 +1160,8 @@ namespace Barotrauma
|
||||
{
|
||||
SoundPlayer.PlayUISound(GUISoundType.Select);
|
||||
}
|
||||
|
||||
AfterSelected?.Invoke(child, SelectedData);
|
||||
}
|
||||
|
||||
public void Select(IEnumerable<GUIComponent> children)
|
||||
@@ -1160,6 +1171,7 @@ namespace Barotrauma
|
||||
selected.Clear();
|
||||
selected.AddRange(children.Where(c => Content.Children.Contains(c)));
|
||||
foreach (var child in selected) { OnSelected?.Invoke(child, child.UserData); }
|
||||
AfterSelected?.Invoke(children.FirstOrDefault(), SelectedData);
|
||||
}
|
||||
|
||||
public void Deselect()
|
||||
@@ -1172,6 +1184,15 @@ namespace Barotrauma
|
||||
selected.Clear();
|
||||
}
|
||||
|
||||
public void DeselectElement(GUIComponent child)
|
||||
{
|
||||
if (child == null) { return; }
|
||||
if (selected.Contains(child))
|
||||
{
|
||||
selected.Remove(child);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateScrollBarSize()
|
||||
{
|
||||
scrollBarNeedsRecalculation = false;
|
||||
@@ -1268,7 +1289,7 @@ namespace Barotrauma
|
||||
ContentBackground.DrawManually(spriteBatch, alsoChildren: false);
|
||||
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
if (HideChildrenOutsideFrame)
|
||||
if (HideChildrenOutsideFrame && Content.CountChildren > 0)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, Content.Rect);
|
||||
@@ -1306,7 +1327,7 @@ namespace Barotrauma
|
||||
GUI.DrawRectangle(spriteBatch, drawRect, Color.White * 0.5f, thickness: 2f);
|
||||
}
|
||||
|
||||
if (HideChildrenOutsideFrame)
|
||||
if (HideChildrenOutsideFrame && Content.CountChildren > 0)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace Barotrauma
|
||||
GUIStyle.Apply(Text, "", this);
|
||||
Content.Recalculate();
|
||||
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
|
||||
new Point(Text.Rect.Width, Text.Rect.Height);
|
||||
new Point(Text.Rect.Width, Math.Min(Text.Rect.Height, GameMain.GraphicsHeight));
|
||||
Text.RectTransform.IsFixedSize = true;
|
||||
if (headerText.IsNullOrWhiteSpace())
|
||||
{
|
||||
|
||||
@@ -52,6 +52,16 @@ namespace Barotrauma
|
||||
public readonly static GUISprite BrokenIcon = new GUISprite("BrokenIcon");
|
||||
public readonly static GUISprite YouAreHereCircle = new GUISprite("YouAreHereCircle");
|
||||
|
||||
public readonly static GUISprite SubLocationIcon = new GUISprite("SubLocationIcon");
|
||||
public readonly static GUISprite ShuttleIcon = new GUISprite("ShuttleIcon");
|
||||
public readonly static GUISprite WreckIcon = new GUISprite("WreckIcon");
|
||||
public readonly static GUISprite CaveIcon = new GUISprite("CaveIcon");
|
||||
public readonly static GUISprite OutpostIcon = new GUISprite("OutpostIcon");
|
||||
public readonly static GUISprite RuinIcon = new GUISprite("RuinIcon");
|
||||
public readonly static GUISprite EnemyIcon = new GUISprite("EnemyIcon");
|
||||
public readonly static GUISprite CorpseIcon = new GUISprite("CorpseIcon");
|
||||
public readonly static GUISprite BeaconIcon = new GUISprite("BeaconIcon");
|
||||
|
||||
public readonly static GUISprite Radiation = new GUISprite("Radiation");
|
||||
public readonly static GUISpriteSheet RadiationAnimSpriteSheet = new GUISpriteSheet("RadiationAnimSpriteSheet");
|
||||
|
||||
|
||||
@@ -918,5 +918,12 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"GUITextBox: Invalid selection: ({exception})");
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetDelegates()
|
||||
{
|
||||
OnKeyHit = null;
|
||||
OnEnterPressed = null;
|
||||
OnTextChanged = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace Barotrauma
|
||||
GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath, IronmanMode: false } &&
|
||||
GameMain.Client?.CharacterInfo is { PermanentlyDead: true };
|
||||
|
||||
private static bool ReserveBenchEnabled => GameMain.GameSession?.Campaign is MultiPlayerCampaign;
|
||||
|
||||
private bool hadPermissionToHire;
|
||||
private static bool HasPermissionToHire => ReplacingPermanentlyDeadCharacter ?
|
||||
GameMain.NetworkMember?.ServerSettings.ReplaceCostPercentage <= 0 || CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMoney) || CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageHires) :
|
||||
@@ -277,13 +279,42 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
PendingHires?.ForEach(ci => AddPendingHire(ci));
|
||||
PendingHires?.ForEach(ci => AddPendingHire(ci, createNetworkMessage: false));
|
||||
}
|
||||
SetTotalHireCost();
|
||||
}
|
||||
UpdateCrew();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will simply update each of the HR view lists (hireables, pending hires, and crew) from the most up to date information.
|
||||
/// It is a sane version of UpdateLocationView that won't break things even if used outside of whatever arbitrary conditions that one was made for.
|
||||
/// </summary>
|
||||
public void RefreshHRView()
|
||||
{
|
||||
if (campaign?.CurrentLocation is not Location currentLocation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (characterPreviewFrame != null)
|
||||
{
|
||||
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
|
||||
characterPreviewFrame = null;
|
||||
}
|
||||
|
||||
UpdateHireables(currentLocation);
|
||||
|
||||
if (pendingList != null)
|
||||
{
|
||||
pendingList.Content.ClearChildren();
|
||||
PendingHires?.ForEach(ci => AddPendingHire(ci, checkCrewSizeLimit: false, createNetworkMessage: false)); // don't check limits here, just display the data as it is
|
||||
SetTotalHireCost();
|
||||
}
|
||||
|
||||
UpdateCrew();
|
||||
}
|
||||
|
||||
public void UpdateHireables()
|
||||
{
|
||||
UpdateHireables(campaign?.CurrentLocation);
|
||||
@@ -329,10 +360,11 @@ namespace Barotrauma
|
||||
public void UpdateCrew()
|
||||
{
|
||||
crewList.Content.Children.ToList().ForEach(c => crewList.Content.RemoveChild(c));
|
||||
foreach (CharacterInfo c in GameMain.GameSession.CrewManager.GetCharacterInfos())
|
||||
foreach (CharacterInfo ci in GameMain.GameSession.CrewManager.GetCharacterInfos(includeReserveBench: true))
|
||||
{
|
||||
if (c == null || !((c.Character?.IsBot ?? true) || campaign is SinglePlayerCampaign)) { continue; }
|
||||
CreateCharacterFrame(c, crewList);
|
||||
// CrewManager is used to store info on all characters including players, but we only want bots in HR
|
||||
if (ci.Character != null && (ci.Character.IsRemotePlayer || !ci.Character.IsBot)) { continue; }
|
||||
CreateCharacterFrame(ci, crewList);
|
||||
}
|
||||
SortCharacters(crewList, SortingMethod.JobAsc);
|
||||
crewList.UpdateScrollBarSize();
|
||||
@@ -370,6 +402,10 @@ namespace Barotrauma
|
||||
if (sortingMethod == SortingMethod.SkillDesc) { list.Content.RectTransform.ReverseChildren(); }
|
||||
}
|
||||
|
||||
// Always apply this in the end to group by reserve bench status (does nothing if there are no reserve benched bots)
|
||||
list.Content.RectTransform.SortChildren((x, y) =>
|
||||
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.BotStatus.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.BotStatus));
|
||||
|
||||
int? CompareReputationRequirement(GUIComponent c1, GUIComponent c2)
|
||||
{
|
||||
CharacterInfo info1 = ((InfoSkill)c1.UserData).CharacterInfo;
|
||||
@@ -401,6 +437,8 @@ namespace Barotrauma
|
||||
|
||||
public GUIComponent CreateCharacterFrame(CharacterInfo characterInfo, GUIListBox listBox, bool hideSalary = false)
|
||||
{
|
||||
string characterName = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name;
|
||||
|
||||
Skill skill = null;
|
||||
Color? jobColor = null;
|
||||
if (characterInfo.Job != null)
|
||||
@@ -415,6 +453,7 @@ namespace Barotrauma
|
||||
};
|
||||
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = 1,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
@@ -428,12 +467,14 @@ namespace Barotrauma
|
||||
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)) { CanBeFocused = false };
|
||||
GUILayoutGroup nameGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { CanBeFocused = false };
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameGroup.RectTransform),
|
||||
listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name,
|
||||
characterName,
|
||||
textColor: jobColor, textAlignment: Alignment.BottomLeft)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
||||
const float smallColumnWidth = 0.6f / 3;
|
||||
const float skillColumnWidth = smallColumnWidth * 0.7f;
|
||||
const float buttonWidth = 0.12f;
|
||||
|
||||
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
|
||||
characterInfo.Title ?? characterInfo.Job.Name, textColor: Color.White, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft)
|
||||
@@ -449,33 +490,28 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
var fullJobText = jobBlock.Text;
|
||||
jobBlock.Text = ToolBox.LimitString(fullJobText, jobBlock.Font, jobBlock.Rect.Width);
|
||||
if (jobBlock.Text != fullJobText)
|
||||
{
|
||||
jobBlock.ToolTip = fullJobText;
|
||||
jobBlock.CanBeFocused = true;
|
||||
}
|
||||
float width = 0.6f / 3;
|
||||
if (characterInfo.Job != null && skill != null)
|
||||
{
|
||||
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(width, 0.6f), mainGroup.RectTransform), isHorizontal: true);
|
||||
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(skillColumnWidth, 0.6f), mainGroup.RectTransform), isHorizontal: true);
|
||||
float iconWidth = (float)skillGroup.Rect.Height / skillGroup.Rect.Width;
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f - iconWidth, 1.0f), skillGroup.RectTransform), ((int)skill.Level).ToString(),
|
||||
textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
CanBeFocused = false
|
||||
};
|
||||
GUIImage skillIcon = new GUIImage(new RectTransform(Vector2.One, skillGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), skill.Icon, scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
if (jobColor.HasValue) { skillIcon.Color = jobColor.Value; }
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f - iconWidth, 1.0f), skillGroup.RectTransform), ((int)skill.Level).ToString(), textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
|
||||
if (!hideSalary)
|
||||
{
|
||||
if (listBox != crewList)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform),
|
||||
new GUITextBlock(new RectTransform(new Vector2(smallColumnWidth, 1.0f), mainGroup.RectTransform),
|
||||
TextManager.FormatCurrency(ReplacingPermanentlyDeadCharacter ? campaign.NewCharacterCost(characterInfo) : HireManager.GetSalaryFor(characterInfo)),
|
||||
textAlignment: Alignment.Center)
|
||||
{
|
||||
@@ -485,19 +521,24 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
// Just a bit of padding to make list layouts similar
|
||||
new GUIFrame(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false };
|
||||
new GUIFrame(new RectTransform(new Vector2(smallColumnWidth, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false };
|
||||
}
|
||||
}
|
||||
|
||||
if (listBox == hireableList)
|
||||
{
|
||||
var hireButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
|
||||
var hireButton = new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
|
||||
{
|
||||
ToolTip = TextManager.Get("hirebutton"),
|
||||
ToolTip = TextManager.Get(ReserveBenchEnabled ? "hirebutton.crew" : "hirebutton"),
|
||||
ClickSound = GUISoundType.Cart,
|
||||
UserData = characterInfo,
|
||||
Enabled = CanHire(characterInfo) && !ReplacingPermanentlyDeadCharacter,
|
||||
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
|
||||
OnClicked = (b, o) =>
|
||||
{
|
||||
var currentCharacterInfo = (CharacterInfo)o;
|
||||
currentCharacterInfo.BotStatus = BotStatus.PendingHireToActiveService;
|
||||
return AddPendingHire(currentCharacterInfo);
|
||||
}
|
||||
};
|
||||
hireButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
|
||||
{
|
||||
@@ -505,7 +546,7 @@ namespace Barotrauma
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (PendingHires.Count + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
|
||||
if (PendingHires.Count(ci => ci.BotStatus == BotStatus.PendingHireToActiveService) + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
|
||||
{
|
||||
if (btn.Enabled)
|
||||
{
|
||||
@@ -523,7 +564,7 @@ namespace Barotrauma
|
||||
if (ReplacingPermanentlyDeadCharacter)
|
||||
{
|
||||
bool canHire = CanHire(characterInfo) && campaign.CanAffordNewCharacter(characterInfo);
|
||||
var takeoverButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementTakeControlButton")
|
||||
var takeoverButton = new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementTakeControlButton")
|
||||
{
|
||||
ToolTip = canHire ? TextManager.Get("hireandtakecontrol") : TextManager.Get("hireandtakecontroldisabled"),
|
||||
ClickSound = GUISoundType.ConfirmTransaction,
|
||||
@@ -554,25 +595,90 @@ namespace Barotrauma
|
||||
btn.Enabled = canHireCurrently;
|
||||
};
|
||||
}
|
||||
|
||||
if (ReserveBenchEnabled && !ReplacingPermanentlyDeadCharacter)
|
||||
{
|
||||
var hireToReserveBenchButton = new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddAsReserveButton")
|
||||
{
|
||||
ToolTip = TextManager.Get("hirebutton.reservebench"),
|
||||
ClickSound = GUISoundType.Cart,
|
||||
UserData = characterInfo,
|
||||
Enabled = CanHire(characterInfo),
|
||||
OnClicked = (b, o) =>
|
||||
{
|
||||
var currentCharacterInfo = (CharacterInfo)o;
|
||||
currentCharacterInfo.BotStatus = BotStatus.PendingHireToReserveBench;
|
||||
return AddPendingHire(currentCharacterInfo, checkCrewSizeLimit: false);
|
||||
}
|
||||
};
|
||||
hireToReserveBenchButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
|
||||
{
|
||||
btn.Visible = ReserveBenchEnabled;
|
||||
btn.Enabled = CanHire(characterInfo) && !ReplacingPermanentlyDeadCharacter;
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (listBox == pendingList)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementRemoveButton")
|
||||
if (ReserveBenchEnabled && !ReplacingPermanentlyDeadCharacter)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform),
|
||||
style: characterInfo.BotStatus == BotStatus.PendingHireToActiveService ? "CrewManagementReserveBenchButtonActive" : "CrewManagementReserveBenchButtonReserve")
|
||||
{
|
||||
UserData = characterInfo,
|
||||
ToolTip = TextManager.Get(characterInfo.BotStatus == BotStatus.PendingHireToActiveService ? "ReserveBenchTogglePendingHire.Active" : "ReserveBenchTogglePendingHire.Reserve"),
|
||||
Enabled = CanHire(characterInfo) && (characterInfo.BotStatus == BotStatus.PendingHireToActiveService || !ActiveServiceFull()), // note that this is a toggle
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
SelectCharacter(null, null, null);
|
||||
var currentCharacterInfo = (CharacterInfo)obj;
|
||||
GameMain.Client?.ToggleReserveBench(currentCharacterInfo, pendingHire: true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementRemoveButton")
|
||||
{
|
||||
ClickSound = GUISoundType.Cart,
|
||||
UserData = characterInfo,
|
||||
Enabled = CanHire(characterInfo),
|
||||
Enabled = CanHire(characterInfo), // =just check user's rights
|
||||
OnClicked = (b, o) => RemovePendingHire(o as CharacterInfo)
|
||||
};
|
||||
}
|
||||
else if (listBox == crewList && campaign != null)
|
||||
{
|
||||
var currentCrew = GameMain.GameSession.CrewManager.GetCharacterInfos();
|
||||
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementFireButton")
|
||||
if (ReserveBenchEnabled && !ReplacingPermanentlyDeadCharacter)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform),
|
||||
style: characterInfo.BotStatus == BotStatus.ActiveService ? "CrewManagementReserveBenchButtonActive" : "CrewManagementReserveBenchButtonReserve")
|
||||
{
|
||||
UserData = characterInfo,
|
||||
ToolTip = TextManager.Get(characterInfo.BotStatus == BotStatus.ActiveService ? "ReserveBenchToggle.Active" : "ReserveBenchToggle.Reserve"),
|
||||
Enabled = CanHire(characterInfo) && (characterInfo.BotStatus == BotStatus.ActiveService || !ActiveServiceFull()), // note that this is a toggle
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
SelectCharacter(null, null, null);
|
||||
var currentCharacterInfo = (CharacterInfo)obj;
|
||||
if (currentCharacterInfo.BotStatus == BotStatus.ActiveService && // switching to reserve bench
|
||||
characterInfo.Character != null) // may not have a Character to remove if not spawned this round
|
||||
{
|
||||
GameMain.GameSession.CrewManager.RemoveCharacter(characterInfo.Character, removeInfo: true, resetCrewListIndex: true);
|
||||
}
|
||||
GameMain.Client?.ToggleReserveBench(currentCharacterInfo); // update changes to server
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var cm = GameMain.GameSession.CrewManager;
|
||||
// Can't fire if there's only one character in active service
|
||||
var fireButtonEnabled = HasPermissionToHire && (characterInfo.IsOnReserveBench ||
|
||||
(cm.GetCharacterInfos().Contains(characterInfo) && cm.GetCharacterInfos().Count() > 1));
|
||||
new GUIButton(new RectTransform(new Vector2(buttonWidth, 0.9f), mainGroup.RectTransform), style: "CrewManagementFireButton")
|
||||
{
|
||||
UserData = characterInfo,
|
||||
//can't fire if there's only one character in the crew
|
||||
Enabled = currentCrew.Contains(characterInfo) && currentCrew.Count() > 1 && HasPermissionToHire,
|
||||
Enabled = fireButtonEnabled,
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
var confirmDialog = new GUIMessageBox(
|
||||
@@ -587,11 +693,25 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ReserveBenchEnabled && characterInfo.IsOnReserveBench) // Applies to unspecified listings like the death prompt and the bot list after permadeath
|
||||
{
|
||||
new GUIImage(new RectTransform(new Vector2(smallColumnWidth / 2, 0.6f), mainGroup.RectTransform), style: "CrewManagementReserveBenchIconReserve")
|
||||
{
|
||||
ToolTip = TextManager.Get("ReserveBenchStatus.Reserve.WillSpawn")
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUILayoutGroup(new RectTransform(new Vector2(smallColumnWidth / 2, 0.6f), mainGroup.RectTransform)) { CanBeFocused = false };
|
||||
}
|
||||
}
|
||||
|
||||
if (listBox == pendingList || listBox == crewList)
|
||||
{
|
||||
nameBlock.RectTransform.Resize(new Point(nameBlock.Rect.Width - nameBlock.Rect.Height, nameBlock.Rect.Height));
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
||||
nameBlock.Text = ToolBox.LimitString(characterName, nameBlock.Font, nameBlock.Rect.Width);
|
||||
nameBlock.RectTransform.Resize(new Point((int)(nameBlock.Padding.X + nameBlock.TextSize.X + nameBlock.Padding.Z), nameBlock.Rect.Height));
|
||||
Point size = new Point((int)(0.7f * nameBlock.Rect.Height));
|
||||
new GUIImage(new RectTransform(size, nameGroup.RectTransform), "EditIcon") { CanBeFocused = false };
|
||||
@@ -605,6 +725,16 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
//recalculate everything and truncate texts if needed
|
||||
mainGroup.Recalculate();
|
||||
nameBlock.Text = ToolBox.LimitString(characterName, nameBlock.Font, nameBlock.Rect.Width);
|
||||
jobBlock.Text = ToolBox.LimitString(fullJobText, jobBlock.Font, jobBlock.Rect.Width);
|
||||
if (jobBlock.Text != fullJobText)
|
||||
{
|
||||
jobBlock.ToolTip = fullJobText;
|
||||
jobBlock.CanBeFocused = true;
|
||||
}
|
||||
|
||||
bool CanHire(CharacterInfo thisCharacterInfo)
|
||||
{
|
||||
if (!HasPermissionToHire) { return false; }
|
||||
@@ -614,6 +744,15 @@ namespace Barotrauma
|
||||
return frame;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is there (going to be) no space left in active service?
|
||||
/// </summary>
|
||||
private bool ActiveServiceFull()
|
||||
{
|
||||
return (PendingHires.Count(ci => ci.BotStatus == BotStatus.PendingHireToActiveService) + campaign.CrewManager.GetCharacterInfos().Count())
|
||||
>= CrewManager.MaxCrewSize;
|
||||
}
|
||||
|
||||
private bool EnoughReputationToHire(CharacterInfo characterInfo)
|
||||
{
|
||||
if (characterInfo.MinReputationToHire.factionId != Identifier.Empty)
|
||||
@@ -656,7 +795,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup infoLabelGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
|
||||
GUILayoutGroup infoValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
|
||||
float blockHeight = 1.0f / 4;
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("name"));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("name"), textColor: GUIStyle.TextColorBright);
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), "");
|
||||
string name = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name;
|
||||
nameBlock.Text = ToolBox.LimitString(name, nameBlock.Font, nameBlock.Rect.Width);
|
||||
@@ -664,17 +803,17 @@ namespace Barotrauma
|
||||
if (characterInfo.HasSpecifierTags)
|
||||
{
|
||||
var menuCategoryVar = characterInfo.Prefab.MenuCategoryVar;
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(menuCategoryVar));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get(menuCategoryVar), textColor: GUIStyle.TextColorBright);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get(characterInfo.ReplaceVars($"[{menuCategoryVar}]")));
|
||||
}
|
||||
if (characterInfo.Job is Job job)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("tabmenu.job"));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("tabmenu.job"), textColor: GUIStyle.TextColorBright);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), job.Name);
|
||||
}
|
||||
if (characterInfo.PersonalityTrait is NPCPersonalityTrait trait)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("PersonalityTrait"));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("PersonalityTrait"), textColor: GUIStyle.TextColorBright);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), trait.DisplayName);
|
||||
}
|
||||
infoLabelGroup.Recalculate();
|
||||
@@ -727,9 +866,9 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool AddPendingHire(CharacterInfo characterInfo, bool createNetworkMessage = true)
|
||||
private bool AddPendingHire(CharacterInfo characterInfo, bool checkCrewSizeLimit = true, bool createNetworkMessage = true)
|
||||
{
|
||||
if (PendingHires.Count + campaign.CrewManager.GetCharacterInfos().Count() >= CrewManager.MaxCrewSize)
|
||||
if (checkCrewSizeLimit && characterInfo.BotStatus == BotStatus.PendingHireToActiveService && ActiveServiceFull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -792,7 +931,7 @@ namespace Barotrauma
|
||||
List<CharacterInfo> nonDuplicateHires = new List<CharacterInfo>();
|
||||
hires.ForEach(hireInfo =>
|
||||
{
|
||||
if (campaign.CrewManager.GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
|
||||
if (campaign.CrewManager.GetCharacterInfos(includeReserveBench: true).None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
|
||||
{
|
||||
nonDuplicateHires.Add(hireInfo);
|
||||
}
|
||||
@@ -806,12 +945,21 @@ namespace Barotrauma
|
||||
if (!campaign.CanAfford(total)) { return false; }
|
||||
}
|
||||
|
||||
bool atLeastOneHired = false;
|
||||
bool atLeastOneHiredToActiveDuty = false;
|
||||
bool atLeastOneHiredToReserveBench = false;
|
||||
foreach (CharacterInfo ci in nonDuplicateHires)
|
||||
{
|
||||
bool toReserveBench = ci.BotStatus == BotStatus.PendingHireToReserveBench;
|
||||
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci, takeMoney: takeMoney))
|
||||
{
|
||||
atLeastOneHired = true;
|
||||
if (toReserveBench)
|
||||
{
|
||||
atLeastOneHiredToReserveBench = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
atLeastOneHiredToActiveDuty = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -819,15 +967,27 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (atLeastOneHired)
|
||||
if (atLeastOneHiredToActiveDuty || atLeastOneHiredToReserveBench)
|
||||
{
|
||||
UpdateLocationView(campaign.Map.CurrentLocation, true);
|
||||
SelectCharacter(null, null, null);
|
||||
if (createNotification)
|
||||
{
|
||||
LocalizedString msg = string.Empty;
|
||||
if (atLeastOneHiredToActiveDuty)
|
||||
{
|
||||
msg += TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName);
|
||||
}
|
||||
if (atLeastOneHiredToReserveBench)
|
||||
{
|
||||
if (!msg.IsNullOrEmpty()) { msg += "\n\n"; }
|
||||
msg += GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath, IronmanMode: false } ?
|
||||
TextManager.Get("crewhiredmessage.reservebench.permadeath") :
|
||||
TextManager.Get( "crewhiredmessage.reservebench");
|
||||
}
|
||||
|
||||
var dialog = new GUIMessageBox(
|
||||
TextManager.Get("newcrewmembers"),
|
||||
TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName),
|
||||
TextManager.Get("newcrewmembers"), msg,
|
||||
new LocalizedString[] { TextManager.Get("Ok") });
|
||||
dialog.Buttons[0].OnClicked += dialog.Close;
|
||||
}
|
||||
@@ -1034,7 +1194,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPendingHires(List<UInt16> characterInfos, Location location)
|
||||
public void SetPendingHires(List<UInt16> characterInfos, bool[] characterInfoReserveBenchStatuses, Location location, bool checkCrewSizeLimit)
|
||||
{
|
||||
List<CharacterInfo> oldHires = PendingHires.ToList();
|
||||
foreach (CharacterInfo pendingHire in oldHires)
|
||||
@@ -1042,18 +1202,25 @@ namespace Barotrauma
|
||||
RemovePendingHire(pendingHire, createNetworkMessage: false);
|
||||
}
|
||||
PendingHires.Clear();
|
||||
int i = 0;
|
||||
foreach (UInt16 identifier in characterInfos)
|
||||
{
|
||||
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.ID == identifier);
|
||||
if (match != null)
|
||||
{
|
||||
AddPendingHire(match, createNetworkMessage: false);
|
||||
match.BotStatus = characterInfoReserveBenchStatuses[i] ? BotStatus.PendingHireToReserveBench : BotStatus.PendingHireToActiveService;
|
||||
AddPendingHire(match, checkCrewSizeLimit: checkCrewSizeLimit, createNetworkMessage: false);
|
||||
if (!PendingHires.Contains(match))
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to add a pending hire");
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(PendingHires.Contains(match));
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Received a hire that doesn't exist.");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1064,7 +1231,7 @@ namespace Barotrauma
|
||||
/// <param name="renameCharacter">When not null tell the server to rename this character. Item1 is the character to rename, Item2 is the new name, Item3 indicates whether the renamed character is already a part of the crew.</param>
|
||||
/// <param name="firedCharacter">When not null tell the server to fire this character</param>
|
||||
/// <param name="validateHires">When set to true will tell the server to validate pending hires</param>
|
||||
public void SendCrewState(bool updatePending, (CharacterInfo info, string newName) renameCharacter = default, CharacterInfo firedCharacter = null, bool validateHires = false)
|
||||
public void SendCrewState(bool updatePending = false, (CharacterInfo info, string newName) renameCharacter = default, CharacterInfo firedCharacter = null, bool validateHires = false)
|
||||
{
|
||||
if (campaign is MultiPlayerCampaign)
|
||||
{
|
||||
@@ -1078,6 +1245,7 @@ namespace Barotrauma
|
||||
foreach (CharacterInfo pendingHire in PendingHires)
|
||||
{
|
||||
msg.WriteUInt16(pendingHire.ID);
|
||||
msg.WriteBoolean(pendingHire.BotStatus == BotStatus.PendingHireToReserveBench);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1089,7 +1257,9 @@ namespace Barotrauma
|
||||
{
|
||||
msg.WriteUInt16(renameCharacter.info.ID);
|
||||
msg.WriteString(renameCharacter.newName);
|
||||
bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.ID == renameCharacter.info.ID) ?? false;
|
||||
bool existingCrewMember =
|
||||
campaign.CrewManager is CrewManager crewManager &&
|
||||
crewManager.GetCharacterInfos(includeReserveBench: true).Any(ci => ci.ID == renameCharacter.info.ID);
|
||||
msg.WriteBoolean(existingCrewMember);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -1562,7 +1563,7 @@ namespace Barotrauma
|
||||
bool locationHasDealOnItem = isSellingRelatedList ?
|
||||
ActiveStore.RequestedGoods.Contains(pi.ItemPrefab) : ActiveStore.DailySpecials.Contains(pi.ItemPrefab);
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), nameAndQuantityGroup.RectTransform),
|
||||
pi.ItemPrefab.Name, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft)
|
||||
RichString.Rich(pi.ItemPrefab.Name), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Shadow = locationHasDealOnItem,
|
||||
@@ -1573,15 +1574,24 @@ namespace Barotrauma
|
||||
if (locationHasDealOnItem)
|
||||
{
|
||||
var relativeWidth = (0.9f * nameAndQuantityFrame.Rect.Height) / nameAndQuantityFrame.Rect.Width;
|
||||
Vector2 dealIconSize = new Vector2(relativeWidth, 0.9f) * 0.5f;
|
||||
var dealIcon = new GUIImage(
|
||||
new RectTransform(new Vector2(relativeWidth, 0.9f), nameAndQuantityFrame.RectTransform, anchor: Anchor.CenterLeft)
|
||||
new RectTransform(dealIconSize, nameAndQuantityFrame.RectTransform, anchor: Anchor.CenterRight)
|
||||
{
|
||||
AbsoluteOffset = new Point((int)nameBlock.Padding.X, 0)
|
||||
},
|
||||
"StoreDealIcon", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
CanBeFocused = false,
|
||||
UserData = "StoreDealIcon"
|
||||
};
|
||||
var dealIconColor = dealIcon.Color;
|
||||
if (forceDisable)
|
||||
{
|
||||
dealIconColor.A = 0;
|
||||
}
|
||||
|
||||
dealIcon.Color = dealIconColor;
|
||||
dealIcon.SetAsFirstChild();
|
||||
}
|
||||
bool isParentOnLeftSideOfInterface = parentComponent == storeBuyList || parentComponent == storeDailySpecialsGroup ||
|
||||
@@ -1713,7 +1723,7 @@ namespace Barotrauma
|
||||
mainGroup.Recalculate();
|
||||
mainGroup.RectTransform.RecalculateChildren(true, true);
|
||||
amountInput?.LayoutGroup.Recalculate();
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text.SanitizedString, nameBlock.Font, nameBlock.Rect.Width);
|
||||
mainGroup.RectTransform.Children.ForEach(c => c.IsFixedSize = true);
|
||||
|
||||
return frame;
|
||||
@@ -1795,6 +1805,9 @@ namespace Barotrauma
|
||||
|
||||
private void SetItemFrameStatus(GUIComponent itemFrame, bool enabled)
|
||||
{
|
||||
float full = 1f;
|
||||
float dim = 0.7f;
|
||||
float alpha = (enabled ? full : dim);
|
||||
if (itemFrame?.UserData is not PurchasedItem pi) { return; }
|
||||
bool refreshFrameStatus = !pi.IsStoreComponentEnabled.HasValue || pi.IsStoreComponentEnabled.Value != enabled;
|
||||
if (!refreshFrameStatus) { return; }
|
||||
@@ -1802,14 +1815,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (pi.ItemPrefab?.InventoryIcon != null)
|
||||
{
|
||||
icon.Color = pi.ItemPrefab.InventoryIconColor * (enabled ? 1.0f : 0.5f);
|
||||
icon.Color = pi.ItemPrefab.InventoryIconColor * alpha;
|
||||
}
|
||||
else if (pi.ItemPrefab?.Sprite != null)
|
||||
{
|
||||
icon.Color = pi.ItemPrefab.SpriteColor * (enabled ? 1.0f : 0.5f);
|
||||
icon.Color = pi.ItemPrefab.SpriteColor * alpha;
|
||||
}
|
||||
};
|
||||
var color = Color.White * (enabled ? 1.0f : 0.5f);
|
||||
var color = Color.White * alpha;
|
||||
if (itemFrame.FindChild("name", recursive: true) is GUITextBlock name)
|
||||
{
|
||||
name.TextColor = color;
|
||||
@@ -1835,7 +1848,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock priceBlock)
|
||||
{
|
||||
priceBlock.TextColor = isDiscounted ? storeSpecialColor * (enabled ? 1.0f : 0.5f) : color;
|
||||
priceBlock.TextColor = isDiscounted ? storeSpecialColor * alpha : color;
|
||||
}
|
||||
if (itemFrame.FindChild("addbutton", recursive: true) is GUIButton addButton)
|
||||
{
|
||||
@@ -1845,6 +1858,10 @@ namespace Barotrauma
|
||||
{
|
||||
removeButton.Enabled = enabled;
|
||||
}
|
||||
if (itemFrame.FindChild("StoreDealIcon", recursive: true) is GUIImage dealIcon)
|
||||
{
|
||||
dealIcon.Color = dealIcon.Color * alpha;
|
||||
}
|
||||
pi.IsStoreComponentEnabled = enabled;
|
||||
itemFrame.UserData = pi;
|
||||
}
|
||||
@@ -2271,6 +2288,15 @@ namespace Barotrauma
|
||||
{
|
||||
updateStopwatch.Restart();
|
||||
|
||||
if (GameMain.DevMode)
|
||||
{
|
||||
if (PlayerInput.KeyDown(Keys.D0))
|
||||
{
|
||||
CreateUI();
|
||||
needsRefresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.GraphicsWidth != resolutionWhenCreated.X || GameMain.GraphicsHeight != resolutionWhenCreated.Y)
|
||||
{
|
||||
CreateUI();
|
||||
|
||||
@@ -742,8 +742,8 @@ namespace Barotrauma
|
||||
|
||||
private (LocalizedString header, LocalizedString body) GetItemTransferWarningText()
|
||||
{
|
||||
var header = TextManager.Get("itemtransferheader").Fallback("lowfuelheader", useDefaultLanguageIfFound: false);
|
||||
var body = TextManager.Get("itemtransferwarning").Fallback("lowfuelwarning", useDefaultLanguageIfFound: false);
|
||||
var header = TextManager.Get("itemtransferheader").Fallback(TextManager.Get("lowfuelheader"), useDefaultLanguageIfFound: false);
|
||||
var body = TextManager.Get("itemtransferwarning").Fallback(TextManager.Get("lowfuelwarning"), useDefaultLanguageIfFound: false);
|
||||
return (header, body);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,9 +120,20 @@ namespace Barotrauma
|
||||
{
|
||||
if (Client == null) { return; }
|
||||
if (currentPing == Client.Ping) { return; }
|
||||
currentPing = Client.Ping;
|
||||
textBlock.Text = currentPing.ToString();
|
||||
textBlock.TextColor = GetPingColor();
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.ConnectedClients.Contains(Client))
|
||||
{
|
||||
currentPing = Client.Ping;
|
||||
textBlock.Text = currentPing.ToString();
|
||||
textBlock.TextColor = GetPingColor();
|
||||
textBlock.ToolTip = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPing = 0;
|
||||
textBlock.Text = "-";
|
||||
textBlock.TextColor = GUIStyle.Red;
|
||||
textBlock.ToolTip = TextManager.Get("causeofdeathdescription.disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
public void TryPermissionIconRefresh(Sprite icon)
|
||||
@@ -416,7 +427,10 @@ namespace Barotrauma
|
||||
=> TextManager.GetWithVariable("percentageformat", "[value]", $"{(int)MathF.Round(value)}");
|
||||
}
|
||||
|
||||
var submarineButton = createTabButton(InfoFrameTab.Submarine, "submarine");
|
||||
if (Submarine.MainSub != null)
|
||||
{
|
||||
createTabButton(InfoFrameTab.Submarine, "submarine");
|
||||
}
|
||||
|
||||
var talentsButton = createTabButton(InfoFrameTab.Talents, "tabmenu.character");
|
||||
talentsButton.OnAddedToGUIUpdateList += (component) =>
|
||||
@@ -458,17 +472,19 @@ namespace Barotrauma
|
||||
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
|
||||
break;
|
||||
case InfoFrameTab.Talents:
|
||||
talentMenu.CreateGUI(infoFrameHolder, Character.Controlled ?? GameMain.Client?.Character);
|
||||
talentMenu.CreateGUI(infoFrameHolder, Character.Controlled?.Info ?? GameMain.Client?.CharacterInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private const float jobColumnWidthPercentage = 0.138f,
|
||||
characterColumnWidthPercentage = 0.45f,
|
||||
pingColumnWidthPercentage = 0.206f,
|
||||
walletColumnWidthPercentage = 0.206f;
|
||||
private const float JobColumnWidthPercentage = 0.138f,
|
||||
CharacterColumnWidthPercentage = 0.45f,
|
||||
KillColumnWidthPercentage = 0.1f,
|
||||
DeathColumnWidthPercentage = 0.1f,
|
||||
PingColumnWidthPercentage = 0.15f,
|
||||
WalletColumnWidthPercentage = 0.206f;
|
||||
|
||||
private int jobColumnWidth, characterColumnWidth, pingColumnWidth, walletColumnWidth;
|
||||
private int jobColumnWidth, characterColumnWidth, pingColumnWidth, walletColumnWidth, deathColumnWidth, killColumnWidth;
|
||||
|
||||
private void CreateCrewListFrame(GUIFrame crewFrame)
|
||||
{
|
||||
@@ -496,11 +512,21 @@ namespace Barotrauma
|
||||
{
|
||||
if (teamIDs.Count > 1)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, nameHeight), content.RectTransform), CombatMission.GetTeamName(teamIDs[i]), textColor: i == 0 ? GUIStyle.Green : GUIStyle.Orange) { ForceUpperCase = ForceUpperCase.Yes };
|
||||
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, nameHeight), content.RectTransform), CombatMission.GetTeamName(teamIDs[i]), textColor: CombatMission.GetTeamColor(teamIDs[i]))
|
||||
{
|
||||
ForceUpperCase = ForceUpperCase.Yes
|
||||
};
|
||||
var teamIcon = new GUIImage(new RectTransform(Vector2.One, nameText.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.BothHeight),
|
||||
style: teamIDs[i] == CharacterTeamType.Team2 ? "SeparatistIcon" : "CoalitionIcon")
|
||||
{
|
||||
Color = nameText.TextColor
|
||||
};
|
||||
nameText.Padding = new Vector4(teamIcon.Rect.Width + nameText.Padding.X, nameText.Padding.Y, nameText.Padding.Z, nameText.Padding.W);
|
||||
}
|
||||
|
||||
headerFrames[i] = new GUILayoutGroup(new RectTransform(Vector2.Zero, content.RectTransform, Anchor.TopLeft, Pivot.BottomLeft) { AbsoluteOffset = new Point(2, -1) }, isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
AbsoluteSpacing = 2,
|
||||
UserData = i
|
||||
};
|
||||
@@ -587,8 +613,8 @@ namespace Barotrauma
|
||||
|
||||
sizeMultiplier = (headerFrame.Rect.Width - headerFrame.AbsoluteSpacing * (headerFrame.CountChildren - 1)) / (float)headerFrame.Rect.Width;
|
||||
|
||||
jobButton.RectTransform.RelativeSize = new Vector2(jobColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
characterButton.RectTransform.RelativeSize = new Vector2((1f - jobColumnWidthPercentage * sizeMultiplier) * sizeMultiplier, 1f);
|
||||
jobButton.RectTransform.RelativeSize = new Vector2(JobColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
characterButton.RectTransform.RelativeSize = new Vector2((1f - JobColumnWidthPercentage * sizeMultiplier) * sizeMultiplier, 1f);
|
||||
|
||||
jobButton.TextBlock.Font = characterButton.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
jobButton.CanBeFocused = characterButton.CanBeFocused = false;
|
||||
@@ -626,7 +652,8 @@ namespace Barotrauma
|
||||
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AbsoluteSpacing = 2
|
||||
AbsoluteSpacing = 2,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => character.Info.DrawJobIcon(sb, component.Rect))
|
||||
@@ -639,21 +666,29 @@ namespace Barotrauma
|
||||
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(character.Info.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: character.Info.Job.Prefab.UIColor);
|
||||
|
||||
paddedFrame.Recalculate();
|
||||
|
||||
linkedGUIList.Add(new LinkedGUI(character, frame, textBlock: null));
|
||||
}
|
||||
|
||||
private void CreateMultiPlayerListContentHolder(GUILayoutGroup headerFrame)
|
||||
{
|
||||
bool isCampaign = GameMain.GameSession?.Campaign is MultiPlayerCampaign;
|
||||
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton pingButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("serverlistping"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(JobColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(CharacterColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
|
||||
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
var killButton = new GUIButton(new RectTransform(new Vector2(KillColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("killcount"), style: "GUIButtonSmallFreeScale");
|
||||
killColumnWidth = killButton.Rect.Width;
|
||||
var deathButton = new GUIButton(new RectTransform(new Vector2(DeathColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("deathcount"), style: "GUIButtonSmallFreeScale");
|
||||
deathColumnWidth = deathButton.Rect.Width;
|
||||
}
|
||||
|
||||
GUIButton pingButton = new GUIButton(new RectTransform(new Vector2(PingColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("serverlistping"), style: "GUIButtonSmallFreeScale");
|
||||
if (isCampaign)
|
||||
{
|
||||
GUIButton walletButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform)
|
||||
{
|
||||
RelativeSize = new Vector2(walletColumnWidthPercentage * sizeMultiplier, 1f)
|
||||
}, TextManager.Get("crewwallet.wallet"), style: "GUIButtonSmallFreeScale")
|
||||
GUIButton walletButton = new GUIButton(new RectTransform(new Vector2(WalletColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("crewwallet.wallet"), style: "GUIButtonSmallFreeScale")
|
||||
{
|
||||
TextBlock = { Font = GUIStyle.HotkeyFont },
|
||||
CanBeFocused = false,
|
||||
@@ -662,15 +697,12 @@ namespace Barotrauma
|
||||
walletColumnWidth = walletButton.Rect.Width;
|
||||
}
|
||||
|
||||
sizeMultiplier = (headerFrame.Rect.Width - headerFrame.AbsoluteSpacing * (headerFrame.CountChildren - 1)) / (float)headerFrame.Rect.Width;
|
||||
|
||||
jobButton.RectTransform.RelativeSize = new Vector2(jobColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
characterButton.RectTransform.RelativeSize = new Vector2((characterColumnWidthPercentage + (isCampaign ? 0 : walletColumnWidthPercentage)) * sizeMultiplier, 1f);
|
||||
pingButton.RectTransform.RelativeSize = new Vector2(pingColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
|
||||
jobButton.TextBlock.Font = characterButton.TextBlock.Font = pingButton.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
jobButton.CanBeFocused = characterButton.CanBeFocused = pingButton.CanBeFocused = false;
|
||||
jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = pingButton.ForceUpperCase = ForceUpperCase.Yes;
|
||||
foreach (var btn in headerFrame.GetAllChildren<GUIButton>())
|
||||
{
|
||||
btn.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
btn.CanBeFocused = false;
|
||||
btn.ForceUpperCase = ForceUpperCase.Yes;
|
||||
}
|
||||
|
||||
jobColumnWidth = jobButton.Rect.Width;
|
||||
characterColumnWidth = characterButton.Rect.Width;
|
||||
@@ -688,45 +720,68 @@ namespace Barotrauma
|
||||
|
||||
var connectedClients = GameMain.Client.ConnectedClients;
|
||||
|
||||
for (int i = 0; i < teamIDs.Count; i++)
|
||||
for (int teamID = 0; teamID < teamIDs.Count; teamID++)
|
||||
{
|
||||
foreach (Character character in crew.Where(c => c.TeamID == teamIDs[i]))
|
||||
foreach (Character character in crew.Where(c => c.TeamID == teamIDs[teamID]))
|
||||
{
|
||||
if (!(character is AICharacter) && connectedClients.Any(c => c.Character == null && c.Name == character.Name)) { continue; }
|
||||
CreateMultiPlayerCharacterElement(character, GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.Character == character), i);
|
||||
if (character is not AICharacter && connectedClients.Any(c => c.Character == null && c.Name == character.Name)) { continue; }
|
||||
CreateMultiPlayerCharacterElement(character, GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.Character == character), teamID);
|
||||
}
|
||||
|
||||
foreach (CharacterInfo characterInfo in GameMain.GameSession.CrewManager?.GetReserveBenchInfos() ?? Enumerable.Empty<CharacterInfo>())
|
||||
{
|
||||
CreateMultiPlayerCharacterElement(character: null, client: null, teamID, justCharacterInfo: characterInfo);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < connectedClients.Count; j++)
|
||||
{
|
||||
Client client = connectedClients[j];
|
||||
if (!client.InGame || client.Character == null || client.Character.IsDead)
|
||||
if (client.Character == null || client.Character.IsDead)
|
||||
{
|
||||
CreateMultiPlayerClientElement(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMultiPlayerCharacterElement(Character character, Client client, int i)
|
||||
/// <param name="justCharacterInfo">The character element can be generated based on just a CharacterInfo, and Character and Client can be left null. Otherwise, those are required and the CharacterInfo of the Character is used.</param>
|
||||
private void CreateMultiPlayerCharacterElement(Character character, Client client, int teamID, CharacterInfo justCharacterInfo = null)
|
||||
{
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(crewListArray[i].Content.Rect.Width, GUI.IntScale(33f)), crewListArray[i].Content.RectTransform), style: "ListBoxElement")
|
||||
CharacterInfo characterInfo = justCharacterInfo ?? character.Info;
|
||||
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(crewListArray[teamID].Content.Rect.Width, GUI.IntScale(33f)), crewListArray[teamID].Content.RectTransform), style: "ListBoxElement")
|
||||
{
|
||||
UserData = character,
|
||||
UserData = character != null ? character : characterInfo,
|
||||
Color = (GameMain.NetworkMember != null && GameMain.Client.Character == character) ? OwnCharacterBGColor : Color.Transparent
|
||||
};
|
||||
|
||||
frame.OnSecondaryClicked += (component, data) =>
|
||||
if (client != null)
|
||||
{
|
||||
NetLobbyScreen.CreateModerationContextMenu(client);
|
||||
return true;
|
||||
};
|
||||
frame.OnSecondaryClicked += (component, data) =>
|
||||
{
|
||||
NetLobbyScreen.CreateModerationContextMenu(client);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AbsoluteSpacing = 2
|
||||
AbsoluteSpacing = 2,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => character.Info.DrawJobIcon(sb, component.Rect))
|
||||
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center),
|
||||
onDraw: (sb, component) =>
|
||||
{
|
||||
if (client == null)
|
||||
{
|
||||
characterInfo?.DrawJobIcon(sb, component.Rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawClientJobIcon(sb, component.Rect, client);
|
||||
}
|
||||
})
|
||||
{
|
||||
CanBeFocused = false,
|
||||
HoverColor = Color.White,
|
||||
@@ -736,6 +791,19 @@ namespace Barotrauma
|
||||
if (client != null)
|
||||
{
|
||||
CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon);
|
||||
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Point(killColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetClientKillCount(client) ?? 0).ToString()
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Point(deathColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetClientDeathCount(client) ?? 0).ToString()
|
||||
};
|
||||
}
|
||||
|
||||
linkedGUIList.Add(new LinkedGUI(client, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center),
|
||||
permissionIcon));
|
||||
@@ -743,27 +811,62 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(character.Info.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: character.Info.Job.Prefab.UIColor);
|
||||
ToolBox.LimitString(characterInfo.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: characterInfo.Job.Prefab.UIColor);
|
||||
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Point(killColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetBotKillCount(characterInfo) ?? 0).ToString()
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Point(deathColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetBotDeathCount(characterInfo) ?? 0).ToString()
|
||||
};
|
||||
}
|
||||
|
||||
if (character is AICharacter)
|
||||
{
|
||||
// "BOT" instead of ping (which isn't relevant for bots)
|
||||
linkedGUIList.Add(new LinkedGUI(character, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), TextManager.Get("tabmenu.bot"), textAlignment: Alignment.Center) { ForceUpperCase = ForceUpperCase.Yes }));
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), TextManager.Get("tabmenu.bot"), textAlignment: Alignment.Center) { ForceUpperCase = ForceUpperCase.Yes }));
|
||||
}
|
||||
else
|
||||
else if (characterInfo.IsOnReserveBench)
|
||||
{
|
||||
linkedGUIList.Add(new LinkedGUI(client: null, frame, textBlock: null, permissionIcon: null));
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => DrawDisconnectedIcon(sb, component.Rect))
|
||||
// Reserve bench icon
|
||||
new GUIImage(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height - 4), paddedFrame.RectTransform), style: "CrewManagementReserveBenchIconReserve", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
HoverColor = Color.White,
|
||||
SelectedColor = Color.White
|
||||
ToolTip = TextManager.Get("ReserveBenchStatus.Reserve")
|
||||
};
|
||||
}
|
||||
|
||||
if (characterInfo.IsOnReserveBench)
|
||||
{
|
||||
//black bar to dim out the elements (1px shorter and to the right so it won't dim the left border too)
|
||||
new GUIFrame(
|
||||
new RectTransform(new Point(paddedFrame.Rect.Width - 1, frame.Rect.Height), paddedFrame.RectTransform, Anchor.Center)
|
||||
{
|
||||
AbsoluteOffset = new Point(1, 0)
|
||||
},
|
||||
style: null, color: Color.Black * 0.7f)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
CreateWalletCrewFrame(character, paddedFrame);
|
||||
if (character != null)
|
||||
{
|
||||
CreateWalletCrewFrame(character, paddedFrame);
|
||||
}
|
||||
else if (characterInfo.IsOnReserveBench)
|
||||
{
|
||||
// Empty column for reserve benched bots
|
||||
new GUILayoutGroup(new RectTransform(new Point(walletColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), childAnchor: Anchor.Center) { CanBeFocused = false };
|
||||
}
|
||||
|
||||
paddedFrame.Recalculate();
|
||||
}
|
||||
|
||||
private void CreateMultiPlayerClientElement(Client client)
|
||||
@@ -785,11 +888,12 @@ namespace Barotrauma
|
||||
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AbsoluteSpacing = 2
|
||||
AbsoluteSpacing = 2,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center),
|
||||
onDraw: (sb, component) => DrawNotInGameIcon(sb, component.Rect, client))
|
||||
onDraw: (sb, component) => DrawClientJobIcon(sb, component.Rect, client))
|
||||
{
|
||||
CanBeFocused = false,
|
||||
HoverColor = Color.White,
|
||||
@@ -797,14 +901,26 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon);
|
||||
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Point(killColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetClientKillCount(client) ?? 0).ToString()
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Point(deathColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextGetter = () => GameMain.GameSession.Missions.Sum(m => (m as CombatMission)?.GetClientDeathCount(client) ?? 0).ToString()
|
||||
};
|
||||
}
|
||||
|
||||
linkedGUIList.Add(new LinkedGUI(client, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center),
|
||||
permissionIcon));
|
||||
|
||||
if (client.Character is { } character)
|
||||
{
|
||||
CreateWalletCrewFrame(character, paddedFrame);
|
||||
}
|
||||
CreateWalletCrewFrame(client.Character, paddedFrame);
|
||||
|
||||
paddedFrame.Recalculate();
|
||||
}
|
||||
|
||||
private int GetTeamIndex(Client client)
|
||||
@@ -837,12 +953,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return teamIDs.IndexOf(client.TeamID);
|
||||
}
|
||||
|
||||
private void CreateWalletCrewFrame(Character character, GUILayoutGroup paddedFrame)
|
||||
{
|
||||
if (!(GameMain.GameSession?.Campaign is MultiPlayerCampaign)) { return; }
|
||||
if (GameMain.GameSession?.Campaign is not MultiPlayerCampaign) { return; }
|
||||
|
||||
GUILayoutGroup walletLayout = new GUILayoutGroup(new RectTransform(new Point(walletColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), childAnchor: Anchor.Center)
|
||||
{
|
||||
@@ -860,7 +976,7 @@ namespace Barotrauma
|
||||
ToolTip = TextManager.Get("walletdescription")
|
||||
};
|
||||
|
||||
if (character.IsBot) { return; }
|
||||
if (character == null || character.IsBot) { return; }
|
||||
|
||||
Sprite walletSprite = GUIStyle.CrewWalletIconSmall.Value.Sprite;
|
||||
|
||||
@@ -948,7 +1064,6 @@ namespace Barotrauma
|
||||
int xOffset = (int)(jobColumnWidth + characterNameBlock.TextPos.X - GUIStyle.Font.MeasureString(characterNameBlock.Text).X / 2f - paddedFrame.AbsoluteSpacing - iconWidth * paddedFrame.Rect.Width);
|
||||
permissionIcon = new GUIImage(new RectTransform(new Vector2(iconWidth, 1f), paddedFrame.RectTransform) { AbsoluteOffset = new Point(xOffset + 2, 0) }, permissionIconSprite) { IgnoreLayoutGroups = true };
|
||||
|
||||
|
||||
if (client.Character != null && client.Character.IsDead)
|
||||
{
|
||||
characterNameBlock.Strikethrough = new GUITextBlock.StrikethroughSettings(null, GUI.IntScale(1f), GUI.IntScale(5f));
|
||||
@@ -969,18 +1084,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawNotInGameIcon(SpriteBatch spriteBatch, Rectangle area, Client client)
|
||||
private void DrawClientJobIcon(SpriteBatch spriteBatch, Rectangle area, Client client)
|
||||
{
|
||||
if (client.Spectating)
|
||||
{
|
||||
spectateIcon.Draw(spriteBatch, area, Color.White);
|
||||
}
|
||||
else if (client.Character != null && client.Character.IsDead)
|
||||
else if (client.Character != null && client.InGame)
|
||||
{
|
||||
if (client.Character.Info != null)
|
||||
{
|
||||
client.Character.Info.DrawJobIcon(spriteBatch, area);
|
||||
}
|
||||
client.Character.Info?.DrawJobIcon(spriteBatch, area);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1005,6 +1117,12 @@ namespace Barotrauma
|
||||
GUIComponent existingPreview = infoFrameHolder.FindChild("SelectedCharacter");
|
||||
if (existingPreview != null) { infoFrameHolder.RemoveChild(existingPreview); }
|
||||
|
||||
if (userData is CharacterInfo { IsOnReserveBench: true })
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Modal info panel that pops up on the right
|
||||
GUIFrame background = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.69f), infoFrameHolder.RectTransform, Anchor.TopRight, Pivot.TopLeft) { RelativeOffset = new Vector2(-0.061f, 0) })
|
||||
{
|
||||
UserData = "SelectedCharacter"
|
||||
@@ -1028,7 +1146,7 @@ namespace Barotrauma
|
||||
{
|
||||
talentButton.OnClicked = (button, o) =>
|
||||
{
|
||||
talentMenu.CreateGUI(infoFrameHolder, character);
|
||||
talentMenu.CreateGUI(infoFrameHolder, character.Info);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
@@ -1413,7 +1531,7 @@ namespace Barotrauma
|
||||
var headerArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.322f), paddedFrame.RectTransform), isHorizontal: true);
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.425f, 1.0f), headerArea.RectTransform),
|
||||
onDraw: (sb, component) => DrawNotInGameIcon(sb, component.Rect, client));
|
||||
onDraw: (sb, component) => DrawClientJobIcon(sb, component.Rect, client));
|
||||
|
||||
GUIFont font = paddedFrame.Rect.Width < 280 ? GUIStyle.SmallFont : GUIStyle.Font;
|
||||
|
||||
@@ -1503,6 +1621,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
linkedGUIList.Clear();
|
||||
|
||||
foreach (GUIListBox crewList in crewListArray)
|
||||
{
|
||||
crewList.Content.ClearChildren();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddLineToLog(string line, PlayerConnectionChangeType type)
|
||||
@@ -1587,7 +1710,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.DisplayName, font: GUIStyle.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.GetLocationTypeToDisplay().Name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
if (location.Faction?.Prefab != null)
|
||||
{
|
||||
@@ -1633,6 +1756,7 @@ namespace Barotrauma
|
||||
textContent,
|
||||
mission.Difficulty ?? 0,
|
||||
mission.Prefab.Icon, mission.Prefab.IconColor,
|
||||
mission.GetDifficultyToolTipText(),
|
||||
out GUIImage missionIcon);
|
||||
if (missionIcon != null)
|
||||
{
|
||||
@@ -1663,6 +1787,8 @@ namespace Barotrauma
|
||||
|
||||
private static void CreateSubmarineInfo(GUIFrame infoFrame, Submarine sub)
|
||||
{
|
||||
if (sub == null) { return; }
|
||||
|
||||
GUIFrame subInfoFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(Vector2.One * 0.97f, subInfoFrame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
@@ -1765,7 +1891,7 @@ namespace Barotrauma
|
||||
{
|
||||
parent.Content.ClearChildren();
|
||||
List<GUITextBlock> skillNames = new List<GUITextBlock>();
|
||||
foreach (Skill skill in info.Job.GetSkills())
|
||||
foreach (Skill skill in info.Job.GetSkills().OrderByDescending(static s => s.Level))
|
||||
{
|
||||
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.0f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = true };
|
||||
var skillName = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}").Fallback(skill.Identifier.Value));
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using static Barotrauma.TalentTree;
|
||||
using static Barotrauma.TalentTree.TalentStages;
|
||||
@@ -83,17 +84,21 @@ namespace Barotrauma
|
||||
private GUIButton? talentApplyButton,
|
||||
talentResetButton;
|
||||
|
||||
public void CreateGUI(GUIFrame parent, Character? targetCharacter)
|
||||
private delegate void StartAnimation(RectangleF start, RectangleF end, float duration);
|
||||
private StartAnimation? startAnimation;
|
||||
private GUIComponent? talentMainArea;
|
||||
|
||||
public void CreateGUI(GUIFrame parent, CharacterInfo? characterInfo)
|
||||
{
|
||||
this.characterInfo = characterInfo;
|
||||
character = characterInfo?.Character;
|
||||
|
||||
parent.ClearChildren();
|
||||
talentButtons.Clear();
|
||||
talentShowCaseButtons.Clear();
|
||||
talentCornerIcons.Clear();
|
||||
showCaseTalentFrames.Clear();
|
||||
|
||||
character = targetCharacter;
|
||||
characterInfo = targetCharacter?.Info;
|
||||
|
||||
GUIFrame background = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
int padding = GUI.IntScale(15);
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(background.Rect.Width - padding, background.Rect.Height - padding), parent.RectTransform, Anchor.Center), style: null);
|
||||
@@ -136,7 +141,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup playerFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), containerFrame.RectTransform, Anchor.TopCenter));
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
|
||||
|
||||
if (!GameMain.NetLobbyScreen.PermadeathMode)
|
||||
if (!GameMain.NetLobbyScreen.PermadeathMode && GameMain.GameSession?.GameMode is not PvPMode)
|
||||
{
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
|
||||
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
|
||||
@@ -294,7 +299,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
ImmutableHashSet<TalentPrefab?> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(static e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e)).ToImmutableHashSet();
|
||||
if (talentsOutsideTree.Any())
|
||||
if (talentsOutsideTree.Any(static t => t != null && !t.IsHiddenExtraTalent))
|
||||
{
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), nameLayout.RectTransform), style: null);
|
||||
@@ -319,6 +324,7 @@ namespace Barotrauma
|
||||
foreach (var extraTalent in talentsOutsideTree)
|
||||
{
|
||||
if (extraTalent is null) { continue; }
|
||||
if (extraTalent.IsHiddenExtraTalent) { continue; }
|
||||
GUIImage talentImg = new GUIImage(new RectTransform(Vector2.One, extraTalentList.Content.RectTransform, scaleBasis: ScaleBasis.BothHeight), sprite: extraTalent.Icon, scaleToFit: true)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{extraTalent.DisplayName}‖color:end‖" + "\n\n" + ToolBox.ExtendColorToPercentageSigns(extraTalent.Description.Value)),
|
||||
@@ -341,7 +347,15 @@ namespace Barotrauma
|
||||
|
||||
private void CreateTalentMenu(GUIComponent parent, CharacterInfo info, TalentTree tree)
|
||||
{
|
||||
GUIListBox mainList = new GUIListBox(new RectTransform(new Vector2(1f, 0.9f), parent.RectTransform, anchor: Anchor.TopCenter));
|
||||
talentMainArea = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), parent.RectTransform, Anchor.TopCenter), style: null );
|
||||
|
||||
GUIListBox mainList = new GUIListBox(new RectTransform(Vector2.One, talentMainArea.RectTransform));
|
||||
startAnimation = CreatePopupAnimationHandler(talentMainArea);
|
||||
|
||||
if (info is { TalentRefundPoints: > 0, ShowTalentResetPopupOnOpen: true })
|
||||
{
|
||||
CreateTalentResetPopup(talentMainArea);
|
||||
}
|
||||
|
||||
selectedTalents = info.GetUnlockedTalentsInTree().ToHashSet();
|
||||
|
||||
@@ -425,6 +439,130 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTalentResetPopup(GUIComponent parent)
|
||||
{
|
||||
int talentResetCount = 0;
|
||||
if (character?.Info != null)
|
||||
{
|
||||
talentResetCount = Math.Min(character.Info.TalentResetCount, character.Info.GetCurrentLevel());
|
||||
}
|
||||
bool hasResetTalentsBefore = talentResetCount > 0;
|
||||
var bgBlocker = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform, anchor: Anchor.Center), style: "GUIBackgroundBlocker")
|
||||
{
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
var popup = new GUIFrame(new RectTransform(new Vector2(0.6f, 0.8f), bgBlocker.RectTransform, Anchor.Center));
|
||||
|
||||
var popupLayout = new GUILayoutGroup(new RectTransform(ToolBox.PaddingSizeParentRelative(popup.RectTransform, 0.95f), popup.RectTransform, Anchor.Center), isHorizontal: false);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), popupLayout.RectTransform), TextManager.Get("talentresetheader"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, hasResetTalentsBefore ? 0.25f : 0.5f), popupLayout.RectTransform), TextManager.Get("talentresetprompt"), wrap: true);
|
||||
|
||||
if (hasResetTalentsBefore)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), popupLayout.RectTransform),
|
||||
TextManager.GetWithVariable("talentresetpromptwarning", "[count]", talentResetCount.ToString()), wrap: true)
|
||||
{
|
||||
TextColor = GUIStyle.Red
|
||||
};
|
||||
}
|
||||
|
||||
var buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.35f), popupLayout.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true);
|
||||
|
||||
var confirmButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonLayout.RectTransform), TextManager.Get("holdtoconfirm"))
|
||||
{
|
||||
RequireHold = true,
|
||||
HoldDurationSeconds = 1.5f,
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
if (character is null || characterInfo is null) { return false; }
|
||||
|
||||
characterInfo.RefundTalents();
|
||||
selectedTalents.Clear();
|
||||
UpdateTalentInfo();
|
||||
bgBlocker.Visible = false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
var denyButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonLayout.RectTransform), TextManager.Get("decidelater"))
|
||||
{
|
||||
RequireHold = false,
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
if (talentResetButton is not { } resetButton) { return false; }
|
||||
startAnimation?.Invoke(popup.Rect, resetButton.Rect, 0.25f);
|
||||
resetButton.Flash(GUIStyle.Green);
|
||||
bgBlocker.Visible = false;
|
||||
if (characterInfo != null)
|
||||
{
|
||||
characterInfo.ShowTalentResetPopupOnOpen = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static StartAnimation CreatePopupAnimationHandler(GUIComponent parent)
|
||||
{
|
||||
bool drawAnimation = false;
|
||||
|
||||
float animDur = 1f,
|
||||
animTimer = 0f;
|
||||
|
||||
RectangleF drawRect = RectangleF.Empty,
|
||||
animStartRect = RectangleF.Empty,
|
||||
animEndRect = RectangleF.Empty;
|
||||
|
||||
void StartAnimation(RectangleF start, RectangleF end, float duration)
|
||||
{
|
||||
animStartRect = start;
|
||||
animEndRect = end;
|
||||
animTimer = 0;
|
||||
animDur = duration;
|
||||
drawRect = start;
|
||||
drawAnimation = true;
|
||||
}
|
||||
|
||||
void OnDraw(SpriteBatch batch, GUICustomComponent component)
|
||||
{
|
||||
if (!drawAnimation) { return; }
|
||||
|
||||
GUIComponentStyle style = GUIStyle.GetComponentStyle("GUIFrame");
|
||||
|
||||
style.Sprites[GUIComponent.ComponentState.None][0].Draw(batch, drawRect, Color.White);
|
||||
}
|
||||
|
||||
void OnUpdate(float f, GUICustomComponent component)
|
||||
{
|
||||
if (!drawAnimation) { return; }
|
||||
|
||||
animTimer += f;
|
||||
if (animTimer > animDur)
|
||||
{
|
||||
drawRect = animEndRect;
|
||||
drawAnimation = false;
|
||||
return;
|
||||
}
|
||||
|
||||
float lerp = animTimer / animDur;
|
||||
|
||||
drawRect = new RectangleF(
|
||||
MathHelper.Lerp(animStartRect.X, animEndRect.X, lerp),
|
||||
MathHelper.Lerp(animStartRect.Y, animEndRect.Y, lerp),
|
||||
MathHelper.Lerp(animStartRect.Width, animEndRect.Width, lerp),
|
||||
MathHelper.Lerp(animStartRect.Height, animEndRect.Height, lerp));
|
||||
}
|
||||
|
||||
new GUICustomComponent(new RectTransform(Vector2.One, parent.RectTransform), onDraw: OnDraw, onUpdate: OnUpdate)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
return StartAnimation;
|
||||
}
|
||||
|
||||
private void CreateTalentOption(GUIComponent parent, TalentSubTree subTree, int index, TalentOption talentOption, CharacterInfo info, int specializationCount)
|
||||
{
|
||||
int elementPadding = GUI.IntScale(8);
|
||||
@@ -679,6 +817,15 @@ namespace Barotrauma
|
||||
private bool ResetTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
if (characterInfo is null) { return false; }
|
||||
|
||||
int newTalentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
|
||||
// if we don't have talents selected, and we have points to refund, show the refund popup
|
||||
if (characterInfo.TalentRefundPoints > 0 && newTalentCount == 0)
|
||||
{
|
||||
CreateTalentResetPopup(talentMainArea!);
|
||||
return true;
|
||||
}
|
||||
|
||||
selectedTalents = characterInfo.GetUnlockedTalentsInTree().ToHashSet();
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
@@ -844,12 +991,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly LocalizedString refundText = TextManager.Get("refund"),
|
||||
resetText = TextManager.Get("reset");
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (characterInfo is null || talentResetButton is null || talentApplyButton is null) { return; }
|
||||
|
||||
int talentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
|
||||
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
|
||||
talentApplyButton.Enabled = character != null && talentCount > 0;
|
||||
talentResetButton.Enabled = character != null && (talentCount > 0 || characterInfo.TalentRefundPoints > 0);
|
||||
|
||||
if (talentCount == 0 && characterInfo.TalentRefundPoints > 0)
|
||||
{
|
||||
if (talentResetButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
talentResetButton.Flash(GUIStyle.Orange);
|
||||
}
|
||||
|
||||
talentResetButton.Text = refundText;
|
||||
}
|
||||
else
|
||||
{
|
||||
talentResetButton.Text = resetText;
|
||||
}
|
||||
|
||||
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
talentApplyButton.Flash(GUIStyle.Orange);
|
||||
@@ -893,6 +1059,22 @@ namespace Barotrauma
|
||||
return info.GetIdentifierUsingOriginalName() == ownCharacterInfo.GetIdentifierUsingOriginalName();
|
||||
}
|
||||
|
||||
private static bool IsOnSameTeam(CharacterInfo? info)
|
||||
{
|
||||
if (info is null) { return false; }
|
||||
|
||||
CharacterTeamType? ownCharacterTeam = Character.Controlled?.TeamID ?? GameMain.Client?.MyClient?.TeamID;
|
||||
if (ownCharacterTeam is null) { return false; }
|
||||
|
||||
return info.TeamID == ownCharacterTeam;
|
||||
}
|
||||
|
||||
private static bool IsSpectatingInMultiplayer()
|
||||
{
|
||||
if (GameMain.Client?.MyClient is not { } myClient) { return false; }
|
||||
return myClient.Spectating;
|
||||
}
|
||||
|
||||
public static bool CanManageTalents(CharacterInfo targetInfo)
|
||||
{
|
||||
// in singleplayer we can do whatever we want
|
||||
@@ -901,10 +1083,16 @@ namespace Barotrauma
|
||||
// always allow managing talents for own character
|
||||
if (IsOwnCharacter(targetInfo)) { return true; }
|
||||
|
||||
// disallow managing talents while spectating
|
||||
if (IsSpectatingInMultiplayer()) { return false; }
|
||||
|
||||
// don't allow controlling non-bot characters
|
||||
if (targetInfo.Character is not { IsBot: true }) { return false; }
|
||||
|
||||
// lastly check if we have the permission to do this
|
||||
// only allow managing talents for bots on the same team
|
||||
if (!IsOnSameTeam(targetInfo)) { return false; }
|
||||
|
||||
// lastly, check if we have the permission to do this
|
||||
return GameMain.Client is { } client && client.HasPermission(ClientPermissions.ManageBotTalents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,9 +127,10 @@ namespace Barotrauma
|
||||
Campaign.OnMoneyChanged.RegisterOverwriteExisting(eventId, _ => RequestRefresh());
|
||||
}
|
||||
|
||||
public void RequestRefresh()
|
||||
public void RequestRefresh(bool refreshUpgrades = false)
|
||||
{
|
||||
needsRefresh = true;
|
||||
if (refreshUpgrades) { SelectTab(UpgradeTab.Upgrade); }
|
||||
}
|
||||
|
||||
private void RefreshAll()
|
||||
@@ -673,6 +674,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!upgrades.ContainsKey(category) && HasSwappableItems(category))
|
||||
{
|
||||
upgrades.Add(category, new List<UpgradePrefab>());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (category, prefabs) in upgrades)
|
||||
@@ -771,19 +776,29 @@ namespace Barotrauma
|
||||
{
|
||||
if (Submarine.MainSub == null) { return false; }
|
||||
subItems ??= GetSubItems();
|
||||
return subItems.Any(i =>
|
||||
i.Prefab.SwappableItem != null &&
|
||||
!i.IsHidden && i.AllowSwapping &&
|
||||
(i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) &&
|
||||
Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t)));
|
||||
return subItems.Any(item => HasSwappableItems(category, item));
|
||||
}
|
||||
|
||||
private static bool HasSwappableItems(UpgradeCategory category, Item item)
|
||||
{
|
||||
if (Submarine.MainSub == null) { return false; }
|
||||
return
|
||||
item.Prefab.SwappableItem != null &&
|
||||
!item.IsHidden && item.AllowSwapping &&
|
||||
(item.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == item.Prefab.Identifier)) &&
|
||||
Submarine.MainSub.IsEntityFoundOnThisSub(item, true) && category.ItemTags.Any(t => item.HasTag(t));
|
||||
}
|
||||
private static List<Item> GetSubItems() => Submarine.MainSub?.GetItems(true) ?? new List<Item>();
|
||||
|
||||
private void SelectUpgradeCategory(List<UpgradePrefab> prefabs, UpgradeCategory category, Submarine submarine)
|
||||
{
|
||||
if (selectedUpgradeCategoryLayout == null) { return; }
|
||||
|
||||
bool hasSwappableItems = HasSwappableItems(category);
|
||||
bool hasUpgradeModules = prefabs.Count > 0;
|
||||
|
||||
customizeTabOpen = !hasUpgradeModules && hasSwappableItems;
|
||||
|
||||
customizeTabOpen = false;
|
||||
|
||||
GUIComponent[] categoryFrames = GetFrames(category);
|
||||
@@ -799,9 +814,7 @@ namespace Barotrauma
|
||||
GUIFrame frame = new GUIFrame(rectT(1.0f, 0.4f, selectedUpgradeCategoryLayout));
|
||||
GUIFrame paddedFrame = new GUIFrame(rectT(0.93f, 0.9f, frame, Anchor.Center), style: null);
|
||||
|
||||
bool hasSwappableItems = HasSwappableItems(category);
|
||||
|
||||
float listHeight = hasSwappableItems ? 0.9f : 1.0f;
|
||||
float listHeight = hasSwappableItems && hasUpgradeModules ? 0.9f : 1.0f;
|
||||
|
||||
GUIListBox prefabList = new GUIListBox(rectT(1.0f, listHeight, paddedFrame, Anchor.BottomLeft))
|
||||
{
|
||||
@@ -810,7 +823,8 @@ namespace Barotrauma
|
||||
ScrollBarVisible = true
|
||||
};
|
||||
|
||||
if (hasSwappableItems)
|
||||
//both swappable items and upgrade modules -> create 2 tabs
|
||||
if (hasSwappableItems && hasUpgradeModules)
|
||||
{
|
||||
GUILayoutGroup buttonLayout = new GUILayoutGroup(rectT(1.0f, 0.1f, paddedFrame, anchor: Anchor.TopLeft), isHorizontal: true);
|
||||
|
||||
@@ -852,8 +866,15 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
CreateUpgradePrefabList(prefabList, category, prefabs, submarine);
|
||||
//only either upgrade modules or swappable items -> just create the list
|
||||
else if (hasUpgradeModules)
|
||||
{
|
||||
CreateUpgradePrefabList(prefabList, category, prefabs, submarine);
|
||||
}
|
||||
else if (hasSwappableItems)
|
||||
{
|
||||
CreateSwappableItemList(prefabList, category, submarine);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUpgradePrefabList(GUIListBox parent, UpgradeCategory category, List<UpgradePrefab> prefabs, Submarine submarine)
|
||||
@@ -1370,9 +1391,10 @@ namespace Barotrauma
|
||||
Item[] entitiesOnSub = drawnSubmarine.GetItems(true).Where(i => drawnSubmarine.IsEntityFoundOnThisSub(i, true)).ToArray();
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
{
|
||||
//hide categories with no upgrades in them
|
||||
if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category))) { continue; }
|
||||
if (entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
|
||||
//hide categories with no upgrades or swappables in them
|
||||
bool hasSwappableItems = HasSwappableItems(category);
|
||||
if (!hasSwappableItems && UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category))) { continue; }
|
||||
if (hasSwappableItems || entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
|
||||
{
|
||||
yield return category;
|
||||
}
|
||||
@@ -1534,7 +1556,7 @@ namespace Barotrauma
|
||||
|
||||
description.Padding = new Vector4(description.Padding.X, 24 * GUI.Scale, description.Padding.Z, description.Padding.W);
|
||||
List<Entity> pointsOfInterest = (from category in UpgradeCategory.Categories from item in submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs)
|
||||
where category.CanBeApplied(item, null) && item.IsPlayerTeamInteractable select item).Cast<Entity>().Distinct().ToList();
|
||||
where (category.CanBeApplied(item, null) || HasSwappableItems(category, item)) && item.IsPlayerTeamInteractable select item).Cast<Entity>().Distinct().ToList();
|
||||
|
||||
List<ushort> ids = GameMain.GameSession.SubmarineInfo?.LeftBehindDockingPortIDs ?? new List<ushort>();
|
||||
pointsOfInterest.AddRange(submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs).Where(item => ids.Contains(item.ID)));
|
||||
@@ -1799,7 +1821,7 @@ namespace Barotrauma
|
||||
// Disables the parent and only re-enables if the submarine contains valid items
|
||||
if (!category.IsWallUpgrade && drawnSubmarine?.Info != null)
|
||||
{
|
||||
if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category) && p.GetMaxLevel(drawnSubmarine.Info) > 0))
|
||||
if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category) && p.GetMaxLevel(drawnSubmarine.Info) > 0) && !HasSwappableItems(category))
|
||||
{
|
||||
parent.ToolTip = TextManager.Get("upgradecategorynotapplicable");
|
||||
parent.Enabled = false;
|
||||
|
||||
@@ -120,6 +120,7 @@ namespace Barotrauma
|
||||
private readonly GameTime fixedTime;
|
||||
|
||||
public Option<ConnectCommand> ConnectCommand = Option<ConnectCommand>.None();
|
||||
private string clientName;
|
||||
|
||||
private static SpriteBatch spriteBatch;
|
||||
|
||||
@@ -252,6 +253,16 @@ namespace Barotrauma
|
||||
try
|
||||
{
|
||||
ConnectCommand = Barotrauma.Networking.ConnectCommand.Parse(ConsoleArguments);
|
||||
|
||||
string clientNameFlagArg = args.FirstOrDefault(arg => arg.StartsWith("-username"));
|
||||
if (clientNameFlagArg != null)
|
||||
{
|
||||
int nextIndex = args.IndexOf(clientNameFlagArg) + 1;
|
||||
if (nextIndex < args.Length)
|
||||
{
|
||||
clientName = args[nextIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IndexOutOfRangeException e)
|
||||
{
|
||||
@@ -313,6 +324,8 @@ namespace Barotrauma
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
}
|
||||
|
||||
int display = GameSettings.CurrentConfig.Graphics.Display;
|
||||
|
||||
GraphicsWidth = GameSettings.CurrentConfig.Graphics.Width;
|
||||
GraphicsHeight = GameSettings.CurrentConfig.Graphics.Height;
|
||||
|
||||
@@ -340,7 +353,7 @@ namespace Barotrauma
|
||||
GraphicsDeviceManager.PreferredBackBufferFormat = SurfaceFormat.Color;
|
||||
GraphicsDeviceManager.PreferMultiSampling = false;
|
||||
GraphicsDeviceManager.SynchronizeWithVerticalRetrace = GameSettings.CurrentConfig.Graphics.VSync;
|
||||
SetWindowMode(GameSettings.CurrentConfig.Graphics.DisplayMode);
|
||||
SetWindowMode(GameSettings.CurrentConfig.Graphics.DisplayMode, display);
|
||||
|
||||
defaultViewport = new Viewport(0, 0, GraphicsWidth, GraphicsHeight);
|
||||
|
||||
@@ -353,8 +366,17 @@ namespace Barotrauma
|
||||
ResolutionChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void SetWindowMode(WindowMode windowMode)
|
||||
public void SetWindowMode(WindowMode windowMode, int display)
|
||||
{
|
||||
// We can't move the monitor while the window is fullscreen because of a restriction in SDL2, so as a workaround we switch to windowed mode first
|
||||
var prevDisplayMode = WindowMode;
|
||||
if (Window.TargetDisplay != display && prevDisplayMode != WindowMode.Windowed)
|
||||
{
|
||||
GraphicsDeviceManager.IsFullScreen = false;
|
||||
GraphicsDeviceManager.ApplyChanges();
|
||||
}
|
||||
Window.TargetDisplay = display;
|
||||
|
||||
WindowMode = windowMode;
|
||||
GraphicsDeviceManager.HardwareModeSwitch = windowMode != WindowMode.BorderlessWindowed;
|
||||
GraphicsDeviceManager.IsFullScreen = windowMode == WindowMode.Fullscreen || windowMode == WindowMode.BorderlessWindowed;
|
||||
@@ -723,7 +745,7 @@ namespace Barotrauma
|
||||
fixedTime.IsRunningSlowly = gameTime.IsRunningSlowly;
|
||||
TimeSpan addTime = new TimeSpan(0, 0, 0, 0, 16);
|
||||
fixedTime.ElapsedGameTime = addTime;
|
||||
fixedTime.TotalGameTime.Add(addTime);
|
||||
fixedTime.TotalGameTime = fixedTime.TotalGameTime.Add(addTime);
|
||||
base.Update(fixedTime);
|
||||
|
||||
PlayerInput.Update(Timing.Step);
|
||||
@@ -779,7 +801,7 @@ namespace Barotrauma
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveUtil.LoadGame(saveFiles.OrderBy(file => file.SaveTime).Last().FilePath);
|
||||
SaveUtil.LoadGame(CampaignDataPath.CreateRegular(saveFiles.OrderBy(file => file.SaveTime).Last().FilePath));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -804,18 +826,27 @@ namespace Barotrauma
|
||||
}
|
||||
MainMenuScreen.Select();
|
||||
|
||||
string clientNameString = clientName ?? MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(SteamManager.GetUsername());
|
||||
|
||||
if (connectCommand.SteamLobbyIdOption.TryUnwrap(out var lobbyId))
|
||||
{
|
||||
SteamManager.JoinLobby(lobbyId.Value, joinServer: true);
|
||||
}
|
||||
else if (connectCommand.NameAndP2PEndpointsOption.TryUnwrap(out var nameAndEndpoint)
|
||||
&& nameAndEndpoint is { ServerName: var serverName, Endpoints: var endpoints })
|
||||
else if ((connectCommand.NameAndP2PEndpointsOption.TryUnwrap(out var nameAndEndpoint) && nameAndEndpoint is { ServerName: var serverName, Endpoints: var endpoints }))
|
||||
{
|
||||
Client = new GameClient(MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(SteamManager.GetUsername()),
|
||||
Client = new GameClient(clientNameString,
|
||||
endpoints.Cast<Endpoint>().ToImmutableArray(),
|
||||
string.IsNullOrWhiteSpace(serverName) ? endpoints.First().StringRepresentation : serverName,
|
||||
Option<int>.None());
|
||||
}
|
||||
else if ((connectCommand.NameAndLidgrenEndpointOption.TryUnwrap(out var nameAndLidgrenEndpoint) && nameAndLidgrenEndpoint is { ServerName: var lidgrenServerName, Endpoint: var endpoint }))
|
||||
{
|
||||
Client = new GameClient(
|
||||
clientNameString,
|
||||
endpoint,
|
||||
string.IsNullOrWhiteSpace(lidgrenServerName) ? endpoint.StringRepresentation : lidgrenServerName,
|
||||
Option<int>.None());
|
||||
}
|
||||
|
||||
ConnectCommand = Option<ConnectCommand>.None();
|
||||
}
|
||||
@@ -1145,7 +1176,7 @@ namespace Barotrauma
|
||||
}
|
||||
GameSession.Campaign?.End();
|
||||
|
||||
SaveUtil.SaveGame(GameSession.SavePath);
|
||||
SaveUtil.SaveGame(GameSession.DataPath);
|
||||
}
|
||||
|
||||
if (Client != null)
|
||||
@@ -1162,11 +1193,12 @@ namespace Barotrauma
|
||||
GameSession.GameMode?.Preset.Identifier.Value ?? "none",
|
||||
GameSession.RoundDuration);
|
||||
string eventId = "QuitRound:" + (GameSession.GameMode?.Preset.Identifier.Value ?? "none") + ":";
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:CurrentIntensity", GameSession.EventManager.CurrentIntensity);
|
||||
//disabled to reduce the amount of data we collect through GA
|
||||
/*GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:CurrentIntensity", GameSession.EventManager.CurrentIntensity);
|
||||
foreach (var activeEvent in GameSession.EventManager.ActiveEvents)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:ActiveEvents:" + activeEvent.Prefab.Identifier);
|
||||
}
|
||||
}*/
|
||||
GameSession.LogEndRoundStats(eventId);
|
||||
if (GameSession.GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace Barotrauma
|
||||
// check if the store can afford the item
|
||||
if (store.Balance < itemValue) { continue; }
|
||||
// TODO: Write logic for prioritizing certain items over others (e.g. lone Battery Cell should be preferred over one inside a Stun Baton)
|
||||
var matchingItems = sellableItems.Where(i => i.Prefab == item.ItemPrefab);
|
||||
var matchingItems = sellableItems.Where(i => i.Prefab.Identifier == item.ItemPrefabIdentifier);
|
||||
int count = Math.Min(item.Quantity, matchingItems.Count());
|
||||
SoldItem.SellOrigin origin = sellingMode == Store.StoreTab.Sell ? SoldItem.SellOrigin.Character : SoldItem.SellOrigin.Submarine;
|
||||
if (origin == SoldItem.SellOrigin.Character || GameMain.IsSingleplayer)
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Barotrauma
|
||||
public GUIComponent ReportButtonFrame { get; set; }
|
||||
|
||||
private GUIFrame guiFrame;
|
||||
private GUIFrame crewArea;
|
||||
private GUILayoutGroup crewArea;
|
||||
private GUIListBox crewList;
|
||||
private float crewListOpenState;
|
||||
private bool _isCrewMenuOpen = true;
|
||||
@@ -47,7 +47,7 @@ namespace Barotrauma
|
||||
|
||||
/// <summary>
|
||||
/// This property stores the preference in settings. Don't use for automatic logic.
|
||||
/// Use AutoShowCrewList(), AutoHideCrewList(), and ResetCrewList().
|
||||
/// Use AutoHideCrewList(), and ResetCrewList().
|
||||
/// </summary>
|
||||
public bool IsCrewMenuOpen
|
||||
{
|
||||
@@ -62,11 +62,9 @@ namespace Barotrauma
|
||||
|
||||
public static bool PreferCrewMenuOpen = true;
|
||||
|
||||
public bool AutoShowCrewList() => _isCrewMenuOpen = true;
|
||||
|
||||
public void AutoHideCrewList() => _isCrewMenuOpen = false;
|
||||
|
||||
public void ResetCrewList() => _isCrewMenuOpen = PreferCrewMenuOpen;
|
||||
public void ResetCrewListOpenState() => _isCrewMenuOpen = PreferCrewMenuOpen;
|
||||
|
||||
const float CommandNodeAnimDuration = 0.2f;
|
||||
|
||||
@@ -93,12 +91,30 @@ namespace Barotrauma
|
||||
|
||||
#region Crew Area
|
||||
|
||||
crewArea = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform), style: null, color: Color.Transparent)
|
||||
crewArea = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
CanBeFocused = false
|
||||
Stretch = true
|
||||
};
|
||||
crewArea.RectTransform.NonScaledSize = HUDLayoutSettings.CrewArea.Size;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
CharacterTeamType teamId = i == 0 ? CharacterTeamType.Team1 : CharacterTeamType.Team2;
|
||||
var nameText = new GUITextBlock(new RectTransform(new Point(crewArea.Rect.Width - GUI.IntScale(10), GUI.IntScale(30)), crewArea.RectTransform), CombatMission.GetTeamName(teamId), textColor: CombatMission.GetTeamColor(teamId))
|
||||
{
|
||||
ForceUpperCase = ForceUpperCase.Yes,
|
||||
TextGetter = () => CombatMission.GetTeamName(teamId),
|
||||
Visible = false,
|
||||
IgnoreLayoutGroups = true,
|
||||
UserData = teamId
|
||||
};
|
||||
var teamIcon = new GUIImage(new RectTransform(Vector2.One, nameText.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.BothHeight), style: i == 0 ? "CoalitionIcon" : "SeparatistIcon")
|
||||
{
|
||||
Color = nameText.TextColor
|
||||
};
|
||||
nameText.Padding = new Vector4(teamIcon.Rect.Width + nameText.Padding.X, nameText.Padding.Y, nameText.Padding.Z, nameText.Padding.W);
|
||||
}
|
||||
|
||||
// AbsoluteOffset is set in UpdateProjectSpecific based on crewListOpenState
|
||||
crewList = new GUIListBox(new RectTransform(Vector2.One, crewArea.RectTransform), style: null, isScrollBarOnDefaultSide: false)
|
||||
{
|
||||
@@ -177,6 +193,12 @@ namespace Barotrauma
|
||||
|
||||
ChatBox.InputBox.OnTextChanged += ChatBox.TypingChatMessage;
|
||||
}
|
||||
else if (GameMain.Client == null)
|
||||
{
|
||||
//this method would throw a non-descriptive nullref exception later when trying to access the chatbox
|
||||
//if we'd try to continue from here, better to throw a more descriptive one at this point
|
||||
throw new InvalidOperationException($"Attempted to initialize {nameof(CrewManager)} for multiplayer, but no multiplayer client is active. Are you trying to load a multiplayer save in singleplayer?");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -290,6 +312,14 @@ namespace Barotrauma
|
||||
|
||||
#region Character list management
|
||||
|
||||
/// <summary>
|
||||
/// Note: this is only works client-side. TODO: make it work server-side too?
|
||||
/// </summary>
|
||||
public IEnumerable<Character> GetCharacters()
|
||||
{
|
||||
return characters;
|
||||
}
|
||||
|
||||
public Rectangle GetActiveCrewArea()
|
||||
{
|
||||
return crewArea.Rect;
|
||||
@@ -562,9 +592,19 @@ namespace Barotrauma
|
||||
public bool CharacterClicked(GUIComponent component, object selection)
|
||||
{
|
||||
if (!AllowCharacterSwitch) { return false; }
|
||||
if (!(selection is Character character) || character.IsDead || character.IsUnconscious) { return false; }
|
||||
if (selection is not Character character || character.IsDead || character.IsUnconscious) { return false; }
|
||||
if (!character.IsOnPlayerTeam) { return false; }
|
||||
|
||||
if (GameMain.IsMultiplayer)
|
||||
{
|
||||
if (Character.Controlled == null)
|
||||
{
|
||||
Camera cam = Screen.Selected.Cam;
|
||||
cam.Position = character.DrawPosition;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SelectCharacter(character);
|
||||
if (GUI.KeyboardDispatcher.Subscriber == crewList) { GUI.KeyboardDispatcher.Subscriber = null; }
|
||||
return true;
|
||||
@@ -631,10 +671,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (crewList != this.crewList) { return; }
|
||||
if (draggedElementData is not Character) { return; }
|
||||
if (!IsSinglePlayer) { return; }
|
||||
if (crewList.HasDraggedElementIndexChanged)
|
||||
{
|
||||
UpdateCrewListIndices();
|
||||
if (IsSinglePlayer) { UpdateCrewListIndices(); }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -645,10 +684,13 @@ namespace Barotrauma
|
||||
private void ResetCrewListIndex(Character c)
|
||||
{
|
||||
if (c?.Info == null) { return; }
|
||||
c.Info.CrewListIndex = -1;
|
||||
UpdateCrewListIndices();
|
||||
//default to the bottom of the list
|
||||
c.Info.CrewListIndex = int.MaxValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the <see cref="CharacterInfo.CrewListIndex"/> of the characters based on their order in the crew list
|
||||
/// </summary>
|
||||
private void UpdateCrewListIndices()
|
||||
{
|
||||
if (crewList == null) { return; }
|
||||
@@ -661,20 +703,23 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order the crew list according to the characters' <see cref="CharacterInfo.CrewListIndex"/>
|
||||
/// </summary>
|
||||
private void SortCrewList()
|
||||
{
|
||||
if (crewList == null) { return; }
|
||||
crewList.Content.RectTransform.SortChildren((x, y) =>
|
||||
{
|
||||
var infoX = (x.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
|
||||
var infoY = (y.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
|
||||
if (infoX.HasValue)
|
||||
int? index1 = (x.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
|
||||
int? index2 = (y.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
|
||||
if (index1.HasValue)
|
||||
{
|
||||
return infoY.HasValue ? infoX.Value.CompareTo(infoY.Value) : -1;
|
||||
return index2.HasValue ? index1.Value.CompareTo(index2.Value) : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return infoY.HasValue ? 1 : 0;
|
||||
return index2.HasValue ? 1 : 0;
|
||||
}
|
||||
});
|
||||
UpdateCrewListIndices();
|
||||
@@ -1312,6 +1357,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (ConversationAction.IsDialogOpen) { return; }
|
||||
if (!AllowCharacterSwitch) { return; }
|
||||
if (character == null || character.Removed) { return; }
|
||||
//make the previously selected character wait in place for some time
|
||||
//(so they don't immediately start idling and walking away from their station)
|
||||
var aiController = Character.Controlled?.AIController;
|
||||
@@ -1323,6 +1369,16 @@ namespace Barotrauma
|
||||
{
|
||||
GameSession.TabMenuInstance.SelectInfoFrameTab(TabMenu.SelectedTab);
|
||||
}
|
||||
if (character.SelectedItem?.GetComponent<Controller>() == null && character.SelectedCharacter == null)
|
||||
{
|
||||
ResetCrewListOpenState();
|
||||
ChatBox.ResetChatBoxOpenState();
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoHideCrewList();
|
||||
ChatBox.AutoHideChatBox();
|
||||
}
|
||||
}
|
||||
|
||||
private int TryAdjustIndex(int amount)
|
||||
@@ -1340,7 +1396,7 @@ namespace Barotrauma
|
||||
if (index > lastIndex) { index = 0; }
|
||||
if (index < 0) { index = lastIndex; }
|
||||
|
||||
if ((crewList.Content.GetChild(index)?.UserData as Character)?.IsOnPlayerTeam ?? false)
|
||||
if (crewList.Content.GetChild(index)?.UserData is Character { IsOnPlayerTeam: true, Removed: false })
|
||||
{
|
||||
return index;
|
||||
}
|
||||
@@ -1635,6 +1691,18 @@ namespace Barotrauma
|
||||
{
|
||||
crewArea.Visible = characters.Count > 0 && CharacterHealth.OpenHealthWindow == null;
|
||||
|
||||
CharacterTeamType myTeam = Character.Controlled?.TeamID ?? GameMain.Client?.MyClient?.TeamID ?? CharacterTeamType.Team1;
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
var team1Text = crewArea.GetChildByUserData(CharacterTeamType.Team1);
|
||||
team1Text.Visible = myTeam == CharacterTeamType.Team1;
|
||||
team1Text.IgnoreLayoutGroups = !team1Text.Visible;
|
||||
|
||||
var team2Text = crewArea.GetChildByUserData(CharacterTeamType.Team2);
|
||||
team2Text.Visible = myTeam == CharacterTeamType.Team2;
|
||||
team2Text.IgnoreLayoutGroups = !team2Text.Visible;
|
||||
}
|
||||
|
||||
foreach (GUIComponent characterComponent in crewList.Content.Children)
|
||||
{
|
||||
if (characterComponent.UserData is Character character)
|
||||
@@ -1645,7 +1713,7 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
|
||||
characterComponent.Visible = Character.Controlled == null || Character.Controlled.TeamID == character.TeamID;
|
||||
characterComponent.Visible = myTeam == character.TeamID;
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC && Character.Controlled != null &&
|
||||
(character.CurrentHull == Character.Controlled.CurrentHull || Vector2.DistanceSquared(Character.Controlled.WorldPosition, character.WorldPosition) < 500.0f * 500.0f))
|
||||
{
|
||||
@@ -1873,7 +1941,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GameMain.GameSession?.CrewManager == null)
|
||||
if (GameMain.GameSession?.CrewManager == null || Screen.Selected is { IsEditor: true })
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -2010,7 +2078,7 @@ namespace Barotrauma
|
||||
// Character context works differently to others as we still use the "basic" command interface,
|
||||
// but the order will be automatically assigned to this character
|
||||
isContextual = forceContextual;
|
||||
if (entityContext is Character character)
|
||||
if (entityContext is Character { Info: not null } character)
|
||||
{
|
||||
characterContext = character;
|
||||
itemContext = null;
|
||||
@@ -2098,7 +2166,7 @@ namespace Barotrauma
|
||||
CreateNodeConnectors();
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
Character.Controlled.dontFollowCursor = true;
|
||||
Character.Controlled.FollowCursor = false;
|
||||
}
|
||||
|
||||
HintManager.OnShowCommandInterface();
|
||||
@@ -2242,7 +2310,7 @@ namespace Barotrauma
|
||||
returnNodeHotkey = expandNodeHotkey = Keys.None;
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
Character.Controlled.dontFollowCursor = false;
|
||||
Character.Controlled.FollowCursor = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2511,7 +2579,7 @@ namespace Barotrauma
|
||||
// --> Create shortcut node for Steer order
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder("steer") && IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["steer"]) &&
|
||||
subItems.Find(i => i.HasTag(Tags.NavTerminal) && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedItem == nav) &&
|
||||
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
|
||||
nav.GetComponent<Steering>() is Steering { HasPower: true } steering)
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["steer"], steering.Item, steering);
|
||||
AddOrderNode(order);
|
||||
@@ -2625,9 +2693,9 @@ namespace Barotrauma
|
||||
bool IsNonDuplicateOrder(Order order) => IsNonDuplicateOrderPrefab(order.Prefab, order.Option);
|
||||
bool IsNonDuplicateOrderPrefab(OrderPrefab orderPrefab, Identifier option = default)
|
||||
{
|
||||
return characterContext == null || (option.IsEmpty ?
|
||||
return characterContext == null || (characterContext.CurrentOrders != null && (option.IsEmpty ?
|
||||
characterContext.CurrentOrders.None(oi => oi?.Identifier == orderPrefab?.Identifier) :
|
||||
characterContext.CurrentOrders.None(oi => oi?.Identifier == orderPrefab?.Identifier && oi.Option == option));
|
||||
characterContext.CurrentOrders.None(oi => oi?.Identifier == orderPrefab?.Identifier && oi?.Option == option)));
|
||||
}
|
||||
void AddOrderNodeWithIdentifier(string identifier)
|
||||
{
|
||||
@@ -3000,7 +3068,7 @@ namespace Barotrauma
|
||||
{
|
||||
optionElement.OnSecondaryClicked = (button, _) => CreateAssignmentNodes(button);
|
||||
}
|
||||
var colorMultiplier = characters.Any(c => c.CurrentOrders.Any(o => o != null &&
|
||||
var colorMultiplier = characters.Any(c => c.CurrentOrders != null && c.CurrentOrders.Any(o => o != null &&
|
||||
o.Identifier == userData.Order.Identifier &&
|
||||
o.TargetEntity == userData.Order.TargetEntity)) ? 0.5f : 1f;
|
||||
CreateNodeIcon(Vector2.One, optionElement.RectTransform, item.Prefab.MinimapIcon ?? order.SymbolSprite, order.Color * colorMultiplier, tooltip: item.Name);
|
||||
@@ -3654,7 +3722,7 @@ namespace Barotrauma
|
||||
bool hasLeaks = Character.Controlled.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f);
|
||||
ToggleReportButton("reportbreach", hasLeaks);
|
||||
|
||||
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled, false));
|
||||
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled, targetCharactersInOtherSubs: false));
|
||||
ToggleReportButton("reportintruders", hasIntruders);
|
||||
|
||||
foreach (GUIComponent reportButton in ReportButtonFrame.Children)
|
||||
@@ -3780,5 +3848,72 @@ namespace Barotrauma
|
||||
GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
|
||||
}
|
||||
}
|
||||
|
||||
private class CharacterInfoComparer : IEqualityComparer<CharacterInfo>
|
||||
{
|
||||
public bool Equals(CharacterInfo x, CharacterInfo y)
|
||||
{
|
||||
if (ReferenceEquals(x, y))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x is null || y is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return x.ID == y.ID;
|
||||
}
|
||||
|
||||
public int GetHashCode(CharacterInfo obj)
|
||||
{
|
||||
return obj.ID;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UpdateReserveBenchIfNeeded(IEnumerable<CharacterInfo> updatedReserveBench)
|
||||
{
|
||||
var newBench = updatedReserveBench.ToHashSet(new CharacterInfoComparer());
|
||||
var currentBench = reserveBench.ToHashSet(new CharacterInfoComparer());
|
||||
|
||||
bool updateNeeded = !newBench.SetEquals(currentBench);
|
||||
if (updateNeeded)
|
||||
{
|
||||
reserveBench.Clear(); // since this is the reserve bench (characters not instantiated), there's no need to retain any references etc
|
||||
reserveBench.AddRange(updatedReserveBench);
|
||||
}
|
||||
|
||||
return updateNeeded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will update which CharacterInfos should be in CrewManager and which shouldn't, excluding the reserve bench.
|
||||
/// The CharacterInfos themselves aren't updated, they will only be either added, removed, or kept as-is.
|
||||
/// </summary>
|
||||
public bool UpdateCrewManagerIfNecessary(List<CharacterInfo> updatedCrewManager)
|
||||
{
|
||||
// CharacterInfos no longer in the server's CrewManager
|
||||
var toRemove = characterInfos.Where(original => updatedCrewManager.None(updated => updated.ID == original.ID)).ToList();
|
||||
// CharacterInfos that are in the server's CrewManager but not on the client yet
|
||||
var toAdd = updatedCrewManager.Where(updated => characterInfos.None(original => original.ID == updated.ID)).ToList();
|
||||
|
||||
foreach (CharacterInfo characterInfo in toRemove)
|
||||
{
|
||||
if (characterInfo.Character is Character existingCharacter)
|
||||
{
|
||||
if (!existingCharacter.IsBot) { continue; } // on client side players are also stored here, we should skip those in this case
|
||||
RemoveCharacter(characterInfo.Character, removeInfo: true, resetCrewListIndex: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
characterInfos.Remove(characterInfo);
|
||||
}
|
||||
}
|
||||
|
||||
characterInfos.AddRange(toAdd);
|
||||
|
||||
return toRemove.Count > 0 || toAdd.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ namespace Barotrauma
|
||||
default:
|
||||
ShowCampaignUI = true;
|
||||
CampaignUI.SelectTab(npc.CampaignInteractionType, npc);
|
||||
CampaignUI.UpgradeStore?.RequestRefresh();
|
||||
CampaignUI.UpgradeStore?.RequestRefresh(refreshUpgrades: true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -395,13 +395,15 @@ namespace Barotrauma
|
||||
|
||||
protected void TryEndRoundWithFuelCheck(Action onConfirm, Action onReturnToMapScreen)
|
||||
{
|
||||
if (Submarine.MainSub == null) { return; }
|
||||
|
||||
Submarine.MainSub.CheckFuel();
|
||||
bool lowFuel = Submarine.MainSub.Info.LowFuel;
|
||||
if (PendingSubmarineSwitch != null)
|
||||
{
|
||||
lowFuel = TransferItemsOnSubSwitch ? (lowFuel && PendingSubmarineSwitch.LowFuel) : PendingSubmarineSwitch.LowFuel;
|
||||
}
|
||||
if (Level.IsLoadedFriendlyOutpost && lowFuel && CargoManager.PurchasedItems.None(i => i.Value.Any(pi => pi.ItemPrefab.Tags.Contains("reactorfuel"))))
|
||||
if (Level.IsLoadedFriendlyOutpost && lowFuel && CargoManager.PurchasedItems.None(i => i.Value.Any(pi => pi.ItemPrefab.Tags.Contains(Tags.ReactorFuel))))
|
||||
{
|
||||
var extraConfirmationBox =
|
||||
new GUIMessageBox(TextManager.Get("lowfuelheader"),
|
||||
|
||||
@@ -532,6 +532,7 @@ namespace Barotrauma
|
||||
|
||||
bool isFirstRound = msg.ReadBoolean();
|
||||
byte campaignID = msg.ReadByte();
|
||||
byte roundId = msg.ReadByte();
|
||||
UInt16 saveID = msg.ReadUInt16();
|
||||
string mapSeed = msg.ReadString();
|
||||
|
||||
@@ -541,7 +542,7 @@ namespace Barotrauma
|
||||
{
|
||||
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
|
||||
|
||||
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty, mapSeed);
|
||||
GameMain.GameSession = new GameSession(null, Option.None, CampaignDataPath.CreateRegular(savePath), GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty, mapSeed);
|
||||
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
|
||||
campaign.CampaignID = campaignID;
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
@@ -553,7 +554,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.Misc))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (Misc)");
|
||||
DebugConsole.Log("Received campaign update (Misc), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
bool purchasedHullRepairs = msg.ReadBoolean();
|
||||
bool purchasedItemRepairs = msg.ReadBoolean();
|
||||
@@ -571,7 +572,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.MapAndMissions))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (MapAndMissions)");
|
||||
DebugConsole.Log("Received campaign update (MapAndMissions), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
bool forceMapUI = msg.ReadBoolean();
|
||||
bool allowDebugTeleport = msg.ReadBoolean();
|
||||
@@ -634,7 +635,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.SubList))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (SubList)");
|
||||
DebugConsole.Log("Received campaign update (SubList), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
ushort ownedSubCount = msg.ReadUInt16();
|
||||
List<ushort> ownedSubIndices = new List<ushort>();
|
||||
@@ -679,7 +680,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.UpgradeManager))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (UpgradeManager)");
|
||||
DebugConsole.Log("Received campaign update (UpgradeManager), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
|
||||
ushort pendingUpgradeCount = msg.ReadUInt16();
|
||||
@@ -737,7 +738,7 @@ namespace Barotrauma
|
||||
|
||||
if (requiredFlags.HasFlag(NetFlags.ItemsInBuyCrate))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (ItemsInBuyCrate)");
|
||||
DebugConsole.Log("Received campaign update (ItemsInBuyCrate), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
var buyCrateItems = ReadPurchasedItems(msg, sender: null);
|
||||
if (ShouldApply(NetFlags.ItemsInBuyCrate, id, requireUpToDateSave: true))
|
||||
@@ -753,7 +754,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.ItemsInSellFromSubCrate))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (ItemsInSellFromSubCrate)");
|
||||
DebugConsole.Log("Received campaign update (ItemsInSellFromSubCrate), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
var subSellCrateItems = ReadPurchasedItems(msg, sender: null);
|
||||
if (ShouldApply(NetFlags.ItemsInSellFromSubCrate, id, requireUpToDateSave: true))
|
||||
@@ -769,7 +770,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.PurchasedItems))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (PuchasedItems)");
|
||||
DebugConsole.Log("Received campaign update (PuchasedItems), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
var purchasedItems = ReadPurchasedItems(msg, sender: null);
|
||||
if (ShouldApply(NetFlags.PurchasedItems, id, requireUpToDateSave: true))
|
||||
@@ -785,7 +786,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.SoldItems))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (SoldItems)");
|
||||
DebugConsole.Log("Received campaign update (SoldItems), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
var soldItems = ReadSoldItems(msg);
|
||||
if (ShouldApply(NetFlags.SoldItems, id, requireUpToDateSave: true))
|
||||
@@ -801,7 +802,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.Reputation))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (Reputation)");
|
||||
DebugConsole.Log("Received campaign update (Reputation), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
|
||||
byte factionsCount = msg.ReadByte();
|
||||
@@ -828,7 +829,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.CharacterInfo))
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (CharacterInfo)");
|
||||
DebugConsole.Log("Received campaign update (CharacterInfo), round id: " + roundId);
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
bool hasCharacterData = msg.ReadBoolean();
|
||||
CharacterInfo myCharacterInfo = null;
|
||||
@@ -837,16 +838,27 @@ namespace Barotrauma
|
||||
{
|
||||
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg, requireJobPrefabFound: !waitForModsDownloaded);
|
||||
}
|
||||
if (!waitForModsDownloaded && ShouldApply(NetFlags.CharacterInfo, id, requireUpToDateSave: true))
|
||||
//don't require the correct round ID for the character info if we're in the lobby
|
||||
// = allow updating the character to the latest one in the lobby, even though we've not loaded to the same round as the server
|
||||
if (!waitForModsDownloaded && ShouldApply(NetFlags.CharacterInfo, id, requireUpToDateSave: true, requireCorrectRoundId: Screen.Selected != GameMain.NetLobbyScreen))
|
||||
{
|
||||
if (myCharacterInfo != null)
|
||||
{
|
||||
GameMain.Client.CharacterInfo = myCharacterInfo;
|
||||
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo);
|
||||
GameMain.GameSession.RefreshAnyOpenPlayerInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
//don't reset the character info nor the open UI here here,
|
||||
//the client needs it to be able to customize the character they want to next spawn as
|
||||
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
|
||||
//if we've already discarded our current character and the server is just "verifying" that,
|
||||
//no need to refresh the UI (no changes, refreshing would just throw the client out of the character settings panel)
|
||||
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
|
||||
{
|
||||
GameMain.GameSession.RefreshAnyOpenPlayerInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -863,8 +875,14 @@ namespace Barotrauma
|
||||
}
|
||||
campaign.SuppressStateSending = false;
|
||||
|
||||
bool ShouldApply(NetFlags flag, UInt16 id, bool requireUpToDateSave)
|
||||
bool ShouldApply(NetFlags flag, UInt16 id, bool requireUpToDateSave, bool requireCorrectRoundId = true)
|
||||
{
|
||||
if (requireCorrectRoundId && roundId != campaign.RoundID)
|
||||
{
|
||||
DebugConsole.Log($"Received campaing update for a different round (client: {campaign.RoundID}, server: {roundId}), ignoring...");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NetIdUtils.IdMoreRecent(id, campaign.GetLastUpdateIdForFlag(flag)) &&
|
||||
(!requireUpToDateSave || saveID == campaign.LastSaveID))
|
||||
{
|
||||
@@ -919,26 +937,42 @@ namespace Barotrauma
|
||||
|
||||
ushort pendingHireLength = msg.ReadUInt16();
|
||||
List<UInt16> pendingHires = new List<UInt16>();
|
||||
bool[] pendingHiresToReserveBench = new bool[pendingHireLength];
|
||||
for (int i = 0; i < pendingHireLength; i++)
|
||||
{
|
||||
pendingHires.Add(msg.ReadUInt16());
|
||||
pendingHiresToReserveBench[i] = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
ushort hiredLength = msg.ReadUInt16();
|
||||
List<CharacterInfo> hiredCharacters = new List<CharacterInfo>();
|
||||
for (int i = 0; i < hiredLength; i++)
|
||||
List<CharacterInfo> updatedCrewManager = new List<CharacterInfo>();
|
||||
ushort crewLength = msg.ReadUInt16();
|
||||
for (int i = 0; i < crewLength; i++)
|
||||
{
|
||||
CharacterInfo hired = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
|
||||
hired.Salary = msg.ReadInt32();
|
||||
hiredCharacters.Add(hired);
|
||||
CharacterInfo crewMember = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
|
||||
if (crewMember.IsNewHire)
|
||||
{
|
||||
hiredCharacters.Add(crewMember);
|
||||
}
|
||||
updatedCrewManager.Add(crewMember);
|
||||
}
|
||||
bool crewManagerUpdated = GameMain.GameSession.CrewManager?.UpdateCrewManagerIfNecessary(updatedCrewManager) ?? false;
|
||||
|
||||
ushort reserveBenchLength = msg.ReadUInt16();
|
||||
List<CharacterInfo> updatedReserveBench = new List<CharacterInfo>();
|
||||
for (int i = 0; i < reserveBenchLength; i++)
|
||||
{
|
||||
CharacterInfo info = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
|
||||
updatedReserveBench.Add(info);
|
||||
}
|
||||
bool reserveBenchUpdated = GameMain.GameSession.CrewManager?.UpdateReserveBenchIfNeeded(updatedReserveBench) ?? false;
|
||||
|
||||
bool renameCrewMember = msg.ReadBoolean();
|
||||
if (renameCrewMember)
|
||||
{
|
||||
UInt16 renamedIdentifier = msg.ReadUInt16();
|
||||
string newName = msg.ReadString();
|
||||
CharacterInfo renamedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.ID == renamedIdentifier);
|
||||
CharacterInfo renamedCharacter = CrewManager.GetCharacterInfos(includeReserveBench: true).FirstOrDefault(info => info.ID == renamedIdentifier);
|
||||
if (renamedCharacter != null)
|
||||
{
|
||||
CrewManager.RenameCharacter(renamedCharacter, newName);
|
||||
@@ -955,7 +989,7 @@ namespace Barotrauma
|
||||
if (fireCharacter)
|
||||
{
|
||||
UInt16 firedIdentifier = msg.ReadUInt16();
|
||||
CharacterInfo firedCharacter = CrewManager.GetCharacterInfos().FirstOrDefault(info => info.ID == firedIdentifier);
|
||||
CharacterInfo firedCharacter = CrewManager.GetCharacterInfos(includeReserveBench: true).FirstOrDefault(info => info.ID == firedIdentifier);
|
||||
// this one might and is allowed to be null since the character is already fired on the original sender's game
|
||||
if (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); }
|
||||
}
|
||||
@@ -967,8 +1001,8 @@ namespace Barotrauma
|
||||
{
|
||||
CampaignUI.HRManagerUI.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (hiredCharacters.Any()) { CampaignUI.HRManagerUI.ValidateHires(hiredCharacters, takeMoney: false, createNotification: createNotification); }
|
||||
CampaignUI.HRManagerUI.SetPendingHires(pendingHires, map.CurrentLocation);
|
||||
if (renameCrewMember || fireCharacter) { CampaignUI.HRManagerUI.UpdateCrew(); }
|
||||
//don't check the crew size limit: if the server says someone's hired, then it's so
|
||||
CampaignUI.HRManagerUI.SetPendingHires(pendingHires, pendingHiresToReserveBench, map.CurrentLocation, checkCrewSizeLimit: false);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -979,6 +1013,11 @@ namespace Barotrauma
|
||||
CurrentLocation?.ForceHireableCharacters(availableHires);
|
||||
}
|
||||
|
||||
if (fireCharacter || renameCrewMember || crewManagerUpdated || reserveBenchUpdated)
|
||||
{
|
||||
CampaignUI?.HRManagerUI?.RefreshHRView();
|
||||
GameMain.GameSession?.DeathPrompt?.UpdateBotList();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientReadMoney(IReadMessage inc)
|
||||
@@ -1044,7 +1083,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
public override void Save(XElement element, bool isSavingOnLoading)
|
||||
{
|
||||
//do nothing, the clients get the save files from the server
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ namespace Barotrauma
|
||||
if (!savedOnStart)
|
||||
{
|
||||
GUI.SetSavingIndicatorState(true);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.DataPath, isSavingOnLoading: true);
|
||||
savedOnStart = true;
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ namespace Barotrauma
|
||||
if (success)
|
||||
{
|
||||
// Event history must be registered before ending the round or it will be cleared
|
||||
GameMain.GameSession.EventManager.RegisterEventHistory();
|
||||
GameMain.GameSession.EventManager.StoreEventDataAtRoundEnd();
|
||||
}
|
||||
GameMain.GameSession.EndRound("", transitionType);
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
@@ -448,7 +448,7 @@ namespace Barotrauma
|
||||
if (success)
|
||||
{
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.DataPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -479,7 +479,7 @@ namespace Barotrauma
|
||||
protected override void EndCampaignProjSpecific()
|
||||
{
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.DataPath);
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
GameMain.CampaignEndScreen.OnFinished = () =>
|
||||
@@ -672,7 +672,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
public override void Save(XElement element, bool isSavingOnLoading)
|
||||
{
|
||||
XElement modeElement = new XElement("SinglePlayerCampaign",
|
||||
new XAttribute("purchasedlostshuttles", PurchasedLostShuttles),
|
||||
|
||||
@@ -7,7 +7,7 @@ using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class TestGameMode : GameMode
|
||||
partial class TestGameMode : GameMode
|
||||
{
|
||||
public Action OnRoundEnd;
|
||||
|
||||
@@ -22,18 +22,6 @@ namespace Barotrauma
|
||||
|
||||
private GUIButton createEventButton;
|
||||
|
||||
public TestGameMode(GameModePreset preset) : base(preset)
|
||||
{
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs.OrderBy(p => p.Identifier))
|
||||
{
|
||||
for (int i = 0; i < jobPrefab.InitialCount; i++)
|
||||
{
|
||||
var variant = Rand.Range(0, jobPrefab.Variants);
|
||||
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
@@ -42,14 +30,21 @@ 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)
|
||||
switch (submarine.Info.Type)
|
||||
{
|
||||
submarine.PhysicsBody.BodyType = FarseerPhysics.BodyType.Static;
|
||||
case SubmarineType.Outpost:
|
||||
case SubmarineType.OutpostModule:
|
||||
case SubmarineType.Wreck:
|
||||
case SubmarineType.BeaconStation:
|
||||
//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
|
||||
submarine.PhysicsBody.BodyType = FarseerPhysics.BodyType.Static;
|
||||
if (submarine.Info.ShouldBeRuin)
|
||||
{
|
||||
submarine.Info.Type = SubmarineType.Ruin;
|
||||
}
|
||||
submarine.TeamID = submarine.Info.IsOutpost ? CharacterTeamType.FriendlyNPC : CharacterTeamType.None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
GameMain.GameSession = new GameSession(subInfo, GameModePreset.Tutorial, missionPrefabs: null);
|
||||
GameMain.GameSession = new GameSession(subInfo, Option.None, GameModePreset.Tutorial, missionPrefabs: null);
|
||||
(GameMain.GameSession.GameMode as TutorialMode).Tutorial = this;
|
||||
|
||||
if (generationParams is not null)
|
||||
@@ -138,7 +138,7 @@ namespace Barotrauma.Tutorials
|
||||
character = Character.Create(charInfo, wayPoint.WorldPosition, "", isRemotePlayer: false, hasAi: false);
|
||||
character.TeamID = CharacterTeamType.Team1;
|
||||
Character.Controlled = character;
|
||||
character.GiveJobItems(null);
|
||||
character.GiveJobItems(isPvPMode: false, null);
|
||||
|
||||
var idCard = character.Inventory.FindItemByTag("identitycard".ToIdentifier());
|
||||
if (idCard == null)
|
||||
|
||||
@@ -110,9 +110,13 @@ namespace Barotrauma
|
||||
deathChoiceInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), parent: topLeftButtonGroup.RectTransform)
|
||||
{ MaxSize = new Point(HUDLayoutSettings.ButtonAreaTop.Width / 3, int.MaxValue) }, style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Visible = false
|
||||
};
|
||||
respawnInfoText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform), "", wrap: true);
|
||||
respawnInfoText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform), "", wrap: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
deathChoiceButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), deathChoiceInfoFrame.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = HUDLayoutSettings.Padding,
|
||||
@@ -189,7 +193,7 @@ namespace Barotrauma
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
GameMain.NetLobbyScreen.CharacterAppearanceCustomizationMenu?.AddToGUIUpdateList();
|
||||
GameMain.NetLobbyScreen?.JobSelectionFrame?.AddToGUIUpdateList();
|
||||
GameMain.NetLobbyScreen?.JobSelectionFrame?.AddToGUIUpdateList(order: 1);
|
||||
}
|
||||
|
||||
DeathPrompt?.AddToGUIUpdateList();
|
||||
@@ -284,7 +288,7 @@ namespace Barotrauma
|
||||
if (topLeftButtonGroup == null) { return; }
|
||||
|
||||
bool permadeathMode = GameMain.NetworkMember?.ServerSettings is { RespawnMode: RespawnMode.Permadeath };
|
||||
bool ironmanMode = GameMain.NetworkMember is { ServerSettings: { RespawnMode: RespawnMode.Permadeath, IronmanMode: true } };
|
||||
bool ironmanMode = GameMain.NetworkMember?.ServerSettings is { IronmanModeActive: true };
|
||||
|
||||
bool hasRespawnOptions;
|
||||
if (permadeathMode)
|
||||
@@ -336,6 +340,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If there are any menu panels etc. open that contain information about the current player character, refresh it.
|
||||
/// Useful when the player character changes, e.g. at permadeath, and subsequent taking over of a bot character.
|
||||
/// </summary>
|
||||
public void RefreshAnyOpenPlayerInfo()
|
||||
{
|
||||
DebugConsole.NewMessage($"Refreshing any open player info");
|
||||
if (IsTabMenuOpen && TabMenu.SelectedTab == TabMenu.InfoFrameTab.Talents)
|
||||
{
|
||||
TabMenuInstance.SelectInfoFrameTab(TabMenu.InfoFrameTab.Talents);
|
||||
}
|
||||
// TODO: This can be expanded as need arises
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
GameMode?.Draw(spriteBatch);
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace Barotrauma
|
||||
|
||||
if (Character.Controlled.SelectedItem.GetComponent<Reactor>() is Reactor reactor && reactor.PowerOn &&
|
||||
Character.Controlled.SelectedItem.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
|
||||
containedItems.Count(i => i.HasTag(Tags.Fuel)) > 1)
|
||||
containedItems.Count(i => i.HasTag(Tags.ReactorFuel)) > 1)
|
||||
{
|
||||
if (DisplayHint("onisinteracting.reactorwithextrarods".ToIdentifier())) { return; }
|
||||
}
|
||||
@@ -316,7 +316,7 @@ namespace Barotrauma
|
||||
if (affliction?.Prefab == null) { continue; }
|
||||
if (affliction.Prefab.IsBuff) { continue; }
|
||||
if (affliction.Prefab == AfflictionPrefab.OxygenLow) { continue; }
|
||||
if (affliction.Prefab == AfflictionPrefab.RadiationSickness && (GameMain.GameSession.Map?.Radiation?.IsEntityRadiated(character) ?? false)) { continue; }
|
||||
if (affliction.Prefab == AfflictionPrefab.RadiationSickness && (GameMain.GameSession.Map?.Radiation?.DepthInRadiation(character) ?? 0) > 0) { continue; }
|
||||
if (affliction.Strength < affliction.Prefab.ShowIconThreshold) { continue; }
|
||||
DisplayHint("onafflictiondisplayed".ToIdentifier(),
|
||||
variables: new[] { ("[key]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Health)) },
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class PvPMode : MissionMode
|
||||
{
|
||||
private GUIComponent scoreContainer;
|
||||
private readonly GUITextBlock[] scoreTexts = new GUITextBlock[2];
|
||||
private readonly GUITextBlock[] scoreTextShadows = new GUITextBlock[2];
|
||||
private readonly int[] prevScores = new int[2];
|
||||
|
||||
private void InitUI()
|
||||
{
|
||||
scoreContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.TutorialObjectiveListArea, GUI.Canvas), childAnchor: Anchor.TopRight)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var frame = new GUIFrame(new RectTransform(new Point(scoreContainer.Rect.Width, GUI.IntScale(80)), scoreContainer.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
new GUIImage(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight), style: i == 0 ? "CoalitionIcon" : "SeparatistIcon")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
scoreTextShadows[i] = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(GUI.IntScale(38), GUI.IntScale(2)) },
|
||||
string.Empty, textColor: GUIStyle.TextColorDark, textAlignment: Alignment.CenterRight, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
scoreTexts[i] = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(GUI.IntScale(40), 0) },
|
||||
string.Empty, textAlignment: Alignment.CenterRight, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
base.AddToGUIUpdateList();
|
||||
|
||||
if (scoreContainer == null) { InitUI(); }
|
||||
|
||||
scoreContainer.Visible = false;
|
||||
foreach (var mission in Missions)
|
||||
{
|
||||
if (mission is CombatMission combatMission && combatMission.HasWinScore)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var scoreText = scoreTexts[i];
|
||||
//one team very close to the win score, start flashing the score
|
||||
if (combatMission.Scores[i] > combatMission.WinScore * 0.9f ||
|
||||
combatMission.Scores[i] == combatMission.WinScore - 1)
|
||||
{
|
||||
if (scoreText.Parent.FlashTimer <= 0.0f)
|
||||
{
|
||||
scoreText.Parent.Flash(GUIStyle.Orange);
|
||||
scoreText.Pulsate(Vector2.One, Vector2.One * 1.2f, scoreText.Parent.FlashTimer);
|
||||
}
|
||||
}
|
||||
if (prevScores[i] != combatMission.Scores[i] || scoreText.Text.IsNullOrEmpty())
|
||||
{
|
||||
scoreText.Text = scoreTextShadows[i].Text = $"{combatMission.Scores[i]}/{combatMission.WinScore}";
|
||||
scoreText.Parent.Flash(GUIStyle.Green);
|
||||
scoreText.Parent.GetAnyChild<GUIImage>().Pulsate(Vector2.One, Vector2.One * 1.2f, scoreText.Parent.FlashTimer);
|
||||
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
|
||||
}
|
||||
scoreText.Parent.RectTransform.NonScaledSize =
|
||||
new Point(
|
||||
(int)(scoreText.TextSize.X + scoreText.Padding.X + scoreText.Padding.X) + scoreText.Parent.GetChild<GUIImage>().Rect.Width + GUI.IntScale(10),
|
||||
scoreText.Parent.Rect.Height);
|
||||
scoreText.Parent.ForceLayoutRecalculation();
|
||||
prevScores[i] = combatMission.Scores[i];
|
||||
}
|
||||
scoreContainer.Visible = true;
|
||||
}
|
||||
}
|
||||
scoreContainer.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -13,11 +14,13 @@ namespace Barotrauma
|
||||
private float crewListAnimDelay = 0.25f;
|
||||
private float missionIconAnimDelay;
|
||||
|
||||
private const float jobColumnWidthPercentage = 0.11f;
|
||||
private const float characterColumnWidthPercentage = 0.44f;
|
||||
private const float statusColumnWidthPercentage = 0.45f;
|
||||
private const float JobColumnWidthPercentage = 0.05f;
|
||||
private const float CharacterColumnWidthPercentage = 0.35f;
|
||||
private const float StatusColumnWidthPercentage = 0.25f;
|
||||
private const float KillColumnWidthPercentage = 0.05f;
|
||||
private const float DeathColumnWidthPercentage = 0.05f;
|
||||
|
||||
private int jobColumnWidth, characterColumnWidth, statusColumnWidth;
|
||||
private int jobColumnWidth, characterColumnWidth, statusColumnWidth, killColumnWidth, deathColumnWidth;
|
||||
|
||||
private readonly List<Mission> selectedMissions;
|
||||
private readonly Location startLocation, endLocation;
|
||||
@@ -109,6 +112,16 @@ namespace Barotrauma
|
||||
CombatMission.GetTeamName(CharacterTeamType.Team2), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
|
||||
crewHeader2.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader2.Rect.Height * 2.0f));
|
||||
CreateCrewList(crewContent2, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID == CharacterTeamType.Team2), traitorResults);
|
||||
|
||||
if (CombatMission.Winner != CharacterTeamType.None)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewHeader.RectTransform),
|
||||
TextManager.Get(CombatMission.Winner == CharacterTeamType.Team1 ? "pvpmode.victory" : "pvpmode.defeat"), textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont,
|
||||
textColor: CombatMission.Winner == CharacterTeamType.Team1 ? GUIStyle.Green : GUIStyle.Red);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewHeader2.RectTransform),
|
||||
TextManager.Get(CombatMission.Winner == CharacterTeamType.Team2 ? "pvpmode.victory" : "pvpmode.defeat"), textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont,
|
||||
textColor: CombatMission.Winner == CharacterTeamType.Team2 ? GUIStyle.Green : GUIStyle.Red);
|
||||
}
|
||||
}
|
||||
|
||||
//header -------------------------------------------------------------------------------
|
||||
@@ -238,11 +251,12 @@ namespace Barotrauma
|
||||
textContent,
|
||||
mission.Difficulty ?? 0,
|
||||
mission.Prefab.Icon, mission.Prefab.IconColor,
|
||||
mission.GetDifficultyToolTipText(),
|
||||
out GUIImage missionIcon);
|
||||
|
||||
if (selectedMissions.Contains(mission))
|
||||
{
|
||||
UpdateMissionStateIcon(mission.Completed, missionIcon, animDelay);
|
||||
UpdateMissionStateIcon(mission is CombatMission combatMission ? CombatMission.IsInWinningTeam(GameMain.Client?.Character) : mission.Completed, missionIcon, animDelay);
|
||||
animDelay += 0.25f;
|
||||
}
|
||||
}
|
||||
@@ -429,13 +443,14 @@ namespace Barotrauma
|
||||
textContent,
|
||||
difficultyIconCount: 0,
|
||||
icon, GUIStyle.Red,
|
||||
difficultyTooltipText: null,
|
||||
out GUIImage missionIcon);
|
||||
UpdateMissionStateIcon(traitorResults.VotedCorrectTraitor, missionIcon, iconAnimDelay);
|
||||
return content;
|
||||
}
|
||||
|
||||
public static GUIComponent CreateMissionEntry(GUIComponent parent, LocalizedString header, List<LocalizedString> textContent, int difficultyIconCount,
|
||||
Sprite icon, Color iconColor, out GUIImage missionIcon)
|
||||
Sprite icon, Color iconColor, RichString difficultyTooltipText, out GUIImage missionIcon)
|
||||
{
|
||||
int spacing = GUI.IntScale(5);
|
||||
|
||||
@@ -486,7 +501,8 @@ namespace Barotrauma
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Point(missionTextGroup.Rect.Width, defaultLineHeight), parent: missionTextGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = 1
|
||||
AbsoluteSpacing = 1,
|
||||
CanBeFocused = true
|
||||
};
|
||||
difficultyIndicatorGroup.RectTransform.MinSize = new Point(0, defaultLineHeight);
|
||||
var difficultyColor = Mission.GetDifficultyColor(difficultyIconCount);
|
||||
@@ -494,8 +510,8 @@ namespace Barotrauma
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = difficultyColor
|
||||
Color = difficultyColor,
|
||||
ToolTip = difficultyTooltipText
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -567,7 +583,11 @@ namespace Barotrauma
|
||||
LocalizedString locationName = Submarine.MainSub is { AtEndExit: true } ? endLocation?.DisplayName : startLocation?.DisplayName;
|
||||
|
||||
string textTag;
|
||||
if (gameOver)
|
||||
if (gameMode is PvPMode)
|
||||
{
|
||||
textTag = "RoundSummaryRoundHasEnded";
|
||||
}
|
||||
else if (gameOver)
|
||||
{
|
||||
textTag = "RoundSummaryGameOver";
|
||||
}
|
||||
@@ -596,7 +616,14 @@ namespace Barotrauma
|
||||
textTag = "RoundSummaryReturnToEmptyLocation";
|
||||
break;
|
||||
default:
|
||||
textTag = Submarine.MainSub.AtEndExit ? "RoundSummaryProgress" : "RoundSummaryReturn";
|
||||
if (Submarine.MainSub == null)
|
||||
{
|
||||
textTag = "RoundSummaryRoundHasEnded";
|
||||
}
|
||||
else
|
||||
{
|
||||
textTag = Submarine.MainSub.AtEndExit ? "RoundSummaryProgress" : "RoundSummaryReturn";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -628,26 +655,34 @@ namespace Barotrauma
|
||||
{
|
||||
var headerFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform, Anchor.TopCenter, minSize: new Point(0, (int)(30 * GUI.Scale))) { }, isHorizontal: true)
|
||||
{
|
||||
AbsoluteSpacing = 2
|
||||
AbsoluteSpacing = 2,
|
||||
Stretch = true
|
||||
};
|
||||
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton statusButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("label.statuslabel"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(JobColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
|
||||
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(CharacterColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
|
||||
|
||||
float sizeMultiplier = 1.0f;
|
||||
//sizeMultiplier = (headerFrame.Rect.Width - headerFrame.AbsoluteSpacing * (headerFrame.CountChildren - 1)) / (float)headerFrame.Rect.Width;
|
||||
if (gameMode is PvPMode && GameMain.NetworkMember?.RespawnManager != null)
|
||||
{
|
||||
var killButton = new GUIButton(new RectTransform(new Vector2(KillColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("killcount"), style: "GUIButtonSmallFreeScale");
|
||||
killColumnWidth = killButton.Rect.Width;
|
||||
var deathButton = new GUIButton(new RectTransform(new Vector2(DeathColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("deathcount"), style: "GUIButtonSmallFreeScale");
|
||||
deathColumnWidth = deathButton.Rect.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
GUIButton statusButton = new GUIButton(new RectTransform(new Vector2(StatusColumnWidthPercentage, 1f), headerFrame.RectTransform), TextManager.Get("label.statuslabel"), style: "GUIButtonSmallFreeScale");
|
||||
statusColumnWidth = statusButton.Rect.Width;
|
||||
}
|
||||
|
||||
jobButton.RectTransform.RelativeSize = new Vector2(jobColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
characterButton.RectTransform.RelativeSize = new Vector2(characterColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
statusButton.RectTransform.RelativeSize = new Vector2(statusColumnWidthPercentage * sizeMultiplier, 1f);
|
||||
|
||||
jobButton.TextBlock.Font = characterButton.TextBlock.Font = statusButton.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
jobButton.CanBeFocused = characterButton.CanBeFocused = statusButton.CanBeFocused = false;
|
||||
jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = statusButton.ForceUpperCase = ForceUpperCase.Yes;
|
||||
foreach (var btn in headerFrame.GetAllChildren<GUIButton>())
|
||||
{
|
||||
btn.TextBlock.Font = GUIStyle.HotkeyFont;
|
||||
btn.ForceUpperCase = ForceUpperCase.Yes;
|
||||
btn.CanBeFocused = false;
|
||||
}
|
||||
|
||||
jobColumnWidth = jobButton.Rect.Width;
|
||||
characterColumnWidth = characterButton.Rect.Width;
|
||||
statusColumnWidth = statusButton.Rect.Width;
|
||||
|
||||
GUIListBox crewList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform))
|
||||
{
|
||||
@@ -658,8 +693,28 @@ namespace Barotrauma
|
||||
|
||||
headerFrame.RectTransform.RelativeSize -= new Vector2(crewList.ScrollBar.RectTransform.RelativeSize.X, 0.0f);
|
||||
|
||||
killCounts.Clear();
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
foreach (CharacterInfo characterInfo in characterInfos)
|
||||
{
|
||||
if (characterInfo == null) { continue; }
|
||||
Character character = characterInfo.Character;
|
||||
Client ownerClient = GameMain.NetworkMember.ConnectedClients.FirstOrDefault(c => c.Character == character);
|
||||
int killCount = 0, deathCount = 0;
|
||||
foreach (var mission in selectedMissions)
|
||||
{
|
||||
if (mission is not CombatMission combatMission) { continue; }
|
||||
killCount += ownerClient == null ? combatMission.GetBotKillCount(characterInfo) : combatMission.GetClientKillCount(ownerClient);
|
||||
deathCount += ownerClient == null ? combatMission.GetBotDeathCount(characterInfo) : combatMission.GetClientDeathCount(ownerClient);
|
||||
}
|
||||
killCounts[characterInfo] = killCount;
|
||||
deathCounts[characterInfo] = deathCount;
|
||||
}
|
||||
}
|
||||
|
||||
float delay = crewListAnimDelay;
|
||||
foreach (CharacterInfo characterInfo in characterInfos)
|
||||
foreach (CharacterInfo characterInfo in characterInfos.OrderByDescending(ci => killCounts.GetValueOrDefault(ci)))
|
||||
{
|
||||
if (characterInfo == null) { continue; }
|
||||
CreateCharacterElement(characterInfo, crewList, traitorResults, delay);
|
||||
@@ -670,6 +725,10 @@ namespace Barotrauma
|
||||
return crewList;
|
||||
}
|
||||
|
||||
private readonly Dictionary<CharacterInfo, int> killCounts = new();
|
||||
private readonly Dictionary<CharacterInfo, int> deathCounts = new();
|
||||
|
||||
|
||||
private void CreateCharacterElement(CharacterInfo characterInfo, GUIListBox listBox, TraitorManager.TraitorResults? traitorResults, float animDelay)
|
||||
{
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, GUI.IntScale(45)), listBox.Content.RectTransform), style: "ListBoxElement")
|
||||
@@ -701,7 +760,7 @@ namespace Barotrauma
|
||||
Character character = characterInfo.Character;
|
||||
if (character == null || character.IsDead)
|
||||
{
|
||||
if (character == null && characterInfo.IsNewHire && characterInfo.CauseOfDeath == null)
|
||||
if (character == null && (characterInfo.IsNewHire || characterInfo.BotStatus == BotStatus.ActiveService) && characterInfo.CauseOfDeath == null)
|
||||
{
|
||||
statusText = TextManager.Get("CampaignCrew.NewHire");
|
||||
statusColor = GUIStyle.Blue;
|
||||
@@ -741,8 +800,21 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
GUITextBlock statusBlock = new GUITextBlock(new RectTransform(new Point(statusColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(statusText.Value, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: statusColor);
|
||||
if (gameMode is PvPMode && GameMain.NetworkMember?.RespawnManager != null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Point(killColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
killCounts.GetValueOrDefault(characterInfo).ToString(), textAlignment: Alignment.Center);
|
||||
new GUITextBlock(new RectTransform(new Point(deathColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
deathCounts.GetValueOrDefault(characterInfo).ToString(), textAlignment: Alignment.Center);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUITextBlock statusBlock = new GUITextBlock(new RectTransform(new Point(statusColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(statusText.Value, GUIStyle.SmallFont, statusColumnWidth), textAlignment: Alignment.Center, textColor: statusColor, font: GUIStyle.SmallFont)
|
||||
{
|
||||
ToolTip = statusText.Value
|
||||
};
|
||||
}
|
||||
|
||||
frame.FadeIn(animDelay, 0.15f);
|
||||
foreach (var child in frame.GetAllChildren())
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace Barotrauma
|
||||
get { return layout; }
|
||||
set
|
||||
{
|
||||
if (layout == value) return;
|
||||
if (layout == value) { return; }
|
||||
layout = value;
|
||||
SetSlotPositions(layout);
|
||||
}
|
||||
@@ -259,8 +259,8 @@ namespace Barotrauma
|
||||
int spacing = GUI.IntScale(5);
|
||||
|
||||
SlotSize = (SlotSpriteSmall.size * UIScale * GUI.AspectRatioAdjustment).ToPoint();
|
||||
int bottomOffset = SlotSize.Y + spacing * 2 + ContainedIndicatorHeight;
|
||||
int personalSlotY = GameMain.GraphicsHeight - bottomOffset * 2 - spacing * 2 - (int)(UnequippedIndicator.size.Y * UIScale);
|
||||
int bottomOffset = GetBottomOffset(multiplier: 2);
|
||||
int personalSlotY = GetVerticalOffsetFromBottom(multiplier: 2);
|
||||
|
||||
if (visualSlots == null) { CreateSlots(); }
|
||||
if (visualSlots.None()) { return; }
|
||||
@@ -353,7 +353,15 @@ namespace Barotrauma
|
||||
case Layout.Left:
|
||||
{
|
||||
int x = HUDLayoutSettings.InventoryAreaLower.X;
|
||||
if (!GUI.IsUltrawide && GUI.IsHUDScaled)
|
||||
{
|
||||
// On non-ultra-wide aspect ratios, the inventories can easily overlap with each other, if there's any scaling.
|
||||
// So let's offset the other inventory to the left.
|
||||
const float margin = 100;
|
||||
x -= HUDLayoutSettings.ChatBoxArea.Width - (int)margin;
|
||||
}
|
||||
int personalSlotX = x;
|
||||
float y = GameMain.GraphicsHeight - bottomOffset;
|
||||
|
||||
for (int i = 0; i < SlotPositions.Length; i++)
|
||||
{
|
||||
@@ -366,7 +374,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
|
||||
SlotPositions[i] = new Vector2(x, y);
|
||||
x += visualSlots[i].Rect.Width + spacing;
|
||||
}
|
||||
}
|
||||
@@ -380,7 +388,7 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
if (!HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
|
||||
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
|
||||
SlotPositions[i] = new Vector2(x, y);
|
||||
x += visualSlots[i].Rect.Width + spacing;
|
||||
}
|
||||
}
|
||||
@@ -446,6 +454,9 @@ namespace Barotrauma
|
||||
visualSlots[i].DrawOffset = Vector2.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
int GetBottomOffset(int multiplier) => SlotSize.Y + spacing * multiplier + ContainedIndicatorHeight;
|
||||
int GetVerticalOffsetFromBottom(int multiplier) => GameMain.GraphicsHeight - (GetBottomOffset(multiplier) + spacing) * multiplier - (int)(UnequippedIndicator.size.Y * UIScale);
|
||||
}
|
||||
|
||||
protected override void ControlInput(Camera cam)
|
||||
|
||||
@@ -184,6 +184,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
shakePos = Vector2.Zero;
|
||||
}
|
||||
if (Character.Controlled is Character character && character.FocusedItem == item)
|
||||
{
|
||||
if ((IsFullyOpen || IsFullyClosed) && MathF.Abs(openState - lastOpenState) > 0)
|
||||
{
|
||||
CharacterHUD.RecreateHudTexts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void AddTooltipInfo(ref LocalizedString name, ref LocalizedString description)
|
||||
{
|
||||
bool mergedMaterialTainted = false;
|
||||
if (!materialName.IsNullOrEmpty() && item.ContainedItems.Count() > 0)
|
||||
{
|
||||
LocalizedString mergedMaterialName = materialName;
|
||||
@@ -22,11 +23,15 @@ namespace Barotrauma.Items.Components
|
||||
var containedMaterial = containedItem.GetComponent<GeneticMaterial>();
|
||||
if (containedMaterial == null) { continue; }
|
||||
mergedMaterialName += ", " + containedMaterial.materialName;
|
||||
if (containedMaterial.Tainted)
|
||||
{
|
||||
mergedMaterialTainted = true;
|
||||
}
|
||||
}
|
||||
name = name.Replace(materialName, mergedMaterialName);
|
||||
}
|
||||
|
||||
if (Tainted)
|
||||
if (Tainted || mergedMaterialTainted)
|
||||
{
|
||||
name = TextManager.GetWithVariable("entityname.taintedgeneticmaterial", "[geneticmaterialname]", name);
|
||||
}
|
||||
@@ -48,25 +53,46 @@ namespace Barotrauma.Items.Components
|
||||
description += '\n' + containedDescription;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.DevMode && Tainted && selectedTaintedEffect != null)
|
||||
{
|
||||
description = $"{description}\n{selectedTaintedEffect.Name}: {selectedTaintedEffect.GetDescription(0f, AfflictionPrefab.Description.TargetType.OtherCharacter)}";
|
||||
}
|
||||
}
|
||||
|
||||
public void ModifyDeconstructInfo(Deconstructor deconstructor, ref LocalizedString buttonText, ref LocalizedString infoText)
|
||||
{
|
||||
if (deconstructor.InputContainer.Inventory.AllItems.Count() == 2)
|
||||
{
|
||||
var otherGeneticMaterial =
|
||||
deconstructor.InputContainer.Inventory.AllItems.FirstOrDefault(it => it != item && it.Prefab == item.Prefab)?.GetComponent<GeneticMaterial>();
|
||||
var otherItem = deconstructor.InputContainer.Inventory.AllItems.FirstOrDefault(it => it != item);
|
||||
if (otherItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var otherGeneticMaterial = otherItem.GetComponent<GeneticMaterial>();
|
||||
if (otherGeneticMaterial == null)
|
||||
{
|
||||
buttonText = TextManager.Get("researchstation.combine");
|
||||
infoText = TextManager.Get("researchstation.combine.infotext");
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
var combineRefineResult = GetCombineRefineResult(otherGeneticMaterial);
|
||||
|
||||
if (combineRefineResult == CombineResult.None)
|
||||
{
|
||||
infoText = TextManager.Get("researchstation.novalidcombination");
|
||||
}
|
||||
else if (combineRefineResult == CombineResult.Refined)
|
||||
{
|
||||
buttonText = TextManager.Get("researchstation.refine");
|
||||
int taintedProbability = (int)(GetTaintedProbabilityOnRefine(otherGeneticMaterial, Character.Controlled) * 100);
|
||||
infoText = TextManager.GetWithVariable("researchstation.refine.infotext", "[taintedprobability]", taintedProbability.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonText = TextManager.Get("researchstation.combine");
|
||||
infoText = TextManager.Get("researchstation.combine.infotext");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,13 +71,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
public override bool ValidateEventData(NetEntityEvent.IData data)
|
||||
=> TryExtractEventData<EventData>(data, out _);
|
||||
=> TryExtractEventData<AttachEventData>(data, out _);
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (!attachable || body == null) { return; }
|
||||
|
||||
var eventData = ExtractEventData<EventData>(extraData);
|
||||
var eventData = ExtractEventData<AttachEventData>(extraData);
|
||||
|
||||
Vector2 attachPos = eventData.AttachPos;
|
||||
msg.WriteSingle(attachPos.X);
|
||||
@@ -94,7 +94,9 @@ namespace Barotrauma.Items.Components
|
||||
bool shouldBeAttached = msg.ReadBoolean();
|
||||
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
UInt16 submarineID = msg.ReadUInt16();
|
||||
UInt16 attacherID = msg.ReadUInt16();
|
||||
Submarine sub = Entity.FindEntityByID(submarineID) as Submarine;
|
||||
Character attacher = Entity.FindEntityByID(attacherID) as Character;
|
||||
|
||||
if (shouldBeAttached)
|
||||
{
|
||||
@@ -104,6 +106,8 @@ namespace Barotrauma.Items.Components
|
||||
item.SetTransform(simPosition, 0.0f);
|
||||
item.Submarine = sub;
|
||||
AttachToWall();
|
||||
PlaySound(ActionType.OnUse, attacher);
|
||||
ApplyStatusEffects(ActionType.OnUse, (float)Timing.Step, character: attacher, user: attacher);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
private Vector2 _chargeSoundWindupPitchSlide;
|
||||
|
||||
public Vector2 BarrelScreenPos => Screen.Selected.Cam.WorldToScreen(item.DrawPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos));
|
||||
|
||||
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||
private readonly List<ParticleEmitter> particleEmitterCharges = new List<ParticleEmitter>();
|
||||
|
||||
@@ -86,28 +88,19 @@ namespace Barotrauma.Items.Components
|
||||
currentCrossHairScale = currentCrossHairPointerScale = cam == null ? 1.0f : cam.Zoom;
|
||||
if (crosshairSprite != null)
|
||||
{
|
||||
Vector2 aimRefWorldPos = character.AimRefPosition;
|
||||
if (character.Submarine != null) { aimRefWorldPos += character.Submarine.Position; }
|
||||
Vector2 itemPos = cam.WorldToScreen(aimRefWorldPos);
|
||||
float rotation = (item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi;
|
||||
Vector2 barrelDir = new Vector2((float)Math.Cos(rotation), -(float)Math.Sin(rotation));
|
||||
|
||||
Vector2 mouseDiff = itemPos - PlayerInput.MousePosition;
|
||||
crosshairPos = new Vector2(
|
||||
MathHelper.Clamp(itemPos.X + barrelDir.X * mouseDiff.Length(), 0, GameMain.GraphicsWidth),
|
||||
MathHelper.Clamp(itemPos.Y + barrelDir.Y * mouseDiff.Length(), 0, GameMain.GraphicsHeight));
|
||||
// Set position based on in-world aim
|
||||
Vector2 barrelDir = (MathF.Cos(item.body.TransformedRotation), -MathF.Sin(item.body.TransformedRotation));
|
||||
float mouseDist = Vector2.Distance(BarrelScreenPos, PlayerInput.MousePosition);
|
||||
crosshairPos = Vector2.Clamp(BarrelScreenPos + barrelDir * mouseDist, Vector2.Zero, (GameMain.GraphicsWidth, GameMain.GraphicsHeight));
|
||||
|
||||
// Resize pointer based on current spread
|
||||
float spread = GetSpread(character);
|
||||
Projectile projectile = FindProjectile();
|
||||
if (projectile != null)
|
||||
if (FindProjectile() is Projectile projectile)
|
||||
{
|
||||
spread += MathHelper.ToRadians(projectile.Spread);
|
||||
}
|
||||
|
||||
float crossHairDist = Vector2.Distance(item.WorldPosition, cam.ScreenToWorld(crosshairPos));
|
||||
float spreadDist = (float)Math.Sin(spread) * crossHairDist;
|
||||
|
||||
currentCrossHairPointerScale = MathHelper.Clamp(spreadDist / Math.Min(crosshairSprite.size.X, crosshairSprite.size.Y), 0.1f, 10.0f);
|
||||
float spreadAtRange = MathF.Sin(spread) * Vector2.Distance(BarrelScreenPos, crosshairPos);
|
||||
currentCrossHairPointerScale = MathHelper.Clamp(spreadAtRange / Math.Min(crosshairSprite.size.X, crosshairSprite.size.Y), 0.1f, 10f);
|
||||
}
|
||||
currentCrossHairScale *= CrossHairScale;
|
||||
crosshairPointerPos = PlayerInput.MousePosition;
|
||||
@@ -141,7 +134,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (chargeSound != null)
|
||||
{
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling, freqMult: chargeSound.GetRandomFrequencyMultiplier());
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound, item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
if (chargeSoundChannel != null) { chargeSoundChannel.Looping = true; }
|
||||
}
|
||||
}
|
||||
@@ -177,6 +170,8 @@ namespace Barotrauma.Items.Components
|
||||
//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; }
|
||||
|
||||
base.DrawHUD(spriteBatch, character);
|
||||
|
||||
GUI.HideCursor = (crosshairSprite != null || crosshairPointerSprite != null) &&
|
||||
GUI.MouseOn == null && !Inventory.IsMouseOnInventory && !GameMain.Instance.Paused;
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ namespace Barotrauma.Items.Components
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
if (character == null || !character.IsKeyDown(InputType.Aim)) { return; }
|
||||
base.DrawHUD(spriteBatch, character);
|
||||
GUI.HideCursor = targetSections.Count > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,10 @@ namespace Barotrauma.Items.Components
|
||||
private readonly Dictionary<ActionType, List<ItemSound>> sounds;
|
||||
private Dictionary<ActionType, SoundSelectionMode> soundSelectionModes;
|
||||
|
||||
/// <summary>
|
||||
/// Starts the timer for delayed client-side corrections (<see cref="StartDelayedCorrection(IReadMessage, float, bool)"/>) - in other words,
|
||||
/// the client will not attempt to read server updates for this component until the timer elapses.
|
||||
/// </summary>
|
||||
protected float correctionTimer;
|
||||
|
||||
public float IsActiveTimer;
|
||||
@@ -150,6 +154,17 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public GUIFrame GuiFrame { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Overlay (just a non-interactable sprite) drawn when the item is selected, equipped or focused to via Controllers (e.g. when operating a turret via a periscope or a camera via a monitor).
|
||||
/// </summary>
|
||||
public Sprite HUDOverlay { get; set; }
|
||||
|
||||
public float HUDOverlayAnimSpeed
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private GUIDragHandle guiFrameDragHandle;
|
||||
|
||||
private bool guiFrameUpdatePending;
|
||||
@@ -161,6 +176,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
public bool CloseByClickingOutsideGUIFrame
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private ItemComponent linkToUIComponent;
|
||||
[Serialize("", IsPropertySaveable.No)]
|
||||
public string LinkUIToComponent
|
||||
@@ -261,7 +283,7 @@ namespace Barotrauma.Items.Components
|
||||
if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
|
||||
|
||||
//above the top boundary of the level (in an inactive respawn shuttle?)
|
||||
if (item.Submarine != null && Level.Loaded != null && item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y)
|
||||
if (item.Submarine != null && item.Submarine.IsAboveLevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -294,11 +316,13 @@ namespace Barotrauma.Items.Components
|
||||
0.01f,
|
||||
loopingSound.RoundSound.GetRandomFrequencyMultiplier(),
|
||||
SoundPlayer.ShouldMuffleSound(Character.Controlled, item.WorldPosition, loopingSound.Range, Character.Controlled?.CurrentHull));
|
||||
loopingSoundChannel.Looping = true;
|
||||
item.CheckNeedsSoundUpdate(this);
|
||||
//TODO: tweak
|
||||
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
|
||||
loopingSoundChannel.Far = loopingSound.Range;
|
||||
if (loopingSoundChannel != null)
|
||||
{
|
||||
loopingSoundChannel.Looping = true;
|
||||
item.CheckNeedsSoundUpdate(this);
|
||||
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
|
||||
loopingSoundChannel.Far = loopingSound.Range;
|
||||
}
|
||||
}
|
||||
|
||||
// Looping sound with manual selection mode should be changed if value of ManuallySelectedSound has changed
|
||||
@@ -340,7 +364,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (soundSelectionMode == SoundSelectionMode.Manual)
|
||||
{
|
||||
index = Math.Clamp(ManuallySelectedSound, 0, matchingSounds.Count);
|
||||
index = Math.Clamp(ManuallySelectedSound, 0, matchingSounds.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -374,22 +398,20 @@ namespace Barotrauma.Items.Components
|
||||
float volume = GetSoundVolume(itemSound);
|
||||
if (volume <= 0.0001f) { return; }
|
||||
loopingSound = itemSound;
|
||||
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
|
||||
new Vector3(position.X, position.Y, 0.0f),
|
||||
0.01f,
|
||||
freqMult: itemSound.RoundSound.GetRandomFrequencyMultiplier(),
|
||||
muffle: SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull));
|
||||
loopingSoundChannel.Looping = true;
|
||||
//TODO: tweak
|
||||
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
|
||||
loopingSoundChannel.Far = loopingSound.Range;
|
||||
loopingSoundChannel = SoundPlayer.PlaySound(loopingSound.RoundSound, position, volume: 0.01f, hullGuess: item.CurrentHull);
|
||||
if (loopingSoundChannel != null)
|
||||
{
|
||||
loopingSoundChannel.Looping = true;
|
||||
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
|
||||
loopingSoundChannel.Far = loopingSound.Range;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float volume = GetSoundVolume(itemSound);
|
||||
if (volume <= 0.0001f) { return; }
|
||||
var channel = SoundPlayer.PlaySound(itemSound.RoundSound.Sound, position, volume, itemSound.Range, itemSound.RoundSound.GetRandomFrequencyMultiplier(), item.CurrentHull, ignoreMuffling: itemSound.RoundSound.IgnoreMuffling);
|
||||
var channel = SoundPlayer.PlaySound(itemSound.RoundSound, position, volume, hullGuess: item.CurrentHull);
|
||||
if (channel != null) { playingOneshotSoundChannels.Add(channel); }
|
||||
}
|
||||
}
|
||||
@@ -416,12 +438,23 @@ namespace Barotrauma.Items.Components
|
||||
if (sound == null) { return 0.0f; }
|
||||
if (sound.VolumeProperty == "") { return sound.VolumeMultiplier; }
|
||||
|
||||
if (SerializableProperties.TryGetValue(sound.VolumeProperty, out SerializableProperty property))
|
||||
SerializableProperty property = null;
|
||||
ISerializableEntity targetEntity = null;
|
||||
if (SerializableProperties.TryGetValue(sound.VolumeProperty, out property))
|
||||
{
|
||||
targetEntity = this;
|
||||
}
|
||||
else if (Item.SerializableProperties.TryGetValue(sound.VolumeProperty, out property))
|
||||
{
|
||||
targetEntity = Item;
|
||||
}
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
float newVolume;
|
||||
try
|
||||
{
|
||||
newVolume = property.GetFloatValue(this);
|
||||
newVolume = property.GetFloatValue(targetEntity);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -470,7 +503,24 @@ namespace Barotrauma.Items.Components
|
||||
return linkToUIComponent;
|
||||
}
|
||||
|
||||
public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { }
|
||||
public virtual void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
if (HUDOverlay != null)
|
||||
{
|
||||
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
if (HUDOverlay is SpriteSheet spriteSheet)
|
||||
{
|
||||
spriteSheet.Draw(spriteBatch,
|
||||
spriteIndex: (int)(Math.Floor(Timing.TotalTimeUnpaused * HUDOverlayAnimSpeed) % spriteSheet.FrameCount),
|
||||
pos: screenSize / 2, color: Color.White, origin: HUDOverlay.Origin, rotate: 0, scale: screenSize / spriteSheet.FrameSize.ToVector2());
|
||||
}
|
||||
else
|
||||
{
|
||||
HUDOverlay.Draw(spriteBatch,
|
||||
pos: screenSize / 2, color: Color.White, origin: HUDOverlay.Origin, rotate: 0, scale: screenSize / HUDOverlay.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AddToGUIUpdateList(int order = 0)
|
||||
{
|
||||
@@ -513,6 +563,13 @@ namespace Barotrauma.Items.Components
|
||||
GuiFrameSource = subElement;
|
||||
ReloadGuiFrame();
|
||||
break;
|
||||
case "hudoverlayanimated":
|
||||
HUDOverlay = new SpriteSheet(subElement);
|
||||
HUDOverlayAnimSpeed = subElement.GetAttributeFloat("animspeed", 1.0f);
|
||||
break;
|
||||
case "hudoverlay":
|
||||
HUDOverlay = new Sprite(subElement);
|
||||
break;
|
||||
case "alternativelayout":
|
||||
AlternativeLayout = GUILayoutSettings.Load(subElement);
|
||||
break;
|
||||
@@ -687,6 +744,9 @@ namespace Barotrauma.Items.Components
|
||||
}),
|
||||
new ContextMenuOption(TextManager.Get(LockGuiFramePosition ? "item.unlockuiposition" : "item.lockuiposition"), isEnabled: true, onSelected: () =>
|
||||
{
|
||||
//ensure the offset is set to where the frame is now
|
||||
//(it may have been repositioned by the overlap prevention logic, which doesn't set this offset)
|
||||
GuiFrameOffset = GuiFrame.RectTransform.ScreenSpaceOffset;
|
||||
LockGuiFramePosition = !LockGuiFramePosition;
|
||||
guiFrameDragHandle.Enabled = !LockGuiFramePosition;
|
||||
if (SerializableProperties.TryGetValue(nameof(LockGuiFramePosition).ToIdentifier(), out var property))
|
||||
@@ -711,7 +771,11 @@ namespace Barotrauma.Items.Components
|
||||
/// </summary>
|
||||
protected virtual void CreateGUI() { }
|
||||
|
||||
//Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero.
|
||||
/// <summary>
|
||||
/// Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero.
|
||||
/// Useful in cases where we a client is constantly adjusting some value, and we don't want state updates from the server to interfere with it
|
||||
/// (e.g. setting the value back to what a client just set it to, when the client has already modified the value further).
|
||||
/// </summary>
|
||||
protected void StartDelayedCorrection(IReadMessage buffer, float sendingTime, bool waitForMidRoundSync = false)
|
||||
{
|
||||
if (delayedCorrectionCoroutine != null) { CoroutineManager.StopCoroutines(delayedCorrectionCoroutine); }
|
||||
|
||||
@@ -458,54 +458,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void DrawContainedItems(SpriteBatch spriteBatch, float itemDepth, Color? overrideColor = null)
|
||||
{
|
||||
Vector2 transformedItemPos = ItemPos * item.Scale;
|
||||
Vector2 transformedItemInterval = ItemInterval * item.Scale;
|
||||
Vector2 transformedItemIntervalHorizontal = new Vector2(transformedItemInterval.X, 0.0f);
|
||||
Vector2 transformedItemIntervalVertical = new Vector2(0.0f, transformedItemInterval.Y);
|
||||
var rootBody = item.RootContainer?.body ?? item.body;
|
||||
|
||||
if (item.body == null)
|
||||
{
|
||||
if (item.FlippedX)
|
||||
{
|
||||
transformedItemPos.X = -transformedItemPos.X;
|
||||
transformedItemPos.X += item.Rect.Width;
|
||||
transformedItemInterval.X = -transformedItemInterval.X;
|
||||
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
|
||||
}
|
||||
if (item.FlippedY)
|
||||
{
|
||||
transformedItemPos.Y = -transformedItemPos.Y;
|
||||
transformedItemPos.Y -= item.Rect.Height;
|
||||
transformedItemInterval.Y = -transformedItemInterval.Y;
|
||||
transformedItemIntervalVertical.Y = -transformedItemIntervalVertical.Y;
|
||||
}
|
||||
transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
|
||||
if (item.Submarine != null) { transformedItemPos += item.Submarine.DrawPosition; }
|
||||
|
||||
if (Math.Abs(item.RotationRad) > 0.01f)
|
||||
{
|
||||
Matrix transform = Matrix.CreateRotationZ(-item.RotationRad);
|
||||
transformedItemPos = Vector2.Transform(transformedItemPos - item.DrawPosition, transform) + item.DrawPosition;
|
||||
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
|
||||
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
|
||||
transformedItemIntervalVertical = Vector2.Transform(transformedItemIntervalVertical, transform);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Matrix transform = Matrix.CreateRotationZ(item.body.DrawRotation);
|
||||
if (item.body.Dir == -1.0f)
|
||||
{
|
||||
transformedItemPos.X = -transformedItemPos.X;
|
||||
transformedItemInterval.X = -transformedItemInterval.X;
|
||||
transformedItemIntervalHorizontal.X = -transformedItemIntervalHorizontal.X;
|
||||
}
|
||||
|
||||
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
|
||||
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
|
||||
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
|
||||
transformedItemPos += item.body.DrawPosition;
|
||||
}
|
||||
Vector2 transformedItemPos = GetContainedPosition(
|
||||
drawPosition: true,
|
||||
out Vector2 transformedItemIntervalHorizontal,
|
||||
out Vector2 transformedItemIntervalVertical,
|
||||
out bool flippedX,
|
||||
out bool flippedY);
|
||||
|
||||
Vector2 currentItemPos = transformedItemPos;
|
||||
|
||||
@@ -514,30 +474,41 @@ namespace Barotrauma.Items.Components
|
||||
int i = 0;
|
||||
foreach (ContainedItem contained in containedItems)
|
||||
{
|
||||
Vector2 itemPos = currentItemPos;
|
||||
|
||||
if (contained.Item?.Sprite == null) { continue; }
|
||||
|
||||
if (contained.Hide) { continue; }
|
||||
|
||||
Vector2 itemPos = transformedItemPos;
|
||||
int targetSlotIndex = ItemsUseInventoryPlacement ? Inventory.FindIndex(contained.Item) : i;
|
||||
//interval set on both axes -> use a grid layout
|
||||
if (Math.Abs(ItemInterval.X) > 0.001f && Math.Abs(ItemInterval.Y) > 0.001f)
|
||||
{
|
||||
itemPos += transformedItemIntervalHorizontal * (targetSlotIndex % ItemsPerRow);
|
||||
itemPos += transformedItemIntervalVertical * (targetSlotIndex / ItemsPerRow);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemPos += (transformedItemIntervalHorizontal + transformedItemIntervalVertical) * targetSlotIndex;
|
||||
}
|
||||
|
||||
if (contained.ItemPos.HasValue)
|
||||
{
|
||||
Vector2 pos = contained.ItemPos.Value;
|
||||
if (item.body != null)
|
||||
{
|
||||
Matrix transform = Matrix.CreateRotationZ(item.body.DrawRotation);
|
||||
pos.X *= item.body.Dir;
|
||||
pos.X *= rootBody.Dir;
|
||||
itemPos = Vector2.Transform(pos, transform) + item.body.DrawPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemPos = pos;
|
||||
// This code is aped based on above. Not tested.
|
||||
if (item.FlippedX)
|
||||
if (flippedX)
|
||||
{
|
||||
itemPos.X = -itemPos.X;
|
||||
itemPos.X += item.Rect.Width;
|
||||
}
|
||||
if (item.FlippedY)
|
||||
if (flippedY)
|
||||
{
|
||||
itemPos.Y = -itemPos.Y;
|
||||
itemPos.Y -= item.Rect.Height;
|
||||
@@ -555,15 +526,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (AutoInteractWithContained)
|
||||
if (CanAutoInteractWithContained(contained.Item) && Screen.Selected is not { IsEditor: true })
|
||||
{
|
||||
contained.Item.IsHighlighted = item.IsHighlighted;
|
||||
item.IsHighlighted = false;
|
||||
}
|
||||
|
||||
Vector2 origin = contained.Item.Sprite.Origin;
|
||||
if (item.FlippedX) { origin.X = contained.Item.Sprite.SourceRect.Width - origin.X; }
|
||||
if (item.FlippedY) { origin.Y = contained.Item.Sprite.SourceRect.Height - origin.Y; }
|
||||
if (flippedX) { origin.X = contained.Item.Sprite.SourceRect.Width - origin.X; }
|
||||
if (flippedY) { origin.Y = contained.Item.Sprite.SourceRect.Height - origin.Y; }
|
||||
|
||||
float containedSpriteDepth = ContainedSpriteDepth < 0.0f ? contained.Item.Sprite.Depth : ContainedSpriteDepth;
|
||||
if (i < containedSpriteDepths.Length)
|
||||
@@ -578,12 +549,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
spriteRotation = contained.Rotation;
|
||||
}
|
||||
bool flipX = (item.body != null && item.body.Dir == -1) || item.FlippedX;
|
||||
|
||||
bool flipX = rootBody is { Dir: -1 } || flippedX;
|
||||
if (flipX)
|
||||
{
|
||||
spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipVertically : SpriteEffects.FlipHorizontally;
|
||||
}
|
||||
bool flipY = item.FlippedY;
|
||||
bool flipY = flippedY;
|
||||
if (flipY)
|
||||
{
|
||||
spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
|
||||
@@ -598,7 +570,8 @@ namespace Barotrauma.Items.Components
|
||||
contained.Item.Scale,
|
||||
spriteEffects,
|
||||
depth: containedSpriteDepth);
|
||||
contained.Item.DrawDecorativeSprites(spriteBatch, itemPos, flipX,flipY, (contained.Item.body == null ? 0.0f : contained.Item.body.DrawRotation),
|
||||
|
||||
contained.Item.DrawDecorativeSprites(spriteBatch, itemPos, flipX, flipY, (contained.Item.body == null ? 0.0f : contained.Item.body.DrawRotation),
|
||||
containedSpriteDepth, overrideColor);
|
||||
|
||||
foreach (ItemContainer ic in contained.Item.GetComponents<ItemContainer>())
|
||||
@@ -608,20 +581,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
i++;
|
||||
if (Math.Abs(ItemInterval.X) > 0.001f && Math.Abs(ItemInterval.Y) > 0.001f)
|
||||
{
|
||||
//interval set on both axes -> use a grid layout
|
||||
currentItemPos += transformedItemIntervalHorizontal;
|
||||
if (i % ItemsPerRow == 0)
|
||||
{
|
||||
currentItemPos = transformedItemPos;
|
||||
currentItemPos += transformedItemIntervalVertical * (i / ItemsPerRow);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentItemPos += transformedItemInterval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string text;
|
||||
[Serialize("", IsPropertySaveable.Yes, translationTextTag: "Label.", description: "The text displayed in the label.", alwaysUseInstanceValues: true), Editable(100)]
|
||||
[Serialize("", IsPropertySaveable.Yes, translationTextTag: "Label.", description: "The text displayed in the label.", alwaysUseInstanceValues: true), Editable(MaxLength = 100)]
|
||||
public string Text
|
||||
{
|
||||
get { return text; }
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Light.SpriteScale = Vector2.One * item.Scale;
|
||||
Light.Position = ParentBody != null ? ParentBody.Position : item.Position;
|
||||
SetLightSourceTransformProjSpecific();
|
||||
}
|
||||
|
||||
partial void SetLightSourceState(bool enabled, float brightness)
|
||||
@@ -51,27 +52,42 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void SetLightSourceTransformProjSpecific()
|
||||
{
|
||||
Vector2 offset = Vector2.Zero;
|
||||
if (LightOffset != Vector2.Zero)
|
||||
{
|
||||
offset = Vector2.Transform(LightOffset, Matrix.CreateRotationZ(item.FlippedY ? -item.RotationRad - MathHelper.Pi : -item.RotationRad)) * item.Scale;
|
||||
}
|
||||
|
||||
if (ParentBody != null)
|
||||
{
|
||||
Light.ParentBody = ParentBody;
|
||||
Light.OffsetFromBody = offset;
|
||||
}
|
||||
else if (turret != null)
|
||||
{
|
||||
Light.Position = new Vector2(item.Rect.X + turret.TransformedBarrelPos.X, item.Rect.Y - turret.TransformedBarrelPos.Y);
|
||||
Light.Position = new Vector2(item.Rect.X + turret.TransformedBarrelPos.X, item.Rect.Y - turret.TransformedBarrelPos.Y) + offset;
|
||||
}
|
||||
else if (item.body != null)
|
||||
{
|
||||
Light.ParentBody = item.body;
|
||||
Light.OffsetFromBody = offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
Light.Position = item.Position;
|
||||
Light.Position = item.Position + offset;
|
||||
}
|
||||
PhysicsBody body = Light.ParentBody;
|
||||
if (body != null && body.Enabled)
|
||||
if (body != null)
|
||||
{
|
||||
Light.Rotation = body.Dir > 0.0f ? body.DrawRotation : body.DrawRotation - MathHelper.Pi;
|
||||
Light.LightSpriteEffect = (body.Dir > 0.0f) ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
if (body.Enabled)
|
||||
{
|
||||
Light.LightSpriteEffect = (body.Dir > 0.0f) ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
}
|
||||
else
|
||||
{
|
||||
Light.LightSpriteEffect = item.SpriteEffects;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -85,11 +101,13 @@ namespace Barotrauma.Items.Components
|
||||
if (Light?.LightSprite == null) { return; }
|
||||
if ((item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled)
|
||||
{
|
||||
Vector2 offset = Vector2.Transform(LightOffset, Matrix.CreateRotationZ(item.FlippedY ? -item.RotationRad - MathHelper.Pi : -item.RotationRad)) * item.Scale;
|
||||
|
||||
Vector2 origin = Light.LightSprite.Origin;
|
||||
if ((Light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = Light.LightSprite.SourceRect.Width - origin.X; }
|
||||
if ((Light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = Light.LightSprite.SourceRect.Height - origin.Y; }
|
||||
|
||||
Vector2 drawPos = item.body?.DrawPosition ?? item.DrawPosition;
|
||||
Vector2 drawPos = item.body?.DrawPosition ?? item.DrawPosition + offset;
|
||||
|
||||
Color color = lightColor;
|
||||
if (Light.OverrideLightSpriteAlpha.HasValue)
|
||||
|
||||
@@ -6,11 +6,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Controller : ItemComponent
|
||||
{
|
||||
private bool chatBoxOriginalState;
|
||||
private bool isHUDsHidden;
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
base.DrawHUD(spriteBatch, character);
|
||||
if (focusTarget != null && character.ViewTarget == focusTarget)
|
||||
{
|
||||
foreach (ItemComponent ic in focusTarget.Components)
|
||||
@@ -23,48 +23,31 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList(int order = 0)
|
||||
{
|
||||
base.AddToGUIUpdateList(order);
|
||||
if (focusTarget != null && Character.Controlled.ViewTarget == focusTarget)
|
||||
{
|
||||
focusTarget.AddToGUIUpdateList(order);
|
||||
}
|
||||
}
|
||||
|
||||
partial void HideHUDs(bool value)
|
||||
{
|
||||
if (isHUDsHidden == value) { return; }
|
||||
if (value == true)
|
||||
if (value)
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.AutoHideCrewList();
|
||||
ToggleChatBox(false, storeOriginalState: true);
|
||||
ChatBox.AutoHideChatBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.ResetCrewList();
|
||||
ToggleChatBox(chatBoxOriginalState, storeOriginalState: false);
|
||||
GameMain.GameSession?.CrewManager?.ResetCrewListOpenState();
|
||||
ChatBox.ResetChatBoxOpenState();
|
||||
}
|
||||
isHUDsHidden = value;
|
||||
}
|
||||
|
||||
private void ToggleChatBox(bool value, bool storeOriginalState)
|
||||
{
|
||||
var crewManager = GameMain.GameSession?.CrewManager;
|
||||
if (crewManager == null) { return; }
|
||||
|
||||
if (crewManager.IsSinglePlayer)
|
||||
{
|
||||
if (crewManager.ChatBox != null)
|
||||
{
|
||||
if (storeOriginalState)
|
||||
{
|
||||
chatBoxOriginalState = crewManager.ChatBox.ToggleOpen;
|
||||
}
|
||||
crewManager.ChatBox.ToggleOpen = value;
|
||||
}
|
||||
}
|
||||
else if (GameMain.Client != null)
|
||||
{
|
||||
if (storeOriginalState)
|
||||
{
|
||||
chatBoxOriginalState = GameMain.Client.ChatBox.ToggleOpen;
|
||||
}
|
||||
GameMain.Client.ChatBox.ToggleOpen = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public override void CreateEditingHUD(SerializableEntityEditor editor)
|
||||
{
|
||||
|
||||
@@ -154,12 +154,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
infoArea.Text = TextManager.Get(InfoText).Fallback(InfoText);
|
||||
}
|
||||
|
||||
if (IsActive)
|
||||
{
|
||||
activateButton.Text = TextManager.Get("DeconstructorCancel");
|
||||
infoArea.Text = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
bool outputsFound = false;
|
||||
foreach (var (inputItem, deconstructItem) in GetAvailableOutputs(checkRequiredOtherItems: true))
|
||||
{
|
||||
@@ -174,27 +176,34 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
inputItem.GetComponent<GeneticMaterial>()?.ModifyDeconstructInfo(this, ref buttonText, ref infoText);
|
||||
activateButton.Text = buttonText;
|
||||
if (infoArea != null)
|
||||
{
|
||||
infoArea.Text = infoText;
|
||||
}
|
||||
infoArea.Text = infoText;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LocalizedString activateButtonText = TextManager.Get(ActivateButtonText);
|
||||
activateButton.Enabled = outputsFound || !InputContainer.Inventory.IsEmpty();
|
||||
activateButton.Text = activateButtonText;
|
||||
|
||||
//no valid outputs found: check if we're missing some required items from the input slots and display a message about it if possible
|
||||
if (!outputsFound && infoArea != null)
|
||||
{
|
||||
foreach (var (inputItem, deconstructItem) in GetAvailableOutputs(checkRequiredOtherItems: false))
|
||||
{
|
||||
LocalizedString infoText = string.Empty;
|
||||
if (deconstructItem.RequiredOtherItem.Any() && !string.IsNullOrEmpty(deconstructItem.InfoTextOnOtherItemMissing))
|
||||
{
|
||||
LocalizedString missingItemName = TextManager.Get("entityname." + deconstructItem.RequiredOtherItem.First());
|
||||
infoArea.Text = TextManager.GetWithVariable(deconstructItem.InfoTextOnOtherItemMissing, "[itemname]", missingItemName);
|
||||
infoText = TextManager.GetWithVariable(deconstructItem.InfoTextOnOtherItemMissing, "[itemname]", missingItemName);
|
||||
}
|
||||
|
||||
inputItem.GetComponent<GeneticMaterial>()?.ModifyDeconstructInfo(this, ref activateButtonText, ref infoText);
|
||||
|
||||
activateButton.Text = activateButtonText;
|
||||
infoArea.Text = infoText;
|
||||
}
|
||||
}
|
||||
activateButton.Enabled = outputsFound || !InputContainer.Inventory.IsEmpty();
|
||||
activateButton.Text = TextManager.Get(ActivateButtonText);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -415,7 +424,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
inSufficientPowerWarning.Visible = IsActive && !hasPower;
|
||||
inSufficientPowerWarning.Visible = IsActive && !HasPower;
|
||||
}
|
||||
|
||||
private bool OnActivateButtonClicked(GUIButton button, object obj)
|
||||
|
||||
@@ -11,11 +11,21 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Fabricator : Powered, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private enum SortBy
|
||||
{
|
||||
Category,
|
||||
Alphabetical,
|
||||
SkillRequirement,
|
||||
Price
|
||||
}
|
||||
|
||||
private GUIListBox itemList;
|
||||
|
||||
private GUIFrame selectedItemFrame;
|
||||
private GUIFrame selectedItemReqsFrame;
|
||||
|
||||
private GUILayoutGroup outputTopArea, paddedOutputArea;
|
||||
|
||||
private GUITextBlock amountTextMax;
|
||||
private GUIScrollBar amountInput;
|
||||
|
||||
@@ -26,6 +36,8 @@ namespace Barotrauma.Items.Components
|
||||
private GUIButton activateButton;
|
||||
|
||||
private GUITextBox itemFilterBox;
|
||||
private GUITickBox availableOnlyTickBox;
|
||||
private GUIDropDown sortByDropdown;
|
||||
|
||||
private GUIComponent outputSlot;
|
||||
private GUIComponent inputInventoryHolder, outputInventoryHolder;
|
||||
@@ -33,6 +45,9 @@ namespace Barotrauma.Items.Components
|
||||
private readonly List<GUIButton> itemCategoryButtons = new List<GUIButton>();
|
||||
private MapEntityCategory? selectedItemCategory;
|
||||
|
||||
private GUITextBlock requiresRecipeText;
|
||||
private GUITextBlock nothingToShowText;
|
||||
|
||||
public FabricationRecipe SelectedItem
|
||||
{
|
||||
get { return selectedItem; }
|
||||
@@ -65,6 +80,12 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize("vendingmachine.outofstock", IsPropertySaveable.Yes)]
|
||||
public string FabricationLimitReachedText { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
public bool ShowSortByDropdown { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
public bool ShowAvailableOnlyTickBox { get; set; }
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
protected override void OnResolutionChanged()
|
||||
@@ -154,14 +175,13 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
|
||||
// === TOP AREA ===
|
||||
var topFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.65f), mainFrame.RectTransform), style: "InnerFrameDark");
|
||||
var topFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.8f), mainFrame.RectTransform), style: "InnerFrameDark");
|
||||
|
||||
// === ITEM LIST ===
|
||||
var itemListFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), topFrame.RectTransform), childAnchor: Anchor.Center);
|
||||
var paddedItemFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), itemListFrame.RectTransform))
|
||||
var paddedItemFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), itemListFrame.RectTransform), isHorizontal: false)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.03f
|
||||
Stretch = true
|
||||
};
|
||||
var filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), paddedItemFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
@@ -169,7 +189,8 @@ namespace Barotrauma.Items.Components
|
||||
RelativeSpacing = 0.03f,
|
||||
UserData = "filterarea"
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUIStyle.SubHeadingFont)
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.4f, 1f), filterArea.RectTransform), TextManager.Get("serverlog.filter"),
|
||||
font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
AutoScaleVertical = true
|
||||
@@ -183,29 +204,91 @@ namespace Barotrauma.Items.Components
|
||||
FilterEntities(selectedItemCategory, text);
|
||||
return true;
|
||||
};
|
||||
filterArea.RectTransform.MinSize = new Point(0, itemFilterBox.Rect.Height);
|
||||
filterArea.RectTransform.MaxSize = new Point(int.MaxValue, itemFilterBox.Rect.Height);
|
||||
|
||||
itemList = new GUIListBox(new RectTransform(new Vector2(1f, 0.9f), paddedItemFrame.RectTransform), style: null)
|
||||
var sortByArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), paddedItemFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.03f,
|
||||
Visible = ShowSortByDropdown,
|
||||
IgnoreLayoutGroups = !ShowSortByDropdown
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.4f, 1f), sortByArea.RectTransform), TextManager.Get("campaignstore.sortby"),
|
||||
font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
AutoScaleVertical = true
|
||||
};
|
||||
sortByDropdown = new GUIDropDown(new RectTransform(new Vector2(0.8f, 1.0f), sortByArea.RectTransform));
|
||||
foreach (SortBy sortBy in Enum.GetValues<SortBy>())
|
||||
{
|
||||
sortByDropdown.AddItem(TextManager.Get("fabricator.sortby." + sortBy), userData: sortBy);
|
||||
}
|
||||
sortByDropdown.Select(index: 0);
|
||||
sortByDropdown.AfterSelected += (GUIComponent selected, object userdata) =>
|
||||
{
|
||||
FilterEntities(selectedItemCategory, itemFilterBox.Text);
|
||||
SortItems(character: Character.Controlled);
|
||||
return true;
|
||||
};
|
||||
sortByArea.RectTransform.MinSize = new Point(0, sortByDropdown.Rect.Height);
|
||||
sortByArea.RectTransform.MaxSize = new Point(int.MaxValue, sortByDropdown.Rect.Height);
|
||||
|
||||
var availableOnlyTickBoxArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), paddedItemFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
Visible = ShowAvailableOnlyTickBox,
|
||||
IgnoreLayoutGroups = !ShowAvailableOnlyTickBox
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.4f, 1f), availableOnlyTickBoxArea.RectTransform), TextManager.Get("fabricator.onlyshowavailable"),
|
||||
font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
AutoScaleVertical = true
|
||||
};
|
||||
availableOnlyTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f), availableOnlyTickBoxArea.RectTransform, scaleBasis: ScaleBasis.BothHeight), label: string.Empty)
|
||||
{
|
||||
ToolTip = TextManager.Get("fabricator.onlyshowavailable.tooltip")
|
||||
};
|
||||
availableOnlyTickBox.OnSelected += (tickbox) =>
|
||||
{
|
||||
FilterEntities(selectedItemCategory, itemFilterBox.Text);
|
||||
return true;
|
||||
};
|
||||
availableOnlyTickBox.RectTransform.MinSize = new Point(availableOnlyTickBox.Rect.Height);
|
||||
availableOnlyTickBox.RectTransform.IsFixedSize = true;
|
||||
availableOnlyTickBoxArea.RectTransform.MinSize = new Point(0, availableOnlyTickBox.Rect.Height);
|
||||
availableOnlyTickBoxArea.RectTransform.MaxSize = new Point(int.MaxValue, availableOnlyTickBox.Rect.Height);
|
||||
|
||||
itemList = new GUIListBox(new RectTransform(new Vector2(1f, 0.8f), paddedItemFrame.RectTransform), style: null)
|
||||
{
|
||||
PlaySoundOnSelect = true,
|
||||
OnSelected = (component, userdata) =>
|
||||
{
|
||||
selectedItem = userdata as FabricationRecipe;
|
||||
if (selectedItem != null) { SelectItem(Character.Controlled, selectedItem); }
|
||||
return true;
|
||||
if (userdata is FabricationRecipe fabricationRecipe)
|
||||
{
|
||||
selectedItem = fabricationRecipe;
|
||||
SelectItem(Character.Controlled, selectedItem);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// === SEPARATOR === //
|
||||
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), topFrame.RectTransform, Anchor.Center), style: "VerticalLine");
|
||||
// === SEPARATOR === //
|
||||
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), topFrame.RectTransform, Anchor.Center), style: "VerticalLine");
|
||||
|
||||
// === OUTPUT AREA === //
|
||||
var outputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1f), topFrame.RectTransform, Anchor.TopRight), childAnchor: Anchor.Center);
|
||||
var paddedOutputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), outputArea.RectTransform));
|
||||
var outputTopArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5F), paddedOutputArea.RectTransform, Anchor.Center), isHorizontal: true);
|
||||
paddedOutputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), outputArea.RectTransform)) { Stretch = true };
|
||||
outputTopArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), paddedOutputArea.RectTransform, Anchor.Center), isHorizontal: true);
|
||||
// === OUTPUT SLOT === //
|
||||
outputSlot = new GUIFrame(new RectTransform(new Vector2(0.4f, 1f), outputTopArea.RectTransform), style: null);
|
||||
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.2f), outputSlot.RectTransform, Anchor.BottomCenter), style: null);
|
||||
outputSlot = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.4f), outputTopArea.RectTransform, scaleBasis: ScaleBasis.BothWidth), style: null);
|
||||
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.0f), outputSlot.RectTransform, Anchor.BottomCenter), style: null);
|
||||
new GUICustomComponent(new RectTransform(Vector2.One, outputInventoryHolder.RectTransform), DrawOutputOverLay) { CanBeFocused = false };
|
||||
// === DESCRIPTION === //
|
||||
selectedItemFrame = new GUIFrame(new RectTransform(new Vector2(0.6f, 1f), outputTopArea.RectTransform), style: null);
|
||||
@@ -213,7 +296,7 @@ namespace Barotrauma.Items.Components
|
||||
selectedItemReqsFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), paddedOutputArea.RectTransform), style: null);
|
||||
|
||||
// === BOTTOM AREA === //
|
||||
var bottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.3f), mainFrame.RectTransform), style: null);
|
||||
var bottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.2f), mainFrame.RectTransform), style: null);
|
||||
|
||||
if (inputContainer.Capacity > 0)
|
||||
{
|
||||
@@ -298,6 +381,33 @@ namespace Barotrauma.Items.Components
|
||||
CanBeFocused = false
|
||||
};
|
||||
CreateRecipes();
|
||||
|
||||
foreach (MapEntityCategory category in itemCategories)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
|
||||
TextManager.Get("MapEntityCategory." + category), textColor: GUIStyle.TextColorBright)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
UserData = category,
|
||||
Visible = false
|
||||
};
|
||||
}
|
||||
|
||||
requiresRecipeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
|
||||
TextManager.Get("fabricatorrequiresrecipe"), textColor: Color.Red, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
AutoScaleHorizontal = true,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
nothingToShowText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.8f), itemList.Content.RectTransform), TextManager.Get("noitemsheader"),
|
||||
textAlignment: Alignment.Center, textColor: GUIStyle.TextColorDim)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
SortItems(character: Character.Controlled);
|
||||
}
|
||||
|
||||
private void RefreshActivateButtonText()
|
||||
@@ -343,7 +453,8 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), container.RectTransform), GetRecipeNameAndAmount(fi))
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), container.RectTransform),
|
||||
RichString.Rich(GetRecipeNameAndAmount(fi)), font: GUIStyle.SmallFont)
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
AutoScaleVertical = true,
|
||||
@@ -372,17 +483,17 @@ namespace Barotrauma.Items.Components
|
||||
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
|
||||
}
|
||||
|
||||
private static LocalizedString GetRecipeNameAndAmount(FabricationRecipe fabricationRecipe)
|
||||
private static RichString GetRecipeNameAndAmount(FabricationRecipe fabricationRecipe)
|
||||
{
|
||||
if (fabricationRecipe == null) { return ""; }
|
||||
if (fabricationRecipe.Amount > 1)
|
||||
{
|
||||
return TextManager.GetWithVariables("fabricationrecipenamewithamount",
|
||||
("[name]", fabricationRecipe.DisplayName), ("[amount]", fabricationRecipe.Amount.ToString()));
|
||||
("[name]", RichString.Rich(fabricationRecipe.DisplayName)), ("[amount]", fabricationRecipe.Amount.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return fabricationRecipe.DisplayName;
|
||||
return RichString.Rich(fabricationRecipe.DisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,73 +508,106 @@ namespace Barotrauma.Items.Components
|
||||
if (character != Character.Controlled) { return; }
|
||||
|
||||
var nonItems = itemList.Content.Children.Where(c => c.UserData is not FabricationRecipe).ToList();
|
||||
nonItems.ForEach(i => itemList.Content.RemoveChild(i));
|
||||
nonItems.ForEach(i => i.Visible = false);
|
||||
|
||||
SortItems(character: null);
|
||||
FilterEntities(selectedItemCategory, itemFilterBox?.Text ?? string.Empty);
|
||||
HideEmptyItemListCategories();
|
||||
}
|
||||
|
||||
private void SortItems(Character character)
|
||||
{
|
||||
SortBy sortBy = (SortBy)sortByDropdown.SelectedData;
|
||||
|
||||
itemList.Content.RectTransform.SortChildren((c1, c2) =>
|
||||
{
|
||||
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
|
||||
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
|
||||
|
||||
int itemPlacement1 = calculatePlacement(item1);
|
||||
int itemPlacement2 = calculatePlacement(item2);
|
||||
if (itemPlacement1 != itemPlacement2)
|
||||
if (item1 == null && item2 == null)
|
||||
{
|
||||
return itemPlacement1 > itemPlacement2 ? -1 : 1;
|
||||
return 0;
|
||||
}
|
||||
else if (item1 == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (item2 == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int calculatePlacement(FabricationRecipe recipe)
|
||||
bool missingRecipe1 = MissingRequiredRecipe(item1, character);
|
||||
bool missingRecipe2 = MissingRequiredRecipe(item2, character);
|
||||
if (missingRecipe1 != missingRecipe2)
|
||||
{
|
||||
if (recipe.RequiresRecipe && !AnyOneHasRecipeForItem(character, recipe.TargetItem))
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
int placement = FabricationDegreeOfSuccess(character, recipe.RequiredSkills) >= 0.5f ? 0 : -1;
|
||||
return placement;
|
||||
return missingRecipe1.CompareTo(missingRecipe2);
|
||||
}
|
||||
|
||||
return string.Compare(item1.DisplayName.Value, item2.DisplayName.Value);
|
||||
switch (sortBy)
|
||||
{
|
||||
case SortBy.Alphabetical:
|
||||
return string.Compare(item1.DisplayName.Value, item2.DisplayName.Value);
|
||||
case SortBy.Category:
|
||||
var category1 = EnumExtensions.GetIndividualFlags(item1.TargetItem.Category).FirstOrDefault();
|
||||
var category2 = EnumExtensions.GetIndividualFlags(item2.TargetItem.Category).FirstOrDefault();
|
||||
if (category1 == category2)
|
||||
{
|
||||
return string.Compare(item1.DisplayName.Value, item2.DisplayName.Value);
|
||||
}
|
||||
return category1.CompareTo(category2);
|
||||
case SortBy.SkillRequirement:
|
||||
float skillRequirement1 = item1.RequiredSkills.Sum(skill => skill.Level);
|
||||
float skillRequirement2 = item2.RequiredSkills.Sum(skill => skill.Level);
|
||||
if (MathUtils.NearlyEqual(skillRequirement1, skillRequirement2))
|
||||
{
|
||||
return string.Compare(item1.DisplayName.Value, item2.DisplayName.Value);
|
||||
}
|
||||
return skillRequirement1.CompareTo(skillRequirement2);
|
||||
case SortBy.Price:
|
||||
float itemValue1 = item1.TargetItem.DefaultPrice?.Price ?? 0;
|
||||
float itemValue2 = item2.TargetItem.DefaultPrice?.Price ?? 0;
|
||||
if (MathUtils.NearlyEqual(itemValue1, itemValue2))
|
||||
{
|
||||
return string.Compare(item1.DisplayName.Value, item2.DisplayName.Value);
|
||||
}
|
||||
return itemValue2.CompareTo(itemValue1);
|
||||
default:
|
||||
throw new NotImplementedException($"Sorting by {sortBy} has not been implemented.");
|
||||
}
|
||||
});
|
||||
|
||||
var sufficientSkillsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
|
||||
TextManager.Get("fabricatorsufficientskills"), textColor: GUIStyle.Green, font: GUIStyle.SubHeadingFont)
|
||||
if (sortBy == SortBy.Category)
|
||||
{
|
||||
AutoScaleHorizontal = true,
|
||||
CanBeFocused = false
|
||||
};
|
||||
sufficientSkillsText.RectTransform.SetAsFirstChild();
|
||||
foreach (var categoryText in itemList.Content.Children.Where(c => c.UserData?.GetType() == typeof(MapEntityCategory)).ToList())
|
||||
{
|
||||
categoryText.RectTransform.SetAsLastChild();
|
||||
var category = (MapEntityCategory)categoryText.UserData;
|
||||
var firstChildWithMatchingCategory = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe recipe && EnumExtensions.GetIndividualFlags(recipe.TargetItem.Category).FirstOrDefault() == category);
|
||||
if (firstChildWithMatchingCategory != null)
|
||||
{
|
||||
categoryText.RectTransform.RepositionChildInHierarchy(itemList.Content.GetChildIndex(firstChildWithMatchingCategory));
|
||||
categoryText.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
categoryText.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var insufficientSkillsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
|
||||
TextManager.Get("fabricatorinsufficientskills"), textColor: Color.Orange, font: GUIStyle.SubHeadingFont)
|
||||
requiresRecipeText.RectTransform.SetAsLastChild();
|
||||
var firstMissingRecipe = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe recipe && MissingRequiredRecipe(recipe, character));
|
||||
if (firstMissingRecipe != null)
|
||||
{
|
||||
AutoScaleHorizontal = true,
|
||||
CanBeFocused = false
|
||||
};
|
||||
var firstinSufficient = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe fabricableItem && FabricationDegreeOfSuccess(character, fabricableItem.RequiredSkills) < 0.5f);
|
||||
if (firstinSufficient != null)
|
||||
{
|
||||
insufficientSkillsText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstinSufficient.RectTransform));
|
||||
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.GetChildIndex(firstMissingRecipe));
|
||||
requiresRecipeText.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sufficientSkillsText.Visible = insufficientSkillsText.Visible = false;
|
||||
sufficientSkillsText.Enabled = insufficientSkillsText.Enabled = false;
|
||||
requiresRecipeText.Visible = false;
|
||||
}
|
||||
|
||||
var requiresRecipeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
|
||||
TextManager.Get("fabricatorrequiresrecipe"), textColor: Color.Red, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
AutoScaleHorizontal = true,
|
||||
CanBeFocused = false
|
||||
};
|
||||
var firstRequiresRecipe = itemList.Content.Children.FirstOrDefault(c =>
|
||||
c.UserData is FabricationRecipe fabricableItem &&
|
||||
fabricableItem.RequiresRecipe && !AnyOneHasRecipeForItem(character, fabricableItem.TargetItem));
|
||||
if (firstRequiresRecipe != null)
|
||||
{
|
||||
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform));
|
||||
}
|
||||
|
||||
FilterEntities(selectedItemCategory, itemFilterBox?.Text ?? string.Empty);
|
||||
HideEmptyItemListCategories();
|
||||
}
|
||||
|
||||
@@ -757,6 +901,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool FilterEntities(MapEntityCategory? category, string filter)
|
||||
{
|
||||
bool onlyShowAvailable = availableOnlyTickBox is { Selected: true };
|
||||
|
||||
bool anyVisible = false;
|
||||
foreach (GUIComponent child in itemList.Content.Children)
|
||||
{
|
||||
FabricationRecipe recipe = child.UserData as FabricationRecipe;
|
||||
@@ -771,9 +918,26 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (recipe.RequiresRecipe && recipe.HideIfNoRecipe)
|
||||
{
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
if (!AnyOneHasRecipeForItem(Character.Controlled, recipe.TargetItem))
|
||||
{
|
||||
child.Visible = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child.Visible =
|
||||
(string.IsNullOrWhiteSpace(filter) || recipe.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase)) &&
|
||||
(!category.HasValue || recipe.TargetItem.Category.HasFlag(category.Value));
|
||||
(!category.HasValue || recipe.TargetItem.Category.HasFlag(category.Value)) &&
|
||||
(!onlyShowAvailable || CanBeFabricated(recipe, availableIngredients, Character.Controlled));
|
||||
if (child.Visible)
|
||||
{
|
||||
anyVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (GUIButton btn in itemCategoryButtons)
|
||||
@@ -781,6 +945,8 @@ namespace Barotrauma.Items.Components
|
||||
btn.Selected = (MapEntityCategory?)btn.UserData == selectedItemCategory;
|
||||
}
|
||||
HideEmptyItemListCategories();
|
||||
nothingToShowText.Visible = !anyVisible;
|
||||
itemList.UserData = "itemlist";
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -788,7 +954,7 @@ namespace Barotrauma.Items.Components
|
||||
private void HideEmptyItemListCategories()
|
||||
{
|
||||
bool visibleElementsChanged = false;
|
||||
//go through the elements backwards, and disable the labels ("insufficient skills to fabricate", "recipe required...") if there's no items below them
|
||||
//go through the elements backwards, and disable the labels if there's no items below them
|
||||
bool recipeVisible = false;
|
||||
foreach (GUIComponent child in itemList.Content.Children.Reverse())
|
||||
{
|
||||
@@ -810,6 +976,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
SortBy sortBy = (SortBy)sortByDropdown.SelectedData;
|
||||
if (sortBy != SortBy.Category)
|
||||
{
|
||||
itemList.Content.Children.Where(c => c.UserData?.GetType() == typeof(MapEntityCategory)).ForEach(c => c.Visible = false);
|
||||
}
|
||||
|
||||
if (visibleElementsChanged)
|
||||
{
|
||||
itemList.UpdateScrollBarSize();
|
||||
@@ -841,8 +1013,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void CreateSelectedItemUI(SelectedRecipe recipe)
|
||||
{
|
||||
var (user, selectedItem, overrideRequiredTime) = recipe;
|
||||
int max = Math.Max(selectedItem.TargetItem.GetMaxStackSize(outputContainer.Inventory) / selectedItem.Amount, 1);
|
||||
var (user, selectedRecipe, overrideRequiredTime) = recipe;
|
||||
int max = Math.Max(selectedRecipe.TargetItem.GetMaxStackSize(outputContainer.Inventory) / selectedRecipe.Amount, 1);
|
||||
|
||||
if (amountInput != null)
|
||||
{
|
||||
@@ -859,18 +1031,59 @@ namespace Barotrauma.Items.Components
|
||||
selectedItemFrame.ClearChildren();
|
||||
selectedItemReqsFrame.ClearChildren();
|
||||
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f, CanBeFocused = true };
|
||||
var paddedReqFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemReqsFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
|
||||
|
||||
LocalizedString itemName = GetRecipeNameAndAmount(selectedItem);
|
||||
LocalizedString itemName = GetRecipeNameAndAmount(selectedRecipe);
|
||||
LocalizedString name = itemName;
|
||||
|
||||
QualityResult result = GetFabricatedItemQuality(selectedItem, user);
|
||||
QualityResult result = GetFabricatedItemQuality(selectedRecipe, user);
|
||||
|
||||
float quality = selectedItem.Quality ?? result.Quality;
|
||||
if (quality > 0 || result.HasRandomQualityRollChance)
|
||||
float minimumQuality = selectedRecipe.Quality ?? result.Quality;
|
||||
|
||||
LocalizedString qualityTooltip = string.Empty;
|
||||
if (result.HasRandomQualityRollChance)
|
||||
{
|
||||
name = TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", itemName + '\n')
|
||||
float plusOnePercentage = result.TotalPlusOnePercentage;
|
||||
float plusTwoPercentage = result.TotalPlusTwoPercentage;
|
||||
|
||||
string plusOnePercentageText = plusOnePercentage.ToString("F1", CultureInfo.InvariantCulture);
|
||||
string plusTwoPercentageText = plusTwoPercentage.ToString("F1", CultureInfo.InvariantCulture);
|
||||
|
||||
int plusOneQuality = Math.Clamp(result.Quality + 1, min: 0, max: 3);
|
||||
int plusTwoQuality = Math.Clamp(result.Quality + 2, min: 0, max: 3);
|
||||
|
||||
LocalizedString plusOneQualityText = TextManager.Get($"quality{plusOneQuality}");
|
||||
LocalizedString plusTwoQualityText = TextManager.Get($"quality{plusTwoQuality}");
|
||||
|
||||
string localizationTag = plusTwoPercentage > 0f && plusOnePercentage > 0 && plusOneQuality != plusTwoQuality ? "meetsbonusrequirementtwice" : "meetsbonusrequirement";
|
||||
|
||||
var variables = new (string Key, LocalizedString Value)[]
|
||||
{
|
||||
("[chance]", plusOnePercentageText), ("[quality]", plusOneQualityText),
|
||||
("[chance2]", plusTwoPercentageText), ("[quality2]", plusTwoQualityText)
|
||||
};
|
||||
|
||||
if (MathUtils.NearlyEqual(plusOnePercentage, 0))
|
||||
{
|
||||
variables = new[] { ("[chance]", plusTwoPercentageText), ("[quality]", plusTwoQualityText) };
|
||||
}
|
||||
|
||||
if (plusOneQuality == plusTwoQuality)
|
||||
{
|
||||
LocalizedString rawPercentage = result.PlusOnePercentage.ToString("F1", CultureInfo.InvariantCulture);
|
||||
variables = new[] { ("[chance]", rawPercentage), ("[quality]", plusOneQualityText) };
|
||||
}
|
||||
|
||||
if (plusOnePercentage >= 100.0f) { minimumQuality = plusOneQuality; }
|
||||
if (plusTwoPercentage >= 100.0f) { minimumQuality = plusTwoQuality; }
|
||||
|
||||
qualityTooltip = TextManager.GetWithVariables(localizationTag, variables);
|
||||
}
|
||||
|
||||
if (minimumQuality > 0 || result.HasRandomQualityRollChance)
|
||||
{
|
||||
name = TextManager.GetWithVariable("itemname.quality" + (int)minimumQuality, "[itemname]", itemName + '\n')
|
||||
.Fallback(TextManager.GetWithVariable("itemname.quality3", "[itemname]", itemName + '\n'));
|
||||
}
|
||||
|
||||
@@ -884,44 +1097,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
var iconLayout = new GUIFrame(new RectTransform(new Vector2(0.4f, 1f), selectedItemFrame.RectTransform, anchor: Anchor.TopRight), style: null);
|
||||
var icon = GameSession.CreateNotificationIcon(iconLayout, offset: true);
|
||||
|
||||
float percentage1 = result.TotalPlusOnePercentage;
|
||||
float percentage2 = result.TotalPlusTwoPercentage;
|
||||
|
||||
string chance1text = percentage1.ToString("F1", CultureInfo.InvariantCulture);
|
||||
string chance2text = percentage2.ToString("F1", CultureInfo.InvariantCulture);
|
||||
|
||||
int quality1 = Math.Clamp(result.Quality + 1, min: 0, max: 3);
|
||||
int quality2 = Math.Clamp(result.Quality + 2, min: 0, max: 3);
|
||||
|
||||
LocalizedString quality1Text = TextManager.Get($"quality{quality1}");
|
||||
LocalizedString quality2Text = TextManager.Get($"quality{quality2}");
|
||||
|
||||
string localizationTag = percentage2 > 0f && percentage1 > 0 && quality1 != quality2 ? "meetsbonusrequirementtwice" : "meetsbonusrequirement";
|
||||
|
||||
var variables = new (string Key, LocalizedString Value)[]
|
||||
{
|
||||
("[chance]", chance1text), ("[quality]", quality1Text),
|
||||
("[chance2]", chance2text), ("[quality2]", quality2Text)
|
||||
};
|
||||
|
||||
if (MathUtils.NearlyEqual(percentage1, 0))
|
||||
{
|
||||
variables = new[] { ("[chance]", chance2text), ("[quality]", quality2Text) };
|
||||
}
|
||||
|
||||
if (quality1 == quality2)
|
||||
{
|
||||
LocalizedString rawPercentage = result.PlusOnePercentage.ToString("F1", CultureInfo.InvariantCulture);
|
||||
variables = new[] { ("[chance]", rawPercentage), ("[quality]", quality1Text) };
|
||||
}
|
||||
|
||||
LocalizedString qualityTooltip = TextManager.GetWithVariables(localizationTag, variables);
|
||||
|
||||
icon.ToolTip = RichString.Rich(qualityTooltip);
|
||||
icon.Visible = icon.CanBeFocused = true;
|
||||
}
|
||||
|
||||
outputTopArea.RectTransform.MaxSize = new Point(int.MaxValue, outputInventoryHolder.Rect.Height);
|
||||
paddedOutputArea.Recalculate();
|
||||
|
||||
nameBlock.Padding = new Vector4(0, nameBlock.Padding.Y, GUI.IntScale(5), nameBlock.Padding.W);
|
||||
if (nameBlock.TextScale < 0.7f)
|
||||
{
|
||||
@@ -932,31 +1114,41 @@ namespace Barotrauma.Items.Components
|
||||
nameBlock.RectTransform.MinSize = new Point(0, (int)(nameBlock.TextSize.Y * nameBlock.TextScale));
|
||||
}
|
||||
|
||||
if (!selectedItem.TargetItem.Description.IsNullOrEmpty())
|
||||
bool largeUI = GuiFrame.Rect.Height > GUI.IntScale(500);
|
||||
if (largeUI)
|
||||
{
|
||||
var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
|
||||
RichString.Rich(selectedItem.TargetItem.Description),
|
||||
font: GUIStyle.SmallFont, wrap: true);
|
||||
description.Padding = new Vector4(0, description.Padding.Y, description.Padding.Z, description.Padding.W);
|
||||
paddedFrame.ChildAnchor = Anchor.CenterLeft;
|
||||
}
|
||||
|
||||
while (description.Rect.Height + nameBlock.Rect.Height > paddedFrame.Rect.Height)
|
||||
if (!selectedRecipe.TargetItem.Description.IsNullOrEmpty())
|
||||
{
|
||||
var descriptionParent = largeUI ? paddedReqFrame : paddedFrame;
|
||||
var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), descriptionParent.RectTransform),
|
||||
RichString.Rich(selectedRecipe.TargetItem.Description),
|
||||
font: GUIStyle.SmallFont, wrap: true);
|
||||
if (!largeUI)
|
||||
{
|
||||
description.Padding = new Vector4(0, description.Padding.Y, description.Padding.Z, description.Padding.W);
|
||||
}
|
||||
|
||||
while (description.Rect.Height + nameBlock.Rect.Height > descriptionParent.Rect.Height)
|
||||
{
|
||||
var lines = description.WrappedText.Split('\n');
|
||||
if (lines.Count <= 1) { break; }
|
||||
var newString = string.Join('\n', lines.Take(lines.Count - 1));
|
||||
description.Text = newString.Substring(0, newString.Length - 4) + "...";
|
||||
description.CalculateHeightFromText();
|
||||
description.ToolTip = selectedItem.TargetItem.Description;
|
||||
description.ToolTip = selectedRecipe.TargetItem.Description;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<Skill> inadequateSkills = Enumerable.Empty<Skill>();
|
||||
if (user != null)
|
||||
{
|
||||
inadequateSkills = selectedItem.RequiredSkills.Where(skill => user.GetSkillLevel(skill.Identifier) < Math.Round(skill.Level * SkillRequirementMultiplier));
|
||||
inadequateSkills = selectedRecipe.RequiredSkills.Where(skill => user.GetSkillLevel(skill.Identifier) < Math.Round(skill.Level * SkillRequirementMultiplier));
|
||||
}
|
||||
|
||||
if (selectedItem.RequiredSkills.Any())
|
||||
if (selectedRecipe.RequiredSkills.Any())
|
||||
{
|
||||
LocalizedString text = "";
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform),
|
||||
@@ -965,20 +1157,20 @@ namespace Barotrauma.Items.Components
|
||||
AutoScaleHorizontal = true,
|
||||
ToolTip = TextManager.Get("fabricatorrequiredskills.tooltip")
|
||||
};
|
||||
foreach (Skill skill in selectedItem.RequiredSkills)
|
||||
foreach (Skill skill in selectedRecipe.RequiredSkills)
|
||||
{
|
||||
text += TextManager.Get("SkillName." + skill.Identifier) + " " + TextManager.Get("Lvl").ToLower() + " " + Math.Round(skill.Level * SkillRequirementMultiplier);
|
||||
if (skill != selectedItem.RequiredSkills.Last()) { text += "\n"; }
|
||||
if (skill != selectedRecipe.RequiredSkills.Last()) { text += "\n"; }
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), text, font: GUIStyle.SmallFont);
|
||||
}
|
||||
|
||||
float degreeOfSuccess = user == null ? 0.0f : FabricationDegreeOfSuccess(user, selectedItem.RequiredSkills);
|
||||
float degreeOfSuccess = user == null ? 0.0f : FabricationDegreeOfSuccess(user, selectedRecipe.RequiredSkills);
|
||||
if (degreeOfSuccess > 0.5f) { degreeOfSuccess = 1.0f; }
|
||||
|
||||
float requiredTime = overrideRequiredTime.TryUnwrap(out var time)
|
||||
? time
|
||||
: (user == null ? selectedItem.RequiredTime : GetRequiredTime(selectedItem, user));
|
||||
: (user == null ? selectedRecipe.RequiredTime : GetRequiredTime(selectedRecipe, user));
|
||||
|
||||
if ((int)requiredTime > 0)
|
||||
{
|
||||
@@ -991,7 +1183,7 @@ namespace Barotrauma.Items.Components
|
||||
font: GUIStyle.SmallFont);
|
||||
}
|
||||
|
||||
if (SelectedItem.RequiredMoney > 0)
|
||||
if (selectedRecipe.RequiredMoney > 0)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform),
|
||||
TextManager.Get("subeditor.price"), textColor: ToolBox.GradientLerp(degreeOfSuccess, GUIStyle.Red, Color.Yellow, GUIStyle.Green), font: GUIStyle.SubHeadingFont)
|
||||
@@ -1000,7 +1192,6 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), TextManager.FormatCurrency(SelectedItem.RequiredMoney),
|
||||
font: GUIStyle.SmallFont);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,7 +1222,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (selectedItem == null) { return false; }
|
||||
if (fabricatedItem == null &&
|
||||
!outputContainer.Inventory.CanBePut(selectedItem.TargetItem, selectedItem.OutCondition * selectedItem.TargetItem.Health))
|
||||
!outputContainer.Inventory.CanProbablyBePut(selectedItem.TargetItem, selectedItem.OutCondition * selectedItem.TargetItem.Health))
|
||||
{
|
||||
outputSlot.Flash(GUIStyle.Red);
|
||||
return false;
|
||||
@@ -1101,8 +1292,14 @@ namespace Barotrauma.Items.Components
|
||||
activateButton.Enabled = canBeFabricated;
|
||||
}
|
||||
|
||||
bool sufficientSkills = FabricationDegreeOfSuccess(character, recipe.RequiredSkills) >= 0.5f;
|
||||
|
||||
Color baseColor = MissingRequiredRecipe(recipe, character) ?
|
||||
GUIStyle.Red :
|
||||
(sufficientSkills ? GUIStyle.TextColorNormal : GUIStyle.Orange);
|
||||
|
||||
var childContainer = child.GetChild<GUILayoutGroup>();
|
||||
childContainer.GetChild<GUITextBlock>().TextColor = Color.White * (canBeFabricated ? 1.0f : 0.5f);
|
||||
childContainer.GetChild<GUITextBlock>().TextColor = baseColor * (canBeFabricated ? 1.0f : 0.5f);
|
||||
childContainer.GetChild<GUIImage>().Color = recipe.TargetItem.InventoryIconColor * (canBeFabricated ? 1.0f : 0.5f);
|
||||
|
||||
var limitReachedText = child.FindChild(nameof(FabricationLimitReachedText));
|
||||
|
||||
@@ -10,21 +10,10 @@ using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
internal readonly struct MiniMapGUIComponent
|
||||
internal readonly record struct MiniMapGUIComponent(GUIComponent RectComponent, GUIComponent BorderComponent)
|
||||
{
|
||||
public readonly GUIComponent RectComponent;
|
||||
public readonly GUIComponent BorderComponent;
|
||||
|
||||
public MiniMapGUIComponent(GUIComponent rectComponent)
|
||||
public MiniMapGUIComponent(GUIComponent rectComponent) : this(rectComponent, rectComponent)
|
||||
{
|
||||
RectComponent = rectComponent;
|
||||
BorderComponent = rectComponent;
|
||||
}
|
||||
|
||||
public MiniMapGUIComponent(GUIComponent frame, GUIComponent linkedHullComponent)
|
||||
{
|
||||
RectComponent = frame;
|
||||
BorderComponent = linkedHullComponent;
|
||||
}
|
||||
|
||||
public void Deconstruct(out GUIComponent component, out GUIComponent borderComponent)
|
||||
@@ -34,21 +23,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
internal readonly struct MiniMapSprite
|
||||
internal readonly record struct MiniMapSprite(Sprite? Sprite, Color Color)
|
||||
{
|
||||
public readonly Sprite? Sprite;
|
||||
public readonly Color Color;
|
||||
|
||||
public MiniMapSprite(JobPrefab prefab)
|
||||
public MiniMapSprite(JobPrefab prefab) : this(prefab.IconSmall, prefab.UIColor)
|
||||
{
|
||||
Sprite = prefab.IconSmall;
|
||||
Color = prefab.UIColor;
|
||||
}
|
||||
|
||||
public MiniMapSprite(Order order)
|
||||
public MiniMapSprite(Order order) : this(order.SymbolSprite, order.Color)
|
||||
{
|
||||
Sprite = order.SymbolSprite;
|
||||
Color = order.Color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,6 +206,26 @@ namespace Barotrauma.Items.Components
|
||||
ElectricalBaseColor = GUIStyle.Orange,
|
||||
NoPowerElectricalColor = ElectricalBaseColor * 0.1f;
|
||||
|
||||
// If this is portable, only allow displaying data in the player sub (not enemy subs, ruins, wrecks or other unknown places)
|
||||
private bool IsPortableItemAllowed
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsUsableOutsidePlayerSub) { return true; }
|
||||
if (item.Submarine == null) { return false; }
|
||||
if (item.GetComponent<Pickable>() is not Pickable handheldItem) { return true; }
|
||||
// This will effectively make sure wherever we are, it belongs to the player
|
||||
return handheldItem.Picker?.TeamID == item.Submarine.TeamID;
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If this item is portable, should it be usable outside the player submarine?")]
|
||||
public bool IsUsableOutsidePlayerSub
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
hullDatas = new Dictionary<Hull, HullData>();
|
||||
@@ -425,22 +427,25 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool VisibleOnItemFinder(Item it)
|
||||
private bool VisibleOnItemFinder(Item targetItem)
|
||||
{
|
||||
if (it?.Submarine == null) { return false; }
|
||||
if (item.Submarine == null || !item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true)) { return false; }
|
||||
if (it.NonInteractable || it.IsHidden) { return false; }
|
||||
if (it.GetComponent<Pickable>() == null) { return false; }
|
||||
if (targetItem?.Submarine == null || item.Submarine == null) { return false; }
|
||||
|
||||
var holdable = it.GetComponent<Holdable>();
|
||||
if (!IsPortableItemAllowed) { return false; }
|
||||
|
||||
if (!item.Submarine.IsEntityFoundOnThisSub(targetItem, includingConnectedSubs: true)) { return false; }
|
||||
if (targetItem.NonInteractable || targetItem.IsHidden) { return false; }
|
||||
if (targetItem.GetComponent<Pickable>() == null) { return false; }
|
||||
|
||||
var holdable = targetItem.GetComponent<Holdable>();
|
||||
if (holdable != null && holdable.Attached) { return false; }
|
||||
|
||||
var wire = it.GetComponent<Wire>();
|
||||
var wire = targetItem.GetComponent<Wire>();
|
||||
if (wire != null && wire.Connections.Any(c => c != null)) { return false; }
|
||||
|
||||
if (it.Container?.GetComponent<ItemContainer>() is { DrawInventory: false } or { AllowAccess: false }) { return false; }
|
||||
if (targetItem.Container?.GetComponent<ItemContainer>() is { DrawInventory: false } or { AllowAccess: false }) { return false; }
|
||||
|
||||
if (it.HasTag(Tags.TraitorMissionItem)) { return false; }
|
||||
if (targetItem.HasTag(Tags.TraitorMissionItem)) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -455,18 +460,24 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateHUD()
|
||||
private void ClearHUD()
|
||||
{
|
||||
subEntities.Clear();
|
||||
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
submarineContainer.ClearChildren();
|
||||
displayedSubs.Clear();
|
||||
}
|
||||
|
||||
if (item.Submarine is null)
|
||||
private void RefreshHUD()
|
||||
{
|
||||
ClearHUD();
|
||||
|
||||
if (item.Submarine is null || !IsPortableItemAllowed)
|
||||
{
|
||||
displayedSubs.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
|
||||
scissorComponent = new GUIScissorComponent(new RectTransform(Vector2.One, submarineContainer.RectTransform, Anchor.Center));
|
||||
miniMapContainer = new GUIFrame(new RectTransform(Vector2.One, scissorComponent.Content.RectTransform, Anchor.Center), style: null) { CanBeFocused = false };
|
||||
|
||||
@@ -574,18 +585,25 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
//recreate HUD if the subs we should display have changed
|
||||
if (item.Submarine == null && displayedSubs.Count > 0 || // item not inside a sub anymore, but display is still showing subs
|
||||
// Refresh HUD (including possibly just clearing it away) if the subs we should display have changed
|
||||
if (item.Submarine == null && displayedSubs.Count > 0 || // item not inside a sub anymore, but display is still showing subs
|
||||
item.Submarine is { } itemSub &&
|
||||
(
|
||||
!displayedSubs.Contains(itemSub) || // current sub not displayed
|
||||
itemSub.DockedTo.Where(s => s.TeamID == item.Submarine.TeamID).Any(s => !displayedSubs.Contains(s) && itemSub.ConnectedDockingPorts[s].IsLocked) || // some of the docked subs not displayed
|
||||
displayedSubs.Any(s => s != itemSub && !itemSub.DockedTo.Contains(s)) // displaying a sub that shouldn't be displayed
|
||||
// current sub not displayed
|
||||
!displayedSubs.Contains(itemSub) ||
|
||||
// some of the docked subs not displayed
|
||||
itemSub.DockedTo.Where(s => s.TeamID == item.Submarine.TeamID).Any(s => !displayedSubs.Contains(s) && itemSub.ConnectedDockingPorts[s].IsLocked) ||
|
||||
// displaying a sub that shouldn't be displayed
|
||||
displayedSubs.Any(s => s != itemSub && !itemSub.DockedTo.Contains(s))
|
||||
) ||
|
||||
prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight || // resolution changed
|
||||
!submarineContainer.Children.Any()) // We lack a GUI
|
||||
// If this item is portable and not in a player sub and using it otherwise is disallowed
|
||||
!IsPortableItemAllowed ||
|
||||
// resolution changed
|
||||
prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight ||
|
||||
// We lack a GUI
|
||||
!submarineContainer.Children.Any())
|
||||
{
|
||||
CreateHUD();
|
||||
RefreshHUD();
|
||||
}
|
||||
|
||||
//reset data if we haven't received anything in a while
|
||||
@@ -737,7 +755,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (Voltage < MinVoltage)
|
||||
if (!HasPower)
|
||||
{
|
||||
Vector2 textSize = GUIStyle.Font.MeasureString(noPowerTip);
|
||||
Vector2 textPos = GuiFrame.Rect.Center.ToVector2();
|
||||
@@ -747,7 +765,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentMode == MiniMapMode.HullStatus && item.Submarine != null)
|
||||
if (currentMode == MiniMapMode.HullStatus && item.Submarine != null && IsPortableItemAllowed)
|
||||
{
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
spriteBatch.End();
|
||||
@@ -958,19 +976,19 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
MiniMapBlips = positions.ToImmutableHashSet();
|
||||
|
||||
if (searchAutoComplete is null) { return; }
|
||||
searchAutoComplete.Visible = false;
|
||||
HideGUIComponent(searchAutoComplete);
|
||||
}
|
||||
|
||||
private void UpdateHUDBack()
|
||||
{
|
||||
if (item.Submarine == null) { return; }
|
||||
// Clear up mode-specific elements before checking if drawing should continue, so they'll be gone if not
|
||||
HideModeSpecificFrames();
|
||||
|
||||
if (hullInfoFrame != null) { hullInfoFrame.Visible = false; }
|
||||
reportFrame.Visible = false;
|
||||
searchBarFrame.Visible = false;
|
||||
electricalFrame.Visible = false;
|
||||
miniMapFrame.Visible = false;
|
||||
if (item.Submarine == null || !IsPortableItemAllowed)
|
||||
{
|
||||
ClearHUD();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (currentMode)
|
||||
{
|
||||
@@ -989,6 +1007,23 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void HideModeSpecificFrames()
|
||||
{
|
||||
HideGUIComponent(hullInfoFrame);
|
||||
HideGUIComponent(reportFrame);
|
||||
HideGUIComponent(searchBarFrame);
|
||||
HideGUIComponent(electricalFrame);
|
||||
HideGUIComponent(miniMapFrame);
|
||||
}
|
||||
|
||||
private static void HideGUIComponent(GUIComponent? component)
|
||||
{
|
||||
if (component != null)
|
||||
{
|
||||
component.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHullStatus()
|
||||
{
|
||||
bool canHoverOverHull = true;
|
||||
@@ -1007,7 +1042,7 @@ namespace Barotrauma.Items.Components
|
||||
child.Color = child.OutlineColor = NoPowerDoorColor;
|
||||
}
|
||||
|
||||
if (Voltage < MinVoltage) { continue; }
|
||||
if (!HasPower) { continue; }
|
||||
|
||||
child.Color = child.OutlineColor = DoorIndicatorColor;
|
||||
if (GUI.MouseOn == child)
|
||||
@@ -1037,7 +1072,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (Voltage < MinVoltage) { continue; }
|
||||
if (!HasPower) { continue; }
|
||||
|
||||
hullDatas.TryGetValue(hull, out HullData? hullData);
|
||||
if (hullData is null)
|
||||
@@ -1187,7 +1222,7 @@ namespace Barotrauma.Items.Components
|
||||
component.Color = component.OutlineColor = NoPowerElectricalColor;
|
||||
}
|
||||
|
||||
if (Voltage < MinVoltage || !miniMapGuiComponent.RectComponent.Visible) { continue; }
|
||||
if (!HasPower || !miniMapGuiComponent.RectComponent.Visible) { continue; }
|
||||
|
||||
int durability = (int)(it.Condition / (it.MaxCondition / it.MaxRepairConditionMultiplier) * 100f);
|
||||
Color color = ToolBox.GradientLerp(durability / 100f, GUIStyle.Red, GUIStyle.Orange, GUIStyle.Green, GUIStyle.Green);
|
||||
@@ -1229,11 +1264,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void DrawHUDBack(SpriteBatch spriteBatch, GUICustomComponent container)
|
||||
{
|
||||
if (item.Submarine == null) { return; }
|
||||
if (item.Submarine == null || !IsPortableItemAllowed) { return; }
|
||||
|
||||
DrawSubmarine(spriteBatch);
|
||||
|
||||
if (Voltage < MinVoltage) { return; }
|
||||
if (!HasPower) { return; }
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
|
||||
@@ -475,11 +475,8 @@ namespace Barotrauma.Items.Components
|
||||
if (sound != null)
|
||||
{
|
||||
SoundPlayer.PlaySound(
|
||||
sound.Sound,
|
||||
sound,
|
||||
item.WorldPosition,
|
||||
sound.Volume,
|
||||
sound.Range,
|
||||
freqMult: sound.GetRandomFrequencyMultiplier(),
|
||||
hullGuess: item.CurrentHull);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ namespace Barotrauma.Items.Components
|
||||
private bool isConnectedToSteering;
|
||||
|
||||
private static LocalizedString caveLabel;
|
||||
private static LocalizedString enemyLabel;
|
||||
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
@@ -164,6 +165,8 @@ namespace Barotrauma.Items.Components
|
||||
TextManager.Get("cave").Fallback(
|
||||
TextManager.Get("missiontype.nest"));
|
||||
|
||||
enemyLabel = TextManager.Get("enemysubmarine");
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -216,8 +219,9 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 size = isConnectedToSteering ? controlBoxSize : new Vector2(0.46f, 0.4f);
|
||||
|
||||
controlContainer = new GUIFrame(new RectTransform(size, GuiFrame.RectTransform, Anchor.BottomLeft), "ItemUI");
|
||||
if (!isConnectedToSteering && !GUI.IsFourByThree())
|
||||
if (!isConnectedToSteering && GUI.AspectRatioDifference <= 0)
|
||||
{
|
||||
// In wider than 4:3 aspect ratio, we'll limit the max size.
|
||||
controlContainer.RectTransform.MaxSize = new Point((int)(380 * GUI.xScale), (int)(300 * GUI.yScale));
|
||||
}
|
||||
var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center)
|
||||
@@ -1019,6 +1023,38 @@ namespace Barotrauma.Items.Components
|
||||
cave.StartPos.ToVector2(), transducerCenter,
|
||||
DisplayScale, center, DisplayRadius);
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember is { } networkMember && GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
if (networkMember.ServerSettings.TrackOpponentInPvP
|
||||
&& Submarine.MainSubs[0] is { } coalitionSub
|
||||
&& Submarine.MainSubs[1] is { } separatistSub
|
||||
&& Character.Controlled is { } player)
|
||||
{
|
||||
Submarine whichSubToDraw = player.TeamID switch
|
||||
{
|
||||
CharacterTeamType.Team1 => separatistSub,
|
||||
CharacterTeamType.Team2 => coalitionSub,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (whichSubToDraw != null)
|
||||
{
|
||||
DrawOffsetMarker(spriteBatch,
|
||||
enemyLabel.Value,
|
||||
Tags.Submarine,
|
||||
Tags.Enemy,
|
||||
whichSubToDraw.WorldPosition,
|
||||
transducerCenter,
|
||||
distanceThresholds: new Range<float>(start: MetersToUnits(150), end: MetersToUnits(1600)),
|
||||
offset: new Range<float>(start: MetersToUnits(100), end: MetersToUnits(400)),
|
||||
minOffset: MetersToUnits(10));
|
||||
|
||||
static float MetersToUnits(float m)
|
||||
=> m / Physics.DisplayToRealWorldRatio;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int missionIndex = 0;
|
||||
@@ -1042,7 +1078,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
if (HasMineralScanner && UseMineralScanner && CurrentMode == Mode.Active && MineralClusters != null &&
|
||||
(item.CurrentHull == null || !DetectSubmarineWalls))
|
||||
(item.CurrentHull == null || !DetectSubmarineWalls) &&
|
||||
HasPower)
|
||||
{
|
||||
foreach (var c in MineralClusters)
|
||||
{
|
||||
@@ -1076,7 +1113,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!sub.ShowSonarMarker) { continue; }
|
||||
if (connectedSubs.Contains(sub)) { continue; }
|
||||
if (Level.Loaded != null && sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
if (sub.IsAboveLevel) { continue; }
|
||||
|
||||
if (item.Submarine != null || Character.Controlled != null)
|
||||
{
|
||||
@@ -1185,7 +1222,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (DockingPort dockingPort in DockingPort.List)
|
||||
{
|
||||
if (Level.Loaded != null && dockingPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
if (dockingPort.Item.Submarine.IsAboveLevel) { continue; }
|
||||
if (dockingPort.Item.IsHidden) { continue; }
|
||||
if (dockingPort.Item.Submarine == null) { continue; }
|
||||
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
|
||||
@@ -1198,8 +1235,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//don't show the docking ports of the opposing team on the sonar
|
||||
if (item.Submarine != null &&
|
||||
item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
|
||||
dockingPort.Item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
|
||||
!item.Submarine.IsRespawnShuttle &&
|
||||
!dockingPort.Item.Submarine.IsRespawnShuttle &&
|
||||
!dockingPort.Item.Submarine.Info.IsOutpost &&
|
||||
!dockingPort.Item.Submarine.Info.IsBeacon)
|
||||
{
|
||||
@@ -1792,8 +1829,47 @@ namespace Barotrauma.Items.Components
|
||||
sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f * blip.Alpha, sonarBlip.Origin, 0, scale, SpriteEffects.None, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used in DrawOffsetMarker to cache the randomized location of the marker
|
||||
/// </summary>
|
||||
private readonly Dictionary<Identifier, CachedLocation> cachedLocations = new Dictionary<Identifier, CachedLocation>();
|
||||
|
||||
private void DrawOffsetMarker(SpriteBatch spriteBatch, string label, Identifier iconIdentifier, Identifier targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, Range<float> distanceThresholds, Range<float> offset, float minOffset)
|
||||
{
|
||||
Vector2 pos;
|
||||
|
||||
if (!cachedLocations.TryGetValue(targetIdentifier, out CachedLocation cachedLocation))
|
||||
{
|
||||
cachedLocation = CreateCachedLocation();
|
||||
cachedLocations.Add(targetIdentifier, cachedLocation);
|
||||
pos = cachedLocation.Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Timing.TotalTime > cachedLocation.RecalculationTime)
|
||||
{
|
||||
cachedLocation = CreateCachedLocation();
|
||||
cachedLocations[targetIdentifier] = cachedLocation;
|
||||
}
|
||||
|
||||
pos = cachedLocation.Location;
|
||||
}
|
||||
|
||||
DrawMarker(spriteBatch, label, iconIdentifier, targetIdentifier, pos, transducerPosition, DisplayScale, center, DisplayRadius);
|
||||
|
||||
CachedLocation CreateCachedLocation()
|
||||
{
|
||||
float distance = Vector2.Distance(worldPosition, transducerPosition);
|
||||
|
||||
float maxOffset = MathHelper.Lerp(offset.Start, offset.End, MathHelper.Clamp((distance - distanceThresholds.Start) / (distanceThresholds.End - distanceThresholds.Start), 0.0f, 1.0f));
|
||||
|
||||
Vector2 randomPos = Rand.Vector(Rand.Range(minOffset, maxOffset));
|
||||
return new CachedLocation(worldPosition + randomPos, Timing.TotalTime + Rand.Range(10.0f, 30.0f));
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMarker(SpriteBatch spriteBatch, string label, Identifier iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius,
|
||||
bool onlyShowTextOnMouseOver = false)
|
||||
bool onlyShowTextOnMouseOver = false)
|
||||
{
|
||||
float linearDist = Vector2.Distance(worldPosition, transducerPosition);
|
||||
float dist = linearDist;
|
||||
@@ -1903,25 +1979,6 @@ namespace Barotrauma.Items.Components
|
||||
2, GUIStyle.SmallFont);
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
sonarBlip?.Remove();
|
||||
pingCircle?.Remove();
|
||||
directionalPingCircle?.Remove();
|
||||
screenOverlay?.Remove();
|
||||
screenBackground?.Remove();
|
||||
lineSprite?.Remove();
|
||||
|
||||
foreach (var t in targetIcons.Values)
|
||||
{
|
||||
t.Item1.Remove();
|
||||
}
|
||||
targetIcons.Clear();
|
||||
|
||||
MineralClusters = null;
|
||||
}
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.WriteBoolean(currentMode == Mode.Active);
|
||||
|
||||
@@ -57,24 +57,42 @@ namespace Barotrauma.Items.Components
|
||||
private GUIMessageBox enterOutpostPrompt, exitOutpostPrompt;
|
||||
|
||||
private bool levelStartSelected;
|
||||
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes, AlwaysUseInstanceValues = true)]
|
||||
public bool LevelStartSelected
|
||||
{
|
||||
get { return levelStartTickBox.Selected; }
|
||||
set { levelStartTickBox.Selected = value; }
|
||||
get
|
||||
{
|
||||
return levelStartTickBox?.Selected ?? levelStartSelected;
|
||||
}
|
||||
set
|
||||
{
|
||||
TrySetTickBoxSelected(levelStartTickBox, ref levelStartSelected, value);
|
||||
}
|
||||
}
|
||||
|
||||
private bool levelEndSelected;
|
||||
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes, AlwaysUseInstanceValues = true)]
|
||||
public bool LevelEndSelected
|
||||
{
|
||||
get { return levelEndTickBox.Selected; }
|
||||
set { levelEndTickBox.Selected = value; }
|
||||
get { return levelEndTickBox?.Selected ?? levelEndSelected; }
|
||||
set
|
||||
{
|
||||
TrySetTickBoxSelected(levelEndTickBox, ref levelEndSelected, value);
|
||||
}
|
||||
}
|
||||
|
||||
private bool maintainPos;
|
||||
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes, AlwaysUseInstanceValues = true)]
|
||||
public bool MaintainPos
|
||||
{
|
||||
get { return maintainPosTickBox.Selected; }
|
||||
set { maintainPosTickBox.Selected = value; }
|
||||
get
|
||||
{
|
||||
return maintainPosTickBox?.Selected ?? maintainPos;
|
||||
}
|
||||
set
|
||||
{
|
||||
TrySetTickBoxSelected(maintainPosTickBox, ref maintainPos, value);
|
||||
}
|
||||
}
|
||||
|
||||
private float steerRadius;
|
||||
@@ -554,7 +572,7 @@ namespace Barotrauma.Items.Components
|
||||
int x = rect.X;
|
||||
int y = rect.Y;
|
||||
|
||||
if (Voltage < MinVoltage) { return; }
|
||||
if (!HasPower) { return; }
|
||||
|
||||
Rectangle velRect = new Rectangle(x + 20, y + 20, width - 40, height - 40);
|
||||
Vector2 steeringOrigin = steerArea.Rect.Center.ToVector2();
|
||||
@@ -759,7 +777,7 @@ namespace Barotrauma.Items.Components
|
||||
dockingButton.Text = dockText;
|
||||
}
|
||||
|
||||
if (Voltage < MinVoltage)
|
||||
if (!HasPower)
|
||||
{
|
||||
tipContainer.Visible = true;
|
||||
tipContainer.Text = noPowerTip;
|
||||
@@ -829,7 +847,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null)
|
||||
{
|
||||
steeringAdjustSpeed = character == null ? DefaultSteeringAdjustSpeed : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f);
|
||||
steeringAdjustSpeed = character == null ? DefaultSteeringAdjustSpeed : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel(Tags.HelmSkill) / 100.0f);
|
||||
Vector2 input = Vector2.Zero;
|
||||
if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; }
|
||||
if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; }
|
||||
@@ -909,7 +927,7 @@ namespace Barotrauma.Items.Components
|
||||
if (targetPort.Docked || targetPort.Item.Submarine == null) { continue; }
|
||||
if (targetPort.Item.Submarine == controlledSub || targetPort.IsHorizontal != sourcePort.IsHorizontal) { continue; }
|
||||
if (targetPort.Item.Submarine.DockedTo?.Contains(sourcePort.Item.Submarine) ?? false) { continue; }
|
||||
if (Level.Loaded != null && targetPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
if (targetPort.Item.Submarine.IsAboveLevel) { continue; }
|
||||
if (sourceDir == targetPort.GetDir()) { continue; }
|
||||
|
||||
float dist = Vector2.DistanceSquared(sourcePort.Item.WorldPosition, targetPort.Item.WorldPosition);
|
||||
@@ -924,6 +942,21 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the specified tickbox, or if it hasn't been instantiated (yet?), just the value of the backing field.
|
||||
/// </summary>
|
||||
private void TrySetTickBoxSelected(GUITickBox tickBox, ref bool backingValue, bool newValue)
|
||||
{
|
||||
if (tickBox == null)
|
||||
{
|
||||
backingValue = newValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
tickBox.Selected = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
private bool NudgeButtonClicked(GUIButton btn, object userdata)
|
||||
{
|
||||
if (!MaintainPos || !AutoPilot)
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Barotrauma.Items.Components
|
||||
Hull hull = Entity.FindEntityByID(hullID) as Hull;
|
||||
item.Submarine = submarine;
|
||||
item.CurrentHull = hull;
|
||||
item.body.SetTransform(simPosition, item.body.Rotation);
|
||||
item.body.SetTransformIgnoreContacts(simPosition, item.body.Rotation);
|
||||
|
||||
switch (targetType)
|
||||
{
|
||||
@@ -180,7 +180,11 @@ namespace Barotrauma.Items.Components
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "particleemitter":
|
||||
particleEmitters.Add(new ParticleEmitter(subElement));
|
||||
var emitter = new ParticleEmitter(subElement);
|
||||
//backwards compatibility: previously it was not possible to change if the particles use tracer points, they were always used on projectiles
|
||||
//now emitters don't use them by default, except on projectiles
|
||||
emitter.Prefab.Properties.UseTracerPoints = subElement.GetAttributeBool(nameof(emitter.Prefab.Properties.UseTracerPoints), true);
|
||||
particleEmitters.Add(emitter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
base.DrawHUD(spriteBatch, character);
|
||||
currentTarget?.DrawHUD(spriteBatch, Screen.Selected.Cam, character);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
partial void UseProjSpecific(float deltaTime, Vector2 raystart)
|
||||
{
|
||||
foreach (ParticleEmitter particleEmitter in particleEmitters)
|
||||
@@ -88,25 +87,18 @@ namespace Barotrauma.Items.Components
|
||||
MathUtils.InverseLerp(targetStructure.Prefab.MinHealth, targetStructure.Health, targetStructure.Health - targetStructure.SectionDamage(sectionIndex)),
|
||||
GUIStyle.Red, GUIStyle.Green);
|
||||
|
||||
if (progressBar != null) progressBar.Size = new Vector2(60.0f, 20.0f);
|
||||
|
||||
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
|
||||
if (targetStructure.Submarine != null) particlePos += targetStructure.Submarine.DrawPosition;
|
||||
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
|
||||
foreach (var emitter in particleEmitterHitStructure)
|
||||
{
|
||||
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
|
||||
EmitParticle(emitter, deltaTime, pickedPosition, targetStructure.Submarine);
|
||||
}
|
||||
}
|
||||
|
||||
partial void FixCharacterProjSpecific(Character user, float deltaTime, Character targetCharacter)
|
||||
{
|
||||
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
|
||||
if (targetCharacter.Submarine != null) particlePos += targetCharacter.Submarine.DrawPosition;
|
||||
foreach (var emitter in particleEmitterHitCharacter)
|
||||
{
|
||||
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
|
||||
EmitParticle(emitter, deltaTime, pickedPosition, targetCharacter.Submarine);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,15 +126,22 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
|
||||
if (targetItem.Submarine != null) particlePos += targetItem.Submarine.DrawPosition;
|
||||
foreach ((RelatedItem relatedItem, ParticleEmitter emitter) in particleEmitterHitItem)
|
||||
{
|
||||
if (!relatedItem.MatchesItem(targetItem)) { continue; }
|
||||
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
|
||||
EmitParticle(emitter, deltaTime, pickedPosition, targetItem.Submarine);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitParticle(ParticleEmitter emitter, float deltaTime, Vector2 simPosition, Submarine targetSub)
|
||||
{
|
||||
Vector2 particlePos = ConvertUnits.ToDisplayUnits(simPosition);
|
||||
if (targetSub != null) { particlePos += targetSub.DrawPosition; }
|
||||
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi,
|
||||
tracerPoints: new Tuple<Vector2, Vector2>(item.WorldPosition + TransformedBarrelPos, particlePos));
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
|
||||
@@ -299,6 +299,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
base.DrawHUD(spriteBatch, character);
|
||||
|
||||
IsActive = true;
|
||||
|
||||
float defaultMaxCondition = (item.MaxCondition / item.MaxRepairConditionMultiplier);
|
||||
|
||||
@@ -52,9 +52,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Vector2 sourcePos = GetSourcePos();
|
||||
|
||||
//need to double the size because this is essentially just the radius, we need the diameter
|
||||
// + some extra margin to be on the safe side
|
||||
return new Vector2(
|
||||
Math.Abs(target.DrawPosition.X - sourcePos.X),
|
||||
Math.Abs(target.DrawPosition.Y - sourcePos.Y)) * 1.5f;
|
||||
Math.Abs(target.DrawPosition.Y - sourcePos.Y)) * 2.2f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +124,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (turret.BarrelSprite != null)
|
||||
{
|
||||
startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f;
|
||||
startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * turret.Item.Scale * BarrelLengthMultiplier;
|
||||
}
|
||||
startPos -= turret.GetRecoilOffset();
|
||||
}
|
||||
@@ -227,7 +229,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (reelSoundChannel is not { IsPlaying: true })
|
||||
{
|
||||
reelSoundChannel = SoundPlayer.PlaySound(sound.Sound, position, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling, freqMult: sound.GetRandomFrequencyMultiplier());
|
||||
reelSoundChannel = SoundPlayer.PlaySound(sound, position);
|
||||
if (reelSoundChannel != null)
|
||||
{
|
||||
reelSoundChannel.Looping = true;
|
||||
@@ -242,7 +244,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
SoundPlayer.PlaySound(sound.Sound, position, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling, freqMult: sound.GetRandomFrequencyMultiplier());
|
||||
SoundPlayer.PlaySound(sound, position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -16,7 +15,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
{
|
||||
terminalButtonStyles = new string[RequiredSignalCount];
|
||||
terminalButtonStyles = new string[requiredSignalCount];
|
||||
int i = 0;
|
||||
foreach (var childElement in element.GetChildElements("TerminalButton"))
|
||||
{
|
||||
@@ -38,11 +37,11 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
paddedFrame.OnAddedToGUIUpdateList += (component) =>
|
||||
{
|
||||
bool buttonsEnabled = AllowUsingButtons;
|
||||
foreach (var child in component.Children)
|
||||
bool buttonsEnabled = IsActivated;
|
||||
foreach (GUIComponent child in component.Children)
|
||||
{
|
||||
if (!(child is GUIButton)) { continue; }
|
||||
if (!(child.UserData is int)) { continue; }
|
||||
if (child is not GUIButton) { continue; }
|
||||
if (child.UserData is not int) { continue; }
|
||||
child.Enabled = buttonsEnabled;
|
||||
child.Children.ForEach(c => c.Enabled = buttonsEnabled);
|
||||
}
|
||||
@@ -59,7 +58,7 @@ namespace Barotrauma.Items.Components
|
||||
containerIndicator.OverrideState = itemsContained ? GUIComponent.ComponentState.Selected : GUIComponent.ComponentState.None;
|
||||
};
|
||||
|
||||
float x = 1.0f / (1 + RequiredSignalCount);
|
||||
float x = 1.0f / (1 + requiredSignalCount);
|
||||
float y = Math.Min((x * paddedFrame.Rect.Width) / paddedFrame.Rect.Height, 0.5f);
|
||||
Vector2 relativeSize = new Vector2(x, y);
|
||||
|
||||
@@ -69,7 +68,7 @@ namespace Barotrauma.Items.Components
|
||||
containerIndicator = new GUIImage(new RectTransform(new Vector2(0.5f, 0.5f * (1.0f - y)), containerSection.RectTransform, anchor: Anchor.BottomCenter),
|
||||
style: "IndicatorLightRed", scaleToFit: true);
|
||||
|
||||
for (int i = 0; i < RequiredSignalCount; i++)
|
||||
for (int i = 0; i < requiredSignalCount; i++)
|
||||
{
|
||||
var button = new GUIButton(new RectTransform(relativeSize, paddedFrame.RectTransform), style: null)
|
||||
{
|
||||
@@ -111,7 +110,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
SendSignal(msg.ReadRangedInteger(0, Signals.Length - 1), sender: null, isServerMessage: true);
|
||||
SendSignal(msg.ReadRangedInteger(0, Signals.Length - 1), sender: null, ignoreState: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -129,7 +129,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void RemoveComponents(IReadOnlyCollection<CircuitBoxComponent> node)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (IsLocked()) { return; }
|
||||
var ids = node.Select(static n => n.ID).ToImmutableArray();
|
||||
|
||||
if (GameMain.NetworkMember is null)
|
||||
@@ -146,7 +146,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void AddWire(CircuitBoxConnection one, CircuitBoxConnection two)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (IsLocked()) { return; }
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
Connect(one, two, static delegate { }, CircuitBoxWire.SelectedWirePrefab);
|
||||
@@ -160,7 +160,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void RemoveWires(IReadOnlyCollection<CircuitBoxWire> wires)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (IsLocked()) { return; }
|
||||
var ids = wires.Select(static w => w.ID).ToImmutableArray();
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
@@ -230,7 +230,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void MoveComponent(Vector2 moveAmount, IReadOnlyCollection<CircuitBoxNode> moveables)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (IsLocked()) { return; }
|
||||
var ids = ImmutableArray.CreateBuilder<ushort>();
|
||||
var ios = ImmutableArray.CreateBuilder<CircuitBoxInputOutputNode.Type>();
|
||||
var labelIds = ImmutableArray.CreateBuilder<ushort>();
|
||||
@@ -265,7 +265,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void AddComponent(ItemPrefab prefab, Vector2 pos)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (IsLocked()) { return; }
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
ItemPrefab resource;
|
||||
@@ -292,7 +292,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void RenameLabel(CircuitBoxLabelNode label, Color color, NetLimitedString header, NetLimitedString body)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (IsLocked()) { return; }
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
label.EditText(header, body);
|
||||
@@ -316,7 +316,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void ResizeNode(CircuitBoxNode node, CircuitBoxResizeDirection dir, Vector2 amount)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (IsLocked()) { return; }
|
||||
var resize = node.ResizeBy(dir, amount);
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
@@ -341,7 +341,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void AddLabel(Vector2 pos)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (IsLocked()) { return; }
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
AddLabelInternal(ICircuitBoxIdentifiable.FindFreeID(Labels), GUIStyle.Blue, pos, CircuitBoxLabelNode.DefaultHeaderText, NetLimitedString.Empty);
|
||||
@@ -353,7 +353,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void RemoveLabel(IReadOnlyCollection<CircuitBoxLabelNode> labels)
|
||||
{
|
||||
if (Locked) { return; }
|
||||
if (IsLocked()) { return; }
|
||||
if (!labels.Any()) { return; }
|
||||
|
||||
var ids = labels.Select(static n => n.ID).ToImmutableArray();
|
||||
|
||||
@@ -12,7 +12,9 @@ namespace Barotrauma.Items.Components
|
||||
private readonly List<GUIComponent> uiElements = new List<GUIComponent>();
|
||||
private GUILayoutGroup uiElementContainer;
|
||||
|
||||
private bool readingNetworkEvent;
|
||||
private bool suppressNetworkEvents;
|
||||
|
||||
private GUIComponent insufficientPowerWarning;
|
||||
|
||||
private Point ElementMaxSize => new Point(uiElementContainer.Rect.Width, (int)(65 * GUI.yScale));
|
||||
|
||||
@@ -40,7 +42,7 @@ namespace Barotrauma.Items.Components
|
||||
float elementSize = Math.Min(1.0f / visibleElements.Count(), 1);
|
||||
foreach (CustomInterfaceElement ciElement in visibleElements)
|
||||
{
|
||||
if (ciElement.HasPropertyName)
|
||||
if (ciElement.InputType is CustomInterfaceElement.InputTypeOption.Number or CustomInterfaceElement.InputTypeOption.Text)
|
||||
{
|
||||
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, elementSize), uiElementContainer.RectTransform), isHorizontal: true)
|
||||
{
|
||||
@@ -49,7 +51,7 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform),
|
||||
TextManager.Get(ciElement.Label).Fallback(ciElement.Label));
|
||||
if (!ciElement.IsNumberInput)
|
||||
if (ciElement.InputType is CustomInterfaceElement.InputTypeOption.Text)
|
||||
{
|
||||
var textBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform), ciElement.Signal, style: "GUITextBoxNoIcon")
|
||||
{
|
||||
@@ -68,7 +70,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
CreateClientEventWithCorrectionDelay();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -98,13 +100,10 @@ namespace Barotrauma.Items.Components
|
||||
ValueStep = numberInputStep,
|
||||
OnValueChanged = (ni) =>
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
ValueChanged(ni.UserData as CustomInterfaceElement, ni.FloatValue);
|
||||
if (!suppressNetworkEvents && GameMain.Client != null)
|
||||
{
|
||||
ValueChanged(ni.UserData as CustomInterfaceElement, ni.FloatValue);
|
||||
}
|
||||
else if (!readingNetworkEvent)
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
CreateClientEventWithCorrectionDelay();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -124,13 +123,10 @@ namespace Barotrauma.Items.Components
|
||||
ValueStep = numberInputStep,
|
||||
OnValueChanged = (ni) =>
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
ValueChanged(ni.UserData as CustomInterfaceElement, ni.IntValue);
|
||||
if (!suppressNetworkEvents && GameMain.Client != null)
|
||||
{
|
||||
ValueChanged(ni.UserData as CustomInterfaceElement, ni.IntValue);
|
||||
}
|
||||
else if (!readingNetworkEvent)
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
CreateClientEventWithCorrectionDelay();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -149,7 +145,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ciElement.ContinuousSignal)
|
||||
else if (ciElement.InputType is CustomInterfaceElement.InputTypeOption.TickBox)
|
||||
{
|
||||
var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, elementSize), uiElementContainer.RectTransform)
|
||||
{
|
||||
@@ -160,13 +156,10 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
tickBox.OnSelected += (tBox) =>
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
TickBoxToggled(tBox.UserData as CustomInterfaceElement, tBox.Selected);
|
||||
if (!suppressNetworkEvents && GameMain.Client != null)
|
||||
{
|
||||
TickBoxToggled(tBox.UserData as CustomInterfaceElement, tBox.Selected);
|
||||
}
|
||||
else if (!readingNetworkEvent)
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
CreateClientEventWithCorrectionDelay();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@@ -175,7 +168,7 @@ namespace Barotrauma.Items.Components
|
||||
tickBox.RectTransform.MaxSize = new Point(int.MaxValue, int.MaxValue);
|
||||
uiElements.Add(tickBox);
|
||||
}
|
||||
else
|
||||
else if (ciElement.InputType is CustomInterfaceElement.InputTypeOption.Button)
|
||||
{
|
||||
var btn = new GUIButton(new RectTransform(new Vector2(1.0f, elementSize), uiElementContainer.RectTransform),
|
||||
TextManager.Get(ciElement.Label).Fallback(ciElement.Label), style: "DeviceButton")
|
||||
@@ -189,8 +182,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
ButtonClicked(btnElement);
|
||||
}
|
||||
else if (!readingNetworkEvent)
|
||||
else if (!suppressNetworkEvents && GameMain.Client != null)
|
||||
{
|
||||
//don't use CreateClientEventWithCorrectionDelay here, because buttons have no state,
|
||||
//which means we don't need to worry about server updates interfering with client-side changes to the values in the interface
|
||||
item.CreateClientEvent(this, new EventData(btnElement));
|
||||
}
|
||||
return true;
|
||||
@@ -203,6 +198,22 @@ namespace Barotrauma.Items.Components
|
||||
uiElements.Add(btn);
|
||||
}
|
||||
}
|
||||
|
||||
if (ShowInsufficientPowerWarning)
|
||||
{
|
||||
insufficientPowerWarning = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), GuiFrame.RectTransform, Anchor.BottomCenter, Pivot.TopCenter) { MinSize = new Point(0, GUI.IntScale(30)) },
|
||||
TextManager.Get("SteeringNoPowerTip"), font: GUIStyle.Font, wrap: true, style: "GUIToolTip", textAlignment: Alignment.Center)
|
||||
{
|
||||
AutoScaleHorizontal = true,
|
||||
Visible = false
|
||||
};
|
||||
}
|
||||
|
||||
void CreateClientEventWithCorrectionDelay()
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
correctionTimer = CorrectionDelay;
|
||||
}
|
||||
}
|
||||
|
||||
public override void CreateEditingHUD(SerializableEntityEditor editor)
|
||||
@@ -253,7 +264,20 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (uiElement.UserData is not CustomInterfaceElement element) { continue; }
|
||||
bool visible = Screen.Selected == GameMain.SubEditorScreen || element.StatusEffects.Any() || element.HasPropertyName || (element.Connection != null && element.Connection.Wires.Count > 0);
|
||||
if (visible) { visibleElementCount++; }
|
||||
if (visible)
|
||||
{
|
||||
visibleElementCount++;
|
||||
if (element.GetValueInterval > 0.0f && correctionTimer <= 0.0f)
|
||||
{
|
||||
element.GetValueTimer -= deltaTime;
|
||||
if (element.GetValueTimer <= 0.0f)
|
||||
{
|
||||
SetSignalToPropertyValue(element);
|
||||
UpdateSignalProjSpecific(uiElement);
|
||||
element.GetValueTimer = element.GetValueInterval;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uiElement.Visible != visible)
|
||||
{
|
||||
uiElement.Visible = visible;
|
||||
@@ -274,6 +298,11 @@ namespace Barotrauma.Items.Components
|
||||
GuiFrame.Visible = visibleElementCount > 0;
|
||||
uiElementContainer.Recalculate();
|
||||
}
|
||||
|
||||
if (insufficientPowerWarning != null)
|
||||
{
|
||||
insufficientPowerWarning.Visible = item.GetComponents<Powered>().Any(p => p.PowerConsumption > 0.0f && p.Voltage < p.MinVoltage);
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateLabelsProjSpecific()
|
||||
@@ -300,7 +329,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
LocalizedString CreateLabelText(int elementIndex)
|
||||
{
|
||||
var label = customInterfaceElementList[elementIndex].Label;
|
||||
string label = customInterfaceElementList[elementIndex].Label;
|
||||
return string.IsNullOrWhiteSpace(label) ?
|
||||
TextManager.GetWithVariable("connection.signaloutx", "[num]", (elementIndex + 1).ToString()) :
|
||||
TextManager.Get(label).Fallback(label);
|
||||
@@ -336,22 +365,43 @@ namespace Barotrauma.Items.Components
|
||||
if (signals == null) { return; }
|
||||
for (int i = 0; i < signals.Length && i < uiElements.Count; i++)
|
||||
{
|
||||
string signal = customInterfaceElementList[i].Signal;
|
||||
if (uiElements[i] is GUITextBox tb)
|
||||
UpdateSignalProjSpecific(uiElements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSignalProjSpecific(GUIComponent uiElement)
|
||||
{
|
||||
if (uiElement.UserData is not CustomInterfaceElement element) { return; }
|
||||
|
||||
suppressNetworkEvents = true;
|
||||
|
||||
string signal = element.Signal;
|
||||
if (uiElement is GUITextBox tb)
|
||||
{
|
||||
tb.Text = Screen.Selected is { IsEditor: true } ?
|
||||
signal :
|
||||
TextManager.Get(signal).Fallback(signal).Value;
|
||||
}
|
||||
else if (uiElement is GUINumberInput ni)
|
||||
{
|
||||
if (ni.InputType == NumberType.Int)
|
||||
{
|
||||
tb.Text = Screen.Selected is { IsEditor: true } ?
|
||||
signal :
|
||||
TextManager.Get(signal).Fallback(signal).Value;
|
||||
}
|
||||
else if (uiElements[i] is GUINumberInput ni)
|
||||
{
|
||||
if (ni.InputType == NumberType.Int)
|
||||
if (int.TryParse(signal, out int value))
|
||||
{
|
||||
int.TryParse(signal, out int value);
|
||||
ni.IntValue = value;
|
||||
}
|
||||
else if (float.TryParse(signal, out float floatValue))
|
||||
{
|
||||
ni.IntValue = (int)MathF.Round(floatValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (uiElement is GUITickBox tickBox)
|
||||
{
|
||||
tickBox.Selected = signal.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
suppressNetworkEvents = false;
|
||||
}
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
@@ -360,14 +410,9 @@ namespace Barotrauma.Items.Components
|
||||
for (int i = 0; i < customInterfaceElementList.Count; i++)
|
||||
{
|
||||
var element = customInterfaceElementList[i];
|
||||
if (element.HasPropertyName)
|
||||
switch (element.InputType)
|
||||
{
|
||||
if (!element.IsNumberInput)
|
||||
{
|
||||
msg.WriteString(((GUITextBox)uiElements[i]).Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
case CustomInterfaceElement.InputTypeOption.Number:
|
||||
switch (element.NumberType)
|
||||
{
|
||||
case NumberType.Float:
|
||||
@@ -378,59 +423,82 @@ namespace Barotrauma.Items.Components
|
||||
msg.WriteString(((GUINumberInput)uiElements[i]).IntValue.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (element.ContinuousSignal)
|
||||
{
|
||||
msg.WriteBoolean(((GUITickBox)uiElements[i]).Selected);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.WriteBoolean(extraData is Item.ComponentStateEventData { ComponentData: EventData eventData } && eventData.BtnElement == customInterfaceElementList[i]);
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.Text:
|
||||
msg.WriteString(((GUITextBox)uiElements[i]).Text);
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.TickBox:
|
||||
msg.WriteBoolean(((GUITickBox)uiElements[i]).Selected);
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.Button:
|
||||
msg.WriteBoolean(extraData is Item.ComponentStateEventData { ComponentData: EventData eventData } && eventData.BtnElement == customInterfaceElementList[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
readingNetworkEvent = true;
|
||||
int msgStartPos = msg.BitPosition;
|
||||
suppressNetworkEvents = true;
|
||||
try
|
||||
{
|
||||
string[] stringValues = new string[customInterfaceElementList.Count];
|
||||
bool[] boolValues = new bool[customInterfaceElementList.Count];
|
||||
for (int i = 0; i < customInterfaceElementList.Count; i++)
|
||||
{
|
||||
var element = customInterfaceElementList[i];
|
||||
if (element.HasPropertyName)
|
||||
switch (element.InputType)
|
||||
{
|
||||
string newValue = msg.ReadString();
|
||||
if (!element.IsNumberInput)
|
||||
{
|
||||
TextChanged(element, newValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
case CustomInterfaceElement.InputTypeOption.Number:
|
||||
case CustomInterfaceElement.InputTypeOption.Text:
|
||||
stringValues[i] = msg.ReadString();
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.TickBox:
|
||||
case CustomInterfaceElement.InputTypeOption.Button:
|
||||
boolValues[i] = msg.ReadBoolean();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (correctionTimer > 0.0f)
|
||||
{
|
||||
int msgLength = msg.BitPosition - msgStartPos;
|
||||
msg.BitPosition = msgStartPos;
|
||||
StartDelayedCorrection(msg.ExtractBits(msgLength), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < customInterfaceElementList.Count; i++)
|
||||
{
|
||||
var element = customInterfaceElementList[i];
|
||||
switch (element.InputType)
|
||||
{
|
||||
case CustomInterfaceElement.InputTypeOption.Number:
|
||||
switch (element.NumberType)
|
||||
{
|
||||
case NumberType.Int when int.TryParse(newValue, out int value):
|
||||
case NumberType.Int when int.TryParse(stringValues[i], out int value):
|
||||
ValueChanged(element, value);
|
||||
break;
|
||||
case NumberType.Float when TryParseFloatInvariantCulture(newValue, out float value):
|
||||
case NumberType.Float when TryParseFloatInvariantCulture(stringValues[i], out float value):
|
||||
ValueChanged(element, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool elementState = msg.ReadBoolean();
|
||||
if (element.ContinuousSignal)
|
||||
{
|
||||
((GUITickBox)uiElements[i]).Selected = elementState;
|
||||
TickBoxToggled(element, elementState);
|
||||
}
|
||||
else if (elementState)
|
||||
{
|
||||
ButtonClicked(element);
|
||||
}
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.Text:
|
||||
TextChanged(element, stringValues[i]);
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.TickBox:
|
||||
bool tickBoxState = boolValues[i];
|
||||
((GUITickBox)uiElements[i]).Selected = tickBoxState;
|
||||
TickBoxToggled(element, tickBoxState);
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.Button:
|
||||
if (boolValues[i])
|
||||
{
|
||||
ButtonClicked(element);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +506,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
finally
|
||||
{
|
||||
readingNetworkEvent = false;
|
||||
suppressNetworkEvents = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -21,13 +22,16 @@ namespace Barotrauma.Items.Components
|
||||
private GUIListBox historyBox;
|
||||
private GUITextBlock fillerBlock;
|
||||
private GUITextBox inputBox;
|
||||
private GUILayoutGroup layoutGroup;
|
||||
private bool shouldSelectInputBox;
|
||||
|
||||
private readonly List<GUIComponent> inputElements = new List<GUIComponent>();
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
{
|
||||
float marginMultiplier = element.GetAttributeFloat("marginmultiplier", 1.0f);
|
||||
|
||||
var layoutGroup = new GUILayoutGroup(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin.Multiply(marginMultiplier), GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset.Multiply(marginMultiplier) })
|
||||
layoutGroup = new GUILayoutGroup(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin.Multiply(marginMultiplier), GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset.Multiply(marginMultiplier) })
|
||||
{
|
||||
ChildAnchor = Anchor.TopCenter,
|
||||
RelativeSpacing = 0.02f,
|
||||
@@ -39,43 +43,53 @@ namespace Barotrauma.Items.Components
|
||||
AutoHideScrollBar = this.AutoHideScrollbar
|
||||
};
|
||||
|
||||
if (!Readonly)
|
||||
inputElements.Add(CreateFillerBlock());
|
||||
inputElements.Add(new GUIFrame(new RectTransform(new Vector2(0.9f, 0.01f), layoutGroup.RectTransform), style: "HorizontalLine"));
|
||||
|
||||
inputBox = new GUITextBox(new RectTransform(new Vector2(1, .1f), layoutGroup.RectTransform), textColor: TextColor)
|
||||
{
|
||||
CreateFillerBlock();
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(0.9f, 0.01f), layoutGroup.RectTransform), style: "HorizontalLine");
|
||||
|
||||
inputBox = new GUITextBox(new RectTransform(new Vector2(1, .1f), layoutGroup.RectTransform), textColor: TextColor)
|
||||
MaxTextLength = MaxMessageLength,
|
||||
OverflowClip = true,
|
||||
OnEnterPressed = (GUITextBox textBox, string text) =>
|
||||
{
|
||||
MaxTextLength = MaxMessageLength,
|
||||
OverflowClip = true,
|
||||
OnEnterPressed = (GUITextBox textBox, string text) =>
|
||||
if (GameMain.NetworkMember == null)
|
||||
{
|
||||
if (GameMain.NetworkMember == null)
|
||||
{
|
||||
SendOutput(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CreateClientEvent(this, new ClientEventData(text));
|
||||
}
|
||||
textBox.Text = string.Empty;
|
||||
return true;
|
||||
SendOutput(text);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CreateClientEvent(this, new ClientEventData(text));
|
||||
}
|
||||
textBox.Text = string.Empty;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
inputElements.Add(inputBox);
|
||||
RefreshInputElements();
|
||||
}
|
||||
|
||||
layoutGroup.Recalculate();
|
||||
/// <summary>
|
||||
/// Refreshes the visibility of the input box and the layout of the UI depending on whether the terminal is readonly or not.
|
||||
/// </summary>
|
||||
private void RefreshInputElements()
|
||||
{
|
||||
foreach (var inputElement in inputElements)
|
||||
{
|
||||
inputElement.Visible = !_readonly;
|
||||
inputElement.IgnoreLayoutGroups = !inputElement.Visible;
|
||||
}
|
||||
layoutGroup?.Recalculate();
|
||||
}
|
||||
|
||||
// Create fillerBlock to cover historyBox so new values appear at the bottom of historyBox
|
||||
// This could be removed if GUIListBox supported aligning its children
|
||||
public void CreateFillerBlock()
|
||||
public GUIComponent CreateFillerBlock()
|
||||
{
|
||||
fillerBlock = new GUITextBlock(new RectTransform(new Vector2(1, 1), historyBox.Content.RectTransform, anchor: Anchor.TopCenter), string.Empty)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
return fillerBlock;
|
||||
}
|
||||
|
||||
private void SendOutput(string input)
|
||||
|
||||
@@ -21,9 +21,14 @@ namespace Barotrauma.Items.Components
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, range, 32, Color.Cyan * 0.5f, 3);
|
||||
}
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
SharedEventWrite(msg);
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
Channel = msg.ReadRangedInteger(MinChannel, MaxChannel);
|
||||
SharedEventRead(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,13 +106,6 @@ namespace Barotrauma.Items.Components
|
||||
private static int? selectedNodeIndex;
|
||||
private static int? highlightedNodeIndex;
|
||||
|
||||
[Serialize(0.3f, IsPropertySaveable.No)]
|
||||
public float Width
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Vector2 DrawSize
|
||||
{
|
||||
get { return sectionExtents; }
|
||||
@@ -199,6 +192,8 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (Width * wireSprite.size.Y * Screen.Selected.Cam.Zoom < 1.0f) { return; }
|
||||
|
||||
Vector2 drawOffset = GetDrawOffset() + offset;
|
||||
|
||||
float baseDepth = UseSpriteDepth ? item.SpriteDepth : wireSprite.Depth;
|
||||
|
||||
@@ -175,6 +175,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character == null) { return; }
|
||||
|
||||
base.DrawHUD(spriteBatch, character);
|
||||
|
||||
if (OverlayColor.A > 0)
|
||||
{
|
||||
GUIStyle.UIGlow.Draw(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight),
|
||||
@@ -206,44 +208,51 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (ThermalGoggles)
|
||||
{
|
||||
spriteBatch.End();
|
||||
GameMain.LightManager.SolidColorEffect.Parameters["color"].SetValue(Color.Red.ToVector4() * (0.3f + MathF.Sin(thermalEffectState) * 0.05f));
|
||||
GameMain.LightManager.SolidColorEffect.CurrentTechnique = GameMain.LightManager.SolidColorEffect.Techniques["SolidColorBlur"];
|
||||
GameMain.LightManager.SolidColorEffect.Parameters["blurDistance"].SetValue(0.01f + MathF.Sin(thermalEffectState) * 0.005f);
|
||||
GameMain.LightManager.SolidColorEffect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: Screen.Selected.Cam.Transform, effect: GameMain.LightManager.SolidColorEffect);
|
||||
|
||||
Entity refEntity = equipper;
|
||||
if (!isEquippable || refEntity == null)
|
||||
{
|
||||
refEntity = item;
|
||||
}
|
||||
DrawThermalOverlay(spriteBatch, refEntity, character, OverlayColor, Range, thermalEffectState, ShowDeadCharacters);
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c == character || !c.Enabled || c.Removed || c.Params.HideInThermalGoggles) { continue; }
|
||||
if (!ShowDeadCharacters && c.IsDead) { continue; }
|
||||
|
||||
float dist = Vector2.DistanceSquared(refEntity.WorldPosition, c.WorldPosition);
|
||||
if (dist > Range * Range) { continue; }
|
||||
|
||||
Sprite pingCircle = GUIStyle.UIThermalGlow.Value.Sprite;
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (limb.Mass < 0.5f && limb != c.AnimController.MainLimb) { continue; }
|
||||
float noise1 = PerlinNoise.GetPerlin((thermalEffectState + limb.Params.ID + c.ID) * 0.01f, (thermalEffectState + limb.Params.ID + c.ID) * 0.02f);
|
||||
float noise2 = PerlinNoise.GetPerlin((thermalEffectState + limb.Params.ID + c.ID) * 0.01f, (thermalEffectState + limb.Params.ID + c.ID) * 0.008f);
|
||||
Vector2 spriteScale = ConvertUnits.ToDisplayUnits(limb.body.GetSize()) / pingCircle.size * (noise1 * 0.5f + 2f);
|
||||
Vector2 drawPos = new Vector2(limb.body.DrawPosition.X + (noise1 - 0.5f) * 100, -limb.body.DrawPosition.Y + (noise2 - 0.5f) * 100);
|
||||
pingCircle.Draw(spriteBatch, drawPos, 0.0f, scale: Math.Max(spriteScale.X, spriteScale.Y));
|
||||
}
|
||||
}
|
||||
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawThermalOverlay(SpriteBatch spriteBatch, Entity refEntity, Character user, Color overlayColor, float range, float effectState, bool showDeadCharacters)
|
||||
{
|
||||
spriteBatch.End();
|
||||
float colorIntensityBase = 0.5f; //Multiplies the overlay color by this amount, the higher the value, the more bright/vibrant the color.
|
||||
float colorIntensityVariance = 0.05f; //The variance of the pulse effect affecting the color's brightness/vibrance
|
||||
GameMain.LightManager.SolidColorEffect.Parameters["color"].SetValue(overlayColor.ToVector4() * (colorIntensityBase + MathF.Sin(effectState) * colorIntensityVariance));
|
||||
GameMain.LightManager.SolidColorEffect.CurrentTechnique = GameMain.LightManager.SolidColorEffect.Techniques["SolidColorBlur"];
|
||||
GameMain.LightManager.SolidColorEffect.Parameters["blurDistance"].SetValue(0.01f + MathF.Sin(effectState) * 0.005f);
|
||||
GameMain.LightManager.SolidColorEffect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: Screen.Selected.Cam.Transform, effect: GameMain.LightManager.SolidColorEffect);
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c == user || !c.Enabled || c.Removed || c.Params.HideInThermalGoggles) { continue; }
|
||||
if (!showDeadCharacters && c.IsDead) { continue; }
|
||||
|
||||
float dist = Vector2.DistanceSquared(refEntity.WorldPosition, c.WorldPosition);
|
||||
if (dist > range * range) { continue; }
|
||||
|
||||
Sprite pingCircle = GUIStyle.UIThermalGlow.Value.Sprite;
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (limb.Mass < 0.5f && limb != c.AnimController.MainLimb) { continue; }
|
||||
float noise1 = PerlinNoise.GetPerlin((effectState + limb.Params.ID + c.ID) * 0.01f, (effectState + limb.Params.ID + c.ID) * 0.02f);
|
||||
float noise2 = PerlinNoise.GetPerlin((effectState + limb.Params.ID + c.ID) * 0.01f, (effectState + limb.Params.ID + c.ID) * 0.008f);
|
||||
Vector2 spriteScale = ConvertUnits.ToDisplayUnits(limb.body.GetSize()) / pingCircle.size * (noise1 * 0.5f + 2f);
|
||||
Vector2 drawPos = new Vector2(limb.body.DrawPosition.X + (noise1 - 0.5f) * 100, -limb.body.DrawPosition.Y + (noise2 - 0.5f) * 100);
|
||||
pingCircle.Draw(spriteBatch, drawPos, 0.0f, scale: Math.Max(spriteScale.X, spriteScale.Y));
|
||||
}
|
||||
}
|
||||
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
|
||||
}
|
||||
|
||||
private void DrawCharacterInfo(SpriteBatch spriteBatch, Character target, float alpha = 1.0f)
|
||||
{
|
||||
Vector2 hudPos = GameMain.GameScreen.Cam.WorldToScreen(target.DrawPosition);
|
||||
@@ -273,7 +282,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!target.CustomInteractHUDText.IsNullOrEmpty() && target.AllowCustomInteract)
|
||||
if (target.ShouldShowCustomInteractText)
|
||||
{
|
||||
texts.Add(target.CustomInteractHUDText);
|
||||
textColors.Add(GUIStyle.Green);
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class TriggerComponent : ItemComponent, IServerSerializable
|
||||
partial class TriggerComponent : ItemComponent, IServerSerializable, IDrawableComponent
|
||||
{
|
||||
public Vector2 DrawSize =>
|
||||
Vector2.One *
|
||||
(Radius > 0.0f ? Radius * 2 : Math.Max(Width, Height));
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (editing)
|
||||
{
|
||||
PhysicsBody.DebugDraw(spriteBatch, Color.LightGray * 0.7f);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
CurrentForceFluctuation = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,16 +92,17 @@ namespace Barotrauma.Items.Components
|
||||
get
|
||||
{
|
||||
float size = Math.Max(transformedBarrelPos.X, transformedBarrelPos.Y);
|
||||
if (barrelSprite != null)
|
||||
if (railSprite != null && barrelSprite != null)
|
||||
{
|
||||
if (railSprite != null)
|
||||
{
|
||||
size += Math.Max(Math.Max(barrelSprite.size.X, barrelSprite.size.Y), Math.Max(railSprite.size.X, railSprite.size.Y)) * item.Scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
size += Math.Max(barrelSprite.size.X, barrelSprite.size.Y) * item.Scale;
|
||||
}
|
||||
size += Math.Max(Math.Max(barrelSprite.size.X, barrelSprite.size.Y), Math.Max(railSprite.size.X, railSprite.size.Y)) * item.Scale;
|
||||
}
|
||||
else if (railSprite != null)
|
||||
{
|
||||
size += Math.Max(railSprite.size.X, railSprite.size.Y) * item.Scale;
|
||||
}
|
||||
else if (barrelSprite != null)
|
||||
{
|
||||
size += Math.Max(barrelSprite.size.X, barrelSprite.size.Y) * item.Scale;
|
||||
}
|
||||
return Vector2.One * size * 2;
|
||||
}
|
||||
@@ -227,14 +228,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (moveSoundChannel == null && startMoveSound != null)
|
||||
{
|
||||
moveSoundChannel = SoundPlayer.PlaySound(startMoveSound.Sound, item.WorldPosition, startMoveSound.Volume, startMoveSound.Range, ignoreMuffling: startMoveSound.IgnoreMuffling, freqMult: startMoveSound.GetRandomFrequencyMultiplier());
|
||||
moveSoundChannel = SoundPlayer.PlaySound(startMoveSound, item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
}
|
||||
else if (moveSoundChannel == null || !moveSoundChannel.IsPlaying)
|
||||
{
|
||||
if (moveSound != null)
|
||||
{
|
||||
moveSoundChannel?.FadeOutAndDispose();
|
||||
moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, item.WorldPosition, moveSound.Volume, moveSound.Range, ignoreMuffling: moveSound.IgnoreMuffling, freqMult: moveSound.GetRandomFrequencyMultiplier());
|
||||
moveSoundChannel = SoundPlayer.PlaySound(moveSound, item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
if (moveSoundChannel != null) { moveSoundChannel.Looping = true;}
|
||||
}
|
||||
}
|
||||
@@ -246,7 +247,7 @@ namespace Barotrauma.Items.Components
|
||||
if (endMoveSound != null && moveSoundChannel.Sound != endMoveSound.Sound)
|
||||
{
|
||||
moveSoundChannel.FadeOutAndDispose();
|
||||
moveSoundChannel = SoundPlayer.PlaySound(endMoveSound.Sound, item.WorldPosition, endMoveSound.Volume, endMoveSound.Range, ignoreMuffling: endMoveSound.IgnoreMuffling, freqMult: endMoveSound.GetRandomFrequencyMultiplier());
|
||||
moveSoundChannel = SoundPlayer.PlaySound(endMoveSound, item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
if (moveSoundChannel != null) { moveSoundChannel.Looping = false; }
|
||||
}
|
||||
else if (!moveSoundChannel.IsPlaying)
|
||||
@@ -275,7 +276,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (chargeSound != null)
|
||||
{
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling, freqMult: chargeSound.GetRandomFrequencyMultiplier());
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound, item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
if (chargeSoundChannel != null) { chargeSoundChannel.Looping = true; }
|
||||
}
|
||||
}
|
||||
@@ -385,17 +386,20 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.Condition > 0.0f || !HideBarrelWhenBroken)
|
||||
{
|
||||
railSprite?.Draw(spriteBatch,
|
||||
var currentRailSprite = item.Condition <= 0.0f && railSpriteBroken != null ? railSpriteBroken : railSprite;
|
||||
var currentBarrelSprite = item.Condition <= 0.0f && barrelSpriteBroken != null ? barrelSpriteBroken : barrelSprite;
|
||||
|
||||
currentRailSprite?.Draw(spriteBatch,
|
||||
drawPos,
|
||||
overrideColor ?? item.SpriteColor,
|
||||
Rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth));
|
||||
SpriteEffects.None, item.SpriteDepth + (currentRailSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
barrelSprite?.Draw(spriteBatch,
|
||||
currentBarrelSprite?.Draw(spriteBatch,
|
||||
drawPos - GetRecoilOffset() * item.Scale,
|
||||
overrideColor ?? item.SpriteColor,
|
||||
Rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
|
||||
SpriteEffects.None, item.SpriteDepth + (currentBarrelSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
float chargeRatio = currentChargeTime / MaxChargeTime;
|
||||
|
||||
@@ -702,6 +706,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
base.DrawHUD(spriteBatch, character);
|
||||
|
||||
if (HudTint.A > 0)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight),
|
||||
|
||||
@@ -1359,7 +1359,7 @@ namespace Barotrauma
|
||||
if (selectedInventory.GetItemAt(slotIndex)?.OwnInventory?.Container is { } container &&
|
||||
container.Inventory.CanBePut(item))
|
||||
{
|
||||
if (!container.AllowDragAndDrop || !container.AllowAccess)
|
||||
if (!container.AllowDragAndDrop || !container.IsAccessible())
|
||||
{
|
||||
allowCombine = false;
|
||||
}
|
||||
@@ -1603,7 +1603,8 @@ namespace Barotrauma
|
||||
shadowSprite.Draw(spriteBatch,
|
||||
new Rectangle(itemPos.ToPoint() - new Point((iconSize / 2 - shadowPadding.X) * textDir - shadowSize.X * textOffset, iconSize / 2 + shadowPadding.Y), shadowSize), Color.Black * 0.8f);
|
||||
|
||||
GUI.DrawString(spriteBatch, textPos + new Vector2(nameSize.X * textOffset, -iconSize / 2), DraggingItems.First().Name, Color.White);
|
||||
var richString = RichString.Rich(DraggingItems.First().Name);
|
||||
GUI.DrawStringWithColors(spriteBatch, textPos + new Vector2(nameSize.X * textOffset, -iconSize / 2), richString.SanitizedValue, Color.White, richString.RichTextData);
|
||||
GUI.DrawString(spriteBatch, textPos + new Vector2(toolTipSize.X * textOffset, 0), toolTip,
|
||||
color: toolTipColor,
|
||||
font: GUIStyle.SmallFont);
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float GetDrawDepth()
|
||||
public override float GetDrawDepth()
|
||||
{
|
||||
return GetDrawDepth(SpriteDepth + DrawDepthOffset, Sprite);
|
||||
}
|
||||
@@ -287,7 +287,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
int padding = 100;
|
||||
int padding = 0;
|
||||
|
||||
RectangleF boundingBox = GetTransformedQuad().BoundingAxisAlignedRectangle;
|
||||
Vector2 min = new Vector2(-boundingBox.Width / 2 - padding, -boundingBox.Height / 2 - padding);
|
||||
@@ -302,11 +302,11 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (DecorativeSprite decorativeSprite in Prefab.DecorativeSprites)
|
||||
{
|
||||
float scale = decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale;
|
||||
min.X = Math.Min(-decorativeSprite.Sprite.size.X * decorativeSprite.Sprite.RelativeOrigin.X * scale, min.X);
|
||||
min.Y = Math.Min(-decorativeSprite.Sprite.size.Y * (1.0f - decorativeSprite.Sprite.RelativeOrigin.Y) * scale, min.Y);
|
||||
max.X = Math.Max(decorativeSprite.Sprite.size.X * (1.0f - decorativeSprite.Sprite.RelativeOrigin.X) * scale, max.X);
|
||||
max.Y = Math.Max(decorativeSprite.Sprite.size.Y * decorativeSprite.Sprite.RelativeOrigin.Y * scale, max.Y);
|
||||
Vector2 scale = decorativeSprite.GetScale(ref spriteAnimState[decorativeSprite].ScaleState, spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale;
|
||||
min.X = Math.Min(-decorativeSprite.Sprite.size.X * decorativeSprite.Sprite.RelativeOrigin.X * scale.X, min.X);
|
||||
min.Y = Math.Min(-decorativeSprite.Sprite.size.Y * (1.0f - decorativeSprite.Sprite.RelativeOrigin.Y) * scale.Y, min.Y);
|
||||
max.X = Math.Max(decorativeSprite.Sprite.size.X * (1.0f - decorativeSprite.Sprite.RelativeOrigin.X) * scale.X, max.X);
|
||||
max.Y = Math.Max(decorativeSprite.Sprite.size.Y * decorativeSprite.Sprite.RelativeOrigin.Y * scale.Y, max.Y);
|
||||
}
|
||||
cachedVisibleExtents = extents = new Rectangle(min.ToPoint(), max.ToPoint());
|
||||
}
|
||||
@@ -316,6 +316,9 @@ namespace Barotrauma
|
||||
if (worldPosition.X + extents.X > worldView.Right || worldPosition.X + extents.Width < worldView.X) { return false; }
|
||||
if (worldPosition.Y + extents.Height < worldView.Y - worldView.Height || worldPosition.Y + extents.Y > worldView.Y) { return false; }
|
||||
|
||||
if (extents.Width * Screen.Selected.Cam.Zoom < 1.0f) { return false; }
|
||||
if (extents.Height * Screen.Selected.Cam.Zoom < 1.0f) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -405,7 +408,7 @@ namespace Barotrauma
|
||||
fadeInBrokenSprite.Sprite.effects ^= SpriteEffects;
|
||||
}
|
||||
|
||||
if (body == null)
|
||||
if (body == null || body.BodyType == BodyType.Static)
|
||||
{
|
||||
if (Prefab.ResizeHorizontal || Prefab.ResizeVertical)
|
||||
{
|
||||
@@ -490,7 +493,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
var head = holdable.Picker.AnimController.GetLimb(LimbType.Head);
|
||||
if (head != null)
|
||||
if (head?.Sprite != null)
|
||||
{
|
||||
//ensure the holdable item is always drawn in front of the head no matter what the wearables or whatnot do with the sprite depths
|
||||
depth =
|
||||
@@ -523,8 +526,8 @@ namespace Barotrauma
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -RotationRad) * Scale;
|
||||
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
|
||||
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
|
||||
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
|
||||
rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
|
||||
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color, decorativeSprite.Sprite.Origin,
|
||||
rotation, decorativeSprite.GetScale(ref spriteAnimState[decorativeSprite].ScaleState, spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
|
||||
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
|
||||
}
|
||||
}
|
||||
@@ -673,14 +676,8 @@ namespace Barotrauma
|
||||
origin.Y = -origin.Y + decorativeSprite.Sprite.size.Y;
|
||||
spriteEffects |= SpriteEffects.FlipVertically;
|
||||
}
|
||||
if (body != null)
|
||||
{
|
||||
var ca = MathF.Cos(-body.DrawRotation);
|
||||
var sa = MathF.Sin(-body.DrawRotation);
|
||||
offset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
|
||||
}
|
||||
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(drawPos.X + offset.X, -(drawPos.Y + offset.Y)), decorativeSpriteColor, origin,
|
||||
-rotation + spriteRotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffects,
|
||||
-rotation + spriteRotation, decorativeSprite.GetScale(ref spriteAnimState[decorativeSprite].ScaleState, spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffects,
|
||||
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
|
||||
}
|
||||
}
|
||||
@@ -795,6 +792,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var containedItem in ContainedItems)
|
||||
{
|
||||
containedItem.UpdateSpriteStates(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateEditing(Camera cam, float deltaTime)
|
||||
@@ -1069,12 +1071,18 @@ namespace Barotrauma
|
||||
|
||||
foreach (RelatedItem relatedItem in requiredItems)
|
||||
{
|
||||
//TODO: add to localization
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)),
|
||||
relatedItem.Type.ToString() + " required", font: GUIStyle.SmallFont)
|
||||
TextManager.Get($"{relatedItem.Type}.required").Fallback($"{relatedItem.Type} required"), font: GUIStyle.SmallFont)
|
||||
{
|
||||
Padding = new Vector4(10.0f, 0.0f, 10.0f, 0.0f)
|
||||
};
|
||||
|
||||
var tooltip = TextManager.Get($"{relatedItem.Type}.required.tooltip").Fallback(LocalizedString.EmptyString);
|
||||
if (!tooltip.IsNullOrWhiteSpace())
|
||||
{
|
||||
textBlock.ToolTip = tooltip;
|
||||
}
|
||||
|
||||
textBlock.RectTransform.IsFixedSize = true;
|
||||
componentEditor.AddCustomContent(textBlock, 1);
|
||||
|
||||
@@ -1543,10 +1551,19 @@ namespace Barotrauma
|
||||
debugInitialHudPositions.Clear();
|
||||
foreach (ItemComponent ic in activeHUDs)
|
||||
{
|
||||
if (ic.GuiFrame == null || ic.AllowUIOverlap || ic.GetLinkUIToComponent() != null) { continue; }
|
||||
if (!ignoreLocking && ic.LockGuiFramePosition) { continue; }
|
||||
//if the frame covers nearly all of the screen, don't trying to prevent overlaps because it'd fail anyway
|
||||
if (ic.GuiFrame.Rect.Width >= GameMain.GraphicsWidth * 0.9f && ic.GuiFrame.Rect.Height >= GameMain.GraphicsHeight * 0.9f) { continue; }
|
||||
if (ic.GuiFrame == null || ic.GetLinkUIToComponent() != null) { continue; }
|
||||
|
||||
bool nearlyCoversScreen = ic.GuiFrame.Rect.Width >= GameMain.GraphicsWidth * 0.9f &&
|
||||
ic.GuiFrame.Rect.Height >= GameMain.GraphicsHeight * 0.9f;
|
||||
|
||||
// when we are not using overlap prevention, we still need to clamp the frame to the screen area to
|
||||
// prevent frames becoming inaccessible outside the screen for example after a resolution change
|
||||
if (ic.AllowUIOverlap || (!ignoreLocking && ic.LockGuiFramePosition) || nearlyCoversScreen)
|
||||
{
|
||||
ic.GuiFrame.ClampToArea(new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight));
|
||||
continue;
|
||||
}
|
||||
|
||||
ic.GuiFrame.RectTransform.ScreenSpaceOffset = ic.GuiFrameOffset;
|
||||
elementsToMove.Add(ic.GuiFrame);
|
||||
debugInitialHudPositions.Add(ic.GuiFrame.Rect);
|
||||
@@ -1744,7 +1761,7 @@ namespace Barotrauma
|
||||
if (texts.Any() && !recreateHudTexts) { return texts; }
|
||||
texts.Clear();
|
||||
|
||||
string nameText = Name;
|
||||
string nameText = RichString.Rich(Prefab.Name).SanitizedValue;
|
||||
if (Prefab.Tags.Contains("identitycard") || Tags.Contains("despawncontainer"))
|
||||
{
|
||||
string[] readTags = Tags.Split(',');
|
||||
@@ -2074,6 +2091,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.SwapItem:
|
||||
ushort newId = msg.ReadUInt16();
|
||||
uint prefabUintId = msg.ReadUInt32();
|
||||
ItemPrefab newPrefab = ItemPrefab.Prefabs.FirstOrDefault(p => p.UintIdentifier == prefabUintId);
|
||||
if (newPrefab is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error while reading {EventType.SwapItem} message: could not find an item prefab with the hash {prefabUintId}.");
|
||||
break;
|
||||
}
|
||||
ReplaceFromNetwork(newPrefab, newId);
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Malformed incoming item event: unsupported event type {eventType}");
|
||||
}
|
||||
@@ -2171,6 +2199,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
//if the item is outside the level, but not in a sub, it implies the item is inside a sub server-side but the client failed to properly move it
|
||||
// -> let's correct that by finding the correct sub
|
||||
if (Level.IsPositionAboveLevel(WorldPosition) && Submarine == null)
|
||||
{
|
||||
var newSub = Submarine.FindContainingInLocalCoordinates(ConvertUnits.ToDisplayUnits(body.SimPosition), inflate: 0.0f);
|
||||
if (newSub != null)
|
||||
{
|
||||
Submarine = newSub;
|
||||
FindHull();
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition);
|
||||
rect.X = (int)(displayPos.X - rect.Width / 2.0f);
|
||||
rect.Y = (int)(displayPos.Y + rect.Height / 2.0f);
|
||||
@@ -2249,7 +2289,13 @@ namespace Barotrauma
|
||||
if (!components.Contains(ic)) { return; }
|
||||
|
||||
var eventData = new ComponentStateEventData(ic, extraData);
|
||||
if (!ic.ValidateEventData(eventData)) { throw new Exception($"Component event creation failed: {typeof(T).Name}.{nameof(ItemComponent.ValidateEventData)} returned false"); }
|
||||
if (!ic.ValidateEventData(eventData)) {
|
||||
string errorMsg =
|
||||
$"Client-side component event creation for the item \"{Prefab.Identifier}\" failed: {typeof(T).Name}.{nameof(ItemComponent.ValidateEventData)} returned false. " +
|
||||
$"Data: {extraData?.GetType().ToString() ?? "null"}";
|
||||
GameAnalyticsManager.AddErrorEventOnce($"Item.CreateClientEvent:ValidateEventData:{Prefab.Identifier}", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
GameMain.Client.CreateEntityEvent(this, eventData);
|
||||
}
|
||||
|
||||
@@ -2329,15 +2375,15 @@ namespace Barotrauma
|
||||
ownerSheetIndex = (x, y);
|
||||
}
|
||||
|
||||
bool tagsChanged = msg.ReadBoolean();
|
||||
bool tagsChanged = msg.ReadBoolean();
|
||||
string tags = "";
|
||||
if (tagsChanged)
|
||||
{
|
||||
HashSet<Identifier> addedTags = msg.ReadString().Split(',').ToIdentifiers().ToHashSet();
|
||||
HashSet<Identifier> removedTags = msg.ReadString().Split(',').ToIdentifiers().ToHashSet();
|
||||
HashSet<Identifier> addedTags = msg.ReadString().ToIdentifiers().ToHashSet();
|
||||
HashSet<Identifier> removedTags = msg.ReadString().ToIdentifiers().ToHashSet();
|
||||
if (itemPrefab != null)
|
||||
{
|
||||
tags = string.Join(',',itemPrefab.Tags.Where(t => !removedTags.Contains(t)).Concat(addedTags));
|
||||
tags = string.Join(',', itemPrefab.Tags.Where(t => !removedTags.Contains(t)).Union(addedTags));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2455,12 +2501,24 @@ namespace Barotrauma
|
||||
|
||||
if (inventory != null)
|
||||
{
|
||||
if (inventorySlotIndex >= 0 && inventorySlotIndex < 255 &&
|
||||
inventory.TryPutItem(item, inventorySlotIndex, false, false, null, false))
|
||||
if (inventorySlotIndex is >= 0 and < 255 &&
|
||||
!inventory.TryPutItem(item, inventorySlotIndex, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: false, ignoreCondition: true) &&
|
||||
inventory.IsSlotEmpty(inventorySlotIndex))
|
||||
{
|
||||
return item;
|
||||
//If the item won't go nicely, force it to the slot. If the server says the item is in the slot, it should go in the slot.
|
||||
//May happen e.g. when a character is configured to spawn with an item that won't normally go in its inventory slots.
|
||||
inventory.ForceToSlot(item, index: inventorySlotIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
inventory.TryPutItem(item, user: null, allowedSlots: item.AllowedSlots, createNetworkEvent: false);
|
||||
}
|
||||
item.SetTransform(inventory.Owner.SimPosition, 0.0f);
|
||||
item.Submarine = inventory.Owner.Submarine;
|
||||
if (inventory.Owner is Character { Enabled: false } && item.body != null)
|
||||
{
|
||||
item.body.Enabled = false;
|
||||
}
|
||||
inventory.TryPutItem(item, null, item.AllowedSlots, false);
|
||||
}
|
||||
|
||||
return item;
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsVisible(Rectangle worldView)
|
||||
{
|
||||
return Screen.Selected == GameMain.SubEditorScreen || GameMain.DebugDraw;
|
||||
if (Screen.Selected != GameMain.SubEditorScreen && !GameMain.DebugDraw) { return false; }
|
||||
return base.IsVisible(worldView);
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch sb, bool editing, bool back = true)
|
||||
@@ -95,9 +96,13 @@ namespace Barotrauma
|
||||
new Vector2(Math.Sign(targetHull.Rect.Center.X - rect.Center.X), 0.0f)
|
||||
: new Vector2(0.0f, Math.Sign((rect.Y - rect.Height / 2.0f) - (targetHull.Rect.Y - targetHull.Rect.Height / 2.0f)));
|
||||
|
||||
Vector2 arrowPos = new Vector2(WorldRect.Center.X, -(WorldRect.Y - WorldRect.Height / 2));
|
||||
Vector2 arrowPos = new Vector2(WorldRect.Center.X, WorldRect.Y - WorldRect.Height / 2);
|
||||
if (Submarine != null)
|
||||
{
|
||||
arrowPos += (Submarine.DrawPosition - Submarine.Position);
|
||||
}
|
||||
arrowPos.Y = -arrowPos.Y;
|
||||
arrowPos += new Vector2(dir.X * (WorldRect.Width / 2), dir.Y * (WorldRect.Height / 2));
|
||||
|
||||
bool invalidDir = false;
|
||||
if (dir == Vector2.Zero)
|
||||
{
|
||||
|
||||
@@ -81,16 +81,12 @@ namespace Barotrauma
|
||||
public override bool IsVisible(Rectangle worldView)
|
||||
{
|
||||
if (BallastFlora != null) { return true; }
|
||||
|
||||
if (Screen.Selected != GameMain.SubEditorScreen && !GameMain.DebugDraw)
|
||||
{
|
||||
if (decals.Count == 0 && paintAmount < minimumPaintAmountToDraw) { return false; }
|
||||
|
||||
Rectangle worldRect = WorldRect;
|
||||
if (worldRect.X > worldView.Right || worldRect.Right < worldView.X) { return false; }
|
||||
if (worldRect.Y < worldView.Y - worldView.Height || worldRect.Y - worldRect.Height > worldView.Y) { return false; }
|
||||
}
|
||||
return true;
|
||||
return base.IsVisible(worldView);
|
||||
}
|
||||
|
||||
public override bool IsMouseOn(Vector2 position)
|
||||
@@ -103,12 +99,31 @@ namespace Barotrauma
|
||||
|
||||
private GUIComponent CreateEditingHUD(bool inGame = false)
|
||||
{
|
||||
int heightScaled = GUI.IntScale(20);
|
||||
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
|
||||
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null)
|
||||
{
|
||||
CanTakeKeyBoardFocus = false
|
||||
};
|
||||
new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUIStyle.LargeFont);
|
||||
var hullEditor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUIStyle.LargeFont);
|
||||
|
||||
if (!inGame)
|
||||
{
|
||||
if (Linkable)
|
||||
{
|
||||
var linkText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, heightScaled), isFixedSize: true), TextManager.Get("HoldToLink"), font: GUIStyle.SmallFont);
|
||||
var hullLinkText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, heightScaled), isFixedSize: true), TextManager.Get("hulllinkinfo"), font: GUIStyle.SmallFont);
|
||||
var itemsText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, heightScaled), isFixedSize: true), TextManager.Get("AllowedLinks"), font: GUIStyle.SmallFont);
|
||||
LocalizedString allowedItems = AllowedLinks.None() ? TextManager.Get("None") : string.Join(", ", AllowedLinks);
|
||||
itemsText.Text = TextManager.AddPunctuation(':', itemsText.Text, allowedItems);
|
||||
hullEditor.AddCustomContent(linkText, 1);
|
||||
hullEditor.AddCustomContent(hullLinkText, 2);
|
||||
hullEditor.AddCustomContent(itemsText, 3);
|
||||
linkText.TextColor = GUIStyle.Orange;
|
||||
hullLinkText.TextColor = GUIStyle.Orange;
|
||||
itemsText.TextColor = GUIStyle.Orange;
|
||||
}
|
||||
}
|
||||
|
||||
PositionEditingHUD();
|
||||
|
||||
@@ -389,8 +404,8 @@ namespace Barotrauma
|
||||
//GUI.DrawRectangle(spriteBatch, new Rectangle((int)fs.LastExtinguishPos.X, (int)-fs.LastExtinguishPos.Y, 5,5), Color.Yellow, true);
|
||||
}
|
||||
|
||||
|
||||
GUI.DrawLine(spriteBatch, new Vector2(drawRect.X, -WorldSurface), new Vector2(drawRect.Right, -WorldSurface), Color.Cyan * 0.5f);
|
||||
float worldSurface = surface + Submarine.DrawPosition.Y;
|
||||
GUI.DrawLine(spriteBatch, new Vector2(drawRect.X, -worldSurface), new Vector2(drawRect.Right, -worldSurface), Color.Cyan * 0.5f);
|
||||
for (int i = 0; i < waveY.Length - 1; i++)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch,
|
||||
@@ -564,7 +579,6 @@ namespace Barotrauma
|
||||
//bottom right
|
||||
corners[5] = new Vector3(x + width, bottom, 0.0f);
|
||||
|
||||
Vector2[] uvCoords = new Vector2[4];
|
||||
for (int n = 0; n < 4; n++)
|
||||
{
|
||||
uvCoords[n] = Vector2.Transform(new Vector2(corners[n].X, -corners[n].Y), transform);
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Barotrauma
|
||||
|
||||
private Vector3 velocity;
|
||||
|
||||
private float depth;
|
||||
public float Depth { get; private set; }
|
||||
|
||||
private float alpha = 1.0f;
|
||||
|
||||
@@ -42,6 +42,8 @@ namespace Barotrauma
|
||||
|
||||
Vector2 drawPosition;
|
||||
|
||||
private bool flippedHorizontally;
|
||||
|
||||
public Vector2[,] CurrentSpriteDeformation
|
||||
{
|
||||
get;
|
||||
@@ -88,6 +90,8 @@ namespace Barotrauma
|
||||
Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly),
|
||||
Rand.Range(0.0f, prefab.WanderZAmount, Rand.RandSync.ClientOnly));
|
||||
|
||||
Depth = Rand.Range(prefab.MinDepth, prefab.MaxDepth, Rand.RandSync.ClientOnly);
|
||||
|
||||
checkWallsTimer = Rand.Range(0.0f, CheckWallsInterval, Rand.RandSync.ClientOnly);
|
||||
|
||||
foreach (var subElement in prefab.Config.Elements())
|
||||
@@ -104,6 +108,7 @@ namespace Barotrauma
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
int j = 0;
|
||||
foreach (XElement animationElement in subElement.Elements())
|
||||
{
|
||||
SpriteDeformation deformation = null;
|
||||
@@ -118,7 +123,21 @@ namespace Barotrauma
|
||||
deformation = SpriteDeformation.Load(animationElement, prefab.Name);
|
||||
if (deformation != null)
|
||||
{
|
||||
deformation.Params = Prefab.SpriteDeformations[j].Params;
|
||||
uniqueSpriteDeformations.Add(deformation);
|
||||
if (prefab.DeformableSprite != null)
|
||||
{
|
||||
if (deformation.Resolution.X > prefab.DeformableSprite.Subdivisions.X ||
|
||||
deformation.Resolution.Y > prefab.DeformableSprite.Subdivisions.Y)
|
||||
{
|
||||
DebugConsole.AddWarning(
|
||||
$"Potential error in background creature {Prefab.Identifier}: deformation {deformation.GetType()} has a larger resolution ({deformation.Resolution})"+
|
||||
$" than the amount of subdivisions on the deformable sprite ({prefab.DeformableSprite.Subdivisions}). Should the sprite be subdivided further to make full use of the deformation?",
|
||||
contentPackage: Prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (deformation != null)
|
||||
@@ -127,12 +146,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flashTimer = Rand.Range(0.0f, prefab.FlashInterval, Rand.RandSync.Unsynced);
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
position += new Vector2(velocity.X, velocity.Y) * deltaTime;
|
||||
depth = MathHelper.Clamp(depth + velocity.Z * deltaTime, Prefab.MinDepth, Prefab.MaxDepth * 10);
|
||||
Depth = MathHelper.Clamp(Depth + velocity.Z * deltaTime, Prefab.MinDepth, Prefab.MaxDepth);
|
||||
|
||||
if (Prefab.FlashInterval > 0.0f)
|
||||
{
|
||||
@@ -144,7 +165,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
//value goes from 0 to 1 and back to 0 during the flash
|
||||
alpha = (float)Math.Sin(-flashTimer / Prefab.FlashDuration * MathHelper.Pi) * PerlinNoise.GetPerlin((float)Timing.TotalTime * 0.1f, (float)Timing.TotalTime * 0.2f);
|
||||
alpha = (float)Math.Sin(-flashTimer / Prefab.FlashDuration * MathHelper.Pi) * PerlinNoise.GetPerlin((float)Timing.TotalTime, (float)Timing.TotalTime * 0.5f);
|
||||
if (flashTimer < -Prefab.FlashDuration)
|
||||
{
|
||||
flashTimer = Prefab.FlashInterval;
|
||||
@@ -228,7 +249,13 @@ namespace Barotrauma
|
||||
|
||||
velocity = Vector3.Lerp(velocity, new Vector3(Steering.X, Steering.Y, velocity.Z), deltaTime);
|
||||
|
||||
UpdateDeformations(deltaTime);
|
||||
//only flip if there's some horizontal movement speed (10% of the creature's speed)
|
||||
//otherwise a creature swimming roughly up/down can flip around very frequently when the horizontal speed fluctuates around 0
|
||||
if (Math.Abs(velocity.X) > Prefab.Speed * 0.1f)
|
||||
{
|
||||
flippedHorizontally = !Prefab.DisableFlipping && velocity.X < 0.0f;
|
||||
}
|
||||
UpdateDeformations(deltaTime, flippedHorizontally);
|
||||
}
|
||||
|
||||
public void DrawLightSprite(SpriteBatch spriteBatch, Camera cam)
|
||||
@@ -238,12 +265,17 @@ namespace Barotrauma
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
Color color =
|
||||
Prefab.FadeOut ?
|
||||
Color.Lerp(Color.White, Level.Loaded.BackgroundColor, Depth / Prefab.FadeOutDepth) * alpha :
|
||||
Color.White * alpha;
|
||||
|
||||
Draw(spriteBatch,
|
||||
cam,
|
||||
Prefab.Sprite,
|
||||
Prefab.DeformableSprite,
|
||||
CurrentSpriteDeformation,
|
||||
Color.Lerp(Color.White, Level.Loaded.BackgroundColor, depth / Math.Max(MaxDepth, Prefab.MaxDepth)) * alpha);
|
||||
color);
|
||||
}
|
||||
|
||||
private void Draw(SpriteBatch spriteBatch, Camera cam, Sprite sprite, DeformableSprite deformableSprite, Vector2[,] currentSpriteDeformation, Color color)
|
||||
@@ -255,7 +287,7 @@ namespace Barotrauma
|
||||
if (!Prefab.DisableRotation)
|
||||
{
|
||||
rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y));
|
||||
if (velocity.X < 0.0f) { rotation -= MathHelper.Pi; }
|
||||
if (flippedHorizontally) { rotation -= MathHelper.Pi; }
|
||||
}
|
||||
|
||||
drawPosition = GetDrawPosition(cam);
|
||||
@@ -266,8 +298,8 @@ namespace Barotrauma
|
||||
color,
|
||||
rotation,
|
||||
scale,
|
||||
Prefab.DisableFlipping || velocity.X > 0.0f ? SpriteEffects.None : SpriteEffects.FlipHorizontally,
|
||||
Math.Min(depth / MaxDepth, 1.0f));
|
||||
flippedHorizontally ? SpriteEffects.FlipHorizontally : SpriteEffects.None,
|
||||
Math.Min(Depth / MaxDepth, 1.0f));
|
||||
|
||||
if (deformableSprite != null)
|
||||
{
|
||||
@@ -280,29 +312,29 @@ namespace Barotrauma
|
||||
deformableSprite.Reset();
|
||||
}
|
||||
deformableSprite?.Draw(cam,
|
||||
new Vector3(drawPosition.X, drawPosition.Y, Math.Min(depth / 10000.0f, 1.0f)),
|
||||
new Vector3(drawPosition.X, drawPosition.Y, Math.Min(Depth / 10000.0f, 1.0f)),
|
||||
deformableSprite.Origin,
|
||||
rotation,
|
||||
Vector2.One * scale,
|
||||
color,
|
||||
mirror: Prefab.DisableFlipping || velocity.X <= 0.0f);
|
||||
mirror: flippedHorizontally);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 GetDrawPosition(Camera cam)
|
||||
{
|
||||
Vector2 drawPosition = WorldPosition;
|
||||
if (depth >= 0)
|
||||
if (Depth >= 0)
|
||||
{
|
||||
Vector2 camOffset = drawPosition - cam.WorldViewCenter;
|
||||
drawPosition -= camOffset * depth / MaxDepth;
|
||||
drawPosition -= camOffset * Depth / MaxDepth;
|
||||
}
|
||||
return drawPosition;
|
||||
}
|
||||
|
||||
public float GetScale()
|
||||
{
|
||||
return Math.Max(1.0f - depth / MaxDepth, 0.05f) * Prefab.Scale;
|
||||
return Math.Max(1.0f - Depth / MaxDepth, 0.05f) * Prefab.Scale;
|
||||
}
|
||||
|
||||
public Rectangle GetExtents(Camera cam)
|
||||
@@ -328,7 +360,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDeformations(float deltaTime)
|
||||
private void UpdateDeformations(float deltaTime, bool flippedHorizontally)
|
||||
{
|
||||
foreach (SpriteDeformation deformation in uniqueSpriteDeformations)
|
||||
{
|
||||
@@ -336,11 +368,13 @@ namespace Barotrauma
|
||||
}
|
||||
if (spriteDeformations.Count > 0)
|
||||
{
|
||||
CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, Prefab.DeformableSprite.Size);
|
||||
CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, Prefab.DeformableSprite.Size,
|
||||
flippedHorizontally: flippedHorizontally);
|
||||
}
|
||||
if (lightSpriteDeformations.Count > 0)
|
||||
{
|
||||
CurrentLightSpriteDeformation = SpriteDeformation.GetDeformation(lightSpriteDeformations, Prefab.DeformableLightSprite.Size);
|
||||
CurrentLightSpriteDeformation = SpriteDeformation.GetDeformation(lightSpriteDeformations, Prefab.DeformableLightSprite.Size,
|
||||
flippedHorizontally: flippedHorizontally);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,19 @@ namespace Barotrauma
|
||||
|
||||
private float checkVisibleTimer;
|
||||
|
||||
private readonly List<BackgroundCreaturePrefab> prefabs = new List<BackgroundCreaturePrefab>();
|
||||
private readonly List<BackgroundCreature> creatures = new List<BackgroundCreature>();
|
||||
|
||||
public BackgroundCreatureManager(IEnumerable<BackgroundCreaturePrefabsFile> files)
|
||||
private readonly List<BackgroundCreature> visibleCreatures = new List<BackgroundCreature>();
|
||||
|
||||
public BackgroundCreatureManager()
|
||||
{
|
||||
foreach(var file in files)
|
||||
/*foreach(var file in files)
|
||||
{
|
||||
LoadConfig(file.Path);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
public BackgroundCreatureManager(string path)
|
||||
/*public BackgroundCreatureManager(string path)
|
||||
{
|
||||
DebugConsole.AddWarning($"Couldn't find any BackgroundCreaturePrefabs files, falling back to {path}");
|
||||
LoadConfig(ContentPath.FromRaw(null, path));
|
||||
@@ -42,35 +43,34 @@ namespace Barotrauma
|
||||
if (mainElement.IsOverride())
|
||||
{
|
||||
mainElement = mainElement.FirstElement();
|
||||
prefabs.Clear();
|
||||
Prefabs.Clear();
|
||||
DebugConsole.NewMessage($"Overriding all background creatures with '{configPath}'", Color.MediumPurple);
|
||||
}
|
||||
else if (prefabs.Any())
|
||||
else if (Prefabs.Any())
|
||||
{
|
||||
DebugConsole.NewMessage($"Loading additional background creatures from file '{configPath}'");
|
||||
}
|
||||
|
||||
foreach (var element in mainElement.Elements())
|
||||
{
|
||||
prefabs.Add(new BackgroundCreaturePrefab(element));
|
||||
Prefabs.Add(new BackgroundCreaturePrefab(element));
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError(String.Format("Failed to load BackgroundCreatures from {0}", configPath), e);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
public void SpawnCreatures(Level level, int count, Vector2? position = null)
|
||||
{
|
||||
creatures.Clear();
|
||||
|
||||
if (prefabs.Count == 0) { return; }
|
||||
List<BackgroundCreaturePrefab> availablePrefabs = new List<BackgroundCreaturePrefab>(BackgroundCreaturePrefab.Prefabs.OrderBy(p => p.Identifier.Value));
|
||||
if (availablePrefabs.Count == 0) { return; }
|
||||
|
||||
count = Math.Min(count, MaxCreatures);
|
||||
|
||||
List<BackgroundCreaturePrefab> availablePrefabs = new List<BackgroundCreaturePrefab>(prefabs);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Vector2 pos = Vector2.Zero;
|
||||
@@ -93,7 +93,7 @@ namespace Barotrauma
|
||||
pos = (Vector2)position;
|
||||
}
|
||||
|
||||
var prefab = ToolBox.SelectWeightedRandom(availablePrefabs, availablePrefabs.Select(p => p.GetCommonness(level.GenerationParams)).ToList(), Rand.RandSync.ClientOnly);
|
||||
var prefab = ToolBox.SelectWeightedRandom(availablePrefabs, availablePrefabs.Select(p => p.GetCommonness(level?.LevelData)).ToList(), Rand.RandSync.ClientOnly);
|
||||
if (prefab == null) { break; }
|
||||
|
||||
int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax + 1, Rand.RandSync.ClientOnly);
|
||||
@@ -125,16 +125,27 @@ namespace Barotrauma
|
||||
{
|
||||
if (checkVisibleTimer < 0.0f)
|
||||
{
|
||||
visibleCreatures.Clear();
|
||||
int margin = 500;
|
||||
foreach (BackgroundCreature creature in creatures)
|
||||
{
|
||||
Rectangle extents = creature.GetExtents(cam);
|
||||
bool wasVisible = creature.Visible;
|
||||
creature.Visible =
|
||||
extents.Right >= cam.WorldView.X - margin &&
|
||||
extents.X <= cam.WorldView.Right + margin &&
|
||||
extents.Bottom >= cam.WorldView.Y - cam.WorldView.Height - margin &&
|
||||
extents.Y <= cam.WorldView.Y + margin;
|
||||
if (creature.Visible)
|
||||
{
|
||||
//insertion sort according to depth
|
||||
int i = 0;
|
||||
while (i < visibleCreatures.Count)
|
||||
{
|
||||
if (visibleCreatures[i].Depth < creature.Depth) { break; }
|
||||
i++;
|
||||
}
|
||||
visibleCreatures.Insert(i, creature);
|
||||
}
|
||||
}
|
||||
|
||||
checkVisibleTimer = VisibilityCheckInterval;
|
||||
@@ -144,27 +155,24 @@ namespace Barotrauma
|
||||
checkVisibleTimer -= deltaTime;
|
||||
}
|
||||
|
||||
foreach (BackgroundCreature creature in creatures)
|
||||
foreach (BackgroundCreature creature in visibleCreatures)
|
||||
{
|
||||
if (!creature.Visible) { continue; }
|
||||
creature.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
foreach (BackgroundCreature creature in creatures)
|
||||
foreach (BackgroundCreature creature in visibleCreatures)
|
||||
{
|
||||
if (!creature.Visible) { continue; }
|
||||
creature.Draw(spriteBatch, cam);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawLights(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
foreach (BackgroundCreature creature in creatures)
|
||||
foreach (BackgroundCreature creature in visibleCreatures)
|
||||
{
|
||||
if (!creature.Visible) { continue; }
|
||||
creature.DrawLightSprite(spriteBatch, cam);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.SpriteDeformations;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class BackgroundCreaturePrefab
|
||||
class BackgroundCreaturePrefab : Prefab, ISerializableEntity
|
||||
{
|
||||
public readonly Sprite Sprite, LightSprite;
|
||||
public readonly DeformableSprite DeformableSprite, DeformableLightSprite;
|
||||
public readonly static PrefabCollection<BackgroundCreaturePrefab> Prefabs = new PrefabCollection<BackgroundCreaturePrefab>();
|
||||
|
||||
public readonly string Name;
|
||||
public Sprite Sprite { get; private set; }
|
||||
public Sprite LightSprite { get; private set; }
|
||||
public DeformableSprite DeformableSprite { get; private set; }
|
||||
public DeformableSprite DeformableLightSprite { get; private set; }
|
||||
|
||||
private readonly string name;
|
||||
|
||||
public readonly XElement Config;
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
public float Speed { get; private set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f, DecimalCount = 3)]
|
||||
public float WanderAmount { get; private set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 3)]
|
||||
public float WanderZAmount { get; private set; }
|
||||
|
||||
[Serialize(1, IsPropertySaveable.Yes)]
|
||||
[Serialize(1, IsPropertySaveable.Yes), Editable(MinValueInt = 0, MaxValueInt = 1000)]
|
||||
public int SwarmMin { get; private set; }
|
||||
|
||||
[Serialize(1, IsPropertySaveable.Yes)]
|
||||
[Serialize(1, IsPropertySaveable.Yes), Editable(MinValueInt = 0, MaxValueInt = 1000)]
|
||||
public int SwarmMax { get; private set; }
|
||||
|
||||
[Serialize(200.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(200.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)]
|
||||
public float SwarmRadius { get; private set; }
|
||||
|
||||
[Serialize(0.2f, IsPropertySaveable.Yes)]
|
||||
[Serialize(0.2f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
|
||||
public float SwarmCohesion { get; private set; }
|
||||
|
||||
[Serialize(10.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(10.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)]
|
||||
public float MinDepth { get; private set; }
|
||||
|
||||
[Serialize(1000.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(1000.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)]
|
||||
public float MaxDepth { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
[Serialize(10000.0f, IsPropertySaveable.Yes, description: "Creatures fade out to the background color of the level the further they are from the camera. This value is the depth at which the object becomes \"maximally\" faded out."), Editable]
|
||||
public float FadeOutDepth
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
[Serialize(true, IsPropertySaveable.Yes), Editable]
|
||||
public bool FadeOut { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool DisableRotation { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool DisableFlipping { get; private set; }
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
public float Scale { get; private set; }
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
public float Commonness { get; private set; }
|
||||
|
||||
[Serialize(1000, IsPropertySaveable.Yes)]
|
||||
[Serialize(1000, IsPropertySaveable.Yes), Editable(MinValueInt = 0, MaxValueInt = 1000)]
|
||||
public int MaxCount { get; private set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
public float FlashInterval { get; private set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
public float FlashDuration { get; private set; }
|
||||
|
||||
public string Name => name;
|
||||
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only used for editing sprite deformation parameters. The actual LevelObjects use separate SpriteDeformation instances.
|
||||
/// </summary>
|
||||
public List<SpriteDeformation> SpriteDeformations
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new List<SpriteDeformation>();
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the commonness of the object in a specific level type.
|
||||
@@ -67,13 +93,13 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public Dictionary<Identifier, float> OverrideCommonness = new Dictionary<Identifier, float>();
|
||||
|
||||
public BackgroundCreaturePrefab(ContentXElement element)
|
||||
public BackgroundCreaturePrefab(ContentXElement element, BackgroundCreaturePrefabsFile file) : base(file, ParseIdentifier(element))
|
||||
{
|
||||
Name = element.Name.ToString();
|
||||
name = element.Name.ToString();
|
||||
|
||||
Config = element;
|
||||
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
@@ -84,6 +110,14 @@ namespace Barotrauma
|
||||
break;
|
||||
case "deformablesprite":
|
||||
DeformableSprite = new DeformableSprite(subElement, lazyLoad: true);
|
||||
foreach (XElement deformElement in subElement.Elements())
|
||||
{
|
||||
var deformation = SpriteDeformation.Load(deformElement, Name);
|
||||
if (deformation != null)
|
||||
{
|
||||
SpriteDeformations.Add(deformation);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "lightsprite":
|
||||
LightSprite = new Sprite(subElement, lazyLoad: true);
|
||||
@@ -102,17 +136,42 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float GetCommonness(LevelGenerationParams generationParams)
|
||||
public static Identifier ParseIdentifier(XElement element)
|
||||
{
|
||||
if (generationParams != null &&
|
||||
!generationParams.Identifier.IsEmpty &&
|
||||
(OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness) ||
|
||||
(!generationParams.OldIdentifier.IsEmpty && OverrideCommonness.TryGetValue(generationParams.OldIdentifier, out commonness))))
|
||||
Identifier identifier = element.GetAttributeIdentifier("identifier", "");
|
||||
if (identifier.IsEmpty)
|
||||
{
|
||||
identifier = element.NameAsIdentifier();
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public float GetCommonness(LevelData levelData)
|
||||
{
|
||||
if (levelData?.GenerationParams is not { } generationParams || generationParams.Identifier.IsEmpty)
|
||||
{
|
||||
return Commonness;
|
||||
}
|
||||
|
||||
if (OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness) || (!generationParams.OldIdentifier.IsEmpty && OverrideCommonness.TryGetValue(generationParams.OldIdentifier, out commonness)) ||
|
||||
OverrideCommonness.TryGetValue(levelData.Biome.Identifier, out commonness))
|
||||
{
|
||||
return commonness;
|
||||
}
|
||||
return Commonness;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Sprite?.Remove();
|
||||
Sprite = null;
|
||||
LightSprite?.Remove();
|
||||
LightSprite = null;
|
||||
DeformableLightSprite?.Remove();
|
||||
DeformableLightSprite = null;
|
||||
DeformableSprite?.Remove();
|
||||
DeformableSprite = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,26 +9,56 @@ namespace Barotrauma
|
||||
{
|
||||
static partial class CaveGenerator
|
||||
{
|
||||
public static List<VertexPositionTexture> GenerateWallVertices(List<Vector2[]> triangles, LevelGenerationParams generationParams, float zCoord)
|
||||
public static List<VertexPositionColor> GenerateWallVertices(List<Vector2[]> triangles, Color color, float zCoord)
|
||||
{
|
||||
var vertices = new List<VertexPositionTexture>();
|
||||
var vertices = new List<VertexPositionColor>();
|
||||
for (int i = 0; i < triangles.Count; i++)
|
||||
{
|
||||
foreach (Vector2 vertex in triangles[i])
|
||||
{
|
||||
Vector2 uvCoords = vertex / generationParams.WallTextureSize;
|
||||
vertices.Add(new VertexPositionTexture(new Vector3(vertex, zCoord), uvCoords));
|
||||
vertices.Add(new VertexPositionColor(new Vector3(vertex, zCoord), color));
|
||||
}
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
public static List<VertexPositionTexture> GenerateWallEdgeVertices(List<VoronoiCell> cells, Level level, float zCoord)
|
||||
/// <summary>
|
||||
/// Generates texture coordinates for the vertices based on their positions
|
||||
/// </summary>
|
||||
public static VertexPositionColorTexture[] ConvertToTextured(VertexPositionColor[] verts, float textureSize)
|
||||
{
|
||||
float outWardThickness = level.GenerationParams.WallEdgeExpandOutwardsAmount;
|
||||
VertexPositionColorTexture[] texturedVerts = new VertexPositionColorTexture[verts.Length];
|
||||
for (int i = 0; i < verts.Length; i++)
|
||||
{
|
||||
VertexPositionColor vertex = verts[i];
|
||||
texturedVerts[i] = new VertexPositionColorTexture(vertex.Position, vertex.Color, textureCoordinate: Vector2.Zero);
|
||||
}
|
||||
GenerateTextureCoordinates(texturedVerts, textureSize);
|
||||
return texturedVerts;
|
||||
}
|
||||
|
||||
List<VertexPositionTexture> vertices = new List<VertexPositionTexture>();
|
||||
/// <summary>
|
||||
/// Generates texture coordinates for the vertices based on their positions
|
||||
/// </summary>
|
||||
public static void GenerateTextureCoordinates(VertexPositionColorTexture[] verts, float textureSize)
|
||||
{
|
||||
for (int i = 0; i < verts.Length; i++)
|
||||
{
|
||||
VertexPositionColorTexture vertex = verts[i];
|
||||
Vector2 uvCoords = new Vector2(vertex.Position.X, vertex.Position.Y) / textureSize;
|
||||
verts[i] = new VertexPositionColorTexture(verts[i].Position, verts[i].Color, uvCoords);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<VertexPositionColorTexture> GenerateWallEdgeVertices(
|
||||
List<VoronoiCell> cells,
|
||||
float expandOutwards, float expandInwards,
|
||||
Color outerColor, Color innerColor,
|
||||
Level level, float zCoord, bool preventExpandThroughCell = false)
|
||||
{
|
||||
float outWardThickness = expandOutwards;
|
||||
|
||||
List<VertexPositionColorTexture> vertices = new List<VertexPositionColorTexture>();
|
||||
foreach (VoronoiCell cell in cells)
|
||||
{
|
||||
Vector2 minVert = cell.Edges[0].Point1;
|
||||
@@ -49,7 +79,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (!edge.IsSolid) { continue; }
|
||||
|
||||
GraphEdge leftEdge = cell.Edges.Find(e => e != edge && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
|
||||
//the left-side edge on this same cell
|
||||
GraphEdge myLeftEdge = cell.Edges.Find(e => e != edge && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
|
||||
//the left-side edge on either this cell, or the adjacent one if this is attached to another cell
|
||||
GraphEdge leftEdge = myLeftEdge;
|
||||
var leftAdjacentCell = leftEdge?.AdjacentCell(cell);
|
||||
if (leftAdjacentCell != null)
|
||||
{
|
||||
@@ -57,7 +90,10 @@ namespace Barotrauma
|
||||
if (adjEdge != null) { leftEdge = adjEdge; }
|
||||
}
|
||||
|
||||
GraphEdge rightEdge = cell.Edges.Find(e => e != edge && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
|
||||
//the right-side edge on this same cell
|
||||
GraphEdge myRightEdge = cell.Edges.Find(e => e != edge && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
|
||||
//the right-side edge on either this cell, or the adjacent one if this is attached to another cell
|
||||
GraphEdge rightEdge = myRightEdge;
|
||||
var rightAdjacentCell = rightEdge?.AdjacentCell(cell);
|
||||
if (rightAdjacentCell != null)
|
||||
{
|
||||
@@ -67,18 +103,25 @@ namespace Barotrauma
|
||||
|
||||
Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero;
|
||||
|
||||
float inwardThickness1 = level.GenerationParams.WallEdgeExpandInwardsAmount;
|
||||
float inwardThickness2 = level.GenerationParams.WallEdgeExpandInwardsAmount;
|
||||
float inwardThickness1 = Math.Min(expandInwards, edge.Length);
|
||||
float inwardThickness2 = inwardThickness1;
|
||||
if (leftEdge != null && !leftEdge.IsSolid)
|
||||
{
|
||||
//the left-side edge is non-solid (an edge between two cells, not an actual solid wall edge)
|
||||
// -> expand in the direction of that edge
|
||||
leftNormal = edge.Point1.NearlyEquals(leftEdge.Point1) ?
|
||||
Vector2.Normalize(leftEdge.Point2 - leftEdge.Point1) :
|
||||
Vector2.Normalize(leftEdge.Point1 - leftEdge.Point2);
|
||||
//maximum expansion is half of the size of the edge (otherwise the expansions from different sides of the edge could overlap or even extend "through" the cell)
|
||||
inwardThickness1 = Math.Min(inwardThickness1, leftEdge.Length / 2);
|
||||
}
|
||||
else if (leftEdge != null)
|
||||
{
|
||||
//use the average of this edge's and the adjacent edge's normals
|
||||
leftNormal = -Vector2.Normalize(edge.GetNormal(cell) + leftEdge.GetNormal(leftAdjacentCell ?? cell));
|
||||
if (!MathUtils.IsValid(leftNormal)) { leftNormal = -edge.GetNormal(cell); }
|
||||
//maximum expansion is the length of the adjacent edge (more expansion causes the textures to distort)
|
||||
inwardThickness1 = Math.Min(Math.Min(inwardThickness1, leftEdge.Length), myLeftEdge.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -109,11 +152,13 @@ namespace Barotrauma
|
||||
rightNormal = edge.Point2.NearlyEquals(rightEdge.Point1) ?
|
||||
Vector2.Normalize(rightEdge.Point2 - rightEdge.Point1) :
|
||||
Vector2.Normalize(rightEdge.Point1 - rightEdge.Point2);
|
||||
inwardThickness2 = Math.Min(inwardThickness2, rightEdge.Length / 2);
|
||||
}
|
||||
else if (rightEdge != null)
|
||||
{
|
||||
rightNormal = -Vector2.Normalize(edge.GetNormal(cell) + rightEdge.GetNormal(rightAdjacentCell ?? cell));
|
||||
if (!MathUtils.IsValid(rightNormal)) { rightNormal = -edge.GetNormal(cell); }
|
||||
inwardThickness2 = Math.Min(Math.Min(inwardThickness2, rightEdge.Length), myRightEdge.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -150,10 +195,51 @@ namespace Barotrauma
|
||||
point1UV = point1UV / MathHelper.TwoPi * textureRepeatCount;
|
||||
point2UV = point2UV / MathHelper.TwoPi * textureRepeatCount;
|
||||
|
||||
//if calculating the UVs based on polar coordinates would result in stretching (using less than 10% of the texture for a wall the size of the texture)
|
||||
//just calculate the UVs based on the length of the wall
|
||||
//(this will mean the textures don't align at point2, but it doesn't seem that noticeable)
|
||||
if ((point2UV - point1UV) * level.GenerationParams.WallEdgeTextureWidth < edge.Length * 0.1f)
|
||||
{
|
||||
point2UV = point1UV + edge.Length / 2 / level.GenerationParams.WallEdgeTextureWidth;
|
||||
}
|
||||
|
||||
//"extruding" inwards, need to make sure we don't make the edge poke through the cell from the other side
|
||||
if (preventExpandThroughCell)
|
||||
{
|
||||
foreach (GraphEdge otherEdge in cell.Edges)
|
||||
{
|
||||
if (otherEdge == edge || Vector2.Dot(otherEdge.GetNormal(cell), edge.GetNormal(cell)) > 0) { continue; }
|
||||
if (otherEdge != leftEdge)
|
||||
{
|
||||
inwardThickness1 = ClampThickness(otherEdge, edge.Point1, leftNormal, inwardThickness1);
|
||||
}
|
||||
if (otherEdge != rightEdge)
|
||||
{
|
||||
inwardThickness2 = ClampThickness(otherEdge, edge.Point2, rightNormal, inwardThickness2);
|
||||
}
|
||||
}
|
||||
|
||||
static float ClampThickness(GraphEdge otherEdge, Vector2 thisPoint, Vector2 thisEdgeNormal, float currThickness)
|
||||
{
|
||||
if (MathUtils.GetLineIntersection(
|
||||
thisPoint, thisPoint + thisEdgeNormal * currThickness,
|
||||
otherEdge.Point1, otherEdge.Point2, areLinesInfinite: false, out Vector2 intersection1))
|
||||
{
|
||||
return Math.Min(currThickness, Vector2.Distance(thisPoint, intersection1));
|
||||
}
|
||||
return currThickness;
|
||||
}
|
||||
}
|
||||
|
||||
//there needs to be some minimum amount of inward thickness,
|
||||
//if the edge texture doesn't extend inside at all you can see through between the edge texture and the solid part of the cell
|
||||
inwardThickness1 = Math.Max(inwardThickness1, Math.Min(100.0f, expandInwards));
|
||||
inwardThickness2 = Math.Max(inwardThickness2, Math.Min(100.0f, expandInwards));
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Vector2[] verts = new Vector2[3];
|
||||
VertexPositionTexture[] vertPos = new VertexPositionTexture[3];
|
||||
VertexPositionColorTexture[] vertPos = new VertexPositionColorTexture[3];
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
@@ -161,9 +247,9 @@ namespace Barotrauma
|
||||
verts[1] = edge.Point2 - rightNormal * outWardThickness;
|
||||
verts[2] = edge.Point1 + leftNormal * inwardThickness1;
|
||||
|
||||
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 0.0f));
|
||||
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
|
||||
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point1UV, 1.0f));
|
||||
vertPos[0] = new VertexPositionColorTexture(new Vector3(verts[0], zCoord), outerColor, new Vector2(point1UV, 0.0f));
|
||||
vertPos[1] = new VertexPositionColorTexture(new Vector3(verts[1], zCoord), outerColor, new Vector2(point2UV, 0.0f));
|
||||
vertPos[2] = new VertexPositionColorTexture(new Vector3(verts[2], zCoord), innerColor, new Vector2(point1UV, 1.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -172,9 +258,9 @@ namespace Barotrauma
|
||||
verts[1] = edge.Point2 - rightNormal * outWardThickness;
|
||||
verts[2] = edge.Point2 + rightNormal * inwardThickness2;
|
||||
|
||||
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 1.0f));
|
||||
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
|
||||
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point2UV, 1.0f));
|
||||
vertPos[0] = new VertexPositionColorTexture(new Vector3(verts[0], zCoord), innerColor, new Vector2(point1UV, 1.0f));
|
||||
vertPos[1] = new VertexPositionColorTexture(new Vector3(verts[1], zCoord), outerColor, new Vector2(point2UV, 0.0f));
|
||||
vertPos[2] = new VertexPositionColorTexture(new Vector3(verts[2], zCoord), innerColor, new Vector2(point2UV, 1.0f));
|
||||
}
|
||||
vertices.AddRange(vertPos);
|
||||
}
|
||||
|
||||
@@ -47,62 +47,75 @@ namespace Barotrauma
|
||||
if (renderer == null) { return; }
|
||||
renderer.DrawDebugOverlay(spriteBatch, cam);
|
||||
|
||||
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
foreach (InterestingPosition pos in PositionsOfInterest)
|
||||
if (Screen.Selected.Cam.Zoom > 0.1f)
|
||||
{
|
||||
Color color = Color.Yellow;
|
||||
if (pos.PositionType == PositionType.Cave || pos.PositionType == PositionType.AbyssCave)
|
||||
foreach (InterestingPosition pos in PositionsOfInterest)
|
||||
{
|
||||
color = Color.DarkOrange;
|
||||
}
|
||||
else if (pos.PositionType == PositionType.Ruin)
|
||||
{
|
||||
color = Color.LightGray;
|
||||
}
|
||||
if (!pos.IsValid)
|
||||
{
|
||||
color = Color.Red;
|
||||
Color color = Color.Yellow;
|
||||
if (pos.PositionType == PositionType.Cave || pos.PositionType == PositionType.AbyssCave)
|
||||
{
|
||||
color = Color.DarkOrange;
|
||||
}
|
||||
else if (pos.PositionType == PositionType.Ruin)
|
||||
{
|
||||
color = Color.LightGray;
|
||||
}
|
||||
if (!pos.IsValid)
|
||||
{
|
||||
color = Color.Red;
|
||||
}
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(pos.Position.X - 15.0f, -pos.Position.Y - 15.0f), new Vector2(30.0f, 30.0f), color, true);
|
||||
}
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(pos.Position.X - 15.0f, -pos.Position.Y - 15.0f), new Vector2(30.0f, 30.0f), color, true);
|
||||
foreach (RuinGeneration.Ruin ruin in Ruins)
|
||||
{
|
||||
Rectangle ruinArea = ruin.Area;
|
||||
ruinArea.Y = -ruinArea.Y - ruinArea.Height;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, ruinArea, Color.DarkSlateBlue, false, 0, 5);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (RuinGeneration.Ruin ruin in Ruins)
|
||||
{
|
||||
Rectangle ruinArea = ruin.Area;
|
||||
ruinArea.Y = -ruinArea.Y - ruinArea.Height;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, ruinArea, Color.DarkSlateBlue, false, 0, 5);
|
||||
}
|
||||
|
||||
foreach (var positions in wreckPositions.Values)
|
||||
float zoomFactor = MathHelper.Lerp(20, 1, MathUtils.InverseLerp(Screen.Selected.Cam.MinZoom, Screen.Selected.Cam.DefaultZoom, Screen.Selected.Cam.Zoom));
|
||||
foreach ((string debugInfo, List<Vector2> positions) in positionHistory)
|
||||
{
|
||||
for (int i = 0; i < positions.Count; i++)
|
||||
{
|
||||
float t = (i + 1) / (float)positions.Count;
|
||||
float multiplier = MathHelper.Lerp(0, 1, t);
|
||||
float multiplier = MathHelper.Lerp(0.1f, 1, t);
|
||||
Color color = Color.Red * multiplier;
|
||||
var pos = positions[i];
|
||||
pos.Y = -pos.Y;
|
||||
var size = new Vector2(100);
|
||||
GUI.DrawRectangle(spriteBatch, pos - size / 2, size, color, thickness: 10);
|
||||
var size = new Vector2(200);
|
||||
if (i == 0)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, pos - size, size * 2, Color.Red, thickness: 2 * zoomFactor);
|
||||
GUI.DrawString(spriteBatch, pos - new Vector2(10, 20), debugInfo, Color.White, font: GUIStyle.LargeFont, forceUpperCase: ForceUpperCase.Yes);
|
||||
}
|
||||
if (i < positions.Count - 1)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, pos - size / 2, size, Color.Red, isFilled: true);
|
||||
}
|
||||
var nextPos = positions[i + 1];
|
||||
nextPos.Y = -nextPos.Y;
|
||||
GUI.DrawLine(spriteBatch, pos, nextPos, color, width: 10);
|
||||
GUI.DrawLine(spriteBatch, pos, nextPos, color, width: 4 * zoomFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var rects in blockedRects.Values)
|
||||
foreach ((Submarine sub, List<Rectangle> rects) in blockedRects)
|
||||
{
|
||||
foreach (var rect in rects)
|
||||
foreach (Rectangle t in rects)
|
||||
{
|
||||
Rectangle newRect = rect;
|
||||
Rectangle newRect = t;
|
||||
newRect.Y = -newRect.Y;
|
||||
GUI.DrawRectangle(spriteBatch, newRect, Color.Red, thickness: 5);
|
||||
GUI.DrawRectangle(spriteBatch, newRect, Color.Red * 0.1f, isFilled: true);
|
||||
GUI.DrawString(spriteBatch, newRect.Center.ToVector2(), $"{sub.Info.Name}", Color.White, font: GUIStyle.LargeFont, forceUpperCase: ForceUpperCase.Yes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace Barotrauma
|
||||
Prefab.OverrideProperties.Any(p => p != null && (p.Sprites.Any() || p.DeformableSprite != null));
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
public void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
CurrentRotation = Rotation;
|
||||
if (ActivePrefab.SwingFrequency > 0.0f)
|
||||
@@ -199,11 +199,15 @@ namespace Barotrauma
|
||||
|
||||
if (LightSources != null)
|
||||
{
|
||||
Vector2 position2D = new Vector2(Position.X, Position.Y);
|
||||
Vector2 camDiff = position2D - cam.WorldViewCenter;
|
||||
for (int i = 0; i < LightSources.Length; i++)
|
||||
{
|
||||
if (LightSourceTriggers[i] != null) LightSources[i].Enabled = LightSourceTriggers[i].IsTriggered;
|
||||
if (LightSourceTriggers[i] != null) { LightSources[i].Enabled = LightSourceTriggers[i].IsTriggered; }
|
||||
LightSources[i].Rotation = -CurrentRotation;
|
||||
LightSources[i].SpriteScale = CurrentScale;
|
||||
LightSources[i].Position =
|
||||
position2D - camDiff * Position.Z * LevelObjectManager.ParallaxStrength;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +241,10 @@ namespace Barotrauma
|
||||
{
|
||||
SoundChannels[i] = roundSound.Sound.Play(roundSound.Volume, roundSound.Range, roundSound.GetRandomFrequencyMultiplier(), soundPos);
|
||||
}
|
||||
SoundChannels[i].Position = new Vector3(soundPos.X, soundPos.Y, 0.0f);
|
||||
if (SoundChannels[i] != null)
|
||||
{
|
||||
SoundChannels[i].Position = new Vector3(soundPos.X, soundPos.Y, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (SoundChannels[i] != null && SoundChannels[i].IsPlaying)
|
||||
@@ -259,7 +266,7 @@ namespace Barotrauma
|
||||
}
|
||||
deformation.Update(deltaTime);
|
||||
}
|
||||
CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, ActivePrefab.DeformableSprite.Size);
|
||||
CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, ActivePrefab.DeformableSprite.Size, flippedHorizontally: false);
|
||||
if (LightSources != null)
|
||||
{
|
||||
foreach (LightSource lightSource in LightSources)
|
||||
|
||||
@@ -10,9 +10,11 @@ namespace Barotrauma
|
||||
{
|
||||
partial class LevelObjectManager
|
||||
{
|
||||
private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>();
|
||||
private readonly List<LevelObject> visibleObjectsMid = new List<LevelObject>();
|
||||
private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>();
|
||||
// Pre-initialized to the max size, so that we don't have to resize the lists at runtime. TODO: Could the capacity (of some collections?) be lower?
|
||||
private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>(MaxVisibleObjects);
|
||||
private readonly List<LevelObject> visibleObjectsMid = new List<LevelObject>(MaxVisibleObjects);
|
||||
private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>(MaxVisibleObjects);
|
||||
private readonly HashSet<LevelObject> allVisibleObjects = new HashSet<LevelObject>(MaxVisibleObjects);
|
||||
|
||||
private double NextRefreshTime;
|
||||
|
||||
@@ -31,25 +33,41 @@ namespace Barotrauma
|
||||
visibleObjectsFront.Clear();
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
partial void UpdateProjSpecific(float deltaTime, Camera cam)
|
||||
{
|
||||
foreach (LevelObject obj in visibleObjectsBack)
|
||||
{
|
||||
obj.Update(deltaTime);
|
||||
obj.Update(deltaTime, cam);
|
||||
}
|
||||
foreach (LevelObject obj in visibleObjectsMid)
|
||||
{
|
||||
obj.Update(deltaTime);
|
||||
obj.Update(deltaTime, cam);
|
||||
}
|
||||
foreach (LevelObject obj in visibleObjectsFront)
|
||||
{
|
||||
obj.Update(deltaTime);
|
||||
obj.Update(deltaTime, cam);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<LevelObject> GetVisibleObjects()
|
||||
/// <summary>
|
||||
/// Returns all visible objects, but not in order, because internally uses a HashSet.
|
||||
/// </summary>
|
||||
public IEnumerable<LevelObject> GetAllVisibleObjects()
|
||||
{
|
||||
return visibleObjectsBack.Union(visibleObjectsMid).Union(visibleObjectsFront);
|
||||
allVisibleObjects.Clear();
|
||||
foreach (LevelObject obj in visibleObjectsBack)
|
||||
{
|
||||
allVisibleObjects.Add(obj);
|
||||
}
|
||||
foreach (LevelObject obj in visibleObjectsMid)
|
||||
{
|
||||
allVisibleObjects.Add(obj);
|
||||
}
|
||||
foreach (LevelObject obj in visibleObjectsFront)
|
||||
{
|
||||
allVisibleObjects.Add(obj);
|
||||
}
|
||||
return allVisibleObjects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -207,7 +225,7 @@ namespace Barotrauma
|
||||
activeSprite?.Draw(
|
||||
spriteBatch,
|
||||
new Vector2(obj.Position.X, -obj.Position.Y) - camDiff * obj.Position.Z * ParallaxStrength,
|
||||
Color.Lerp(obj.Prefab.SpriteColor, obj.Prefab.SpriteColor.Multiply(Level.Loaded.BackgroundTextureColor), obj.Position.Z / 3000.0f),
|
||||
Color.Lerp(obj.Prefab.SpriteColor, obj.Prefab.SpriteColor.Multiply(Level.Loaded.BackgroundTextureColor), obj.Position.Z / obj.Prefab.FadeOutDepth),
|
||||
activeSprite.Origin,
|
||||
obj.CurrentRotation,
|
||||
obj.CurrentScale,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user