v1.4.4.1 (Blood in the Water Update)

This commit is contained in:
Regalis11
2024-04-24 18:09:05 +03:00
parent 89b91d1c3e
commit ff1b8951a7
397 changed files with 15250 additions and 6479 deletions
@@ -1,3 +1,4 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Barotrauma.Particles;
@@ -9,7 +10,6 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -42,6 +42,8 @@ namespace Barotrauma
private set;
} = true;
public bool ShowInteractionLabels { get; private set; }
//the Character that the player is currently controlling
private static Character controlled;
@@ -230,14 +232,51 @@ namespace Barotrauma
}
}
private float pressureEffectTimer;
private readonly List<ObjectiveEntity> activeObjectiveEntities = new List<ObjectiveEntity>();
public IEnumerable<ObjectiveEntity> ActiveObjectiveEntities
{
get { return activeObjectiveEntities; }
}
private static readonly List<SpeechBubble> speechBubbles = new List<SpeechBubble>();
private SpeechBubble textlessSpeechBubble;
sealed class SpeechBubble
{
public float LifeTime;
public Vector2 PrevPosition;
public Vector2 Position;
public Vector2 DrawPosition;
public float MoveUpAmount;
public readonly string Text;
public readonly Character Character;
public readonly Submarine Submarine;
public readonly Vector2 TextSize;
public Color Color;
public bool Moving;
public SpeechBubble(Character character, float lifeTime, Color color, string text = "")
{
Text = ToolBox.WrapText(text, GUI.IntScale(300), GUIStyle.SmallFont.GetFontForStr(text));
TextSize = GUIStyle.SmallFont.MeasureString(Text);
Character = character;
Position = GetDesiredPosition();
Submarine = character.Submarine;
LifeTime = lifeTime;
Color = color;
}
public Vector2 GetDesiredPosition()
{
return Character.Position + Vector2.UnitY * 100;
}
}
private float pressureEffectTimer;
partial void InitProjSpecific(ContentXElement mainElement)
{
soundTimer = Rand.Range(0.0f, Params.SoundInterval);
@@ -272,6 +311,9 @@ namespace Barotrauma
}
}
private readonly List<Item> previousInteractablesInRange = new();
private readonly List<Item> interactablesInRange = new();
private bool wasFiring;
/// <summary>
@@ -279,6 +321,7 @@ namespace Barotrauma
/// </summary>
public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
{
if (DisableControls || GUI.InputBlockingMenuOpen)
{
foreach (Key key in keys)
@@ -316,6 +359,13 @@ namespace Barotrauma
}
}
ShowInteractionLabels = keys[(int)InputType.ShowInteractionLabels].Held;
if (ShowInteractionLabels)
{
focusedItem = InteractionLabelManager.HoveredItem;
}
//if we were firing (= pressing the aim and shoot keys at the same time)
//and the fire key is the same as Select or Use, reset the key to prevent accidentally selecting/using items
if (wasFiring && !keys[(int)InputType.Shoot].Held)
@@ -549,19 +599,61 @@ namespace Barotrauma
if (Lights.LightManager.ViewTarget == this) { Lights.LightManager.ViewTarget = null; }
}
private void UpdateInteractablesInRange()
{
// keep two lists to detect changes to the current state of interactables in range
previousInteractablesInRange.Clear();
previousInteractablesInRange.AddRange(interactablesInRange);
interactablesInRange.Clear();
//use the list of visible entities if it exists
var entityList = Submarine.VisibleEntities ?? Item.ItemList;
foreach (MapEntity entity in entityList)
{
if (entity is not Item item) { continue; }
if (item.body != null && !item.body.Enabled) { continue; }
if (item.ParentInventory != null) { continue; }
if (item.Prefab.RequireCampaignInteract &&
item.CampaignInteractionType == CampaignMode.InteractionType.None)
{
continue;
}
if (Screen.Selected is SubEditorScreen { WiringMode: true } &&
item.GetComponent<ConnectionPanel>() == null)
{
continue;
}
if (CanInteractWith(item))
{
interactablesInRange.Add(item);
}
}
if (!interactablesInRange.SequenceEqual(previousInteractablesInRange))
{
InteractionLabelManager.RefreshInteractablesInRange(interactablesInRange);
}
}
private readonly List<Item> debugInteractablesInRange = new List<Item>();
private readonly List<Item> debugInteractablesAtCursor = new List<Item>();
private readonly List<(Item item, float dist)> debugInteractablesNearCursor = new List<(Item item, float dist)>();
/// <summary>
/// Finds the front (lowest depth) interactable item at a position. "Interactable" in this case means that the character can "reach" the item.
/// Finds the front (lowest depth) interactable item at a position. "Interactable" in this case means that the character can "reach" the item.
/// </summary>
/// <param name="character">The Character who is looking for the interactable item, only items that are close enough to this character are returned</param>
/// <param name="simPosition">The item at the simPosition, with the lowest depth, is returned</param>
/// <param name="allowFindingNearestItem">If this is true and an item cannot be found at simPosition then a nearest item will be returned if possible</param>
/// <param name="hull">If a hull is specified, only items within that hull are returned</param>
public Item FindItemAtPosition(Vector2 simPosition, float aimAssistModifier = 0.0f, Item[] ignoredItems = null)
/// <param name="itemCollection">Item collection to look in</param>
/// <param name="simPosition">sim position for distance comparison (such as mouse position)</param>
/// <param name="aimAssistModifier">aim assist modifier</param>
/// <returns></returns>
public Item FindClosestItem(List<Item> itemCollection, Vector2 simPosition, float aimAssistModifier = 0.0f)
{
if (Submarine != null)
{
@@ -580,24 +672,11 @@ namespace Barotrauma
float aimAssistAmount = SelectedItem == null ? 100.0f * aimAssistModifier : 1.0f;
Vector2 displayPosition = ConvertUnits.ToDisplayUnits(simPosition);
//use the list of visible entities if it exists
var entityList = Submarine.VisibleEntities ?? Item.ItemList;
Item closestItem = null;
float closestItemDistance = Math.Max(aimAssistAmount, 2.0f);
foreach (MapEntity entity in entityList)
foreach (var item in itemCollection)
{
if (entity is not Item item)
{
continue;
}
if (item.body != null && !item.body.Enabled) { continue; }
if (item.ParentInventory != null) { continue; }
if (ignoredItems != null && ignoredItems.Contains(item)) { continue; }
if (item.Prefab.RequireCampaignInteract && item.CampaignInteractionType == CampaignMode.InteractionType.None) { continue; }
if (Screen.Selected is SubEditorScreen editor && editor.WiringMode && item.GetComponent<ConnectionPanel>() == null) { continue; }
if (draggingItemToWorld)
{
if (item.OwnInventory == null ||
@@ -644,7 +723,7 @@ namespace Barotrauma
distanceToItem = 2.0f + Vector2.Distance(rectIntersectionPoint, displayPosition);
}
}
if (distanceToItem > closestItemDistance) { continue; }
if (!CanInteractWith(item)) { continue; }
@@ -661,7 +740,7 @@ namespace Barotrauma
Character closestCharacter = null;
maxDist = ConvertUnits.ToSimUnits(maxDist);
float closestDist = maxDist * maxDist;
float closestDist = maxDist;
foreach (Character c in CharacterList)
{
if (!CanInteractWith(c, checkVisibility: false) || (c.AnimController?.SimplePhysicsEnabled ?? true)) { continue; }
@@ -704,6 +783,12 @@ namespace Barotrauma
}
guiMessages.RemoveAll(m => m.Timer >= m.Lifetime);
if (textlessSpeechBubble != null)
{
textlessSpeechBubble.LifeTime -= deltaTime;
if (textlessSpeechBubble.LifeTime <= 0) { textlessSpeechBubble = null; }
}
if (!enabled) { return; }
if (!IsIncapacitated)
@@ -890,13 +975,6 @@ namespace Barotrauma
pos.Y = -pos.Y;
if (speechBubbleTimer > 0.0f)
{
GUIStyle.SpeechBubbleIcon.Value.Sprite.Draw(spriteBatch, pos - Vector2.UnitY * 5,
speechBubbleColor * Math.Min(speechBubbleTimer, 1.0f), 0.0f,
Math.Min(speechBubbleTimer, 1.0f));
}
if (this == controlled)
{
if (DebugDrawInteract)
@@ -921,113 +999,232 @@ namespace Barotrauma
ToolBox.GradientLerp(dist, GUIStyle.Red, GUIStyle.Orange, GUIStyle.Green), width: 2);
}
}
return;
}
float hoverRange = 300.0f;
float fadeOutRange = 200.0f;
float cursorDist = Vector2.Distance(WorldPosition, cam.ScreenToWorld(PlayerInput.MousePosition));
float hudInfoAlpha =
CampaignInteractionType == CampaignMode.InteractionType.None ?
MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f) :
1.0f;
if (!GUI.DisableCharacterNames && hudInfoVisible &&
(controlled == null || this != controlled.FocusedCharacter || IsPet) && cam.Zoom > 0.4f)
else
{
if (info != null)
float hoverRange = 300.0f;
float fadeOutRange = 200.0f;
float cursorDist = Vector2.Distance(WorldPosition, cam.ScreenToWorld(PlayerInput.MousePosition));
float hudInfoAlpha =
CampaignInteractionType == CampaignMode.InteractionType.None ?
MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f) :
1.0f;
if (!GUI.DisableCharacterNames && hudInfoVisible &&
(controlled == null || this != controlled.FocusedCharacter || IsPet) && cam.Zoom > 0.4f)
{
LocalizedString name = Info.DisplayName;
if (controlled == null && name != Info.Name)
{
name += " " + TextManager.Get("Disguised");
}
else if (Info.Title != null && TeamID != CharacterTeamType.Team1)
if (info != null)
{
name += '\n' + Info.Title;
LocalizedString name = Info.DisplayName;
if (controlled == null && name != Info.Name)
{
name += " " + TextManager.Get("Disguised");
}
else if (Info.Title != null && TeamID != CharacterTeamType.Team1)
{
name += '\n' + Info.Title;
}
Vector2 nameSize = GUIStyle.Font.MeasureString(name);
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
Color nameColor = GetNameColor();
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
namePos *= screenSize / viewportSize;
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
namePos *= viewportSize / screenSize;
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
{
var iconStyle = GUIStyle.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
if (iconStyle != null)
{
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y;
nameColor = iconStyle.Color;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
float iconScale = (30.0f / icon.Sprite.size.X / cam.Zoom) * GUI.Scale;
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
}
}
GUIStyle.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
GUIStyle.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
if (GameMain.DebugDraw)
{
GUIStyle.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
}
}
Vector2 nameSize = GUIStyle.Font.MeasureString(name);
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
Color nameColor = GetNameColor();
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
namePos *= screenSize / viewportSize;
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
namePos *= viewportSize / screenSize;
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
var petBehavior = (AIController as EnemyAIController)?.PetBehavior;
if (petBehavior != null && !IsDead && !IsUnconscious)
{
var iconStyle = GUIStyle.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
var petStatus = petBehavior.GetCurrentStatusIndicatorType();
var iconStyle = GUIStyle.GetComponentStyle("PetIcon." + petStatus);
if (iconStyle != null)
{
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y;
nameColor = iconStyle.Color;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
float iconScale = (30.0f / icon.Sprite.size.X / cam.Zoom) * GUI.Scale;
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
}
}
}
GUIStyle.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
GUIStyle.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
if (GameMain.DebugDraw)
if (IsDead) { return; }
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
if (healthBarMode != EnemyHealthBarMode.ShowAll)
{
if (Controlled == null)
{
GUIStyle.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
if (!IsOnPlayerTeam) { return; }
}
else
{
if (!HumanAIController.IsFriendly(Controlled, this) ||
(AIController is HumanAIController humanAi && humanAi.ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective && HumanAIController.IsFriendly(Controlled, combatObjective.Enemy)))
{
return;
}
}
}
var petBehavior = (AIController as EnemyAIController)?.PetBehavior;
if (petBehavior != null && !IsDead && !IsUnconscious)
if (Params.ShowHealthBar && CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
{
var petStatus = petBehavior.GetCurrentStatusIndicatorType();
var iconStyle = GUIStyle.GetComponentStyle("PetIcon." + petStatus);
if (iconStyle != null)
{
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
}
hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f));
Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y);
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f),
CharacterHealth.DisplayedVitality / MaxVitality,
Color.Lerp(GUIStyle.Red, GUIStyle.Green, CharacterHealth.DisplayedVitality / MaxVitality) * 0.8f * hudInfoAlpha,
new Color(0.5f, 0.57f, 0.6f, 1.0f) * hudInfoAlpha);
}
}
if (IsDead) { return; }
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
if (healthBarMode != EnemyHealthBarMode.ShowAll)
if (textlessSpeechBubble != null)
{
if (Controlled == null)
Vector2 iconPos = pos - Vector2.UnitY * 5;
GUIStyle.SpeechBubbleIcon.Value.Sprite.Draw(spriteBatch, iconPos,
textlessSpeechBubble.Color * Math.Min(textlessSpeechBubble.LifeTime, 1.0f), 0.0f,
Math.Min(textlessSpeechBubble.LifeTime, 1.0f));
}
}
public void ShowSpeechBubble(Color color, string text)
{
if (!GameSettings.CurrentConfig.ChatSpeechBubbles)
{
ShowTextlessSpeechBubble(1.0f, color);
return;
}
float duration = MathHelper.Lerp(1.0f, 8.0f, Math.Min(text.Length / 100.0f, 1.0f));
speechBubbles.Add(new SpeechBubble(this, duration, color, text));
textlessSpeechBubble = null;
}
public void ShowTextlessSpeechBubble(float duration, Color color)
{
if (speechBubbles.Any(sb => sb.Character == this)) { return; }
if (textlessSpeechBubble == null)
{
textlessSpeechBubble = new SpeechBubble(this, duration, color);
}
else
{
textlessSpeechBubble.Color = color;
textlessSpeechBubble.LifeTime = Math.Max(textlessSpeechBubble.LifeTime, duration);
}
}
public static void DrawSpeechBubbles(SpriteBatch spriteBatch, Camera cam)
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform);
foreach (var bubble in speechBubbles)
{
Vector2 iconPos = Timing.Interpolate(bubble.PrevPosition, bubble.Position);
iconPos += Vector2.UnitY * bubble.MoveUpAmount;
if (bubble.Submarine != null)
{
if (!IsOnPlayerTeam) { return; }
iconPos += bubble.Submarine.DrawPosition;
}
else
float alpha = 1.0f;
float mouseDist = Vector2.Distance(cam.WorldToScreen(iconPos), PlayerInput.MousePosition);
//treat the size of the bubble from corner to corner as the
float textSize = bubble.TextSize.Length();
if (mouseDist < textSize)
{
if (!HumanAIController.IsFriendly(Controlled, this) ||
(AIController is HumanAIController humanAi && humanAi.ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective && HumanAIController.IsFriendly(Controlled, combatObjective.Enemy)))
{
return;
alpha *= Math.Max(mouseDist / textSize, 0.5f);
}
iconPos.Y = -iconPos.Y;
if (GUIStyle.SpeechBubbleIconSliced.Value is { } speechBubbleIconSliced)
{
Vector2 bubbleSize = bubble.TextSize + Vector2.One * GUI.IntScale(15);
speechBubbleIconSliced.Draw(spriteBatch, new RectangleF(iconPos - bubbleSize / 2, bubbleSize), bubble.Color * Math.Min(bubble.LifeTime, 1.0f) * alpha);
}
GUI.DrawString(spriteBatch, iconPos - bubble.TextSize / 2, bubble.Text, bubble.Color * Math.Min(bubble.LifeTime, 1.0f) * alpha, font: GUIStyle.SmallFont);
}
spriteBatch.End();
}
static partial void UpdateSpeechBubbles(float deltaTime)
{
for (int i = speechBubbles.Count - 1; i >= 0; i--)
{
var bubble = speechBubbles[i];
bubble.LifeTime -= deltaTime;
if (bubble.LifeTime <= 0 || bubble.Character is { Removed: true })
{
speechBubbles.RemoveAt(i);
continue;
}
bubble.PrevPosition = bubble.Position;
Vector2 desiredPos = bubble.GetDesiredPosition();
Vector2 diff = desiredPos - bubble.Position;
float dist = diff.Length();
//how far the bubble needs to be from the desired position to start moving
const float MoveThreshold = 100.0f;
const float MaxSpeed = 1000.0f;
if (dist < 1)
{
bubble.Moving = false;
}
else if (dist > MoveThreshold || bubble.Moving)
{
Vector2 moveAmount = diff / dist * MathHelper.Clamp(dist * 5, 0, MaxSpeed) * deltaTime;
//slower vertical movement (don't want to interfere too much with the bubbles floating up
//and the overlap prevention which works vertically)
moveAmount.Y *= 0.1f;
bubble.Position += moveAmount;
bubble.Moving = true;
}
bubble.MoveUpAmount += deltaTime * 5.0f;
//go through the newer bubbles, move this one out of the way if one is overlapping
for (int j = i + 1; j < speechBubbles.Count; j++)
{
var otherBubble = speechBubbles[j];
{
if (Math.Abs(bubble.Position.X - otherBubble.Position.X) < (bubble.TextSize.X + otherBubble.TextSize.X) / 2 &&
Math.Abs(bubble.Position.Y - otherBubble.Position.Y) < (bubble.TextSize.Y + otherBubble.TextSize.Y) / 2 + 10)
{
bubble.Position += Vector2.UnitY * deltaTime * 50.0f;
}
}
}
}
if (Params.ShowHealthBar && CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
{
hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f));
Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y);
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f),
CharacterHealth.DisplayedVitality / MaxVitality,
Color.Lerp(GUIStyle.Red, GUIStyle.Green, CharacterHealth.DisplayedVitality / MaxVitality) * 0.8f * hudInfoAlpha,
new Color(0.5f, 0.57f, 0.6f, 1.0f) * hudInfoAlpha);
}
}
public Color GetNameColor()