v0.11.0.9

This commit is contained in:
Joonas Rikkonen
2020-12-09 16:34:16 +02:00
parent bbf06f0984
commit f433a7ba10
325 changed files with 13947 additions and 3652 deletions
@@ -328,9 +328,9 @@ namespace Barotrauma
{ {
previousOffset = offset; previousOffset = offset;
} }
//how much to zoom out (zoom completely out when offset is 1000) //how much to zoom out (zoom completely out when offset is 1000)
float zoomOutAmount = Math.Min(offset.Length() / 1000.0f, 1.0f); float zoomOutAmount = GetZoomAmount(offset);
//zoom amount when resolution is not taken into account //zoom amount when resolution is not taken into account
float unscaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount); float unscaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount);
//zoom with resolution taken into account (zoom further out on smaller resolutions) //zoom with resolution taken into account (zoom further out on smaller resolutions)
@@ -409,5 +409,15 @@ namespace Barotrauma
//Vector2 screenCoords = Vector2.Transform(coords, transform); //Vector2 screenCoords = Vector2.Transform(coords, transform);
return Vector2.Transform(coords, transform); return Vector2.Transform(coords, transform);
} }
private float GetZoomAmount(Vector2 offset)
{
return Math.Min(offset.Length() / 1000.0f, 1.0f);
}
public float GetZoomAmountFromPrevious()
{
return GetZoomAmount(previousOffset);
}
} }
} }
@@ -5,8 +5,6 @@ namespace Barotrauma
{ {
partial class HumanAIController : AIController partial class HumanAIController : AIController
{ {
public static bool debugai;
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
/*if (GameMain.GameSession != null && GameMain.GameSession.CrewManager != null) /*if (GameMain.GameSession != null && GameMain.GameSession.CrewManager != null)
@@ -520,7 +520,7 @@ namespace Barotrauma
foreach (Character c in CharacterList) foreach (Character c in CharacterList)
{ {
if (!CanInteractWith(c, checkVisibility: false)) continue; if (!CanInteractWith(c, checkVisibility: false) || (c.AnimController?.SimplePhysicsEnabled ?? true)) { continue; }
float dist = Vector2.DistanceSquared(mouseSimPos, c.SimPosition); float dist = Vector2.DistanceSquared(mouseSimPos, c.SimPosition);
if (dist < maxDist * maxDist && (closestCharacter == null || dist < closestDist)) if (dist < maxDist * maxDist && (closestCharacter == null || dist < closestDist))
@@ -686,7 +686,7 @@ namespace Barotrauma
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam) public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
{ {
if (!Enabled || InvisibleTimer > 0.0f) { return; } if (!Enabled || InvisibleTimer > 0.0f || (AnimController?.SimplePhysicsEnabled ?? true)) { return; }
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
@@ -810,7 +810,7 @@ namespace Barotrauma
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType); var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
if (iconStyle != null) if (iconStyle != null)
{ {
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f; Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos; Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y; iconPos.Y = -iconPos.Y;
nameColor = iconStyle.Color; nameColor = iconStyle.Color;
@@ -835,7 +835,7 @@ namespace Barotrauma
var iconStyle = GUI.Style.GetComponentStyle("PetIcon." + petStatus); var iconStyle = GUI.Style.GetComponentStyle("PetIcon." + petStatus);
if (iconStyle != null) if (iconStyle != null)
{ {
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f; Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos; Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y; iconPos.Y = -iconPos.Y;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First(); var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
@@ -11,15 +11,15 @@ namespace Barotrauma
{ {
class CharacterHUD class CharacterHUD
{ {
private static Dictionary<ISpatialEntity, int> orderIndicatorCount = new Dictionary<ISpatialEntity, int>(); private static readonly Dictionary<ISpatialEntity, int> orderIndicatorCount = new Dictionary<ISpatialEntity, int>();
const float ItemOverlayDelay = 1.0f; const float ItemOverlayDelay = 1.0f;
private static Item focusedItem; private static Item focusedItem;
private static float focusedItemOverlayTimer; private static float focusedItemOverlayTimer;
private static List<Item> brokenItems = new List<Item>(); private static readonly List<Item> brokenItems = new List<Item>();
private static float brokenItemsCheckTimer; private static float brokenItemsCheckTimer;
private static Dictionary<string, string> cachedHudTexts = new Dictionary<string, string>(); private static readonly Dictionary<string, string> cachedHudTexts = new Dictionary<string, string>();
private static GUIFrame hudFrame; private static GUIFrame hudFrame;
public static GUIFrame HUDFrame public static GUIFrame HUDFrame
@@ -126,6 +126,11 @@ namespace Barotrauma
{ {
character.Inventory.Update(deltaTime, cam); character.Inventory.Update(deltaTime, cam);
} }
else
{
character.Inventory.ClearSubInventories();
}
for (int i = 0; i < character.Inventory.Items.Length - 1; i++) for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
{ {
var item = character.Inventory.Items[i]; var item = character.Inventory.Items[i];
@@ -199,9 +204,18 @@ namespace Barotrauma
if (GameMain.GameSession?.CrewManager != null) if (GameMain.GameSession?.CrewManager != null)
{ {
orderIndicatorCount.Clear(); orderIndicatorCount.Clear();
foreach (Pair<Order, float> timedOrder in GameMain.GameSession.CrewManager.ActiveOrders) foreach (Pair<Order, float?> activeOrder in GameMain.GameSession.CrewManager.ActiveOrders)
{ {
DrawOrderIndicator(spriteBatch, cam, character, timedOrder.First, MathHelper.Clamp(timedOrder.Second / 10.0f, 0.2f, 1.0f)); if (activeOrder.Second.HasValue)
{
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First, iconAlpha: MathHelper.Clamp(activeOrder.Second.Value / 10.0f, 0.2f, 1.0f));
}
else
{
float iconAlpha = GetDistanceBasedIconAlpha(activeOrder.First.TargetSpatialEntity, maxDistance: 350.0f);
if (iconAlpha <= 0.0f) { continue; }
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First, iconAlpha: iconAlpha, createOffset: false, scaleMultiplier: 0.5f);
}
} }
if (character.CurrentOrder != null) if (character.CurrentOrder != null)
@@ -218,14 +232,18 @@ namespace Barotrauma
foreach (Item brokenItem in brokenItems) foreach (Item brokenItem in brokenItems)
{ {
if (brokenItem.NonInteractable) { continue; } if (brokenItem.NonInteractable) { continue; }
float dist = Vector2.Distance(character.WorldPosition, brokenItem.WorldPosition); float alpha = GetDistanceBasedIconAlpha(brokenItem);
Vector2 drawPos = brokenItem.DrawPosition;
float alpha = Math.Min((1000.0f - dist) / 1000.0f * 2.0f, 1.0f);
if (alpha <= 0.0f) continue; if (alpha <= 0.0f) continue;
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, GUI.BrokenIcon, GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUI.BrokenIcon,
Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha); Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
} }
float GetDistanceBasedIconAlpha(ISpatialEntity target, float maxDistance = 1000.0f)
{
float dist = Vector2.Distance(character.WorldPosition, target.WorldPosition);
return Math.Min((maxDistance - dist) / maxDistance * 2.0f, 1.0f);
}
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() == null))) if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() == null)))
{ {
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected) if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
@@ -362,8 +380,8 @@ namespace Barotrauma
(int)(HUDLayoutSettings.BottomRightInfoArea.X + HUDLayoutSettings.BottomRightInfoArea.Width * 0.05f), (int)(HUDLayoutSettings.BottomRightInfoArea.X + HUDLayoutSettings.BottomRightInfoArea.Width * 0.05f),
(int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f), (int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f),
(int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2), (int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2),
(int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f))); (int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f)), character.Info.IsDisguisedAsAnother);
character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true); character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true, character.Info.IsDisguisedAsAnother);
} }
mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud(); mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud();
if (mouseOnPortrait) if (mouseOnPortrait)
@@ -475,19 +493,12 @@ namespace Barotrauma
return character.ShouldLockHud(); return character.ShouldLockHud();
} }
private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f) private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f, bool createOffset = true, float scaleMultiplier = 1.0f)
{ {
if (order?.SymbolSprite == null) { return; } if (order?.SymbolSprite == null) { return; }
if (order.IsReport && order.OrderGiver != character && !order.HasAppropriateJob(character)) { return; }
if (order.TargetAllCharacters) ISpatialEntity target = order.ConnectedController?.Item ?? order.TargetSpatialEntity;
{
if (order.OrderGiver != character && !order.HasAppropriateJob(character))
{
return;
}
}
ISpatialEntity target = order.ConnectedController != null ? order.ConnectedController.Item : order.TargetSpatialEntity;
if (target == null) { return; } if (target == null) { return; }
//don't show the indicator if far away and not inside the same sub //don't show the indicator if far away and not inside the same sub
@@ -503,7 +514,7 @@ namespace Barotrauma
Vector2 drawPos = target is Entity ? (target as Entity).DrawPosition : Vector2 drawPos = target is Entity ? (target as Entity).DrawPosition :
target.Submarine == null ? target.Position : target.Position + target.Submarine.DrawPosition; target.Submarine == null ? target.Position : target.Position + target.Submarine.DrawPosition;
drawPos += Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target]; drawPos += Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target];
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha); GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha, createOffset: createOffset, scaleMultiplier: scaleMultiplier);
orderIndicatorCount[target] = orderIndicatorCount[target] + 1; orderIndicatorCount[target] = orderIndicatorCount[target] + 1;
} }
@@ -6,6 +6,8 @@ using System.Collections.Generic;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq; using System.Xml.Linq;
using System.IO;
using Barotrauma.Items.Components;
namespace Barotrauma namespace Barotrauma
{ {
@@ -15,6 +17,12 @@ namespace Barotrauma
public bool LastControlled; public bool LastControlled;
private Sprite disguisedPortrait;
private List<WearableSprite> disguisedAttachmentSprites;
private Vector2? disguisedSheetIndex;
private Sprite disguisedJobIcon;
private Color disguisedJobColor;
public static void Init() public static void Init()
{ {
infoAreaPortraitBG = GUI.Style.GetComponentStyle("InfoAreaPortraitBG")?.GetDefaultSprite(); infoAreaPortraitBG = GUI.Style.GetComponentStyle("InfoAreaPortraitBG")?.GetDefaultSprite();
@@ -175,6 +183,195 @@ namespace Barotrauma
} }
} }
private void GetDisguisedSprites(IdCard idCard)
{
if (idCard.StoredJobPrefab == null || idCard.StoredPortrait == null)
{
string[] readTags = idCard.Item.Tags.Split(',');
if (idCard.StoredJobPrefab == null)
{
string jobIdTag = readTags.First(s => s.StartsWith("jobid:"));
if (jobIdTag != string.Empty && jobIdTag.Length > 6)
{
string jobId = jobIdTag.Substring(6);
if (jobId != string.Empty)
{
idCard.StoredJobPrefab = JobPrefab.Get(jobId);
}
}
}
if (idCard.StoredPortrait == null)
{
string disguisedGender = string.Empty;
string disguisedRace = string.Empty;
string disguisedHeadSpriteId = string.Empty;
int disguisedHairIndex = -1;
int disguisedBeardIndex = -1;
int disguisedMoustacheIndex = -1;
int disguisedFaceAttachmentIndex = -1;
foreach (string tag in readTags)
{
string[] s = tag.Split(':');
switch (s[0])
{
case "gender":
disguisedGender = s[1];
break;
case "race":
disguisedRace = s[1];
break;
case "headspriteid":
disguisedHeadSpriteId = s[1];
break;
case "hairindex":
disguisedHairIndex = int.Parse(s[1]);
break;
case "beardindex":
disguisedBeardIndex = int.Parse(s[1]);
break;
case "moustacheindex":
disguisedMoustacheIndex = int.Parse(s[1]);
break;
case "faceattachmentindex":
disguisedFaceAttachmentIndex = int.Parse(s[1]);
break;
case "sheetindex":
string[] vectorValues = s[1].Split(";");
idCard.StoredSheetIndex = new Vector2(float.Parse(vectorValues[0]), float.Parse(vectorValues[1]));
break;
}
}
if (disguisedGender == string.Empty || disguisedRace == string.Empty || disguisedHeadSpriteId == string.Empty)
{
idCard.StoredPortrait = null;
idCard.StoredAttachments = null;
return;
}
foreach (XElement limbElement in Ragdoll.MainElement.Elements())
{
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
XElement spriteElement = limbElement.Element("sprite");
if (spriteElement == null) { continue; }
string spritePath = spriteElement.Attribute("texture").Value;
spritePath = spritePath.Replace("[GENDER]", disguisedGender);
spritePath = spritePath.Replace("[RACE]", disguisedRace.ToLowerInvariant());
spritePath = spritePath.Replace("[HEADID]", disguisedHeadSpriteId);
string fileName = Path.GetFileNameWithoutExtension(spritePath);
//go through the files in the directory to find a matching sprite
foreach (string file in Directory.GetFiles(Path.GetDirectoryName(spritePath)))
{
if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
{
continue;
}
string fileWithoutTags = Path.GetFileNameWithoutExtension(file);
fileWithoutTags = fileWithoutTags.Split('[', ']').First();
if (fileWithoutTags != fileName) { continue; }
idCard.StoredPortrait = new Sprite(spriteElement, "", file) { RelativeOrigin = Vector2.Zero };
break;
}
break;
}
if (Wearables != null)
{
XElement disguisedHairElement, disguisedBeardElement, disguisedMoustacheElement, disguisedFaceAttachmentElement;
List<XElement> disguisedHairs, disguisedBeards, disguisedMoustaches, disguisedFaceAttachments;
Gender disguisedGenderEnum = disguisedGender == "female" ? Gender.Female : Gender.Male;
Race disguisedRaceEnum = (Race)Enum.Parse(typeof(Race), disguisedRace);
int headSpriteId = int.Parse(disguisedHeadSpriteId);
float commonness = disguisedGenderEnum == Gender.Female ? 0.05f : 0.2f;
disguisedHairs = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Hair, headSpriteId), WearableType.Hair, commonness);
disguisedBeards = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Beard, headSpriteId), WearableType.Beard);
disguisedMoustaches = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Moustache, headSpriteId), WearableType.Moustache);
disguisedFaceAttachments = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.FaceAttachment, headSpriteId), WearableType.FaceAttachment);
if (IsValidIndex(disguisedHairIndex, disguisedHairs))
{
disguisedHairElement = disguisedHairs[disguisedHairIndex];
}
else
{
disguisedHairElement = GetRandomElement(disguisedHairs);
}
if (IsValidIndex(disguisedBeardIndex, disguisedBeards))
{
disguisedBeardElement = disguisedBeards[disguisedBeardIndex];
}
else
{
disguisedBeardElement = GetRandomElement(disguisedBeards);
}
if (IsValidIndex(disguisedMoustacheIndex, disguisedMoustaches))
{
disguisedMoustacheElement = disguisedMoustaches[disguisedMoustacheIndex];
}
else
{
disguisedMoustacheElement = GetRandomElement(disguisedMoustaches);
}
if (IsValidIndex(disguisedFaceAttachmentIndex, disguisedFaceAttachments))
{
disguisedFaceAttachmentElement = disguisedFaceAttachments[disguisedFaceAttachmentIndex];
}
else
{
disguisedFaceAttachmentElement = GetRandomElement(disguisedFaceAttachments);
}
idCard.StoredAttachments = new List<WearableSprite>();
disguisedFaceAttachmentElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.FaceAttachment)));
disguisedBeardElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Beard)));
disguisedMoustacheElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Moustache)));
disguisedHairElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Hair)));
if (OmitJobInPortraitClothing)
{
JobPrefab.NoJobElement?.Element("PortraitClothing")?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
else
{
idCard.StoredJobPrefab?.ClothingElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
}
}
}
if (idCard.StoredJobPrefab != null)
{
disguisedJobIcon = idCard.StoredJobPrefab.Icon;
disguisedJobColor = idCard.StoredJobPrefab.UIColor;
}
disguisedPortrait = idCard.StoredPortrait;
disguisedSheetIndex = idCard.StoredSheetIndex;
disguisedAttachmentSprites = idCard.StoredAttachments;
}
partial void LoadAttachmentSprites(bool omitJob) partial void LoadAttachmentSprites(bool omitJob)
{ {
if (attachmentSprites == null) if (attachmentSprites == null)
@@ -219,23 +416,42 @@ namespace Barotrauma
HUDLayoutSettings.BottomRightInfoArea.Height / (float)infoAreaPortraitBG.SourceRect.Height)); HUDLayoutSettings.BottomRightInfoArea.Height / (float)infoAreaPortraitBG.SourceRect.Height));
} }
public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false) public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false, bool evaluateDisguise = false)
{ {
if (Portrait != null) if (evaluateDisguise && IsDisguised) return;
Vector2? sheetIndex;
Sprite portraitToDraw;
List<WearableSprite> attachmentsToDraw;
if (!IsDisguisedAsAnother || !evaluateDisguise)
{
sheetIndex = Head.SheetIndex;
portraitToDraw = Portrait;
attachmentsToDraw = AttachmentSprites;
}
else
{
sheetIndex = disguisedSheetIndex;
portraitToDraw = disguisedPortrait;
attachmentsToDraw = disguisedAttachmentSprites;
}
if (portraitToDraw != null)
{ {
// Scale down the head sprite 10% // Scale down the head sprite 10%
float scale = targetWidth * 0.9f / Portrait.size.X; float scale = targetWidth * 0.9f / Portrait.size.X;
if (Head.SheetIndex.HasValue) if (sheetIndex.HasValue)
{ {
Portrait.SourceRect = new Rectangle(CalculateOffset(Portrait, Head.SheetIndex.Value.ToPoint()), Portrait.SourceRect.Size); portraitToDraw.SourceRect = new Rectangle(CalculateOffset(portraitToDraw, sheetIndex.Value.ToPoint()), portraitToDraw.SourceRect.Size);
} }
Portrait.Draw(spriteBatch, screenPos + offset, Color.White, Portrait.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None); portraitToDraw.Draw(spriteBatch, screenPos + offset, Color.White, portraitToDraw.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
if (AttachmentSprites != null) if (attachmentsToDraw != null)
{ {
float depthStep = 0.000001f; float depthStep = 0.000001f;
foreach (var attachment in AttachmentSprites) foreach (var attachment in attachmentsToDraw)
{ {
DrawAttachmentSprite(spriteBatch, attachment, Portrait, screenPos + offset, scale, depthStep, flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None); DrawAttachmentSprite(spriteBatch, attachment, portraitToDraw, sheetIndex, screenPos + offset, scale, depthStep, flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
depthStep += depthStep; depthStep += depthStep;
} }
} }
@@ -258,30 +474,34 @@ namespace Barotrauma
float depthStep = 0.000001f; float depthStep = 0.000001f;
foreach (var attachment in AttachmentSprites) foreach (var attachment in AttachmentSprites)
{ {
DrawAttachmentSprite(spriteBatch, attachment, headSprite, screenPos, scale, depthStep); DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep);
depthStep += depthStep; depthStep += depthStep;
} }
} }
} }
} }
public void DrawJobIcon(SpriteBatch spriteBatch, Vector2 pos, float scale = 1.0f) public void DrawJobIcon(SpriteBatch spriteBatch, Vector2 pos, float scale = 1.0f, bool evaluateDisguise = false)
{ {
var icon = Job?.Prefab?.Icon; if (evaluateDisguise && IsDisguised) return;
var icon = !IsDisguisedAsAnother || !evaluateDisguise ? Job?.Prefab?.Icon : disguisedJobIcon;
if (icon == null) { return; } if (icon == null) { return; }
icon.Draw(spriteBatch, pos, Job.Prefab.UIColor, scale: scale); Color iconColor = !IsDisguisedAsAnother || !evaluateDisguise ? Job.Prefab.UIColor : disguisedJobColor;
}
public void DrawJobIcon(SpriteBatch spriteBatch, Rectangle area) icon.Draw(spriteBatch, pos, iconColor, scale: scale);
{
var icon = Job?.Prefab?.Icon;
if (icon == null) { return; }
icon.Draw(spriteBatch,
area.Center.ToVector2(),
Job.Prefab.UIColor,
scale: Math.Min(area.Width / (float)icon.SourceRect.Width, area.Height / (float)icon.SourceRect.Height));
} }
private void DrawAttachmentSprite(SpriteBatch spriteBatch, WearableSprite attachment, Sprite head, Vector2 drawPos, float scale, float depthStep, SpriteEffects spriteEffects = SpriteEffects.None) public void DrawJobIcon(SpriteBatch spriteBatch, Rectangle area, bool evaluateDisguise = false)
{
if (evaluateDisguise && IsDisguised) return;
var icon = !IsDisguisedAsAnother || !evaluateDisguise ? Job?.Prefab?.Icon : disguisedJobIcon;
if (icon == null) { return; }
Color iconColor = !IsDisguisedAsAnother || !evaluateDisguise ? Job.Prefab.UIColor : disguisedJobColor;
icon.Draw(spriteBatch, area.Center.ToVector2(), iconColor, scale: Math.Min(area.Width / (float)icon.SourceRect.Width, area.Height / (float)icon.SourceRect.Height));
}
private void DrawAttachmentSprite(SpriteBatch spriteBatch, WearableSprite attachment, Sprite head, Vector2? sheetIndex, Vector2 drawPos, float scale, float depthStep, SpriteEffects spriteEffects = SpriteEffects.None)
{ {
if (attachment.InheritSourceRect) if (attachment.InheritSourceRect)
{ {
@@ -289,9 +509,9 @@ namespace Barotrauma
{ {
attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, attachment.SheetIndex.Value), head.SourceRect.Size); attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, attachment.SheetIndex.Value), head.SourceRect.Size);
} }
else if (Head.SheetIndex.HasValue) else if (sheetIndex.HasValue)
{ {
attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, Head.SheetIndex.Value.ToPoint()), head.SourceRect.Size); attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, sheetIndex.Value.ToPoint()), head.SourceRect.Size);
} }
else else
{ {
@@ -416,8 +416,7 @@ namespace Barotrauma
Character character = null; Character character = null;
if (noInfo) if (noInfo)
{ {
character = Create(speciesName, position, seed, null, false); character = Create(speciesName, position, seed, characterInfo: null, id: id, isRemotePlayer: false);
character.ID = id;
bool containsStatusData = inc.ReadBoolean(); bool containsStatusData = inc.ReadBoolean();
if (containsStatusData) if (containsStatusData)
{ {
@@ -434,8 +433,7 @@ namespace Barotrauma
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc); CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
character = Create(speciesName, position, seed, info, ownerId > 0 && GameMain.Client.ID != ownerId, hasAi); character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.ID != ownerId, hasAi: hasAi);
character.ID = id;
character.TeamID = (TeamType)teamID; character.TeamID = (TeamType)teamID;
character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte(); character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte();
if (character.CampaignInteractionType != CampaignMode.InteractionType.None) if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
@@ -53,7 +53,7 @@ namespace Barotrauma
private GUIFrame healthWindow; private GUIFrame healthWindow;
private GUIComponent deadIndicator; private GUITextBlock deadIndicator;
private GUIComponent lowSkillIndicator; private GUIComponent lowSkillIndicator;
@@ -225,13 +225,14 @@ namespace Barotrauma
Character.Controlled.ResetInteract = true; Character.Controlled.ResetInteract = true;
if (openHealthWindow != null) if (openHealthWindow != null)
{ {
if (value.Character.Info == null || Character.Controlled.HasEquippedItem("healthscanner")) if (value.Character.Info == null || value.Character == Character.Controlled || Character.Controlled.HasEquippedItem("healthscanner"))
{ {
openHealthWindow.characterName.Text = value.Character.Name; openHealthWindow.characterName.Text = value.Character.Name;
} }
else else
{ {
openHealthWindow.characterName.Text = value.Character.Info.DisplayName; openHealthWindow.characterName.Text = value.Character.Info.DisplayName;
value.Character.Info.CheckDisguiseStatus(false);
} }
if (Character.Controlled.SelectedConstruction != null && Character.Controlled.SelectedConstruction.GetComponent<Ladder>() == null) if (Character.Controlled.SelectedConstruction != null && Character.Controlled.SelectedConstruction.GetComponent<Ladder>() == null)
@@ -330,11 +331,19 @@ namespace Barotrauma
} }
); );
deadIndicator = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.1f), limbSelection.RectTransform, Anchor.Center), deadIndicator = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.1f), limbSelection.RectTransform, Anchor.Center),
text: TextManager.Get("Deceased"), font: GUI.LargeFont, textAlignment: Alignment.Center, wrap: true, style: "GUIToolTip") text: TextManager.Get("Deceased"), font: GUI.LargeFont, textAlignment: Alignment.Center, style: "GUIToolTip")
{ {
Visible = false, Visible = false,
CanBeFocused = false CanBeFocused = false
}; };
if (deadIndicator.Text.Contains(' '))
{
deadIndicator.Wrap = true;
}
else
{
deadIndicator.AutoScaleHorizontal = true;
}
var rightSide = new GUIFrame(new RectTransform(new Vector2(0.4f, 1.0f), paddedHealthWindow.RectTransform), style: null); var rightSide = new GUIFrame(new RectTransform(new Vector2(0.4f, 1.0f), paddedHealthWindow.RectTransform), style: null);
@@ -405,15 +414,17 @@ namespace Barotrauma
CanBeFocused = true CanBeFocused = true
}; };
textLayout.RectTransform.RelativeOffset = new Vector2(0, 0.025f);
var nameContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), textLayout.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true) var nameContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), textLayout.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true)
{ {
Stretch = true Stretch = true
}; };
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform), new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform, Anchor.CenterLeft),
onDraw: (spriteBatch, component) => onDraw: (spriteBatch, component) =>
{ {
character.Info.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), Vector2.Zero, component.Rect.Width); character.Info.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), Vector2.Zero, component.Rect.Width, false, openHealthWindow?.Character != Character.Controlled);
}); });
characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont)
{ {
@@ -422,7 +433,7 @@ namespace Barotrauma
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform), new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform),
onDraw: (spriteBatch, component) => onDraw: (spriteBatch, component) =>
{ {
character.Info.DrawJobIcon(spriteBatch, component.Rect); character.Info.DrawJobIcon(spriteBatch, component.Rect, openHealthWindow?.Character != Character.Controlled);
}); });
@@ -951,6 +962,28 @@ namespace Barotrauma
SuicideButton.Visible = Character == Character.Controlled && !Character.IsDead && Character.IsIncapacitated; SuicideButton.Visible = Character == Character.Controlled && !Character.IsDead && Character.IsIncapacitated;
if (GameMain.GameSession?.Campaign is { } campaign)
{
RectTransform endRoundButton = campaign?.EndRoundButton?.RectTransform;
RectTransform readyCheckButton = campaign?.ReadyCheckButton?.RectTransform;
if (endRoundButton != null)
{
if (SuicideButton.Visible)
{
Point offset = new Point(0, SuicideButton.Rect.Height);
endRoundButton.ScreenSpaceOffset = offset;
}
else if (endRoundButton.ScreenSpaceOffset != Point.Zero)
{
endRoundButton.ScreenSpaceOffset = Point.Zero;
}
if (readyCheckButton != null)
{
readyCheckButton.ScreenSpaceOffset = endRoundButton.ScreenSpaceOffset;
}
}
}
cprButton.Visible = cprButton.Visible =
Character == Character.Controlled?.SelectedCharacter Character == Character.Controlled?.SelectedCharacter
&& (Character.IsUnconscious || Character.Stun > 0.0f) && (Character.IsUnconscious || Character.Stun > 0.0f)
@@ -328,8 +328,7 @@ namespace Barotrauma
deformation = ragdoll.Limbs deformation = ragdoll.Limbs
.Where(l => l != null) .Where(l => l != null)
.SelectMany(l => l.Deformations) .SelectMany(l => l.Deformations)
.Where(d => d.TypeName == typeName && d.Sync == sync) .FirstOrDefault(d => d.TypeName == typeName && d.Sync == sync);
.FirstOrDefault();
} }
if (deformation == null) if (deformation == null)
{ {
@@ -971,11 +970,11 @@ namespace Barotrauma
XElement element; XElement element;
if (random) if (random)
{ {
element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), type)?.GetRandom(Rand.RandSync.ClientOnly); element = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Gender, info.Race), type, info.Head.HeadSpriteId)?.GetRandom(Rand.RandSync.ClientOnly);
} }
else else
{ {
element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), type)?.FirstOrDefault(); element = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Gender, info.Race), type, info.Head.HeadSpriteId)?.FirstOrDefault();
} }
if (element != null) if (element != null)
{ {
@@ -491,7 +491,11 @@ namespace Barotrauma
{ {
SteamManager.NetworkingDebugLog = !SteamManager.NetworkingDebugLog; SteamManager.NetworkingDebugLog = !SteamManager.NetworkingDebugLog;
SteamManager.SetSteamworksNetworkingDebugLog(SteamManager.NetworkingDebugLog); SteamManager.SetSteamworksNetworkingDebugLog(SteamManager.NetworkingDebugLog);
}));
commands.Add(new Command("readycheck", "Commence a ready check in multiplayer.", (string[] args) =>
{
NewMessage("Ready checks can only be commenced in multiplayer.", Color.Red);
})); }));
AssignRelayToServer("kick", false); AssignRelayToServer("kick", false);
@@ -527,6 +531,7 @@ namespace Barotrauma
AssignRelayToServer("traitorlist", true); AssignRelayToServer("traitorlist", true);
AssignRelayToServer("money", true); AssignRelayToServer("money", true);
AssignRelayToServer("setskill", true); AssignRelayToServer("setskill", true);
AssignRelayToServer("readycheck", true);
AssignOnExecute("control", (string[] args) => AssignOnExecute("control", (string[] args) =>
{ {
@@ -547,14 +552,15 @@ namespace Barotrauma
AssignOnExecute("explosion", (string[] args) => AssignOnExecute("explosion", (string[] args) =>
{ {
Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
float range = 500, force = 10, damage = 50, structureDamage = 10, itemDamage = 100, empStrength = 0.0f; float range = 500, force = 10, damage = 50, structureDamage = 10, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
if (args.Length > 0) float.TryParse(args[0], out range); if (args.Length > 0) float.TryParse(args[0], out range);
if (args.Length > 1) float.TryParse(args[1], out force); if (args.Length > 1) float.TryParse(args[1], out force);
if (args.Length > 2) float.TryParse(args[2], out damage); if (args.Length > 2) float.TryParse(args[2], out damage);
if (args.Length > 3) float.TryParse(args[3], out structureDamage); if (args.Length > 3) float.TryParse(args[3], out structureDamage);
if (args.Length > 4) float.TryParse(args[4], out itemDamage); if (args.Length > 4) float.TryParse(args[4], out itemDamage);
if (args.Length > 5) float.TryParse(args[5], out empStrength); if (args.Length > 5) float.TryParse(args[5], out empStrength);
new Explosion(range, force, damage, structureDamage, itemDamage, empStrength).Explode(explosionPos, null); if (args.Length > 6) float.TryParse(args[6], out ballastFloraStrength);
new Explosion(range, force, damage, structureDamage, itemDamage, empStrength, ballastFloraStrength).Explode(explosionPos, null);
}); });
AssignOnExecute("teleportcharacter|teleport", (string[] args) => AssignOnExecute("teleportcharacter|teleport", (string[] args) =>
@@ -2231,6 +2237,37 @@ namespace Barotrauma
} }
); );
#if DEBUG
commands.Add(new Command("setcurrentlocationtype", "setcurrentlocationtype [location type]: Change the type of the current location.", (string[] args) =>
{
var character = Character.Controlled;
if (GameMain.GameSession?.Campaign == null)
{
ThrowError("Campaign not active!");
return;
}
if (args.Length == 0)
{
ThrowError("Please give the location type after the command.");
return;
}
var locationType = LocationType.List.Find(lt => lt.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase));
if (locationType == null)
{
ThrowError($"Could not find the location type \"{args[0]}\".");
return;
}
GameMain.GameSession.Campaign.Map.CurrentLocation.ChangeType(locationType);
},
() =>
{
return new string[][]
{
LocationType.List.Select(lt => lt.Identifier).ToArray()
};
}));
#endif
commands.Add(new Command("limbscale", "Define the limbscale for the controlled character. Provide id or name if you want to target another character. Note: the changes are not saved!", (string[] args) => commands.Add(new Command("limbscale", "Define the limbscale for the controlled character. Provide id or name if you want to target another character. Note: the changes are not saved!", (string[] args) =>
{ {
var character = Character.Controlled; var character = Character.Controlled;
@@ -0,0 +1,15 @@
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma
{
partial class BeaconMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
return;
}
}
}
@@ -2,7 +2,7 @@
namespace Barotrauma namespace Barotrauma
{ {
partial class CombatMission partial class CombatMission : Mission
{ {
public override string Description public override string Description
{ {
@@ -0,0 +1,56 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class MineralMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
for (int i = 0; i < ResourceClusters.Count; i++)
{
var amount = msg.ReadByte();
var rotation = msg.ReadSingle();
for (int j = 0; j < amount; j++)
{
var item = Item.ReadSpawnData(msg);
if (item.GetComponent<Holdable>() is Holdable h)
{
h.AttachToWall();
item.Rotation = rotation;
}
if (SpawnedResources.TryGetValue(item.Prefab.Identifier, out var resources))
{
resources.Add(item);
}
else
{
SpawnedResources.Add(item.Prefab.Identifier, new List<Item>() { item });
}
}
}
CalculateMissionClusterPositions();
for(int i = 0; i < ResourceClusters.Count; i++)
{
var identifier = msg.ReadString();
var count = msg.ReadByte();
var resources = new Item[count];
for (int j = 0; j < count; j++)
{
var id = msg.ReadUInt16();
var entity = Entity.FindEntityByID(id);
if (!(entity is Item item)) { continue; }
resources[j] = item;
}
RelevantLevelResources.Add(identifier, resources);
}
}
}
}
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
{ {
partial class Mission abstract partial class Mission
{ {
partial void ShowMessageProjSpecific(int missionState) partial void ShowMessageProjSpecific(int missionState)
{ {
@@ -3,7 +3,7 @@ using System;
namespace Barotrauma namespace Barotrauma
{ {
partial class MissionMode : GameMode abstract partial class MissionMode : GameMode
{ {
public override void ShowStartMessage() public override void ShowStartMessage()
{ {
@@ -0,0 +1,26 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
partial class NestMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
nestPosition = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)
{
var item = Item.ReadSpawnData(msg);
items.Add(item);
if (item.body != null)
{
item.body.FarseerBody.BodyType = BodyType.Kinematic;
}
}
}
}
}
@@ -1256,17 +1256,26 @@ namespace Barotrauma
#region Element drawing #region Element drawing
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color)
/// <param name="createOffset">Should the indicator move based on the camera position?</param>
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color, bool createOffset = true, float scaleMultiplier = 1.0f)
{ {
Vector2 diff = worldPosition - cam.WorldViewCenter; Vector2 diff = worldPosition - cam.WorldViewCenter;
float dist = diff.Length(); float dist = diff.Length();
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f); float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier;
if (dist > hideDist) if (dist > hideDist)
{ {
float alpha = Math.Min((dist - hideDist) / 100.0f, 1.0f); float alpha = Math.Min((dist - hideDist) / 100.0f, 1.0f);
Vector2 targetScreenPos = cam.WorldToScreen(worldPosition); Vector2 targetScreenPos = cam.WorldToScreen(worldPosition);
if (!createOffset)
{
sprite.Draw(spriteBatch, targetScreenPos, color * alpha, rotate: 0.0f, scale: symbolScale);
return;
}
float screenDist = Vector2.Distance(cam.WorldToScreen(cam.WorldViewCenter), targetScreenPos); float screenDist = Vector2.Distance(cam.WorldToScreen(cam.WorldViewCenter), targetScreenPos);
float angle = MathUtils.VectorToAngle(diff); float angle = MathUtils.VectorToAngle(diff);
@@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -218,6 +219,12 @@ namespace Barotrauma
GUI.Style.ButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent)); GUI.Style.ButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
} }
if (UserData is string s && s == "ReadyCheckButton" && ReadyCheck.lastReadyCheck > DateTime.Now)
{
float progress = (ReadyCheck.lastReadyCheck - DateTime.Now).Seconds / 60.0f;
Frame.Color = ToolBox.GradientLerp(progress, Color.White, GUI.Style.Red);
}
} }
protected override void Update(float deltaTime) protected override void Update(float deltaTime)
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma namespace Barotrauma
{ {
@@ -20,7 +21,8 @@ namespace Barotrauma
public enum Type public enum Type
{ {
Default, Default,
InGame InGame,
Vote
} }
public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>(); public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
@@ -47,6 +49,9 @@ namespace Barotrauma
Icon.Color = value; Icon.Color = value;
} }
} }
public bool Draggable { get; set; }
public Vector2 DraggingPosition = Vector2.Zero;
public GUIImage BackgroundIcon { get; private set; } public GUIImage BackgroundIcon { get; private set; }
private GUIImage newBackgroundIcon; private GUIImage newBackgroundIcon;
@@ -60,7 +65,7 @@ namespace Barotrauma
private bool iconSwitching; private bool iconSwitching;
private bool closing; private bool closing;
private Type type; private readonly Type type;
public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault(); public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
@@ -97,12 +102,25 @@ namespace Barotrauma
}; };
} }
InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, type == Type.InGame ? Anchor.TopCenter : Anchor.Center) { IsFixedSize = false }, style: null); Anchor anchor = type switch
{
Type.InGame => Anchor.TopCenter,
Type.Vote => Anchor.TopRight,
_ => Anchor.Center
};
InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, anchor) { IsFixedSize = false }, style: null);
if (type == Type.Vote)
{
int offset = GUI.IntScale(64);
InnerFrame.RectTransform.ScreenSpaceOffset = new Point(-offset, offset);
CanBeFocused = false;
}
GUI.Style.Apply(InnerFrame, "", this); GUI.Style.Apply(InnerFrame, "", this);
this.type = type; this.type = type;
Tag = tag; Tag = tag;
if (type == Type.Default) if (type == Type.Default || type == Type.Vote)
{ {
Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 }; Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 };
@@ -271,92 +289,114 @@ namespace Barotrauma
protected override void Update(float deltaTime) protected override void Update(float deltaTime)
{ {
if (type != Type.InGame) { return; } if (Draggable)
Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
if (!closing)
{ {
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint(); if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton))
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{ {
BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2); GUI.MouseCursor = CursorState.Move;
if (!MathUtils.NearlyEqual(openState, 1.0f)) if (PlayerInput.PrimaryMouseButtonDown())
{ {
BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White); DraggingPosition = RectTransform.ScreenSpaceOffset.ToVector2() - PlayerInput.MousePosition;
} }
} }
if (!(Screen.Selected is RoundSummaryScreen) && !MessageBoxes.Any(mb => mb.UserData is RoundSummary))
{
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
}
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose) if (PlayerInput.PrimaryMouseButtonHeld() && DraggingPosition != Vector2.Zero)
{ {
inGameCloseTimer += deltaTime; GUI.MouseCursor = CursorState.Dragging;
} RectTransform.ScreenSpaceOffset = (PlayerInput.MousePosition + DraggingPosition).ToPoint();
if (inGameCloseTimer >= inGameCloseTime)
{
Close();
}
}
else
{
openState += deltaTime * 2.0f;
Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
}
if (openState >= 2.0f)
{
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
}
if (newBackgroundIcon != null)
{
if (!iconSwitching)
{
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
if (BackgroundIcon.Color.A == 0)
{
BackgroundIcon = null;
iconSwitching = true;
RemoveChild(BackgroundIcon);
}
}
else
{
iconSwitching = true;
}
iconState = 0;
} }
else else
{ {
newBackgroundIcon.SetAsFirstChild(); DraggingPosition = Vector2.Zero;
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2); }
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White); }
if (newBackgroundIcon.Color.A == 255)
{ if (type == Type.InGame)
BackgroundIcon = newBackgroundIcon; {
BackgroundIcon.SetAsFirstChild(); Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
newBackgroundIcon = null; Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
iconSwitching = false; Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
}
if (!closing)
iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f); {
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
if (!MathUtils.NearlyEqual(openState, 1.0f))
{
BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
}
}
if (!(Screen.Selected is RoundSummaryScreen) && !MessageBoxes.Any(mb => mb.UserData is RoundSummary))
{
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
}
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
{
inGameCloseTimer += deltaTime;
}
if (inGameCloseTimer >= inGameCloseTime)
{
Close();
}
}
else
{
openState += deltaTime * 2.0f;
Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
}
if (openState >= 2.0f)
{
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
}
if (newBackgroundIcon != null)
{
if (!iconSwitching)
{
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
if (BackgroundIcon.Color.A == 0)
{
BackgroundIcon = null;
iconSwitching = true;
RemoveChild(BackgroundIcon);
}
}
else
{
iconSwitching = true;
}
iconState = 0;
}
else
{
newBackgroundIcon.SetAsFirstChild();
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White);
if (newBackgroundIcon.Color.A == 255)
{
BackgroundIcon = newBackgroundIcon;
BackgroundIcon.SetAsFirstChild();
newBackgroundIcon = null;
iconSwitching = false;
}
iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
}
} }
} }
} }
@@ -707,6 +707,8 @@ namespace Barotrauma
private void SortItems(GUIListBox list, SortingMethod sortingMethod) private void SortItems(GUIListBox list, SortingMethod sortingMethod)
{ {
if (CurrentLocation == null) { return; }
if (sortingMethod == SortingMethod.AlphabeticalAsc || sortingMethod == SortingMethod.AlphabeticalDesc) if (sortingMethod == SortingMethod.AlphabeticalAsc || sortingMethod == SortingMethod.AlphabeticalDesc)
{ {
list.Content.RectTransform.SortChildren( list.Content.RectTransform.SortChildren(
@@ -868,13 +870,13 @@ namespace Barotrauma
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f), TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
UserData = "price" UserData = "price"
}; };
if(listBox == storeSellList || listBox == shoppingCrateSellList) if (listBox == storeSellList || listBox == shoppingCrateSellList)
{ {
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation.GetAdjustedItemSellPrice(priceInfo)); priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(priceInfo) ?? 0);
} }
else else
{ {
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation.GetAdjustedItemBuyPrice(priceInfo)); priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(priceInfo) ?? 0);
} }
if (listBox == storeDealsList || listBox == storeBuyList || listBox == storeSellList) if (listBox == storeDealsList || listBox == storeBuyList || listBox == storeSellList)
@@ -32,6 +32,7 @@ namespace Barotrauma
public Vector2 DrawPos { get; set; } public Vector2 DrawPos { get; set; }
public int size = 10; public int size = 10;
public float thickness = 1f;
/// <summary> /// <summary>
/// Used only for circles. /// Used only for circles.
/// </summary> /// </summary>
@@ -157,7 +158,7 @@ namespace Barotrauma
{ {
GUI.DrawRectangle(spriteBatch, drawRect, secondaryColor.Value, isFilled, thickness: 2); GUI.DrawRectangle(spriteBatch, drawRect, secondaryColor.Value, isFilled, thickness: 2);
} }
GUI.DrawRectangle(spriteBatch, drawRect, color, isFilled, thickness: IsSelected ? 3 : 1); GUI.DrawRectangle(spriteBatch, drawRect, color, isFilled, thickness: IsSelected ? (int)(thickness * 3) : (int)thickness);
break; break;
case Shape.Circle: case Shape.Circle:
if (secondaryColor.HasValue) if (secondaryColor.HasValue)
@@ -182,7 +183,7 @@ namespace Barotrauma
{ {
if (showTooltip && !string.IsNullOrEmpty(tooltip)) if (showTooltip && !string.IsNullOrEmpty(tooltip))
{ {
var offset = tooltipOffset ?? new Vector2(size, -size / 2); var offset = tooltipOffset ?? new Vector2(size, -size / 2f);
GUI.DrawString(spriteBatch, DrawPos + offset, tooltip, textColor, textBackgroundColor); GUI.DrawString(spriteBatch, DrawPos + offset, tooltip, textColor, textBackgroundColor);
} }
} }
@@ -525,6 +525,7 @@ namespace Barotrauma
Tutorials.Tutorial.Init(); Tutorials.Tutorial.Init();
MapGenerationParams.Init(); MapGenerationParams.Init();
LevelGenerationParams.LoadPresets(); LevelGenerationParams.LoadPresets();
CaveGenerationParams.LoadPresets();
OutpostGenerationParams.LoadPresets(); OutpostGenerationParams.LoadPresets();
WreckAIConfig.LoadAll(); WreckAIConfig.LoadAll();
EventSet.LoadPrefabs(); EventSet.LoadPrefabs();
@@ -533,6 +534,7 @@ namespace Barotrauma
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings)); SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
Order.Init(); Order.Init();
EventManagerSettings.Init(); EventManagerSettings.Init();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
TitleScreen.LoadState = 50.0f; TitleScreen.LoadState = 50.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -632,6 +634,7 @@ namespace Barotrauma
/// </summary> /// </summary>
protected override void UnloadContent() protected override void UnloadContent()
{ {
TextureLoader.CancelAll();
CoroutineManager.StopCoroutines("Load"); CoroutineManager.StopCoroutines("Load");
Video.Close(); Video.Close();
VoipCapture.Instance?.Dispose(); VoipCapture.Instance?.Dispose();
@@ -680,7 +683,7 @@ namespace Barotrauma
} }
public void OnLobbyJoinRequested(Steamworks.Data.Lobby lobby, Steamworks.SteamId friendId) public void OnLobbyJoinRequested(Steamworks.Data.Lobby lobby, Steamworks.SteamId friendId)
{ {
SteamManager.JoinLobby(lobby.Id, true); SteamManager.JoinLobby(lobby.Id, true);
} }
@@ -199,7 +199,7 @@ namespace Barotrauma
var headset = GetHeadset(Character.Controlled, true); var headset = GetHeadset(Character.Controlled, true);
if (headset != null && headset.CanTransmit()) if (headset != null && headset.CanTransmit())
{ {
headset.TransmitSignal(stepsTaken: 0, signal: msg, source: headset.Item, sender: Character.Controlled, sendToChat: false); headset.TransmitSignal(stepsTaken: 0, signal: msg, source: headset.Item, sender: Character.Controlled, sentFromChat: true);
} }
} }
textbox.Deselect(); textbox.Deselect();
@@ -232,7 +232,7 @@ namespace Barotrauma
}; };
} }
var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null); var reports = Order.PrefabList.FindAll(o => o.IsReport && o.SymbolSprite != null);
if (reports.None()) if (reports.None())
{ {
DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined."); DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined.");
@@ -252,7 +252,7 @@ namespace Barotrauma
//report buttons //report buttons
foreach (Order order in reports) foreach (Order order in reports)
{ {
if (!order.TargetAllCharacters || order.SymbolSprite == null) { continue; } if (!order.IsReport || order.SymbolSprite == null) { continue; }
var btn = new GUIButton(new RectTransform(new Point(ReportButtonFrame.Rect.Width), ReportButtonFrame.RectTransform), style: null) var btn = new GUIButton(new RectTransform(new Point(ReportButtonFrame.Rect.Width), ReportButtonFrame.RectTransform), style: null)
{ {
OnClicked = (GUIButton button, object userData) => OnClicked = (GUIButton button, object userData) =>
@@ -667,6 +667,8 @@ namespace Barotrauma
#region Crew List Order Displayment #region Crew List Order Displayment
// TODO: CHECK ALL THE ORDER CONSTUCTOR CALLS
/// <summary> /// <summary>
/// Sets the character's current order (if it's close enough to receive messages from orderGiver) and /// Sets the character's current order (if it's close enough to receive messages from orderGiver) and
/// displays the order in the crew UI /// displays the order in the crew UI
@@ -675,16 +677,69 @@ namespace Barotrauma
{ {
if (order != null && order.TargetAllCharacters) if (order != null && order.TargetAllCharacters)
{ {
if (orderGiver == null || orderGiver.CurrentHull == null) { return; } Hull hull = null;
var hull = orderGiver.CurrentHull; if (order.IsReport)
AddOrder(new Order(order.Prefab ?? order, hull, null, orderGiver), order.FadeOutTime); {
if (orderGiver?.CurrentHull == null) { return; }
hull = orderGiver.CurrentHull;
AddOrder(new Order(order.Prefab ?? order, hull, null, orderGiver), order.FadeOutTime);
}
else if(order.IsIgnoreOrder)
{
WallSection ws = null;
if (order.TargetType == Order.OrderTargetType.Entity && order.TargetEntity is MapEntity me)
{
if (order.Identifier == "ignorethis")
{
me.SetIgnoreByAI(true);
AddOrder(new Order(order.Prefab ?? order, order.TargetEntity, order.TargetItemComponent, orderGiver), null);
}
else
{
me.SetIgnoreByAI(false);
ActiveOrders.RemoveAll(p => p.First.Identifier == "ignorethis" && p.First.TargetEntity == order.TargetEntity);
}
}
else if (order.TargetType == Order.OrderTargetType.WallSection && order.TargetEntity is Structure s)
{
var wallSectionIndex = order.WallSectionIndex ?? s.Sections.IndexOf(wallContext);
ws = s.GetSection(wallSectionIndex);
if (ws != null)
{
if (order.Identifier == "ignorethis")
{
ws.SetIgnoreByAI(true);
AddOrder(new Order(order.Prefab ?? order, s, wallSectionIndex, orderGiver), null);
}
else
{
ws.SetIgnoreByAI(false);
ActiveOrders.RemoveAll(p => p.First.Identifier == "ignorethis" && p.First.TargetEntity == s && p.First.WallSectionIndex == wallSectionIndex);
}
}
}
if (ws != null)
{
hull = Hull.FindHull(ws.WorldPosition);
}
else if (order.TargetEntity is Item i)
{
hull = i.CurrentHull;
}
else if (order.TargetEntity is ISpatialEntity se)
{
hull = Hull.FindHull(se.WorldPosition);
}
}
if (IsSinglePlayer) if (IsSinglePlayer)
{ {
orderGiver.Speak(order.GetChatMessage("", hull.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order); orderGiver.Speak(order.GetChatMessage("", hull?.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order);
} }
else else
{ {
OrderChatMessage msg = new OrderChatMessage(order, "", hull, null, orderGiver); OrderChatMessage msg = new OrderChatMessage(order, "", order.IsReport ? hull : order.TargetEntity, null, orderGiver);
GameMain.Client?.SendChatMessage(msg); GameMain.Client?.SendChatMessage(msg);
} }
} }
@@ -696,7 +751,7 @@ namespace Barotrauma
if (IsSinglePlayer) if (IsSinglePlayer)
{ {
character.SetOrder(order, option, orderGiver, speak: orderGiver != character); character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
orderGiver?.Speak(order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null); orderGiver?.Speak(order?.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option));
} }
else if (orderGiver != null) else if (orderGiver != null)
{ {
@@ -1591,6 +1646,7 @@ namespace Barotrauma
private Character characterContext; private Character characterContext;
private Item itemContext; private Item itemContext;
private Hull hullContext; private Hull hullContext;
private WallSection wallContext;
private bool isContextual; private bool isContextual;
private readonly List<Order> contextualOrders = new List<Order>(); private readonly List<Order> contextualOrders = new List<Order>();
private Point shorcutCenterNodeOffset; private Point shorcutCenterNodeOffset;
@@ -1640,7 +1696,7 @@ namespace Barotrauma
} }
} }
else if (TryGetBreachedHullAtHoveredWall(out Hull breachedHull)) else if (TryGetBreachedHullAtHoveredWall(out Hull breachedHull, out wallContext))
{ {
return breachedHull; return breachedHull;
} }
@@ -1662,16 +1718,24 @@ namespace Barotrauma
if (entityContext is Character character) if (entityContext is Character character)
{ {
characterContext = character; characterContext = character;
itemContext = null;
hullContext = null;
wallContext = null;
isContextual = false; isContextual = false;
} }
else if (entityContext is Item item) else if (entityContext is Item item)
{ {
itemContext = item; itemContext = item;
characterContext = null;
hullContext = null;
wallContext = null;
isContextual = true; isContextual = true;
} }
else if (entityContext is Hull hull) else if (entityContext is Hull hull)
{ {
hullContext = hull; hullContext = hull;
characterContext = null;
itemContext = null;
isContextual = true; isContextual = true;
} }
@@ -1785,7 +1849,7 @@ namespace Barotrauma
availableCategories = new List<OrderCategory>(); availableCategories = new List<OrderCategory>();
foreach (OrderCategory category in Enum.GetValues(typeof(OrderCategory))) foreach (OrderCategory category in Enum.GetValues(typeof(OrderCategory)))
{ {
if (Order.PrefabList.Any(o => o.Category == category && !o.TargetAllCharacters)) if (Order.PrefabList.Any(o => o.Category == category && !o.IsReport))
{ {
availableCategories.Add(category); availableCategories.Add(category);
} }
@@ -2129,7 +2193,7 @@ namespace Barotrauma
if (Order.GetPrefab(orderIdentifier) is Order orderPrefab && if (Order.GetPrefab(orderIdentifier) is Order orderPrefab &&
shortcutNodes.None(n => (n.UserData is Order order && order.Identifier == orderIdentifier) || shortcutNodes.None(n => (n.UserData is Order order && order.Identifier == orderIdentifier) ||
(n.UserData is Tuple<Order, string> orderWithOption && orderWithOption.Item1.Identifier == orderIdentifier)) && (n.UserData is Tuple<Order, string> orderWithOption && orderWithOption.Item1.Identifier == orderIdentifier)) &&
!orderPrefab.TargetAllCharacters && orderPrefab.Category != null) !orderPrefab.IsReport && orderPrefab.Category != null)
{ {
if (!orderPrefab.MustSetTarget || orderPrefab.GetMatchingItems(sub, true).Any()) if (!orderPrefab.MustSetTarget || orderPrefab.GetMatchingItems(sub, true).Any())
{ {
@@ -2172,7 +2236,7 @@ namespace Barotrauma
private void CreateOrderNodes(OrderCategory orderCategory) private void CreateOrderNodes(OrderCategory orderCategory)
{ {
var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.TargetAllCharacters); var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.IsReport);
Order order; Order order;
bool disableNode; bool disableNode;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
@@ -2250,34 +2314,38 @@ namespace Barotrauma
if (contextualOrders.None()) if (contextualOrders.None())
{ {
orderIdentifier = "cleanupitems"; orderIdentifier = "cleanupitems";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled)) if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false))
{ {
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled)); contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled));
} }
} }
AddIgnoreOrder(itemContext);
} }
else if (hullContext != null) else if (hullContext != null)
{ {
contextualOrders.Add(new Order(Order.GetPrefab("fixleaks"), hullContext, targetItem: null, Character.Controlled)); contextualOrders.Add(new Order(Order.GetPrefab("fixleaks"), hullContext, targetItem: null, Character.Controlled));
if (wallContext != null)
{
AddIgnoreOrder(wallContext);
}
} }
if (contextualOrders.None(o => o.Category != OrderCategory.Movement)) orderIdentifier = "wait";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{ {
orderIdentifier = "wait"; Vector2 position = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
Hull hull = Hull.FindHull(position, guess: Character.Controlled?.CurrentHull);
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), new OrderTarget(position, hull), Character.Controlled));
}
if (contextualOrders.None(o => o.Category != OrderCategory.Movement) && characters.Any(c => c != Character.Controlled))
{
orderIdentifier = "follow";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier))) if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{ {
Vector2 position = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); contextualOrders.Add(Order.GetPrefab(orderIdentifier));
Hull hull = Hull.FindHull(position, guess: Character.Controlled?.CurrentHull);
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), new OrderTarget(position, hull), Character.Controlled));
}
if (characters.Any(c => c != Character.Controlled))
{
orderIdentifier = "follow";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
contextualOrders.Add(Order.GetPrefab(orderIdentifier));
}
} }
} }
@@ -2298,6 +2366,35 @@ namespace Barotrauma
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false), CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
!disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None)); !disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None));
} }
void AddIgnoreOrder(ISpatialEntity target)
{
var orderIdentifier = "ignorethis";
if (!target.IgnoreByAI && contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
AddOrder(orderIdentifier, target);
}
else
{
orderIdentifier = "unignorethis";
if (target.IgnoreByAI && contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
AddOrder(orderIdentifier, target);
}
}
void AddOrder(string id, ISpatialEntity target)
{
if (target is WallSection ws)
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), ws.Wall, ws.Wall.Sections.IndexOf(ws), orderGiver: Character.Controlled));
}
else
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), target as Entity, null, Character.Controlled));
}
}
}
} }
// TODO: there's duplicate logic here and above -> would be better to refactor so that the conditions are only defined in one place // TODO: there's duplicate logic here and above -> would be better to refactor so that the conditions are only defined in one place
@@ -2306,7 +2403,7 @@ namespace Barotrauma
if (Order.PrefabList.Any(o => o.TargetItems.Length > 0 && o.TargetItems.Contains(item.Prefab.Identifier))) { return true; } if (Order.PrefabList.Any(o => o.TargetItems.Length > 0 && o.TargetItems.Contains(item.Prefab.Identifier))) { return true; }
if (Order.PrefabList.Any(o => item.HasTag(o.TargetItems))) { return true; } if (Order.PrefabList.Any(o => item.HasTag(o.TargetItems))) { return true; }
if (Order.PrefabList.Any(o => o.TryGetTargetItemComponent(item, out _))) { return true; } if (Order.PrefabList.Any(o => o.TryGetTargetItemComponent(item, out _))) { return true; }
if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled)) { return true; } if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled, checkInventory: false)) { return true; }
if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) { return true; } if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) { return true; }
var operateWeaponsPrefab = Order.GetPrefab("operateweapons"); var operateWeaponsPrefab = Order.GetPrefab("operateweapons");
@@ -2367,11 +2464,13 @@ namespace Barotrauma
{ {
o = new Order(o.Prefab, orderTargetEntity, orderTargetEntity.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: order.OrderGiver); o = new Order(o.Prefab, orderTargetEntity, orderTargetEntity.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: order.OrderGiver);
} }
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o), o, null, Character.Controlled); var character = !o.TargetAllCharacters ? characterContext ?? GetCharacterForQuickAssignment(o) : null;
SetCharacterOrder(character, o, null, Character.Controlled);
DisableCommandUI(); DisableCommandUI();
} }
return true; return true;
}; };
// TODO: Might need to edit the tooltip
var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color, var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color,
tooltip: mustSetOptionOrTarget || characterContext != null ? order.Name : order.Name + tooltip: mustSetOptionOrTarget || characterContext != null ? order.Name : order.Name +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") + "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") +
@@ -2948,9 +3047,10 @@ namespace Barotrauma
return (degrees < 0) ? (degrees + 360) : degrees; return (degrees < 0) ? (degrees + 360) : degrees;
} }
private bool TryGetBreachedHullAtHoveredWall(out Hull breachedHull) private bool TryGetBreachedHullAtHoveredWall(out Hull breachedHull, out WallSection hoveredWall)
{ {
breachedHull = null; breachedHull = null;
hoveredWall = null;
// Based on the IsValidTarget() method of AIObjectiveFixLeaks class // Based on the IsValidTarget() method of AIObjectiveFixLeaks class
List<Gap> leaks = Gap.GapList.FindAll(g => List<Gap> leaks = Gap.GapList.FindAll(g =>
g != null && g.ConnectedWall != null && g.ConnectedDoor == null && g.Open > 0 && g.linkedTo.Any(l => l != null) && g != null && g.ConnectedWall != null && g.ConnectedDoor == null && g.Open > 0 && g.linkedTo.Any(l => l != null) &&
@@ -2962,6 +3062,15 @@ namespace Barotrauma
if (Submarine.RectContains(leak.ConnectedWall.WorldRect, mouseWorldPosition)) if (Submarine.RectContains(leak.ConnectedWall.WorldRect, mouseWorldPosition))
{ {
breachedHull = leak.FlowTargetHull; breachedHull = leak.FlowTargetHull;
foreach (var section in leak.ConnectedWall.Sections)
{
if (Submarine.RectContains(section.WorldRect, mouseWorldPosition))
{
hoveredWall = section;
break;
}
}
return true; return true;
} }
} }
@@ -20,6 +20,7 @@ namespace Barotrauma
protected GUIButton endRoundButton; protected GUIButton endRoundButton;
public GUIButton ReadyCheckButton;
public GUIButton EndRoundButton => endRoundButton; public GUIButton EndRoundButton => endRoundButton;
protected GUIFrame campaignUIContainer; protected GUIFrame campaignUIContainer;
@@ -142,7 +143,8 @@ namespace Barotrauma
if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition")) if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"))
{ {
endRoundButton.Visible = false; endRoundButton.Visible = false;
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = false; }
return; return;
} }
if (Submarine.MainSub == null) { return; } if (Submarine.MainSub == null) { return; }
@@ -188,6 +190,8 @@ namespace Barotrauma
break; break;
} }
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = endRoundButton.Visible; }
if (endRoundButton.Visible) if (endRoundButton.Visible)
{ {
if (!AllowedToEndRound()) { buttonText = TextManager.Get("map"); } if (!AllowedToEndRound()) { buttonText = TextManager.Get("map"); }
@@ -211,6 +215,10 @@ namespace Barotrauma
} }
} }
endRoundButton.DrawManually(spriteBatch); endRoundButton.DrawManually(spriteBatch);
if (this is MultiPlayerCampaign)
{
ReadyCheckButton?.DrawManually(spriteBatch);
}
} }
public Task SelectSummaryScreen(RoundSummary roundSummary, LevelData newLevel, bool mirror, Action action) public Task SelectSummaryScreen(RoundSummary roundSummary, LevelData newLevel, bool mirror, Action action)
@@ -279,6 +287,7 @@ namespace Barotrauma
base.AddToGUIUpdateList(); base.AddToGUIUpdateList();
CrewManager.AddToGUIUpdateList(); CrewManager.AddToGUIUpdateList();
endRoundButton.AddToGUIUpdateList(); endRoundButton.AddToGUIUpdateList();
ReadyCheckButton?.AddToGUIUpdateList();
} }
public override void Update(float deltaTime) public override void Update(float deltaTime)
@@ -34,7 +34,6 @@ namespace Barotrauma
} }
} }
public static void StartCampaignSetup(IEnumerable<string> saveFiles) public static void StartCampaignSetup(IEnumerable<string> saveFiles)
{ {
var parent = GameMain.NetLobbyScreen.CampaignSetupFrame; var parent = GameMain.NetLobbyScreen.CampaignSetupFrame;
@@ -103,11 +102,13 @@ namespace Barotrauma
{ {
CanBeFocused = false CanBeFocused = false
}; };
int buttonHeight = (int)(GUI.Scale * 40);
int buttonWidth = GUI.IntScale(450);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUICanvas.Instance), int buttonHeight = (int) (GUI.Scale * 40),
buttonWidth = GUI.IntScale(450),
buttonCenter = buttonHeight / 2,
screenMiddle = GameMain.GraphicsWidth / 2;
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle - buttonWidth / 2, HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, buttonWidth, buttonHeight), GUICanvas.Instance),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton") TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
{ {
Pulse = true, Pulse = true,
@@ -140,6 +141,25 @@ namespace Barotrauma
return true; return true;
} }
}; };
int readyButtonHeight = buttonHeight;
int readyButtonWidth = (int) (GUI.Scale * 50);
ReadyCheckButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle + (buttonWidth / 2) + GUI.IntScale(16), HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, readyButtonWidth, readyButtonHeight), GUICanvas.Instance),
style: "RepairBuyButton")
{
ToolTip = TextManager.Get("ReadyCheck.Tooltip"),
OnClicked = delegate
{
if (CrewManager != null && CrewManager.ActiveReadyCheck == null)
{
ReadyCheck.CreateReadyCheck();
}
return true;
},
UserData = "ReadyCheckButton"
};
buttonContainer.Recalculate(); buttonContainer.Recalculate();
} }
@@ -378,6 +398,7 @@ namespace Barotrauma
if (!GUI.DisableHUD && !GUI.DisableUpperHUD) if (!GUI.DisableHUD && !GUI.DisableUpperHUD)
{ {
endRoundButton.UpdateManually(deltaTime); endRoundButton.UpdateManually(deltaTime);
ReadyCheckButton?.UpdateManually(deltaTime);
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || ForceMapUI) { return; } if (CoroutineManager.IsCoroutineRunning("LevelTransition") || ForceMapUI) { return; }
} }
@@ -368,6 +368,9 @@ namespace Barotrauma
SoundPlayer.OverrideMusicDuration = 18.0f; SoundPlayer.OverrideMusicDuration = 18.0f;
crewDead = false; crewDead = false;
LevelData lvlData = GameMain.GameSession.LevelData;
bool beaconActive = GameMain.GameSession.Level.CheckBeaconActive();
GameMain.GameSession.EndRound("", traitorResults, transitionType); GameMain.GameSession.EndRound("", traitorResults, transitionType);
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton; var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
RoundSummary roundSummary = null; RoundSummary roundSummary = null;
@@ -452,6 +455,8 @@ namespace Barotrauma
} }
} }
lvlData.IsBeaconActive = beaconActive;
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
} }
else else
@@ -39,6 +39,10 @@ namespace Barotrauma
base.Start(); base.Start();
CrewManager.InitSinglePlayerRound(); CrewManager.InitSinglePlayerRound();
foreach (Submarine submarine in Submarine.Loaded)
{
submarine.NeutralizeBallast();
}
if (SpawnOutpost) if (SpawnOutpost)
{ {
@@ -121,7 +121,7 @@ namespace Barotrauma.Tutorials
return; return;
} }
character = Character.Create(charInfo, wayPoint.WorldPosition, "", false, false); character = Character.Create(charInfo, wayPoint.WorldPosition, "", isRemotePlayer: false, hasAi: false);
character.TeamID = Character.TeamType.Team1; character.TeamID = Character.TeamType.Team1;
Character.Controlled = character; Character.Controlled = character;
character.GiveJobItems(null); character.GiveJobItems(null);
@@ -0,0 +1,287 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
internal partial class ReadyCheck
{
private static string readyCheckBody(string name) => string.IsNullOrWhiteSpace(name) ? TextManager.Get("readycheck.serverbody") : TextManager.GetWithVariable("readycheck.body", "[player]", name);
private static string readyCheckStatus(int ready, int total) => TextManager.GetWithVariables("readycheck.readycount", new[] { "[ready]", "[total]" }, new[] { ready.ToString(), total.ToString() });
private static string readyCheckPleaseWait(int seconds) => TextManager.GetWithVariable("readycheck.pleasewait", "[seconds]", seconds.ToString());
private static readonly string readyCheckHeader = TextManager.Get("ReadyCheck.Title");
private static readonly string noButton = TextManager.Get("No"),
yesButton = TextManager.Get("Yes"),
closeButton = TextManager.Get("Close");
private const string TimerData = "Timer",
PromptData = "ReadyCheck",
ResultData = "ReadyCheckResults",
UserListData = "ReadyUserList",
ReadySpriteData = "ReadySprite";
private int lastSecond;
private GUIMessageBox? msgBox;
private GUIMessageBox? resultsBox;
public static DateTime lastReadyCheck = DateTime.MinValue;
private void CreateMessageBox(string author)
{
Vector2 relativeSize = new Vector2(GUI.IsFourByThree() ? 0.3f : 0.2f, 0.15f);
Point minSize = new Point(300, 200);
msgBox = new GUIMessageBox(readyCheckHeader, readyCheckBody(author), new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable = true };
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.125f), msgBox.Content.RectTransform), childAnchor: Anchor.Center);
new GUIProgressBar(new RectTransform(new Vector2(0.8f, 1f), contentLayout.RectTransform), time / endTime, GUI.Style.Orange) { UserData = TimerData };
// Yes
msgBox.Buttons[0].OnClicked = delegate
{
msgBox.Close();
SendState(ReadyStatus.Yes);
CreateResultsMessage();
return true;
};
// No
msgBox.Buttons[1].OnClicked = delegate
{
msgBox.Close();
SendState(ReadyStatus.No);
CreateResultsMessage();
return true;
};
}
private void CreateResultsMessage()
{
Vector2 relativeSize = new Vector2(0.2f, 0.3f);
Point minSize = new Point(300, 400);
resultsBox = new GUIMessageBox(readyCheckHeader, string.Empty, new[] { closeButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = ResultData, Draggable = true };
if (msgBox != null)
{
resultsBox.RectTransform.ScreenSpaceOffset = msgBox.RectTransform.ScreenSpaceOffset;
}
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.8f), resultsBox.Content.RectTransform)) { UserData = UserListData };
foreach (var (id, _) in Clients)
{
Client? client = GameMain.Client.ConnectedClients.FirstOrDefault(c => c.ID == id);
GUIFrame container = new GUIFrame(new RectTransform(new Vector2(1f, 0.15f), listBox.Content.RectTransform), style: "ListBoxElement") { UserData = id };
GUILayoutGroup frame = new GUILayoutGroup(new RectTransform(Vector2.One, container.RectTransform), isHorizontal: true) { Stretch = true };
int height = frame.Rect.Height;
JobPrefab? jobPrefab = client?.Character?.Info?.Job?.Prefab;
if (client == null)
{
string list = GameMain.Client.ConnectedClients.Aggregate("Available clients:\n", (current, c) => current + $"{c.ID}: {c.Name}\n");
DebugConsole.ThrowError($"Client ID {id} was reported in ready check but was not found.\n" + list.TrimEnd('\n'));
}
if (jobPrefab?.Icon != null)
{
// job icon
new GUIImage(new RectTransform(new Point(height, height), frame.RectTransform), jobPrefab.Icon, scaleToFit: true) { Color = jobPrefab.UIColor };
}
new GUITextBlock(new RectTransform(new Vector2(0.75f, 1), frame.RectTransform), client?.Name ?? $"Unknown ID {id}", jobPrefab?.UIColor ?? Color.White, textAlignment: Alignment.Center) { AutoScaleHorizontal = true };
new GUIImage(new RectTransform(new Point(height, height), frame.RectTransform), null, scaleToFit: true) { UserData = ReadySpriteData };
}
resultsBox.Buttons[0].OnClicked = delegate
{
resultsBox.Close();
return true;
};
}
private void UpdateBar()
{
if (msgBox != null && !msgBox.Closed && GUIMessageBox.MessageBoxes.Contains(msgBox))
{
if (msgBox.FindChild(TimerData, true) is GUIProgressBar bar)
{
bar.BarSize = time / endTime;
}
}
// play click sound after a second has passed
int second = (int) Math.Ceiling(time);
if (second < lastSecond)
{
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
lastSecond = second;
}
}
public static void ClientRead(IReadMessage inc)
{
ReadyCheckState state = (ReadyCheckState) inc.ReadByte();
CrewManager? crewManager = GameMain.GameSession?.CrewManager;
List<Client> otherClients = GameMain.Client.ConnectedClients;
if (crewManager == null || otherClients == null)
{
if (state == ReadyCheckState.Start)
{
SendState(ReadyStatus.No);
}
return;
}
switch (state)
{
case ReadyCheckState.Start:
bool isOwn = false;
byte authorId = 0;
float duration = inc.ReadSingle();
string author = inc.ReadString();
bool hasAuthor = inc.ReadBoolean();
if (hasAuthor)
{
authorId = inc.ReadByte();
isOwn = authorId == GameMain.Client.ID;
}
ushort clientCount = inc.ReadUInt16();
List<byte> clients = new List<byte>();
for (int i = 0; i < clientCount; i++)
{
clients.Add(inc.ReadByte());
}
ReadyCheck rCheck = new ReadyCheck(clients, duration);
crewManager.ActiveReadyCheck = rCheck;
if (isOwn)
{
SendState(ReadyStatus.Yes);
rCheck.CreateResultsMessage();
}
else
{
rCheck.CreateMessageBox(author);
}
if (hasAuthor && rCheck.Clients.ContainsKey(authorId))
{
rCheck.Clients[authorId] = ReadyStatus.Yes;
}
break;
case ReadyCheckState.Update:
float time = inc.ReadSingle();
ReadyStatus newState = (ReadyStatus) inc.ReadByte();
byte targetId = inc.ReadByte();
if (crewManager.ActiveReadyCheck != null)
{
crewManager.ActiveReadyCheck.time = time;
crewManager.ActiveReadyCheck?.UpdateState(targetId, newState);
}
break;
case ReadyCheckState.End:
ushort count = inc.ReadUInt16();
for (int i = 0; i < count; i++)
{
byte id = inc.ReadByte();
ReadyStatus status = (ReadyStatus) inc.ReadByte();
crewManager.ActiveReadyCheck?.UpdateState(id, status);
}
crewManager.ActiveReadyCheck?.EndReadyCheck();
crewManager.ActiveReadyCheck?.msgBox?.Close();
crewManager.ActiveReadyCheck = null;
break;
}
}
partial void EndReadyCheck()
{
if (IsFinished) { return; }
IsFinished = true;
int readyCount = Clients.Count(pair => pair.Value == ReadyStatus.Yes);
int totalCount = Clients.Count;
GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, readyCheckStatus(readyCount, totalCount), ChatMessageType.Server, null));
}
private void UpdateState(byte id, ReadyStatus status)
{
if (Clients.ContainsKey(id))
{
Clients[id] = status;
}
if (resultsBox == null || resultsBox.Closed || !GUIMessageBox.MessageBoxes.Contains(resultsBox)) { return; }
if (resultsBox.Content.FindChild(UserListData) is GUIListBox userList)
{
// for some reason FindChild doesn't work here?
foreach (GUIComponent child in userList.Content.Children)
{
if (!(child.UserData is byte b) || b != id) { continue; }
if (child.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is GUIImage image)
{
string style;
switch (status)
{
case ReadyStatus.Yes:
style = "MissionCompletedIcon";
break;
case ReadyStatus.No:
style = "MissionFailedIcon";
break;
default:
return;
}
image.ApplyStyle(GUI.Style.GetComponentStyle(style));
}
}
}
}
private static void SendState(ReadyStatus status)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ClientPacketHeader.READY_CHECK);
msg.Write((byte) ReadyCheckState.Update);
msg.Write((byte) status);
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
}
public static void CreateReadyCheck()
{
if (lastReadyCheck < DateTime.Now)
{
#if !DEBUG
lastReadyCheck = DateTime.Now.AddMinutes(1);
#endif
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ClientPacketHeader.READY_CHECK);
msg.Write((byte) ReadyCheckState.Start);
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
return;
}
GUIMessageBox msgBox = new GUIMessageBox(readyCheckHeader, readyCheckPleaseWait((lastReadyCheck - DateTime.Now).Seconds), new[] { closeButton });
msgBox.Buttons[0].OnClicked = delegate
{
msgBox.Close();
return true;
};
}
}
}
@@ -503,7 +503,7 @@ namespace Barotrauma
Character character = characterInfo.Character; Character character = characterInfo.Character;
if (character == null || character.IsDead) if (character == null || character.IsDead)
{ {
if (character == null && characterInfo.IsNewHire) if (character == null && characterInfo.IsNewHire && characterInfo.CauseOfDeath == null)
{ {
statusText = TextManager.Get("CampaignCrew.NewHire"); statusText = TextManager.Get("CampaignCrew.NewHire");
statusColor = GUI.Style.Blue; statusColor = GUI.Style.Blue;
@@ -65,10 +65,8 @@ namespace Barotrauma
keyMapping[(int)InputType.Voice] = new KeyOrMouse(Keys.V); keyMapping[(int)InputType.Voice] = new KeyOrMouse(Keys.V);
keyMapping[(int)InputType.LocalVoice] = new KeyOrMouse(Keys.B); keyMapping[(int)InputType.LocalVoice] = new KeyOrMouse(Keys.B);
keyMapping[(int)InputType.Command] = new KeyOrMouse(MouseButton.MiddleMouse); keyMapping[(int)InputType.Command] = new KeyOrMouse(MouseButton.MiddleMouse);
#if DEBUG
keyMapping[(int)InputType.PreviousFireMode] = new KeyOrMouse(MouseButton.MouseWheelDown); keyMapping[(int)InputType.PreviousFireMode] = new KeyOrMouse(MouseButton.MouseWheelDown);
keyMapping[(int)InputType.NextFireMode] = new KeyOrMouse(MouseButton.MouseWheelUp); keyMapping[(int)InputType.NextFireMode] = new KeyOrMouse(MouseButton.MouseWheelUp);
#endif
if (Language == "French") if (Language == "French")
{ {
@@ -1335,7 +1333,7 @@ namespace Barotrauma
}; };
msgBox.Buttons[0].OnClicked = (yesButton, obj) => msgBox.Buttons[0].OnClicked = (yesButton, obj) =>
{ {
LoadDefaultConfig(setLanguage: false); LoadDefaultConfig(setLanguage: false, loadContentPackages: Screen.Selected != GameMain.GameScreen);
CheckBindings(true); CheckBindings(true);
RefreshItemMessages(); RefreshItemMessages();
ApplySettings(); ApplySettings();
@@ -291,6 +291,7 @@ namespace Barotrauma.Items.Components
bool broken = msg.ReadBoolean(); bool broken = msg.ReadBoolean();
bool forcedOpen = msg.ReadBoolean(); bool forcedOpen = msg.ReadBoolean();
bool isStuck = msg.ReadBoolean(); bool isStuck = msg.ReadBoolean();
bool isJammed = msg.ReadBoolean();
SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen); SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen);
stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8); stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
UInt16 lastUserID = msg.ReadUInt16(); UInt16 lastUserID = msg.ReadUInt16();
@@ -301,6 +302,7 @@ namespace Barotrauma.Items.Components
toggleCooldownTimer = ToggleCoolDown; toggleCooldownTimer = ToggleCoolDown;
} }
this.isStuck = isStuck; this.isStuck = isStuck;
this.isJammed = isJammed;
if (isStuck) { OpenState = 0.0f; } if (isStuck) { OpenState = 0.0f; }
IsBroken = broken; IsBroken = broken;
PredictedState = null; PredictedState = null;
@@ -8,46 +8,6 @@ using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
internal partial class VineTile
{
public void Draw(SpriteBatch spriteBatch, Vector2 position, float depth, float leafDepth)
{
Vector2 pos = position + Position;
pos.Y = -pos.Y;
VineSprite vineSprite = Parent.VineSprites[Type];
Color color = Parent.Decayed ? Parent.DeadTint : Parent.VineTint;
float layer1 = depth + 0.01f, // flowers
layer2 = depth + 0.02f, // decay atlas
layer3 = depth + 0.03f; // branches and leaves
float scale = Parent.VineScale * VineStep;
if (Parent.VineAtlas != null)
{
spriteBatch.Draw(Parent.VineAtlas.Texture, pos + offset, vineSprite.SourceRect, color, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer3);
}
if (Parent.DecayAtlas != null)
{
spriteBatch.Draw(Parent.DecayAtlas.Texture, pos, vineSprite.SourceRect, HealthColor, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer2);
}
if (FlowerConfig.Variant >= 0 && !Parent.Decayed)
{
Sprite flowerSprite = Parent.FlowerSprites[FlowerConfig.Variant];
flowerSprite.Draw(spriteBatch, pos, Parent.FlowerTint, flowerSprite.Origin, scale: Parent.BaseFlowerScale * FlowerConfig.Scale * FlowerStep, rotate: FlowerConfig.Rotation, depth: layer1);
}
if (LeafConfig.Variant >= 0)
{
Sprite leafSprite = Parent.LeafSprites[LeafConfig.Variant];
leafSprite.Draw(spriteBatch, pos, Parent.Decayed ? Parent.DeadTint : Parent.LeafTint, leafSprite.Origin, scale: Parent.BaseLeafScale * LeafConfig.Scale * FlowerStep, rotate: LeafConfig.Rotation, depth: layer3 + leafDepth);
}
}
}
internal class VineSprite internal class VineSprite
{ {
[Serialize("0,0,0,0", false)] [Serialize("0,0,0,0", false)]
@@ -97,7 +57,7 @@ namespace Barotrauma.Items.Components
foreach (VineTile vine in Vines) foreach (VineTile vine in Vines)
{ {
leafDepth += zStep; leafDepth += zStep;
vine.Draw(spriteBatch, planter.Item.DrawPosition + offset, depth, leafDepth); DrawBranch(vine, spriteBatch, planter.Item.DrawPosition + offset, depth, leafDepth);
} }
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
@@ -111,6 +71,43 @@ namespace Barotrauma.Items.Components
} }
} }
} }
private void DrawBranch(VineTile vine, SpriteBatch spriteBatch, Vector2 position, float depth, float leafDepth)
{
Vector2 pos = position + vine.Position;
pos.Y = -pos.Y;
VineSprite vineSprite = VineSprites[vine.Type];
Color color = Decayed ? DeadTint : VineTint;
float layer1 = depth + 0.01f, // flowers
layer2 = depth + 0.02f, // decay atlas
layer3 = depth + 0.03f; // branches and leaves
float scale = VineScale * vine.VineStep;
if (VineAtlas != null && VineAtlas.Loaded)
{
spriteBatch.Draw(VineAtlas.Texture, pos + vine.offset, vineSprite.SourceRect, color, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer3);
}
if (DecayAtlas != null && DecayAtlas.Loaded)
{
spriteBatch.Draw(DecayAtlas.Texture, pos, vineSprite.SourceRect, vine.HealthColor, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer2);
}
if (vine.FlowerConfig.Variant >= 0 && !Decayed)
{
Sprite flowerSprite = FlowerSprites[vine.FlowerConfig.Variant];
flowerSprite.Draw(spriteBatch, pos, FlowerTint, flowerSprite.Origin, scale: BaseFlowerScale * vine.FlowerConfig.Scale * vine.FlowerStep, rotate: vine.FlowerConfig.Rotation, depth: layer1);
}
if (vine.LeafConfig.Variant >= 0)
{
Sprite leafSprite = LeafSprites[vine.LeafConfig.Variant];
leafSprite.Draw(spriteBatch, pos, Decayed ? DeadTint : LeafTint, leafSprite.Origin, scale: BaseLeafScale * vine.LeafConfig.Scale * vine.FlowerStep, rotate: vine.LeafConfig.Rotation, depth: layer3 + leafDepth);
}
}
partial void LoadVines(XElement element) partial void LoadVines(XElement element)
{ {
@@ -2,9 +2,6 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -15,10 +12,16 @@ namespace Barotrauma.Items.Components
get { return item.Rect.Size.ToVector2(); } get { return item.Rect.Size.ToVector2(); }
} }
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{ {
if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) { return; } if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled)
{
Drawable = false;
return;
}
Vector2 gridPos = picker.Position; Vector2 gridPos = picker.Position;
Vector2 roundedGridPos = new Vector2( Vector2 roundedGridPos = new Vector2(
MathUtils.RoundTowardsClosest(picker.Position.X, Submarine.GridSize.X), MathUtils.RoundTowardsClosest(picker.Position.X, Submarine.GridSize.X),
@@ -0,0 +1,13 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
namespace Barotrauma.Items.Components
{
partial class IdCard
{
public Sprite StoredPortrait;
public Vector2 StoredSheetIndex;
public JobPrefab StoredJobPrefab;
public List<WearableSprite> StoredAttachments;
}
}
@@ -55,13 +55,8 @@ namespace Barotrauma.Items.Components
{ {
if (character == null || !character.IsKeyDown(InputType.Aim)) return; if (character == null || !character.IsKeyDown(InputType.Aim)) return;
#if DEBUG
if (PlayerInput.KeyHit(InputType.PreviousFireMode)) if (PlayerInput.KeyHit(InputType.PreviousFireMode))
#else
if (PlayerInput.MouseWheelDownClicked())
#endif
{ {
if (spraySetting > 0) if (spraySetting > 0)
{ {
spraySetting--; spraySetting--;
@@ -74,11 +69,7 @@ namespace Barotrauma.Items.Components
targetSections.Clear(); targetSections.Clear();
} }
#if DEBUG
if (PlayerInput.KeyHit(InputType.NextFireMode)) if (PlayerInput.KeyHit(InputType.NextFireMode))
#else
if (PlayerInput.MouseWheelUpClicked())
#endif
{ {
if (spraySetting < 2) if (spraySetting < 2)
{ {
@@ -257,6 +257,8 @@ namespace Barotrauma.Items.Components
spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically; spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
} }
bool isWiringMode = SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode();
int i = 0; int i = 0;
foreach (Item containedItem in Inventory.Items) foreach (Item containedItem in Inventory.Items)
{ {
@@ -278,7 +280,7 @@ namespace Barotrauma.Items.Components
containedItem.Sprite.Draw( containedItem.Sprite.Draw(
spriteBatch, spriteBatch,
new Vector2(currentItemPos.X, -currentItemPos.Y), new Vector2(currentItemPos.X, -currentItemPos.Y),
containedItem.GetSpriteColor(), isWiringMode ? containedItem.GetSpriteColor() * 0.15f : containedItem.GetSpriteColor(),
origin, origin,
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation + MathHelper.ToRadians(-item.Rotation)), -(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation + MathHelper.ToRadians(-item.Rotation)),
containedItem.Scale, containedItem.Scale,
@@ -95,23 +95,21 @@ namespace Barotrauma.Items.Components
// TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental. // TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental.
// Only do this if there's only one linked component. If you link more containers then may // Only do this if there's only one linked component. If you link more containers then may
// GUI.PreventElementOverlap have mercy on your HUD layout // GUI.PreventElementOverlap have mercy on your HUD layout
if (item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1) if (GuiFrame != null && item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1)
{ {
foreach (MapEntity linkedTo in item.linkedTo) foreach (MapEntity linkedTo in item.linkedTo)
{ {
if (!(linkedTo is Item linkedItem)) continue; if (!(linkedTo is Item linkedItem) || !linkedItem.DisplaySideBySideWhenLinked) { continue; }
if (!linkedItem.Components.Any()) continue; if (!linkedItem.Components.Any()) { continue; }
var itemContainer = linkedItem.Components.First(); var itemContainer = linkedItem.GetComponent<ItemContainer>();
if (itemContainer == null) { continue; } if (itemContainer?.GuiFrame == null || itemContainer.AllowUIOverlap) { continue; }
if (!itemContainer.Item.DisplaySideBySideWhenLinked) continue;
// how much spacing do we want between the components // how much spacing do we want between the components
var padding = (int) (8 * GUI.Scale); var padding = (int) (8 * GUI.Scale);
// Move the linked container to the right and move the fabricator to the left // Move the linked container to the right and move the deconstructor to the left
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(GuiFrame.Rect.Width / -2 - padding, 0); itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(100, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0); GuiFrame.RectTransform.AbsoluteOffset = new Point(-100, 0);
} }
} }
return base.Select(character); return base.Select(character);
@@ -212,23 +212,21 @@ namespace Barotrauma.Items.Components
// TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental. // TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental.
// Only do this if there's only one linked component. If you link more containers then may // Only do this if there's only one linked component. If you link more containers then may
// GUI.PreventElementOverlap have mercy on your HUD layout // GUI.PreventElementOverlap have mercy on your HUD layout
if (item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1) if (GuiFrame != null && item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1)
{ {
foreach (MapEntity linkedTo in item.linkedTo) foreach (MapEntity linkedTo in item.linkedTo)
{ {
if (!(linkedTo is Item linkedItem)) continue; if (!(linkedTo is Item linkedItem) || !linkedItem.DisplaySideBySideWhenLinked) { continue; }
if (!linkedItem.Components.Any()) continue; if (!linkedItem.Components.Any()) { continue; }
var itemContainer = linkedItem.Components.First();
if (itemContainer == null) { continue; }
if (!itemContainer.Item.DisplaySideBySideWhenLinked) continue; var itemContainer = linkedItem.GetComponent<ItemContainer>();
if (itemContainer?.GuiFrame == null || itemContainer.AllowUIOverlap) { continue; }
// how much spacing do we want between the components // how much spacing do we want between the components
var padding = (int) (8 * GUI.Scale); var padding = (int) (8 * GUI.Scale);
// Move the linked container to the right and move the fabricator to the left // Move the linked container to the right and move the fabricator to the left
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(GuiFrame.Rect.Width / -2 - padding, 0); itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(-100, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0); GuiFrame.RectTransform.AbsoluteOffset = new Point(100, 0);
} }
} }
@@ -73,7 +73,7 @@ namespace Barotrauma.Items.Components
{ {
OnClicked = (button, data) => OnClicked = (button, data) =>
{ {
targetLevel = null; TargetLevel = null;
IsActive = !IsActive; IsActive = !IsActive;
if (GameMain.Client != null) if (GameMain.Client != null)
{ {
@@ -112,7 +112,7 @@ namespace Barotrauma.Items.Components
{ {
if (pumpSpeedLockTimer <= 0.0f) if (pumpSpeedLockTimer <= 0.0f)
{ {
targetLevel = null; TargetLevel = null;
} }
float newValue = barScroll * 200.0f - 100.0f; float newValue = barScroll * 200.0f - 100.0f;
if (Math.Abs(newValue - FlowPercentage) < 0.1f) { return false; } if (Math.Abs(newValue - FlowPercentage) < 0.1f) { return false; }
@@ -215,14 +215,33 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
int msgStartPos = msg.BitPosition;
float flowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
bool isActive = msg.ReadBoolean();
bool hijacked = msg.ReadBoolean();
float? targetLevel;
if (msg.ReadBoolean())
{
targetLevel = msg.ReadSingle();
}
else
{
targetLevel = null;
}
if (correctionTimer > 0.0f) if (correctionTimer > 0.0f)
{ {
StartDelayedCorrection(type, msg.ExtractBits(5 + 1), sendingTime); int msgLength = msg.BitPosition - msgStartPos;
msg.BitPosition = msgStartPos;
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
return; return;
} }
FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f; FlowPercentage = flowPercentage;
IsActive = msg.ReadBoolean(); IsActive = isActive;
Hijacked = hijacked;
TargetLevel = targetLevel;
} }
} }
} }
@@ -502,8 +502,16 @@ namespace Barotrauma.Items.Components
{ {
if (item.Removed) { return; } if (item.Removed) { return; }
Vector2 clampedOptimalTurbineOutput = optimalTurbineOutput;
Vector2 clampedAllowedTurbineOutput = allowedTurbineOutput;
if (clampedOptimalTurbineOutput.X > 100.0f)
{
clampedOptimalTurbineOutput = new Vector2(92.0f, 110.0f);
clampedAllowedTurbineOutput = new Vector2(85.0f, 110.0f);
}
DrawMeter(spriteBatch, container.Rect, DrawMeter(spriteBatch, container.Rect,
turbineOutputMeter, TurbineOutput, new Vector2(0.0f, 100.0f), optimalTurbineOutput, allowedTurbineOutput); turbineOutputMeter, TurbineOutput, new Vector2(0.0f, 100.0f), clampedOptimalTurbineOutput, clampedAllowedTurbineOutput);
} }
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
@@ -1,4 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Extensions;
using Barotrauma.Networking;
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -14,7 +15,8 @@ namespace Barotrauma.Items.Components
public enum BlipType public enum BlipType
{ {
Default, Default,
Disruption Disruption,
Destructible
} }
private PathFinder pathFinder; private PathFinder pathFinder;
@@ -28,11 +30,18 @@ namespace Barotrauma.Items.Components
private GUITickBox activeTickBox, passiveTickBox; private GUITickBox activeTickBox, passiveTickBox;
private GUITextBlock signalWarningText; private GUITextBlock signalWarningText;
private GUIFrame lowerAreaFrame;
private GUIScrollBar zoomSlider; private GUIScrollBar zoomSlider;
private GUIButton directionalModeSwitch; private GUIButton directionalModeSwitch;
private Vector2? pingDragDirection = null; private Vector2? pingDragDirection = null;
/// <summary>
/// Can be null if the property HasMineralScanner is false
/// </summary>
private GUIButton mineralScannerSwitch;
private GUIFrame controlContainer; private GUIFrame controlContainer;
private GUICustomComponent sonarView; private GUICustomComponent sonarView;
@@ -46,7 +55,7 @@ namespace Barotrauma.Items.Components
private Sprite sonarBlip; private Sprite sonarBlip;
private Sprite lineSprite; private Sprite lineSprite;
private readonly Dictionary<string, Sprite> targetIcons = new Dictionary<string, Sprite>(); private readonly Dictionary<string, Tuple<Sprite, Color>> targetIcons = new Dictionary<string, Tuple<Sprite, Color>>();
private float displayBorderSize; private float displayBorderSize;
@@ -60,10 +69,12 @@ namespace Barotrauma.Items.Components
private const float DisruptionUpdateInterval = 0.2f; private const float DisruptionUpdateInterval = 0.2f;
private float disruptionUpdateTimer; private float disruptionUpdateTimer;
private float zoomSqrt;
private float showDirectionalIndicatorTimer; private float showDirectionalIndicatorTimer;
private List<LevelObject> nearbyObjects = new List<LevelObject>();
private const float NearbyObjectUpdateInterval = 1.0f;
float nearbyObjectUpdateTimer;
//Vector2 = vector from the ping source to the position of the disruption //Vector2 = vector from the ping source to the position of the disruption
//float = strength of the disruption, between 0-1 //float = strength of the disruption, between 0-1
private readonly List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>(); private readonly List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>();
@@ -103,6 +114,10 @@ namespace Barotrauma.Items.Components
{ {
BlipType.Disruption, BlipType.Disruption,
new Color[] { Color.TransparentBlack, new Color(254, 68, 19), new Color(255, 220, 62), new Color(255, 255, 255) } new Color[] { Color.TransparentBlack, new Color(254, 68, 19), new Color(255, 220, 62), new Color(255, 255, 255) }
},
{
BlipType.Destructible,
new Color[] { Color.TransparentBlack, new Color(74, 113, 75) * 0.8f, new Color(151, 236, 172) * 0.8f, new Color(153, 217, 234) * 0.8f }
} }
}; };
@@ -114,6 +129,15 @@ namespace Barotrauma.Items.Components
public static Vector2 GUISizeCalculation => Vector2.One * Math.Min(GUI.RelativeHorizontalAspectRatio, 1f) * sonarAreaSize; public static Vector2 GUISizeCalculation => Vector2.One * Math.Min(GUI.RelativeHorizontalAspectRatio, 1f) * sonarAreaSize;
private List<Tuple<Vector2, List<Item>>> MineralClusters { get; set; }
private readonly List<GUITextBlock> textBlocksToScaleAndNormalize = new List<GUITextBlock>();
private bool isConnectedToSteering;
private bool AllowUsingMineralScanner =>
HasMineralScanner && !isConnectedToSteering;
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
{ {
System.Diagnostics.Debug.Assert(Enum.GetValues(typeof(BlipType)).Cast<BlipType>().All(t => blipColorGradient.ContainsKey(t))); System.Diagnostics.Debug.Assert(Enum.GetValues(typeof(BlipType)).Cast<BlipType>().All(t => blipColorGradient.ContainsKey(t)));
@@ -151,7 +175,9 @@ namespace Barotrauma.Items.Components
break; break;
case "icon": case "icon":
var targetIconSprite = new Sprite(subElement); var targetIconSprite = new Sprite(subElement);
targetIcons.Add(subElement.GetAttributeString("identifier", ""), targetIconSprite); var color = subElement.GetAttributeColor("color", Color.White);
targetIcons.Add(subElement.GetAttributeString("identifier", ""),
new Tuple<Sprite, Color>(targetIconSprite, color));
break; break;
} }
} }
@@ -166,16 +192,20 @@ namespace Barotrauma.Items.Components
protected override void CreateGUI() protected override void CreateGUI()
{ {
bool isConnectedToSteering = item.GetComponent<Steering>() != null; isConnectedToSteering = item.GetComponent<Steering>() != null;
Vector2 size = isConnectedToSteering ? controlBoxSize : new Vector2(controlBoxSize.X * 2.0f, controlBoxSize.Y); Vector2 size = isConnectedToSteering ? controlBoxSize : new Vector2(0.46f, 0.4f);
controlContainer = new GUIFrame(new RectTransform(size, GuiFrame.RectTransform, Anchor.BottomRight, Pivot.BottomLeft), "ItemUI"); controlContainer = new GUIFrame(new RectTransform(size, GuiFrame.RectTransform, Anchor.BottomLeft), "ItemUI");
if (!isConnectedToSteering && !GUI.IsFourByThree())
{
controlContainer.RectTransform.MaxSize = new Point((int)(380 * GUI.xScale), (int)(300 * GUI.yScale));
}
var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center) var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center)
{ {
AbsoluteOffset = GUIStyle.ItemFrameOffset AbsoluteOffset = GUIStyle.ItemFrameOffset
}, style: null); }, style: null);
// Based on the height difference to the steering control box so that the elements keep the same size // Based on the height difference to the steering control box so that the elements keep the same size
float extraHeight = 0.03f; float extraHeight = 0.0694f;
var sonarModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.TopCenter), style: null); var sonarModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.TopCenter), style: null);
SonarModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), sonarModeArea.RectTransform), string.Empty, style: "SwitchVertical") SonarModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), sonarModeArea.RectTransform), string.Empty, style: "SwitchVertical")
{ {
@@ -215,10 +245,15 @@ namespace Barotrauma.Items.Components
passiveTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor); passiveTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor);
activeTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor); activeTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor);
var lowerArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.BottomCenter), style: null); textBlocksToScaleAndNormalize.Clear();
var zoomContainer = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerArea.RectTransform, Anchor.TopCenter), style: null); textBlocksToScaleAndNormalize.Add(passiveTickBox.TextBlock);
textBlocksToScaleAndNormalize.Add(activeTickBox.TextBlock);
lowerAreaFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.BottomCenter), style: null);
var zoomContainer = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.TopCenter), style: null);
var zoomText = new GUITextBlock(new RectTransform(new Vector2(0.3f, 0.6f), zoomContainer.RectTransform, Anchor.CenterLeft), var zoomText = new GUITextBlock(new RectTransform(new Vector2(0.3f, 0.6f), zoomContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get("SonarZoom"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight); TextManager.Get("SonarZoom"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight);
textBlocksToScaleAndNormalize.Add(zoomText);
zoomSlider = new GUIScrollBar(new RectTransform(new Vector2(0.5f, 0.8f), zoomContainer.RectTransform, Anchor.CenterLeft) zoomSlider = new GUIScrollBar(new RectTransform(new Vector2(0.5f, 0.8f), zoomContainer.RectTransform, Anchor.CenterLeft)
{ {
RelativeOffset = new Vector2(0.35f, 0) RelativeOffset = new Vector2(0.35f, 0)
@@ -236,9 +271,10 @@ namespace Barotrauma.Items.Components
} }
}; };
new GUIFrame(new RectTransform(new Vector2(0.8f, 0.01f), paddedControlContainer.RectTransform, Anchor.Center), style: "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(0.8f, 0.01f), paddedControlContainer.RectTransform, Anchor.Center), style: "HorizontalLine")
{ UserData = "horizontalline" };
var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerArea.RectTransform, Anchor.BottomCenter), style: null); var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null);
directionalModeSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), directionalModeFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal") directionalModeSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), directionalModeFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
{ {
OnClicked = (button, data) => OnClicked = (button, data) =>
@@ -255,11 +291,20 @@ namespace Barotrauma.Items.Components
}; };
var directionalModeSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), directionalModeFrame.RectTransform, Anchor.CenterRight), var directionalModeSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), directionalModeFrame.RectTransform, Anchor.CenterRight),
TextManager.Get("SonarDirectionalPing"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft); TextManager.Get("SonarDirectionalPing"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft);
textBlocksToScaleAndNormalize.Add(directionalModeSwitchText);
if (AllowUsingMineralScanner)
{
AddMineralScannerSwitchToGUI();
}
else
{
mineralScannerSwitch = null;
}
GuiFrame.CanBeFocused = false; GuiFrame.CanBeFocused = false;
GUITextBlock.AutoScaleAndNormalize(passiveTickBox.TextBlock, activeTickBox.TextBlock, zoomText, directionalModeSwitchText); GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
sonarView = new GUICustomComponent(new RectTransform(Vector2.One * 0.7f, GuiFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight), sonarView = new GUICustomComponent(new RectTransform(Vector2.One * 0.7f, GuiFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight),
(spriteBatch, guiCustomComponent) => { DrawSonar(spriteBatch, guiCustomComponent.Rect); }, null); (spriteBatch, guiCustomComponent) => { DrawSonar(spriteBatch, guiCustomComponent.Rect); }, null);
@@ -271,11 +316,16 @@ namespace Barotrauma.Items.Components
if (isConnectedToSteering) if (isConnectedToSteering)
{ {
controlContainer.RectTransform.RelativeOffset = controlBoxOffset; controlContainer.RectTransform.RelativeOffset = controlBoxOffset;
controlContainer.RectTransform.SetPosition(Anchor.TopLeft); controlContainer.RectTransform.SetPosition(Anchor.TopRight);
sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest; sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest;
sonarView.RectTransform.SetPosition(Anchor.CenterRight); sonarView.RectTransform.SetPosition(Anchor.CenterLeft);
sonarView.RectTransform.Resize(GUISizeCalculation); sonarView.RectTransform.Resize(GUISizeCalculation);
GUITextBlock.AutoScaleAndNormalize(passiveTickBox.TextBlock, activeTickBox.TextBlock, zoomText, directionalModeSwitchText); GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
}
else if (GUI.RelativeHorizontalAspectRatio > 0.75f)
{
sonarView.RectTransform.RelativeOffset = new Vector2(0.13f * GUI.RelativeHorizontalAspectRatio, 0);
sonarView.RectTransform.SetPosition(Anchor.BottomRight);
} }
} }
@@ -293,10 +343,58 @@ namespace Barotrauma.Items.Components
{ {
base.OnItemLoaded(); base.OnItemLoaded();
zoomSlider.BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom); zoomSlider.BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom);
if (AllowUsingMineralScanner && mineralScannerSwitch == null)
{
AddMineralScannerSwitchToGUI();
GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
}
//make the sonarView customcomponent render the steering view so it gets drawn in front of the sonar //make the sonarView customcomponent render the steering view so it gets drawn in front of the sonar
item.GetComponent<Steering>()?.AttachToSonarHUD(sonarView); item.GetComponent<Steering>()?.AttachToSonarHUD(sonarView);
} }
private void AddMineralScannerSwitchToGUI()
{
// First adjust other elements to make room for the additional switch
controlContainer.RectTransform.RelativeSize = new Vector2(
controlContainer.RectTransform.RelativeSize.X,
controlContainer.RectTransform.RelativeSize.Y * 1.25f);
SonarModeSwitch.Parent.RectTransform.RelativeSize = new Vector2(
SonarModeSwitch.Parent.RectTransform.RelativeSize.X,
SonarModeSwitch.Parent.RectTransform.RelativeSize.Y * 0.8f);
lowerAreaFrame.Parent.GetChildByUserData("horizontalline").RectTransform.RelativeOffset =
new Vector2(0.0f, -0.1f);
lowerAreaFrame.RectTransform.RelativeSize = new Vector2(
lowerAreaFrame.RectTransform.RelativeSize.X,
lowerAreaFrame.RectTransform.RelativeSize.Y * 1.2f);
zoomSlider.Parent.RectTransform.RelativeSize = new Vector2(
zoomSlider.Parent.RectTransform.RelativeSize.X,
zoomSlider.Parent.RectTransform.RelativeSize.Y * (2.0f / 3.0f));
directionalModeSwitch.Parent.RectTransform.RelativeSize = new Vector2(
directionalModeSwitch.Parent.RectTransform.RelativeSize.X,
zoomSlider.Parent.RectTransform.RelativeSize.Y);
directionalModeSwitch.Parent.RectTransform.SetPosition(Anchor.Center);
// Then add the scanner switch
var mineralScannerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, zoomSlider.Parent.RectTransform.RelativeSize.Y), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null);
mineralScannerSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), mineralScannerFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
{
OnClicked = (button, data) =>
{
useMineralScanner = !useMineralScanner;
button.Selected = useMineralScanner;
if (GameMain.Client != null)
{
unsentChanges = true;
correctionTimer = CorrectionDelay;
}
return true;
}
};
var mineralScannerSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), mineralScannerFrame.RectTransform, Anchor.CenterRight),
TextManager.Get("SonarMineralScanner"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft);
textBlocksToScaleAndNormalize.Add(mineralScannerSwitchText);
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{ {
showDirectionalIndicatorTimer -= deltaTime; showDirectionalIndicatorTimer -= deltaTime;
@@ -339,6 +437,32 @@ namespace Barotrauma.Items.Components
Vector2.DistanceSquared(sonarView.Rect.Center.ToVector2(), PlayerInput.MousePosition) < Vector2.DistanceSquared(sonarView.Rect.Center.ToVector2(), PlayerInput.MousePosition) <
(sonarView.Rect.Width / 2 * sonarView.Rect.Width / 2); (sonarView.Rect.Width / 2 * sonarView.Rect.Width / 2);
if (AllowUsingMineralScanner && Level.Loaded != null && !Level.Loaded.Generating)
{
if (MineralClusters == null)
{
MineralClusters = new List<Tuple<Vector2, List<Item>>>();
foreach (var p in Level.Loaded.PathPoints)
{
foreach (var c in p.ClusterLocations)
{
if (c.Resources.None(i => i != null && !i.Removed && i.Tags.Contains("ore"))) { continue; }
var pos = Vector2.Zero;
foreach (var r in c.Resources)
{
pos += r.WorldPosition;
}
pos /= c.Resources.Count;
MineralClusters.Add(new Tuple<Vector2, List<Item>>(pos, c.Resources));
}
}
}
else
{
MineralClusters.RemoveAll(t => t.Item2 == null || t.Item2.None() || t.Item2.All(i => i == null || i.Removed));
}
}
if (UseTransducers && connectedTransducers.Count == 0) if (UseTransducers && connectedTransducers.Count == 0)
{ {
return; return;
@@ -348,21 +472,45 @@ namespace Barotrauma.Items.Components
if (Level.Loaded != null) if (Level.Loaded != null)
{ {
nearbyObjectUpdateTimer -= deltaTime;
if (nearbyObjectUpdateTimer <= 0.0f)
{
nearbyObjects.Clear();
foreach (var nearbyObject in Level.Loaded.LevelObjectManager.GetAllObjects(transducerCenter, range * zoom))
{
if (!nearbyObject.VisibleOnSonar) { continue; }
float objectRange = range + nearbyObject.SonarRadius;
if (Vector2.DistanceSquared(transducerCenter, nearbyObject.WorldPosition) < objectRange * objectRange)
{
nearbyObjects.Add(nearbyObject);
}
}
nearbyObjectUpdateTimer = NearbyObjectUpdateInterval;
}
List<LevelTrigger> ballastFloraSpores = new List<LevelTrigger>();
Dictionary<LevelTrigger, Vector2> levelTriggerFlows = new Dictionary<LevelTrigger, Vector2>(); Dictionary<LevelTrigger, Vector2> levelTriggerFlows = new Dictionary<LevelTrigger, Vector2>();
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex) for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{ {
var activePing = activePings[pingIndex]; var activePing = activePings[pingIndex];
foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(transducerCenter, range * activePing.State / zoom)) float pingRange = range * activePing.State / zoom;
foreach (LevelObject levelObject in nearbyObjects)
{ {
if (levelObject.Triggers == null) { continue; }
//gather all nearby triggers that are causing the water to flow into the dictionary //gather all nearby triggers that are causing the water to flow into the dictionary
foreach (LevelTrigger trigger in levelObject.Triggers) foreach (LevelTrigger trigger in levelObject.Triggers)
{ {
Vector2 flow = trigger.GetWaterFlowVelocity(); Vector2 flow = trigger.GetWaterFlowVelocity();
//ignore ones that are barely doing anything (flow^2 < 1) //ignore ones that are barely doing anything (flow^2 <= 1)
if (flow.LengthSquared() > 1.0f && !levelTriggerFlows.ContainsKey(trigger)) if (flow.LengthSquared() >= 1.0f && !levelTriggerFlows.ContainsKey(trigger))
{ {
levelTriggerFlows.Add(trigger, flow); levelTriggerFlows.Add(trigger, flow);
} }
if (!string.IsNullOrWhiteSpace(trigger.InfectIdentifier) &&
Vector2.DistanceSquared(transducerCenter, trigger.WorldPosition) < pingRange / 2 * pingRange / 2)
{
ballastFloraSpores.Add(trigger);
}
} }
} }
} }
@@ -400,6 +548,19 @@ namespace Barotrauma.Items.Components
} }
} }
foreach (LevelTrigger spore in ballastFloraSpores)
{
Vector2 blipPos = spore.WorldPosition + Rand.Vector(spore.ColliderRadius * Rand.Range(0.0f, 1.0f));
SonarBlip sporeBlip = new SonarBlip(blipPos, Rand.Range(0.1f, 0.5f), 0.5f)
{
Rotation = Rand.Range(-MathHelper.TwoPi, MathHelper.TwoPi),
BlipType = BlipType.Default,
Velocity = Rand.Vector(100f, Rand.RandSync.Unsynced)
};
sonarBlips.Add(sporeBlip);
}
float outsideLevelFlow = 0.0f; float outsideLevelFlow = 0.0f;
if (transducerCenter.X < 0.0f) if (transducerCenter.X < 0.0f)
{ {
@@ -721,6 +882,23 @@ namespace Barotrauma.Items.Components
} }
} }
if (AllowUsingMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null)
{
foreach (var t in MineralClusters)
{
var unobtainedMinerals = t.Item2.Where(i => i != null && i.GetRootInventoryOwner() == i);
if (unobtainedMinerals.None()) { continue; }
if (!CheckResourceMarkerVisibility(t.Item1, transducerCenter)) { continue; }
var i = unobtainedMinerals.FirstOrDefault();
if (i == null) { continue; }
DrawMarker(spriteBatch,
i.Name, "mineral", i,
t.Item1, transducerCenter,
displayScale, center, DisplayRadius * 0.95f,
onlyShowTextOnMouseOver: true);
}
}
foreach (Submarine sub in Submarine.Loaded) foreach (Submarine sub in Submarine.Loaded)
{ {
if (!sub.ShowSonarMarker) { continue; } if (!sub.ShowSonarMarker) { continue; }
@@ -967,8 +1145,7 @@ namespace Barotrauma.Items.Components
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex) for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{ {
var activePing = activePings[pingIndex]; foreach (LevelObject levelObject in nearbyObjects)
foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(pingSource, range * activePing.State))
{ {
if (levelObject.ActivePrefab?.SonarDisruption <= 0.0f) { continue; } if (levelObject.ActivePrefab?.SonarDisruption <= 0.0f) { continue; }
@@ -1101,9 +1278,9 @@ namespace Barotrauma.Items.Components
{ {
foreach (Voronoi2.GraphEdge edge in cell.Edges) foreach (Voronoi2.GraphEdge edge in cell.Edges)
{ {
if (!edge.IsSolid) continue; if (!edge.IsSolid) { continue; }
float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center); float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center);
if (cellDot > 0) continue; if (cellDot > 0) { continue; }
float facingDot = Vector2.Dot( float facingDot = Vector2.Dot(
Vector2.Normalize(edge.Point1 - edge.Point2), Vector2.Normalize(edge.Point1 - edge.Point2),
@@ -1114,7 +1291,8 @@ namespace Barotrauma.Items.Components
edge.Point2 + cell.Translation, edge.Point2 + cell.Translation,
pingSource, transducerPos, pingSource, transducerPos,
pingRadius, prevPingRadius, pingRadius, prevPingRadius,
350.0f, 3.0f * (Math.Abs(facingDot) + 1.0f), range, pingStrength, passive); 350.0f, 3.0f * (Math.Abs(facingDot) + 1.0f), range, pingStrength, passive,
blipType : cell.IsDestructible ? BlipType.Destructible : BlipType.Default);
} }
} }
@@ -1203,7 +1381,7 @@ namespace Barotrauma.Items.Components
} }
private void CreateBlipsForLine(Vector2 point1, Vector2 point2, Vector2 pingSource, Vector2 transducerPos, float pingRadius, float prevPingRadius, private void CreateBlipsForLine(Vector2 point1, Vector2 point2, Vector2 pingSource, Vector2 transducerPos, float pingRadius, float prevPingRadius,
float lineStep, float zStep, float range, float pingStrength, bool passive) float lineStep, float zStep, float range, float pingStrength, bool passive, BlipType blipType = BlipType.Default)
{ {
lineStep /= zoom; lineStep /= zoom;
zStep /= zoom; zStep /= zoom;
@@ -1219,13 +1397,13 @@ namespace Barotrauma.Items.Components
//ignore if outside the display //ignore if outside the display
Vector2 transducerDiff = point - transducerPos; Vector2 transducerDiff = point - transducerPos;
Vector2 transducerDisplayDiff = transducerDiff * displayScale; Vector2 transducerDisplayDiff = transducerDiff * displayScale;
if (transducerDisplayDiff.LengthSquared() > DisplayRadius * DisplayRadius) continue; if (transducerDisplayDiff.LengthSquared() > DisplayRadius * DisplayRadius) { continue; }
//ignore if the point is not within the ping //ignore if the point is not within the ping
Vector2 pointDiff = point - pingSource; Vector2 pointDiff = point - pingSource;
Vector2 displayPointDiff = pointDiff * displayScale; Vector2 displayPointDiff = pointDiff * displayScale;
float displayPointDistSqr = displayPointDiff.LengthSquared(); float displayPointDistSqr = displayPointDiff.LengthSquared();
if (displayPointDistSqr < prevPingRadius * prevPingRadius || displayPointDistSqr > pingRadius * pingRadius) continue; if (displayPointDistSqr < prevPingRadius * prevPingRadius || displayPointDistSqr > pingRadius * pingRadius) { continue; }
//ignore if direction is disrupted //ignore if direction is disrupted
float transducerDist = transducerDiff.Length(); float transducerDist = transducerDiff.Length();
@@ -1240,7 +1418,7 @@ namespace Barotrauma.Items.Components
break; break;
} }
} }
if (disrupted) continue; if (disrupted) { continue; }
float displayPointDist = (float)Math.Sqrt(displayPointDistSqr); float displayPointDist = (float)Math.Sqrt(displayPointDistSqr);
float alpha = pingStrength * Rand.Range(1.5f, 2.0f); float alpha = pingStrength * Rand.Range(1.5f, 2.0f);
@@ -1252,8 +1430,8 @@ namespace Barotrauma.Items.Components
int minDist = (int)(200 / zoom); int minDist = (int)(200 / zoom);
sonarBlips.RemoveAll(b => b.FadeTimer < fadeTimer && Math.Abs(pos.X - b.Position.X) < minDist && Math.Abs(pos.Y - b.Position.Y) < minDist); sonarBlips.RemoveAll(b => b.FadeTimer < fadeTimer && Math.Abs(pos.X - b.Position.X) < minDist && Math.Abs(pos.Y - b.Position.Y) < minDist);
var blip = new SonarBlip(pos, fadeTimer, 1.0f + ((displayPointDist + z) / DisplayRadius)); var blip = new SonarBlip(pos, fadeTimer, 1.0f + ((displayPointDist + z) / DisplayRadius), blipType);
if (!passive && !CheckBlipVisibility(blip, transducerPos)) continue; if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; }
sonarBlips.Add(blip); sonarBlips.Add(blip);
zStep += 0.5f / zoom; zStep += 0.5f / zoom;
@@ -1267,7 +1445,7 @@ namespace Barotrauma.Items.Components
alpha -= 0.1f; alpha -= 0.1f;
} }
if (alpha < 0) break; if (alpha < 0) { break; }
} }
} }
} }
@@ -1296,6 +1474,30 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
/// <summary>
/// Based largely on existing CheckBlipVisibility() code
/// </summary>
private bool CheckResourceMarkerVisibility(Vector2 resourcePos, Vector2 transducerPos)
{
var distSquared = Vector2.DistanceSquared(transducerPos, resourcePos);
if (distSquared > Range * Range)
{
return false;
}
if (currentPingIndex != -1 && activePings[currentPingIndex].IsDirectional)
{
var pos = (resourcePos - transducerPos) * displayScale * zoom;
pos.Y = -pos.Y;
var length = pos.Length();
var dir = pos / length;
if (Vector2.Dot(activePings[currentPingIndex].Direction, dir) < DirectionalPingDotProduct)
{
return false;
}
}
return true;
}
private void DrawBlip(SpriteBatch spriteBatch, SonarBlip blip, Vector2 transducerPos, Vector2 center, float strength, float blipScale) private void DrawBlip(SpriteBatch spriteBatch, SonarBlip blip, Vector2 transducerPos, Vector2 center, float strength, float blipScale)
{ {
strength = MathHelper.Clamp(strength, 0.0f, 1.0f); strength = MathHelper.Clamp(strength, 0.0f, 1.0f);
@@ -1334,7 +1536,8 @@ namespace Barotrauma.Items.Components
sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale, SpriteEffects.None, 0); sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale, SpriteEffects.None, 0);
} }
private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius) private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius,
bool onlyShowTextOnMouseOver = false)
{ {
float linearDist = Vector2.Distance(worldPosition, transducerPosition); float linearDist = Vector2.Distance(worldPosition, transducerPosition);
float dist = linearDist; float dist = linearDist;
@@ -1391,16 +1594,27 @@ namespace Barotrauma.Items.Components
markerPos.Y = (int)markerPos.Y; markerPos.Y = (int)markerPos.Y;
float alpha = 1.0f; float alpha = 1.0f;
if (linearDist * scale < radius) if (!onlyShowTextOnMouseOver)
{ {
float normalizedDist = linearDist * scale / radius; if (linearDist * scale < radius)
alpha = Math.Max(normalizedDist - 0.4f, 0.0f);
float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos);
float hoverThreshold = 150.0f;
if (mouseDist < hoverThreshold)
{ {
alpha += (hoverThreshold - mouseDist) / hoverThreshold; float normalizedDist = linearDist * scale / radius;
alpha = Math.Max(normalizedDist - 0.4f, 0.0f);
float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos);
float hoverThreshold = 150.0f;
if (mouseDist < hoverThreshold)
{
alpha += (hoverThreshold - mouseDist) / hoverThreshold;
}
}
}
else
{
float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos);
if (mouseDist > 5)
{
alpha = 0.0f;
} }
} }
@@ -1410,7 +1624,8 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
targetIcons[iconIdentifier].Draw(spriteBatch, markerPos); var iconInfo = targetIcons[iconIdentifier];
iconInfo.Item1.Draw(spriteBatch, markerPos, iconInfo.Item2);
} }
if (alpha <= 0.0f) { return; } if (alpha <= 0.0f) { return; }
@@ -1441,11 +1656,13 @@ namespace Barotrauma.Items.Components
screenBackground?.Remove(); screenBackground?.Remove();
lineSprite?.Remove(); lineSprite?.Remove();
foreach (Sprite sprite in targetIcons.Values) foreach (var t in targetIcons.Values)
{ {
sprite.Remove(); t.Item1.Remove();
} }
targetIcons.Clear(); targetIcons.Clear();
MineralClusters = null;
} }
public void ClientWrite(IWriteMessage msg, object[] extraData = null) public void ClientWrite(IWriteMessage msg, object[] extraData = null)
@@ -1460,6 +1677,7 @@ namespace Barotrauma.Items.Components
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection)); float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8); msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
} }
msg.Write(useMineralScanner);
} }
} }
@@ -1471,6 +1689,7 @@ namespace Barotrauma.Items.Components
float zoomT = 1.0f; float zoomT = 1.0f;
bool directionalPing = useDirectionalPing; bool directionalPing = useDirectionalPing;
float directionT = 0.0f; float directionT = 0.0f;
bool mineralScanner = useMineralScanner;
if (isActive) if (isActive)
{ {
zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8); zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
@@ -1479,6 +1698,7 @@ namespace Barotrauma.Items.Components
{ {
directionT = msg.ReadRangedSingle(0.0f, 1.0f, 8); directionT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
} }
mineralScanner = msg.ReadBoolean();
} }
if (correctionTimer > 0.0f) if (correctionTimer > 0.0f)
@@ -1500,6 +1720,11 @@ namespace Barotrauma.Items.Components
pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle)); pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle));
} }
useDirectionalPing = directionalModeSwitch.Selected = directionalPing; useDirectionalPing = directionalModeSwitch.Selected = directionalPing;
useMineralScanner = mineralScanner;
if (mineralScannerSwitch != null)
{
mineralScannerSwitch.Selected = mineralScanner;
}
} }
} }
@@ -1510,6 +1735,10 @@ namespace Barotrauma.Items.Components
passiveTickBox.Selected = !isActive; passiveTickBox.Selected = !isActive;
activeTickBox.Selected = isActive; activeTickBox.Selected = isActive;
directionalModeSwitch.Selected = useDirectionalPing; directionalModeSwitch.Selected = useDirectionalPing;
if (mineralScannerSwitch != null)
{
mineralScannerSwitch.Selected = useMineralScanner;
}
} }
} }
@@ -112,7 +112,7 @@ namespace Barotrauma.Items.Components
protected override void CreateGUI() protected override void CreateGUI()
{ {
controlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterLeft), "ItemUI"); controlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterRight), "ItemUI");
var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center) var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center)
{ {
AbsoluteOffset = GUIStyle.ItemFrameOffset AbsoluteOffset = GUIStyle.ItemFrameOffset
@@ -265,7 +265,7 @@ namespace Barotrauma.Items.Components
levelStartSelected ? Destination.LevelStart : Destination.LevelEnd); levelStartSelected ? Destination.LevelStart : Destination.LevelEnd);
// Status -> // Status ->
statusContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomLeft) statusContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomRight)
{ {
RelativeOffset = Sonar.controlBoxOffset RelativeOffset = Sonar.controlBoxOffset
}, "ItemUI"); }, "ItemUI");
@@ -319,8 +319,7 @@ namespace Barotrauma.Items.Components
centerText = $"({TextManager.Get("Meter")})"; centerText = $"({TextManager.Get("Meter")})";
rightTextGetter = () => rightTextGetter = () =>
{ {
Vector2 pos = controlledSub == null ? Vector2.Zero : controlledSub.Position; float realWorldDepth = controlledSub == null ? -1000.0f : controlledSub.RealWorldDepth;
float realWorldDepth = Level.Loaded == null ? 0.0f : Math.Abs(pos.Y - Level.Loaded.Size.Y) * Physics.DisplayToRealWorldRatio;
return ((int)realWorldDepth).ToString(); return ((int)realWorldDepth).ToString();
}; };
break; break;
@@ -340,9 +339,9 @@ namespace Barotrauma.Items.Components
//docking interface ---------------------------------------------------- //docking interface ----------------------------------------------------
float dockingButtonSize = 1.1f; float dockingButtonSize = 1.1f;
float elementScale = 0.6f; float elementScale = 0.6f;
dockingContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomLeft, scaleBasis: ScaleBasis.Smallest) dockingContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)
{ {
RelativeOffset = new Vector2(Sonar.controlBoxOffset.X + 0.05f, Sonar.controlBoxOffset.Y) RelativeOffset = new Vector2(Sonar.controlBoxOffset.X + 0.05f, -0.05f)
}, style: null); }, style: null);
dockText = TextManager.Get("label.navterminaldock", fallBackTag: "captain.dock"); dockText = TextManager.Get("label.navterminaldock", fallBackTag: "captain.dock");
@@ -437,7 +436,7 @@ namespace Barotrauma.Items.Components
}; };
// Sonar area // Sonar area
steerArea = new GUICustomComponent(new RectTransform(Sonar.GUISizeCalculation, GuiFrame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest), steerArea = new GUICustomComponent(new RectTransform(Sonar.GUISizeCalculation, GuiFrame.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest),
(spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null); (spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null);
steerRadius = steerArea.Rect.Width / 2; steerRadius = steerArea.Rect.Width / 2;
@@ -1,8 +1,7 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Xml.Linq; using System.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -10,6 +9,23 @@ namespace Barotrauma.Items.Components
{ {
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
bool launch = msg.ReadBoolean();
if (launch)
{
ushort userId = msg.ReadUInt16();
User = Entity.FindEntityByID(userId) as Character;
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
float rotation = msg.ReadSingle();
if (User != null)
{
Shoot(User, simPosition, simPosition, rotation, ignoredBodies: User.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList(), createNetworkEvent: false);
}
else
{
Launch(User, simPosition, rotation);
}
}
bool isStuck = msg.ReadBoolean(); bool isStuck = msg.ReadBoolean();
if (isStuck) if (isStuck)
{ {
@@ -56,7 +72,15 @@ namespace Barotrauma.Items.Components
else if (entity is Item item) else if (entity is Item item)
{ {
if (item.Removed) { return; } if (item.Removed) { return; }
StickToTarget(item.body.FarseerBody, axis); var door = item.GetComponent<Door>();
if (door != null)
{
StickToTarget(door.Body.FarseerBody, axis);
}
else if (item.body != null)
{
StickToTarget(item.body.FarseerBody, axis);
}
} }
else if (entity is Submarine sub) else if (entity is Submarine sub)
{ {
@@ -1,6 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -70,7 +69,13 @@ namespace Barotrauma.Items.Components
partial void ShowOnDisplay(string input) partial void ShowOnDisplay(string input)
{ {
while (historyBox.Content.CountChildren > 60) messageHistory.Add(input);
while (messageHistory.Count > MaxMessages)
{
messageHistory.RemoveAt(0);
}
while (historyBox.Content.CountChildren > MaxMessages)
{ {
historyBox.RemoveChild(historyBox.Content.Children.First()); historyBox.RemoveChild(historyBox.Content.Children.First());
} }
@@ -114,11 +119,6 @@ namespace Barotrauma.Items.Components
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
base.AddToGUIUpdateList(); base.AddToGUIUpdateList();
if (!string.IsNullOrEmpty(DisplayedWelcomeMessage))
{
ShowOnDisplay(DisplayedWelcomeMessage);
DisplayedWelcomeMessage = "";
}
if (shouldSelectInputBox) if (shouldSelectInputBox)
{ {
inputBox.Select(); inputBox.Select();
@@ -117,9 +117,9 @@ namespace Barotrauma.Items.Components
{ {
if (defaultWireSprite == null) if (defaultWireSprite == null)
{ {
defaultWireSprite = new Sprite("Content/Items/wireHorizontal.png", new Vector2(0.5f, 0.5f)) defaultWireSprite = new Sprite("Content/Items/Electricity/signalcomp.png", new Rectangle(970, 47, 14, 16), new Vector2(0.5f, 0.5f))
{ {
Depth = 0.85f Depth = 0.855f
}; };
} }
@@ -156,7 +156,7 @@ namespace Barotrauma.Items.Components
drawOffset = sub.DrawPosition + sub.HiddenSubPosition; drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
} }
float depth = item.IsSelected ? 0.0f : Screen.Selected is SubEditorScreen editor && editor.WiringMode ? 0.00002f : wireSprite.Depth + ((item.ID % 100) * 0.00001f); float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : wireSprite.Depth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
if (item.IsHighlighted) if (item.IsHighlighted)
{ {
@@ -261,7 +261,7 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-3, -3), new Vector2(6, 6), item.Color, true, 0.0f); GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-3, -3), new Vector2(6, 6), item.Color, true, 0.015f);
} }
} }
} }
@@ -247,7 +247,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{ {
if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation)) if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation) || !MathUtils.NearlyEqual(item.Scale, prevScale))
{ {
UpdateTransformedBarrelPos(); UpdateTransformedBarrelPos();
} }
@@ -286,75 +286,52 @@ namespace Barotrauma.Items.Components
rotation + MathHelper.PiOver2, item.Scale, rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth)); SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
if (!editing || GUI.DisableHUD || !item.IsSelected) { return; } if (!GameMain.DebugDraw && (!editing || GUI.DisableHUD || !item.IsSelected)) { return; }
float widgetRadius = 60.0f; const float widgetRadius = 60.0f;
GUI.DrawLine(spriteBatch,
drawPos,
drawPos + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * widgetRadius,
GUI.Style.Green);
GUI.DrawLine(spriteBatch,
drawPos,
drawPos + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * widgetRadius,
GUI.Style.Green);
Vector2 center = new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2));
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
drawPos, drawPos,
drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * widgetRadius, drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * widgetRadius,
Color.LightGreen); Color.LightGreen);
Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: 10, initMethod: (widget) => if (GameMain.DebugDraw)
{ {
widget.Selected += () => center = new Vector2((float)Math.Cos(targetRotation), (float)Math.Sin(targetRotation));
{ GUI.DrawLine(spriteBatch,
oldRotation = RotationLimits; drawPos,
}; drawPos + center * widgetRadius,
widget.MouseDown += () => Color.Red);
{
widget.color = GUI.Style.Green; for (int i = 0; i < 5; i++)
prevAngle = minRotation; {
}; center = new Vector2((float)Math.Cos(rotation + (angularVelocity * 0.05f * i)), (float)Math.Sin(rotation + (angularVelocity * 0.05f * i)));
widget.Deselected += () => GUI.DrawLine(spriteBatch,
{ drawPos,
widget.color = Color.Yellow; drawPos + center * widgetRadius,
item.CreateEditingHUD(); Color.Lerp(Color.Black, Color.Yellow, i * 0.25f));
if (SubEditorScreen.IsSubEditor()) }
{ }
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
} const float coneRadius = 300.0f;
}; float radians = maxRotation - minRotation;
widget.MouseHeld += (deltaTime) => float circleRadius = coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
{ float lineThickness = 1f / Screen.Selected.Cam.Zoom;
minRotation = GetRotationAngle(GetDrawPos());
if (minRotation > maxRotation) if (radians > Math.PI * 2)
{ {
float temp = minRotation; spriteBatch.DrawCircle(drawPos, circleRadius, 180, GUI.Style.Red, thickness: lineThickness);
minRotation = maxRotation; }
maxRotation = temp; else
} {
MapEntity.DisableSelect = true; spriteBatch.DrawSector(drawPos, circleRadius, radians, (int)Math.Abs(90 * radians), GUI.Style.Green, offset: minRotation, thickness: lineThickness);
}; }
widget.PreUpdate += (deltaTime) =>
{ int baseWidgetScale = GUI.IntScale(16);
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y); int widgetSize = (int) (Math.Max(baseWidgetScale, baseWidgetScale / Screen.Selected.Cam.Zoom));
widget.DrawPos = Screen.Selected.Cam.WorldToScreen(widget.DrawPos); float widgetThickness = Math.Max(1f, lineThickness);
}; Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: widgetSize, thickness: widgetThickness, initMethod: (widget) =>
widget.PostUpdate += (deltaTime) =>
{
widget.DrawPos = Screen.Selected.Cam.ScreenToWorld(widget.DrawPos);
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
};
widget.PreDraw += (sprtBtch, deltaTime) =>
{
widget.tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * widgetRadius;
widget.Update(deltaTime);
};
});
Widget maxRotationWidget = GetWidget("maxrotation", spriteBatch, size: 10, initMethod: (widget) =>
{ {
widget.Selected += () => widget.Selected += () =>
{ {
@@ -369,6 +346,52 @@ namespace Barotrauma.Items.Components
{ {
widget.color = Color.Yellow; widget.color = Color.Yellow;
item.CreateEditingHUD(); item.CreateEditingHUD();
RotationLimits = RotationLimits;
if (SubEditorScreen.IsSubEditor())
{
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
}
};
widget.MouseHeld += (deltaTime) =>
{
minRotation = GetRotationAngle(GetDrawPos());
UpdateBarrel();
MapEntity.DisableSelect = true;
};
widget.PreUpdate += (deltaTime) =>
{
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
widget.DrawPos = Screen.Selected.Cam.WorldToScreen(widget.DrawPos);
};
widget.PostUpdate += (deltaTime) =>
{
widget.DrawPos = Screen.Selected.Cam.ScreenToWorld(widget.DrawPos);
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
};
widget.PreDraw += (sprtBtch, deltaTime) =>
{
widget.tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
widget.Update(deltaTime);
};
});
Widget maxRotationWidget = GetWidget("maxrotation", spriteBatch, size: widgetSize, thickness: widgetThickness, initMethod: (widget) =>
{
widget.Selected += () =>
{
oldRotation = RotationLimits;
};
widget.MouseDown += () =>
{
widget.color = GUI.Style.Green;
prevAngle = maxRotation;
};
widget.Deselected += () =>
{
widget.color = Color.Yellow;
item.CreateEditingHUD();
RotationLimits = RotationLimits;
if (SubEditorScreen.IsSubEditor()) if (SubEditorScreen.IsSubEditor())
{ {
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation)); SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
@@ -377,12 +400,7 @@ namespace Barotrauma.Items.Components
widget.MouseHeld += (deltaTime) => widget.MouseHeld += (deltaTime) =>
{ {
maxRotation = GetRotationAngle(GetDrawPos()); maxRotation = GetRotationAngle(GetDrawPos());
if (minRotation > maxRotation) UpdateBarrel();
{
float temp = minRotation;
minRotation = maxRotation;
maxRotation = temp;
}
MapEntity.DisableSelect = true; MapEntity.DisableSelect = true;
}; };
widget.PreUpdate += (deltaTime) => widget.PreUpdate += (deltaTime) =>
@@ -398,7 +416,7 @@ namespace Barotrauma.Items.Components
widget.PreDraw += (sprtBtch, deltaTime) => widget.PreDraw += (sprtBtch, deltaTime) =>
{ {
widget.tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation); widget.tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation);
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * widgetRadius; widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
widget.Update(deltaTime); widget.Update(deltaTime);
}; };
}); });
@@ -412,22 +430,32 @@ namespace Barotrauma.Items.Components
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
return drawPos; return drawPos;
} }
void UpdateBarrel()
{
rotation = (minRotation + maxRotation) / 2;
}
} }
private Widget GetWidget(string id, SpriteBatch spriteBatch, int size = 5, Action<Widget> initMethod = null) private Widget GetWidget(string id, SpriteBatch spriteBatch, int size = 5, float thickness = 1f, Action<Widget> initMethod = null)
{ {
Vector2 offset = new Vector2(size / 2 + 5, -10);
if (!widgets.TryGetValue(id, out Widget widget)) if (!widgets.TryGetValue(id, out Widget widget))
{ {
widget = new Widget(id, size, Widget.Shape.Rectangle) widget = new Widget(id, size, Widget.Shape.Rectangle)
{ {
color = Color.Yellow, color = Color.Yellow,
tooltipOffset = new Vector2(size / 2 + 5, -10), tooltipOffset = offset,
inputAreaMargin = 20, inputAreaMargin = 20,
RequireMouseOn = false RequireMouseOn = false
}; };
widgets.Add(id, widget); widgets.Add(id, widget);
initMethod?.Invoke(widget); initMethod?.Invoke(widget);
} }
widget.size = size;
widget.tooltipOffset = offset;
widget.thickness = thickness;
return widget; return widget;
} }
@@ -580,7 +608,6 @@ namespace Barotrauma.Items.Components
} }
Launch(projectile, launchRotation: newTargetRotation); Launch(projectile, launchRotation: newTargetRotation);
} }
} }
} }
} }
@@ -439,7 +439,7 @@ namespace Barotrauma
protected virtual void ControlInput(Camera cam) protected virtual void ControlInput(Camera cam)
{ {
// Note that these targets are static. Therefore the outcome is the same if this method is called multiple times or only once. // Note that these targets are static. Therefore the outcome is the same if this method is called multiple times or only once.
if (selectedSlot != null && !DraggingItemToWorld) if (selectedSlot != null && !DraggingItemToWorld && cam.GetZoomAmountFromPrevious() <= 0.25f)
{ {
cam.Freeze = true; cam.Freeze = true;
} }
@@ -461,7 +461,7 @@ namespace Barotrauma
}*/ }*/
bool mouseOn = interactRect.Contains(PlayerInput.MousePosition) && !Locked && !mouseOnGUI && !slot.Disabled; bool mouseOn = interactRect.Contains(PlayerInput.MousePosition) && !Locked && !mouseOnGUI && !slot.Disabled;
// Delete item from container in sub editor // Delete item from container in sub editor
if (SubEditorScreen.IsSubEditor() && PlayerInput.IsCtrlDown()) if (SubEditorScreen.IsSubEditor() && PlayerInput.IsCtrlDown())
{ {
@@ -477,8 +477,12 @@ namespace Barotrauma
SoundPlayer.PlayUISound(GUISoundType.PickItem); SoundPlayer.PlayUISound(GUISoundType.PickItem);
} }
SubEditorScreen.BulkItemBufferInUse = true; if (!item.Removed)
SubEditorScreen.BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { item }, true)); {
SubEditorScreen.BulkItemBufferInUse = SubEditorScreen.ItemRemoveMutex;
SubEditorScreen.BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { item }, true));
}
item.OwnInventory?.DeleteAllItems(); item.OwnInventory?.DeleteAllItems();
item.Remove(); item.Remove();
} }
@@ -688,6 +692,18 @@ namespace Barotrauma
subInventory.Update(deltaTime, cam, true); subInventory.Update(deltaTime, cam, true);
} }
public void ClearSubInventories()
{
if (highlightedSubInventorySlots.Count == 0) return;
foreach (SlotReference highlightedSubInventorySlot in highlightedSubInventorySlots)
{
highlightedSubInventorySlot.Inventory.HideTimer = 0.0f;
}
highlightedSubInventorySlots.Clear();
}
public virtual void Draw(SpriteBatch spriteBatch, bool subInventory = false) public virtual void Draw(SpriteBatch spriteBatch, bool subInventory = false)
{ {
if (slots == null || isSubInventory != subInventory) return; if (slots == null || isSubInventory != subInventory) return;
@@ -62,9 +62,9 @@ namespace Barotrauma
} }
} }
public override bool DrawBelowWater => (!(Screen.Selected is SubEditorScreen editor) || !editor.WiringMode || !isWire) && (base.DrawBelowWater || ParentInventory is CharacterInventory); public override bool DrawBelowWater => (!(Screen.Selected is SubEditorScreen editor) || !editor.WiringMode || !isWire || !isLogic) && (base.DrawBelowWater || ParentInventory is CharacterInventory);
public override bool DrawOverWater => base.DrawOverWater || (IsSelected || Screen.Selected is SubEditorScreen editor && editor.WiringMode) && isWire; public override bool DrawOverWater => base.DrawOverWater || (IsSelected || Screen.Selected is SubEditorScreen editor && editor.WiringMode) && (isWire || isLogic);
private GUITextBlock itemInUseWarning; private GUITextBlock itemInUseWarning;
private GUITextBlock ItemInUseWarning private GUITextBlock ItemInUseWarning
@@ -93,6 +93,11 @@ namespace Barotrauma
} }
} }
public float GetDrawDepth()
{
return GetDrawDepth(SpriteDepth, Sprite);
}
public Color GetSpriteColor() public Color GetSpriteColor()
{ {
Color color = spriteColor; Color color = spriteColor;
@@ -239,6 +244,10 @@ namespace Barotrauma
Color color = IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange : GetSpriteColor(); Color color = IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange : GetSpriteColor();
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f); //if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
if (renderTransparent) { color *= 0.15f; }
BrokenItemSprite fadeInBrokenSprite = null; BrokenItemSprite fadeInBrokenSprite = null;
float fadeInBrokenSpriteAlpha = 0.0f; float fadeInBrokenSpriteAlpha = 0.0f;
@@ -269,6 +278,7 @@ namespace Barotrauma
} }
float depth = GetDrawDepth(); float depth = GetDrawDepth();
if (isWiringMode && isLogic && !PlayerInput.IsShiftDown()) { depth = 0.01f; }
if (activeSprite != null) if (activeSprite != null)
{ {
SpriteEffects oldEffects = activeSprite.effects; SpriteEffects oldEffects = activeSprite.effects;
@@ -296,6 +306,8 @@ namespace Barotrauma
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.DrawTiled(spriteBatch, decorativeSprite.Sprite.DrawTiled(spriteBatch,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)), new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
size, color: color, size, color: color,
@@ -316,11 +328,18 @@ namespace Barotrauma
} }
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth); activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f); fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f);
if (Infector != null && Infector.ParentBallastFlora.HasBrokenThrough)
{
Prefab.InfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, Prefab.InfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.001f);
Prefab.DamagedInfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, Infector.HealthColor, Prefab.DamagedInfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.002f);
}
foreach (var decorativeSprite in Prefab.DecorativeSprites) foreach (var decorativeSprite in Prefab.DecorativeSprites)
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color, decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotationRad + rot, decorativeSprite.Scale * Scale, activeSprite.effects, rotationRad + rot, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f)); depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
@@ -367,7 +386,8 @@ namespace Barotrauma
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
var ca = (float)Math.Cos(-body.Rotation); var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation); var sa = (float)Math.Sin(-body.Rotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y); Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
@@ -386,9 +406,10 @@ namespace Barotrauma
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
var (xOff, yOff) = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + xOff, -(DrawPosition.Y + yOff)), color, if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotation, decorativeSprite.Scale * Scale, activeSprite.effects, rotation, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth)); depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
} }
@@ -417,6 +438,14 @@ namespace Barotrauma
} }
} }
if (editing && IsSelected && PlayerInput.KeyDown(Keys.Space))
{
if (GetComponent<ElectricalDischarger>() is { } discharger)
{
discharger.DrawElectricity(spriteBatch);
}
}
if (!editing || (body != null && !body.Enabled)) if (!editing || (body != null && !body.Enabled))
{ {
return; return;
@@ -505,7 +534,19 @@ namespace Barotrauma
} }
if (Screen.Selected != GameMain.SubEditorScreen) { return; } if (Screen.Selected != GameMain.SubEditorScreen) { return; }
if (GetComponent<ElectricalDischarger>() is { } discharger)
{
if (PlayerInput.KeyDown(Keys.Space))
{
discharger.FindNodes(WorldPosition, discharger.Range);
}
else
{
discharger.IsActive = false;
}
}
if (Character.Controlled == null) { activeHUDs.Clear(); } if (Character.Controlled == null) { activeHUDs.Clear(); }
if (!Linkable) { return; } if (!Linkable) { return; }
@@ -1341,6 +1382,10 @@ namespace Barotrauma
DebugConsole.Log($"Received entity spawn message for item \"{itemName}\" (identifier: {itemIdentifier}, id: {itemId})"); DebugConsole.Log($"Received entity spawn message for item \"{itemName}\" (identifier: {itemIdentifier}, id: {itemId})");
var itemPrefab = string.IsNullOrEmpty(itemIdentifier) ?
MapEntityPrefab.Find(itemName, null, showErrorMessages: false) as ItemPrefab :
MapEntityPrefab.Find(itemName, itemIdentifier, showErrorMessages: false) as ItemPrefab;
Vector2 pos = Vector2.Zero; Vector2 pos = Vector2.Zero;
Submarine sub = null; Submarine sub = null;
int itemContainerIndex = -1; int itemContainerIndex = -1;
@@ -1369,16 +1414,24 @@ namespace Barotrauma
string tags = ""; string tags = "";
if (tagsChanged) if (tagsChanged)
{ {
tags = msg.ReadString(); string[] addedTags = msg.ReadString().Split(',');
string[] removedTags = msg.ReadString().Split(',');
if (itemPrefab != null)
{
tags = string.Join(',',itemPrefab.Tags.Where(t => !removedTags.Contains(t)).Concat(addedTags));
}
}
bool isNameTag = msg.ReadBoolean();
string writtenName = "";
if (isNameTag)
{
writtenName = msg.ReadString();
} }
if (!spawn) return null; if (!spawn) { return null; }
//---------------------------------------- //----------------------------------------
var itemPrefab = string.IsNullOrEmpty(itemIdentifier) ?
MapEntityPrefab.Find(itemName, null, showErrorMessages: false) as ItemPrefab :
MapEntityPrefab.Find(itemName, itemIdentifier, showErrorMessages: false) as ItemPrefab;
if (itemPrefab == null) if (itemPrefab == null)
{ {
string errorMsg = "Failed to spawn item, prefab not found (name: " + (itemName ?? "null") + ", identifier: " + (itemIdentifier ?? "null") + ")"; string errorMsg = "Failed to spawn item, prefab not found (name: " + (itemName ?? "null") + ", identifier: " + (itemIdentifier ?? "null") + ")";
@@ -1425,9 +1478,8 @@ namespace Barotrauma
} }
} }
var item = new Item(itemPrefab, pos, sub) var item = new Item(itemPrefab, pos, sub, id: itemId)
{ {
ID = itemId,
SpawnedInOutpost = spawnedInOutpost SpawnedInOutpost = spawnedInOutpost
}; };
@@ -1442,6 +1494,11 @@ namespace Barotrauma
} }
if (descriptionChanged) { item.Description = itemDesc; } if (descriptionChanged) { item.Description = itemDesc; }
if (tagsChanged) { item.Tags = tags; } if (tagsChanged) { item.Tags = tags; }
var nameTag = item.GetComponent<NameTag>();
if (nameTag != null)
{
nameTag.WrittenName = writtenName;
}
if (sub != null) if (sub != null)
{ {
@@ -56,6 +56,8 @@ namespace Barotrauma
public Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>(); public Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
public Sprite InventoryIcon; public Sprite InventoryIcon;
public Sprite MinimapIcon; public Sprite MinimapIcon;
public Sprite InfectedSprite;
public Sprite DamagedInfectedSprite;
//only used to display correct color in the sub editor, item instances have their own property that can be edited on a per-item basis //only used to display correct color in the sub editor, item instances have their own property that can be edited on a per-item basis
[Serialize("1.0,1.0,1.0,1.0", false)] [Serialize("1.0,1.0,1.0,1.0", false)]
@@ -0,0 +1,395 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.MapCreatures.Behavior
{
partial class BallastFloraBehavior
{
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global, UnusedAutoPropertyAccessor.Global, MemberCanBePrivate.Global
internal class DamageParticle
{
[Serialize(defaultValue: "", isSaveable: false)]
public string Identifier { get; set; } = "";
[Serialize(defaultValue: 0f, isSaveable: false)]
public float MinRotation { get; set; }
[Serialize(defaultValue: 0f, isSaveable: false)]
public float MaxRotation { get; set; }
[Serialize(defaultValue: 0f, isSaveable: false)]
public float MinVelocity { get; set; }
[Serialize(defaultValue: 0f, isSaveable: false)]
public float MaxVelocity { get; set; }
[Serialize(defaultValue: "255,255,255,255", isSaveable: false)]
public Color ColorMultiplier { get; set; }
private float RandRotation() => Rand.Range(MinRotation, MaxRotation);
private float RandVelocity() => Rand.Range(MinVelocity, MaxVelocity);
public void Emit(Vector2 pos)
{
Particle particle = GameMain.ParticleManager.CreateParticle(Identifier, pos, RandRotation(), RandVelocity());
if (particle != null)
{
particle.ColorMultiplier = ColorMultiplier.ToVector4();
}
}
public DamageParticle(XElement element)
{
SerializableProperty.DeserializeProperties(this, element);
}
}
public Sprite? branchAtlas, decayAtlas;
public readonly Dictionary<VineTileType, VineSprite> BranchSprites = new Dictionary<VineTileType, VineSprite>();
public readonly List<Sprite> FlowerSprites = new List<Sprite>(), DamagedFlowerSprites = new List<Sprite>();
public readonly List<Sprite> HiddenFlowerSprites = new List<Sprite>();
public readonly List<Sprite> LeafSprites = new List<Sprite>(), DamagedLeafSprites = new List<Sprite>();
public readonly List<DamageParticle> DamageParticles = new List<DamageParticle>();
public readonly List<DamageParticle> DeathParticles = new List<DamageParticle>();
partial void LoadPrefab(XElement element)
{
string? branchAtlasPath = element.GetAttributeString("branchatlas", null);
string? decayAtlasPath = element.GetAttributeString("decayatlas", null);
if (branchAtlasPath != null)
{
branchAtlas = new Sprite(branchAtlasPath, Rectangle.Empty);
}
if (decayAtlasPath != null)
{
decayAtlas = new Sprite(decayAtlasPath, Rectangle.Empty);
}
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "branchsprite":
var tileType = subElement.GetAttributeString("type", null);
VineTileType type = Enum.Parse<VineTileType>(tileType);
BranchSprites.Add(type, new VineSprite(subElement));
break;
case "flowersprite":
FlowerSprites.Add(new Sprite(subElement));
break;
case "damagedflowersprite":
DamagedFlowerSprites.Add(new Sprite(subElement));
break;
case "hiddenflowersprite":
HiddenFlowerSprites.Add(new Sprite(subElement));
break;
case "leafsprite":
LeafSprites.Add(new Sprite(subElement));
break;
case "damagedleafsprite":
DamagedLeafSprites.Add(new Sprite(subElement));
break;
case "damageparticle":
DamageParticles.Add(new DamageParticle(subElement));
break;
case "deathparticle":
DeathParticles.Add(new DamageParticle(subElement));
break;
case "targets":
LoadTargets(subElement);
break;
}
flowerVariants = FlowerSprites.Count;
leafVariants = LeafSprites.Count;
}
}
private void CreateShapnel(Vector2 pos)
{
float particleAmount = Rand.Range(16, 32);
for (int i = 0; i < particleAmount; i++)
{
GameMain.ParticleManager.CreateParticle("shrapnel", pos, Rand.Vector(Rand.Range(-50f, 50.0f)));
}
}
private void CreateDamageParticle(BallastFloraBranch branch, float damage)
{
Vector2 pos = GetWorldPosition() + branch.Position;
int amount = (int)Math.Clamp(damage / 10f, 1, 10);
for (int i = 0; i < amount; i++)
{
foreach (DamageParticle particle in DamageParticles)
{
particle.Emit(pos);
}
}
}
private void CreateDeathParticle(BallastFloraBranch branch)
{
Vector2 pos = GetWorldPosition() + branch.Position;
int amount = (int)Math.Clamp(branch.MaxHealth / 10f, 1, 10);
for (int i = 0; i < amount; i++)
{
foreach (DamageParticle particle in DeathParticles)
{
particle.Emit(pos);
}
}
}
public void Draw(SpriteBatch spriteBatch)
{
const float zStep = 0.000001f;
float leafDepth = zStep;
float flowerDepth = zStep;
if (GameMain.DebugDraw)
{
foreach (Body body in bodies)
{
Vector2 pos = Parent.Submarine.DrawPosition + ConvertUnits.ToDisplayUnits(body.Position);
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, pos, 32f, 32f, 0f, Color.Cyan, 0.1f, thickness: 1);
}
foreach (var (key, steps) in IgnoredTargets)
{
string label = $"Ignored \"{key.Name}\" for {steps} steps";
var (sizeX, sizeY) = GUI.SubHeadingFont.MeasureString(label);
Vector2 targetPos = key.WorldPosition;
targetPos.Y = -targetPos.Y;
GUI.DrawString(spriteBatch, targetPos - new Vector2(sizeX / 2f, sizeY), label, GUI.Style.Red, font: GUI.SubHeadingFont);
}
}
foreach (BallastFloraBranch branch in Branches)
{
Vector2 pos = Parent.DrawPosition + Offset + branch.Position;
pos.Y = -pos.Y;
float depth = BranchDepth;
float layer1 = depth + 0.01f,
layer2 = depth + 0.02f,
layer3 = depth + 0.03f;
VineSprite branchSprite = BranchSprites[branch.Type];
Color branchColor = Color.White;
if (GameMain.DebugDraw)
{
#if DEBUG
Vector2 basePos = Parent.WorldPosition;
foreach (var (from, to) in debugSearchLines)
{
Vector2 pos1 = basePos - from;
pos1.Y = -pos1.Y;
Vector2 pos2 = basePos - to;
pos2.Y = -pos2.Y;
GUI.DrawLine(spriteBatch, pos1, pos2, GUI.Style.Yellow * 0.8f, width: 4);
}
#endif
string label = "";
if (branch == Branches[^1])
{
label += $"Current State: {StateMachine.State?.GetType().Name ?? "null!"}\n";
}
if (StateMachine.State is GrowToTargetState targetState)
{
if (targetState.TargetBranches.Contains(branch))
{
GUI.DrawRectangle(spriteBatch, pos, branch.Rect.Width, branch.Rect.Height, 0f, Color.Red, thickness: 4);
}
if (targetState.TargetBranches[^1] == branch)
{
label += $"Target: {targetState.Target.Name}\n";
Vector2 targetPos = targetState.Target.WorldPosition;
targetPos.Y = -targetPos.Y;
GUI.DrawLine(spriteBatch, pos, targetPos, Color.Red, width: 4);
}
}
var (sizeX, sizeY) = GUI.SubHeadingFont.MeasureString(label);
GUI.DrawString(spriteBatch, pos - new Vector2(sizeX / 2f, branch.Rect.Height + sizeY), label, Color.White, font: GUI.SubHeadingFont);
}
bool isDamaged = branch.Health < branch.MaxHealth;
if (HasBrokenThrough)
{
if (branchAtlas != null && branchAtlas.Loaded)
{
spriteBatch.Draw(branchAtlas.Texture, pos + branch.offset, branchSprite.SourceRect, branchColor, 0f, branchSprite.AbsoluteOrigin, BaseBranchScale * branch.VineStep, SpriteEffects.None, layer2);
}
if (decayAtlas != null && isDamaged && decayAtlas.Loaded)
{
spriteBatch.Draw(decayAtlas.Texture, pos + branch.offset, branchSprite.SourceRect, branch.HealthColor, 0f, branchSprite.AbsoluteOrigin, BaseBranchScale * branch.VineStep, SpriteEffects.None, layer2 - zStep);
}
}
if (branch.FlowerConfig.Variant >= 0)
{
int variant = branch.FlowerConfig.Variant;
Sprite flowerSprite = HasBrokenThrough ? FlowerSprites[variant] : HiddenFlowerSprites[variant];
float flowerScale = BaseFlowerScale * branch.FlowerConfig.Scale * branch.FlowerStep;
if (HasBrokenThrough) { flowerScale *= branch.Pulse; }
flowerSprite.Draw(spriteBatch, pos, branchColor, flowerSprite.Origin, scale: flowerScale, rotate: branch.FlowerConfig.Rotation, depth: layer1 - flowerDepth);
if (isDamaged && HasBrokenThrough && DamagedFlowerSprites.Count > variant)
{
DamagedFlowerSprites[variant].Draw(spriteBatch, pos, branch.HealthColor, flowerSprite.Origin, scale: flowerScale, rotate: branch.FlowerConfig.Rotation, depth: layer1 - flowerDepth - zStep);
}
flowerDepth -= zStep;
if (flowerDepth > 0.01f)
{
flowerDepth = zStep;
}
}
if (branch.LeafConfig.Variant >= 0 && HasBrokenThrough)
{
int variant = branch.LeafConfig.Variant;
Sprite leafSprite = LeafSprites[variant];
leafSprite.Draw(spriteBatch, pos, branchColor, leafSprite.Origin, scale: BaseLeafScale * branch.LeafConfig.Scale * branch.FlowerStep, rotate: branch.LeafConfig.Rotation, depth: layer3 + leafDepth);
if (isDamaged && DamagedLeafSprites.Count > variant)
{
DamagedLeafSprites[variant].Draw(spriteBatch, pos, branch.HealthColor, leafSprite.Origin, scale: BaseLeafScale * branch.LeafConfig.Scale * branch.FlowerStep, rotate: branch.LeafConfig.Rotation, depth: layer3 + leafDepth - zStep);
}
leafDepth += zStep;
if (leafDepth > 0.01f)
{
flowerDepth = zStep;
}
}
}
}
public void ClientRead(IReadMessage msg, NetworkHeader header)
{
switch (header)
{
case NetworkHeader.Infect:
int infectBranch = -1;
ushort itemId = msg.ReadUInt16();
bool infect = msg.ReadBoolean();
if (infect)
{
infectBranch = msg.ReadInt32();
}
Entity? entity = Entity.FindEntityByID(itemId);
if (entity is Item item)
{
if (infect)
{
ClaimTarget(item, Branches.FirstOrDefault(b => b.ID == infectBranch));
}
else
{
RemoveClaim(itemId);
}
}
else
{
DebugConsole.AddWarning($"Received Infect.{infect} Network Header with invalid item ID: {itemId}, which belongs to {entity?.ToString() ?? "null!"}");
}
break;
case NetworkHeader.BranchCreate:
int parentId = msg.ReadInt32();
BallastFloraBranch branch = ReadBranch(msg);
BallastFloraBranch? parent = Branches.FirstOrDefault(b => b.ID == parentId);
if (parent == null)
{
DebugConsole.AddWarning($"Received BranchCreate with an invalid parent ID: {parentId}, Maximum ID is {Branches.Max(b => b.ID)}");
}
UpdateConnections(branch, parent);
Branches.Add(branch);
OnBranchGrowthSuccess(branch);
break;
case NetworkHeader.BranchRemove:
int removedBranchId = msg.ReadInt32();
BallastFloraBranch removedBranch = Branches.FirstOrDefault(b => b.ID == removedBranchId);
if (removedBranch != null)
{
RemoveBranch(removedBranch);
}
else
{
DebugConsole.AddWarning($"Received BranchRemove for a branch that doesn't exist. ID: {removedBranchId}, Maximum ID is {Branches.Max(b => b.ID)}");
}
break;
case NetworkHeader.BranchDamage:
int damageBranchId = msg.ReadInt32();
float damage = msg.ReadSingle();
float health = msg.ReadSingle();
BallastFloraBranch damagedBranch = Branches.FirstOrDefault(b => b.ID == damageBranchId);
if (damagedBranch != null)
{
CreateDamageParticle(damagedBranch, damage);
damagedBranch.Health = health;
}
else
{
DebugConsole.AddWarning($"Received BranchDamage for a branch that doesn't exist. ID: {damageBranchId}, Maximum ID is {Branches.Max(b => b.ID)}");
}
break;
case NetworkHeader.Kill:
Kill();
break;
}
PowerConsumptionTimer = msg.ReadSingle();
}
private BallastFloraBranch ReadBranch(IReadMessage msg)
{
int id = msg.ReadInt32();
byte type = (byte) msg.ReadRangedInteger(0b0000, 0b1111);
byte sides = (byte) msg.ReadRangedInteger(0b0000, 0b1111);
int flowerConfig = msg.ReadRangedInteger(0, 0xFFF);
int leafConfig = msg.ReadRangedInteger(0, 0xFFF);
int maxHealth = msg.ReadUInt16();
int posX = msg.ReadInt32(), posY = msg.ReadInt32();
Vector2 pos = new Vector2(posX * VineTile.Size, posY * VineTile.Size);
return new BallastFloraBranch(this, pos, (VineTileType)type, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafConfig))
{
ID = id,
MaxHealth = maxHealth,
Sides = (TileSide) sides
};
}
}
}
@@ -36,8 +36,8 @@ namespace Barotrauma
Rand.Range(worldPosition.Y - size.Y, worldPosition.Y + 20.0f)); Rand.Range(worldPosition.Y - size.Y, worldPosition.Y + 20.0f));
Vector2 particleVel = new Vector2( Vector2 particleVel = new Vector2(
(particlePos.X - (worldPosition.X + size.X / 2.0f)), particlePos.X - (worldPosition.X + size.X / 2.0f),
(float)Math.Sqrt(size.X) * Rand.Range(0.0f, 15.0f) * growModifier); Math.Max((float)Math.Sqrt(size.X) * Rand.Range(0.0f, 15.0f) * growModifier, 0.0f));
particleVel.X = MathHelper.Clamp(particleVel.X, -200.0f, 200.0f); particleVel.X = MathHelper.Clamp(particleVel.X, -200.0f, 200.0f);
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.MapCreatures.Behavior;
namespace Barotrauma namespace Barotrauma
{ {
@@ -48,7 +49,7 @@ namespace Barotrauma
{ {
get get
{ {
return decals.Count > 0; return decals.Count > 0 || BallastFlora != null;
} }
} }
@@ -65,6 +66,8 @@ namespace Barotrauma
public override bool IsVisible(Rectangle worldView) public override bool IsVisible(Rectangle worldView)
{ {
if (BallastFlora != null) { return true; }
if (Screen.Selected != GameMain.SubEditorScreen && !GameMain.DebugDraw) if (Screen.Selected != GameMain.SubEditorScreen && !GameMain.DebugDraw)
{ {
if (decals.Count == 0 && paintAmount < minimumPaintAmountToDraw) { return false; } if (decals.Count == 0 && paintAmount < minimumPaintAmountToDraw) { return false; }
@@ -229,6 +232,7 @@ namespace Barotrauma
{ {
if (back && Screen.Selected != GameMain.SubEditorScreen) if (back && Screen.Selected != GameMain.SubEditorScreen)
{ {
BallastFlora?.Draw(spriteBatch);
DrawDecals(spriteBatch); DrawDecals(spriteBatch);
return; return;
} }
@@ -244,7 +248,7 @@ namespace Barotrauma
alpha = Math.Min((float)(Timing.TotalTime - lastAmbientLightEditTime) / hideTimeAfterEdit - 1.0f, 1.0f); alpha = Math.Min((float)(Timing.TotalTime - lastAmbientLightEditTime) / hideTimeAfterEdit - 1.0f, 1.0f);
} }
Rectangle drawRect = Rectangle drawRect =
Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height); Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height);
if ((IsSelected || IsHighlighted) && editing) if ((IsSelected || IsHighlighted) && editing)
@@ -332,6 +336,7 @@ namespace Barotrauma
public void DrawSectionColors(SpriteBatch spriteBatch) public void DrawSectionColors(SpriteBatch spriteBatch)
{ {
if (BackgroundSections == null || BackgroundSections.Count == 0) { return; }
Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition; Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
Point sectionSize = BackgroundSections[0].Rect.Size; Point sectionSize = BackgroundSections[0].Rect.Size;
Vector2 drawPos = drawOffset + new Vector2(rect.Location.X + sectionSize.X / 2, rect.Location.Y - sectionSize.Y / 2); Vector2 drawPos = drawOffset + new Vector2(rect.Location.X + sectionSize.X / 2, rect.Location.Y - sectionSize.Y / 2);
@@ -607,6 +612,26 @@ namespace Barotrauma
public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime)
{ {
bool isBallastFloraUpdate = message.ReadBoolean();
if (isBallastFloraUpdate)
{
BallastFloraBehavior.NetworkHeader header = (BallastFloraBehavior.NetworkHeader) message.ReadByte();
if (header == BallastFloraBehavior.NetworkHeader.Spawn)
{
string identifier = message.ReadString();
float x = message.ReadSingle();
float y = message.ReadSingle();
BallastFlora = new BallastFloraBehavior(this, BallastFloraPrefab.Find(identifier), new Vector2(x, y), firstGrowth: true)
{
PowerConsumptionTimer = message.ReadSingle()
};
}
else if (BallastFlora != null)
{
BallastFlora.ClientRead(message, header);
}
return;
}
remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
@@ -1,30 +1,38 @@
using Microsoft.Xna.Framework; using Barotrauma.SpriteDeformations;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class BackgroundCreature : ISteerable class BackgroundCreature : ISteerable
{ {
const float MaxDepth = 100.0f; const float MaxDepth = 10000.0f;
const float CheckWallsInterval = 5.0f; const float CheckWallsInterval = 5.0f;
public bool Enabled; public bool Visible;
private BackgroundCreaturePrefab prefab; public readonly BackgroundCreaturePrefab Prefab;
private readonly List<SpriteDeformation> uniqueSpriteDeformations = new List<SpriteDeformation>();
private readonly List<SpriteDeformation> spriteDeformations = new List<SpriteDeformation>();
private readonly List<SpriteDeformation> lightSpriteDeformations = new List<SpriteDeformation>();
private Vector2 position; private Vector2 position;
private Vector3 velocity; private Vector3 velocity;
private float depth; private float depth;
private SteeringManager steeringManager;
private float checkWallsTimer; private float alpha = 1.0f;
private readonly SteeringManager steeringManager;
private float checkWallsTimer, flashTimer;
private float wanderZPhase; private float wanderZPhase;
private Vector2 obstacleDiff; private Vector2 obstacleDiff;
@@ -33,9 +41,16 @@ namespace Barotrauma
public Swarm Swarm; public Swarm Swarm;
Vector2 drawPosition; Vector2 drawPosition;
public Vector2 TransformedPosition
public Vector2[,] CurrentSpriteDeformation
{ {
get { return drawPosition; } get;
private set;
}
public Vector2[,] CurrentLightSpriteDeformation
{
get;
private set;
} }
public Vector2 SimPosition public Vector2 SimPosition
@@ -58,11 +73,10 @@ namespace Barotrauma
get; get;
set; set;
} }
public BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position) public BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position)
{ {
this.prefab = prefab; this.Prefab = prefab;
this.position = position; this.position = position;
drawPosition = position; drawPosition = position;
@@ -70,18 +84,73 @@ namespace Barotrauma
steeringManager = new SteeringManager(this); steeringManager = new SteeringManager(this);
velocity = new Vector3( velocity = new Vector3(
Rand.Range(-prefab.Speed, prefab.Speed), Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly),
Rand.Range(-prefab.Speed, prefab.Speed), Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly),
Rand.Range(0.0f, prefab.WanderZAmount)); Rand.Range(0.0f, prefab.WanderZAmount, Rand.RandSync.ClientOnly));
checkWallsTimer = Rand.Range(0.0f, CheckWallsInterval); checkWallsTimer = Rand.Range(0.0f, CheckWallsInterval, Rand.RandSync.ClientOnly);
foreach (XElement subElement in prefab.Config.Elements())
{
List<SpriteDeformation> deformationList = null;
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "deformablesprite":
deformationList = spriteDeformations;
break;
case "deformablelightsprite":
deformationList = lightSpriteDeformations;
break;
default:
continue;
}
foreach (XElement animationElement in subElement.Elements())
{
SpriteDeformation deformation = null;
int sync = animationElement.GetAttributeInt("sync", -1);
if (sync > -1)
{
string typeName = animationElement.GetAttributeString("type", "").ToLowerInvariant();
deformation = uniqueSpriteDeformations.Find(d => d.TypeName == typeName && d.Sync == sync);
}
if (deformation == null)
{
deformation = SpriteDeformation.Load(animationElement, prefab.Name);
if (deformation != null)
{
uniqueSpriteDeformations.Add(deformation);
}
}
if (deformation != null)
{
deformationList.Add(deformation);
}
}
}
} }
public void Update(float deltaTime) public void Update(float deltaTime)
{ {
position += new Vector2(velocity.X, velocity.Y) * deltaTime; position += new Vector2(velocity.X, velocity.Y) * deltaTime;
depth = MathHelper.Clamp(depth + velocity.Z * deltaTime, 0.0f, MaxDepth); depth = MathHelper.Clamp(depth + velocity.Z * deltaTime, Prefab.MinDepth, Prefab.MaxDepth * 10);
if (Prefab.FlashInterval > 0.0f)
{
flashTimer -= deltaTime;
if (flashTimer > 0.0f)
{
alpha = 0.0f;
}
else
{
//value goes from 0 to 1 and back to 0 during the flash
alpha = (float)Math.Sin(-flashTimer / Prefab.FlashDuration * MathHelper.Pi) * PerlinNoise.GetPerlin((float)Timing.TotalTime * 0.1f, (float)Timing.TotalTime * 0.2f);
if (flashTimer < -Prefab.FlashDuration)
{
flashTimer = Prefab.FlashInterval;
}
}
}
checkWallsTimer -= deltaTime; checkWallsTimer -= deltaTime;
if (checkWallsTimer <= 0.0f && Level.Loaded != null) if (checkWallsTimer <= 0.0f && Level.Loaded != null)
@@ -134,54 +203,145 @@ namespace Barotrauma
float midPointDist = Vector2.Distance(SimPosition, midPoint) * 100.0f; float midPointDist = Vector2.Distance(SimPosition, midPoint) * 100.0f;
if (midPointDist > Swarm.MaxDistance) if (midPointDist > Swarm.MaxDistance)
{ {
steeringManager.SteeringSeek(midPoint, ((midPointDist / Swarm.MaxDistance) - 1.0f) * prefab.Speed); steeringManager.SteeringSeek(midPoint, ((midPointDist / Swarm.MaxDistance) - 1.0f) * Prefab.Speed);
} }
steeringManager.SteeringManual(deltaTime, Swarm.AvgVelocity() * Swarm.Cohesion); steeringManager.SteeringManual(deltaTime, Swarm.AvgVelocity() * Swarm.Cohesion);
} }
if (prefab.WanderAmount > 0.0f) if (Prefab.WanderAmount > 0.0f)
{ {
steeringManager.SteeringWander(prefab.Speed); steeringManager.SteeringWander(Prefab.Speed);
} }
if (obstacleDiff != Vector2.Zero) if (obstacleDiff != Vector2.Zero)
{ {
steeringManager.SteeringManual(deltaTime, -obstacleDiff * (1.0f - obstacleDist / 5000.0f) * prefab.Speed); steeringManager.SteeringManual(deltaTime, -obstacleDiff * (1.0f - obstacleDist / 5000.0f) * Prefab.Speed);
} }
steeringManager.Update(prefab.Speed); steeringManager.Update(Prefab.Speed);
if (prefab.WanderZAmount > 0.0f) if (Prefab.WanderZAmount > 0.0f)
{ {
wanderZPhase += Rand.Range(-prefab.WanderZAmount, prefab.WanderZAmount); wanderZPhase += Rand.Range(-Prefab.WanderZAmount, Prefab.WanderZAmount);
velocity.Z = (float)Math.Sin(wanderZPhase) * prefab.Speed; velocity.Z = (float)Math.Sin(wanderZPhase) * Prefab.Speed;
} }
velocity = Vector3.Lerp(velocity, new Vector3(Steering.X, Steering.Y, velocity.Z), deltaTime); velocity = Vector3.Lerp(velocity, new Vector3(Steering.X, Steering.Y, velocity.Z), deltaTime);
UpdateDeformations(deltaTime);
}
public void DrawLightSprite(SpriteBatch spriteBatch, Camera cam)
{
Draw(spriteBatch, cam, Prefab.LightSprite, Prefab.DeformableLightSprite, CurrentLightSpriteDeformation, Color.White * alpha);
} }
public void Draw(SpriteBatch spriteBatch, Camera cam) public void Draw(SpriteBatch spriteBatch, Camera cam)
{ {
Draw(spriteBatch,
cam,
Prefab.Sprite,
Prefab.DeformableSprite,
CurrentSpriteDeformation,
Color.Lerp(Color.White, Level.Loaded.BackgroundColor, depth / Math.Max(MaxDepth, Prefab.MaxDepth)) * alpha);
}
private void Draw(SpriteBatch spriteBatch, Camera cam, Sprite sprite, DeformableSprite deformableSprite, Vector2[,] currentSpriteDeformation, Color color)
{
if (sprite == null && deformableSprite == null) { return; }
if (color.A == 0) { return; }
float rotation = 0.0f; float rotation = 0.0f;
if (!prefab.DisableRotation) if (!Prefab.DisableRotation)
{ {
rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y)); rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y));
if (velocity.X < 0.0f) rotation -= MathHelper.Pi; if (velocity.X < 0.0f) { rotation -= MathHelper.Pi; }
} }
drawPosition = position; drawPosition = GetDrawPosition(cam);
if (depth > 0.0f)
float scale = GetScale();
sprite?.Draw(spriteBatch,
new Vector2(drawPosition.X, -drawPosition.Y),
color,
rotation,
scale,
Prefab.DisableFlipping || velocity.X > 0.0f ? SpriteEffects.None : SpriteEffects.FlipHorizontally,
Math.Min(depth / MaxDepth, 1.0f));
if (deformableSprite != null)
{
if (currentSpriteDeformation != null)
{
deformableSprite.Deform(currentSpriteDeformation);
}
else
{
deformableSprite.Reset();
}
deformableSprite?.Draw(cam,
new Vector3(drawPosition.X, drawPosition.Y, Math.Min(depth / 10000.0f, 1.0f)),
deformableSprite.Origin,
rotation,
Vector2.One * scale,
color,
mirror: Prefab.DisableFlipping || velocity.X <= 0.0f);
}
}
public Vector2 GetDrawPosition(Camera cam)
{
Vector2 drawPosition = WorldPosition;
if (depth >= 0)
{ {
Vector2 camOffset = drawPosition - cam.WorldViewCenter; Vector2 camOffset = drawPosition - cam.WorldViewCenter;
drawPosition -= camOffset * (depth / MaxDepth) * 0.05f; drawPosition -= camOffset * depth / MaxDepth;
} }
return drawPosition;
}
prefab.Sprite.Draw(spriteBatch, public float GetScale()
new Vector2(drawPosition.X, -drawPosition.Y), {
Color.Lerp(Color.White, Level.Loaded.BackgroundColor, (depth / MaxDepth) * 0.2f), return Math.Max(1.0f - depth / MaxDepth, 0.05f) * Prefab.Scale;
rotation, (1.0f - (depth / MaxDepth) * 0.2f) * prefab.Scale, }
velocity.X > 0.0f ? SpriteEffects.None : SpriteEffects.FlipHorizontally,
(depth / MaxDepth)); public Rectangle GetExtents(Camera cam)
{
Vector2 min = GetDrawPosition(cam);
Vector2 max = min;
float scale = GetScale();
GetSpriteExtents(Prefab.Sprite, ref min, ref max);
GetSpriteExtents(Prefab.LightSprite, ref min, ref max);
GetSpriteExtents(Prefab.DeformableSprite?.Sprite, ref min, ref max);
GetSpriteExtents(Prefab.DeformableLightSprite?.Sprite, ref min, ref max);
return new Rectangle(min.ToPoint(), (max - min).ToPoint());
void GetSpriteExtents(Sprite sprite, ref Vector2 min, ref Vector2 max)
{
if (sprite == null) { return; }
min.X = Math.Min(min.X, min.X - sprite.size.X * sprite.RelativeOrigin.X * scale);
min.Y = Math.Min(min.Y, min.Y - sprite.size.Y * sprite.RelativeOrigin.Y * scale);
max.X = Math.Max(max.X, max.X + sprite.size.X * (1.0f - sprite.RelativeOrigin.X) * scale);
max.Y = Math.Max(max.Y, max.Y + sprite.size.Y * (1.0f - sprite.RelativeOrigin.Y) * scale);
}
}
private void UpdateDeformations(float deltaTime)
{
foreach (SpriteDeformation deformation in uniqueSpriteDeformations)
{
deformation.Update(deltaTime);
}
if (spriteDeformations.Count > 0)
{
CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, Prefab.DeformableSprite.Size);
}
if (lightSpriteDeformations.Count > 0)
{
CurrentLightSpriteDeformation = SpriteDeformation.GetDeformation(lightSpriteDeformations, Prefab.DeformableLightSprite.Size);
}
} }
} }
@@ -9,14 +9,14 @@ namespace Barotrauma
{ {
class BackgroundCreatureManager class BackgroundCreatureManager
{ {
const int MaxSprites = 100; const int MaxCreatures = 100;
const float CheckActiveInterval = 1.0f; const float VisibilityCheckInterval = 1.0f;
private float checkActiveTimer; private float checkVisibleTimer;
private List<BackgroundCreaturePrefab> prefabs = new List<BackgroundCreaturePrefab>(); private readonly List<BackgroundCreaturePrefab> prefabs = new List<BackgroundCreaturePrefab>();
private List<BackgroundCreature> activeSprites = new List<BackgroundCreature>(); private readonly List<BackgroundCreature> creatures = new List<BackgroundCreature>();
public BackgroundCreatureManager(string configPath) public BackgroundCreatureManager(string configPath)
{ {
@@ -60,92 +60,111 @@ namespace Barotrauma
} }
} }
public void SpawnSprites(int count, Vector2? position = null) public void SpawnCreatures(Level level, int count, Vector2? position = null)
{ {
activeSprites.Clear(); creatures.Clear();
if (prefabs.Count == 0) return; if (prefabs.Count == 0) { return; }
count = Math.Min(count, MaxSprites); count = Math.Min(count, MaxCreatures);
for (int i = 0; i < count; i++ ) List<BackgroundCreaturePrefab> availablePrefabs = new List<BackgroundCreaturePrefab>(prefabs);
for (int i = 0; i < count; i++)
{ {
Vector2 pos = Vector2.Zero; Vector2 pos = Vector2.Zero;
if (position == null) if (position == null)
{ {
var wayPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine==null); var wayPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine == null);
if (wayPoints.Any()) if (wayPoints.Any())
{ {
WayPoint wp = wayPoints[Rand.Int(wayPoints.Count, Rand.RandSync.ClientOnly)]; WayPoint wp = wayPoints[Rand.Int(wayPoints.Count, Rand.RandSync.ClientOnly)];
pos = new Vector2(wp.Rect.X, wp.Rect.Y); pos = new Vector2(wp.Rect.X, wp.Rect.Y);
pos += Rand.Vector(200.0f, Rand.RandSync.ClientOnly); pos += Rand.Vector(200.0f, Rand.RandSync.ClientOnly);
} }
else else
{ {
pos = Rand.Vector(2000.0f, Rand.RandSync.ClientOnly); pos = Rand.Vector(2000.0f, Rand.RandSync.ClientOnly);
} }
} }
else else
{ {
pos = (Vector2)position; pos = (Vector2)position;
} }
var prefab = ToolBox.SelectWeightedRandom(availablePrefabs, availablePrefabs.Select(p => p.GetCommonness(level.GenerationParams)).ToList(), Rand.RandSync.ClientOnly);
var prefab = prefabs[Rand.Int(prefabs.Count, Rand.RandSync.ClientOnly)]; if (prefab == null) { break; }
int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax, Rand.RandSync.ClientOnly); int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax, Rand.RandSync.ClientOnly);
List<BackgroundCreature> swarmMembers = new List<BackgroundCreature>(); List<BackgroundCreature> swarmMembers = new List<BackgroundCreature>();
for (int n = 0; n < amount; n++) for (int n = 0; n < amount; n++)
{ {
var newSprite = new BackgroundCreature(prefab, pos); var creature = new BackgroundCreature(prefab, pos + Rand.Vector(Rand.Range(0.0f, prefab.SwarmRadius, Rand.RandSync.ClientOnly), Rand.RandSync.ClientOnly));
activeSprites.Add(newSprite); creatures.Add(creature);
swarmMembers.Add(newSprite); swarmMembers.Add(creature);
} }
if (amount > 0) if (amount > 1)
{ {
new Swarm(swarmMembers, prefab.SwarmRadius, prefab.SwarmCohesion); new Swarm(swarmMembers, prefab.SwarmRadius, prefab.SwarmCohesion);
} }
if (creatures.Count(c => c.Prefab == prefab) > prefab.MaxCount)
{
availablePrefabs.Remove(prefab);
if (availablePrefabs.Count <= 0) { break; }
}
} }
} }
public void ClearSprites() public void Clear()
{ {
activeSprites.Clear(); creatures.Clear();
} }
public void Update(float deltaTime, Camera cam) public void Update(float deltaTime, Camera cam)
{ {
if (checkActiveTimer < 0.0f) if (checkVisibleTimer < 0.0f)
{ {
foreach (BackgroundCreature sprite in activeSprites) int margin = 500;
foreach (BackgroundCreature creature in creatures)
{ {
sprite.Enabled = Math.Abs(sprite.TransformedPosition.X - cam.WorldViewCenter.X) < cam.WorldView.Width && Rectangle extents = creature.GetExtents(cam);
Math.Abs(sprite.TransformedPosition.Y - cam.WorldViewCenter.Y) < cam.WorldView.Height; bool wasVisible = creature.Visible;
creature.Visible =
extents.Right >= cam.WorldView.X - margin &&
extents.X <= cam.WorldView.Right + margin &&
extents.Bottom >= cam.WorldView.Y - cam.WorldView.Height - margin &&
extents.Y <= cam.WorldView.Y + margin;
} }
checkActiveTimer = CheckActiveInterval; checkVisibleTimer = VisibilityCheckInterval;
} }
else else
{ {
checkActiveTimer -= deltaTime; checkVisibleTimer -= deltaTime;
} }
foreach (BackgroundCreature sprite in activeSprites) foreach (BackgroundCreature creature in creatures)
{ {
if (!sprite.Enabled) continue; if (!creature.Visible) { continue; }
sprite.Update(deltaTime); creature.Update(deltaTime);
} }
} }
public void Draw(SpriteBatch spriteBatch, Camera cam) public void Draw(SpriteBatch spriteBatch, Camera cam)
{ {
foreach (BackgroundCreature sprite in activeSprites) foreach (BackgroundCreature creature in creatures)
{ {
if (!sprite.Enabled) continue; if (!creature.Visible) { continue; }
sprite.Draw(spriteBatch, cam); creature.Draw(spriteBatch, cam);
}
}
public void DrawLights(SpriteBatch spriteBatch, Camera cam)
{
foreach (BackgroundCreature creature in creatures)
{
if (!creature.Visible) { continue; }
creature.DrawLightSprite(spriteBatch, cam);
} }
} }
} }
@@ -1,50 +1,117 @@
using System.Xml.Linq; using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class BackgroundCreaturePrefab class BackgroundCreaturePrefab
{ {
public readonly Sprite Sprite; public readonly Sprite Sprite, LightSprite;
public readonly DeformableSprite DeformableSprite, DeformableLightSprite;
public readonly float Speed; public readonly string Name;
public readonly float WanderAmount; public readonly XElement Config;
public readonly float WanderZAmount; [Serialize(1.0f, true)]
public float Speed { get; private set; }
public readonly int SwarmMin, SwarmMax; [Serialize(0.0f, true)]
public readonly float SwarmRadius, SwarmCohesion; public float WanderAmount { get; private set; }
public readonly bool DisableRotation; [Serialize(0.0f, true)]
public float WanderZAmount { get; private set; }
[Serialize(1, true)]
public int SwarmMin { get; private set; }
[Serialize(1, true)]
public int SwarmMax { get; private set; }
[Serialize(200.0f, true)]
public float SwarmRadius { get; private set; }
[Serialize(0.2f, true)]
public float SwarmCohesion { get; private set; }
[Serialize(10.0f, true)]
public float MinDepth { get; private set; }
[Serialize(1000.0f, true)]
public float MaxDepth { get; private set; }
[Serialize(false, true)]
public bool DisableRotation { get; private set; }
[Serialize(false, true)]
public bool DisableFlipping { get; private set; }
[Serialize(1.0f, true)]
public float Scale { get; private set; }
[Serialize(1.0f, true)]
public float Commonness { get; private set; }
[Serialize(1000, true)]
public int MaxCount { get; private set; }
[Serialize(0.0f, true)]
public float FlashInterval { get; private set; }
[Serialize(0.0f, true)]
public float FlashDuration { get; private set; }
/// <summary>
/// Overrides the commonness of the object in a specific level type.
/// Key = name of the level type, value = commonness in that level type.
/// </summary>
public Dictionary<string, float> OverrideCommonness = new Dictionary<string, float>();
public readonly float Scale;
public BackgroundCreaturePrefab(XElement element) public BackgroundCreaturePrefab(XElement element)
{ {
Speed = element.GetAttributeFloat("speed", 1.0f); Name = element.Name.ToString();
WanderAmount = element.GetAttributeFloat("wanderamount", 0.0f); Config = element;
WanderZAmount = element.GetAttributeFloat("wanderzamount", 0.0f); SerializableProperty.DeserializeProperties(this, element);
SwarmMin = element.GetAttributeInt("swarmmin", 1);
SwarmMax = element.GetAttributeInt("swarmmax", 1);
SwarmRadius = element.GetAttributeFloat("swarmradius", 200.0f);
SwarmCohesion = element.GetAttributeFloat("swarmcohesion", 0.2f);
DisableRotation = element.GetAttributeBool("disablerotation", false);
Scale = element.GetAttributeFloat("scale", 1.0f);
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (!subElement.Name.ToString().Equals("sprite", System.StringComparison.OrdinalIgnoreCase)) { continue; } switch (subElement.Name.ToString().ToLowerInvariant())
{
Sprite = new Sprite(subElement, lazyLoad: true); case "sprite":
break; Sprite = new Sprite(subElement, lazyLoad: true);
break;
case "deformablesprite":
DeformableSprite = new DeformableSprite(subElement, lazyLoad: true);
break;
case "lightsprite":
LightSprite = new Sprite(subElement, lazyLoad: true);
break;
case "deformablelightsprite":
DeformableLightSprite = new DeformableSprite(subElement, lazyLoad: true);
break;
case "overridecommonness":
string levelType = subElement.GetAttributeString("leveltype", "").ToLowerInvariant();
if (!OverrideCommonness.ContainsKey(levelType))
{
OverrideCommonness.Add(levelType, subElement.GetAttributeFloat("commonness", 1.0f));
}
break;
}
} }
} }
public float GetCommonness(LevelGenerationParams generationParams)
{
if (generationParams?.Identifier != null &&
(OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness) ||
(generationParams.OldIdentifier != null && OverrideCommonness.TryGetValue(generationParams.OldIdentifier, out commonness))))
{
return commonness;
}
return Commonness;
}
} }
} }
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework; using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -8,72 +9,82 @@ namespace Barotrauma
{ {
static partial class CaveGenerator static partial class CaveGenerator
{ {
public static List<VertexPositionTexture> GenerateRenderVerticeList(List<Vector2[]> triangles) public static List<VertexPositionTexture> GenerateWallVertices(List<Vector2[]> triangles, LevelGenerationParams generationParams, float zCoord)
{ {
var verticeList = new List<VertexPositionTexture>(); var vertices = new List<VertexPositionTexture>();
for (int i = 0; i < triangles.Count; i++) for (int i = 0; i < triangles.Count; i++)
{ {
foreach (Vector2 vertex in triangles[i]) foreach (Vector2 vertex in triangles[i])
{ {
//shift the coordinates around a bit to make the texture repetition less obvious Vector2 uvCoords = vertex / generationParams.WallTextureSize;
Vector2 uvCoords = new Vector2( vertices.Add(new VertexPositionTexture(new Vector3(vertex, zCoord), uvCoords));
vertex.X / 2000.0f + (float)Math.Sin(vertex.X / 500.0f) * 0.15f,
vertex.Y / 2000.0f + (float)Math.Sin(vertex.Y / 700.0f) * 0.15f);
verticeList.Add(new VertexPositionTexture(new Vector3(vertex, 1.0f), uvCoords));
} }
} }
return verticeList; return vertices;
} }
public static VertexPositionTexture[] GenerateWallShapes(List<VoronoiCell> cells, Level level) public static List<VertexPositionTexture> GenerateWallEdgeVertices(List<VoronoiCell> cells, Level level, float zCoord)
{ {
float outWardThickness = 30.0f; float outWardThickness = level.GenerationParams.WallEdgeExpandOutwardsAmount;
List<VertexPositionTexture> verticeList = new List<VertexPositionTexture>(); List<VertexPositionTexture> vertices = new List<VertexPositionTexture>();
foreach (VoronoiCell cell in cells) foreach (VoronoiCell cell in cells)
{ {
CompareCCW compare = new CompareCCW(cell.Center); Vector2 minVert = cell.Edges[0].Point1;
Vector2 maxVert = cell.Edges[0].Point1;
float circumference = 0.0f;
foreach (GraphEdge edge in cell.Edges) foreach (GraphEdge edge in cell.Edges)
{ {
if (edge.Cell1 != null && edge.Cell1.Body == null && edge.Cell1.CellType != CellType.Empty) edge.Cell1 = null; circumference += Vector2.Distance(edge.Point1, edge.Point2);
if (edge.Cell2 != null && edge.Cell2.Body == null && edge.Cell2.CellType != CellType.Empty) edge.Cell2 = null; minVert = new Vector2(
Math.Min(minVert.X, edge.Point1.X),
if (compare.Compare(edge.Point1, edge.Point2) == -1) Math.Min(minVert.Y, edge.Point1.Y));
{ maxVert = new Vector2(
var temp = edge.Point1; Math.Max(maxVert.X, edge.Point1.X),
edge.Point1 = edge.Point2; Math.Max(maxVert.Y, edge.Point1.Y));
edge.Point2 = temp;
}
} }
} Vector2 center = (minVert + maxVert) / 2;
foreach (VoronoiCell cell in cells)
{
foreach (GraphEdge edge in cell.Edges) foreach (GraphEdge edge in cell.Edges)
{ {
if (!edge.IsSolid) continue; if (!edge.IsSolid) { continue; }
GraphEdge leftEdge = cell.Edges.Find(e => e != edge && (edge.Point1 == e.Point1 || edge.Point1 == e.Point2)); GraphEdge leftEdge = cell.Edges.Find(e => e != edge && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
GraphEdge rightEdge = cell.Edges.Find(e => e != edge && (edge.Point2 == e.Point1 || edge.Point2 == e.Point2)); var leftAdjacentCell = leftEdge?.AdjacentCell(cell);
if (leftAdjacentCell != null)
{
var adjEdge = leftAdjacentCell.Edges.Find(e => e != leftEdge && e.IsSolid && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
if (adjEdge != null) { leftEdge = adjEdge; }
}
GraphEdge rightEdge = cell.Edges.Find(e => e != edge && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
var rightAdjacentCell = rightEdge?.AdjacentCell(cell);
if (rightAdjacentCell != null)
{
var adjEdge = rightAdjacentCell.Edges.Find(e => e != rightEdge && e.IsSolid && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
if (adjEdge != null) { rightEdge = adjEdge; }
}
Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero; Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero;
float inwardThickness1 = 100; float inwardThickness1 = level.GenerationParams.WallEdgeExpandInwardsAmount;
float inwardThickness2 = 100; float inwardThickness2 = level.GenerationParams.WallEdgeExpandInwardsAmount;
if (leftEdge != null && !leftEdge.IsSolid) if (leftEdge != null && !leftEdge.IsSolid)
{ {
leftNormal = edge.Point1 == leftEdge.Point1 ? leftNormal = edge.Point1.NearlyEquals(leftEdge.Point1) ?
Vector2.Normalize(leftEdge.Point2 - leftEdge.Point1) : Vector2.Normalize(leftEdge.Point2 - leftEdge.Point1) :
Vector2.Normalize(leftEdge.Point1 - leftEdge.Point2); Vector2.Normalize(leftEdge.Point1 - leftEdge.Point2);
inwardThickness1 = Vector2.Distance(leftEdge.Point1, leftEdge.Point2) / 2; }
else if (leftEdge != null)
{
leftNormal = -Vector2.Normalize(edge.GetNormal(cell) + leftEdge.GetNormal(leftAdjacentCell ?? cell));
if (!MathUtils.IsValid(leftNormal)) { leftNormal = -edge.GetNormal(cell); }
} }
else else
{ {
leftNormal = Vector2.Normalize(cell.Center - edge.Point1); leftNormal = Vector2.Normalize(cell.Center - edge.Point1);
inwardThickness1 = Vector2.Distance(edge.Point1, cell.Center) / 2;
} }
inwardThickness1 = Math.Min(Vector2.Distance(edge.Point1, cell.Center), inwardThickness1);
if (!MathUtils.IsValid(leftNormal)) if (!MathUtils.IsValid(leftNormal))
{ {
@@ -86,7 +97,7 @@ namespace Barotrauma
if (cell.Body != null) if (cell.Body != null)
{ {
GameMain.World.Remove(cell.Body); if (GameMain.World.BodyList.Contains(cell.Body)) { GameMain.World.Remove(cell.Body); }
cell.Body = null; cell.Body = null;
} }
leftNormal = Vector2.UnitX; leftNormal = Vector2.UnitX;
@@ -95,16 +106,20 @@ namespace Barotrauma
if (rightEdge != null && !rightEdge.IsSolid) if (rightEdge != null && !rightEdge.IsSolid)
{ {
rightNormal = edge.Point2 == rightEdge.Point1 ? rightNormal = edge.Point2.NearlyEquals(rightEdge.Point1) ?
Vector2.Normalize(rightEdge.Point2 - rightEdge.Point1) : Vector2.Normalize(rightEdge.Point2 - rightEdge.Point1) :
Vector2.Normalize(rightEdge.Point1 - rightEdge.Point2); Vector2.Normalize(rightEdge.Point1 - rightEdge.Point2);
inwardThickness2 = Vector2.Distance(rightEdge.Point1, rightEdge.Point2) / 2; }
else if (rightEdge != null)
{
rightNormal = -Vector2.Normalize(edge.GetNormal(cell) + rightEdge.GetNormal(rightAdjacentCell ?? cell));
if (!MathUtils.IsValid(rightNormal)) { rightNormal = -edge.GetNormal(cell); }
} }
else else
{ {
rightNormal = Vector2.Normalize(cell.Center - edge.Point2); rightNormal = Vector2.Normalize(cell.Center - edge.Point2);
inwardThickness2 = Vector2.Distance(edge.Point2, cell.Center) / 2;
} }
inwardThickness2 = Math.Min(Vector2.Distance(edge.Point2, cell.Center), inwardThickness2);
if (!MathUtils.IsValid(rightNormal)) if (!MathUtils.IsValid(rightNormal))
{ {
@@ -117,24 +132,23 @@ namespace Barotrauma
if (cell.Body != null) if (cell.Body != null)
{ {
GameMain.World.Remove(cell.Body); if (GameMain.World.BodyList.Contains(cell.Body)) { GameMain.World.Remove(cell.Body); }
cell.Body = null; cell.Body = null;
} }
rightNormal = Vector2.UnitX; rightNormal = Vector2.UnitX;
break; break;
} }
float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - cell.Center)); float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - center));
float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - cell.Center)); float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - center));
//handle wrapping around 0/360 //handle wrapping around 0/360
if (point1UV - point2UV > MathHelper.Pi) if (point1UV - point2UV > MathHelper.Pi)
{ {
point2UV += MathHelper.TwoPi; point1UV -= MathHelper.TwoPi;
} }
//the texture wraps around the cell 4 times int textureRepeatCount = (int)Math.Max(circumference / 2 / level.GenerationParams.WallEdgeTextureWidth, 1);
//TODO: define the uv scale in level generation parameters? point1UV = point1UV / MathHelper.TwoPi * textureRepeatCount;
point1UV = point1UV / MathHelper.TwoPi * 4; point2UV = point2UV / MathHelper.TwoPi * textureRepeatCount;
point2UV = point2UV / MathHelper.TwoPi * 4;
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
@@ -147,9 +161,9 @@ namespace Barotrauma
verts[1] = edge.Point2 - rightNormal * outWardThickness; verts[1] = edge.Point2 - rightNormal * outWardThickness;
verts[2] = edge.Point1 + leftNormal * inwardThickness1; verts[2] = edge.Point1 + leftNormal * inwardThickness1;
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(point1UV, 0.0f)); vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 0.0f));
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), new Vector2(point2UV, 0.0f)); vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(point1UV, 0.5f)); vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point1UV, 1.0f));
} }
else else
{ {
@@ -158,16 +172,16 @@ namespace Barotrauma
verts[1] = edge.Point2 - rightNormal * outWardThickness; verts[1] = edge.Point2 - rightNormal * outWardThickness;
verts[2] = edge.Point2 + rightNormal * inwardThickness2; verts[2] = edge.Point2 + rightNormal * inwardThickness2;
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(point1UV, 0.5f)); vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 1.0f));
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), new Vector2(point2UV, 0.0f)); vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(point2UV, 0.5f)); vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point2UV, 1.0f));
} }
verticeList.AddRange(vertPos); vertices.AddRange(vertPos);
} }
} }
} }
return verticeList.ToArray(); return vertices;
} }
} }
} }
@@ -0,0 +1,62 @@
using Microsoft.Xna.Framework;
using System;
using System.Linq;
namespace Barotrauma
{
partial class DestructibleLevelWall : LevelWall, IDamageable
{
public override float Alpha
{
get
{
if (FadeOutDuration <= 0.0f || FadeOutTimer < FadeOutDuration - 1.0f) { return 1.0f; }
return MathHelper.Clamp(FadeOutDuration - FadeOutTimer, 0.0f, 1.0f);
}
}
partial void AddDamageProjSpecific(float damage, Vector2 worldPosition)
{
if (damage <= 0.0f) { return; }
Vector2 particlePos = worldPosition;
if (!Cells.Any(c => c.IsPointInside(particlePos)))
{
bool intersectionFound = false;
foreach (var cell in Cells)
{
foreach (var edge in cell.Edges)
{
if (MathUtils.GetLineIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
{
intersectionFound = true;
particlePos = intersection;
break;
}
}
if (intersectionFound) { break; }
}
}
Vector2 particleDir = particlePos - WorldPosition;
if (particleDir.LengthSquared() > 0.0001f) { particleDir = Vector2.Normalize(particleDir); }
int particleAmount = MathHelper.Clamp((int)damage, 1, 10);
for (int i = 0; i < particleAmount; i++)
{
var particle = GameMain.ParticleManager.CreateParticle("iceshards",
particlePos + Rand.Vector(5.0f),
particleDir * Rand.Range(200.0f, 500.0f) + Rand.Vector(100.0f));
}
}
public void SetDamage(float damage)
{
Damage = damage;
if (Damage >= MaxHealth && !Destroyed)
{
CreateFragments();
Destroy();
}
}
}
}
@@ -14,6 +14,8 @@ namespace Barotrauma
private BackgroundCreatureManager backgroundCreatureManager; private BackgroundCreatureManager backgroundCreatureManager;
public BackgroundCreatureManager BackgroundCreatureManager => backgroundCreatureManager;
public LevelRenderer Renderer => renderer; public LevelRenderer Renderer => renderer;
public void ReloadTextures() public void ReloadTextures()
@@ -33,14 +35,6 @@ namespace Barotrauma
uniqueSprites.Add(sprite); uniqueSprites.Add(sprite);
} }
} }
foreach (Sprite specularSprite in levelObj.Prefab.SpecularSprites)
{
if (!uniqueTextures.Contains(specularSprite.Texture))
{
uniqueTextures.Add(specularSprite.Texture);
uniqueSprites.Add(specularSprite);
}
}
} }
foreach (Sprite sprite in uniqueSprites) foreach (Sprite sprite in uniqueSprites)
@@ -51,7 +45,7 @@ namespace Barotrauma
public void DrawFront(SpriteBatch spriteBatch, Camera cam) public void DrawFront(SpriteBatch spriteBatch, Camera cam)
{ {
if (renderer == null) return; if (renderer == null) { return; }
renderer.Draw(spriteBatch, cam); renderer.Draw(spriteBatch, cam);
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f) if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
@@ -123,22 +117,34 @@ namespace Barotrauma
if (renderer == null) return; if (renderer == null) return;
renderer.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager); renderer.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager);
} }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
foreach (LevelWall levelWall in ExtraWalls) bool isGlobalUpdate = msg.ReadBoolean();
if (isGlobalUpdate)
{ {
if (levelWall.Body.BodyType == BodyType.Static) continue; foreach (LevelWall levelWall in ExtraWalls)
Vector2 bodyPos = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
levelWall.MoveState = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 16);
if (Vector2.DistanceSquared(bodyPos, levelWall.Body.Position) > 0.5f)
{ {
levelWall.Body.SetTransformIgnoreContacts(ref bodyPos, levelWall.Body.Rotation); if (levelWall.Body.BodyType == BodyType.Static) { continue; }
Vector2 bodyPos = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
levelWall.MoveState = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 16);
DestructibleLevelWall destructibleWall = levelWall as DestructibleLevelWall;
if (Vector2.DistanceSquared(bodyPos, levelWall.Body.Position) > 0.5f && (destructibleWall == null || !destructibleWall.Destroyed))
{
levelWall.Body.SetTransformIgnoreContacts(ref bodyPos, levelWall.Body.Rotation);
}
}
}
else
{
int index = msg.ReadUInt16();
byte damageByte = msg.ReadByte();
if (index < ExtraWalls.Count && ExtraWalls[index] is DestructibleLevelWall destructibleWall)
{
destructibleWall.SetDamage(destructibleWall.MaxHealth * damageByte / 255.0f);
} }
} }
} }
@@ -1,14 +1,14 @@
using Barotrauma.Lights; using Barotrauma.Lights;
using Barotrauma.Networking;
using Barotrauma.Particles; using Barotrauma.Particles;
using Barotrauma.Sounds; using Barotrauma.Sounds;
using Barotrauma.Networking; using Barotrauma.SpriteDeformations;
using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.SpriteDeformations;
using System.Linq; using System.Linq;
using FarseerPhysics.Dynamics; using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -74,10 +74,21 @@ namespace Barotrauma
private set; private set;
} }
public bool VisibleOnSonar
{
get;
private set;
}
public float SonarRadius
{
get;
private set;
}
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
Sprite?.EnsureLazyLoaded(); Sprite?.EnsureLazyLoaded();
SpecularSprite?.EnsureLazyLoaded();
Prefab.DeformableSprite?.EnsureLazyLoaded(); Prefab.DeformableSprite?.EnsureLazyLoaded();
CurrentSwingAmount = Prefab.SwingAmountRad; CurrentSwingAmount = Prefab.SwingAmountRad;
@@ -98,7 +109,7 @@ namespace Barotrauma
} }
} }
if (Prefab.LightSourceParams != null) if (Prefab.LightSourceParams != null && Prefab.LightSourceParams.Count > 0)
{ {
LightSources = new LightSource[Prefab.LightSourceParams.Count]; LightSources = new LightSource[Prefab.LightSourceParams.Count];
LightSourceTriggers = new LevelTrigger[Prefab.LightSourceParams.Count]; LightSourceTriggers = new LevelTrigger[Prefab.LightSourceParams.Count];
@@ -138,6 +149,13 @@ namespace Barotrauma
} }
} }
} }
VisibleOnSonar = Prefab.SonarDisruption > 0.0f || Prefab.OverrideProperties.Any(p => p != null && p.SonarDisruption > 0.0f) ||
(Triggers != null && Triggers.Any(t => !MathUtils.NearlyEqual(t.Force, Vector2.Zero) && t.ForceMode != LevelTrigger.TriggerForceMode.LimitVelocity || !string.IsNullOrWhiteSpace(t.InfectIdentifier)));
if (VisibleOnSonar && Triggers.Any())
{
SonarRadius = Triggers.Select(t => t.ColliderRadius * 1.5f).Max();
}
} }
public void Update(float deltaTime) public void Update(float deltaTime)
@@ -232,17 +250,21 @@ namespace Barotrauma
deformation.Update(deltaTime); deformation.Update(deltaTime);
} }
CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, ActivePrefab.DeformableSprite.Size); CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, ActivePrefab.DeformableSprite.Size);
foreach (LightSource lightSource in LightSources) if (LightSources != null)
{ {
if (lightSource?.DeformableLightSprite != null) foreach (LightSource lightSource in LightSources)
{ {
lightSource.DeformableLightSprite.Deform(CurrentSpriteDeformation); if (lightSource?.DeformableLightSprite != null)
{
lightSource.DeformableLightSprite.Deform(CurrentSpriteDeformation);
}
} }
} }
} }
private void UpdatePositionalDeformation(PositionalDeformation positionalDeformation, float deltaTime) private void UpdatePositionalDeformation(PositionalDeformation positionalDeformation, float deltaTime)
{ {
if (Triggers == null) { return; }
Matrix matrix = ActivePrefab.DeformableSprite.GetTransform( Matrix matrix = ActivePrefab.DeformableSprite.GetTransform(
Position, Position,
ActivePrefab.DeformableSprite.Origin, ActivePrefab.DeformableSprite.Origin,
@@ -258,7 +280,7 @@ namespace Barotrauma
Vector2 moveAmount = triggerer.WorldPosition - trigger.TriggererPosition[triggerer]; Vector2 moveAmount = triggerer.WorldPosition - trigger.TriggererPosition[triggerer];
moveAmount = Vector2.Transform(moveAmount, rotationMatrix); moveAmount = Vector2.Transform(moveAmount, rotationMatrix);
moveAmount /= (ActivePrefab.DeformableSprite.Size * Scale); moveAmount /= ActivePrefab.DeformableSprite.Size * Scale;
moveAmount.Y = -moveAmount.Y; moveAmount.Y = -moveAmount.Y;
positionalDeformation.Deform(trigger.WorldPosition, moveAmount, deltaTime, Matrix.Invert(matrix) * positionalDeformation.Deform(trigger.WorldPosition, moveAmount, deltaTime, Matrix.Invert(matrix) *
@@ -269,9 +291,10 @@ namespace Barotrauma
public void ClientRead(IReadMessage msg) public void ClientRead(IReadMessage msg)
{ {
if (Triggers == null) { return; }
for (int i = 0; i < Triggers.Count; i++) for (int i = 0; i < Triggers.Count; i++)
{ {
if (!Triggers[i].UseNetworkSyncing) continue; if (!Triggers[i].UseNetworkSyncing) { continue; }
Triggers[i].ClientRead(msg); Triggers[i].ClientRead(msg);
} }
} }
@@ -12,6 +12,8 @@ namespace Barotrauma
private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>(); private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>();
private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>(); private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>();
private double NextRefreshTime;
//Maximum number of visible objects drawn at once. Should be large enough to not have an effect during normal gameplay, //Maximum number of visible objects drawn at once. Should be large enough to not have an effect during normal gameplay,
//but small enough to prevent wrecking performance when zooming out very far //but small enough to prevent wrecking performance when zooming out very far
const int MaxVisibleObjects = 500; const int MaxVisibleObjects = 500;
@@ -38,11 +40,13 @@ namespace Barotrauma
/// <summary> /// <summary>
/// Checks which level objects are in camera view and adds them to the visibleObjects lists /// Checks which level objects are in camera view and adds them to the visibleObjects lists
/// </summary> /// </summary>
private void RefreshVisibleObjects(Rectangle currentIndices) private void RefreshVisibleObjects(Rectangle currentIndices, float zoom)
{ {
visibleObjectsBack.Clear(); visibleObjectsBack.Clear();
visibleObjectsFront.Clear(); visibleObjectsFront.Clear();
float minSizeToDraw = MathHelper.Lerp(10.0f, 5.0f, Math.Min(zoom * 20.0f, 1.0f));
for (int x = currentIndices.X; x <= currentIndices.Width; x++) for (int x = currentIndices.X; x <= currentIndices.Width; x++)
{ {
for (int y = currentIndices.Y; y <= currentIndices.Height; y++) for (int y = currentIndices.Y; y <= currentIndices.Height; y++)
@@ -50,6 +54,22 @@ namespace Barotrauma
if (objectGrid[x, y] == null) { continue; } if (objectGrid[x, y] == null) { continue; }
foreach (LevelObject obj in objectGrid[x, y]) foreach (LevelObject obj in objectGrid[x, y])
{ {
if (zoom < 0.05f)
{
//hide if the sprite is very small when zoomed this far out
if ((obj.Sprite != null && Math.Min(obj.Sprite.size.X * zoom, obj.Sprite.size.Y * zoom) < 5.0f) ||
(obj.ActivePrefab?.DeformableSprite != null && Math.Min(obj.ActivePrefab.DeformableSprite.Sprite.size.X * zoom, obj.ActivePrefab.DeformableSprite.Sprite.size.Y * zoom) < minSizeToDraw))
{
continue;
}
float zCutoff = MathHelper.Lerp(5000.0f, 500.0f, (0.05f - zoom) * 20.0f);
if (obj.Position.Z > zCutoff)
{
continue;
}
}
var objectList = obj.Position.Z >= 0 ? visibleObjectsBack : visibleObjectsFront; var objectList = obj.Position.Z >= 0 ? visibleObjectsBack : visibleObjectsFront;
int drawOrderIndex = 0; int drawOrderIndex = 0;
for (int i = 0; i < objectList.Count; i++) for (int i = 0; i < objectList.Count; i++)
@@ -83,7 +103,7 @@ namespace Barotrauma
} }
public void DrawObjects(SpriteBatch spriteBatch, Camera cam, bool drawFront, bool specular = false) public void DrawObjects(SpriteBatch spriteBatch, Camera cam, bool drawFront)
{ {
Rectangle indices = Rectangle.Empty; Rectangle indices = Rectangle.Empty;
indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize); indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize);
@@ -102,9 +122,14 @@ namespace Barotrauma
indices.Height = Math.Min(indices.Height, objectGrid.GetLength(1) - 1); indices.Height = Math.Min(indices.Height, objectGrid.GetLength(1) - 1);
float z = 0.0f; float z = 0.0f;
if (currentGridIndices != indices) if (currentGridIndices != indices && Timing.TotalTime > NextRefreshTime)
{ {
RefreshVisibleObjects(indices); RefreshVisibleObjects(indices, cam.Zoom);
if (cam.Zoom < 0.1f)
{
//when zoomed very far out, refresh a little less often
NextRefreshTime = Timing.TotalTime + MathHelper.Lerp(1.0f, 0.0f, cam.Zoom * 10.0f);
}
} }
var objectList = drawFront ? visibleObjectsFront : visibleObjectsBack; var objectList = drawFront ? visibleObjectsFront : visibleObjectsBack;
@@ -113,19 +138,17 @@ namespace Barotrauma
Vector2 camDiff = new Vector2(obj.Position.X, obj.Position.Y) - cam.WorldViewCenter; Vector2 camDiff = new Vector2(obj.Position.X, obj.Position.Y) - cam.WorldViewCenter;
camDiff.Y = -camDiff.Y; camDiff.Y = -camDiff.Y;
Sprite activeSprite = specular ? obj.SpecularSprite : obj.Sprite; Sprite activeSprite = obj.Sprite;
activeSprite?.Draw( activeSprite?.Draw(
spriteBatch, spriteBatch,
new Vector2(obj.Position.X, -obj.Position.Y) - camDiff * obj.Position.Z / 10000.0f, new Vector2(obj.Position.X, -obj.Position.Y) - camDiff * obj.Position.Z / 10000.0f,
Color.Lerp(Color.White, Level.Loaded.BackgroundTextureColor, obj.Position.Z / 5000.0f), Color.Lerp(Color.White, Level.Loaded.BackgroundTextureColor, obj.Position.Z / 3000.0f),
activeSprite.Origin, activeSprite.Origin,
obj.CurrentRotation, obj.CurrentRotation,
obj.CurrentScale, obj.CurrentScale,
SpriteEffects.None, SpriteEffects.None,
z); z);
if (specular) continue;
if (obj.ActivePrefab.DeformableSprite != null) if (obj.ActivePrefab.DeformableSprite != null)
{ {
if (obj.CurrentSpriteDeformation != null) if (obj.CurrentSpriteDeformation != null)
@@ -149,6 +172,7 @@ namespace Barotrauma
{ {
GUI.DrawRectangle(spriteBatch, new Vector2(obj.Position.X, -obj.Position.Y), new Vector2(10.0f, 10.0f), GUI.Style.Red, true); GUI.DrawRectangle(spriteBatch, new Vector2(obj.Position.X, -obj.Position.Y), new Vector2(10.0f, 10.0f), GUI.Style.Red, true);
if (obj.Triggers == null) { continue; }
foreach (LevelTrigger trigger in obj.Triggers) foreach (LevelTrigger trigger in obj.Triggers)
{ {
if (trigger.PhysicsBody == null) continue; if (trigger.PhysicsBody == null) continue;
@@ -126,7 +126,6 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
case "childobject": case "childobject":
case "lightsource":
subElement.Remove(); subElement.Remove();
break; break;
case "deformablesprite": case "deformablesprite":
@@ -141,11 +140,31 @@ namespace Barotrauma
} }
} }
foreach (LightSourceParams lightSourceParams in LightSourceParams) for (int i = 0; i < LightSourceParams.Count; i++)
{ {
var lightElement = new XElement("LightSource"); int elementIndex = 0;
SerializableProperty.SerializeProperties(lightSourceParams, lightElement); bool wasSaved = false;
element.Add(lightElement); foreach (XElement subElement in element.Elements().ToList())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "lightsource":
if (elementIndex == i)
{
SerializableProperty.SerializeProperties(LightSourceParams[i], subElement);
wasSaved = true;
break;
}
elementIndex++;
break;
}
}
if (!wasSaved)
{
var lightElement = new XElement("LightSource");
SerializableProperty.SerializeProperties(LightSourceParams[i], lightElement);
element.Add(lightElement);
}
} }
foreach (ChildObject childObj in ChildObjects) foreach (ChildObject childObj in ChildObjects)
@@ -162,7 +181,7 @@ namespace Barotrauma
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().Equals("overridecommonness", System.StringComparison.OrdinalIgnoreCase) if (subElement.Name.ToString().Equals("overridecommonness", System.StringComparison.OrdinalIgnoreCase)
&& subElement.GetAttributeString("leveltype", "") == overrideCommonness.Key) && subElement.GetAttributeString("leveltype", "").Equals(overrideCommonness.Key, System.StringComparison.OrdinalIgnoreCase))
{ {
subElement.Attribute("commonness").Value = overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture); subElement.Attribute("commonness").Value = overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture);
elementFound = true; elementFound = true;
@@ -3,10 +3,68 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Voronoi2; using Voronoi2;
namespace Barotrauma namespace Barotrauma
{ {
class LevelWallVertexBuffer : IDisposable
{
public VertexBuffer WallEdgeBuffer, WallBuffer;
public readonly Texture2D WallTexture, EdgeTexture;
private VertexPositionColorTexture[] wallVertices;
private VertexPositionColorTexture[] wallEdgeVertices;
public bool IsDisposed
{
get;
private set;
}
public LevelWallVertexBuffer(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
{
this.wallVertices = LevelRenderer.GetColoredVertices(wallVertices, color);
WallBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, wallVertices.Length, BufferUsage.WriteOnly);
WallBuffer.SetData(this.wallVertices);
WallTexture = wallTexture;
this.wallEdgeVertices = LevelRenderer.GetColoredVertices(wallEdgeVertices, color);
WallEdgeBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, wallEdgeVertices.Length, BufferUsage.WriteOnly);
WallEdgeBuffer.SetData(this.wallEdgeVertices);
EdgeTexture = edgeTexture;
}
public void Append(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Color color)
{
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);
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);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
IsDisposed = true;
WallEdgeBuffer?.Dispose();
WallBuffer?.Dispose();
}
}
class LevelRenderer : IDisposable class LevelRenderer : IDisposable
{ {
private static BasicEffect wallEdgeEffect, wallCenterEffect; private static BasicEffect wallEdgeEffect, wallCenterEffect;
@@ -15,11 +73,11 @@ namespace Barotrauma
private Vector2 defaultDustVelocity; private Vector2 defaultDustVelocity;
private Vector2 dustVelocity; private Vector2 dustVelocity;
private RasterizerState cullNone; private readonly RasterizerState cullNone;
private Level level; private readonly Level level;
private VertexBuffer wallVertices, bodyVertices; private readonly List<LevelWallVertexBuffer> vertexBuffers = new List<LevelWallVertexBuffer>();
public LevelRenderer(Level level) public LevelRenderer(Level level)
{ {
@@ -68,6 +126,7 @@ namespace Barotrauma
Vector2 currentDustVel = defaultDustVelocity; Vector2 currentDustVel = defaultDustVelocity;
foreach (LevelObject levelObject in level.LevelObjectManager.GetVisibleObjects()) foreach (LevelObject levelObject in level.LevelObjectManager.GetVisibleObjects())
{ {
if (levelObject.Triggers == null) { continue; }
//use the largest water flow velocity of all the triggers //use the largest water flow velocity of all the triggers
Vector2 objectMaxFlow = Vector2.Zero; Vector2 objectMaxFlow = Vector2.Zero;
foreach (LevelTrigger trigger in levelObject.Triggers) foreach (LevelTrigger trigger in levelObject.Triggers)
@@ -94,7 +153,6 @@ namespace Barotrauma
while (dustOffset.Y <= -waterTextureSize.Y) dustOffset.Y += waterTextureSize.Y; while (dustOffset.Y <= -waterTextureSize.Y) dustOffset.Y += waterTextureSize.Y;
while (dustOffset.Y >= waterTextureSize.Y) dustOffset.Y -= waterTextureSize.Y; while (dustOffset.Y >= waterTextureSize.Y) dustOffset.Y -= waterTextureSize.Y;
} }
} }
public static VertexPositionColorTexture[] GetColoredVertices(VertexPositionTexture[] vertices, Color color) public static VertexPositionColorTexture[] GetColoredVertices(VertexPositionTexture[] vertices, Color color)
@@ -107,38 +165,27 @@ namespace Barotrauma
return verts; return verts;
} }
public void SetWallVertices(VertexPositionTexture[] vertices, Color color) public void SetVertices(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
{ {
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); var existingBuffer = vertexBuffers.Find(vb => vb.WallTexture == wallTexture && vb.EdgeTexture == edgeTexture);
wallVertices.SetData(GetColoredVertices(vertices, color)); if (existingBuffer != null)
{
existingBuffer.Append(wallVertices, wallEdgeVertices,color);
}
else
{
vertexBuffers.Add(new LevelWallVertexBuffer(wallVertices, wallEdgeVertices, wallTexture, edgeTexture, color));
}
} }
public void SetBodyVertices(VertexPositionTexture[] vertices, Color color) public void DrawBackground(SpriteBatch spriteBatch, Camera cam,
{ LevelObjectManager backgroundSpriteManager = null,
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
bodyVertices.SetData(GetColoredVertices(vertices, color));
}
public void SetWallVertices(VertexPositionColorTexture[] vertices)
{
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length,BufferUsage.WriteOnly);
wallVertices.SetData(vertices);
}
public void SetBodyVertices(VertexPositionColorTexture[] vertices)
{
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
bodyVertices.SetData(vertices);
}
public void DrawBackground(SpriteBatch spriteBatch, Camera cam,
LevelObjectManager backgroundSpriteManager = null,
BackgroundCreatureManager backgroundCreatureManager = null) BackgroundCreatureManager backgroundCreatureManager = null)
{ {
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap);
Vector2 backgroundPos = cam.WorldViewCenter; Vector2 backgroundPos = cam.WorldViewCenter;
backgroundPos.Y = -backgroundPos.Y; backgroundPos.Y = -backgroundPos.Y;
backgroundPos *= 0.05f; backgroundPos *= 0.05f;
@@ -173,10 +220,13 @@ namespace Barotrauma
SamplerState.LinearWrap, DepthStencilState.DepthRead, null, null, SamplerState.LinearWrap, DepthStencilState.DepthRead, null, null,
cam.Transform); cam.Transform);
if (backgroundSpriteManager != null) backgroundSpriteManager.DrawObjects(spriteBatch, cam, drawFront: false); backgroundSpriteManager?.DrawObjects(spriteBatch, cam, drawFront: false);
if (backgroundCreatureManager != null) backgroundCreatureManager.Draw(spriteBatch, cam); if (cam.Zoom > 0.05f)
{
backgroundCreatureManager?.Draw(spriteBatch, cam);
}
if (level.GenerationParams.WaterParticles != null) if (level.GenerationParams.WaterParticles != null && cam.Zoom > 0.05f)
{ {
float textureScale = level.GenerationParams.WaterParticleScale; float textureScale = level.GenerationParams.WaterParticleScale;
@@ -216,7 +266,7 @@ namespace Barotrauma
spriteBatch.End(); spriteBatch.End();
RenderWalls(GameMain.Instance.GraphicsDevice, cam, specular: false); RenderWalls(GameMain.Instance.GraphicsDevice, cam);
spriteBatch.Begin(SpriteSortMode.Deferred, spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.NonPremultiplied, BlendState.NonPremultiplied,
@@ -243,7 +293,8 @@ namespace Barotrauma
foreach (GraphEdge edge in cell.Edges) foreach (GraphEdge edge in cell.Edges)
{ {
GUI.DrawLine(spriteBatch, new Vector2(edge.Point1.X + cell.Translation.X, -(edge.Point1.Y + cell.Translation.Y)), GUI.DrawLine(spriteBatch, new Vector2(edge.Point1.X + cell.Translation.X, -(edge.Point1.Y + cell.Translation.Y)),
new Vector2(edge.Point2.X + cell.Translation.X, -(edge.Point2.Y + cell.Translation.Y)), cell.Body == null ? Color.Cyan * 0.5f : Color.White); new Vector2(edge.Point2.X + cell.Translation.X, -(edge.Point2.Y + cell.Translation.Y)), edge.NextToCave ? Color.Red : (cell.Body == null ? Color.Cyan * 0.5f : (edge.IsSolid ? Color.White : Color.Gray)),
width: edge.NextToCave ? 8 :1);
} }
foreach (Vector2 point in cell.BodyVertices) foreach (Vector2 point in cell.BodyVertices)
@@ -252,7 +303,7 @@ namespace Barotrauma
} }
} }
foreach (List<Point> nodeList in level.SmallTunnels) /*foreach (List<Point> nodeList in level.SmallTunnels)
{ {
for (int i = 1; i < nodeList.Count; i++) for (int i = 1; i < nodeList.Count; i++)
{ {
@@ -261,7 +312,7 @@ namespace Barotrauma
new Vector2(nodeList[i].X, -nodeList[i].Y), new Vector2(nodeList[i].X, -nodeList[i].Y),
Color.Lerp(Color.Yellow, GUI.Style.Red, i / (float)nodeList.Count), 0, 10); Color.Lerp(Color.Yellow, GUI.Style.Red, i / (float)nodeList.Count), 0, 10);
} }
} }*/
foreach (var ruin in level.Ruins) foreach (var ruin in level.Ruins)
{ {
@@ -270,108 +321,142 @@ namespace Barotrauma
} }
Vector2 pos = new Vector2(0.0f, -level.Size.Y); Vector2 pos = new Vector2(0.0f, -level.Size.Y);
if (cam.WorldView.Y >= -pos.Y - 1024) if (cam.WorldView.Y >= -pos.Y - 1024)
{ {
pos.X = cam.WorldView.X -1024; int topBarrierWidth = level.GenerationParams.WallEdgeSprite.Texture.Width;
int width = (int)(Math.Ceiling(cam.WorldView.Width / 1024 + 4.0f) * 1024); int topBarrierHeight = level.GenerationParams.WallEdgeSprite.Texture.Height;
GUI.DrawRectangle(spriteBatch,new Rectangle( pos.X = cam.WorldView.X - topBarrierWidth;
(int)(MathUtils.Round(pos.X, 1024)), int width = (int)(Math.Ceiling(cam.WorldView.Width / 1024 + 4.0f) * topBarrierWidth);
-cam.WorldView.Y,
width, GUI.DrawRectangle(spriteBatch, new Rectangle(
(int)(cam.WorldView.Y + pos.Y) - 30), (int)MathUtils.Round(pos.X, topBarrierWidth),
-cam.WorldView.Y,
width,
(int)(cam.WorldView.Y + pos.Y) - 60),
Color.Black, true); Color.Black, true);
spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture, spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture,
new Rectangle((int)(MathUtils.Round(pos.X, 1024)), (int)pos.Y-1000, width, 1024), new Rectangle((int)MathUtils.Round(pos.X, topBarrierWidth), (int)(pos.Y - topBarrierHeight + level.GenerationParams.WallEdgeExpandOutwardsAmount), width, topBarrierHeight),
new Rectangle(0, 0, width, -1024), new Rectangle(0, 0, width, -topBarrierHeight),
level.BackgroundTextureColor, 0.0f, GameMain.LightManager?.LightingEnabled ?? false ? GameMain.LightManager.AmbientLight : level.WallColor, 0.0f,
Vector2.Zero, Vector2.Zero,
SpriteEffects.None, 0.0f); SpriteEffects.None, 0.0f);
} }
if (cam.WorldView.Y - cam.WorldView.Height < level.SeaFloorTopPos + 1024) if (cam.WorldView.Y - cam.WorldView.Height < level.SeaFloorTopPos + 1024)
{ {
pos = new Vector2(cam.WorldView.X - 1024, -level.BottomPos); int bottomBarrierWidth = level.GenerationParams.WallEdgeSprite.Texture.Width;
int bottomBarrierHeight = level.GenerationParams.WallEdgeSprite.Texture.Height;
int width = (int)(Math.Ceiling(cam.WorldView.Width / 1024 + 4.0f) * 1024); pos = new Vector2(cam.WorldView.X - bottomBarrierWidth, -level.BottomPos);
int width = (int)(Math.Ceiling(cam.WorldView.Width / bottomBarrierWidth + 4.0f) * bottomBarrierWidth);
GUI.DrawRectangle(spriteBatch, new Rectangle( GUI.DrawRectangle(spriteBatch, new Rectangle(
(int)(MathUtils.Round(pos.X, 1024)), (int)(MathUtils.Round(pos.X, bottomBarrierWidth)),
(int)-(level.BottomPos - 30), -(level.BottomPos - 60),
width, width,
(int)(level.BottomPos - (cam.WorldView.Y - cam.WorldView.Height))), level.BottomPos - (cam.WorldView.Y - cam.WorldView.Height)),
Color.Black, true); Color.Black, true);
spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture, spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture,
new Rectangle((int)(MathUtils.Round(pos.X, 1024)), (int)-level.BottomPos, width, 1024), new Rectangle((int)MathUtils.Round(pos.X, bottomBarrierWidth), -level.BottomPos - (int)level.GenerationParams.WallEdgeExpandOutwardsAmount, width, bottomBarrierHeight),
new Rectangle(0, 0, width, -1024), new Rectangle(0, 0, width, -bottomBarrierHeight),
level.BackgroundTextureColor, 0.0f, GameMain.LightManager?.LightingEnabled ?? false ? GameMain.LightManager.AmbientLight : level.WallColor, 0.0f,
Vector2.Zero, Vector2.Zero,
SpriteEffects.FlipVertically, 0.0f); SpriteEffects.FlipVertically, 0.0f);
} }
} }
public void RenderWalls(GraphicsDevice graphicsDevice, Camera cam, bool specular) public void RenderWalls(GraphicsDevice graphicsDevice, Camera cam)
{ {
if (wallVertices == null) return; if (!vertexBuffers.Any()) { return; }
bool renderLevel = cam.WorldView.Y >= 0.0f; var defaultRasterizerState = graphicsDevice.RasterizerState;
bool renderSeaFloor = cam.WorldView.Y - cam.WorldView.Height < level.SeaFloorTopPos + 1024;
if (!renderLevel && !renderSeaFloor) return;
Matrix transformMatrix = cam.ShaderTransform Matrix transformMatrix = cam.ShaderTransform
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 100) * 0.5f; * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 100) * 0.5f;
wallEdgeEffect.Texture = specular && level.GenerationParams.WallEdgeSpriteSpecular != null ?
level.GenerationParams.WallEdgeSpriteSpecular.Texture :
level.GenerationParams.WallEdgeSprite.Texture;
wallEdgeEffect.World = transformMatrix;
wallCenterEffect.Texture = specular && level.GenerationParams.WallSpriteSpecular != null ?
level.GenerationParams.WallSpriteSpecular.Texture :
level.GenerationParams.WallSprite.Texture;
wallCenterEffect.World = transformMatrix;
graphicsDevice.SamplerStates[0] = SamplerState.LinearWrap; graphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
if (renderLevel)
{
graphicsDevice.SetVertexBuffer(bodyVertices);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(bodyVertices.VertexCount / 3.0f));
}
foreach (LevelWall wall in level.ExtraWalls)
{
if (!renderSeaFloor && wall == level.SeaFloor) continue;
wallCenterEffect.World = wall.GetTransform() * transformMatrix;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.BodyVertices);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.BodyVertices.VertexCount / 3.0f));
}
var defaultRasterizerState = graphicsDevice.RasterizerState;
graphicsDevice.RasterizerState = cullNone; graphicsDevice.RasterizerState = cullNone;
wallEdgeEffect.World = transformMatrix;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
if (renderLevel) //render destructible walls
for (int i = 0; i < 2; i++)
{ {
wallEdgeEffect.CurrentTechnique.Passes[0].Apply(); var wallList = i == 0 ? level.ExtraWalls : level.UnsyncedExtraWalls;
graphicsDevice.SetVertexBuffer(wallVertices); foreach (LevelWall wall in wallList)
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wallVertices.VertexCount / 3.0f)); {
if (!(wall is DestructibleLevelWall destructibleWall) || destructibleWall.Destroyed) { continue; }
wallCenterEffect.Texture = level.GenerationParams.DestructibleWallSprite?.Texture ?? level.GenerationParams.WallSprite.Texture;
wallCenterEffect.World = wall.GetTransform() * transformMatrix;
wallCenterEffect.Alpha = wall.Alpha;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallBuffer.VertexCount / 3.0f));
if (destructibleWall.Damage > 0.0f)
{
wallCenterEffect.Texture = level.GenerationParams.WallSpriteDestroyed.Texture;
wallCenterEffect.Alpha = MathHelper.Lerp(0.2f, 1.0f, destructibleWall.Damage / destructibleWall.MaxHealth) * wall.Alpha;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallEdgeBuffer.VertexCount / 3.0f));
}
wallEdgeEffect.Texture = level.GenerationParams.DestructibleWallEdgeSprite?.Texture ?? level.GenerationParams.WallEdgeSprite.Texture;
wallEdgeEffect.World = wall.GetTransform() * transformMatrix;
wallEdgeEffect.Alpha = wall.Alpha;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallEdgeBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallEdgeBuffer.VertexCount / 3.0f));
}
} }
foreach (LevelWall wall in level.ExtraWalls)
wallEdgeEffect.Alpha = 1.0f;
wallCenterEffect.Alpha = 1.0f;
wallCenterEffect.World = transformMatrix;
wallEdgeEffect.World = transformMatrix;
//render static walls
foreach (var vertexBuffer in vertexBuffers)
{ {
if (!renderSeaFloor && wall == level.SeaFloor) continue; wallCenterEffect.Texture = vertexBuffer.WallTexture;
wallEdgeEffect.World = wall.GetTransform() * transformMatrix; wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(vertexBuffer.WallBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(vertexBuffer.WallBuffer.VertexCount / 3.0f));
wallEdgeEffect.Texture = vertexBuffer.EdgeTexture;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply(); wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallVertices); graphicsDevice.SetVertexBuffer(vertexBuffer.WallEdgeBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallVertices.VertexCount / 3.0f)); graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(vertexBuffer.WallEdgeBuffer.VertexCount / 3.0f));
} }
wallCenterEffect.Texture = level.GenerationParams.WallSprite.Texture;
wallEdgeEffect.Texture = level.GenerationParams.WallEdgeSprite.Texture;
//render non-destructible extra walls
for (int i = 0; i < 2; i++)
{
var wallList = i == 0 ? level.ExtraWalls : level.UnsyncedExtraWalls;
foreach (LevelWall wall in wallList)
{
if (wall is DestructibleLevelWall) { continue; }
//TODO: use LevelWallVertexBuffers for extra walls as well
wallCenterEffect.World = wall.GetTransform() * transformMatrix;
wallCenterEffect.Alpha = wall.Alpha;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallBuffer.VertexCount / 3.0f));
wallEdgeEffect.World = wall.GetTransform() * transformMatrix;
wallEdgeEffect.Alpha = wall.Alpha;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallEdgeBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallEdgeBuffer.VertexCount / 3.0f));
}
}
graphicsDevice.RasterizerState = defaultRasterizerState; graphicsDevice.RasterizerState = defaultRasterizerState;
} }
@@ -383,8 +468,11 @@ namespace Barotrauma
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (wallVertices != null) wallVertices.Dispose(); foreach (var vertexBuffer in vertexBuffers)
if (bodyVertices != null) bodyVertices.Dispose(); {
vertexBuffer.Dispose();
}
vertexBuffers.Clear();
} }
} }
} }
@@ -2,55 +2,44 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
{ {
partial class LevelWall : IDisposable partial class LevelWall : IDisposable
{ {
private VertexBuffer wallVertices, bodyVertices; public LevelWallVertexBuffer VertexBuffer { get; private set; }
public VertexBuffer WallVertices public VertexBuffer WallBuffer { get { return VertexBuffer.WallBuffer; } }
{
get { return wallVertices; }
}
public VertexBuffer BodyVertices public VertexBuffer WallEdgeBuffer { get { return VertexBuffer.WallEdgeBuffer; } }
{
get { return bodyVertices; } public virtual float Alpha => 1.0f;
}
public Matrix GetTransform() public Matrix GetTransform()
{ {
return body.BodyType == BodyType.Static ? return Body.FixedRotation ?
Matrix.Identity : Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(Body.Position), 0.0f)) :
Matrix.CreateRotationZ(body.Rotation) * Matrix.CreateRotationZ(Body.Rotation) *
Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(body.Position), 0.0f)); Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(Body.Position), 0.0f));
} }
public void SetWallVertices(VertexPositionTexture[] vertices, Color color) public void SetWallVertices(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
{ {
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); if (VertexBuffer != null && !VertexBuffer.IsDisposed) { VertexBuffer.Dispose(); }
wallVertices.SetData(LevelRenderer.GetColoredVertices(vertices, color)); VertexBuffer = new LevelWallVertexBuffer(wallVertices, wallEdgeVertices, wallTexture, edgeTexture, color);
} }
public void SetBodyVertices(VertexPositionTexture[] vertices, Color color) public void GenerateVertices()
{ {
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); float zCoord = this is DestructibleLevelWall ? Rand.Range(0.9f, 1.0f) : 0.9f;
bodyVertices.SetData(LevelRenderer.GetColoredVertices(vertices, color)); List<VertexPositionTexture> wallVertices = CaveGenerator.GenerateWallVertices(triangles, level.GenerationParams, zCoord);
} SetWallVertices(
wallVertices.ToArray(),
public void SetWallVertices(VertexPositionColorTexture[] vertices) CaveGenerator.GenerateWallEdgeVertices(Cells, level, zCoord).ToArray(),
{ level.GenerationParams.WallSprite.Texture,
if (wallVertices != null && !wallVertices.IsDisposed) wallVertices.Dispose(); level.GenerationParams.WallEdgeSprite.Texture,
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); color);
wallVertices.SetData(vertices);
}
public void SetBodyVertices(VertexPositionColorTexture[] vertices)
{
if (bodyVertices != null && !bodyVertices.IsDisposed) bodyVertices.Dispose();
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
bodyVertices.SetData(vertices);
} }
} }
} }
@@ -132,7 +132,7 @@ namespace Barotrauma.Lights
public void AddLight(LightSource light) public void AddLight(LightSource light)
{ {
if (!lights.Contains(light)) lights.Add(light); if (!lights.Contains(light)) { lights.Add(light); }
} }
public void RemoveLight(LightSource light) public void RemoveLight(LightSource light)
@@ -151,7 +151,16 @@ namespace Barotrauma.Lights
private readonly List<LightSource> activeLights = new List<LightSource>(capacity: 100); private readonly List<LightSource> activeLights = new List<LightSource>(capacity: 100);
public void UpdateLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null) public void Update(float deltaTime)
{
foreach (LightSource light in activeLights)
{
if (!light.Enabled) { continue; }
light.Update(deltaTime);
}
}
public void RenderLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null)
{ {
if (!LightingEnabled) { return; } if (!LightingEnabled) { return; }
@@ -203,9 +212,9 @@ namespace Barotrauma.Lights
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
if (light.IsBackground) { continue; } if (light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
//draw limb lights at this point, because they were skipped over previously to prevent them from being obstructed //draw limb lights at this point, because they were skipped over previously to prevent them from being obstructed
if (light.ParentBody?.UserData is Limb) { light.DrawSprite(spriteBatch, cam); } if (light.ParentBody?.UserData is Limb limb && !limb.Hide) { light.DrawSprite(spriteBatch, cam); }
} }
spriteBatch.End(); spriteBatch.End();
@@ -215,9 +224,10 @@ namespace Barotrauma.Lights
graphics.Clear(AmbientLight); graphics.Clear(AmbientLight);
graphics.BlendState = BlendState.Additive; graphics.BlendState = BlendState.Additive;
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
Level.Loaded?.BackgroundCreatureManager?.DrawLights(spriteBatch, cam);
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
if (!light.IsBackground) { continue; } if (!light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawSprite(spriteBatch, cam); light.DrawSprite(spriteBatch, cam);
light.DrawLightVolume(spriteBatch, lightEffect, transform); light.DrawLightVolume(spriteBatch, lightEffect, transform);
} }
@@ -262,7 +272,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
//don't draw limb lights at this point, they need to be drawn after lights have been obstructed by characters //don't draw limb lights at this point, they need to be drawn after lights have been obstructed by characters
if (light.IsBackground || light.ParentBody?.UserData is Limb) { continue; } if (light.IsBackground || light.ParentBody?.UserData is Limb || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawSprite(spriteBatch, cam); light.DrawSprite(spriteBatch, cam);
} }
spriteBatch.End(); spriteBatch.End();
@@ -327,7 +337,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
if (light.IsBackground) { continue; } if (light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawLightVolume(spriteBatch, lightEffect, transform); light.DrawLightVolume(spriteBatch, lightEffect, transform);
} }
@@ -7,7 +7,6 @@ using System.Linq;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
namespace Barotrauma.Lights namespace Barotrauma.Lights
{ {
class LightSourceParams : ISerializableEntity class LightSourceParams : ISerializableEntity
@@ -33,7 +32,7 @@ namespace Barotrauma.Lights
get { return range; } get { return range; }
set set
{ {
range = MathHelper.Clamp(value, 0.0f, 2048.0f); range = MathHelper.Clamp(value, 0.0f, 4096.0f);
TextureRange = range; TextureRange = range;
if (OverrideLightTexture != null) if (OverrideLightTexture != null)
{ {
@@ -49,7 +48,63 @@ namespace Barotrauma.Lights
[Serialize("0, 0", true), Editable(ValueStep = 1, DecimalCount = 1, MinValueFloat = -1000f, MaxValueFloat = 1000f)] [Serialize("0, 0", true), Editable(ValueStep = 1, DecimalCount = 1, MinValueFloat = -1000f, MaxValueFloat = 1000f)]
public Vector2 Offset { get; set; } public Vector2 Offset { get; set; }
[Serialize(0f, true), Editable(MinValueFloat = -360, MaxValueFloat = 360, ValueStep = 1, DecimalCount = 0)]
public float Rotation { get; set; }
public Vector2 GetOffset() => Vector2.Transform(Offset, Matrix.CreateRotationZ(Rotation));
private float flicker;
[Editable, Serialize(0.0f, false, description: "How heavily the light flickers. 0 = no flickering, 1 = the light will alternate between completely dark and full brightness.")]
public float Flicker
{
get { return flicker; }
set
{
flicker = MathHelper.Clamp(value, 0.0f, 1.0f);
}
}
[Editable, Serialize(1.0f, false, description: "How fast the light flickers.")]
public float FlickerSpeed
{
get;
set;
}
private float pulseFrequency;
[Editable, Serialize(0.0f, true, description: "How rapidly the light pulsates (in Hz). 0 = no blinking.")]
public float PulseFrequency
{
get { return pulseFrequency; }
set
{
pulseFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
}
}
private float pulseAmount;
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.0f, true, description: "How much light pulsates (in Hz). 0 = not at all, 1 = alternates between full brightness and off.")]
public float PulseAmount
{
get { return pulseAmount; }
set
{
pulseAmount = MathHelper.Clamp(value, 0.0f, 1.0f);
}
}
private float blinkFrequency;
[Editable, Serialize(0.0f, true, description: "How rapidly the light blinks on and off (in Hz). 0 = no blinking.")]
public float BlinkFrequency
{
get { return blinkFrequency; }
set
{
blinkFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
}
}
public float TextureRange public float TextureRange
{ {
get; get;
@@ -88,6 +143,7 @@ namespace Barotrauma.Lights
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
case "sprite": case "sprite":
case "lightsprite":
{ {
LightSprite = new Sprite(subElement); LightSprite = new Sprite(subElement);
float spriteAlpha = subElement.GetAttributeFloat("alpha", -1.0f); float spriteAlpha = subElement.GetAttributeFloat("alpha", -1.0f);
@@ -144,6 +200,8 @@ namespace Barotrauma.Lights
private static Texture2D lightTexture; private static Texture2D lightTexture;
private float blinkTimer, flickerState, pulseState;
private VertexPositionColorTexture[] vertices; private VertexPositionColorTexture[] vertices;
private short[] indices; private short[] indices;
@@ -296,6 +354,12 @@ namespace Barotrauma.Lights
get { return lightSourceParams.Color; } get { return lightSourceParams.Color; }
set { lightSourceParams.Color = value; } set { lightSourceParams.Color = value; }
} }
public float CurrentBrightness
{
get;
private set;
}
public float Range public float Range
{ {
@@ -402,7 +466,33 @@ namespace Barotrauma.Lights
texture = LightTexture; texture = LightTexture;
diffToSub = new Dictionary<Submarine, Vector2>(); diffToSub = new Dictionary<Submarine, Vector2>();
if (addLight) { GameMain.LightManager.AddLight(this); } if (addLight) { GameMain.LightManager.AddLight(this); }
}
public void Update(float deltaTime)
{
float brightness = 1.0f;
if (lightSourceParams.BlinkFrequency > 0.0f)
{
blinkTimer = (blinkTimer + deltaTime * lightSourceParams.BlinkFrequency) % 1.0f;
if (blinkTimer > 0.5f)
{
CurrentBrightness = 0.0f;
return;
}
}
if (lightSourceParams.PulseFrequency > 0.0f && lightSourceParams.PulseAmount > 0.0f)
{
pulseState = (pulseState + deltaTime * lightSourceParams.PulseFrequency) % 1.0f;
//oscillate between 0-1
brightness *= 1.0f - (float)(Math.Sin(pulseState * MathHelper.TwoPi) + 1.0f) / 2.0f * lightSourceParams.PulseAmount;
}
if (lightSourceParams.Flicker > 0.0f)
{
flickerState += deltaTime * lightSourceParams.FlickerSpeed;
flickerState %= 255;
brightness *= 1.0f - PerlinNoise.GetPerlin(flickerState, flickerState * 0.5f) * lightSourceParams.Flicker;
}
CurrentBrightness = brightness;
} }
/// <summary> /// <summary>
@@ -508,13 +598,12 @@ namespace Barotrauma.Lights
//recalculate vertices if the subs have moved > 5 px relative to each other //recalculate vertices if the subs have moved > 5 px relative to each other
Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition; Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition;
Vector2 prevDiff; if (!diffToSub.TryGetValue(sub, out Vector2 prevDiff))
if (!diffToSub.TryGetValue(sub, out prevDiff))
{ {
diffToSub.Add(sub, diff); diffToSub.Add(sub, diff);
NeedsRecalculation = true; NeedsRecalculation = true;
} }
else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f*5.0f) else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f * 5.0f)
{ {
diffToSub[sub] = diff; diffToSub[sub] = diff;
NeedsRecalculation = true; NeedsRecalculation = true;
@@ -884,7 +973,12 @@ namespace Barotrauma.Lights
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height); overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTextureOrigin; Vector2 origin = OverrideLightTextureOrigin;
if (LightSpriteEffect == SpriteEffects.FlipHorizontally) { origin.X = OverrideLightTexture.SourceRect.Width - origin.X; } if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
{
origin.X = OverrideLightTexture.SourceRect.Width - origin.X;
cosAngle = -cosAngle;
sinAngle = -sinAngle;
}
if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; } if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; }
uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f); uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
} }
@@ -1021,7 +1115,7 @@ namespace Barotrauma.Lights
lightVolumeBuffer.SetData<VertexPositionColorTexture>(vertices, 0, vertexCount); lightVolumeBuffer.SetData<VertexPositionColorTexture>(vertices, 0, vertexCount);
lightVolumeIndexBuffer.SetData<short>(indices, 0, indexCount); lightVolumeIndexBuffer.SetData<short>(indices, 0, indexCount);
Vector2 GetUV(Vector2 vert, SpriteEffects effects) static Vector2 GetUV(Vector2 vert, SpriteEffects effects)
{ {
if (effects == SpriteEffects.FlipHorizontally) if (effects == SpriteEffects.FlipHorizontally)
{ {
@@ -1098,7 +1192,7 @@ namespace Barotrauma.Lights
if (DeformableLightSprite != null) if (DeformableLightSprite != null)
{ {
Vector2 origin = DeformableLightSprite.Origin + LightSourceParams.Offset; Vector2 origin = DeformableLightSprite.Origin + LightSourceParams.GetOffset();
Vector2 drawPos = position; Vector2 drawPos = position;
if (ParentSub != null) if (ParentSub != null)
{ {
@@ -1115,14 +1209,14 @@ namespace Barotrauma.Lights
DeformableLightSprite.Draw( DeformableLightSprite.Draw(
cam, new Vector3(drawPos, 0.0f), cam, new Vector3(drawPos, 0.0f),
origin, -Rotation, SpriteScale, origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale,
new Color(Color, lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f), new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness),
LightSpriteEffect == SpriteEffects.FlipVertically); LightSpriteEffect == SpriteEffects.FlipVertically);
} }
if (LightSprite != null) if (LightSprite != null)
{ {
Vector2 origin = LightSprite.Origin + LightSourceParams.Offset; Vector2 origin = LightSprite.Origin + LightSourceParams.GetOffset();
if ((LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) if ((LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally)
{ {
origin.X = LightSprite.SourceRect.Width - origin.X; origin.X = LightSprite.SourceRect.Width - origin.X;
@@ -1140,9 +1234,9 @@ namespace Barotrauma.Lights
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
LightSprite.Draw( LightSprite.Draw(
spriteBatch, drawPos, spriteBatch, drawPos,
new Color(Color, lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f), new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness),
origin, -Rotation, SpriteScale, LightSpriteEffect); origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale, LightSpriteEffect);
} }
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f) if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
@@ -1185,7 +1279,7 @@ namespace Barotrauma.Lights
public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform) public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform)
{ {
if (Range < 1.0f || Color.A < 1) { return; } if (Range < 1.0f || Color.A < 1 || CurrentBrightness <= 0.0f) { return; }
if (CastShadows) if (CastShadows)
{ {
@@ -1207,7 +1301,7 @@ namespace Barotrauma.Lights
if (ParentSub != null) drawPos += ParentSub.DrawPosition; if (ParentSub != null) drawPos += ParentSub.DrawPosition;
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
spriteBatch.Draw(currentTexture, drawPos, null, Color, -rotation, center, scale, SpriteEffects.None, 1); spriteBatch.Draw(currentTexture, drawPos, null, Color.Multiply(CurrentBrightness), -rotation, center, scale, SpriteEffects.None, 1);
return; return;
} }
@@ -1229,7 +1323,7 @@ namespace Barotrauma.Lights
if (vertexCount == 0) { return; } if (vertexCount == 0) { return; }
lightEffect.DiffuseColor = (new Vector3(Color.R, Color.G, Color.B) * (Color.A / 255.0f)) / 255.0f; lightEffect.DiffuseColor = (new Vector3(Color.R, Color.G, Color.B) * (Color.A / 255.0f * CurrentBrightness)) / 255.0f;
if (OverrideLightTexture != null) if (OverrideLightTexture != null)
{ {
lightEffect.Texture = OverrideLightTexture.Texture; lightEffect.Texture = OverrideLightTexture.Texture;
@@ -63,6 +63,8 @@ namespace Barotrauma
private Sprite[,] mapTiles; private Sprite[,] mapTiles;
private bool[,] tileDiscovered; private bool[,] tileDiscovered;
private Pair<Rectangle, string> connectionTooltip;
#if DEBUG #if DEBUG
private GUIComponent editor; private GUIComponent editor;
@@ -410,6 +412,8 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, GUICustomComponent mapContainer) public void Draw(SpriteBatch spriteBatch, GUICustomComponent mapContainer)
{ {
connectionTooltip = null;
Rectangle rect = mapContainer.Rect; Rectangle rect = mapContainer.Rect;
Vector2 viewSize = new Vector2(rect.Width / zoom, rect.Height / zoom); Vector2 viewSize = new Vector2(rect.Width / zoom, rect.Height / zoom);
@@ -639,6 +643,10 @@ namespace Barotrauma
{ {
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First); GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
} }
if (connectionTooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, connectionTooltip.Second, connectionTooltip.First);
}
spriteBatch.End(); spriteBatch.End();
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect; GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
@@ -689,12 +697,16 @@ namespace Barotrauma
int startIndex = connection.CrackSegments.Count > 2 ? 1 : 0; int startIndex = connection.CrackSegments.Count > 2 ? 1 : 0;
int endIndex = connection.CrackSegments.Count > 2 ? connection.CrackSegments.Count - 1 : connection.CrackSegments.Count; int endIndex = connection.CrackSegments.Count > 2 ? connection.CrackSegments.Count - 1 : connection.CrackSegments.Count;
Vector2? connectionStart = null;
Vector2? connectionEnd = null;
for (int i = startIndex; i < endIndex; i++) for (int i = startIndex; i < endIndex; i++)
{ {
var segment = connection.CrackSegments[i]; var segment = connection.CrackSegments[i];
Vector2 start = rectCenter + (segment[0] + viewOffset) * zoom; Vector2 start = rectCenter + (segment[0] + viewOffset) * zoom;
Vector2 end = rectCenter + (segment[1] + viewOffset) * zoom; if (!connectionStart.HasValue) { connectionStart = start; }
Vector2 end = rectCenter + (segment[1] + viewOffset) * zoom;
connectionEnd = end;
if (!viewArea.Contains(start) && !viewArea.Contains(end)) if (!viewArea.Contains(start) && !viewArea.Contains(end))
{ {
@@ -734,6 +746,59 @@ namespace Barotrauma
connectionSprite.SourceRect, connectionColor * a, MathUtils.VectorToAngle(end - start), connectionSprite.SourceRect, connectionColor * a, MathUtils.VectorToAngle(end - start),
new Vector2(0, connectionSprite.size.Y / 2), SpriteEffects.None, 0.01f); new Vector2(0, connectionSprite.size.Y / 2), SpriteEffects.None, 0.01f);
} }
if (connectionStart.HasValue && connectionEnd.HasValue)
{
GUIComponentStyle crushDepthWarningIconStyle = null;
string tooltip = null;
var subCrushDepth = Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth;
if (GameMain.GameSession?.Campaign?.UpgradeManager != null)
{
var hullUpgradePrefab = UpgradePrefab.Find("increasewallhealth");
if (hullUpgradePrefab != null)
{
int pendingLevel = GameMain.GameSession.Campaign.UpgradeManager.GetUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First());
int currentLevel = GameMain.GameSession.Campaign.UpgradeManager.GetRealUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First());
if (pendingLevel > currentLevel)
{
string updateValueStr = hullUpgradePrefab.SourceElement?.Element("Structure")?.GetAttributeString("crushdepth", null);
if (!string.IsNullOrEmpty(updateValueStr))
{
subCrushDepth = PropertyReference.CalculateUpgrade(subCrushDepth, pendingLevel - currentLevel, updateValueStr);
}
}
}
}
if (connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio > subCrushDepth)
{
crushDepthWarningIconStyle = GUI.Style.GetComponentStyle("CrushDepthWarningHighIcon");
tooltip = "crushdepthwarninghigh";
}
else if ((connection.LevelData.InitialDepth + connection.LevelData.Size.Y) * Physics.DisplayToRealWorldRatio > subCrushDepth)
{
crushDepthWarningIconStyle = GUI.Style.GetComponentStyle("CrushDepthWarningLowIcon");
tooltip = "crushdepthwarninglow";
}
if (crushDepthWarningIconStyle != null)
{
Vector2 iconPos = (connectionStart.Value + connectionEnd.Value) / 2;
float iconSize = 32.0f * GUI.Scale;
bool mouseOn = HighlightedLocation == null && Vector2.DistanceSquared(iconPos, PlayerInput.MousePosition) < iconSize * iconSize;
Sprite crushDepthWarningIcon = crushDepthWarningIconStyle.GetDefaultSprite();
crushDepthWarningIcon.Draw(spriteBatch, iconPos,
mouseOn ? crushDepthWarningIconStyle.HoverColor : crushDepthWarningIconStyle.Color,
scale: iconSize / crushDepthWarningIcon.size.X);
if (mouseOn)
{
connectionTooltip = new Pair<Rectangle, string>(
new Rectangle(iconPos.ToPoint(), new Point((int)iconSize)),
TextManager.Get(tooltip)
.Replace("[initialdepth]", ((int)(connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio)).ToString())
.Replace("[submarinecrushdepth]", ((int)subCrushDepth).ToString()));
}
}
}
if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames) if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames)
{ {
@@ -114,6 +114,18 @@ namespace Barotrauma
public MapEntity ReplacedBy; public MapEntity ReplacedBy;
public virtual void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { } public virtual void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { }
/// <summary>
/// A method that modifies the draw depth to prevent z-fighting between entities with the same sprite depth
/// </summary>
public float GetDrawDepth(float baseDepth, Sprite sprite)
{
float depth = baseDepth
//take texture into account to get entities with (roughly) the same base depth and texture to render consecutively to minimize texture swaps
+ (sprite?.Texture?.SortingKey ?? 0) % 100 * 0.00001f
+ ID % 100 * 0.000001f;
return Math.Min(depth, 1.0f);
}
/// <summary> /// <summary>
/// Update the selection logic in submarine editor /// Update the selection logic in submarine editor
@@ -329,8 +341,8 @@ namespace Barotrauma
{ {
var clones = Clone(selectedList).Where(c => c != null).ToList(); var clones = Clone(selectedList).Where(c => c != null).ToList();
selectedList = clones; selectedList = clones;
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
selectedList.ForEach(c => c.Move(moveAmount)); selectedList.ForEach(c => c.Move(moveAmount));
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
} }
else // move else // move
{ {
@@ -897,10 +909,10 @@ namespace Barotrauma
{ {
if (entities.Count == 0) { return; } if (entities.Count == 0) { return; }
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
CopyEntities(entities); CopyEntities(entities);
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
entities.ForEach(e => { if (!e.Removed) { e.Remove(); } }); entities.ForEach(e => { if (!e.Removed) { e.Remove(); } });
entities.Clear(); entities.Clear();
} }
@@ -913,7 +925,6 @@ namespace Barotrauma
Clone(copiedList); Clone(copiedList);
var clones = mapEntityList.Except(prevEntities).ToList(); var clones = mapEntityList.Except(prevEntities).ToList();
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
var nonWireClones = clones.Where(c => !(c is Item item) || item.GetComponent<Wire>() == null); var nonWireClones = clones.Where(c => !(c is Item item) || item.GetComponent<Wire>() == null);
if (!nonWireClones.Any()) { nonWireClones = clones; } if (!nonWireClones.Any()) { nonWireClones = clones; }
@@ -929,6 +940,8 @@ namespace Barotrauma
clone.Move(moveAmount); clone.Move(moveAmount);
clone.Submarine = Submarine.MainSub; clone.Submarine = Submarine.MainSub;
} }
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false, handleInventoryBehavior: false));
} }
/// <summary> /// <summary>
@@ -222,9 +222,7 @@ namespace Barotrauma
public float GetDrawDepth() public float GetDrawDepth()
{ {
float depth = SpriteDepthOverrideIsSet ? SpriteOverrideDepth : prefab.sprite.Depth; return GetDrawDepth(SpriteDepthOverrideIsSet ? SpriteOverrideDepth : prefab.sprite.Depth, prefab.sprite);
depth -= (ID % 255) * 0.000001f;
return depth;
} }
private void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Effect damageEffect = null) private void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Effect damageEffect = null)
@@ -238,6 +236,7 @@ namespace Barotrauma
else if (HiddenInGame) { return; } else if (HiddenInGame) { return; }
Color color = IsHighlighted ? GUI.Style.Orange : spriteColor; Color color = IsHighlighted ? GUI.Style.Orange : spriteColor;
if (IsSelected && editing) if (IsSelected && editing)
{ {
//color = Color.Lerp(color, Color.Gold, 0.5f); //color = Color.Lerp(color, Color.Gold, 0.5f);
@@ -253,15 +252,19 @@ namespace Barotrauma
thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom))); thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
} }
bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode();
if (isWiringMode) { color *= 0.15f; }
Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition; Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
float depth = GetDrawDepth(); float depth = GetDrawDepth();
Vector2 textureOffset = this.textureOffset; Vector2 textureOffset = this.textureOffset;
if (FlippedX) textureOffset.X = -textureOffset.X; if (FlippedX) { textureOffset.X = -textureOffset.X; }
if (FlippedY) textureOffset.Y = -textureOffset.Y; if (FlippedY) { textureOffset.Y = -textureOffset.Y; }
if (back && damageEffect == null) if (back && damageEffect == null && !isWiringMode)
{ {
if (Prefab.BackgroundSprite != null) if (Prefab.BackgroundSprite != null)
{ {
@@ -299,7 +302,7 @@ namespace Barotrauma
color: Prefab.BackgroundSpriteColor, color: Prefab.BackgroundSpriteColor,
textureScale: TextureScale * Scale, textureScale: TextureScale * Scale,
startOffset: backGroundOffset, startOffset: backGroundOffset,
depth: Math.Max(Prefab.BackgroundSprite.Depth + (ID % 255) * 0.000001f, depth + 0.000001f)); depth: Math.Max(GetDrawDepth(Prefab.BackgroundSprite.Depth, Prefab.BackgroundSprite), depth + 0.000001f));
if (UseDropShadow) if (UseDropShadow)
{ {
@@ -324,6 +327,7 @@ namespace Barotrauma
for (int i = 0; i < Sections.Length; i++) for (int i = 0; i < Sections.Length; i++)
{ {
Rectangle drawSection = Sections[i].rect;
if (damageEffect != null) if (damageEffect != null)
{ {
float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / MaxHealth); float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / MaxHealth);
@@ -340,21 +344,30 @@ namespace Barotrauma
Submarine.DamageEffectColor = color; Submarine.DamageEffectColor = color;
} }
} }
if (!HasDamage && i == 0)
{
drawSection = new Rectangle(
drawSection.X,
drawSection.Y,
Sections[Sections.Length -1 ].rect.Right - drawSection.X,
drawSection.Y - (Sections[Sections.Length - 1].rect.Y - Sections[Sections.Length - 1].rect.Height));
i = Sections.Length;
}
Vector2 sectionOffset = new Vector2( Vector2 sectionOffset = new Vector2(
Math.Abs(rect.Location.X - Sections[i].rect.Location.X), Math.Abs(rect.Location.X - drawSection.Location.X),
Math.Abs(rect.Location.Y - Sections[i].rect.Location.Y)); Math.Abs(rect.Location.Y - drawSection.Location.Y));
if (FlippedX && IsHorizontal) sectionOffset.X = Sections[i].rect.Right - rect.Right; if (FlippedX && IsHorizontal) { sectionOffset.X = drawSection.Right - rect.Right; }
if (FlippedY && !IsHorizontal) sectionOffset.Y = (rect.Y - rect.Height) - (Sections[i].rect.Y - Sections[i].rect.Height); if (FlippedY && !IsHorizontal) { sectionOffset.Y = (rect.Y - rect.Height) - (drawSection.Y - drawSection.Height); }
sectionOffset.X += MathUtils.PositiveModulo((int)-textureOffset.X, prefab.sprite.SourceRect.Width); sectionOffset.X += MathUtils.PositiveModulo((int)-textureOffset.X, prefab.sprite.SourceRect.Width);
sectionOffset.Y += MathUtils.PositiveModulo((int)-textureOffset.Y, prefab.sprite.SourceRect.Height); sectionOffset.Y += MathUtils.PositiveModulo((int)-textureOffset.Y, prefab.sprite.SourceRect.Height);
prefab.sprite.DrawTiled( prefab.sprite.DrawTiled(
spriteBatch, spriteBatch,
new Vector2(Sections[i].rect.X + drawOffset.X, -(Sections[i].rect.Y + drawOffset.Y)), new Vector2(drawSection.X + drawOffset.X, -(drawSection.Y + drawOffset.Y)),
new Vector2(Sections[i].rect.Width, Sections[i].rect.Height), new Vector2(drawSection.Width, drawSection.Height),
color: color, color: color,
startOffset: sectionOffset, startOffset: sectionOffset,
depth: depth, depth: depth,
@@ -394,12 +394,10 @@ namespace Barotrauma
float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY); float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY);
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
horizontalLine,
new Vector2(topLeft.X - expandX, -bottomRight.Y + i * GridSize.Y), new Vector2(topLeft.X - expandX, -bottomRight.Y + i * GridSize.Y),
new Vector2(bottomRight.X + expandX, -bottomRight.Y + i * GridSize.Y), new Vector2(bottomRight.X + expandX, -bottomRight.Y + i * GridSize.Y),
Color.White * (1.0f - normalizedDistY) * alpha, depth: 0.6f, width: 3); Color.White * (1.0f - normalizedDistY) * alpha, depth: 0.6f, width: 3);
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
verticalLine,
new Vector2(topLeft.X + i * GridSize.X, -topLeft.Y + expandY), new Vector2(topLeft.X + i * GridSize.X, -topLeft.Y + expandY),
new Vector2(topLeft.X + i * GridSize.X, -bottomRight.Y - expandY), new Vector2(topLeft.X + i * GridSize.X, -bottomRight.Y - expandY),
Color.White * (1.0f - normalizedDistX) * alpha, depth: 0.6f, width: 3); Color.White * (1.0f - normalizedDistX) * alpha, depth: 0.6f, width: 3);
@@ -10,10 +10,7 @@ namespace Barotrauma
{ {
if (GameMain.Client == null) { return; } if (GameMain.Client == null) { return; }
Vector2 newVelocity = Body.LinearVelocity; Body.CorrectPosition(positionBuffer, out Vector2 newPosition, out Vector2 newVelocity, out _, out _);
Vector2 newPosition = Body.SimPosition;
Body.CorrectPosition(positionBuffer, out newPosition, out newVelocity, out _, out _);
Vector2 moveAmount = ConvertUnits.ToDisplayUnits(newPosition - Body.SimPosition); Vector2 moveAmount = ConvertUnits.ToDisplayUnits(newPosition - Body.SimPosition);
newVelocity = newVelocity.ClampLength(100.0f); newVelocity = newVelocity.ClampLength(100.0f);
if (!MathUtils.IsValid(newVelocity) || moveAmount.LengthSquared() < 0.0001f) if (!MathUtils.IsValid(newVelocity) || moveAmount.LengthSquared() < 0.0001f)
@@ -24,12 +21,12 @@ namespace Barotrauma
List<Submarine> subsToMove = submarine.GetConnectedSubs(); List<Submarine> subsToMove = submarine.GetConnectedSubs();
foreach (Submarine dockedSub in subsToMove) foreach (Submarine dockedSub in subsToMove)
{ {
if (dockedSub == submarine) continue; if (dockedSub == submarine) { continue; }
//clear the position buffer of the docked subs to prevent unnecessary position corrections //clear the position buffer of the docked subs to prevent unnecessary position corrections
dockedSub.SubBody.positionBuffer.Clear(); dockedSub.SubBody.positionBuffer.Clear();
} }
Submarine closestSub = null; Submarine closestSub;
if (Character.Controlled == null) if (Character.Controlled == null)
{ {
closestSub = Submarine.FindClosest(GameMain.GameScreen.Cam.Position); closestSub = Submarine.FindClosest(GameMain.GameScreen.Cam.Position);
@@ -63,5 +60,33 @@ namespace Barotrauma
if (Character.Controlled != null) Character.Controlled.CursorPosition += moveAmount; if (Character.Controlled != null) Character.Controlled.CursorPosition += moveAmount;
} }
} }
private void PlayDamageSounds(Dictionary<Structure, float> damagedStructures, Vector2 impactSimPos, float impact, string soundTag)
{
if (impact < MinCollisionImpact) { return; }
//play a damage sound for the structure that took the most damage
float maxDamage = 0.0f;
Structure maxDamageStructure = null;
foreach (KeyValuePair<Structure, float> structureDamage in damagedStructures)
{
if (maxDamageStructure == null || structureDamage.Value > maxDamage)
{
maxDamage = structureDamage.Value;
maxDamageStructure = structureDamage.Key;
}
}
if (maxDamageStructure != null)
{
SoundPlayer.PlayDamageSound(
soundTag,
impact * 10.0f,
ConvertUnits.ToDisplayUnits(impactSimPos),
MathHelper.Lerp(2000.0f, 10000.0f, (impact - MinCollisionImpact) / 2.0f),
maxDamageStructure.Tags);
}
}
} }
} }
@@ -50,6 +50,8 @@ namespace Barotrauma.Networking
Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16()); Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16());
int optionIndex = msg.ReadByte(); int optionIndex = msg.ReadByte();
OrderTarget orderTargetPosition = null; OrderTarget orderTargetPosition = null;
Order.OrderTargetType orderTargetType = (Order.OrderTargetType)msg.ReadByte();
int wallSectionIndex = 0;
if (msg.ReadBoolean()) if (msg.ReadBoolean())
{ {
var x = msg.ReadSingle(); var x = msg.ReadSingle();
@@ -57,6 +59,10 @@ namespace Barotrauma.Networking
var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull; var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull;
orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true); orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true);
} }
else if(orderTargetType == Order.OrderTargetType.WallSection)
{
wallSectionIndex = msg.ReadByte();
}
Order orderPrefab; Order orderPrefab;
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count) if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
@@ -78,17 +84,31 @@ namespace Barotrauma.Networking
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen) if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{ {
var order = orderTargetPosition == null ? Order order = null;
new Order(orderPrefab, targetEntity, orderPrefab.GetTargetItemComponent(targetEntity as Item), orderGiver: senderCharacter) : switch (orderTargetType)
new Order(orderPrefab, orderTargetPosition, orderGiver: senderCharacter);
if (order.TargetAllCharacters)
{ {
GameMain.GameSession?.CrewManager?.AddOrder(order, orderPrefab.FadeOutTime); case Order.OrderTargetType.Entity:
order = new Order(orderPrefab, targetEntity, orderPrefab.GetTargetItemComponent(targetEntity as Item), orderGiver: senderCharacter);
break;
case Order.OrderTargetType.Position:
order = new Order(orderPrefab, orderTargetPosition, orderGiver: senderCharacter);
break;
case Order.OrderTargetType.WallSection:
order = new Order(orderPrefab, targetEntity as Structure, wallSectionIndex, orderGiver: senderCharacter);
break;
} }
else if (targetCharacter != null)
if (order != null)
{ {
targetCharacter.SetOrder(order, orderOption, senderCharacter); if (order.TargetAllCharacters)
{
var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null;
GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
}
else if (targetCharacter != null)
{
targetCharacter.SetOrder(order, orderOption, senderCharacter);
}
} }
} }
@@ -1,11 +1,5 @@
using System; using System.Diagnostics;
using System.Collections.Generic;
using System.Diagnostics;
using Barotrauma.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -26,7 +20,15 @@ namespace Barotrauma.Networking
PrivateStart(); PrivateStart();
processInfo.Arguments += " -pipes " + writePipe.GetClientHandleAsString() + " " + readPipe.GetClientHandleAsString(); processInfo.Arguments += " -pipes " + writePipe.GetClientHandleAsString() + " " + readPipe.GetClientHandleAsString();
Process = Process.Start(processInfo); try
{
Process = Process.Start(processInfo);
}
catch
{
DebugConsole.ThrowError($"Failed to start ChildServerRelay Process. File: {processInfo.FileName}, arguments: {processInfo.Arguments}");
throw;
}
localHandlesDisposed = false; localHandlesDisposed = false;
} }
@@ -272,6 +272,7 @@ namespace Barotrauma.Networking
private void ConnectToServer(object endpoint, string hostName) private void ConnectToServer(object endpoint, string hostName)
{ {
LastClientListUpdateID = 0; LastClientListUpdateID = 0;
foreach (var c in ConnectedClients) foreach (var c in ConnectedClients)
{ {
GameMain.NetLobbyScreen.RemovePlayer(c); GameMain.NetLobbyScreen.RemovePlayer(c);
@@ -361,6 +362,14 @@ namespace Barotrauma.Networking
{ {
GameMain.NetLobbyScreen.Select(); GameMain.NetLobbyScreen.Select();
} }
else
{
entityEventManager.ClearSelf();
foreach (Character c in Character.CharacterList)
{
c.ResetNetState();
}
}
chatBox.InputBox.Enabled = true; chatBox.InputBox.Enabled = true;
if (GameMain.NetLobbyScreen?.ChatInput != null) if (GameMain.NetLobbyScreen?.ChatInput != null)
@@ -903,6 +912,9 @@ namespace Barotrauma.Networking
case ServerPacketHeader.CREW: case ServerPacketHeader.CREW:
campaign?.ClientReadCrew(inc); campaign?.ClientReadCrew(inc);
break; break;
case ServerPacketHeader.READY_CHECK:
ReadyCheck.ClientRead(inc);
break;
case ServerPacketHeader.FILE_TRANSFER: case ServerPacketHeader.FILE_TRANSFER:
fileReceiver.ReadMessage(inc); fileReceiver.ReadMessage(inc);
break; break;
@@ -976,7 +988,7 @@ namespace Barotrauma.Networking
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" + string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" +
" (client value #" + i + ": " + Level.Loaded.EqualityCheckValues[i] + " (client value #" + i + ": " + Level.Loaded.EqualityCheckValues[i] +
", server value #" + i + ": " + levelEqualityCheckValues[i].ToString("X") + ", server value #" + i + ": " + levelEqualityCheckValues[i].ToString("X") +
", level value count: " + levelEqualityCheckValues.Count.ToString("X") + ", level value count: " + levelEqualityCheckValues.Count +
", seed: " + Level.Loaded.Seed + ", seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" + ", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" +
", mirrored: " + Level.Loaded.Mirrored + ")."; ", mirrored: " + Level.Loaded.Mirrored + ").";
@@ -2892,8 +2904,9 @@ namespace Barotrauma.Networking
return false; return false;
} }
if (button != null) { button.Enabled = false; } if (button != null) { button.Enabled = false; }
if (campaign != null) LateCampaignJoin = true; if (campaign != null) { LateCampaignJoin = true; }
if (clientPeer == null) { return false; }
IWriteMessage readyToStartMsg = new WriteOnlyMessage(); IWriteMessage readyToStartMsg = new WriteOnlyMessage();
readyToStartMsg.Write((byte)ClientPacketHeader.RESPONSE_STARTGAME); readyToStartMsg.Write((byte)ClientPacketHeader.RESPONSE_STARTGAME);
@@ -61,6 +61,7 @@ namespace Barotrauma
CreateLabeledNumberInput(parent, 0, 20, nameof(AllowedWireDisconnectionsPerMinute)); CreateLabeledNumberInput(parent, 0, 20, nameof(AllowedWireDisconnectionsPerMinute));
CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, nameof(WireDisconnectionKarmaDecrease)); CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, nameof(WireDisconnectionKarmaDecrease));
CreateLabeledSlider(parent, 0.0f, 30.0f, 1.0f, nameof(SpamFilterKarmaDecrease)); CreateLabeledSlider(parent, 0.0f, 30.0f, 1.0f, nameof(SpamFilterKarmaDecrease));
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(BallastFloraKarmaIncrease));
//hide these for now if a localized text is not available //hide these for now if a localized text is not available
if (TextManager.ContainsTag("Karma." + nameof(DangerousItemStealKarmaDecrease))) if (TextManager.ContainsTag("Karma." + nameof(DangerousItemStealKarmaDecrease)))
@@ -193,7 +193,7 @@ namespace Barotrauma.Networking
continue; continue;
} }
byte msgLength = msg.ReadByte(); int msgLength = (int)msg.ReadVariableUInt32();
IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable; IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
entities.Add(entity); entities.Add(entity);
@@ -300,5 +300,15 @@ namespace Barotrauma.Networking
MidRoundSyncingDone = false; MidRoundSyncingDone = false;
} }
/// <summary>
/// Clears events generated by the current client, used
/// when resynchronizing with the server after a timeout.
/// </summary>
public void ClearSelf()
{
ID = 0;
events.Clear();
}
} }
} }
@@ -13,7 +13,8 @@ namespace Barotrauma.Networking
msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID); msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID);
msg.Write(TargetEntity is Entity ? (TargetEntity as Entity).ID : (UInt16)0); msg.Write(TargetEntity is Entity ? (TargetEntity as Entity).ID : (UInt16)0);
msg.Write((byte)Array.IndexOf(Order.Prefab.Options, OrderOption)); msg.Write((byte)Array.IndexOf(Order.Prefab.Options, OrderOption));
if (TargetEntity is OrderTarget orderTarget) msg.Write((byte)Order.TargetType);
if (Order.TargetType == Order.OrderTargetType.Position && TargetEntity is OrderTarget orderTarget)
{ {
msg.Write(true); msg.Write(true);
msg.Write(orderTarget.Position.X); msg.Write(orderTarget.Position.X);
@@ -23,6 +24,10 @@ namespace Barotrauma.Networking
else else
{ {
msg.Write(false); msg.Write(false);
if (Order.TargetType == Order.OrderTargetType.WallSection)
{
msg.Write((byte)(WallSectionIndex ?? Order.WallSectionIndex ?? 0));
}
} }
} }
} }
@@ -378,8 +378,8 @@ namespace Barotrauma.Networking
if (maxPlayersElement > NetConfig.MaxPlayers) if (maxPlayersElement > NetConfig.MaxPlayers)
{ {
DebugConsole.IsOpen = true; /*DebugConsole.IsOpen = true;
DebugConsole.NewMessage($"Setting the maximum amount of players to {maxPlayersElement} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", Color.Red); DebugConsole.NewMessage($"Setting the maximum amount of players to {maxPlayersElement} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", Color.Red);*/
maxPlayersElement = NetConfig.MaxPlayers; maxPlayersElement = NetConfig.MaxPlayers;
} }
@@ -540,5 +540,18 @@ namespace Barotrauma.Networking
return element; return element;
} }
public override bool Equals(object obj)
{
return obj is ServerInfo other ? Equals(other) : base.Equals(obj);
}
public bool Equals(ServerInfo other)
{
return
other.OwnerID == OwnerID &&
(other.LobbyID == LobbyID || other.LobbyID == 0 || LobbyID == 0) &&
((OwnerID == 0) ? (other.IP == IP && other.Port == Port) : true);
}
} }
} }
@@ -3,6 +3,7 @@ using Barotrauma.Networking;
using RestSharp; using RestSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;
@@ -240,7 +240,14 @@ namespace Barotrauma.Particles
{ {
animState += deltaTime; animState += deltaTime;
int frameCount = ((SpriteSheet)prefab.Sprites[spriteIndex]).FrameCount; int frameCount = ((SpriteSheet)prefab.Sprites[spriteIndex]).FrameCount;
animFrame = (int)Math.Min(Math.Floor(animState / prefab.AnimDuration * frameCount), frameCount - 1); if (prefab.LoopAnim)
{
animFrame = (int)(Math.Floor(animState / prefab.AnimDuration * frameCount) % frameCount);
}
else
{
animFrame = (int)Math.Min(Math.Floor(animState / prefab.AnimDuration * frameCount), frameCount - 1);
}
} }
lifeTime -= deltaTime; lifeTime -= deltaTime;
@@ -218,9 +218,12 @@ namespace Barotrauma
sb.AppendLine("Target site: " + exception.TargetSite.ToString()); sb.AppendLine("Target site: " + exception.TargetSite.ToString());
} }
sb.AppendLine("Stack trace: "); if (exception.StackTrace != null)
sb.AppendLine(exception.StackTrace.CleanupStackTrace()); {
sb.AppendLine("\n"); sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.StackTrace.CleanupStackTrace());
sb.AppendLine("\n");
}
if (exception.InnerException != null) if (exception.InnerException != null)
{ {
@@ -229,8 +232,11 @@ namespace Barotrauma
{ {
sb.AppendLine("Target site: " + exception.InnerException.TargetSite.ToString()); sb.AppendLine("Target site: " + exception.InnerException.TargetSite.ToString());
} }
sb.AppendLine("Stack trace: "); if (exception.InnerException.StackTrace != null)
sb.AppendLine(exception.InnerException.StackTrace.CleanupStackTrace()); {
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.InnerException.StackTrace.CleanupStackTrace());
}
} }
sb.AppendLine("Last debug messages:"); sb.AppendLine("Last debug messages:");
@@ -372,6 +372,9 @@ namespace Barotrauma.CharacterEditor
{ {
base.Update(deltaTime); base.Update(deltaTime);
if (Wizard.instance != null) { return; } if (Wizard.instance != null) { return; }
GameMain.LightManager?.Update((float)deltaTime);
spriteSheetRect = CalculateSpritesheetRectangle(); spriteSheetRect = CalculateSpritesheetRectangle();
// Handle shortcut keys // Handle shortcut keys
if (PlayerInput.KeyHit(Keys.F1)) if (PlayerInput.KeyHit(Keys.F1))
@@ -787,7 +790,7 @@ namespace Barotrauma.CharacterEditor
if (GameMain.LightManager.LightingEnabled) if (GameMain.LightManager.LightingEnabled)
{ {
GameMain.LightManager.ObstructVision = Character.Controlled.ObstructVision; GameMain.LightManager.ObstructVision = Character.Controlled.ObstructVision;
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam); GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam);
GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition); GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition);
} }
base.Draw(deltaTime, graphics, spriteBatch); base.Draw(deltaTime, graphics, spriteBatch);
@@ -186,7 +186,7 @@ namespace Barotrauma
spriteBatch.End(); spriteBatch.End();
graphics.SetRenderTarget(null); graphics.SetRenderTarget(null);
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam, renderTarget); GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam, renderTarget);
//------------------------------------------------------------------------ //------------------------------------------------------------------------
graphics.SetRenderTarget(renderTargetBackground); graphics.SetRenderTarget(renderTargetBackground);
@@ -24,24 +24,26 @@ namespace Barotrauma
get { return cam; } get { return cam; }
} }
private GUIFrame leftPanel, rightPanel, bottomPanel, topPanel; private readonly GUIFrame leftPanel, rightPanel, bottomPanel, topPanel;
private LevelGenerationParams selectedParams; private LevelGenerationParams selectedParams;
private LevelObjectPrefab selectedLevelObject; private LevelObjectPrefab selectedLevelObject;
private GUIListBox paramsList, ruinParamsList, outpostParamsList, levelObjectList; private readonly GUIListBox paramsList, ruinParamsList, caveParamsList, outpostParamsList, levelObjectList;
private GUIListBox editorContainer; private readonly GUIListBox editorContainer;
private GUIButton spriteEditDoneButton; private readonly GUIButton spriteEditDoneButton;
private GUITextBox seedBox; private readonly GUITextBox seedBox;
private GUITickBox lightingEnabled, cursorLightEnabled; private readonly GUITickBox lightingEnabled, cursorLightEnabled;
private Sprite editingSprite; private Sprite editingSprite;
private LightSource pointerLightSource; private LightSource pointerLightSource;
private readonly Color[] tunnelDebugColors = new Color[] { Color.White, Color.Cyan, Color.LightGreen, Color.Red, Color.LightYellow, Color.LightSeaGreen };
public LevelEditorScreen() public LevelEditorScreen()
{ {
cam = new Camera() cam = new Camera()
@@ -78,8 +80,17 @@ namespace Barotrauma
return true; return true;
}; };
var caveTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedLeftPanel.RectTransform), TextManager.Get("leveleditor.caveparams"), font: GUI.SubHeadingFont);
caveParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.1f), paddedLeftPanel.RectTransform));
caveParamsList.OnSelected += (GUIComponent component, object obj) =>
{
CreateCaveParamsEditor(obj as CaveGenerationParams);
return true;
};
var outpostTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedLeftPanel.RectTransform), TextManager.Get("leveleditor.outpostparams"), font: GUI.SubHeadingFont); var outpostTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedLeftPanel.RectTransform), TextManager.Get("leveleditor.outpostparams"), font: GUI.SubHeadingFont);
GUITextBlock.AutoScaleAndNormalize(ruinTitle, outpostTitle); GUITextBlock.AutoScaleAndNormalize(ruinTitle, caveTitle, outpostTitle);
outpostParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform)); outpostParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform));
outpostParamsList.OnSelected += (GUIComponent component, object obj) => outpostParamsList.OnSelected += (GUIComponent component, object obj) =>
@@ -237,13 +248,17 @@ namespace Barotrauma
{ {
OnClicked = (btn, obj) => OnClicked = (btn, obj) =>
{ {
bool wasLevelLoaded = Level.Loaded != null;
Submarine.Unload(); Submarine.Unload();
GameMain.LightManager.ClearLights(); GameMain.LightManager.ClearLights();
LevelData levelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams); LevelData levelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
levelData.ForceOutpostGenerationParams = outpostParamsList.SelectedData as OutpostGenerationParams; levelData.ForceOutpostGenerationParams = outpostParamsList.SelectedData as OutpostGenerationParams;
Level.Generate(levelData, mirror: false); Level.Generate(levelData, mirror: false);
GameMain.LightManager.AddLight(pointerLightSource); GameMain.LightManager.AddLight(pointerLightSource);
cam.Position = new Vector2(Level.Loaded.Size.X / 2, Level.Loaded.Size.Y / 2); if (!wasLevelLoaded || cam.Position.X < 0 || cam.Position.Y < 0 || cam.Position.Y > Level.Loaded.Size.X || cam.Position.Y > Level.Loaded.Size.Y)
{
cam.Position = new Vector2(Level.Loaded.Size.X / 2, Level.Loaded.Size.Y / 2);
}
foreach (GUITextBlock param in paramsList.Content.Children) foreach (GUITextBlock param in paramsList.Content.Children)
{ {
param.TextColor = param.UserData == selectedParams ? GUI.Style.Green : param.Style.TextColor; param.TextColor = param.UserData == selectedParams ? GUI.Style.Green : param.Style.TextColor;
@@ -344,6 +359,7 @@ namespace Barotrauma
editingSprite = null; editingSprite = null;
UpdateParamsList(); UpdateParamsList();
UpdateRuinParamsList(); UpdateRuinParamsList();
UpdateCaveParamsList();
UpdateOutpostParamsList(); UpdateOutpostParamsList();
UpdateLevelObjectsList(); UpdateLevelObjectsList();
} }
@@ -371,6 +387,22 @@ namespace Barotrauma
} }
} }
private void UpdateCaveParamsList()
{
editorContainer.ClearChildren();
caveParamsList.Content.ClearChildren();
foreach (CaveGenerationParams genParams in CaveGenerationParams.CaveParams)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), caveParamsList.Content.RectTransform) { MinSize = new Point(0, 20) },
genParams.Name)
{
Padding = Vector4.Zero,
UserData = genParams
};
}
}
private void UpdateRuinParamsList() private void UpdateRuinParamsList()
{ {
editorContainer.ClearChildren(); editorContainer.ClearChildren();
@@ -425,6 +457,7 @@ namespace Barotrauma
text: ToolBox.LimitString(levelObjPrefab.Name, GUI.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUI.SmallFont) text: ToolBox.LimitString(levelObjPrefab.Name, GUI.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUI.SmallFont)
{ {
CanBeFocused = false, CanBeFocused = false,
ToolTip = levelObjPrefab.Name
}; };
Sprite sprite = levelObjPrefab.Sprites.FirstOrDefault() ?? levelObjPrefab.DeformableSprite?.Sprite; Sprite sprite = levelObjPrefab.Sprites.FirstOrDefault() ?? levelObjPrefab.DeformableSprite?.Sprite;
@@ -437,15 +470,14 @@ namespace Barotrauma
} }
} }
private void CreateLevelObjectEditor(LevelObjectPrefab levelObjectPrefab) private void CreateCaveParamsEditor(CaveGenerationParams caveGenerationParams)
{ {
editorContainer.ClearChildren(); editorContainer.ClearChildren();
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, caveGenerationParams, false, true, elementHeight: 20);
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, levelObjectPrefab, false, true, elementHeight: 20, titleFont: GUI.LargeFont);
if (selectedParams != null) if (selectedParams != null)
{ {
var commonnessContainer = new GUILayoutGroup(new RectTransform(new Point(editor.Rect.Width, 70)) { IsFixedSize = true }, var commonnessContainer = new GUILayoutGroup(new RectTransform(new Point(editor.Rect.Width, 70)) { IsFixedSize = true },
isHorizontal: false, childAnchor: Anchor.TopCenter) isHorizontal: false, childAnchor: Anchor.TopCenter)
{ {
AbsoluteSpacing = 5, AbsoluteSpacing = 5,
@@ -457,15 +489,60 @@ namespace Barotrauma
{ {
MinValueFloat = 0, MinValueFloat = 0,
MaxValueFloat = 100, MaxValueFloat = 100,
FloatValue = levelObjectPrefab.GetCommonness(selectedParams.Identifier), FloatValue = caveGenerationParams.GetCommonness(selectedParams),
OnValueChanged = (numberInput) => OnValueChanged = (numberInput) =>
{ {
levelObjectPrefab.OverrideCommonness[selectedParams.Identifier] = numberInput.FloatValue; caveGenerationParams.OverrideCommonness[selectedParams.Identifier] = numberInput.FloatValue;
} }
}; };
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), commonnessContainer.RectTransform), style: null); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), commonnessContainer.RectTransform), style: null);
editor.AddCustomContent(commonnessContainer, 1); editor.AddCustomContent(commonnessContainer, 1);
} }
}
private void CreateLevelObjectEditor(LevelObjectPrefab levelObjectPrefab)
{
editorContainer.ClearChildren();
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, levelObjectPrefab, false, true, elementHeight: 20, titleFont: GUI.LargeFont);
if (selectedParams != null)
{
List<string> availableIdentifiers = new List<string>();
{
if (selectedParams != null) { availableIdentifiers.Add(selectedParams.Identifier); }
}
foreach (var caveParam in CaveGenerationParams.CaveParams)
{
if (selectedParams != null && caveParam.GetCommonness(selectedParams) <= 0.0f) { continue; }
availableIdentifiers.Add(caveParam.Identifier);
}
availableIdentifiers.Reverse();
foreach (string paramsId in availableIdentifiers)
{
var commonnessContainer = new GUILayoutGroup(new RectTransform(new Point(editor.Rect.Width, 70)) { IsFixedSize = true },
isHorizontal: false, childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = 5,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), commonnessContainer.RectTransform),
TextManager.GetWithVariable("leveleditor.levelobjcommonness", "[leveltype]", paramsId), textAlignment: Alignment.Center);
new GUINumberInput(new RectTransform(new Vector2(0.5f, 0.4f), commonnessContainer.RectTransform), GUINumberInput.NumberType.Float)
{
MinValueFloat = 0,
MaxValueFloat = 100,
FloatValue = selectedParams.Identifier == paramsId ? levelObjectPrefab.GetCommonness(selectedParams) : levelObjectPrefab.GetCommonness(CaveGenerationParams.CaveParams.Find(p => p.Identifier == paramsId)),
OnValueChanged = (numberInput) =>
{
levelObjectPrefab.OverrideCommonness[paramsId] = numberInput.FloatValue;
}
};
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), commonnessContainer.RectTransform), style: null);
editor.AddCustomContent(commonnessContainer, 1);
}
}
Sprite sprite = levelObjectPrefab.Sprites.FirstOrDefault() ?? levelObjectPrefab.DeformableSprite?.Sprite; Sprite sprite = levelObjectPrefab.Sprites.FirstOrDefault() ?? levelObjectPrefab.DeformableSprite?.Sprite;
if (sprite != null) if (sprite != null)
@@ -508,7 +585,7 @@ namespace Barotrauma
foreach (LevelObjectPrefab objPrefab in LevelObjectPrefab.List) foreach (LevelObjectPrefab objPrefab in LevelObjectPrefab.List)
{ {
dropdown.AddItem(objPrefab.Name, objPrefab); dropdown.AddItem(objPrefab.Name, objPrefab);
if (childObj.AllowedNames.Contains(objPrefab.Name)) dropdown.SelectItem(objPrefab); if (childObj.AllowedNames.Contains(objPrefab.Name)) { dropdown.SelectItem(objPrefab); }
} }
dropdown.OnSelected = (selected, obj) => dropdown.OnSelected = (selected, obj) =>
{ {
@@ -590,7 +667,7 @@ namespace Barotrauma
foreach (GUIComponent levelObjFrame in levelObjectList.Content.Children) foreach (GUIComponent levelObjFrame in levelObjectList.Content.Children)
{ {
var levelObj = levelObjFrame.UserData as LevelObjectPrefab; var levelObj = levelObjFrame.UserData as LevelObjectPrefab;
float commonness = levelObj.GetCommonness(selectedParams.Identifier); float commonness = levelObj.GetCommonness(selectedParams);
levelObjFrame.Color = commonness > 0.0f ? GUI.Style.Green * 0.4f : Color.Transparent; levelObjFrame.Color = commonness > 0.0f ? GUI.Style.Green * 0.4f : Color.Transparent;
levelObjFrame.SelectedColor = commonness > 0.0f ? GUI.Style.Green * 0.6f : Color.White * 0.5f; levelObjFrame.SelectedColor = commonness > 0.0f ? GUI.Style.Green * 0.6f : Color.White * 0.5f;
levelObjFrame.HoverColor = commonness > 0.0f ? GUI.Style.Green * 0.7f : Color.White * 0.6f; levelObjFrame.HoverColor = commonness > 0.0f ? GUI.Style.Green * 0.7f : Color.White * 0.6f;
@@ -607,7 +684,7 @@ namespace Barotrauma
{ {
var levelObj1 = c1.GUIComponent.UserData as LevelObjectPrefab; var levelObj1 = c1.GUIComponent.UserData as LevelObjectPrefab;
var levelObj2 = c2.GUIComponent.UserData as LevelObjectPrefab; var levelObj2 = c2.GUIComponent.UserData as LevelObjectPrefab;
return Math.Sign(levelObj2.GetCommonness(selectedParams.Identifier) - levelObj1.GetCommonness(selectedParams.Identifier)); return Math.Sign(levelObj2.GetCommonness(selectedParams) - levelObj1.GetCommonness(selectedParams));
}); });
} }
@@ -630,7 +707,7 @@ namespace Barotrauma
{ {
if (lightingEnabled.Selected) if (lightingEnabled.Selected)
{ {
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam); GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam);
} }
graphics.Clear(Color.Black); graphics.Clear(Color.Black);
@@ -643,6 +720,72 @@ namespace Barotrauma
Submarine.DrawFront(spriteBatch); Submarine.DrawFront(spriteBatch);
Submarine.DrawDamageable(spriteBatch, null); Submarine.DrawDamageable(spriteBatch, null);
GUI.DrawRectangle(spriteBatch, new Rectangle(new Point(0, -Level.Loaded.Size.Y), Level.Loaded.Size), Color.Gray, thickness: (int)(1.0f / cam.Zoom)); GUI.DrawRectangle(spriteBatch, new Rectangle(new Point(0, -Level.Loaded.Size.Y), Level.Loaded.Size), Color.Gray, thickness: (int)(1.0f / cam.Zoom));
for (int i = 0; i < Level.Loaded.Tunnels.Count; i++)
{
var tunnel = Level.Loaded.Tunnels[i];
Color tunnelColor = tunnelDebugColors[i % tunnelDebugColors.Length] * 0.2f;
for (int j = 1; j < tunnel.Nodes.Count; j++)
{
Vector2 start = new Vector2(tunnel.Nodes[j - 1].X, -tunnel.Nodes[j - 1].Y);
Vector2 end = new Vector2(tunnel.Nodes[j].X, -tunnel.Nodes[j].Y);
GUI.DrawLine(spriteBatch, start, end, tunnelColor, width: (int)(2.0f / cam.Zoom));
}
}
foreach (Level.InterestingPosition interestingPos in Level.Loaded.PositionsOfInterest)
{
if (interestingPos.Position.X < cam.WorldView.X || interestingPos.Position.X > cam.WorldView.Right ||
interestingPos.Position.Y > cam.WorldView.Y || interestingPos.Position.Y < cam.WorldView.Y - cam.WorldView.Height)
{
continue;
}
Vector2 pos = new Vector2(interestingPos.Position.X, -interestingPos.Position.Y);
spriteBatch.DrawCircle(pos, 500, 6, Color.White * 0.5f, thickness: (int)(2 / cam.Zoom));
GUI.DrawString(spriteBatch, pos, interestingPos.PositionType.ToString(), Color.White, font: GUI.LargeFont);
}
// TODO: Improve this temporary level editor debug solution (or remove it)
foreach (var pathPoint in Level.Loaded.PathPoints)
{
Vector2 pathPointPos = new Vector2(pathPoint.Position.X, -pathPoint.Position.Y);
foreach (var location in pathPoint.ClusterLocations)
{
if (location.Resources == null) { continue; }
foreach (var resource in location.Resources)
{
Vector2 resourcePos = new Vector2(resource.Position.X, -resource.Position.Y);
spriteBatch.DrawCircle(resourcePos, 100, 6, Color.DarkGreen * 0.5f, thickness: (int)(2 / cam.Zoom));
GUI.DrawString(spriteBatch, resourcePos, resource.Name, Color.DarkGreen, font: GUI.LargeFont);
var dist = Vector2.Distance(resourcePos, pathPointPos);
var lineStartPos = Vector2.Lerp(resourcePos, pathPointPos, 110 / dist);
var lineEndPos = Vector2.Lerp(pathPointPos, resourcePos, 310 / dist);
GUI.DrawLine(spriteBatch, lineStartPos, lineEndPos, Color.DarkGreen * 0.5f, width: (int)(2 / cam.Zoom));
}
}
var color = pathPoint.ShouldContainResources ? Color.DarkGreen : Color.DarkRed;
spriteBatch.DrawCircle(pathPointPos, 300, 6, color * 0.5f, thickness: (int)(2 / cam.Zoom));
GUI.DrawString(spriteBatch, pathPointPos, "Path Point\n" + pathPoint.Id, color, font: GUI.LargeFont);
}
/*for (int i = 0; i < Level.Loaded.distanceField.Count; i++)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(Level.Loaded.distanceField[i].First.X, -Level.Loaded.distanceField[i].First.Y),
Vector2.One * 5 / cam.Zoom,
ToolBox.GradientLerp((float)(Level.Loaded.distanceField[i].Second / 20000.0), Color.Red, Color.Orange, Color.Yellow, Color.LightGreen),
isFilled: true);
}*/
/*for (int i = 0; i < Level.Loaded.siteCoordsX.Count; i++)
{
GUI.DrawRectangle(spriteBatch,
new Vector2((float)Level.Loaded.siteCoordsX[i], -(float)Level.Loaded.siteCoordsY[i]),
Vector2.One * 5 / cam.Zoom,
Color.Red,
isFilled: true);
}*/
spriteBatch.End(); spriteBatch.End();
if (lightingEnabled.Selected) if (lightingEnabled.Selected)
@@ -659,12 +802,23 @@ namespace Barotrauma
} }
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
if (Level.Loaded != null)
{
float crushDepthScreen = cam.WorldToScreen(new Vector2(0.0f, -Level.Loaded.CrushDepth)).Y;
if (crushDepthScreen > 0.0f && crushDepthScreen < GameMain.GraphicsHeight)
{
GUI.DrawLine(spriteBatch, new Vector2(0, crushDepthScreen), new Vector2(GameMain.GraphicsWidth, crushDepthScreen), GUI.Style.Red * 0.25f, width: 5);
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, crushDepthScreen), "Crush depth", GUI.Style.Red, backgroundColor: Color.Black);
}
}
GUI.Draw(Cam, spriteBatch); GUI.Draw(Cam, spriteBatch);
spriteBatch.End(); spriteBatch.End();
} }
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
GameMain.LightManager?.Update((float)deltaTime);
pointerLightSource.Position = cam.ScreenToWorld(PlayerInput.MousePosition); pointerLightSource.Position = cam.ScreenToWorld(PlayerInput.MousePosition);
pointerLightSource.Enabled = cursorLightEnabled.Selected; pointerLightSource.Enabled = cursorLightEnabled.Selected;
pointerLightSource.IsBackground = true; pointerLightSource.IsBackground = true;
@@ -694,7 +848,40 @@ namespace Barotrauma
{ {
foreach (XElement element in doc.Root.Elements()) foreach (XElement element in doc.Root.Elements())
{ {
XElement levelParamElement = element; if (element.IsOverride())
{
foreach (XElement subElement in element.Elements())
{
string id = element.GetAttributeString("identifier", null) ?? element.Name.ToString();
if (!id.Equals(genParams.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
SerializableProperty.SerializeProperties(genParams, element, true);
}
}
else
{
string id = element.GetAttributeString("identifier", null) ?? element.Name.ToString();
if (!id.Equals(genParams.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
SerializableProperty.SerializeProperties(genParams, element, true);
}
break;
}
}
using (var writer = XmlWriter.Create(configFile.Path, settings))
{
doc.WriteTo(writer);
writer.Flush();
}
}
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.CaveGenerationParameters))
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { continue; }
foreach (CaveGenerationParams genParams in CaveGenerationParams.CaveParams)
{
foreach (XElement element in doc.Root.Elements())
{
if (element.IsOverride()) if (element.IsOverride())
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
@@ -730,7 +917,8 @@ namespace Barotrauma
{ {
foreach (XElement element in doc.Root.Elements()) foreach (XElement element in doc.Root.Elements())
{ {
if (!element.Name.ToString().Equals(levelObjPrefab.Name, StringComparison.OrdinalIgnoreCase)) { continue; } string identifier = element.GetAttributeString("identifier", null);
if (!identifier.Equals(levelObjPrefab.Identifier, StringComparison.OrdinalIgnoreCase)) { continue; }
levelObjPrefab.Save(element); levelObjPrefab.Save(element);
break; break;
} }
@@ -846,7 +1034,7 @@ namespace Barotrauma
return false; return false;
} }
if (LevelObjectPrefab.List.Any(obj => obj.Name.ToLower() == nameBox.Text.ToLower())) if (LevelObjectPrefab.List.Any(obj => obj.Identifier.Equals(nameBox.Text, StringComparison.OrdinalIgnoreCase)))
{ {
nameBox.Flash(GUI.Style.Red); nameBox.Flash(GUI.Style.Red);
GUI.AddMessage(TextManager.Get("leveleditor.levelobjnametaken"), GUI.Style.Red); GUI.AddMessage(TextManager.Get("leveleditor.levelobjnametaken"), GUI.Style.Red);
@@ -860,14 +1048,14 @@ namespace Barotrauma
return false; return false;
} }
newPrefab.Name = nameBox.Text; newPrefab.Identifier = nameBox.Text;
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true }; System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true };
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs)) foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs))
{ {
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path); XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { continue; } if (doc == null) { continue; }
var newElement = new XElement(newPrefab.Name); var newElement = new XElement(newPrefab.Identifier);
newPrefab.Save(newElement); newPrefab.Save(newElement);
newElement.Add(new XElement("Sprite", newElement.Add(new XElement("Sprite",
new XAttribute("texture", texturePathBox.Text), new XAttribute("texture", texturePathBox.Text),
@@ -38,6 +38,9 @@ namespace Barotrauma
private GUIImage playstyleBanner; private GUIImage playstyleBanner;
private GUITextBlock playstyleDescription; private GUITextBlock playstyleDescription;
private GUIComponent remoteContentContainer;
private XDocument remoteContentDoc;
private Tab selectedTab; private Tab selectedTab;
private Sprite backgroundSprite; private Sprite backgroundSprite;
@@ -62,6 +65,14 @@ namespace Barotrauma
} }
CreateHostServerFields(); CreateHostServerFields();
CreateCampaignSetupUI(); CreateCampaignSetupUI();
if (remoteContentDoc?.Root != null)
{
remoteContentContainer.ClearChildren();
foreach (XElement subElement in remoteContentDoc.Root.Elements())
{
GUIComponent.FromXML(subElement, remoteContentContainer.RectTransform);
}
}
}; };
new GUIImage(new RectTransform(new Vector2(0.4f, 0.25f), Frame.RectTransform, Anchor.BottomRight) new GUIImage(new RectTransform(new Vector2(0.4f, 0.25f), Frame.RectTransform, Anchor.BottomRight)
@@ -84,6 +95,11 @@ namespace Barotrauma
RelativeSpacing = 0.02f RelativeSpacing = 0.02f
}; };
remoteContentContainer = new GUIFrame(new RectTransform(Vector2.One, parent: Frame.RectTransform), style: null)
{
CanBeFocused = false
};
#if TEST_REMOTE_CONTENT #if TEST_REMOTE_CONTENT
var doc = XMLExtensions.TryLoadXml("Content/UI/MenuTextTest.xml"); var doc = XMLExtensions.TryLoadXml("Content/UI/MenuTextTest.xml");
@@ -91,7 +107,7 @@ namespace Barotrauma
{ {
foreach (XElement subElement in doc?.Root.Elements()) foreach (XElement subElement in doc?.Root.Elements())
{ {
GUIComponent.FromXML(subElement, Frame.RectTransform); GUIComponent.FromXML(subElement, remoteContentContainer.RectTransform);
} }
} }
#else #else
@@ -1402,10 +1418,10 @@ namespace Barotrauma
if (index > 0) { xml = xml.Substring(index, xml.Length - index); } if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
if (!string.IsNullOrWhiteSpace(xml)) if (!string.IsNullOrWhiteSpace(xml))
{ {
XElement element = XDocument.Parse(xml)?.Root; remoteContentDoc = XDocument.Parse(xml);
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in remoteContentDoc?.Root.Elements())
{ {
GUIComponent.FromXML(subElement, Frame.RectTransform); GUIComponent.FromXML(subElement, remoteContentContainer.RectTransform);
} }
} }
} }
@@ -832,7 +832,7 @@ namespace Barotrauma
var modeFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), modeList.Content.RectTransform), style: null) var modeFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), modeList.Content.RectTransform), style: null)
{ {
UserData = mode UserData = mode
}; };
var modeContent = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.9f), modeFrame.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.02f, 0.0f) }) var modeContent = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.9f), modeFrame.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.02f, 0.0f) })
{ {
@@ -910,21 +910,24 @@ namespace Barotrauma
} }
}; };
missionTypeTickBoxes = new GUITickBox[Enum.GetValues(typeof(MissionType)).Length - 2]; var missionTypes = (MissionType[])Enum.GetValues(typeof(MissionType));
missionTypeTickBoxes = new GUITickBox[missionTypes.Length - 2];
int index = 0; int index = 0;
foreach (MissionType missionType in Enum.GetValues(typeof(MissionType))) for (int i = 0; i < missionTypes.Length; i++)
{ {
var missionType = missionTypes[i];
if (missionType == MissionType.None || missionType == MissionType.All) { continue; } if (missionType == MissionType.None || missionType == MissionType.All) { continue; }
GUIFrame frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), missionTypeList.Content.RectTransform) { MinSize = new Point(0, (int)(30 * GUI.Scale)) }, style: "ListBoxElement") GUIFrame frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), missionTypeList.Content.RectTransform) { MinSize = new Point(0, (int)(30 * GUI.Scale)) }, style: "ListBoxElement")
{ {
UserData = index, UserData = missionType,
}; };
missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform), missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform),
TextManager.Get("MissionType." + missionType.ToString())) TextManager.Get("MissionType." + missionType.ToString()))
{ {
UserData = (int)missionType, UserData = (int)missionType,
ToolTip = TextManager.Get("MissionTypeDescription." + missionType.ToString(), returnNull: true),
OnSelected = (tickbox) => OnSelected = (tickbox) =>
{ {
int missionTypeOr = tickbox.Selected ? (int)tickbox.UserData : (int)MissionType.None; int missionTypeOr = tickbox.Selected ? (int)tickbox.UserData : (int)MissionType.None;
@@ -934,7 +937,6 @@ namespace Barotrauma
} }
}; };
frame.RectTransform.MinSize = missionTypeTickBoxes[index].RectTransform.MinSize; frame.RectTransform.MinSize = missionTypeTickBoxes[index].RectTransform.MinSize;
index++; index++;
} }
clientDisabledElements.AddRange(missionTypeTickBoxes); clientDisabledElements.AddRange(missionTypeTickBoxes);
@@ -1485,7 +1487,7 @@ namespace Barotrauma
return true; return true;
} }
}; };
} }
} }
private void CreateChangesPendingText() private void CreateChangesPendingText()
@@ -2509,7 +2511,7 @@ namespace Barotrauma
Selected = info.Gender == Gender.Female Selected = info.Gender == Gender.Female
}; };
int hairCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables), WearableType.Hair).Count(); int hairCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race), WearableType.Hair, info.HeadSpriteId).Count();
if (hairCount > 0) if (hairCount > 0)
{ {
var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Hair"), font: GUI.SubHeadingFont); var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Hair"), font: GUI.SubHeadingFont);
@@ -2524,7 +2526,7 @@ namespace Barotrauma
}; };
} }
int beardCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables), WearableType.Beard).Count(); int beardCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race), WearableType.Beard, info.HeadSpriteId).Count();
if (beardCount > 0) if (beardCount > 0)
{ {
var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Beard"), font: GUI.SubHeadingFont); var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Beard"), font: GUI.SubHeadingFont);
@@ -2539,7 +2541,7 @@ namespace Barotrauma
}; };
} }
int moustacheCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables), WearableType.Moustache).Count(); int moustacheCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race), WearableType.Moustache, info.HeadSpriteId).Count();
if (moustacheCount > 0) if (moustacheCount > 0)
{ {
var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Moustache"), font: GUI.SubHeadingFont); var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Moustache"), font: GUI.SubHeadingFont);
@@ -2554,7 +2556,7 @@ namespace Barotrauma
}; };
} }
int faceAttachmentCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables), WearableType.FaceAttachment).Count(); int faceAttachmentCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race), WearableType.FaceAttachment, info.HeadSpriteId).Count();
if (faceAttachmentCount > 0) if (faceAttachmentCount > 0)
{ {
var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Accessories"), font: GUI.SubHeadingFont); var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Accessories"), font: GUI.SubHeadingFont);
@@ -2976,7 +2978,7 @@ namespace Barotrauma
public void SelectMode(int modeIndex) public void SelectMode(int modeIndex)
{ {
if (modeIndex < 0 || modeIndex >= modeList.Content.CountChildren) { return; } if (modeIndex < 0 || modeIndex >= modeList.Content.CountChildren) { return; }
if ((GameModePreset)modeList.Content.GetChild(modeIndex).UserData != GameModePreset.MultiPlayerCampaign) if ((GameModePreset)modeList.Content.GetChild(modeIndex).UserData != GameModePreset.MultiPlayerCampaign)
{ {
ToggleCampaignMode(false); ToggleCampaignMode(false);
@@ -3001,16 +3003,33 @@ namespace Barotrauma
RefreshGameModeContent(); RefreshGameModeContent();
} }
private void RefreshMissionTypes()
{
for (int i = 0; i < missionTypeTickBoxes.Length; i++)
{
MissionType missionType = (MissionType)((int)missionTypeTickBoxes[i].UserData);
if (SelectedMode == GameModePreset.Mission)
{
missionTypeTickBoxes[i].Parent.Visible = MissionPrefab.CoOpMissionClasses.ContainsKey(missionType);
}
else if (SelectedMode == GameModePreset.PvP)
{
missionTypeTickBoxes[i].Parent.Visible = MissionPrefab.PvPMissionClasses.ContainsKey(missionType);
}
}
}
private void RefreshGameModeContent() private void RefreshGameModeContent()
{ {
if (GameMain.Client == null) { return; } if (GameMain.Client == null) { return; }
autoRestartBox.Parent.Visible = true; autoRestartBox.Parent.Visible = true;
settingsBlocker.Visible = false; settingsBlocker.Visible = false;
if (SelectedMode == GameModePreset.Mission) if (SelectedMode == GameModePreset.Mission || SelectedMode == GameModePreset.PvP)
{ {
MissionTypeFrame.Visible = true; MissionTypeFrame.Visible = true;
CampaignFrame.Visible = CampaignSetupFrame.Visible = false; CampaignFrame.Visible = CampaignSetupFrame.Visible = false;
RefreshMissionTypes();
} }
else if (SelectedMode == GameModePreset.MultiPlayerCampaign) else if (SelectedMode == GameModePreset.MultiPlayerCampaign)
{ {
@@ -3062,7 +3081,7 @@ namespace Barotrauma
RefreshEnabledElements(); RefreshEnabledElements();
if (enabled) if (enabled)
{ {
modeList.Select(2, true); modeList.Select(GameModePreset.MultiPlayerCampaign, true);
} }
} }
@@ -61,7 +61,7 @@ namespace Barotrauma
{ {
var temp = LoadException; var temp = LoadException;
LoadException = null; LoadException = null;
throw temp; System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(temp).Throw();
} }
} }
} }
@@ -696,6 +696,7 @@ namespace Barotrauma
private void ReadServerMemFromFile(string file, ref List<ServerInfo> servers) private void ReadServerMemFromFile(string file, ref List<ServerInfo> servers)
{ {
if (servers == null) { servers = new List<ServerInfo>(); } if (servers == null) { servers = new List<ServerInfo>(); }
servers.Clear();
if (!File.Exists(file)) { return; } if (!File.Exists(file)) { return; }
@@ -716,11 +717,21 @@ namespace Barotrauma
return; return;
} }
bool saveCleanup = false;
foreach (XElement element in doc.Root.Elements()) foreach (XElement element in doc.Root.Elements())
{ {
if (element.Name != "ServerInfo") { continue; } if (element.Name != "ServerInfo") { continue; }
servers.Add(ServerInfo.FromXElement(element)); var info = ServerInfo.FromXElement(element);
if (!servers.Any(s => s.Equals(info)))
{
servers.Add(info);
}
else
{
saveCleanup = true;
}
} }
if (saveCleanup) { WriteServerMemToFile(file, servers); }
} }
private void WriteServerMemToFile(string file, List<ServerInfo> servers) private void WriteServerMemToFile(string file, List<ServerInfo> servers)
@@ -1061,7 +1072,7 @@ namespace Barotrauma
(!filterFull.Selected || serverInfo.PlayerCount < serverInfo.MaxPlayers) && (!filterFull.Selected || serverInfo.PlayerCount < serverInfo.MaxPlayers) &&
(!filterEmpty.Selected || serverInfo.PlayerCount > 0) && (!filterEmpty.Selected || serverInfo.PlayerCount > 0) &&
(!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) && (!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) &&
(filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) && (!filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) &&
(!filterKarma.Selected || serverInfo.KarmaEnabled == true) && (!filterKarma.Selected || serverInfo.KarmaEnabled == true) &&
(!filterFriendlyFire.Selected || serverInfo.FriendlyFireEnabled == false) && (!filterFriendlyFire.Selected || serverInfo.FriendlyFireEnabled == false) &&
(!filterTraitor.Selected || serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe) && (!filterTraitor.Selected || serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe) &&
@@ -353,6 +353,8 @@ namespace Barotrauma
{ {
element.Elements("sprite").ForEach(s => CreateSprite(s)); element.Elements("sprite").ForEach(s => CreateSprite(s));
element.Elements("Sprite").ForEach(s => CreateSprite(s)); element.Elements("Sprite").ForEach(s => CreateSprite(s));
element.Elements("deformablesprite").ForEach(s => CreateSprite(s));
element.Elements("DeformableSprite").ForEach(s => CreateSprite(s));
element.Elements("backgroundsprite").ForEach(s => CreateSprite(s)); element.Elements("backgroundsprite").ForEach(s => CreateSprite(s));
element.Elements("BackgroundSprite").ForEach(s => CreateSprite(s)); element.Elements("BackgroundSprite").ForEach(s => CreateSprite(s));
element.Elements("brokensprite").ForEach(s => CreateSprite(s)); element.Elements("brokensprite").ForEach(s => CreateSprite(s));
@@ -71,6 +71,7 @@ namespace Barotrauma
private bool entityMenuOpen = true; private bool entityMenuOpen = true;
private float entityMenuOpenState = 1.0f; private float entityMenuOpenState = 1.0f;
private string lastFilter;
public GUIComponent EntityMenu; public GUIComponent EntityMenu;
private GUITextBox entityFilterBox; private GUITextBox entityFilterBox;
private GUIListBox entityList; private GUIListBox entityList;
@@ -105,7 +106,25 @@ namespace Barotrauma
private static GUIComponent autoSaveLabel; private static GUIComponent autoSaveLabel;
private static int maxAutoSaves = GameSettings.MaximumAutoSaves; private static int maxAutoSaves = GameSettings.MaximumAutoSaves;
public static bool BulkItemBufferInUse; public static readonly object ItemAddMutex = new object(), ItemRemoveMutex = new object();
public static bool TransparentWiringMode = true;
private static object bulkItemBufferinUse;
public static object BulkItemBufferInUse
{
get => bulkItemBufferinUse;
set
{
if (value != bulkItemBufferinUse && bulkItemBufferinUse != null)
{
CommitBulkItemBuffer();
}
bulkItemBufferinUse = value;
}
}
public static List<AddOrDeleteCommand> BulkItemBuffer = new List<AddOrDeleteCommand>(); public static List<AddOrDeleteCommand> BulkItemBuffer = new List<AddOrDeleteCommand>();
public static List<WarningType> SuppressedWarnings = new List<WarningType>(); public static List<WarningType> SuppressedWarnings = new List<WarningType>();
@@ -197,7 +216,7 @@ namespace Barotrauma
{ {
if (buoyancyVol / selectedVol < 1.0f) if (buoyancyVol / selectedVol < 1.0f)
{ {
retVal += " (" + TextManager.GetWithVariable("OptimalBallastLevel", "[value]", (buoyancyVol / selectedVol).ToString("0.000")) + ")"; retVal += " (" + TextManager.GetWithVariable("OptimalBallastLevel", "[value]", (buoyancyVol / selectedVol).ToString("0.0000")) + ")";
} }
else else
{ {
@@ -630,35 +649,33 @@ namespace Barotrauma
return Structure.WallList.Count.ToString(); return Structure.WallList.Count.ToString();
}; };
var lightCountText = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("SubEditorLights"), var lightCountLabel = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("SubEditorLights"),
textAlignment: Alignment.CenterLeft, font: GUI.SmallFont); textAlignment: Alignment.CenterLeft, font: GUI.SmallFont);
var lightCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), lightCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight); var lightCountText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), lightCountLabel.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
lightCount.TextGetter = () => lightCountText.TextGetter = () =>
{ {
int disabledItemLightCount = 0; int lightCount = 0;
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (item.ParentInventory == null) { continue; } if (item.ParentInventory != null) { continue; }
disabledItemLightCount += item.GetComponents<LightComponent>().Count(); lightCount += item.GetComponents<LightComponent>().Count();
} }
int count = GameMain.LightManager.Lights.Count() - disabledItemLightCount; lightCountText.TextColor = ToolBox.GradientLerp(lightCount / 250.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
lightCount.TextColor = ToolBox.GradientLerp(count / 250.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red); return lightCount.ToString();
return count.ToString();
}; };
var shadowCastingLightCountText = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("SubEditorShadowCastingLights"), var shadowCastingLightCountLabel = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("SubEditorShadowCastingLights"),
textAlignment: Alignment.CenterLeft, font: GUI.SmallFont, wrap: true); textAlignment: Alignment.CenterLeft, font: GUI.SmallFont, wrap: true);
var shadowCastingLightCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), shadowCastingLightCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight); var shadowCastingLightCountText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), shadowCastingLightCountLabel.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
shadowCastingLightCount.TextGetter = () => shadowCastingLightCountText.TextGetter = () =>
{ {
int disabledItemLightCount = 0; int lightCount = 0;
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (item.ParentInventory == null) { continue; } if (item.ParentInventory != null) { continue; }
disabledItemLightCount += item.GetComponents<LightComponent>().Count(); lightCount += item.GetComponents<LightComponent>().Count(l => l.CastShadows);
} }
int count = GameMain.LightManager.Lights.Count(l => l.CastShadows) - disabledItemLightCount; shadowCastingLightCountText.TextColor = ToolBox.GradientLerp(lightCount / 60.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
shadowCastingLightCount.TextColor = ToolBox.GradientLerp(count / 60.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red); return lightCount.ToString();
return count.ToString();
}; };
entityCountPanel.RectTransform.NonScaledSize = entityCountPanel.RectTransform.NonScaledSize =
new Point( new Point(
@@ -737,7 +754,13 @@ namespace Barotrauma
var filterText = new GUITextBlock(new RectTransform(new Vector2(0.1f, 1.0f), entityMenuTop.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.SubHeadingFont); var filterText = new GUITextBlock(new RectTransform(new Vector2(0.1f, 1.0f), entityMenuTop.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.SubHeadingFont);
filterText.RectTransform.MaxSize = new Point((int)(filterText.TextSize.X * 1.5f), int.MaxValue); filterText.RectTransform.MaxSize = new Point((int)(filterText.TextSize.X * 1.5f), int.MaxValue);
entityFilterBox = new GUITextBox(new RectTransform(new Vector2(0.17f, 1.0f), entityMenuTop.RectTransform), font: GUI.Font, createClearButton: true); entityFilterBox = new GUITextBox(new RectTransform(new Vector2(0.17f, 1.0f), entityMenuTop.RectTransform), font: GUI.Font, createClearButton: true);
entityFilterBox.OnTextChanged += (textBox, text) => { FilterEntities(text); return true; }; entityFilterBox.OnTextChanged += (textBox, text) =>
{
if (text == lastFilter) { return true; }
lastFilter = text;
FilterEntities(text);
return true;
};
//spacing //spacing
new GUIFrame(new RectTransform(new Vector2(0.075f, 1.0f), entityMenuTop.RectTransform), style: null); new GUIFrame(new RectTransform(new Vector2(0.075f, 1.0f), entityMenuTop.RectTransform), style: null);
@@ -994,15 +1017,9 @@ namespace Barotrauma
GameMain.LightManager.AmbientLight = GameMain.LightManager.AmbientLight =
Level.Loaded?.GenerationParams?.AmbientLightColor ?? Level.Loaded?.GenerationParams?.AmbientLightColor ??
LevelGenerationParams.LevelParams?.FirstOrDefault()?.AmbientLightColor ?? new Color(3, 3, 3, 3);
new Color(20, 20, 20, 255);
UpdateEntityList(); UpdateEntityList();
if (!wasSelectedBefore)
{
OpenEntityMenu(MapEntityCategory.Structure);
wasSelectedBefore = true;
}
isAutoSaving = false; isAutoSaving = false;
if (!wasSelectedBefore) if (!wasSelectedBefore)
@@ -1150,7 +1167,7 @@ namespace Barotrauma
{ {
if (dummyCharacter != null) RemoveDummyCharacter(); if (dummyCharacter != null) RemoveDummyCharacter();
dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", hasAi: false); dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.RespawnManagerID, hasAi: false);
dummyCharacter.Info.Name = "Galldren"; dummyCharacter.Info.Name = "Galldren";
//make space for the entity menu //make space for the entity menu
@@ -1195,7 +1212,7 @@ namespace Barotrauma
CrossThread.RequestExecutionOnMainThread(() => CrossThread.RequestExecutionOnMainThread(() =>
{ {
if (AutoSaveInfo?.Root == null) { return; } if (AutoSaveInfo?.Root == null || Submarine.MainSub?.Info == null) { return; }
int saveCount = AutoSaveInfo.Root.Elements().Count(); int saveCount = AutoSaveInfo.Root.Elements().Count();
while (AutoSaveInfo.Root.Elements().Count() > maxAutoSaves) while (AutoSaveInfo.Root.Elements().Count() > maxAutoSaves)
@@ -1602,14 +1619,14 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(0.4f, 1f), subTypeContainer.RectTransform), TextManager.Get("submarinetype")); new GUITextBlock(new RectTransform(new Vector2(0.4f, 1f), subTypeContainer.RectTransform), TextManager.Get("submarinetype"));
var subTypeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.6f, 1f), subTypeContainer.RectTransform)); var subTypeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.6f, 1f), subTypeContainer.RectTransform));
subTypeContainer.RectTransform.MinSize = new Point(0, subTypeContainer.RectTransform.Children.Max(c => c.MinSize.Y)); subTypeContainer.RectTransform.MinSize = new Point(0, subTypeContainer.RectTransform.Children.Max(c => c.MinSize.Y));
subTypeDropdown.AddItem(TextManager.Get("submarinetype.player"), SubmarineType.Player); foreach (SubmarineType subType in Enum.GetValues(typeof(SubmarineType)))
subTypeDropdown.AddItem(TextManager.Get("submarinetype.outpostmodule"), SubmarineType.OutpostModule); {
subTypeDropdown.AddItem(TextManager.Get("submarinetype.outpost"), SubmarineType.Outpost); subTypeDropdown.AddItem(TextManager.Get("submarinetype."+subType.ToString().ToLowerInvariant()), subType);
subTypeDropdown.AddItem(TextManager.Get("submarinetype.wreck"), SubmarineType.Wreck); }
//--------------------------------------- //---------------------------------------
var outpostSettingsContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), leftColumn.RectTransform)) var outpostSettingsContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), leftColumn.RectTransform))
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
CanBeFocused = true, CanBeFocused = true,
@@ -1853,6 +1870,10 @@ namespace Barotrauma
Submarine.MainSub.Info.Price = numberInput.IntValue; Submarine.MainSub.Info.Price = numberInput.IntValue;
} }
}; };
if (Submarine.MainSub?.Info != null)
{
Submarine.MainSub.Info.Price = Math.Max(Submarine.MainSub.Info.Price, basePrice);
}
if (!Submarine.MainSub.Info.HasTag(SubmarineTag.Shuttle)) if (!Submarine.MainSub.Info.HasTag(SubmarineTag.Shuttle))
{ {
@@ -2665,7 +2686,7 @@ namespace Barotrauma
foreach (GUIComponent child in entityList.Content.Children) foreach (GUIComponent child in entityList.Content.Children)
{ {
child.Visible = !entityCategory.HasValue || ((MapEntityPrefab) child.UserData).Category == entityCategory; child.Visible = !entityCategory.HasValue || ((MapEntityPrefab) child.UserData).Category.HasFlag(entityCategory);
if (child.Visible && dummyCharacter?.SelectedConstruction?.OwnInventory != null) if (child.Visible && dummyCharacter?.SelectedConstruction?.OwnInventory != null)
{ {
child.Visible = child.UserData is MapEntityPrefab item && IsItemPrefab(item); child.Visible = child.UserData is MapEntityPrefab item && IsItemPrefab(item);
@@ -2790,11 +2811,17 @@ namespace Barotrauma
if (PlayerInput.IsShiftDown()) if (PlayerInput.IsShiftDown())
{ {
new GUITextBlock(new RectTransform(Point.Zero, contextMenu.Content.RectTransform), new GUITextBlock(new RectTransform(Point.Zero, contextMenu.Content.RectTransform),
TextManager.Get("CharacterEditor.EditBackgroundColor"), font: GUI.SmallFont) TextManager.Get("SubEditor.EditBackgroundColor"), font: GUI.SmallFont)
{ {
UserData = "bgcolor" UserData = "bgcolor"
}; };
new GUITextBlock(new RectTransform(Point.Zero, contextMenu.Content.RectTransform),
TextManager.Get("SubEditor.ToggleTransparency"), font: GUI.SmallFont)
{
UserData = "transparency"
};
new GUITextBlock(new RectTransform(Point.Zero, contextMenu.Content.RectTransform), new GUITextBlock(new RectTransform(Point.Zero, contextMenu.Content.RectTransform),
TextManager.Get("editor.selectsame"), font: GUI.SmallFont) TextManager.Get("editor.selectsame"), font: GUI.SmallFont)
{ {
@@ -2870,6 +2897,9 @@ namespace Barotrauma
case "bgcolor": case "bgcolor":
CreateBackgroundColorPicker(); CreateBackgroundColorPicker();
break; break;
case "transparency":
TransparentWiringMode = !TransparentWiringMode;
break;
case "selectsame": case "selectsame":
IEnumerable<MapEntity> matching = MapEntity.mapEntityList.Where(e => e.prefab != null && targets.Any(t => t.prefab.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e)); IEnumerable<MapEntity> matching = MapEntity.mapEntityList.Where(e => e.prefab != null && targets.Any(t => t.prefab.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e));
MapEntity.SelectedList.AddRange(matching); MapEntity.SelectedList.AddRange(matching);
@@ -3710,6 +3740,24 @@ namespace Barotrauma
} }
} }
private static void CommitBulkItemBuffer()
{
if (BulkItemBuffer.Any())
{
AddOrDeleteCommand master = BulkItemBuffer[0];
for (int i = 1; i < BulkItemBuffer.Count; i++)
{
AddOrDeleteCommand command = BulkItemBuffer[i];
command.MergeInto(master);
}
StoreCommand(master);
BulkItemBuffer.Clear();
}
bulkItemBufferinUse = null;
}
/// <summary> /// <summary>
/// Allows the game to run logic such as updating the world, /// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio. /// checking for collisions, gathering input, and playing audio.
@@ -3939,6 +3987,11 @@ namespace Barotrauma
CloseItem(); CloseItem();
} }
if (lightingEnabled)
{
GameMain.LightManager?.Update((float)deltaTime);
}
if (contextMenu != null) if (contextMenu != null)
{ {
Rectangle expandedRect = contextMenu.Rect; Rectangle expandedRect = contextMenu.Rect;
@@ -4034,7 +4087,7 @@ namespace Barotrauma
// Deposit item from our "infinite stack" into inventory slots // Deposit item from our "infinite stack" into inventory slots
var inv = dummyCharacter?.SelectedConstruction?.OwnInventory; var inv = dummyCharacter?.SelectedConstruction?.OwnInventory;
if (inv?.slots != null) if (inv?.slots != null && !PlayerInput.IsCtrlDown())
{ {
var dragginMouse = MouseDragStart != Vector2.Zero && Vector2.Distance(PlayerInput.MousePosition, MouseDragStart) >= GUI.Scale * 20; var dragginMouse = MouseDragStart != Vector2.Zero && Vector2.Distance(PlayerInput.MousePosition, MouseDragStart) >= GUI.Scale * 20;
@@ -4090,7 +4143,7 @@ namespace Barotrauma
if (!newItem.Removed) if (!newItem.Removed)
{ {
BulkItemBufferInUse = true; BulkItemBufferInUse = ItemAddMutex;
BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { newItem }, false)); BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { newItem }, false));
} }
@@ -4162,7 +4215,7 @@ namespace Barotrauma
List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<MapEntity>().ToList(); List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<MapEntity>().ToList();
if (placedEntities.Any()) if (placedEntities.Any())
{ {
BulkItemBufferInUse = true; BulkItemBufferInUse = ItemAddMutex;
BulkItemBuffer.Add(new AddOrDeleteCommand(placedEntities, false)); BulkItemBuffer.Add(new AddOrDeleteCommand(placedEntities, false));
} }
} }
@@ -4174,18 +4227,9 @@ namespace Barotrauma
} }
} }
if (BulkItemBufferInUse && PlayerInput.PrimaryMouseButtonReleased() && BulkItemBuffer.Any()) if (PlayerInput.PrimaryMouseButtonReleased() && BulkItemBufferInUse != null)
{ {
AddOrDeleteCommand master = BulkItemBuffer[0]; CommitBulkItemBuffer();
for (int i = 1; i < BulkItemBuffer.Count; i++)
{
AddOrDeleteCommand command = BulkItemBuffer[i];
command.MergeInto(master);
}
StoreCommand(master);
BulkItemBuffer.Clear();
BulkItemBufferInUse = false;
} }
if (SerializableEntityEditor.PropertyChangesActive && (SerializableEntityEditor.NextCommandPush < DateTime.Now || MapEntity.EditingHUD == null)) if (SerializableEntityEditor.PropertyChangesActive && (SerializableEntityEditor.NextCommandPush < DateTime.Now || MapEntity.EditingHUD == null))
@@ -4326,7 +4370,7 @@ namespace Barotrauma
cam.UpdateTransform(); cam.UpdateTransform();
if (lightingEnabled) if (lightingEnabled)
{ {
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam); GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam);
} }
foreach (Submarine sub in Submarine.Loaded) foreach (Submarine sub in Submarine.Loaded)
@@ -4493,6 +4537,7 @@ namespace Barotrauma
stream.Dispose(); stream.Dispose();
} }
public static bool IsSubEditor() { return Screen.Selected is SubEditorScreen && !Submarine.Unloading; } public static bool IsSubEditor() => Screen.Selected is SubEditorScreen && !Submarine.Unloading;
public static bool IsWiringMode() => Screen.Selected == GameMain.SubEditorScreen && GameMain.SubEditorScreen.WiringMode && !Submarine.Unloading;
} }
} }
@@ -566,8 +566,13 @@ namespace Barotrauma
{ {
ToolTip = toolTip ToolTip = toolTip
}; };
bool isFlagsAttribute = value.GetType().IsDefined(typeof(FlagsAttribute), false);
foreach (object enumValue in Enum.GetValues(value.GetType())) foreach (object enumValue in Enum.GetValues(value.GetType()))
{ {
if (isFlagsAttribute && !MathHelper.IsPowerOfTwo((int)enumValue)) { continue; }
enumDropDown.AddItem(enumValue.ToString(), enumValue); enumDropDown.AddItem(enumValue.ToString(), enumValue);
if (((int)enumValue != 0 || (int)value == 0) && ((Enum)value).HasFlag((Enum)enumValue)) if (((int)enumValue != 0 || (int)value == 0) && ((Enum)value).HasFlag((Enum)enumValue))
{ {
@@ -625,7 +625,8 @@ namespace Barotrauma
Vector2 soundPos = new Vector2(GameMain.SoundManager.ListenerPosition.X + (fireVolumeRight[i] - fireVolumeLeft[i]) * 100, GameMain.SoundManager.ListenerPosition.Y); Vector2 soundPos = new Vector2(GameMain.SoundManager.ListenerPosition.X + (fireVolumeRight[i] - fireVolumeLeft[i]) * 100, GameMain.SoundManager.ListenerPosition.Y);
if (fireSoundChannels[i] == null || !fireSoundChannels[i].IsPlaying) if (fireSoundChannels[i] == null || !fireSoundChannels[i].IsPlaying)
{ {
fireSoundChannels[i] = GetSound(fireSoundTags[i]).Play(1.0f, FlowSoundRange, soundPos); fireSoundChannels[i] = GetSound(fireSoundTags[i])?.Play(1.0f, FlowSoundRange, soundPos);
if (fireSoundChannels[i] == null) { continue; }
fireSoundChannels[i].Looping = true; fireSoundChannels[i].Looping = true;
} }
fireSoundChannels[i].Gain = Math.Max(fireVolumeRight[i], fireVolumeLeft[i]); fireSoundChannels[i].Gain = Math.Max(fireVolumeRight[i], fireVolumeLeft[i]);
@@ -960,20 +961,18 @@ namespace Barotrauma
if (Level.IsLoadedOutpost && Character.Controlled.Submarine == Level.Loaded.StartOutpost) if (Level.IsLoadedOutpost && Character.Controlled.Submarine == Level.Loaded.StartOutpost)
{ {
// Only return music type for specific outpost types to not assume that // Only return music type for location types which have music tracks defined
// every outpost type has an associated music track (switch-case for future tracks)
var locationType = Level.Loaded.StartLocation?.Type?.Identifier?.ToLowerInvariant(); var locationType = Level.Loaded.StartLocation?.Type?.Identifier?.ToLowerInvariant();
switch (locationType) if (!string.IsNullOrEmpty(locationType) && musicClips.Any(c => c.Type == locationType))
{ {
case "research": return locationType;
return locationType;
} }
} }
} }
Submarine targetSubmarine = Character.Controlled?.Submarine; Submarine targetSubmarine = Character.Controlled?.Submarine;
if ((targetSubmarine != null && targetSubmarine.AtDamageDepth) || if ((targetSubmarine != null && targetSubmarine.AtDamageDepth) ||
(GameMain.GameScreen != null && Screen.Selected == GameMain.GameScreen && GameMain.GameScreen.Cam.Position.Y < SubmarineBody.DamageDepth)) (GameMain.GameScreen != null && Screen.Selected == GameMain.GameScreen && Level.Loaded != null && Level.Loaded.GetRealWorldDepth(GameMain.GameScreen.Cam.Position.Y) > Level.Loaded.RealWorldCrushDepth))
{ {
return "deep"; return "deep";
} }
@@ -1026,7 +1025,8 @@ namespace Barotrauma
{ {
return "levelend"; return "levelend";
} }
if (Timing.TotalTime < GameMain.GameSession.RoundStartTime + 120.0) if (Timing.TotalTime < GameMain.GameSession.RoundStartTime + 120.0 &&
Level.Loaded?.Type == LevelData.LevelType.LocationConnection)
{ {
return "start"; return "start";
} }
@@ -57,65 +57,9 @@ namespace Barotrauma
{ {
if (entity == null) { return; } if (entity == null) { return; }
if (sounds.Count > 0 && playSound) if (playSound)
{ {
if (soundChannel == null || !soundChannel.IsPlaying) PlaySound(entity, hull, worldPosition);
{
if (soundSelectionMode == SoundSelectionMode.All)
{
foreach (RoundSound sound in sounds)
{
if (sound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
soundChannel = SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, hullGuess: hull);
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
}
else
{
int selectedSoundIndex;
if (soundSelectionMode == SoundSelectionMode.ItemSpecific && entity is Item item)
{
selectedSoundIndex = item.ID % sounds.Count;
}
else if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && entity is Character user)
{
selectedSoundIndex = user.ID % sounds.Count;
}
else
{
selectedSoundIndex = Rand.Int(sounds.Count);
}
var selectedSound = sounds[selectedSoundIndex];
if (selectedSound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
if (selectedSound.Sound.Disposed)
{
Submarine.ReloadRoundSound(selectedSound);
}
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, worldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: hull);
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
}
else
{
soundChannel.Position = new Vector3(worldPosition, 0.0f);
}
if (soundChannel != null && soundChannel.Looping)
{
ActiveLoopingSounds.Add(this);
soundEmitter = entity;
loopStartTime = Timing.TotalTime;
}
} }
foreach (ParticleEmitter emitter in particleEmitters) foreach (ParticleEmitter emitter in particleEmitters)
@@ -151,6 +95,69 @@ namespace Barotrauma
} }
} }
private void PlaySound(Entity entity, Hull hull, Vector2 worldPosition)
{
if (sounds.Count == 0) return;
if (soundChannel == null || !soundChannel.IsPlaying)
{
if (soundSelectionMode == SoundSelectionMode.All)
{
foreach (RoundSound sound in sounds)
{
if (sound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
soundChannel = SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, hullGuess: hull);
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
}
else
{
int selectedSoundIndex;
if (soundSelectionMode == SoundSelectionMode.ItemSpecific && entity is Item item)
{
selectedSoundIndex = item.ID % sounds.Count;
}
else if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && entity is Character user)
{
selectedSoundIndex = user.ID % sounds.Count;
}
else
{
selectedSoundIndex = Rand.Int(sounds.Count);
}
var selectedSound = sounds[selectedSoundIndex];
if (selectedSound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
if (selectedSound.Sound.Disposed)
{
Submarine.ReloadRoundSound(selectedSound);
}
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, worldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: hull);
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
}
else
{
soundChannel.Position = new Vector3(worldPosition, 0.0f);
}
if (soundChannel != null && soundChannel.Looping)
{
ActiveLoopingSounds.Add(this);
soundEmitter = entity;
loopStartTime = Timing.TotalTime;
}
}
static partial void UpdateAllProjSpecific(float deltaTime) static partial void UpdateAllProjSpecific(float deltaTime)
{ {
bool doMuffleCheck = Timing.TotalTime > LastMuffleCheckTime + 0.2; bool doMuffleCheck = Timing.TotalTime > LastMuffleCheckTime + 0.2;
@@ -120,7 +120,8 @@ namespace Barotrauma
/// </summary> /// </summary>
/// <param name="receivers">Entities that were deleted or added</param> /// <param name="receivers">Entities that were deleted or added</param>
/// <param name="wasDeleted">Whether or not all entities are or are going to be deleted</param> /// <param name="wasDeleted">Whether or not all entities are or are going to be deleted</param>
public AddOrDeleteCommand(List<MapEntity> receivers, bool wasDeleted) /// <param name="handleInventoryBehavior">Ignore item inventories when set to false, workaround for pasting</param>
public AddOrDeleteCommand(List<MapEntity> receivers, bool wasDeleted, bool handleInventoryBehavior = true)
{ {
WasDeleted = wasDeleted; WasDeleted = wasDeleted;
Receivers = receivers; Receivers = receivers;
@@ -151,14 +152,17 @@ namespace Barotrauma
} }
} }
if (itemsToDelete.Any()) if (itemsToDelete.Any() && handleInventoryBehavior)
{ {
ContainedItemsCommand.Add(new AddOrDeleteCommand(itemsToDelete, true)); ContainedItemsCommand.Add(new AddOrDeleteCommand(itemsToDelete, wasDeleted));
foreach (MapEntity item in itemsToDelete) if (wasDeleted)
{ {
if (item != null && !item.Removed) foreach (MapEntity item in itemsToDelete)
{ {
item.Remove(); if (item != null && !item.Removed)
{
item.Remove();
}
} }
} }
} }
@@ -226,10 +230,10 @@ namespace Barotrauma
Debug.Assert(Receivers.All(entity => entity.GetReplacementOrThis().Removed), "Tried to redo a deletion but some items were not deleted"); Debug.Assert(Receivers.All(entity => entity.GetReplacementOrThis().Removed), "Tried to redo a deletion but some items were not deleted");
List<MapEntity> clones = MapEntity.Clone(CloneList); List<MapEntity> clones = MapEntity.Clone(CloneList);
for (int i = 0; i < Math.Min(Receivers.Count, clones.Count); i++) int length = Math.Min(Receivers.Count, clones.Count);
for (int i = 0; i < length; i++)
{ {
MapEntity clone = clones[i]; MapEntity clone = clones[i], receiver = Receivers[i];
MapEntity receiver = Receivers[i];
if (receiver.GetReplacementOrThis() is Item item && clone is Item cloneItem) if (receiver.GetReplacementOrThis() is Item item && clone is Item cloneItem)
{ {
@@ -252,6 +256,11 @@ namespace Barotrauma
} }
receiver.GetReplacementOrThis().ReplacedBy = clone; receiver.GetReplacementOrThis().ReplacedBy = clone;
}
for (int i = 0; i < length; i++)
{
MapEntity clone = clones[i], receiver = Receivers[i];
if (clone is Item it) if (clone is Item it)
{ {
@@ -19,6 +19,8 @@ namespace Barotrauma
private set; private set;
} }
private static volatile bool cancelAll = false;
public static void Init(GraphicsDevice graphicsDevice, bool needsBmp = false) public static void Init(GraphicsDevice graphicsDevice, bool needsBmp = false)
{ {
_graphicsDevice = graphicsDevice; _graphicsDevice = graphicsDevice;
@@ -36,6 +38,11 @@ namespace Barotrauma
}); });
} }
public static void CancelAll()
{
cancelAll = true;
}
private static byte[] CompressDxt5(byte[] data, int width, int height) private static byte[] CompressDxt5(byte[] data, int width, int height)
{ {
using (System.IO.MemoryStream mstream = new System.IO.MemoryStream()) using (System.IO.MemoryStream mstream = new System.IO.MemoryStream())
@@ -220,6 +227,7 @@ namespace Barotrauma
Texture2D tex = null; Texture2D tex = null;
CrossThread.RequestExecutionOnMainThread(() => CrossThread.RequestExecutionOnMainThread(() =>
{ {
if (cancelAll) { return; }
tex = new Texture2D(_graphicsDevice, width, height, mipmap, format); tex = new Texture2D(_graphicsDevice, width, height, mipmap, format);
tex.SetData(textureData); tex.SetData(textureData);
}); });
@@ -150,7 +150,7 @@ namespace Barotrauma
return Color.Black; return Color.Black;
} }
if (t <= 0.0f) { return gradient[0]; } if (t <= 0.0f || !MathUtils.IsValid(t)) { return gradient[0]; }
if (t >= 1.0f) { return gradient[gradient.Length - 1]; } if (t >= 1.0f) { return gradient[gradient.Length - 1]; }
float scaledT = t * (gradient.Length - 1); float scaledT = t * (gradient.Length - 1);
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.10.6.2</Version> <Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.10.6.2</Version> <Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.10.6.2</Version> <Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.10.6.2</Version> <Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.10.6.2</Version> <Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>

Some files were not shown because too many files have changed in this diff Show More