v1.7.7.0 (Winter Update 2024)
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,7 +42,7 @@ 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);
|
||||
|
||||
@@ -11,7 +11,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;
|
||||
|
||||
@@ -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,28 +130,31 @@ 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;
|
||||
MainLimb.body.LinearVelocity = newVelocity;
|
||||
float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, newPosition);
|
||||
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)
|
||||
{
|
||||
MainLimb.PullJointWorldAnchorB = newPosition;
|
||||
MainLimb.PullJointEnabled = true;
|
||||
if (!ColliderControlsMovement && newVelocity.LengthSquared() < 0.01f) { TryPlatformCorrection(newPosition); }
|
||||
}
|
||||
else
|
||||
{
|
||||
MainLimb.body.LinearVelocity = newVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,9 +186,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (character.MemState.Count < 1) return;
|
||||
if (character.MemState.Count < 1) { return; }
|
||||
|
||||
overrideTargetMovement = Vector2.Zero;
|
||||
overrideTargetMovement = null;
|
||||
|
||||
CharacterStateInfo serverPos = character.MemState.Last();
|
||||
|
||||
@@ -294,6 +301,38 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace Barotrauma
|
||||
public Vector2 Position;
|
||||
public Vector2 DrawPosition;
|
||||
public float MoveUpAmount;
|
||||
public readonly string Text;
|
||||
public readonly RichString Text;
|
||||
public readonly Character Character;
|
||||
public readonly Submarine Submarine;
|
||||
public readonly Vector2 TextSize;
|
||||
@@ -260,7 +260,7 @@ namespace Barotrauma
|
||||
|
||||
public SpeechBubble(Character character, float lifeTime, Color color, string text = "")
|
||||
{
|
||||
Text = ToolBox.WrapText(text, GUI.IntScale(300), GUIStyle.SmallFont.GetFontForStr(text));
|
||||
Text = RichString.Rich(ToolBox.WrapText(text, GUI.IntScale(300), GUIStyle.SmallFont.GetFontForStr(text)));
|
||||
TextSize = GUIStyle.SmallFont.MeasureString(Text);
|
||||
|
||||
Character = character;
|
||||
@@ -322,7 +322,6 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
|
||||
{
|
||||
|
||||
if (DisableControls || GUI.InputBlockingMenuOpen)
|
||||
{
|
||||
foreach (Key key in keys)
|
||||
@@ -417,6 +416,11 @@ namespace Barotrauma
|
||||
|
||||
UpdateLocalCursor(cam);
|
||||
|
||||
if (IsKeyHit(InputType.ToggleRun))
|
||||
{
|
||||
ToggleRun = !ToggleRun;
|
||||
}
|
||||
|
||||
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
|
||||
if (GUI.PauseMenuOpen)
|
||||
{
|
||||
@@ -1189,7 +1193,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.Text.RichTextData, font: GUIStyle.SmallFont);
|
||||
}
|
||||
spriteBatch.End();
|
||||
}
|
||||
@@ -1435,7 +1439,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;
|
||||
@@ -564,7 +562,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 +590,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,7 +771,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 +782,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,14 +801,14 @@ 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)
|
||||
@@ -819,10 +817,10 @@ namespace Barotrauma
|
||||
}
|
||||
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 +848,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 +860,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())
|
||||
{
|
||||
|
||||
@@ -526,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)
|
||||
|
||||
@@ -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();
|
||||
@@ -262,6 +263,11 @@ 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;
|
||||
@@ -273,6 +279,8 @@ namespace Barotrauma
|
||||
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
|
||||
}
|
||||
|
||||
bool ignorePlatforms = msg.ReadBoolean();
|
||||
|
||||
bool readStatus = msg.ReadBoolean();
|
||||
if (readStatus)
|
||||
{
|
||||
@@ -294,7 +302,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;
|
||||
@@ -316,7 +324,7 @@ namespace Barotrauma
|
||||
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++;
|
||||
@@ -328,7 +336,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++;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -1401,7 +1401,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())
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -483,9 +501,20 @@ namespace Barotrauma
|
||||
{
|
||||
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?.Root?.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();
|
||||
@@ -495,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);
|
||||
}
|
||||
@@ -753,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);
|
||||
|
||||
@@ -887,28 +936,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)
|
||||
@@ -967,7 +1016,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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -684,7 +684,7 @@ namespace Barotrauma
|
||||
if (args.Length > 1)
|
||||
{
|
||||
string forceThalamusArg = args[1];
|
||||
if (Enum.TryParse(forceThalamusArg, out LevelData.ThalamusSpawn result))
|
||||
if (Enum.TryParse(forceThalamusArg, ignoreCase: true, out LevelData.ThalamusSpawn result))
|
||||
{
|
||||
NewMessage($"Setting ThalamusSpawn to: {result}", color: Color.Yellow);
|
||||
LevelData.ForceThalamus = result;
|
||||
@@ -2530,6 +2530,18 @@ namespace Barotrauma
|
||||
}));
|
||||
|
||||
#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);
|
||||
|
||||
@@ -36,7 +36,8 @@ namespace Barotrauma
|
||||
{
|
||||
return
|
||||
lastActiveAction != null &&
|
||||
lastActiveAction.ParentEvent != ParentEvent &&
|
||||
!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 &&
|
||||
|
||||
@@ -61,6 +61,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>();
|
||||
|
||||
@@ -49,7 +49,8 @@ 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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -419,8 +419,7 @@ namespace Barotrauma
|
||||
if (anyChanges) { textures[^1].SetData<uint>(currentDynamicPixelBuffer); }
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -246,6 +246,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)
|
||||
{
|
||||
|
||||
@@ -1562,7 +1562,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,
|
||||
|
||||
@@ -902,7 +902,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return teamIDs.IndexOf(client.TeamID);
|
||||
}
|
||||
|
||||
private void CreateWalletCrewFrame(Character character, GUILayoutGroup paddedFrame)
|
||||
@@ -1694,6 +1694,7 @@ namespace Barotrauma
|
||||
textContent,
|
||||
mission.Difficulty ?? 0,
|
||||
mission.Prefab.Icon, mission.Prefab.IconColor,
|
||||
mission.GetDifficultyToolTipText(),
|
||||
out GUIImage missionIcon);
|
||||
if (missionIcon != null)
|
||||
{
|
||||
|
||||
@@ -299,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);
|
||||
@@ -324,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)),
|
||||
@@ -440,7 +441,12 @@ namespace Barotrauma
|
||||
|
||||
private void CreateTalentResetPopup(GUIComponent parent)
|
||||
{
|
||||
bool hasResetTalentsBefore = character?.Info.TalentResetCount > 0;
|
||||
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
|
||||
@@ -455,7 +461,8 @@ namespace Barotrauma
|
||||
|
||||
if (hasResetTalentsBefore)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), popupLayout.RectTransform), TextManager.Get("talentresetpromptwarning"), wrap: true)
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), popupLayout.RectTransform),
|
||||
TextManager.GetWithVariable("talentresetpromptwarning", "[count]", talentResetCount.ToString()), wrap: true)
|
||||
{
|
||||
TextColor = GUIStyle.Red
|
||||
};
|
||||
|
||||
@@ -313,6 +313,8 @@ namespace Barotrauma
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
}
|
||||
|
||||
int display = GameSettings.CurrentConfig.Graphics.Display;
|
||||
|
||||
GraphicsWidth = GameSettings.CurrentConfig.Graphics.Width;
|
||||
GraphicsHeight = GameSettings.CurrentConfig.Graphics.Height;
|
||||
|
||||
@@ -340,7 +342,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 +355,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;
|
||||
|
||||
@@ -1345,6 +1345,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;
|
||||
@@ -1373,7 +1374,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;
|
||||
}
|
||||
@@ -1668,7 +1669,7 @@ namespace Barotrauma
|
||||
{
|
||||
crewArea.Visible = characters.Count > 0 && CharacterHealth.OpenHealthWindow == null;
|
||||
|
||||
var myTeam = Character.Controlled?.TeamID ?? GameMain.Client?.MyClient?.TeamID;
|
||||
CharacterTeamType myTeam = Character.Controlled?.TeamID ?? GameMain.Client?.MyClient?.TeamID ?? CharacterTeamType.Team1;
|
||||
if (GameMain.GameSession?.GameMode is PvPMode)
|
||||
{
|
||||
var team1Text = crewArea.GetChildByUserData(CharacterTeamType.Team1);
|
||||
@@ -1690,7 +1691,7 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
|
||||
characterComponent.Visible = Character.Controlled == null || myTeam == 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))
|
||||
{
|
||||
@@ -2055,7 +2056,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;
|
||||
@@ -2670,9 +2671,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)
|
||||
{
|
||||
@@ -3045,7 +3046,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);
|
||||
|
||||
@@ -251,6 +251,7 @@ namespace Barotrauma
|
||||
textContent,
|
||||
mission.Difficulty ?? 0,
|
||||
mission.Prefab.Icon, mission.Prefab.IconColor,
|
||||
mission.GetDifficultyToolTipText(),
|
||||
out GUIImage missionIcon);
|
||||
|
||||
if (selectedMissions.Contains(mission))
|
||||
@@ -442,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);
|
||||
|
||||
@@ -499,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);
|
||||
@@ -507,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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -760,7 +764,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); }
|
||||
|
||||
@@ -372,17 +372,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1978,25 +1978,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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Barotrauma.Items.Components
|
||||
private readonly List<GUIComponent> uiElements = new List<GUIComponent>();
|
||||
private GUILayoutGroup uiElementContainer;
|
||||
|
||||
private bool readingNetworkEvent;
|
||||
private bool suppressNetworkEvents;
|
||||
|
||||
private GUIComponent insufficientPowerWarning;
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
CreateClientEventWithCorrectionDelay();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -100,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();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -126,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();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -162,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;
|
||||
};
|
||||
@@ -191,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;
|
||||
@@ -215,6 +208,12 @@ namespace Barotrauma.Items.Components
|
||||
Visible = false
|
||||
};
|
||||
}
|
||||
|
||||
void CreateClientEventWithCorrectionDelay()
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
correctionTimer = CorrectionDelay;
|
||||
}
|
||||
}
|
||||
|
||||
public override void CreateEditingHUD(SerializableEntityEditor editor)
|
||||
@@ -268,7 +267,7 @@ namespace Barotrauma.Items.Components
|
||||
if (visible)
|
||||
{
|
||||
visibleElementCount++;
|
||||
if (element.GetValueInterval > 0.0f)
|
||||
if (element.GetValueInterval > 0.0f && correctionTimer <= 0.0f)
|
||||
{
|
||||
element.GetValueTimer -= deltaTime;
|
||||
if (element.GetValueTimer <= 0.0f)
|
||||
@@ -330,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);
|
||||
@@ -373,6 +372,9 @@ namespace Barotrauma.Items.Components
|
||||
private void UpdateSignalProjSpecific(GUIComponent uiElement)
|
||||
{
|
||||
if (uiElement.UserData is not CustomInterfaceElement element) { return; }
|
||||
|
||||
suppressNetworkEvents = true;
|
||||
|
||||
string signal = element.Signal;
|
||||
if (uiElement is GUITextBox tb)
|
||||
{
|
||||
@@ -384,14 +386,22 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (ni.InputType == NumberType.Int)
|
||||
{
|
||||
int.TryParse(signal, out int value);
|
||||
ni.IntValue = value;
|
||||
if (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)
|
||||
@@ -429,38 +439,62 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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];
|
||||
switch (element.InputType)
|
||||
{
|
||||
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:
|
||||
string newValue = msg.ReadString();
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.Text:
|
||||
string newTextValue = msg.ReadString();
|
||||
TextChanged(element, newTextValue);
|
||||
TextChanged(element, stringValues[i]);
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.TickBox:
|
||||
bool tickBoxState = msg.ReadBoolean();
|
||||
bool tickBoxState = boolValues[i];
|
||||
((GUITickBox)uiElements[i]).Selected = tickBoxState;
|
||||
TickBoxToggled(element, tickBoxState);
|
||||
break;
|
||||
case CustomInterfaceElement.InputTypeOption.Button:
|
||||
bool buttonState = msg.ReadBoolean();
|
||||
if (buttonState)
|
||||
if (boolValues[i])
|
||||
{
|
||||
ButtonClicked(element);
|
||||
}
|
||||
@@ -472,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)
|
||||
|
||||
@@ -199,6 +199,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;
|
||||
|
||||
@@ -282,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);
|
||||
|
||||
@@ -1552,10 +1552,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);
|
||||
@@ -2281,7 +2290,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);
|
||||
}
|
||||
|
||||
@@ -2487,12 +2502,18 @@ namespace Barotrauma
|
||||
|
||||
if (inventory != null)
|
||||
{
|
||||
if (inventorySlotIndex >= 0 && inventorySlotIndex < 255 &&
|
||||
inventory.TryPutItem(item, inventorySlotIndex, false, false, null, false))
|
||||
if (inventorySlotIndex is >= 0 and < 255)
|
||||
{
|
||||
return item;
|
||||
if (!inventory.TryPutItem(item, inventorySlotIndex, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: false, ignoreCondition: true) &&
|
||||
inventory.IsSlotEmpty(inventorySlotIndex))
|
||||
{
|
||||
//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);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
inventory.TryPutItem(item, null, item.AllowedSlots, false);
|
||||
inventory.TryPutItem(item, user: null, allowedSlots: item.AllowedSlots, createNetworkEvent: false);
|
||||
}
|
||||
|
||||
return item;
|
||||
|
||||
@@ -96,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)
|
||||
{
|
||||
|
||||
@@ -404,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,
|
||||
@@ -578,8 +578,7 @@ namespace Barotrauma
|
||||
corners[4] = new Vector3(x, bottom, 0.0f);
|
||||
//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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -47,9 +49,25 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
@@ -11,10 +11,25 @@ namespace Barotrauma
|
||||
{
|
||||
class LevelWallVertexBuffer : IDisposable
|
||||
{
|
||||
public VertexBuffer WallEdgeBuffer, WallBuffer;
|
||||
/// <summary>
|
||||
/// Buffer for the vertices of the "actual" wall texture.
|
||||
/// </summary>
|
||||
public VertexBuffer WallBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Buffer for the vertices of the repeating edge texture drawn at the edges of the walls.
|
||||
/// </summary>
|
||||
public VertexBuffer WallEdgeBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Buffer for the vertices of the inner, non-textured black part of the wall.
|
||||
/// </summary>
|
||||
public VertexBuffer WallInnerBuffer;
|
||||
|
||||
public readonly Texture2D WallTexture, EdgeTexture;
|
||||
private VertexPositionColorTexture[] wallVertices;
|
||||
private VertexPositionColorTexture[] wallEdgeVertices;
|
||||
private VertexPositionColor[] wallInnerVertices;
|
||||
|
||||
public bool IsDisposed
|
||||
{
|
||||
@@ -22,7 +37,7 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
public LevelWallVertexBuffer(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
|
||||
public LevelWallVertexBuffer(VertexPositionColorTexture[] wallVertices, VertexPositionColorTexture[] wallEdgeVertices, VertexPositionColor[] wallInnerVertices, Texture2D wallTexture, Texture2D edgeTexture)
|
||||
{
|
||||
if (wallVertices.Length == 0)
|
||||
{
|
||||
@@ -32,32 +47,41 @@ namespace Barotrauma
|
||||
{
|
||||
throw new ArgumentException("Failed to instantiate a LevelWallVertexBuffer (no wall edge vertices).");
|
||||
}
|
||||
this.wallVertices = LevelRenderer.GetColoredVertices(wallVertices, color);
|
||||
this.wallVertices = wallVertices;
|
||||
WallBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, wallVertices.Length, BufferUsage.WriteOnly);
|
||||
WallBuffer.SetData(this.wallVertices);
|
||||
WallTexture = wallTexture;
|
||||
|
||||
this.wallEdgeVertices = LevelRenderer.GetColoredVertices(wallEdgeVertices, color);
|
||||
this.wallEdgeVertices = wallEdgeVertices;
|
||||
WallEdgeBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, wallEdgeVertices.Length, BufferUsage.WriteOnly);
|
||||
WallEdgeBuffer.SetData(this.wallEdgeVertices);
|
||||
EdgeTexture = edgeTexture;
|
||||
|
||||
if (wallInnerVertices != null)
|
||||
{
|
||||
this.wallInnerVertices = wallInnerVertices;
|
||||
WallInnerBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColor.VertexDeclaration, wallInnerVertices.Length, BufferUsage.WriteOnly);
|
||||
WallInnerBuffer.SetData(this.wallInnerVertices);
|
||||
}
|
||||
}
|
||||
|
||||
public void Append(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Color color)
|
||||
public void Append(VertexPositionColorTexture[] newWallVertices, VertexPositionColorTexture[] newWallEdgeVertices, VertexPositionColor[] newWallInnerVertices)
|
||||
{
|
||||
WallBuffer.Dispose();
|
||||
WallBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, this.wallVertices.Length + wallVertices.Length, BufferUsage.WriteOnly);
|
||||
int originalWallVertexCount = this.wallVertices.Length;
|
||||
Array.Resize(ref this.wallVertices, originalWallVertexCount + wallVertices.Length);
|
||||
Array.Copy(LevelRenderer.GetColoredVertices(wallVertices, color), 0, this.wallVertices, originalWallVertexCount, wallVertices.Length);
|
||||
WallBuffer.SetData(this.wallVertices);
|
||||
WallBuffer = Append(WallBuffer, ref wallVertices, newWallVertices, VertexPositionColorTexture.VertexDeclaration);
|
||||
WallEdgeBuffer = Append(WallEdgeBuffer, ref wallEdgeVertices, newWallEdgeVertices, VertexPositionColorTexture.VertexDeclaration);
|
||||
WallInnerBuffer = Append(WallInnerBuffer, ref wallInnerVertices, newWallInnerVertices, VertexPositionColor.VertexDeclaration);
|
||||
|
||||
WallEdgeBuffer.Dispose();
|
||||
WallEdgeBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, this.wallEdgeVertices.Length + wallEdgeVertices.Length, BufferUsage.WriteOnly);
|
||||
int originalWallEdgeVertexCount = this.wallEdgeVertices.Length;
|
||||
Array.Resize(ref this.wallEdgeVertices, originalWallEdgeVertexCount + wallEdgeVertices.Length);
|
||||
Array.Copy(LevelRenderer.GetColoredVertices(wallEdgeVertices, color), 0, this.wallEdgeVertices, originalWallEdgeVertexCount, wallEdgeVertices.Length);
|
||||
WallEdgeBuffer.SetData(this.wallEdgeVertices);
|
||||
static VertexBuffer Append<T>(VertexBuffer buffer, ref T[] currentVertices, T[] newVertices, VertexDeclaration vertexDeclaration) where T : struct, IVertexType
|
||||
{
|
||||
buffer?.Dispose();
|
||||
int originalVertexCount = currentVertices.Length;
|
||||
int newBufferSize = originalVertexCount + newVertices.Length;
|
||||
buffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, vertexDeclaration, newBufferSize, BufferUsage.WriteOnly);
|
||||
Array.Resize(ref currentVertices, newBufferSize);
|
||||
Array.Copy(newVertices, 0, currentVertices, originalVertexCount, newVertices.Length);
|
||||
buffer.SetData(currentVertices);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -70,7 +94,7 @@ namespace Barotrauma
|
||||
|
||||
class LevelRenderer : IDisposable
|
||||
{
|
||||
private static BasicEffect wallEdgeEffect, wallCenterEffect;
|
||||
private static BasicEffect wallEdgeEffect, wallCenterEffect, wallInnerEffect;
|
||||
|
||||
private Vector2 waterParticleOffset;
|
||||
private Vector2 waterParticleVelocity;
|
||||
@@ -129,7 +153,16 @@ namespace Barotrauma
|
||||
};
|
||||
wallCenterEffect.CurrentTechnique = wallCenterEffect.Techniques["BasicEffect_Texture"];
|
||||
}
|
||||
|
||||
|
||||
if (wallInnerEffect == null)
|
||||
{
|
||||
wallInnerEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
|
||||
{
|
||||
VertexColorEnabled = true,
|
||||
TextureEnabled = false
|
||||
};
|
||||
wallInnerEffect.CurrentTechnique = wallInnerEffect.Techniques["BasicEffect_Texture"];
|
||||
}
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
@@ -184,7 +217,7 @@ namespace Barotrauma
|
||||
//calculate the sum of the forces of nearby level triggers
|
||||
//and use it to move the water texture and water distortion effect
|
||||
Vector2 currentWaterParticleVel = level.GenerationParams.WaterParticleVelocity;
|
||||
foreach (LevelObject levelObject in level.LevelObjectManager.GetVisibleObjects())
|
||||
foreach (LevelObject levelObject in level.LevelObjectManager.GetAllVisibleObjects())
|
||||
{
|
||||
if (levelObject.Triggers == null) { continue; }
|
||||
//use the largest water flow velocity of all the triggers
|
||||
@@ -225,16 +258,16 @@ namespace Barotrauma
|
||||
return verts;
|
||||
}
|
||||
|
||||
public void SetVertices(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
|
||||
public void SetVertices(VertexPositionColorTexture[] wallVertices, VertexPositionColorTexture[] wallEdgeVertices, VertexPositionColor[] wallInnerVertices, Texture2D wallTexture, Texture2D edgeTexture)
|
||||
{
|
||||
var existingBuffer = vertexBuffers.Find(vb => vb.WallTexture == wallTexture && vb.EdgeTexture == edgeTexture);
|
||||
if (existingBuffer != null)
|
||||
{
|
||||
existingBuffer.Append(wallVertices, wallEdgeVertices,color);
|
||||
existingBuffer.Append(wallVertices, wallEdgeVertices, wallInnerVertices);
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexBuffers.Add(new LevelWallVertexBuffer(wallVertices, wallEdgeVertices, wallTexture, edgeTexture, color));
|
||||
vertexBuffers.Add(new LevelWallVertexBuffer(wallVertices, wallEdgeVertices, wallInnerVertices, wallTexture, edgeTexture));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,15 +530,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
wallEdgeEffect.Alpha = 1.0f;
|
||||
wallCenterEffect.Alpha = 1.0f;
|
||||
|
||||
wallCenterEffect.World = transformMatrix;
|
||||
wallEdgeEffect.World = transformMatrix;
|
||||
wallEdgeEffect.Alpha = wallInnerEffect.Alpha = wallCenterEffect.Alpha = 1.0f;
|
||||
wallCenterEffect.World = wallInnerEffect.World = wallEdgeEffect.World = transformMatrix;
|
||||
|
||||
//render static walls
|
||||
foreach (var vertexBuffer in vertexBuffers)
|
||||
{
|
||||
wallInnerEffect.CurrentTechnique.Passes[0].Apply();
|
||||
graphicsDevice.SetVertexBuffer(vertexBuffer.WallInnerBuffer);
|
||||
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(vertexBuffer.WallInnerBuffer.VertexCount / 3.0f));
|
||||
|
||||
wallCenterEffect.Texture = vertexBuffer.WallTexture;
|
||||
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
|
||||
graphicsDevice.SetVertexBuffer(vertexBuffer.WallBuffer);
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -26,22 +23,29 @@ namespace Barotrauma
|
||||
Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(Body.Position), 0.0f));
|
||||
}
|
||||
|
||||
public void SetWallVertices(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
|
||||
public void SetWallVertices(
|
||||
VertexPositionColorTexture[] wallVertices, VertexPositionColorTexture[] wallEdgeVertices,
|
||||
Texture2D wallTexture, Texture2D edgeTexture)
|
||||
{
|
||||
if (VertexBuffer != null && !VertexBuffer.IsDisposed) { VertexBuffer.Dispose(); }
|
||||
VertexBuffer = new LevelWallVertexBuffer(wallVertices, wallEdgeVertices, wallTexture, edgeTexture, color);
|
||||
VertexBuffer = new LevelWallVertexBuffer(wallVertices, wallEdgeVertices, wallInnerVertices: null, wallTexture, edgeTexture);
|
||||
}
|
||||
|
||||
public void GenerateVertices()
|
||||
{
|
||||
float zCoord = this is DestructibleLevelWall ? Rand.Range(0.9f, 1.0f) : 0.9f;
|
||||
List<VertexPositionTexture> wallVertices = CaveGenerator.GenerateWallVertices(triangles, level.GenerationParams, zCoord);
|
||||
var nonTexturedWallVerts =
|
||||
CaveGenerator.GenerateWallVertices(triangles, color, zCoord: 0.9f).ToArray();
|
||||
var wallVerts = CaveGenerator.ConvertToTextured(nonTexturedWallVerts, level.GenerationParams.WallTextureSize);
|
||||
SetWallVertices(
|
||||
wallVertices.ToArray(),
|
||||
CaveGenerator.GenerateWallEdgeVertices(Cells, level, zCoord).ToArray(),
|
||||
wallVertices: wallVerts,
|
||||
wallEdgeVertices: CaveGenerator.GenerateWallEdgeVertices(Cells,
|
||||
level.GenerationParams.WallEdgeExpandOutwardsAmount, level.GenerationParams.WallEdgeExpandInwardsAmount,
|
||||
outerColor: color, innerColor: color,
|
||||
level, zCoord)
|
||||
.ToArray(),
|
||||
level.GenerationParams.WallSprite.Texture,
|
||||
level.GenerationParams.WallEdgeSprite.Texture,
|
||||
color);
|
||||
level.GenerationParams.WallEdgeSprite.Texture);
|
||||
}
|
||||
|
||||
public bool IsVisible(Rectangle worldView)
|
||||
|
||||
@@ -443,10 +443,10 @@ namespace Barotrauma.Lights
|
||||
|
||||
public bool Intersects(Rectangle rect)
|
||||
{
|
||||
if (!Enabled) return false;
|
||||
if (!Enabled) { return false; }
|
||||
|
||||
Rectangle transformedBounds = BoundingBox;
|
||||
if (ParentEntity != null && ParentEntity.Submarine != null)
|
||||
if (ParentEntity is { Submarine: not null })
|
||||
{
|
||||
transformedBounds.X += (int)ParentEntity.Submarine.Position.X;
|
||||
transformedBounds.Y += (int)ParentEntity.Submarine.Position.Y;
|
||||
|
||||
@@ -243,6 +243,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="backgroundObstructor">A render target that contains the structures that should obstruct lights in the background. If not given, damageable walls and hulls are rendered to obstruct the background lights.</param>
|
||||
public void RenderLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null)
|
||||
{
|
||||
if (!LightingEnabled) { return; }
|
||||
@@ -411,11 +412,21 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
GameMain.GameScreen.DamageEffect.CurrentTechnique = GameMain.GameScreen.DamageEffect.Techniques["StencilShaderSolidColor"];
|
||||
GameMain.GameScreen.DamageEffect.Parameters["solidColor"].SetValue(Color.Black.ToVector4());
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearWrap, transformMatrix: spriteBatchTransform, effect: GameMain.GameScreen.DamageEffect);
|
||||
Submarine.DrawDamageable(spriteBatch, GameMain.GameScreen.DamageEffect);
|
||||
spriteBatch.End();
|
||||
if (backgroundObstructor != null)
|
||||
{
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearWrap, transformMatrix: Matrix.Identity, effect: GameMain.GameScreen.DamageEffect);
|
||||
spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0,
|
||||
(int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black);
|
||||
spriteBatch.End();
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.GameScreen.DamageEffect.CurrentTechnique = GameMain.GameScreen.DamageEffect.Techniques["StencilShaderSolidColor"];
|
||||
GameMain.GameScreen.DamageEffect.Parameters["solidColor"].SetValue(Color.Black.ToVector4());
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearWrap, transformMatrix: spriteBatchTransform, effect: GameMain.GameScreen.DamageEffect);
|
||||
Submarine.DrawDamageable(spriteBatch, GameMain.GameScreen.DamageEffect);
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
graphics.BlendState = BlendState.Additive;
|
||||
|
||||
@@ -680,11 +691,14 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
return visibleHulls;
|
||||
}
|
||||
|
||||
private static readonly List<VertexPositionColor> ShadowVertices = new List<VertexPositionColor>(500);
|
||||
private static readonly List<VertexPositionTexture> PenumbraVertices = new List<VertexPositionTexture>(500);
|
||||
|
||||
public void UpdateObstructVision(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Vector2 lookAtPosition)
|
||||
{
|
||||
if ((!LosEnabled || LosMode == LosMode.None) && ObstructVisionAmount <= 0.0f) { return; }
|
||||
if (ViewTarget == null) return;
|
||||
if (ViewTarget == null) { return; }
|
||||
|
||||
graphics.SetRenderTarget(LosTexture);
|
||||
|
||||
@@ -767,11 +781,11 @@ namespace Barotrauma.Lights
|
||||
|
||||
if (convexHulls != null)
|
||||
{
|
||||
List<VertexPositionColor> shadowVerts = new List<VertexPositionColor>();
|
||||
List<VertexPositionTexture> penumbraVerts = new List<VertexPositionTexture>();
|
||||
ShadowVertices.Clear();
|
||||
PenumbraVertices.Clear();
|
||||
foreach (ConvexHull convexHull in convexHulls)
|
||||
{
|
||||
if (!convexHull.Enabled || !convexHull.Intersects(camView)) { continue; }
|
||||
if (!convexHull.Intersects(camView)) { continue; }
|
||||
|
||||
Vector2 relativeViewPos = pos;
|
||||
if (convexHull.ParentEntity?.Submarine != null)
|
||||
@@ -783,26 +797,26 @@ namespace Barotrauma.Lights
|
||||
|
||||
for (int i = 0; i < convexHull.ShadowVertexCount; i++)
|
||||
{
|
||||
shadowVerts.Add(convexHull.ShadowVertices[i]);
|
||||
ShadowVertices.Add(convexHull.ShadowVertices[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < convexHull.PenumbraVertexCount; i++)
|
||||
{
|
||||
penumbraVerts.Add(convexHull.PenumbraVertices[i]);
|
||||
PenumbraVertices.Add(convexHull.PenumbraVertices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (shadowVerts.Count > 0)
|
||||
if (ShadowVertices.Count > 0)
|
||||
{
|
||||
ConvexHull.shadowEffect.World = shadowTransform;
|
||||
ConvexHull.shadowEffect.CurrentTechnique.Passes[0].Apply();
|
||||
graphics.DrawUserPrimitives(PrimitiveType.TriangleList, shadowVerts.ToArray(), 0, shadowVerts.Count / 3, VertexPositionColor.VertexDeclaration);
|
||||
graphics.DrawUserPrimitives(PrimitiveType.TriangleList, ShadowVertices.ToArray(), 0, ShadowVertices.Count / 3, VertexPositionColor.VertexDeclaration);
|
||||
|
||||
if (penumbraVerts.Count > 0)
|
||||
if (PenumbraVertices.Count > 0)
|
||||
{
|
||||
ConvexHull.penumbraEffect.World = shadowTransform;
|
||||
ConvexHull.penumbraEffect.CurrentTechnique.Passes[0].Apply();
|
||||
graphics.DrawUserPrimitives(PrimitiveType.TriangleList, penumbraVerts.ToArray(), 0, penumbraVerts.Count / 3, VertexPositionTexture.VertexDeclaration);
|
||||
graphics.DrawUserPrimitives(PrimitiveType.TriangleList, PenumbraVertices.ToArray(), 0, PenumbraVertices.Count / 3, VertexPositionTexture.VertexDeclaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,7 +592,17 @@ namespace Barotrauma.Lights
|
||||
private void CheckHullsInRange(Submarine sub)
|
||||
{
|
||||
//find the list of convexhulls that belong to the sub
|
||||
ConvexHullList chList = convexHullsInRange.FirstOrDefault(chList => chList.Submarine == sub);
|
||||
|
||||
// Performance-sensitive code, hence implemented without Linq.
|
||||
ConvexHullList chList = null;
|
||||
foreach (var chl in convexHullsInRange)
|
||||
{
|
||||
if (chl.Submarine == sub)
|
||||
{
|
||||
chList = chl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//not found -> create one
|
||||
if (chList == null)
|
||||
|
||||
@@ -451,7 +451,7 @@ namespace Barotrauma
|
||||
MathUtils.PositiveModulo(-textureOffset.X, Prefab.BackgroundSprite.SourceRect.Width * TextureScale.X * Scale),
|
||||
MathUtils.PositiveModulo(-textureOffset.Y, Prefab.BackgroundSprite.SourceRect.Height * TextureScale.Y * Scale));
|
||||
|
||||
float rotationRad = rotationForSprite(this.rotationRad, Prefab.BackgroundSprite);
|
||||
float rotationRad = GetRotationForSprite(this.rotationRad, Prefab.BackgroundSprite);
|
||||
|
||||
Prefab.BackgroundSprite.DrawTiled(
|
||||
spriteBatch,
|
||||
@@ -492,7 +492,7 @@ namespace Barotrauma
|
||||
advanceY = advanceY.FlipX();
|
||||
}
|
||||
|
||||
float sectionSpriteRotationRad = rotationForSprite(this.rotationRad, Prefab.Sprite);
|
||||
float sectionSpriteRotationRad = GetRotationForSprite(this.rotationRad, Prefab.Sprite);
|
||||
|
||||
for (int i = 0; i < Sections.Length; i++)
|
||||
{
|
||||
@@ -501,11 +501,17 @@ namespace Barotrauma
|
||||
{
|
||||
float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / MaxHealth);
|
||||
|
||||
if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f || color != Submarine.DamageEffectColor)
|
||||
if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.05f)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront,
|
||||
BlendState.NonPremultiplied, SamplerState.LinearWrap,
|
||||
null, null,
|
||||
damageEffect,
|
||||
Screen.Selected.Cam.Transform);
|
||||
|
||||
damageEffect.Parameters["aCutoff"].SetValue(newCutoff);
|
||||
damageEffect.Parameters["cCutoff"].SetValue(newCutoff * 1.2f);
|
||||
damageEffect.Parameters["inColor"].SetValue(color.ToVector4());
|
||||
|
||||
damageEffect.CurrentTechnique.Passes[0].Apply();
|
||||
|
||||
@@ -570,9 +576,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
static float rotationForSprite(float rotationRad, Sprite sprite)
|
||||
static float GetRotationForSprite(float rotationRad, Sprite sprite)
|
||||
{
|
||||
if (sprite.effects.HasFlag(SpriteEffects.FlipHorizontally) != sprite.effects.HasFlag(SpriteEffects.FlipVertically))
|
||||
// Use bitwise operations instead of HasFlag to avoid boxing, as this is performance-sensitive code.
|
||||
bool flipHorizontally = (sprite.effects & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally;
|
||||
bool flipVertically = (sprite.effects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically;
|
||||
|
||||
if (flipHorizontally != flipVertically)
|
||||
{
|
||||
rotationRad = -rotationRad;
|
||||
}
|
||||
@@ -604,6 +614,10 @@ namespace Barotrauma
|
||||
if (GetSection(i).damage > 0)
|
||||
{
|
||||
var textPos = SectionPosition(i, true);
|
||||
if (Submarine != null)
|
||||
{
|
||||
textPos += (Submarine.DrawPosition - Submarine.Position);
|
||||
}
|
||||
textPos.Y = -textPos.Y;
|
||||
GUI.DrawString(spriteBatch, textPos, "Damage: " + (int)((GetSection(i).damage / MaxHealth) * 100f) + "%", Color.Yellow);
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Barotrauma
|
||||
foreach (Submarine sub in Loaded)
|
||||
{
|
||||
Rectangle worldBorders = sub.Borders;
|
||||
worldBorders.Location += sub.WorldPosition.ToPoint();
|
||||
worldBorders.Location += (sub.DrawPosition + sub.HiddenSubPosition).ToPoint();
|
||||
worldBorders.Y = -worldBorders.Y;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, worldBorders, Color.White, false, 0, 5);
|
||||
@@ -161,38 +161,38 @@ namespace Barotrauma
|
||||
public static float DamageEffectCutoff;
|
||||
public static Color DamageEffectColor;
|
||||
|
||||
private static readonly List<Structure> depthSortedDamageable = new List<Structure>();
|
||||
public static void DrawDamageable(SpriteBatch spriteBatch, Effect damageEffect, bool editing = false, Predicate<MapEntity> predicate = null)
|
||||
{
|
||||
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.MapEntityList;
|
||||
|
||||
depthSortedDamageable.Clear();
|
||||
|
||||
//insertion sort according to draw depth
|
||||
foreach (MapEntity e in entitiesToRender)
|
||||
if (!editing && visibleEntities != null)
|
||||
{
|
||||
if (e is Structure structure && structure.DrawDamageEffect)
|
||||
foreach (MapEntity e in visibleEntities)
|
||||
{
|
||||
if (predicate != null)
|
||||
if (e is Structure structure && structure.DrawDamageEffect)
|
||||
{
|
||||
if (!predicate(e)) { continue; }
|
||||
if (predicate != null)
|
||||
{
|
||||
if (!predicate(structure)) { continue; }
|
||||
}
|
||||
structure.DrawDamage(spriteBatch, damageEffect, editing);
|
||||
}
|
||||
float drawDepth = structure.GetDrawDepth();
|
||||
int i = 0;
|
||||
while (i < depthSortedDamageable.Count)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Structure structure in Structure.WallList)
|
||||
{
|
||||
if (structure.DrawDamageEffect)
|
||||
{
|
||||
float otherDrawDepth = depthSortedDamageable[i].GetDrawDepth();
|
||||
if (otherDrawDepth < drawDepth) { break; }
|
||||
i++;
|
||||
if (predicate != null)
|
||||
{
|
||||
if (!predicate(structure)) { continue; }
|
||||
}
|
||||
structure.DrawDamage(spriteBatch, damageEffect, editing);
|
||||
}
|
||||
depthSortedDamageable.Insert(i, structure);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Structure s in depthSortedDamageable)
|
||||
{
|
||||
s.DrawDamage(spriteBatch, damageEffect, editing);
|
||||
}
|
||||
|
||||
if (damageEffect != null)
|
||||
{
|
||||
damageEffect.Parameters["aCutoff"].SetValue(0.0f);
|
||||
@@ -506,6 +506,16 @@ namespace Barotrauma
|
||||
warnings.Add(SubEditorScreen.WarningType.WaterInHulls);
|
||||
Hull.ShowHulls = true;
|
||||
}
|
||||
|
||||
if (Info.IsWreck)
|
||||
{
|
||||
Point vanillaBrainSize = new Point(204, 204);
|
||||
if (WreckAI.GetPotentialBrainRooms(this, WreckAIConfig.GetRandom(), minSize: vanillaBrainSize).None())
|
||||
{
|
||||
errorMsgs.Add(TextManager.Get("NoSuitableBrainRoomsWarning").Value);
|
||||
warnings.Add(SubEditorScreen.WarningType.NoSuitableBrainRooms);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.NotEnoughContainers))
|
||||
{
|
||||
|
||||
@@ -46,6 +46,10 @@ namespace Barotrauma
|
||||
}
|
||||
if (IsHighlighted || IsHighlighted) { clr = Color.Lerp(clr, Color.White, 0.8f); }
|
||||
|
||||
if (Stairs is { Removed: true }) { Stairs = null; }
|
||||
if (Ladders is { Item.Removed: true }) { Ladders = null; }
|
||||
if (ConnectedGap is { Removed: true }) { ConnectedGap = null; }
|
||||
|
||||
int iconSize = spawnType == SpawnType.Path ? WaypointSize : SpawnPointSize;
|
||||
if (ConnectedDoor != null || Ladders != null || Stairs != null || SpawnType != SpawnType.Path)
|
||||
{
|
||||
|
||||
@@ -519,10 +519,12 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
string errorMsg = "Error while reading a message from server. ";
|
||||
if (GameMain.Client == null) { errorMsg += "Client disposed."; }
|
||||
AppendExceptionInfo(ref errorMsg, e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + e.TargetSite.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", ("[message]", e.Message), ("[targetsite]", e.TargetSite.ToString())))
|
||||
AppendExceptionInfo(ref errorMsg, out Entity causingEntity, e);
|
||||
|
||||
string targetSite = e.TargetSite?.ToString() ?? "unknown";
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.Update:CheckServerMessagesException" + targetSite, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
DebugConsole.ThrowError(errorMsg, contentPackage: causingEntity?.ContentPackage);
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", ("[message]", e.Message), ("[targetsite]", targetSite)))
|
||||
{
|
||||
DisplayInLoadingScreens = true
|
||||
};
|
||||
@@ -662,7 +664,7 @@ namespace Barotrauma.Networking
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = "Error while reading an ingame update message from server.";
|
||||
AppendExceptionInfo(ref errorMsg, e);
|
||||
AppendExceptionInfo(ref errorMsg, out Entity causingEntity, e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadDataMessage:ReadIngameUpdate", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw;
|
||||
}
|
||||
@@ -986,13 +988,15 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (Level.Loaded.EqualityCheckValues[stage] != levelEqualityCheckValues[stage])
|
||||
{
|
||||
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" +
|
||||
" (client value " + stage + ": " + Level.Loaded.EqualityCheckValues[stage].ToString("X") +
|
||||
", server value " + stage + ": " + levelEqualityCheckValues[stage].ToString("X") +
|
||||
", level value count: " + levelEqualityCheckValues.Count +
|
||||
", seed: " + Level.Loaded.Seed +
|
||||
", sub: " + (Submarine.MainSub == null ? "null" : (Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")")) +
|
||||
", mirrored: " + Level.Loaded.Mirrored + "). Round init status: " + roundInitStatus + "." + campaignErrorInfo;
|
||||
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server, " +
|
||||
$"(client value {stage}:{Level.Loaded.EqualityCheckValues[stage].ToString("X")}, " +
|
||||
$"server value {stage}: {levelEqualityCheckValues[stage].ToString("X")}, " +
|
||||
$"level value count: {levelEqualityCheckValues.Count}, " +
|
||||
$"seed: {Level.Loaded.Seed}, " +
|
||||
$"missions: {string.Join(", ", GameMain.GameSession.GameMode.Missions.Select(m => m.Prefab.Identifier))}, " +
|
||||
$"sub: {(Submarine.MainSub == null ? "null" : (Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation))}, " +
|
||||
$"mirrored: {Level.Loaded.Mirrored}). Round init status: {roundInitStatus}." +
|
||||
campaignErrorInfo;
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
@@ -1470,6 +1474,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
string levelSeed = inc.ReadString();
|
||||
float levelDifficulty = inc.ReadSingle();
|
||||
Identifier biomeId = inc.ReadIdentifier();
|
||||
string subName = inc.ReadString();
|
||||
string subHash = inc.ReadString();
|
||||
string shuttleName = inc.ReadString();
|
||||
@@ -1557,7 +1562,7 @@ namespace Barotrauma.Networking
|
||||
var selectedEnemySub = hasEnemySub && GameMain.NetLobbyScreen.SelectedEnemySub is { } enemySub ? Option.Some(enemySub) : Option.None;
|
||||
|
||||
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, selectedEnemySub, gameMode, missionPrefabs: selectedMissions);
|
||||
GameMain.GameSession.StartRound(levelSeed, levelDifficulty, levelGenerationParams: null, forceBiome: ServerSettings.Biome);
|
||||
GameMain.GameSession.StartRound(levelSeed, levelDifficulty, levelGenerationParams: null, forceBiome: biomeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2075,16 +2080,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
UInt16 updateID = inc.ReadUInt16();
|
||||
|
||||
|
||||
UInt16 settingsLen = inc.ReadUInt16();
|
||||
byte[] settingsData = inc.ReadBytes(settingsLen);
|
||||
|
||||
bool isInitialUpdate = inc.ReadBoolean();
|
||||
DebugConsole.Log($"Received {(isInitialUpdate ? "initial" : string.Empty)} lobby update ID: {updateID}, last ID: {GameMain.NetLobbyScreen.LastUpdateID}.");
|
||||
|
||||
if (isInitialUpdate)
|
||||
{
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage("Received initial lobby update, ID: " + updateID + ", last ID: " + GameMain.NetLobbyScreen.LastUpdateID, Color.Gray);
|
||||
}
|
||||
{
|
||||
ReadInitialUpdate(inc);
|
||||
initialUpdateReceived = true;
|
||||
}
|
||||
@@ -2363,7 +2367,7 @@ namespace Barotrauma.Networking
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadInGameUpdate", GameAnalyticsManager.ErrorSeverity.Critical, string.Join("\n", errorLines));
|
||||
|
||||
throw new Exception(
|
||||
$"Exception thrown while reading segment {segment.Identifier} at position {segment.Pointer}." +
|
||||
$"Exception thrown while reading a message of the type \"{segment.Identifier}\" at position {segment.Pointer}." +
|
||||
(prevSegments.Any() ? $" Previous segments: {string.Join(", ", prevSegments)}" : ""),
|
||||
ex);
|
||||
});
|
||||
@@ -3760,16 +3764,24 @@ namespace Barotrauma.Networking
|
||||
eventErrorWritten = true;
|
||||
}
|
||||
|
||||
private static void AppendExceptionInfo(ref string errorMsg, Exception e)
|
||||
private static void AppendExceptionInfo(ref string errorMsg, out Entity causingEntity, Exception e)
|
||||
{
|
||||
if (!errorMsg.EndsWith("\n")) { errorMsg += "\n"; }
|
||||
|
||||
Exception innerMostException = e.GetInnermost();
|
||||
causingEntity = GetCausingEntity(e);
|
||||
|
||||
if (causingEntity != null)
|
||||
{
|
||||
errorMsg += "Entity: " + causingEntity + "\n";
|
||||
}
|
||||
errorMsg += e.Message + "\n";
|
||||
var innermostException = e.GetInnermost();
|
||||
if (innermostException != e)
|
||||
|
||||
if (innerMostException != e)
|
||||
{
|
||||
// If available, only append the stacktrace of the innermost exception,
|
||||
// because that's the most important one to fix
|
||||
errorMsg += "Inner exception: " + innermostException.Message + "\n" + innermostException.StackTrace.CleanupStackTrace();
|
||||
errorMsg += "Inner exception: " + innerMostException.Message + "\n" + innerMostException.StackTrace.CleanupStackTrace();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3777,6 +3789,24 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the exception or any of its inner exceptions are EntityEventExceptions, and returns the entity that caused the innermost EntityEventException.
|
||||
/// </summary>
|
||||
private static Entity GetCausingEntity(Exception e)
|
||||
{
|
||||
Entity causingEntity = null;
|
||||
Exception currentException = e;
|
||||
while (currentException != null)
|
||||
{
|
||||
if (currentException is EntityEventException entityEventException)
|
||||
{
|
||||
causingEntity = entityEventException.Entity;
|
||||
}
|
||||
currentException = currentException.InnerException;
|
||||
}
|
||||
return causingEntity;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public void ForceTimeOut()
|
||||
{
|
||||
|
||||
@@ -154,13 +154,25 @@ namespace Barotrauma.Networking
|
||||
//16 = entity ID, 8 = msg length
|
||||
if (msg.BitPosition + 16 + 8 > msg.LengthBits)
|
||||
{
|
||||
string errorMsg = $"Error while reading a message from the server. Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
|
||||
UInt16 potentialEntityId = Entity.NullEntityID;
|
||||
try
|
||||
{
|
||||
potentialEntityId = msg.ReadUInt16();
|
||||
}
|
||||
catch
|
||||
{
|
||||
//failed to read the ID, do nothing (we would've just used it for the error message)
|
||||
}
|
||||
Entity targetEntity = Entity.FindEntityByID(potentialEntityId);
|
||||
|
||||
string errorMsg = $"Error while reading a message from the server (entity: {targetEntity?.ToString() ?? "unknown"}).";
|
||||
errorMsg += $" Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
|
||||
errorMsg += "\nPrevious entities:";
|
||||
for (int j = tempEntityList.Count - 1; j >= 0; j--)
|
||||
{
|
||||
errorMsg += "\n" + (tempEntityList[j] == null ? "NULL" : tempEntityList[j].ToString());
|
||||
}
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
DebugConsole.ThrowError(errorMsg, contentPackage: targetEntity?.ContentPackage);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -172,7 +184,7 @@ namespace Barotrauma.Networking
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
|
||||
Microsoft.Xna.Framework.Color.Orange);
|
||||
Color.Orange);
|
||||
}
|
||||
tempEntityList.Add(null);
|
||||
if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; }
|
||||
@@ -187,7 +199,7 @@ namespace Barotrauma.Networking
|
||||
//skip the event if we've already received it or if the entity isn't found
|
||||
if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
|
||||
{
|
||||
if (thisEventID != (UInt16) (lastReceivedID + 1))
|
||||
if (thisEventID != (UInt16)(lastReceivedID + 1))
|
||||
{
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
@@ -195,7 +207,7 @@ namespace Barotrauma.Networking
|
||||
"Received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
|
||||
NetIdUtils.IdMoreRecent(thisEventID, (UInt16)(lastReceivedID + 1))
|
||||
? GUIStyle.Red
|
||||
: Microsoft.Xna.Framework.Color.Yellow);
|
||||
: Color.Yellow);
|
||||
}
|
||||
}
|
||||
else if (entity == null)
|
||||
@@ -215,12 +227,18 @@ namespace Barotrauma.Networking
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
|
||||
Microsoft.Xna.Framework.Color.Green);
|
||||
Color.Green);
|
||||
}
|
||||
lastReceivedID++;
|
||||
ReadEvent(msg, entity, sendingTime);
|
||||
msg.ReadPadBits();
|
||||
|
||||
try
|
||||
{
|
||||
ReadEvent(msg, entity, sendingTime);
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new EntityEventException("Failed to read event." , entity as Entity, exception);
|
||||
}
|
||||
if (msg.BitPosition != msgPosition + msgLength * 8)
|
||||
{
|
||||
var prevEntity = tempEntityList.Count >= 2 ? tempEntityList[tempEntityList.Count - 2] : null;
|
||||
@@ -231,7 +249,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
|
||||
throw new Exception(errorMsg);
|
||||
throw new EntityEventException(errorMsg, entity as Entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,16 @@ sealed class SteamConnectSocket : P2PSocket
|
||||
{
|
||||
if (!SteamManager.IsInitialized) { return Result.Failure(new Error(ErrorCode.SteamNotInitialized)); }
|
||||
|
||||
var connectionManager = Steamworks.SteamNetworkingSockets.ConnectRelay<ConnectionManager>(endpoint.SteamId.Value);
|
||||
ConnectionManager connectionManager;
|
||||
try
|
||||
{
|
||||
connectionManager = Steamworks.SteamNetworkingSockets.ConnectRelay<ConnectionManager>(endpoint.SteamId.Value);
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to connect via SteamP2P. Are you logged in to Steam, is the same Steam account already connected to the server?", e);
|
||||
return Result.Failure(new Error(ErrorCode.FailedToCreateSteamP2PSocket));
|
||||
}
|
||||
if (connectionManager is null) { return Result.Failure(new Error(ErrorCode.FailedToCreateSteamP2PSocket)); }
|
||||
connectionManager.SetEndpointAndCallbacks(endpoint, callbacks);
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
DebugConsole.ThrowError("Capture device has been disconnected. You can select another available device in the settings.");
|
||||
Disconnected = true;
|
||||
TryRefreshDevice();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -401,5 +402,65 @@ namespace Barotrauma.Networking
|
||||
captureThread = null;
|
||||
if (captureDevice != IntPtr.Zero) { Alc.CaptureCloseDevice(captureDevice); }
|
||||
}
|
||||
|
||||
public static void TryRefreshDevice()
|
||||
{
|
||||
DebugConsole.NewMessage("Refreshing audio capture device");
|
||||
|
||||
List<string> deviceList = Alc.GetStringList(IntPtr.Zero, Alc.CaptureDeviceSpecifier).ToList();
|
||||
int alcError = Alc.GetError(IntPtr.Zero);
|
||||
if (alcError != Alc.NoError)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to list available audio input devices: " + alcError.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (deviceList.Any())
|
||||
{
|
||||
string device;
|
||||
|
||||
if (deviceList.Find(n => n.Equals(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice, StringComparison.OrdinalIgnoreCase))
|
||||
is string availablePreviousDevice)
|
||||
{
|
||||
DebugConsole.NewMessage($" Previous device choice available: {availablePreviousDevice}");
|
||||
device = availablePreviousDevice;
|
||||
}
|
||||
else
|
||||
{
|
||||
device = Alc.GetString(IntPtr.Zero, Alc.CaptureDefaultDeviceSpecifier);
|
||||
DebugConsole.NewMessage($" Reverting to default device: {device}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(device))
|
||||
{
|
||||
device = deviceList[0];
|
||||
DebugConsole.NewMessage($" No default device found, resorting to first available device: {device}");
|
||||
}
|
||||
|
||||
// Save the new device choice and generate a new voice capture instance with it
|
||||
var currentConfig = GameSettings.CurrentConfig;
|
||||
currentConfig.Audio.VoiceCaptureDevice = device;
|
||||
GameSettings.SetCurrentConfig(currentConfig);
|
||||
if (Instance is VoipCapture currentCaptureInstance)
|
||||
{
|
||||
currentCaptureInstance.Dispose();
|
||||
}
|
||||
Create(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice);
|
||||
}
|
||||
|
||||
// Didn't end up with any capture device, so let's disable voice capture for now
|
||||
if (Instance == null)
|
||||
{
|
||||
DebugConsole.NewMessage($" No devices found, disabling");
|
||||
var currentConfig = GameSettings.CurrentConfig;
|
||||
currentConfig.Audio.VoiceSetting = VoiceMode.Disabled;
|
||||
GameSettings.SetCurrentConfig(currentConfig);
|
||||
}
|
||||
|
||||
if (GUI.SettingsMenuOpen)
|
||||
{
|
||||
SettingsMenu.Instance?.CreateAudioAndVCTab(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Barotrauma.Particles
|
||||
{
|
||||
public static readonly PrefabCollection<ParticlePrefab> Prefabs = new PrefabCollection<ParticlePrefab>();
|
||||
|
||||
[Flags]
|
||||
public enum DrawTargetType { Air = 1, Water = 2, Both = 3 }
|
||||
|
||||
public readonly List<Sprite> Sprites;
|
||||
|
||||
@@ -64,12 +64,12 @@ namespace Barotrauma
|
||||
if (drawOffset != Vector2.Zero)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(FarseerBody.Position);
|
||||
if (Submarine != null) pos += Submarine.DrawPosition;
|
||||
if (Submarine != null) { pos += Submarine.DrawPosition; }
|
||||
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(pos.X, -pos.Y),
|
||||
new Vector2(DrawPosition.X, -DrawPosition.Y),
|
||||
Color.Cyan, 0, 5);
|
||||
Color.Purple * 0.75f, 0, 5);
|
||||
}
|
||||
if (IsValidShape(Radius, Height, Width))
|
||||
{
|
||||
|
||||
@@ -145,6 +145,7 @@ namespace Barotrauma
|
||||
public SettingValue<float> OxygenMultiplier;
|
||||
public SettingValue<float> FuelMultiplier;
|
||||
public SettingValue<float> MissionRewardMultiplier;
|
||||
public SettingValue<float> ExperienceRewardMultiplier;
|
||||
public SettingValue<float> ShopPriceMultiplier;
|
||||
public SettingValue<float> ShipyardPriceMultiplier;
|
||||
public SettingValue<float> RepairFailMultiplier;
|
||||
@@ -167,6 +168,7 @@ namespace Barotrauma
|
||||
OxygenMultiplier = OxygenMultiplier.GetValue(),
|
||||
FuelMultiplier = FuelMultiplier.GetValue(),
|
||||
MissionRewardMultiplier = MissionRewardMultiplier.GetValue(),
|
||||
ExperienceRewardMultiplier = ExperienceRewardMultiplier.GetValue(),
|
||||
ShopPriceMultiplier = ShopPriceMultiplier.GetValue(),
|
||||
ShipyardPriceMultiplier = ShipyardPriceMultiplier.GetValue(),
|
||||
RepairFailMultiplier = RepairFailMultiplier.GetValue(),
|
||||
@@ -344,6 +346,19 @@ namespace Barotrauma
|
||||
verticalSize,
|
||||
OnValuesChanged);
|
||||
|
||||
// Experience reward multiplier
|
||||
CampaignSettings.MultiplierSettings experienceMultiplierSettings = CampaignSettings.GetMultiplierSettings("ExperienceRewardMultiplier");
|
||||
SettingValue<float> experienceMultiplier = CreateGUIFloatInputCarousel(
|
||||
settingsList.Content,
|
||||
TextManager.Get("campaignoption.experiencerewardmultiplier"),
|
||||
TextManager.Get("campaignoption.experiencerewardmultiplier.tooltip"),
|
||||
prevSettings.ExperienceRewardMultiplier,
|
||||
valueStep: experienceMultiplierSettings.Step,
|
||||
minValue: experienceMultiplierSettings.Min,
|
||||
maxValue: experienceMultiplierSettings.Max,
|
||||
verticalSize,
|
||||
OnValuesChanged);
|
||||
|
||||
// Shop buying prices multiplier
|
||||
CampaignSettings.MultiplierSettings shopPriceMultiplierSettings = CampaignSettings.GetMultiplierSettings("ShopPriceMultiplier");
|
||||
SettingValue<float> shopPriceMultiplier = CreateGUIFloatInputCarousel(
|
||||
@@ -501,6 +516,7 @@ namespace Barotrauma
|
||||
oxygenMultiplier.SetValue(settings.OxygenMultiplier);
|
||||
fuelMultiplier.SetValue(settings.FuelMultiplier);
|
||||
rewardMultiplier.SetValue(settings.MissionRewardMultiplier);
|
||||
experienceMultiplier.SetValue(settings.ExperienceRewardMultiplier);
|
||||
shopPriceMultiplier.SetValue(settings.ShopPriceMultiplier);
|
||||
shipyardPriceMultiplier.SetValue(settings.ShipyardPriceMultiplier);
|
||||
repairFailMultiplier.SetValue(settings.RepairFailMultiplier);
|
||||
@@ -530,6 +546,7 @@ namespace Barotrauma
|
||||
OxygenMultiplier = oxygenMultiplier,
|
||||
FuelMultiplier = fuelMultiplier,
|
||||
MissionRewardMultiplier = rewardMultiplier,
|
||||
ExperienceRewardMultiplier = experienceMultiplier,
|
||||
ShopPriceMultiplier = shopPriceMultiplier,
|
||||
ShipyardPriceMultiplier = shipyardPriceMultiplier,
|
||||
RepairFailMultiplier = repairFailMultiplier,
|
||||
|
||||
@@ -236,10 +236,36 @@ namespace Barotrauma
|
||||
{
|
||||
if (saveList.SelectedData is not CampaignMode.SaveInfo saveInfo) { return false; }
|
||||
if (string.IsNullOrWhiteSpace(saveInfo.FilePath)) { return false; }
|
||||
LoadGame?.Invoke(saveInfo.FilePath, backupIndex: Option.None);
|
||||
|
||||
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
|
||||
|
||||
if (saveInfo.RespawnMode != RespawnMode.None && saveInfo.RespawnMode != GameMain.NetworkMember?.ServerSettings?.RespawnMode)
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("Warning"),
|
||||
TextManager.GetWithVariables("RespawnModeMismatch",
|
||||
("[currentrespawnmode]", TextManager.Get($"respawnmode.{GameMain.NetworkMember?.ServerSettings?.RespawnMode}")),
|
||||
("[savedrespawnmode]", TextManager.Get($"respawnmode.{saveInfo.RespawnMode}"))),
|
||||
new LocalizedString[] { TextManager.Get("RespawnModeMismatch.GoBack"), TextManager.Get("RespawnModeMismatch.LoadAnyway") });
|
||||
msgBox.Buttons[0].OnClicked = (button, obj) =>
|
||||
{
|
||||
msgBox.Close();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[1].OnClicked = (button, obj) =>
|
||||
{
|
||||
msgBox.Close();
|
||||
LoadSaveGame();
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadSaveGame();
|
||||
return true;
|
||||
|
||||
void LoadSaveGame()
|
||||
{
|
||||
LoadGame?.Invoke(saveInfo.FilePath, backupIndex: Option.None);
|
||||
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
|
||||
}
|
||||
},
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
@@ -443,20 +443,22 @@ namespace Barotrauma
|
||||
GUILayoutGroup difficultyIndicatorGroup = null;
|
||||
if (mission.Difficulty.HasValue)
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.9f), missionName.RectTransform, anchor: Anchor.CenterRight) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
|
||||
isHorizontal: true, childAnchor: Anchor.CenterRight)
|
||||
{
|
||||
AbsoluteSpacing = 1,
|
||||
UserData = "difficulty"
|
||||
UserData = "difficulty",
|
||||
};
|
||||
difficultyIndicatorGroup.SetAsFirstChild();
|
||||
var difficultyColor = mission.GetDifficultyColor();
|
||||
for (int i = 0; i < mission.Difficulty; i++)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
|
||||
new GUIImage(new RectTransform(Vector2.One * 0.9f, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
Color = difficultyColor,
|
||||
SelectedColor = difficultyColor,
|
||||
HoverColor = difficultyColor
|
||||
HoverColor = difficultyColor,
|
||||
ToolTip = mission.GetDifficultyToolTipText()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1771,7 +1771,7 @@ namespace Barotrauma.CharacterEditor
|
||||
|
||||
// Ragdoll
|
||||
RagdollParams.ClearCache();
|
||||
string ragdollPath = RagdollParams.GetDefaultFile(name, contentPackage);
|
||||
string ragdollPath = RagdollParams.GetDefaultFile(name);
|
||||
RagdollParams ragdollParams = isHumanoid
|
||||
? RagdollParams.CreateDefault<HumanRagdollParams>(ragdollPath, name, ragdoll)
|
||||
: RagdollParams.CreateDefault<FishRagdollParams>(ragdollPath, name, ragdoll);
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Transactions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -15,6 +16,8 @@ namespace Barotrauma
|
||||
private RenderTarget2D renderTargetWater;
|
||||
private RenderTarget2D renderTargetFinal;
|
||||
|
||||
private RenderTarget2D renderTargetDamageable;
|
||||
|
||||
public readonly Effect DamageEffect;
|
||||
private readonly Texture2D damageStencil;
|
||||
private readonly Texture2D distortTexture;
|
||||
@@ -65,6 +68,7 @@ namespace Barotrauma
|
||||
renderTargetBackground = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
renderTargetWater = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
renderTargetFinal = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight, false, SurfaceFormat.Color, DepthFormat.None);
|
||||
renderTargetDamageable = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight, false, SurfaceFormat.Color, DepthFormat.None);
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
@@ -302,7 +306,7 @@ namespace Barotrauma
|
||||
//Draw background structures and wall background sprites
|
||||
//(= the background texture that's revealed when a wall is destroyed) into the background render target
|
||||
//These will be visible through the LOS effect.
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
Submarine.DrawBack(spriteBatch, false, e => e is Structure s && (e.SpriteDepth >= 0.9f || s.Prefab.BackgroundSprite != null) && !IsFromOutpostDrawnBehindSubs(e));
|
||||
Submarine.DrawPaintedColors(spriteBatch, false);
|
||||
spriteBatch.End();
|
||||
@@ -311,8 +315,19 @@ namespace Barotrauma
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:BackStructures", sw.ElapsedTicks);
|
||||
sw.Restart();
|
||||
|
||||
graphics.SetRenderTarget(renderTargetDamageable);
|
||||
graphics.Clear(Color.Transparent);
|
||||
DamageEffect.CurrentTechnique = DamageEffect.Techniques["StencilShader"];
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, SamplerState.LinearWrap, effect: DamageEffect, transformMatrix: cam.Transform);
|
||||
Submarine.DrawDamageable(spriteBatch, DamageEffect, false);
|
||||
spriteBatch.End();
|
||||
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:FrontDamageable", sw.ElapsedTicks);
|
||||
sw.Restart();
|
||||
|
||||
graphics.SetRenderTarget(null);
|
||||
GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam, renderTarget);
|
||||
GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam, renderTargetDamageable);
|
||||
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:Lighting", sw.ElapsedTicks);
|
||||
@@ -330,24 +345,24 @@ namespace Barotrauma
|
||||
Level.Loaded.DrawBack(graphics, spriteBatch, cam);
|
||||
}
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
Submarine.DrawBack(spriteBatch, false, e => e is Structure s && (e.SpriteDepth >= 0.9f || s.Prefab.BackgroundSprite != null) && IsFromOutpostDrawnBehindSubs(e));
|
||||
spriteBatch.End();
|
||||
|
||||
//draw alpha blended particles that are in water and behind subs
|
||||
#if LINUX || OSX
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
#else
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
#endif
|
||||
GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.AlphaBlend);
|
||||
spriteBatch.End();
|
||||
|
||||
//draw additive particles that are in water and behind subs
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.Additive);
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None);
|
||||
spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
|
||||
spriteBatch.End();
|
||||
|
||||
@@ -366,8 +381,8 @@ namespace Barotrauma
|
||||
GraphicsQuad.Render();
|
||||
|
||||
//Draw the rest of the structures, characters and front structures
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
|
||||
Submarine.DrawBack(spriteBatch, false, e => !(e is Structure) || e.SpriteDepth < 0.9f);
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
Submarine.DrawBack(spriteBatch, false, e => e is not Structure || e.SpriteDepth < 0.9f);
|
||||
DrawCharacters(deformed: false, firstPass: true);
|
||||
spriteBatch.End();
|
||||
|
||||
@@ -375,7 +390,7 @@ namespace Barotrauma
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:BackCharactersItems", sw.ElapsedTicks);
|
||||
sw.Restart();
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
DrawCharacters(deformed: true, firstPass: true);
|
||||
DrawCharacters(deformed: true, firstPass: false);
|
||||
DrawCharacters(deformed: false, firstPass: false);
|
||||
@@ -420,19 +435,19 @@ namespace Barotrauma
|
||||
GraphicsQuad.Render();
|
||||
|
||||
//draw alpha blended particles that are inside a sub
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.DepthRead, transformMatrix: cam.Transform);
|
||||
GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.AlphaBlend);
|
||||
spriteBatch.End();
|
||||
|
||||
graphics.SetRenderTarget(renderTarget);
|
||||
|
||||
//draw alpha blended particles that are not in water
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.DepthRead, transformMatrix: cam.Transform);
|
||||
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.AlphaBlend);
|
||||
spriteBatch.End();
|
||||
|
||||
//draw additive particles that are not in water
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
|
||||
spriteBatch.End();
|
||||
|
||||
@@ -449,20 +464,10 @@ namespace Barotrauma
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:FrontParticles", sw.ElapsedTicks);
|
||||
sw.Restart();
|
||||
|
||||
DamageEffect.CurrentTechnique = DamageEffect.Techniques["StencilShader"];
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate,
|
||||
BlendState.NonPremultiplied, SamplerState.LinearWrap,
|
||||
null, null,
|
||||
DamageEffect,
|
||||
cam.Transform);
|
||||
Submarine.DrawDamageable(spriteBatch, DamageEffect, false);
|
||||
spriteBatch.End();
|
||||
GraphicsQuad.UseBasicEffect(renderTargetDamageable);
|
||||
GraphicsQuad.Render();
|
||||
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:FrontDamageable", sw.ElapsedTicks);
|
||||
sw.Restart();
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
Submarine.DrawFront(spriteBatch, false, null);
|
||||
spriteBatch.End();
|
||||
|
||||
@@ -471,7 +476,7 @@ namespace Barotrauma
|
||||
sw.Restart();
|
||||
|
||||
//draw additive particles that are inside a sub
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.Default, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, depthStencilState: DepthStencilState.Default, transformMatrix: cam.Transform);
|
||||
GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.Additive);
|
||||
foreach (var discharger in Items.Components.ElectricalDischarger.List)
|
||||
{
|
||||
@@ -487,7 +492,7 @@ namespace Barotrauma
|
||||
GraphicsQuad.Render();
|
||||
}
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
c.DrawFront(spriteBatch, cam);
|
||||
|
||||
@@ -363,7 +363,7 @@ namespace Barotrauma
|
||||
s.IsPlayer && !s.HasTag(SubmarineTag.Shuttle) &&
|
||||
!nonPlayerFiles.Any(f => f.Path == s.FilePath));
|
||||
GameSession gameSession = new GameSession(subInfo, Option.None, CampaignDataPath.Empty, GameModePreset.TestMode, CampaignSettings.Empty, null);
|
||||
gameSession.StartRound(Level.Loaded.LevelData);
|
||||
gameSession.StartRound(Level.Loaded.LevelData, mirrorLevel.Selected);
|
||||
(gameSession.GameMode as TestGameMode).OnRoundEnd = () =>
|
||||
{
|
||||
GameMain.LevelEditorScreen.Select();
|
||||
@@ -560,9 +560,13 @@ namespace Barotrauma
|
||||
new Vector2(relWidth, relWidth * ((float)levelObjectList.Content.Rect.Width / levelObjectList.Content.Rect.Height)),
|
||||
levelObjectList.Content.RectTransform) { MinSize = new Point(0, 60) }, style: "ListBoxElementSquare")
|
||||
{
|
||||
UserData = levelObjPrefab
|
||||
UserData = levelObjPrefab,
|
||||
ToolTip = levelObjPrefab.Name
|
||||
};
|
||||
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform, Anchor.BottomCenter),
|
||||
text: ToolBox.LimitString(levelObjPrefab.Name, GUIStyle.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
|
||||
@@ -876,9 +880,22 @@ namespace Barotrauma
|
||||
{
|
||||
var levelObj = levelObjFrame.UserData as LevelObjectPrefab;
|
||||
float commonness = levelObj.GetCommonness(levelData);
|
||||
levelObjFrame.Color = commonness > 0.0f ? GUIStyle.Green * 0.4f : Color.Transparent;
|
||||
levelObjFrame.SelectedColor = commonness > 0.0f ? GUIStyle.Green * 0.6f : Color.White * 0.5f;
|
||||
levelObjFrame.HoverColor = commonness > 0.0f ? GUIStyle.Green * 0.7f : Color.White * 0.6f;
|
||||
|
||||
Color color = GUIStyle.Green;
|
||||
|
||||
if (commonness > 0.0f && levelData?.GenerationParams != null)
|
||||
{
|
||||
if (levelObj.MinSurfaceWidth > levelData.GenerationParams.CellSubdivisionLength &&
|
||||
levelObj.SpawnPos.HasFlag(LevelObjectPrefab.SpawnPosType.Wall))
|
||||
{
|
||||
color = Color.Orange;
|
||||
levelObjFrame.ToolTip = $"Potential issue: the level walls in \"{levelData.GenerationParams.Identifier}\" are set to be subdivided every {levelData.GenerationParams.CellSubdivisionLength} pixels, but the level object requires wall segments of at least {levelObj.MinSurfaceWidth} px. The object may be rarer than intended (or fail to spawn at all) in the level.";
|
||||
}
|
||||
}
|
||||
|
||||
levelObjFrame.Color = commonness > 0.0f ? color * 0.4f : Color.Transparent;
|
||||
levelObjFrame.SelectedColor = commonness > 0.0f ? color * 0.6f : Color.White * 0.5f;
|
||||
levelObjFrame.HoverColor = commonness > 0.0f ? color * 0.7f : Color.White * 0.6f;
|
||||
|
||||
levelObjFrame.GetAnyChild<GUIImage>().Color = commonness > 0.0f ? Color.White : Color.DarkGray;
|
||||
if (commonness <= 0.0f)
|
||||
|
||||
@@ -790,12 +790,16 @@ namespace Barotrauma
|
||||
|
||||
var winScoreContainer = CreateLabeledSlider(gameModeSettingsContent, headerTag: string.Empty, valueLabelTag: string.Empty, tooltipTag: "ServerSettingsWinScorePvPTooltip",
|
||||
out var winScorePvPSlider, out var winScorePvPSliderLabel);
|
||||
winScorePvPSlider.Range = new Vector2(1, 1000);
|
||||
winScorePvPSlider.StepValue = 1;
|
||||
winScorePvPSlider.Range = new Vector2(10, 1000);
|
||||
winScorePvPSlider.StepValue = 10;
|
||||
winScorePvPSlider.OnMoved = (scrollBar, _) =>
|
||||
{
|
||||
if (scrollBar.UserData is not GUITextBlock text) { return false; }
|
||||
text.Text = TextManager.GetWithVariable("ServerSettingsWinScoreValuePvP", "[value]", ((int)Math.Round(scrollBar.BarScrollValue, digits: 0)).ToString());
|
||||
return true;
|
||||
};
|
||||
winScorePvPSlider.OnReleased = (scrollBar, _) =>
|
||||
{
|
||||
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Properties);
|
||||
return true;
|
||||
};
|
||||
@@ -931,7 +935,7 @@ namespace Barotrauma
|
||||
//do this before adding the contents, otherwise they get disabled too (and we just want to disable the dropdown itself)
|
||||
clientDisabledElements.AddRange(biomeHolder.GetAllChildren());
|
||||
biomeDropdown.AddItem(TextManager.Get("random"), "Random".ToIdentifier());
|
||||
foreach (var biome in Biome.Prefabs)
|
||||
foreach (var biome in Biome.Prefabs.OrderBy(b => b.MinDifficulty))
|
||||
{
|
||||
if (biome.IsEndBiome) { continue; }
|
||||
biomeDropdown.AddItem(biome.DisplayName, biome.Identifier);
|
||||
@@ -1195,7 +1199,7 @@ namespace Barotrauma
|
||||
var respawnModeHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), settingsContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
respawnModeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.0f), respawnModeHolder.RectTransform), TextManager.Get("RespawnMode"), wrap: true);
|
||||
respawnModeSelection = new GUISelectionCarousel<RespawnMode>(new RectTransform(new Vector2(0.6f, 1.0f), respawnModeHolder.RectTransform));
|
||||
foreach (var respawnMode in Enum.GetValues(typeof(RespawnMode)).Cast<RespawnMode>())
|
||||
foreach (var respawnMode in Enum.GetValues(typeof(RespawnMode)).Cast<RespawnMode>().Where(rm => rm != RespawnMode.None))
|
||||
{
|
||||
respawnModeSelection.AddElement(respawnMode, TextManager.Get($"respawnmode.{respawnMode}"), TextManager.Get($"respawnmode.{respawnMode}.tooltip"));
|
||||
}
|
||||
@@ -3005,10 +3009,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
outpostDropdown.ListBox.Select(prevSelected);
|
||||
GameMain.Client.ServerSettings.AssignGUIComponent(nameof(ServerSettings.SelectedOutpostName), outpostDropdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
outpostDropdown.Parent.Visible = false;
|
||||
//remove assignment, we shouldn't try selecting the outpost when there's none to select
|
||||
GameMain.Client.ServerSettings.AssignGUIComponent(nameof(ServerSettings.SelectedOutpostName), null);
|
||||
}
|
||||
outpostDropdownUpToDate = true;
|
||||
}
|
||||
|
||||
@@ -1003,6 +1003,8 @@ namespace Barotrauma
|
||||
|
||||
private bool ShouldShowServer(ServerInfo serverInfo)
|
||||
{
|
||||
if (serverInfo == null) { return false; }
|
||||
|
||||
#if !DEBUG
|
||||
//never show newer versions
|
||||
//(ignore revision number, it doesn't affect compatibility)
|
||||
|
||||
@@ -56,7 +56,8 @@ namespace Barotrauma
|
||||
WaterInHulls,
|
||||
LowOxygenOutputWarning,
|
||||
TooLargeForEndGame,
|
||||
NotEnoughContainers
|
||||
NotEnoughContainers,
|
||||
NoSuitableBrainRooms
|
||||
}
|
||||
|
||||
public static Vector2 MouseDragStart = Vector2.Zero;
|
||||
@@ -1308,7 +1309,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform, Anchor.BottomCenter),
|
||||
text: name, textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
|
||||
text: RichString.Rich(name), textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
@@ -1320,7 +1321,7 @@ namespace Barotrauma
|
||||
textBlock.Text = frame.ToolTip = ep.Identifier.Value;
|
||||
textBlock.TextColor = GUIStyle.Red;
|
||||
}
|
||||
textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width);
|
||||
textBlock.Text = ToolBox.LimitString(textBlock.Text.SanitizedString, textBlock.Font, textBlock.Rect.Width);
|
||||
|
||||
if (ep.Category == MapEntityCategory.ItemAssembly
|
||||
&& ep.ContentPackage?.Files.Length == 1
|
||||
@@ -5792,7 +5793,7 @@ namespace Barotrauma
|
||||
foreach (LightComponent lightComponent in item.GetComponents<LightComponent>())
|
||||
{
|
||||
lightComponent.Light.Color =
|
||||
(item.body == null || item.body.Enabled || item.ParentInventory is ItemInventory { Container.HideItems: true }) &&
|
||||
(item.body == null || item.body.Enabled || item.ParentInventory is ItemInventory { Container.HideItems: false }) &&
|
||||
/*the light is only visible when worn -> can't be visible in the editor*/
|
||||
lightComponent.Parent is not Wearable ?
|
||||
lightComponent.LightColor :
|
||||
|
||||
@@ -107,6 +107,11 @@ namespace Barotrauma
|
||||
|
||||
public void SelectTab(Tab tab)
|
||||
{
|
||||
if (tab == Tab.AudioAndVC && CurrentDeviceMismatchesDisplayed())
|
||||
{
|
||||
CreateAudioAndVCTab(refresh: true);
|
||||
}
|
||||
|
||||
CurrentTab = tab;
|
||||
SwitchContent(tabContents[tab].Content);
|
||||
tabber.Children.ForEach(c =>
|
||||
@@ -134,6 +139,11 @@ namespace Barotrauma
|
||||
|
||||
private GUIFrame CreateNewContentFrame(Tab tab)
|
||||
{
|
||||
if (tabContents.TryGetValue(tab, out (GUIButton Button, GUIFrame Content) tabContent))
|
||||
{
|
||||
return tabContent.Content;
|
||||
}
|
||||
|
||||
var content = new GUIFrame(new RectTransform(Vector2.One * 0.95f, contentFrame.RectTransform, Anchor.Center, Pivot.Center), style: null);
|
||||
AddButtonToTabber(tab, content);
|
||||
return content;
|
||||
@@ -180,7 +190,7 @@ namespace Barotrauma
|
||||
|
||||
private static GUIDropDown Dropdown<T>(GUILayoutGroup parent, Func<T, LocalizedString> textFunc, Func<T, LocalizedString>? tooltipFunc, IReadOnlyList<T> values, T currentValue, Action<T> setter)
|
||||
{
|
||||
var dropdown = new GUIDropDown(NewItemRectT(parent));
|
||||
var dropdown = new GUIDropDown(NewItemRectT(parent), elementCount: values.Count);
|
||||
values.ForEach(v => dropdown.AddItem(text: textFunc(v), userData: v, toolTip: tooltipFunc?.Invoke(v) ?? null));
|
||||
int childIndex = values.IndexOf(currentValue);
|
||||
dropdown.Select(childIndex);
|
||||
@@ -269,6 +279,11 @@ namespace Barotrauma
|
||||
DropdownEnum(left, (m) => TextManager.Get($"{m}"), null, unsavedConfig.Graphics.DisplayMode, v => unsavedConfig.Graphics.DisplayMode = v);
|
||||
Spacer(left);
|
||||
|
||||
var displayLabel = Label(left, TextManager.Get("TargetDisplay"), GUIStyle.SubHeadingFont);
|
||||
displayLabel.ToolTip = TextManager.Get("TargetDisplay.Tooltip");
|
||||
Dropdown(left, m => TextManager.GetWithVariables(m == 0 ? "PrimaryDisplayFormat" : "SecondaryDisplayFormat", ("[num]", m.ToString()), ("[name]", Display.GetDisplayName(m))), null, Enumerable.Range(0, Display.GetNumberOfDisplays()).ToArray(), unsavedConfig.Graphics.Display, v => unsavedConfig.Graphics.Display = v);
|
||||
Spacer(left);
|
||||
|
||||
Tickbox(left, TextManager.Get("EnableVSync"), TextManager.Get("EnableVSyncTooltip"), unsavedConfig.Graphics.VSync, v => unsavedConfig.Graphics.VSync = v);
|
||||
Tickbox(left, TextManager.Get("EnableTextureCompression"), TextManager.Get("EnableTextureCompressionTooltip"), unsavedConfig.Graphics.CompressTextures, v => unsavedConfig.Graphics.CompressTextures = v);
|
||||
Spacer(right);
|
||||
@@ -347,17 +362,45 @@ namespace Barotrauma
|
||||
current = list[0];
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateAudioAndVCTab()
|
||||
|
||||
private static bool IsCurrentDevice(string savedDeviceName, int deviceType)
|
||||
{
|
||||
try
|
||||
{
|
||||
string currentDevice = Alc.GetString(IntPtr.Zero, deviceType);
|
||||
if (string.IsNullOrEmpty(savedDeviceName) || string.IsNullOrEmpty(currentDevice))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return currentDevice.Equals(savedDeviceName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error checking output device name: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CurrentDeviceMismatchesDisplayed()
|
||||
{
|
||||
return !IsCurrentDevice(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice, Alc.CaptureDefaultDeviceSpecifier) ||
|
||||
!IsCurrentDevice(GameSettings.CurrentConfig.Audio.AudioOutputDevice, Alc.DefaultDeviceSpecifier);
|
||||
}
|
||||
|
||||
public void CreateAudioAndVCTab(bool refresh = false)
|
||||
{
|
||||
if (GameMain.Client == null
|
||||
&& VoipCapture.Instance == null)
|
||||
&& (refresh || VoipCapture.Instance == null))
|
||||
{
|
||||
string currDevice = unsavedConfig.Audio.VoiceCaptureDevice;
|
||||
GetAudioDevices(Alc.CaptureDeviceSpecifier, Alc.CaptureDefaultDeviceSpecifier, out var deviceList, ref currDevice);
|
||||
|
||||
if (deviceList.Any())
|
||||
{
|
||||
if (VoipCapture.Instance is VoipCapture currentCaptureInstance)
|
||||
{
|
||||
currentCaptureInstance.Dispose();
|
||||
}
|
||||
VoipCapture.Create(unsavedConfig.Audio.VoiceCaptureDevice);
|
||||
}
|
||||
if (VoipCapture.Instance == null)
|
||||
@@ -367,7 +410,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
GUIFrame content = CreateNewContentFrame(Tab.AudioAndVC);
|
||||
|
||||
if (refresh)
|
||||
{
|
||||
content.ClearChildren();
|
||||
}
|
||||
var (audio, voiceChat) = CreateSidebars(content, split: true);
|
||||
|
||||
static void audioDeviceElement(
|
||||
@@ -401,6 +447,15 @@ namespace Barotrauma
|
||||
|
||||
string currentOutputDevice = unsavedConfig.Audio.AudioOutputDevice;
|
||||
audioDeviceElement(audio, v => unsavedConfig.Audio.AudioOutputDevice = v, Alc.OutputDevicesSpecifier, Alc.DefaultDeviceSpecifier, ref currentOutputDevice);
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), audio.RectTransform), text: TextManager.Get("RefreshAudioDevices"), style: "GUIButtonSmall")
|
||||
{
|
||||
ToolTip = TextManager.Get("RefreshAudioDevicesToolTip"),
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
CreateAudioAndVCTab(refresh: true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Spacer(audio);
|
||||
|
||||
Label(audio, TextManager.Get("SoundVolume"), GUIStyle.SubHeadingFont);
|
||||
@@ -443,6 +498,15 @@ namespace Barotrauma
|
||||
|
||||
string currentInputDevice = unsavedConfig.Audio.VoiceCaptureDevice;
|
||||
audioDeviceElement(voiceChat, v => unsavedConfig.Audio.VoiceCaptureDevice = v, Alc.CaptureDeviceSpecifier, Alc.CaptureDefaultDeviceSpecifier, ref currentInputDevice);
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), voiceChat.RectTransform), text: TextManager.Get("RefreshAudioDevices"), style: "GUIButtonSmall")
|
||||
{
|
||||
ToolTip = TextManager.Get("RefreshAudioDevicesToolTip"),
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
CreateAudioAndVCTab(refresh: true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Spacer(voiceChat);
|
||||
|
||||
Label(voiceChat, TextManager.Get("VCInputMode"), GUIStyle.SubHeadingFont);
|
||||
|
||||
@@ -258,11 +258,11 @@ namespace OpenAL
|
||||
|
||||
public static void GetInteger(IntPtr device, int param, out int data)
|
||||
{
|
||||
int[] dataArr = new int[1];
|
||||
GCHandle handle = GCHandle.Alloc(dataArr,GCHandleType.Pinned);
|
||||
data = 0; // (Optimization: let's pin an integer on the stack instead of an array on the heap, which previously allocated almost a GB of memory)
|
||||
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
GetIntegerv(device, param, 1, handle.AddrOfPinnedObject());
|
||||
data = Marshal.ReadInt32(handle.AddrOfPinnedObject());
|
||||
handle.Free();
|
||||
data = dataArr[0];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -670,6 +670,7 @@ namespace Barotrauma.Sounds
|
||||
DebugConsole.ThrowError("Playback device has been disconnected. You can select another available device in the settings.");
|
||||
SetAudioOutputDevice("<disconnected>");
|
||||
Disconnected = true;
|
||||
TryRefreshDevice();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -885,5 +886,52 @@ namespace Barotrauma.Sounds
|
||||
throw new Exception("Failed to close ALC device!");
|
||||
}
|
||||
}
|
||||
|
||||
public static void TryRefreshDevice()
|
||||
{
|
||||
DebugConsole.NewMessage("Refreshing audio playback device");
|
||||
|
||||
List<string> deviceList = Alc.GetStringList(IntPtr.Zero, Alc.OutputDevicesSpecifier).ToList();
|
||||
int alcError = Alc.GetError(IntPtr.Zero);
|
||||
if (alcError != Alc.NoError)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to list available audio playback devices: " + alcError.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (deviceList.Any())
|
||||
{
|
||||
string device;
|
||||
|
||||
if (deviceList.Find(n => n.Equals(GameSettings.CurrentConfig.Audio.AudioOutputDevice, StringComparison.OrdinalIgnoreCase))
|
||||
is string availablePreviousDevice)
|
||||
{
|
||||
DebugConsole.NewMessage($" Previous device choice available: {availablePreviousDevice}");
|
||||
device = availablePreviousDevice;
|
||||
}
|
||||
else
|
||||
{
|
||||
device = Alc.GetString(IntPtr.Zero, Alc.DefaultDeviceSpecifier);
|
||||
DebugConsole.NewMessage($" Reverting to default device: {device}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(device))
|
||||
{
|
||||
device = deviceList[0];
|
||||
DebugConsole.NewMessage($" No default device found, resorting to first available device: {device}");
|
||||
}
|
||||
|
||||
// Save the new device choice and initialize it
|
||||
var currentConfig = GameSettings.CurrentConfig;
|
||||
currentConfig.Audio.AudioOutputDevice = device;
|
||||
GameSettings.SetCurrentConfig(currentConfig);
|
||||
GameMain.SoundManager.InitializeAlcDevice(device);
|
||||
}
|
||||
|
||||
if (GUI.SettingsMenuOpen)
|
||||
{
|
||||
SettingsMenu.Instance?.CreateAudioAndVCTab(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ namespace Barotrauma
|
||||
|
||||
private static bool IsFiltered(ServerInfo info, SpamServerFilterType type, string value)
|
||||
{
|
||||
if (info == null) { return true; }
|
||||
|
||||
string desc = info.ServerMessage,
|
||||
name = info.ServerName;
|
||||
|
||||
|
||||
@@ -298,24 +298,27 @@ namespace Barotrauma
|
||||
/// Last version of the game that had broken handling of sprites that were scaled, flipped and offset
|
||||
/// </summary>
|
||||
public static readonly Version LastBrokenTiledSpriteGameVersion = new Version(major: 1, minor: 2, build: 7, revision: 0);
|
||||
|
||||
public void DrawTiled(ISpriteBatch spriteBatch, Vector2 position, Vector2 targetSize, float rotation = 0f, Vector2? origin = null, Color? color = null, Vector2? startOffset = null, Vector2? textureScale = null, float? depth = null)
|
||||
{
|
||||
DrawTiled(spriteBatch, position, targetSize, effects, rotation, origin, color, startOffset, textureScale, depth);
|
||||
}
|
||||
|
||||
public void DrawTiled(ISpriteBatch spriteBatch,
|
||||
Vector2 position,
|
||||
Vector2 targetSize,
|
||||
SpriteEffects spriteEffects,
|
||||
float rotation = 0f,
|
||||
Vector2? origin = null,
|
||||
Color? color = null,
|
||||
Vector2? startOffset = null,
|
||||
Vector2? textureScale = null,
|
||||
float? depth = null,
|
||||
SpriteEffects? spriteEffects = null)
|
||||
float? depth = null)
|
||||
{
|
||||
if (Texture == null) { return; }
|
||||
|
||||
spriteEffects ??= effects;
|
||||
|
||||
bool flipHorizontal = spriteEffects.Value.HasFlag(SpriteEffects.FlipHorizontally);
|
||||
bool flipVertical = spriteEffects.Value.HasFlag(SpriteEffects.FlipVertically);
|
||||
bool flipHorizontal = (spriteEffects & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; // optimized from spriteEffects.HasFlag(SpriteEffects.FlipHorizontally)
|
||||
bool flipVertical = (spriteEffects & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; // optimized from spriteEffects.HasFlag(SpriteEffects.FlipVertically)
|
||||
|
||||
float addedRotation = rotation + this.rotation;
|
||||
if (flipHorizontal != flipVertical) { addedRotation = -addedRotation; }
|
||||
@@ -354,7 +357,7 @@ namespace Barotrauma
|
||||
rotation: addedRotation,
|
||||
origin: Vector2.Zero,
|
||||
scale: scale,
|
||||
effects: spriteEffects.Value,
|
||||
effects: spriteEffects,
|
||||
layerDepth: depth ?? this.depth);
|
||||
}
|
||||
|
||||
|
||||
@@ -777,8 +777,7 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
GUIMessageBox msgBox = new(TextManager.Get("DeleteMods"), TextManager.GetWithVariables("DeleteModsConfirm", ("[amount]", selectedMods.Length.ToString())),
|
||||
new LocalizedString[] { TextManager.Get("Confirm"), TextManager.Get("Cancel")}, textAlignment: Alignment.TopCenter);
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked += (_, _) =>
|
||||
msgBox.Buttons[0].OnClicked += (_, _) =>
|
||||
{
|
||||
foreach (ContentPackage mod in selectedMods)
|
||||
{
|
||||
@@ -788,6 +787,7 @@ namespace Barotrauma.Steam
|
||||
msgBox.Close();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -16,4 +17,22 @@ namespace Barotrauma
|
||||
return -CompareCCW.Compare(a.WorldPos, b.WorldPos, center);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompareVertexPositionColorCCW : IComparer<VertexPositionColor>
|
||||
{
|
||||
private Vector2 center;
|
||||
|
||||
public CompareVertexPositionColorCCW(Vector2 center)
|
||||
{
|
||||
this.center = center;
|
||||
}
|
||||
public int Compare(VertexPositionColor a, VertexPositionColor b)
|
||||
{
|
||||
return -CompareCW.Compare(new Vector2(a.Position.X, a.Position.Y), new Vector2(b.Position.X, b.Position.Y), center);
|
||||
}
|
||||
public static int Compare(VertexPositionColor a, VertexPositionColor b, Vector2 center)
|
||||
{
|
||||
return -CompareCW.Compare(new Vector2(a.Position.X, a.Position.Y), new Vector2(b.Position.X, b.Position.Y), center);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,11 +76,14 @@ namespace Barotrauma
|
||||
float zoom = (float)texWidth / (float)boundingBox.Width;
|
||||
int texHeight = (int)(zoom * boundingBox.Height);
|
||||
|
||||
Camera cam = new Camera();
|
||||
Camera cam = new Camera()
|
||||
{
|
||||
AutoUpdateToScreenResolution = false,
|
||||
MaxZoom = zoom,
|
||||
MinZoom = zoom * 0.5f,
|
||||
Zoom = zoom
|
||||
};
|
||||
cam.SetResolution(new Point(texWidth, texHeight));
|
||||
cam.MaxZoom = zoom;
|
||||
cam.MinZoom = zoom * 0.5f;
|
||||
cam.Zoom = zoom;
|
||||
cam.Position = boundingBox.Center.ToVector2();
|
||||
cam.UpdateTransform(false);
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.6.19.1</Version>
|
||||
<Version>1.7.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
@@ -55,7 +55,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(Configuration)'!='Debug'">
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml;..\BarotraumaShared\LocalMods\[DebugOnlyTest]*\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.6.19.1</Version>
|
||||
<Version>1.7.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
@@ -57,7 +57,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(Configuration)'!='Debug'">
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml;..\BarotraumaShared\LocalMods\[DebugOnlyTest]*\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
|
||||
Texture2D xTexture;
|
||||
sampler TextureSampler : register (s0) = sampler_state { Texture = <xTexture>; };
|
||||
|
||||
@@ -7,8 +7,6 @@ sampler StencilSampler = sampler_state { Texture = <xStencil>; };
|
||||
|
||||
float4 solidColor;
|
||||
|
||||
float4 inColor;
|
||||
|
||||
float aCutoff;
|
||||
float aMultiplier;
|
||||
|
||||
@@ -33,7 +31,7 @@ float4 main(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord
|
||||
|
||||
float4 solidColorStencil(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||
{
|
||||
float4 c = xTexture.Sample(TextureSampler, texCoord) * inColor;
|
||||
float4 c = xTexture.Sample(TextureSampler, texCoord) * color;
|
||||
float4 stencilColor = xStencil.Sample(StencilSampler, texCoord);
|
||||
|
||||
float aDiff = stencilColor.a - aCutoff;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
|
||||
Texture xTexture;
|
||||
sampler TextureSampler : register (s0) = sampler_state { Texture = <xTexture>; };
|
||||
|
||||
@@ -7,8 +7,6 @@ sampler StencilSampler = sampler_state { Texture = <xStencil>; };
|
||||
|
||||
float4 solidColor;
|
||||
|
||||
float4 inColor;
|
||||
|
||||
float aCutoff;
|
||||
float aMultiplier;
|
||||
|
||||
@@ -17,7 +15,7 @@ float cMultiplier;
|
||||
|
||||
float4 main(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||
{
|
||||
float4 c = tex2D(TextureSampler, texCoord) * inColor;
|
||||
float4 c = tex2D(TextureSampler, texCoord) * color;
|
||||
float4 stencilColor = tex2D(StencilSampler, texCoord);
|
||||
|
||||
float aDiff = stencilColor.a - aCutoff;
|
||||
@@ -33,7 +31,7 @@ float4 main(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord
|
||||
|
||||
float4 solidColorStencil(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
|
||||
{
|
||||
float4 c = tex2D(TextureSampler, texCoord) * inColor;
|
||||
float4 c = tex2D(TextureSampler, texCoord) * color;
|
||||
float4 stencilColor = tex2D(StencilSampler, texCoord);
|
||||
|
||||
float aDiff = stencilColor.a - aCutoff;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.6.19.1</Version>
|
||||
<Version>1.7.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2024</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
@@ -63,7 +63,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(Configuration)'!='Debug'">
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml;..\BarotraumaShared\LocalMods\[DebugOnlyTest]*\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.6.19.1</Version>
|
||||
<Version>1.7.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
@@ -55,7 +55,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml;..\BarotraumaShared\LocalMods\[DebugOnlyTest]*\**" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.props" />
|
||||
<Compile Include="..\BarotraumaShared\**\*.cs" />
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.6.19.1</Version>
|
||||
<Version>1.7.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
@@ -59,7 +59,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml;..\BarotraumaShared\LocalMods\[DebugOnlyTest]*\**" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.props" />
|
||||
<Compile Include="..\BarotraumaShared\**\*.cs" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -85,5 +86,16 @@ namespace Barotrauma
|
||||
{
|
||||
GameServer.Log($"{GameServer.CharacterLogName(this)} has gained the talent '{talentPrefab.DisplayName}'", ServerLog.MessageType.Talent);
|
||||
}
|
||||
|
||||
private void SyncInGameEditables(Item item)
|
||||
{
|
||||
foreach (ItemComponent itemComponent in item.Components)
|
||||
{
|
||||
foreach (var serializableProperty in SerializableProperty.GetProperties<InGameEditable>(itemComponent))
|
||||
{
|
||||
GameMain.Server.CreateEntityEvent(item, new Item.ChangePropertyEventData(serializableProperty, itemComponent));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,21 +427,33 @@ namespace Barotrauma
|
||||
tempBuffer.WriteSingle(SimPosition.Y);
|
||||
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
|
||||
AnimController.Collider.LinearVelocity = new Vector2(
|
||||
MathHelper.Clamp(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel),
|
||||
MathHelper.Clamp(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel));
|
||||
NetConfig.Quantize(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel, 12),
|
||||
NetConfig.Quantize(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel, 12));
|
||||
tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel, 12);
|
||||
tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel, 12);
|
||||
|
||||
bool fixedRotation = AnimController.Collider.FarseerBody.FixedRotation || !AnimController.Collider.PhysEnabled;
|
||||
AnimController.TargetMovement = new Vector2(
|
||||
NetConfig.Quantize(AnimController.TargetMovement.X, -Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12),
|
||||
NetConfig.Quantize(AnimController.TargetMovement.Y, -Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12));
|
||||
tempBuffer.WriteRangedSingle(AnimController.TargetMovement.X, -Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12);
|
||||
tempBuffer.WriteRangedSingle(AnimController.TargetMovement.Y, -Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12);
|
||||
|
||||
bool fixedRotation = AnimController.Collider.FarseerBody.FixedRotation;
|
||||
tempBuffer.WriteBoolean(fixedRotation);
|
||||
if (!fixedRotation)
|
||||
{
|
||||
tempBuffer.WriteSingle(AnimController.Collider.Rotation);
|
||||
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
|
||||
AnimController.Collider.AngularVelocity = NetConfig.Quantize(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel, 8);
|
||||
AnimController.Collider.AngularVelocity =
|
||||
AnimController.Collider.PhysEnabled ?
|
||||
0.0f :
|
||||
NetConfig.Quantize(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel, 8);
|
||||
tempBuffer.WriteRangedSingle(MathHelper.Clamp(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel), -MaxAngularVel, MaxAngularVel, 8);
|
||||
}
|
||||
|
||||
|
||||
tempBuffer.WriteBoolean(AnimController.IgnorePlatforms);
|
||||
|
||||
bool writeStatus = healthUpdateTimer <= 0.0f;
|
||||
tempBuffer.WriteBoolean(writeStatus);
|
||||
if (writeStatus)
|
||||
|
||||
70
Barotrauma/BarotraumaServer/ServerSource/Characters/Limb.cs
Normal file
70
Barotrauma/BarotraumaServer/ServerSource/Characters/Limb.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Limb : ISerializableEntity, ISpatialEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// An invisible "ghost body" used for doing lag compensation server side by allowing clients' shots to hit bodies at the positions where
|
||||
/// they "used to be" back when the client fired a weapon.
|
||||
/// </summary>
|
||||
public PhysicsBody LagCompensatedBody { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A queue of past positions of the limb.
|
||||
/// </summary>
|
||||
public Queue<PosInfo> MemState { get; } = new Queue<PosInfo>();
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
{
|
||||
LagCompensatedBody = new PhysicsBody(Params, findNewContacts: false)
|
||||
{
|
||||
BodyType = FarseerPhysics.BodyType.Static,
|
||||
CollisionCategories = Physics.CollisionLagCompensationBody,
|
||||
CollidesWith = Physics.CollisionNone,
|
||||
UserData = this
|
||||
};
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
if (GameMain.Server == null) { return; }
|
||||
|
||||
MemState.Enqueue(new PosInfo(body.SimPosition, body.Rotation, body.LinearVelocity, body.AngularVelocity, (float)Timing.TotalTime));
|
||||
|
||||
//clear old states
|
||||
while (
|
||||
MemState.Any() &&
|
||||
MemState.Peek().Timestamp < Timing.TotalTime - GameMain.Server.ServerSettings.MaxLagCompensationSeconds)
|
||||
{
|
||||
MemState.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetLagCompensatedBodyPositions(Client client)
|
||||
{
|
||||
if (GameMain.Server == null) { return; }
|
||||
//convert from milliseconds to seconds, assume latency is symmetrical (time from client to server is half of the roundtrip time / ping)
|
||||
float latency = client.Ping / 1000.0f / 2;
|
||||
float time = (float)Timing.TotalTime - MathUtils.Min(latency, GameMain.Server.ServerSettings.MaxLagCompensationSeconds);
|
||||
|
||||
foreach (var character in Character.CharacterList)
|
||||
{
|
||||
foreach (var limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.body.Enabled == false || limb.IgnoreCollisions) { continue; }
|
||||
var matchingState = limb.MemState.FirstOrDefault(l => l.Timestamp <= time);
|
||||
if (matchingState == null) { continue; }
|
||||
limb.LagCompensatedBody.SetTransformIgnoreContacts(matchingState.Position, matchingState.Rotation ?? 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void RemoveProjSpecific()
|
||||
{
|
||||
LagCompensatedBody.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2729,6 +2729,11 @@ namespace Barotrauma
|
||||
{
|
||||
GameMain.Server.CreateEntityEvent(wall);
|
||||
}
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.IdFreed) { continue; }
|
||||
hull.CreateStatusEvent();
|
||||
}
|
||||
}));
|
||||
commands.Add(new Command("stallfiletransfers", "stallfiletransfers [seconds]: A debug command that makes all file transfers take at least the specified duration.", (string[] args) =>
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -60,20 +61,35 @@ namespace Barotrauma
|
||||
|
||||
private bool IsBlockedByAnotherConversation(IEnumerable<Entity> targets, float duration)
|
||||
{
|
||||
foreach (Entity e in targets)
|
||||
if (targets == null || targets.None())
|
||||
{
|
||||
if (e is not Character character || !character.IsRemotePlayer) { continue; }
|
||||
Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == character);
|
||||
if (targetClient != null)
|
||||
//if the action doesn't target anyone in specific, it's shown to every client
|
||||
foreach (var client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (lastActiveAction.ContainsKey(targetClient) &&
|
||||
lastActiveAction[targetClient].ParentEvent != ParentEvent &&
|
||||
Timing.TotalTime < lastActiveAction[targetClient].lastActiveTime + duration)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (IsBlockedByAnotherConversation(client, duration)) { return true; }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Entity e in targets)
|
||||
{
|
||||
if (e is not Character character || !character.IsRemotePlayer) { continue; }
|
||||
Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == character);
|
||||
if (targetClient != null && IsBlockedByAnotherConversation(targetClient, duration)) { return true; }
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsBlockedByAnotherConversation(Client targetClient, float duration)
|
||||
{
|
||||
if (lastActiveAction.ContainsKey(targetClient) &&
|
||||
!lastActiveAction[targetClient].ParentEvent.IsFinished &&
|
||||
lastActiveAction[targetClient].ParentEvent != ParentEvent &&
|
||||
Timing.TotalTime < lastActiveAction[targetClient].lastActiveTime + duration)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -91,6 +107,7 @@ namespace Barotrauma
|
||||
{
|
||||
targetClients.Add(targetClient);
|
||||
lastActiveAction[targetClient] = this;
|
||||
lastActiveTime = Timing.TotalTime;
|
||||
ServerWrite(speaker, targetClient, interrupt);
|
||||
}
|
||||
}
|
||||
@@ -105,6 +122,7 @@ namespace Barotrauma
|
||||
{
|
||||
targetClients.Add(c);
|
||||
lastActiveAction[c] = this;
|
||||
lastActiveTime = Timing.TotalTime;
|
||||
ServerWrite(speaker, c, interrupt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -193,6 +194,26 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void AddToScore(CharacterTeamType team, int amount)
|
||||
{
|
||||
if (!HasWinScore) { return; }
|
||||
int index;
|
||||
switch (team)
|
||||
{
|
||||
case CharacterTeamType.Team1:
|
||||
index = 0;
|
||||
break;
|
||||
case CharacterTeamType.Team2:
|
||||
index = 1;
|
||||
break;
|
||||
default:
|
||||
DebugConsole.AddSafeError($"Attempted to increase the score of an invalid team ({team}).");
|
||||
return;
|
||||
}
|
||||
Scores[index] = MathHelper.Clamp(Scores[index] + amount, 0, WinScore);
|
||||
GameMain.Server?.UpdateMissionState(this);
|
||||
}
|
||||
|
||||
private void AddKill(Character character)
|
||||
{
|
||||
kills.Add(new KillCount(character, character.CauseOfDeath?.Killer));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -12,9 +13,9 @@ namespace Barotrauma
|
||||
{
|
||||
item.WriteSpawnData(msg,
|
||||
item.ID,
|
||||
parentInventoryIDs.ContainsKey(item) ? parentInventoryIDs[item] : Entity.NullEntityID,
|
||||
parentItemContainerIndices.ContainsKey(item) ? parentItemContainerIndices[item] : (byte)0,
|
||||
inventorySlotIndices.ContainsKey(item) ? inventorySlotIndices[item] : -1);
|
||||
parentInventoryIDs.GetValueOrDefault(item, Entity.NullEntityID),
|
||||
parentItemContainerIndices.GetValueOrDefault(item, (byte)0),
|
||||
inventorySlotIndices.GetValueOrDefault(item, -1));
|
||||
}
|
||||
ServerWriteScanTargetStatus(msg);
|
||||
}
|
||||
@@ -30,7 +31,7 @@ namespace Barotrauma
|
||||
msg.WriteByte((byte)scanTargets.Count);
|
||||
foreach (var kvp in scanTargets)
|
||||
{
|
||||
msg.WriteUInt16(kvp.Key != null ? kvp.Key.ID : Entity.NullEntityID);
|
||||
msg.WriteUInt16(kvp.Key?.ID ?? Entity.NullEntityID);
|
||||
msg.WriteBoolean(kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -13,10 +14,18 @@ namespace Barotrauma.Items.Components
|
||||
msg.WriteBoolean(writeAttachData);
|
||||
if (!writeAttachData) { return; }
|
||||
|
||||
UInt16 attacherId = Entity.NullEntityID;
|
||||
if (TryExtractEventData(extraData, out AttachEventData attachEventData) &&
|
||||
attachEventData.Attacher != null)
|
||||
{
|
||||
attacherId = attachEventData.Attacher.ID;
|
||||
}
|
||||
|
||||
msg.WriteBoolean(Attached);
|
||||
msg.WriteSingle(body.SimPosition.X);
|
||||
msg.WriteSingle(body.SimPosition.Y);
|
||||
msg.WriteUInt16(item.Submarine?.ID ?? Entity.NullEntityID);
|
||||
msg.WriteUInt16(attacherId);
|
||||
}
|
||||
|
||||
public void ServerEventRead(IReadMessage msg, Client c)
|
||||
@@ -34,7 +43,7 @@ namespace Barotrauma.Items.Components
|
||||
AttachToWall();
|
||||
OnUsed.Invoke(new ItemUseInfo(item, c.Character));
|
||||
|
||||
item.CreateServerEvent(this);
|
||||
item.CreateServerEvent(this, new AttachEventData(simPosition, c.Character));
|
||||
c.Character.Inventory?.CreateNetworkEvent();
|
||||
|
||||
GameServer.Log(GameServer.CharacterLogName(c.Character) + " attached " + item.Name + " to a wall", ServerLog.MessageType.ItemInteraction);
|
||||
|
||||
@@ -424,7 +424,14 @@ namespace Barotrauma
|
||||
if (!components.Contains(ic)) { return; }
|
||||
|
||||
var eventData = new ComponentStateEventData(ic, extraData);
|
||||
if (!ic.ValidateEventData(eventData)) { throw new Exception($"Component event creation for the item \"{Prefab.Identifier}\" failed: {typeof(T).Name}.{nameof(ItemComponent.ValidateEventData)} returned false."); }
|
||||
if (!ic.ValidateEventData(eventData))
|
||||
{
|
||||
string errorMsg =
|
||||
$"Server-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.CreateServerEvent:ValidateEventData:{Prefab.Identifier}", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
GameMain.Server.CreateEntityEvent(this, eventData);
|
||||
}
|
||||
|
||||
@@ -435,10 +442,12 @@ namespace Barotrauma
|
||||
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
if (!(ic is IServerSerializable)) { continue; }
|
||||
var eventData = new ComponentStateEventData(ic, ic.ServerGetEventData());
|
||||
if (!ic.ValidateEventData(eventData)) { continue; }
|
||||
GameMain.Server.CreateEntityEvent(this, eventData);
|
||||
if (ic is not IServerSerializable) { continue; }
|
||||
var eventData = ic.ServerGetEventData();
|
||||
if (eventData == null) { continue; }
|
||||
var componentData = new ComponentStateEventData(ic, eventData);
|
||||
if (!ic.ValidateEventData(componentData)) { continue; }
|
||||
GameMain.Server.CreateEntityEvent(this, componentData);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -76,6 +76,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void CreateStatusEvent()
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new StatusEventData());
|
||||
}
|
||||
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (!(extraData is IEventData eventData)) { throw new Exception($"Malformed hull event: expected {nameof(Hull)}.{nameof(IEventData)}"); }
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace Barotrauma.Networking
|
||||
public UInt16 LastRecvLobbyUpdate
|
||||
= NetIdUtils.GetIdOlderThan(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
|
||||
public bool InitialLobbyUpdateSent;
|
||||
|
||||
public UInt16 LastSentChatMsgID = 0; //last msg this client said
|
||||
public UInt16 LastRecvChatMsgID = 0; //last msg this client knows about
|
||||
|
||||
@@ -166,8 +168,8 @@ namespace Barotrauma.Networking
|
||||
LastSentChatMsgID = 0;
|
||||
LastRecvChatMsgID = ChatMessage.LastID;
|
||||
|
||||
LastRecvLobbyUpdate = 0;
|
||||
|
||||
LastRecvLobbyUpdate = NetIdUtils.GetIdOlderThan(GameMain.NetLobbyScreen.LastUpdateID);
|
||||
InitialLobbyUpdateSent = false;
|
||||
LastRecvEntityEventID = 0;
|
||||
|
||||
UnreceivedEntityEventCount = 0;
|
||||
|
||||
@@ -1286,11 +1286,8 @@ namespace Barotrauma.Networking
|
||||
//check if midround syncing is needed due to missed unique events
|
||||
if (!midroundSyncingDone) { entityEventManager.InitClientMidRoundSync(c); }
|
||||
MissionAction.NotifyMissionsUnlockedThisRound(c);
|
||||
if (GameMain.GameSession.Campaign is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
mpCampaign.SendCrewState();
|
||||
}
|
||||
else if (GameMain.GameSession.GameMode is PvPMode)
|
||||
|
||||
if (GameMain.GameSession.GameMode is PvPMode)
|
||||
{
|
||||
if (c.TeamID == CharacterTeamType.None)
|
||||
{
|
||||
@@ -1299,6 +1296,10 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GameMain.GameSession.Campaign is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
mpCampaign.SendCrewState();
|
||||
}
|
||||
//everyone's in team 1 in non-pvp game modes
|
||||
c.TeamID = CharacterTeamType.Team1;
|
||||
}
|
||||
@@ -2231,12 +2232,13 @@ namespace Barotrauma.Networking
|
||||
outmsg.WriteUInt16((UInt16)settingsBuf.LengthBytes);
|
||||
outmsg.WriteBytes(settingsBuf.Buffer, 0, settingsBuf.LengthBytes);
|
||||
|
||||
outmsg.WriteBoolean(c.LastRecvLobbyUpdate < 1);
|
||||
if (c.LastRecvLobbyUpdate < 1)
|
||||
outmsg.WriteBoolean(!c.InitialLobbyUpdateSent);
|
||||
if (!c.InitialLobbyUpdateSent)
|
||||
{
|
||||
isInitialUpdate = true;
|
||||
initialUpdateBytes = outmsg.LengthBytes;
|
||||
ClientWriteInitial(c, outmsg);
|
||||
c.InitialLobbyUpdateSent = true;
|
||||
initialUpdateBytes = outmsg.LengthBytes - initialUpdateBytes;
|
||||
}
|
||||
outmsg.WriteString(GameMain.NetLobbyScreen.SelectedSub.Name);
|
||||
@@ -3106,6 +3108,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
msg.WriteString(levelSeed);
|
||||
msg.WriteSingle(ServerSettings.SelectedLevelDifficulty);
|
||||
msg.WriteIdentifier(ServerSettings.Biome == "Random".ToIdentifier() ? Identifier.Empty : ServerSettings.Biome);
|
||||
msg.WriteString(gameSession.SubmarineInfo.Name);
|
||||
msg.WriteString(gameSession.SubmarineInfo.MD5Hash.StringRepresentation);
|
||||
var selectedShuttle = GameStarted && RespawnManager != null && RespawnManager.UsingShuttle ?
|
||||
@@ -3740,13 +3743,13 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else //msg sent by an AI character
|
||||
{
|
||||
senderName = senderCharacter.Name;
|
||||
senderName = senderCharacter.DisplayName;
|
||||
}
|
||||
}
|
||||
else //msg sent by a client
|
||||
{
|
||||
senderCharacter = senderClient.Character;
|
||||
senderName = senderCharacter == null ? senderClient.Name : senderCharacter.Name;
|
||||
senderName = senderCharacter == null ? senderClient.Name : senderCharacter.DisplayName;
|
||||
if (type == ChatMessageType.Private)
|
||||
{
|
||||
if (senderCharacter != null && !senderCharacter.IsDead || targetClient.Character != null && !targetClient.Character.IsDead)
|
||||
|
||||
@@ -151,11 +151,11 @@ namespace Barotrauma
|
||||
//increase the strength of the herpes affliction in steps instead of linearly
|
||||
//otherwise clients could determine their exact karma value from the strength
|
||||
float herpesStrength = 0.0f;
|
||||
if (client.Karma < 20)
|
||||
if (client.Karma < HerpesThreshold * 0.5f)
|
||||
herpesStrength = 100.0f;
|
||||
else if (client.Karma < 30)
|
||||
else if (client.Karma < HerpesThreshold * 0.75f)
|
||||
herpesStrength = 60.0f;
|
||||
else if (client.Karma < 40.0f)
|
||||
else if (client.Karma < HerpesThreshold)
|
||||
herpesStrength = 30.0f;
|
||||
|
||||
var existingAffliction = client.Character.CharacterHealth.GetAffliction<AfflictionSpaceHerpes>(AfflictionPrefab.SpaceHerpesType);
|
||||
|
||||
@@ -229,7 +229,9 @@ namespace Barotrauma.Networking
|
||||
GameMain.GameSession.RoundDuration > NetConfig.RoundStartSyncDuration)
|
||||
{
|
||||
lastWarningTime = Timing.TotalTime;
|
||||
GameServer.Log("WARNING: ServerEntityEventManager is lagging behind! Last sent id: " + lastSentToAnyone.ToString() + ", latest create id: " + ID.ToString(), ServerLog.MessageType.ServerMessage);
|
||||
string warningMsg = $"WARNING: ServerEntityEventManager is lagging behind! Last sent id: {lastSentToAnyone}, latest create id: {ID}";
|
||||
warningMsg += "\n" + GetHighEventCountsWarning(events, maxEventsToList: 3);
|
||||
GameServer.Log(warningMsg, ServerLog.MessageType.ServerMessage);
|
||||
events.ForEach(e => e.ResetCreateTime());
|
||||
//TODO: reset clients if this happens, maybe do it if a majority are behind rather than all of them?
|
||||
}
|
||||
@@ -323,30 +325,20 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
//too many events for one packet
|
||||
//(normal right after a round has just started, don't show a warning if it's been less than 10 seconds)
|
||||
if (eventsToSync.Count > 200 && GameMain.GameSession != null && GameMain.GameSession.RoundDuration > 10.0)
|
||||
//(normal right after a round has just started, don't show a warning if it's been less than 30 seconds)
|
||||
if (eventsToSync.Count > 200 && GameMain.GameSession != null && GameMain.GameSession.RoundDuration > 30.0)
|
||||
{
|
||||
if (eventsToSync.Count > 200 && !client.NeedsMidRoundSync && Timing.TotalTime > lastEventCountHighWarning + 2.0)
|
||||
{
|
||||
Color color = eventsToSync.Count > 500 ? Color.Red : Color.Orange;
|
||||
if (eventsToSync.Count < 300) { color = Color.Yellow; }
|
||||
string warningMsg = "WARNING: event count very high: " + eventsToSync.Count;
|
||||
|
||||
var sortedEvents = eventsToSync.GroupBy(e => e.Entity.ToString())
|
||||
.Select(e => new { Value = e.Key, Count = e.Count() })
|
||||
.OrderByDescending(e => e.Count);
|
||||
|
||||
int count = 1;
|
||||
foreach (var sortedEvent in sortedEvents)
|
||||
{
|
||||
warningMsg += "\n" + count + ". " + (sortedEvent.Value?.ToString() ?? "null") + " x" + sortedEvent.Count;
|
||||
count++;
|
||||
if (count > 3) { break; }
|
||||
}
|
||||
warningMsg += "\n" + GetHighEventCountsWarning(eventsToSync, maxEventsToList: 3);
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
GameServer.Log(warningMsg, ServerLog.MessageType.Error);
|
||||
}
|
||||
server.SendConsoleMessage(warningMsg, client, color);
|
||||
DebugConsole.NewMessage(warningMsg, color);
|
||||
lastEventCountHighWarning = Timing.TotalTime;
|
||||
}
|
||||
@@ -373,6 +365,31 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private string GetHighEventCountsWarning(IEnumerable<NetEntityEvent> events, int maxEventsToList)
|
||||
{
|
||||
string warningMsg = string.Empty;
|
||||
|
||||
var sortedEvents = events.GroupBy(e => e.Entity.ToString())
|
||||
.Select(e => new { Value = e.First(), Count = e.Count() })
|
||||
.OrderByDescending(e => e.Count);
|
||||
|
||||
int count = 1;
|
||||
foreach (var sortedEvent in sortedEvents)
|
||||
{
|
||||
Entity targetEntity = sortedEvent.Value.Entity;
|
||||
if (!warningMsg.IsNullOrEmpty()) { warningMsg += "\n"; }
|
||||
warningMsg += count + ". " + (targetEntity?.ToString() ?? "null") + " x" + sortedEvent.Count;
|
||||
if (targetEntity != null && targetEntity.ContentPackage != ContentPackageManager.VanillaCorePackage)
|
||||
{
|
||||
warningMsg += $" (content package: {targetEntity.ContentPackage.Name})";
|
||||
}
|
||||
count++;
|
||||
if (count > maxEventsToList) { break; }
|
||||
}
|
||||
|
||||
return warningMsg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of events that should be sent to the client from the eventList
|
||||
/// </summary>
|
||||
|
||||
@@ -447,7 +447,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (pendingClient.AccountInfo.AccountId != packet.AccountId)
|
||||
{
|
||||
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationFailed));
|
||||
rejectClient();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -503,10 +503,16 @@ namespace Barotrauma.Networking
|
||||
pendingClient.AuthSessionStarted = true;
|
||||
TaskPool.Add($"{nameof(LidgrenServerPeer)}.ProcessAuth", authenticator.VerifyTicket(authTicket), t =>
|
||||
{
|
||||
if (!t.TryGetResult(out AccountInfo accountInfo)
|
||||
|| accountInfo.IsNone)
|
||||
if (!t.TryGetResult(out AccountInfo accountInfo) || accountInfo.IsNone)
|
||||
{
|
||||
rejectClient();
|
||||
if (GameMain.Server.ServerSettings.RequireAuthentication)
|
||||
{
|
||||
rejectClient();
|
||||
}
|
||||
else
|
||||
{
|
||||
acceptClient(new AccountInfo(new UnauthenticatedAccountId(packet.Name)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
var property = netProperties[key];
|
||||
property.SyncValue();
|
||||
if (NetIdUtils.IdMoreRecent(property.LastUpdateID, c.LastRecvLobbyUpdate))
|
||||
if (NetIdUtils.IdMoreRecent(property.LastUpdateID, c.LastRecvLobbyUpdate) || !c.InitialLobbyUpdateSent)
|
||||
{
|
||||
outMsg.WriteUInt32(key);
|
||||
netProperties[key].Write(outMsg);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.6.19.1</Version>
|
||||
<Version>1.7.7.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
@@ -61,7 +61,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
|
||||
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml;..\BarotraumaShared\LocalMods\[DebugOnlyTest]*\**" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.cs" />
|
||||
<Content Remove="..\BarotraumaShared\**\*.props" />
|
||||
<Compile Include="..\BarotraumaShared\**\*.cs" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<CampaignSettingPresets>
|
||||
<CampaignSettingDefinitions>
|
||||
<StartingBalanceAmount high="10000" medium="8500" low="6000" />
|
||||
@@ -20,6 +20,7 @@
|
||||
OxygenMultiplier="1.2"
|
||||
FuelMultiplier="1.2"
|
||||
MissionRewardMultiplier="1.0"
|
||||
ExperienceRewardMultiplier="1.0"
|
||||
ShopPriceMultiplier="0.9"
|
||||
ShipyardPriceMultiplier="0.9"
|
||||
RepairFailMultiplier="1.0"
|
||||
@@ -38,6 +39,7 @@
|
||||
OxygenMultiplier="1.0"
|
||||
FuelMultiplier="1.0"
|
||||
MissionRewardMultiplier="1.0"
|
||||
ExperienceRewardMultiplier="1.0"
|
||||
ShopPriceMultiplier="1.0"
|
||||
ShipyardPriceMultiplier="1.0"
|
||||
RepairFailMultiplier="1.0"
|
||||
@@ -56,6 +58,7 @@
|
||||
OxygenMultiplier="0.7"
|
||||
FuelMultiplier="0.9"
|
||||
MissionRewardMultiplier="1.0"
|
||||
ExperienceRewardMultiplier="1.0"
|
||||
ShopPriceMultiplier="1.5"
|
||||
ShipyardPriceMultiplier="1.5"
|
||||
RepairFailMultiplier="2.0"
|
||||
@@ -74,6 +77,7 @@
|
||||
OxygenMultiplier="0.4"
|
||||
FuelMultiplier="0.8"
|
||||
MissionRewardMultiplier="0.8"
|
||||
ExperienceRewardMultiplier="0.8"
|
||||
ShopPriceMultiplier="2.0"
|
||||
ShipyardPriceMultiplier="2.0"
|
||||
RepairFailMultiplier="5.0"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<CrawlerRun footangle="-15.42238" Flip="True" FlipCooldown="1" FlipDelay="0.5" HeadMoveForce="10" TorsoMoveForce="15" FootMoveForce="5" TailTorque="50" LegTorque="0" ColliderStandAngle="90" FootAngles="10: 1.515006,15: 1.5882496,11: 1.9198622" TailAngle="85" StepSize="0.54825926,0.37847614" HeadPosition="0.799391" TorsoPosition="0.9479357" StepLiftHeadMultiplier="0.5" StepLiftAmount="20" MultiplyByDir="True" StepLiftOffset="0.5000001" StepLiftFrequency="1" BackwardsMovementMultiplier="0.75" MovementSpeed="5.024437" CycleSpeed="2" HeadAngle="7" TorsoAngle="4" HeadTorque="400" TorsoTorque="100" FootTorque="25" AnimationType="Run" ArmIKStrength="1" HandIKStrength="1" type="Crawler" />
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user