v1.4.4.1 (Blood in the Water Update)
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user