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

View File

@@ -328,9 +328,9 @@ namespace Barotrauma
{
previousOffset = offset;
}
//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
float unscaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount);
//zoom with resolution taken into account (zoom further out on smaller resolutions)
@@ -409,5 +409,15 @@ namespace Barotrauma
//Vector2 screenCoords = 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);
}
}
}

View File

@@ -5,8 +5,6 @@ namespace Barotrauma
{
partial class HumanAIController : AIController
{
public static bool debugai;
partial void InitProjSpecific()
{
/*if (GameMain.GameSession != null && GameMain.GameSession.CrewManager != null)

View File

@@ -520,7 +520,7 @@ namespace Barotrauma
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);
if (dist < maxDist * maxDist && (closestCharacter == null || dist < closestDist))
@@ -686,7 +686,7 @@ namespace Barotrauma
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)
{
@@ -810,7 +810,7 @@ namespace Barotrauma
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
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;
iconPos.Y = -iconPos.Y;
nameColor = iconStyle.Color;
@@ -835,7 +835,7 @@ namespace Barotrauma
var iconStyle = GUI.Style.GetComponentStyle("PetIcon." + petStatus);
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;
iconPos.Y = -iconPos.Y;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();

View File

@@ -11,15 +11,15 @@ namespace Barotrauma
{
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;
private static Item focusedItem;
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 Dictionary<string, string> cachedHudTexts = new Dictionary<string, string>();
private static readonly Dictionary<string, string> cachedHudTexts = new Dictionary<string, string>();
private static GUIFrame hudFrame;
public static GUIFrame HUDFrame
@@ -126,6 +126,11 @@ namespace Barotrauma
{
character.Inventory.Update(deltaTime, cam);
}
else
{
character.Inventory.ClearSubInventories();
}
for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
{
var item = character.Inventory.Items[i];
@@ -199,9 +204,18 @@ namespace Barotrauma
if (GameMain.GameSession?.CrewManager != null)
{
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)
@@ -218,14 +232,18 @@ namespace Barotrauma
foreach (Item brokenItem in brokenItems)
{
if (brokenItem.NonInteractable) { continue; }
float dist = Vector2.Distance(character.WorldPosition, brokenItem.WorldPosition);
Vector2 drawPos = brokenItem.DrawPosition;
float alpha = Math.Min((1000.0f - dist) / 1000.0f * 2.0f, 1.0f);
float alpha = GetDistanceBasedIconAlpha(brokenItem);
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);
}
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.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
@@ -362,8 +380,8 @@ namespace Barotrauma
(int)(HUDLayoutSettings.BottomRightInfoArea.X + HUDLayoutSettings.BottomRightInfoArea.Width * 0.05f),
(int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f),
(int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2),
(int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f)));
character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true);
(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.IsDisguisedAsAnother);
}
mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud();
if (mouseOnPortrait)
@@ -475,19 +493,12 @@ namespace Barotrauma
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.IsReport && order.OrderGiver != character && !order.HasAppropriateJob(character)) { return; }
if (order.TargetAllCharacters)
{
if (order.OrderGiver != character && !order.HasAppropriateJob(character))
{
return;
}
}
ISpatialEntity target = order.ConnectedController != null ? order.ConnectedController.Item : order.TargetSpatialEntity;
ISpatialEntity target = order.ConnectedController?.Item ?? order.TargetSpatialEntity;
if (target == null) { return; }
//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 :
target.Submarine == null ? target.Position : target.Position + target.Submarine.DrawPosition;
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;
}

View File

@@ -6,6 +6,8 @@ using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;
using System.IO;
using Barotrauma.Items.Components;
namespace Barotrauma
{
@@ -15,6 +17,12 @@ namespace Barotrauma
public bool LastControlled;
private Sprite disguisedPortrait;
private List<WearableSprite> disguisedAttachmentSprites;
private Vector2? disguisedSheetIndex;
private Sprite disguisedJobIcon;
private Color disguisedJobColor;
public static void Init()
{
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)
{
if (attachmentSprites == null)
@@ -219,23 +416,42 @@ namespace Barotrauma
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%
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);
if (AttachmentSprites != null)
portraitToDraw.Draw(spriteBatch, screenPos + offset, Color.White, portraitToDraw.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
if (attachmentsToDraw != null)
{
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;
}
}
@@ -258,30 +474,34 @@ namespace Barotrauma
float depthStep = 0.000001f;
foreach (var attachment in AttachmentSprites)
{
DrawAttachmentSprite(spriteBatch, attachment, headSprite, screenPos, scale, depthStep);
DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, 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; }
icon.Draw(spriteBatch, pos, Job.Prefab.UIColor, scale: scale);
}
public void DrawJobIcon(SpriteBatch spriteBatch, Rectangle area)
{
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));
Color iconColor = !IsDisguisedAsAnother || !evaluateDisguise ? Job.Prefab.UIColor : disguisedJobColor;
icon.Draw(spriteBatch, pos, iconColor, scale: scale);
}
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)
{
@@ -289,9 +509,9 @@ namespace Barotrauma
{
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
{

View File

@@ -416,8 +416,7 @@ namespace Barotrauma
Character character = null;
if (noInfo)
{
character = Create(speciesName, position, seed, null, false);
character.ID = id;
character = Create(speciesName, position, seed, characterInfo: null, id: id, isRemotePlayer: false);
bool containsStatusData = inc.ReadBoolean();
if (containsStatusData)
{
@@ -434,8 +433,7 @@ namespace Barotrauma
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
character = Create(speciesName, position, seed, info, ownerId > 0 && GameMain.Client.ID != ownerId, hasAi);
character.ID = id;
character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.ID != ownerId, hasAi: hasAi);
character.TeamID = (TeamType)teamID;
character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte();
if (character.CampaignInteractionType != CampaignMode.InteractionType.None)

View File

@@ -53,7 +53,7 @@ namespace Barotrauma
private GUIFrame healthWindow;
private GUIComponent deadIndicator;
private GUITextBlock deadIndicator;
private GUIComponent lowSkillIndicator;
@@ -225,13 +225,14 @@ namespace Barotrauma
Character.Controlled.ResetInteract = true;
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;
}
else
{
openHealthWindow.characterName.Text = value.Character.Info.DisplayName;
value.Character.Info.CheckDisguiseStatus(false);
}
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),
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,
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);
@@ -405,15 +414,17 @@ namespace Barotrauma
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)
{
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) =>
{
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)
{
@@ -422,7 +433,7 @@ namespace Barotrauma
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform),
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;
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 =
Character == Character.Controlled?.SelectedCharacter
&& (Character.IsUnconscious || Character.Stun > 0.0f)

View File

@@ -328,8 +328,7 @@ namespace Barotrauma
deformation = ragdoll.Limbs
.Where(l => l != null)
.SelectMany(l => l.Deformations)
.Where(d => d.TypeName == typeName && d.Sync == sync)
.FirstOrDefault();
.FirstOrDefault(d => d.TypeName == typeName && d.Sync == sync);
}
if (deformation == null)
{
@@ -971,11 +970,11 @@ namespace Barotrauma
XElement element;
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
{
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)
{

View File

@@ -491,7 +491,11 @@ namespace Barotrauma
{
SteamManager.NetworkingDebugLog = !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);
@@ -527,6 +531,7 @@ namespace Barotrauma
AssignRelayToServer("traitorlist", true);
AssignRelayToServer("money", true);
AssignRelayToServer("setskill", true);
AssignRelayToServer("readycheck", true);
AssignOnExecute("control", (string[] args) =>
{
@@ -547,14 +552,15 @@ namespace Barotrauma
AssignOnExecute("explosion", (string[] args) =>
{
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 > 1) float.TryParse(args[1], out force);
if (args.Length > 2) float.TryParse(args[2], out damage);
if (args.Length > 3) float.TryParse(args[3], out structureDamage);
if (args.Length > 4) float.TryParse(args[4], out itemDamage);
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) =>
@@ -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) =>
{
var character = Character.Controlled;

View File

@@ -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;
}
}
}

View File

@@ -2,7 +2,7 @@
namespace Barotrauma
{
partial class CombatMission
partial class CombatMission : Mission
{
public override string Description
{

View File

@@ -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);
}
}
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Barotrauma
{
partial class Mission
abstract partial class Mission
{
partial void ShowMessageProjSpecific(int missionState)
{

View File

@@ -3,7 +3,7 @@ using System;
namespace Barotrauma
{
partial class MissionMode : GameMode
abstract partial class MissionMode : GameMode
{
public override void ShowStartMessage()
{

View File

@@ -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;
}
}
}
}
}

View File

@@ -1256,17 +1256,26 @@ namespace Barotrauma
#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;
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)
{
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 angle = MathUtils.VectorToAngle(diff);

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Microsoft.Xna.Framework;
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));
}
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)

View File

@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -20,7 +21,8 @@ namespace Barotrauma
public enum Type
{
Default,
InGame
InGame,
Vote
}
public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
@@ -47,6 +49,9 @@ namespace Barotrauma
Icon.Color = value;
}
}
public bool Draggable { get; set; }
public Vector2 DraggingPosition = Vector2.Zero;
public GUIImage BackgroundIcon { get; private set; }
private GUIImage newBackgroundIcon;
@@ -60,7 +65,7 @@ namespace Barotrauma
private bool iconSwitching;
private bool closing;
private Type type;
private readonly Type type;
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);
this.type = type;
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 };
@@ -271,92 +289,114 @@ namespace Barotrauma
protected override void Update(float deltaTime)
{
if (type != Type.InGame) { return; }
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)
if (Draggable)
{
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton))
{
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))
GUI.MouseCursor = CursorState.Move;
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;
}
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;
GUI.MouseCursor = CursorState.Dragging;
RectTransform.ScreenSpaceOffset = (PlayerInput.MousePosition + DraggingPosition).ToPoint();
}
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);
DraggingPosition = Vector2.Zero;
}
}
if (type == Type.InGame)
{
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();
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);
}
}
}
}

View File

@@ -707,6 +707,8 @@ namespace Barotrauma
private void SortItems(GUIListBox list, SortingMethod sortingMethod)
{
if (CurrentLocation == null) { return; }
if (sortingMethod == SortingMethod.AlphabeticalAsc || sortingMethod == SortingMethod.AlphabeticalDesc)
{
list.Content.RectTransform.SortChildren(
@@ -868,13 +870,13 @@ namespace Barotrauma
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
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
{
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation.GetAdjustedItemBuyPrice(priceInfo));
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(priceInfo) ?? 0);
}
if (listBox == storeDealsList || listBox == storeBuyList || listBox == storeSellList)

View File

@@ -32,6 +32,7 @@ namespace Barotrauma
public Vector2 DrawPos { get; set; }
public int size = 10;
public float thickness = 1f;
/// <summary>
/// Used only for circles.
/// </summary>
@@ -157,7 +158,7 @@ namespace Barotrauma
{
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;
case Shape.Circle:
if (secondaryColor.HasValue)
@@ -182,7 +183,7 @@ namespace Barotrauma
{
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);
}
}

View File

@@ -525,6 +525,7 @@ namespace Barotrauma
Tutorials.Tutorial.Init();
MapGenerationParams.Init();
LevelGenerationParams.LoadPresets();
CaveGenerationParams.LoadPresets();
OutpostGenerationParams.LoadPresets();
WreckAIConfig.LoadAll();
EventSet.LoadPrefabs();
@@ -533,6 +534,7 @@ namespace Barotrauma
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
Order.Init();
EventManagerSettings.Init();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
TitleScreen.LoadState = 50.0f;
yield return CoroutineStatus.Running;
@@ -632,6 +634,7 @@ namespace Barotrauma
/// </summary>
protected override void UnloadContent()
{
TextureLoader.CancelAll();
CoroutineManager.StopCoroutines("Load");
Video.Close();
VoipCapture.Instance?.Dispose();
@@ -680,7 +683,7 @@ namespace Barotrauma
}
public void OnLobbyJoinRequested(Steamworks.Data.Lobby lobby, Steamworks.SteamId friendId)
{
{
SteamManager.JoinLobby(lobby.Id, true);
}

View File

@@ -199,7 +199,7 @@ namespace Barotrauma
var headset = GetHeadset(Character.Controlled, true);
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();
@@ -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())
{
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
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)
{
OnClicked = (GUIButton button, object userData) =>
@@ -667,6 +667,8 @@ namespace Barotrauma
#region Crew List Order Displayment
// TODO: CHECK ALL THE ORDER CONSTUCTOR CALLS
/// <summary>
/// Sets the character's current order (if it's close enough to receive messages from orderGiver) and
/// displays the order in the crew UI
@@ -675,16 +677,69 @@ namespace Barotrauma
{
if (order != null && order.TargetAllCharacters)
{
if (orderGiver == null || orderGiver.CurrentHull == null) { return; }
var hull = orderGiver.CurrentHull;
AddOrder(new Order(order.Prefab ?? order, hull, null, orderGiver), order.FadeOutTime);
Hull hull = null;
if (order.IsReport)
{
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)
{
orderGiver.Speak(order.GetChatMessage("", hull.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order);
orderGiver.Speak(order.GetChatMessage("", hull?.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order);
}
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);
}
}
@@ -696,7 +751,7 @@ namespace Barotrauma
if (IsSinglePlayer)
{
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)
{
@@ -1591,6 +1646,7 @@ namespace Barotrauma
private Character characterContext;
private Item itemContext;
private Hull hullContext;
private WallSection wallContext;
private bool isContextual;
private readonly List<Order> contextualOrders = new List<Order>();
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;
}
@@ -1662,16 +1718,24 @@ namespace Barotrauma
if (entityContext is Character character)
{
characterContext = character;
itemContext = null;
hullContext = null;
wallContext = null;
isContextual = false;
}
else if (entityContext is Item item)
{
itemContext = item;
characterContext = null;
hullContext = null;
wallContext = null;
isContextual = true;
}
else if (entityContext is Hull hull)
{
hullContext = hull;
characterContext = null;
itemContext = null;
isContextual = true;
}
@@ -1785,7 +1849,7 @@ namespace Barotrauma
availableCategories = new List<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);
}
@@ -2129,7 +2193,7 @@ namespace Barotrauma
if (Order.GetPrefab(orderIdentifier) is Order orderPrefab &&
shortcutNodes.None(n => (n.UserData is Order order && order.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())
{
@@ -2172,7 +2236,7 @@ namespace Barotrauma
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;
bool disableNode;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
@@ -2250,34 +2314,38 @@ namespace Barotrauma
if (contextualOrders.None())
{
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));
}
}
AddIgnoreOrder(itemContext);
}
else if (hullContext != null)
{
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)))
{
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 (characters.Any(c => c != Character.Controlled))
{
orderIdentifier = "follow";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
contextualOrders.Add(Order.GetPrefab(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),
!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
@@ -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 => item.HasTag(o.TargetItems))) { 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; }
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);
}
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o), o, null, Character.Controlled);
var character = !o.TargetAllCharacters ? characterContext ?? GetCharacterForQuickAssignment(o) : null;
SetCharacterOrder(character, o, null, Character.Controlled);
DisableCommandUI();
}
return true;
};
// TODO: Might need to edit the tooltip
var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color,
tooltip: mustSetOptionOrTarget || characterContext != null ? order.Name : order.Name +
"\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;
}
private bool TryGetBreachedHullAtHoveredWall(out Hull breachedHull)
private bool TryGetBreachedHullAtHoveredWall(out Hull breachedHull, out WallSection hoveredWall)
{
breachedHull = null;
hoveredWall = null;
// Based on the IsValidTarget() method of AIObjectiveFixLeaks class
List<Gap> leaks = Gap.GapList.FindAll(g =>
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))
{
breachedHull = leak.FlowTargetHull;
foreach (var section in leak.ConnectedWall.Sections)
{
if (Submarine.RectContains(section.WorldRect, mouseWorldPosition))
{
hoveredWall = section;
break;
}
}
return true;
}
}

View File

@@ -20,6 +20,7 @@ namespace Barotrauma
protected GUIButton endRoundButton;
public GUIButton ReadyCheckButton;
public GUIButton EndRoundButton => endRoundButton;
protected GUIFrame campaignUIContainer;
@@ -142,7 +143,8 @@ namespace Barotrauma
if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"))
{
endRoundButton.Visible = false;
endRoundButton.Visible = false;
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = false; }
return;
}
if (Submarine.MainSub == null) { return; }
@@ -188,6 +190,8 @@ namespace Barotrauma
break;
}
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = endRoundButton.Visible; }
if (endRoundButton.Visible)
{
if (!AllowedToEndRound()) { buttonText = TextManager.Get("map"); }
@@ -211,6 +215,10 @@ namespace Barotrauma
}
}
endRoundButton.DrawManually(spriteBatch);
if (this is MultiPlayerCampaign)
{
ReadyCheckButton?.DrawManually(spriteBatch);
}
}
public Task SelectSummaryScreen(RoundSummary roundSummary, LevelData newLevel, bool mirror, Action action)
@@ -279,6 +287,7 @@ namespace Barotrauma
base.AddToGUIUpdateList();
CrewManager.AddToGUIUpdateList();
endRoundButton.AddToGUIUpdateList();
ReadyCheckButton?.AddToGUIUpdateList();
}
public override void Update(float deltaTime)

View File

@@ -34,7 +34,6 @@ namespace Barotrauma
}
}
public static void StartCampaignSetup(IEnumerable<string> saveFiles)
{
var parent = GameMain.NetLobbyScreen.CampaignSetupFrame;
@@ -103,11 +102,13 @@ namespace Barotrauma
{
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")
{
Pulse = true,
@@ -140,6 +141,25 @@ namespace Barotrauma
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();
}
@@ -378,6 +398,7 @@ namespace Barotrauma
if (!GUI.DisableHUD && !GUI.DisableUpperHUD)
{
endRoundButton.UpdateManually(deltaTime);
ReadyCheckButton?.UpdateManually(deltaTime);
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || ForceMapUI) { return; }
}

View File

@@ -368,6 +368,9 @@ namespace Barotrauma
SoundPlayer.OverrideMusicDuration = 18.0f;
crewDead = false;
LevelData lvlData = GameMain.GameSession.LevelData;
bool beaconActive = GameMain.GameSession.Level.CheckBeaconActive();
GameMain.GameSession.EndRound("", traitorResults, transitionType);
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
RoundSummary roundSummary = null;
@@ -452,6 +455,8 @@ namespace Barotrauma
}
}
lvlData.IsBeaconActive = beaconActive;
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
}
else

View File

@@ -39,6 +39,10 @@ namespace Barotrauma
base.Start();
CrewManager.InitSinglePlayerRound();
foreach (Submarine submarine in Submarine.Loaded)
{
submarine.NeutralizeBallast();
}
if (SpawnOutpost)
{

View File

@@ -121,7 +121,7 @@ namespace Barotrauma.Tutorials
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.Controlled = character;
character.GiveJobItems(null);

View File

@@ -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;
};
}
}
}

View File

@@ -503,7 +503,7 @@ namespace Barotrauma
Character character = characterInfo.Character;
if (character == null || character.IsDead)
{
if (character == null && characterInfo.IsNewHire)
if (character == null && characterInfo.IsNewHire && characterInfo.CauseOfDeath == null)
{
statusText = TextManager.Get("CampaignCrew.NewHire");
statusColor = GUI.Style.Blue;

View File

@@ -65,10 +65,8 @@ namespace Barotrauma
keyMapping[(int)InputType.Voice] = new KeyOrMouse(Keys.V);
keyMapping[(int)InputType.LocalVoice] = new KeyOrMouse(Keys.B);
keyMapping[(int)InputType.Command] = new KeyOrMouse(MouseButton.MiddleMouse);
#if DEBUG
keyMapping[(int)InputType.PreviousFireMode] = new KeyOrMouse(MouseButton.MouseWheelDown);
keyMapping[(int)InputType.NextFireMode] = new KeyOrMouse(MouseButton.MouseWheelUp);
#endif
if (Language == "French")
{
@@ -1335,7 +1333,7 @@ namespace Barotrauma
};
msgBox.Buttons[0].OnClicked = (yesButton, obj) =>
{
LoadDefaultConfig(setLanguage: false);
LoadDefaultConfig(setLanguage: false, loadContentPackages: Screen.Selected != GameMain.GameScreen);
CheckBindings(true);
RefreshItemMessages();
ApplySettings();

View File

@@ -291,6 +291,7 @@ namespace Barotrauma.Items.Components
bool broken = msg.ReadBoolean();
bool forcedOpen = msg.ReadBoolean();
bool isStuck = msg.ReadBoolean();
bool isJammed = msg.ReadBoolean();
SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen);
stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
UInt16 lastUserID = msg.ReadUInt16();
@@ -301,6 +302,7 @@ namespace Barotrauma.Items.Components
toggleCooldownTimer = ToggleCoolDown;
}
this.isStuck = isStuck;
this.isJammed = isJammed;
if (isStuck) { OpenState = 0.0f; }
IsBroken = broken;
PredictedState = null;

View File

@@ -8,46 +8,6 @@ using Microsoft.Xna.Framework.Graphics;
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
{
[Serialize("0,0,0,0", false)]
@@ -97,7 +57,7 @@ namespace Barotrauma.Items.Components
foreach (VineTile vine in Vines)
{
leafDepth += zStep;
vine.Draw(spriteBatch, planter.Item.DrawPosition + offset, depth, leafDepth);
DrawBranch(vine, spriteBatch, planter.Item.DrawPosition + offset, depth, leafDepth);
}
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)
{

View File

@@ -2,9 +2,6 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Barotrauma.Items.Components
{
@@ -15,10 +12,16 @@ namespace Barotrauma.Items.Components
get { return item.Rect.Size.ToVector2(); }
}
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 roundedGridPos = new Vector2(
MathUtils.RoundTowardsClosest(picker.Position.X, Submarine.GridSize.X),

View File

@@ -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;
}
}

View File

@@ -55,13 +55,8 @@ namespace Barotrauma.Items.Components
{
if (character == null || !character.IsKeyDown(InputType.Aim)) return;
#if DEBUG
if (PlayerInput.KeyHit(InputType.PreviousFireMode))
#else
if (PlayerInput.MouseWheelDownClicked())
#endif
{
if (spraySetting > 0)
{
spraySetting--;
@@ -74,11 +69,7 @@ namespace Barotrauma.Items.Components
targetSections.Clear();
}
#if DEBUG
if (PlayerInput.KeyHit(InputType.NextFireMode))
#else
if (PlayerInput.MouseWheelUpClicked())
#endif
{
if (spraySetting < 2)
{

View File

@@ -257,6 +257,8 @@ namespace Barotrauma.Items.Components
spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
}
bool isWiringMode = SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode();
int i = 0;
foreach (Item containedItem in Inventory.Items)
{
@@ -278,7 +280,7 @@ namespace Barotrauma.Items.Components
containedItem.Sprite.Draw(
spriteBatch,
new Vector2(currentItemPos.X, -currentItemPos.Y),
containedItem.GetSpriteColor(),
isWiringMode ? containedItem.GetSpriteColor() * 0.15f : containedItem.GetSpriteColor(),
origin,
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation + MathHelper.ToRadians(-item.Rotation)),
containedItem.Scale,

View File

@@ -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.
// 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
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)
{
if (!(linkedTo is Item linkedItem)) continue;
if (!linkedItem.Components.Any()) continue;
if (!(linkedTo is Item linkedItem) || !linkedItem.DisplaySideBySideWhenLinked) { 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
var padding = (int) (8 * GUI.Scale);
// 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);
GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0);
// Move the linked container to the right and move the deconstructor to the left
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(100, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(-100, 0);
}
}
return base.Select(character);

View File

@@ -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.
// 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
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)
{
if (!(linkedTo is Item linkedItem)) continue;
if (!linkedItem.Components.Any()) continue;
var itemContainer = linkedItem.Components.First();
if (itemContainer == null) { continue; }
if (!(linkedTo is Item linkedItem) || !linkedItem.DisplaySideBySideWhenLinked) { continue; }
if (!linkedItem.Components.Any()) { 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
var padding = (int) (8 * GUI.Scale);
// 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);
GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0);
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(-100, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(100, 0);
}
}

View File

@@ -73,7 +73,7 @@ namespace Barotrauma.Items.Components
{
OnClicked = (button, data) =>
{
targetLevel = null;
TargetLevel = null;
IsActive = !IsActive;
if (GameMain.Client != null)
{
@@ -112,7 +112,7 @@ namespace Barotrauma.Items.Components
{
if (pumpSpeedLockTimer <= 0.0f)
{
targetLevel = null;
TargetLevel = null;
}
float newValue = barScroll * 200.0f - 100.0f;
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)
{
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)
{
StartDelayedCorrection(type, msg.ExtractBits(5 + 1), sendingTime);
int msgLength = msg.BitPosition - msgStartPos;
msg.BitPosition = msgStartPos;
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
return;
}
FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
IsActive = msg.ReadBoolean();
FlowPercentage = flowPercentage;
IsActive = isActive;
Hijacked = hijacked;
TargetLevel = targetLevel;
}
}
}

View File

@@ -502,8 +502,16 @@ namespace Barotrauma.Items.Components
{
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,
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)

View File

@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -14,7 +15,8 @@ namespace Barotrauma.Items.Components
public enum BlipType
{
Default,
Disruption
Disruption,
Destructible
}
private PathFinder pathFinder;
@@ -28,11 +30,18 @@ namespace Barotrauma.Items.Components
private GUITickBox activeTickBox, passiveTickBox;
private GUITextBlock signalWarningText;
private GUIFrame lowerAreaFrame;
private GUIScrollBar zoomSlider;
private GUIButton directionalModeSwitch;
private Vector2? pingDragDirection = null;
/// <summary>
/// Can be null if the property HasMineralScanner is false
/// </summary>
private GUIButton mineralScannerSwitch;
private GUIFrame controlContainer;
private GUICustomComponent sonarView;
@@ -46,7 +55,7 @@ namespace Barotrauma.Items.Components
private Sprite sonarBlip;
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;
@@ -60,10 +69,12 @@ namespace Barotrauma.Items.Components
private const float DisruptionUpdateInterval = 0.2f;
private float disruptionUpdateTimer;
private float zoomSqrt;
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
//float = strength of the disruption, between 0-1
private readonly List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>();
@@ -103,6 +114,10 @@ namespace Barotrauma.Items.Components
{
BlipType.Disruption,
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;
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)
{
System.Diagnostics.Debug.Assert(Enum.GetValues(typeof(BlipType)).Cast<BlipType>().All(t => blipColorGradient.ContainsKey(t)));
@@ -151,7 +175,9 @@ namespace Barotrauma.Items.Components
break;
case "icon":
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;
}
}
@@ -166,16 +192,20 @@ namespace Barotrauma.Items.Components
protected override void CreateGUI()
{
bool isConnectedToSteering = item.GetComponent<Steering>() != null;
Vector2 size = isConnectedToSteering ? controlBoxSize : new Vector2(controlBoxSize.X * 2.0f, controlBoxSize.Y);
isConnectedToSteering = item.GetComponent<Steering>() != null;
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)
{
AbsoluteOffset = GUIStyle.ItemFrameOffset
}, style: null);
// 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);
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);
activeTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor);
var lowerArea = 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), lowerArea.RectTransform, Anchor.TopCenter), style: null);
textBlocksToScaleAndNormalize.Clear();
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),
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)
{
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")
{
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),
TextManager.Get("SonarDirectionalPing"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft);
textBlocksToScaleAndNormalize.Add(directionalModeSwitchText);
if (AllowUsingMineralScanner)
{
AddMineralScannerSwitchToGUI();
}
else
{
mineralScannerSwitch = null;
}
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),
(spriteBatch, guiCustomComponent) => { DrawSonar(spriteBatch, guiCustomComponent.Rect); }, null);
@@ -271,11 +316,16 @@ namespace Barotrauma.Items.Components
if (isConnectedToSteering)
{
controlContainer.RectTransform.RelativeOffset = controlBoxOffset;
controlContainer.RectTransform.SetPosition(Anchor.TopLeft);
controlContainer.RectTransform.SetPosition(Anchor.TopRight);
sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest;
sonarView.RectTransform.SetPosition(Anchor.CenterRight);
sonarView.RectTransform.SetPosition(Anchor.CenterLeft);
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();
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
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)
{
showDirectionalIndicatorTimer -= deltaTime;
@@ -339,6 +437,32 @@ namespace Barotrauma.Items.Components
Vector2.DistanceSquared(sonarView.Rect.Center.ToVector2(), PlayerInput.MousePosition) <
(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)
{
return;
@@ -348,21 +472,45 @@ namespace Barotrauma.Items.Components
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>();
for (var pingIndex = 0; pingIndex < activePingsCount; ++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
foreach (LevelTrigger trigger in levelObject.Triggers)
{
Vector2 flow = trigger.GetWaterFlowVelocity();
//ignore ones that are barely doing anything (flow^2 < 1)
if (flow.LengthSquared() > 1.0f && !levelTriggerFlows.ContainsKey(trigger))
//ignore ones that are barely doing anything (flow^2 <= 1)
if (flow.LengthSquared() >= 1.0f && !levelTriggerFlows.ContainsKey(trigger))
{
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;
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)
{
if (!sub.ShowSonarMarker) { continue; }
@@ -967,8 +1145,7 @@ namespace Barotrauma.Items.Components
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{
var activePing = activePings[pingIndex];
foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(pingSource, range * activePing.State))
foreach (LevelObject levelObject in nearbyObjects)
{
if (levelObject.ActivePrefab?.SonarDisruption <= 0.0f) { continue; }
@@ -1101,9 +1278,9 @@ namespace Barotrauma.Items.Components
{
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);
if (cellDot > 0) continue;
if (cellDot > 0) { continue; }
float facingDot = Vector2.Dot(
Vector2.Normalize(edge.Point1 - edge.Point2),
@@ -1114,7 +1291,8 @@ namespace Barotrauma.Items.Components
edge.Point2 + cell.Translation,
pingSource, transducerPos,
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,
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;
zStep /= zoom;
@@ -1219,13 +1397,13 @@ namespace Barotrauma.Items.Components
//ignore if outside the display
Vector2 transducerDiff = point - transducerPos;
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
Vector2 pointDiff = point - pingSource;
Vector2 displayPointDiff = pointDiff * displayScale;
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
float transducerDist = transducerDiff.Length();
@@ -1240,7 +1418,7 @@ namespace Barotrauma.Items.Components
break;
}
}
if (disrupted) continue;
if (disrupted) { continue; }
float displayPointDist = (float)Math.Sqrt(displayPointDistSqr);
float alpha = pingStrength * Rand.Range(1.5f, 2.0f);
@@ -1252,8 +1430,8 @@ namespace Barotrauma.Items.Components
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);
var blip = new SonarBlip(pos, fadeTimer, 1.0f + ((displayPointDist + z) / DisplayRadius));
if (!passive && !CheckBlipVisibility(blip, transducerPos)) continue;
var blip = new SonarBlip(pos, fadeTimer, 1.0f + ((displayPointDist + z) / DisplayRadius), blipType);
if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; }
sonarBlips.Add(blip);
zStep += 0.5f / zoom;
@@ -1267,7 +1445,7 @@ namespace Barotrauma.Items.Components
alpha -= 0.1f;
}
if (alpha < 0) break;
if (alpha < 0) { break; }
}
}
}
@@ -1296,6 +1474,30 @@ namespace Barotrauma.Items.Components
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)
{
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);
}
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 dist = linearDist;
@@ -1391,16 +1594,27 @@ namespace Barotrauma.Items.Components
markerPos.Y = (int)markerPos.Y;
float alpha = 1.0f;
if (linearDist * scale < radius)
if (!onlyShowTextOnMouseOver)
{
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)
if (linearDist * scale < radius)
{
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
{
targetIcons[iconIdentifier].Draw(spriteBatch, markerPos);
var iconInfo = targetIcons[iconIdentifier];
iconInfo.Item1.Draw(spriteBatch, markerPos, iconInfo.Item2);
}
if (alpha <= 0.0f) { return; }
@@ -1441,11 +1656,13 @@ namespace Barotrauma.Items.Components
screenBackground?.Remove();
lineSprite?.Remove();
foreach (Sprite sprite in targetIcons.Values)
foreach (var t in targetIcons.Values)
{
sprite.Remove();
t.Item1.Remove();
}
targetIcons.Clear();
MineralClusters = null;
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
@@ -1460,6 +1677,7 @@ namespace Barotrauma.Items.Components
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
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;
bool directionalPing = useDirectionalPing;
float directionT = 0.0f;
bool mineralScanner = useMineralScanner;
if (isActive)
{
zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
@@ -1479,6 +1698,7 @@ namespace Barotrauma.Items.Components
{
directionT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
}
mineralScanner = msg.ReadBoolean();
}
if (correctionTimer > 0.0f)
@@ -1500,6 +1720,11 @@ namespace Barotrauma.Items.Components
pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle));
}
useDirectionalPing = directionalModeSwitch.Selected = directionalPing;
useMineralScanner = mineralScanner;
if (mineralScannerSwitch != null)
{
mineralScannerSwitch.Selected = mineralScanner;
}
}
}
@@ -1510,6 +1735,10 @@ namespace Barotrauma.Items.Components
passiveTickBox.Selected = !isActive;
activeTickBox.Selected = isActive;
directionalModeSwitch.Selected = useDirectionalPing;
if (mineralScannerSwitch != null)
{
mineralScannerSwitch.Selected = useMineralScanner;
}
}
}

View File

@@ -112,7 +112,7 @@ namespace Barotrauma.Items.Components
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)
{
AbsoluteOffset = GUIStyle.ItemFrameOffset
@@ -265,7 +265,7 @@ namespace Barotrauma.Items.Components
levelStartSelected ? Destination.LevelStart : Destination.LevelEnd);
// 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
}, "ItemUI");
@@ -319,8 +319,7 @@ namespace Barotrauma.Items.Components
centerText = $"({TextManager.Get("Meter")})";
rightTextGetter = () =>
{
Vector2 pos = controlledSub == null ? Vector2.Zero : controlledSub.Position;
float realWorldDepth = Level.Loaded == null ? 0.0f : Math.Abs(pos.Y - Level.Loaded.Size.Y) * Physics.DisplayToRealWorldRatio;
float realWorldDepth = controlledSub == null ? -1000.0f : controlledSub.RealWorldDepth;
return ((int)realWorldDepth).ToString();
};
break;
@@ -340,9 +339,9 @@ namespace Barotrauma.Items.Components
//docking interface ----------------------------------------------------
float dockingButtonSize = 1.1f;
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);
dockText = TextManager.Get("label.navterminaldock", fallBackTag: "captain.dock");
@@ -437,7 +436,7 @@ namespace Barotrauma.Items.Components
};
// 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);
steerRadius = steerArea.Rect.Width / 2;

View File

@@ -1,8 +1,7 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Xml.Linq;
using System.Linq;
namespace Barotrauma.Items.Components
{
@@ -10,6 +9,23 @@ namespace Barotrauma.Items.Components
{
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();
if (isStuck)
{
@@ -56,7 +72,15 @@ namespace Barotrauma.Items.Components
else if (entity is Item item)
{
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)
{

View File

@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
using System.Xml.Linq;
@@ -70,7 +69,13 @@ namespace Barotrauma.Items.Components
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());
}
@@ -114,11 +119,6 @@ namespace Barotrauma.Items.Components
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
if (!string.IsNullOrEmpty(DisplayedWelcomeMessage))
{
ShowOnDisplay(DisplayedWelcomeMessage);
DisplayedWelcomeMessage = "";
}
if (shouldSelectInputBox)
{
inputBox.Select();

View File

@@ -117,9 +117,9 @@ namespace Barotrauma.Items.Components
{
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;
}
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)
{
@@ -261,7 +261,7 @@ namespace Barotrauma.Items.Components
}
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);
}
}
}

View File

@@ -247,7 +247,7 @@ namespace Barotrauma.Items.Components
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();
}
@@ -286,75 +286,52 @@ namespace Barotrauma.Items.Components
rotation + MathHelper.PiOver2, item.Scale,
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;
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);
const float widgetRadius = 60.0f;
Vector2 center = new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2));
GUI.DrawLine(spriteBatch,
drawPos,
drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * widgetRadius,
Color.LightGreen);
Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: 10, initMethod: (widget) =>
if (GameMain.DebugDraw)
{
widget.Selected += () =>
{
oldRotation = RotationLimits;
};
widget.MouseDown += () =>
{
widget.color = GUI.Style.Green;
prevAngle = minRotation;
};
widget.Deselected += () =>
{
widget.color = Color.Yellow;
item.CreateEditingHUD();
if (SubEditorScreen.IsSubEditor())
{
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
}
};
widget.MouseHeld += (deltaTime) =>
{
minRotation = GetRotationAngle(GetDrawPos());
if (minRotation > maxRotation)
{
float temp = minRotation;
minRotation = maxRotation;
maxRotation = temp;
}
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)) * widgetRadius;
widget.Update(deltaTime);
};
});
Widget maxRotationWidget = GetWidget("maxrotation", spriteBatch, size: 10, initMethod: (widget) =>
center = new Vector2((float)Math.Cos(targetRotation), (float)Math.Sin(targetRotation));
GUI.DrawLine(spriteBatch,
drawPos,
drawPos + center * widgetRadius,
Color.Red);
for (int i = 0; i < 5; i++)
{
center = new Vector2((float)Math.Cos(rotation + (angularVelocity * 0.05f * i)), (float)Math.Sin(rotation + (angularVelocity * 0.05f * i)));
GUI.DrawLine(spriteBatch,
drawPos,
drawPos + center * widgetRadius,
Color.Lerp(Color.Black, Color.Yellow, i * 0.25f));
}
}
const float coneRadius = 300.0f;
float radians = maxRotation - minRotation;
float circleRadius = coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
float lineThickness = 1f / Screen.Selected.Cam.Zoom;
if (radians > Math.PI * 2)
{
spriteBatch.DrawCircle(drawPos, circleRadius, 180, GUI.Style.Red, thickness: lineThickness);
}
else
{
spriteBatch.DrawSector(drawPos, circleRadius, radians, (int)Math.Abs(90 * radians), GUI.Style.Green, offset: minRotation, thickness: lineThickness);
}
int baseWidgetScale = GUI.IntScale(16);
int widgetSize = (int) (Math.Max(baseWidgetScale, baseWidgetScale / Screen.Selected.Cam.Zoom));
float widgetThickness = Math.Max(1f, lineThickness);
Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: widgetSize, thickness: widgetThickness, initMethod: (widget) =>
{
widget.Selected += () =>
{
@@ -369,6 +346,52 @@ namespace Barotrauma.Items.Components
{
widget.color = Color.Yellow;
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())
{
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
@@ -377,12 +400,7 @@ namespace Barotrauma.Items.Components
widget.MouseHeld += (deltaTime) =>
{
maxRotation = GetRotationAngle(GetDrawPos());
if (minRotation > maxRotation)
{
float temp = minRotation;
minRotation = maxRotation;
maxRotation = temp;
}
UpdateBarrel();
MapEntity.DisableSelect = true;
};
widget.PreUpdate += (deltaTime) =>
@@ -398,7 +416,7 @@ namespace Barotrauma.Items.Components
widget.PreDraw += (sprtBtch, deltaTime) =>
{
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);
};
});
@@ -412,22 +430,32 @@ namespace Barotrauma.Items.Components
drawPos.Y = -drawPos.Y;
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))
{
widget = new Widget(id, size, Widget.Shape.Rectangle)
{
color = Color.Yellow,
tooltipOffset = new Vector2(size / 2 + 5, -10),
tooltipOffset = offset,
inputAreaMargin = 20,
RequireMouseOn = false
};
widgets.Add(id, widget);
initMethod?.Invoke(widget);
}
widget.size = size;
widget.tooltipOffset = offset;
widget.thickness = thickness;
return widget;
}
@@ -580,7 +608,6 @@ namespace Barotrauma.Items.Components
}
Launch(projectile, launchRotation: newTargetRotation);
}
}
}
}

View File

@@ -439,7 +439,7 @@ namespace Barotrauma
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.
if (selectedSlot != null && !DraggingItemToWorld)
if (selectedSlot != null && !DraggingItemToWorld && cam.GetZoomAmountFromPrevious() <= 0.25f)
{
cam.Freeze = true;
}
@@ -461,7 +461,7 @@ namespace Barotrauma
}*/
bool mouseOn = interactRect.Contains(PlayerInput.MousePosition) && !Locked && !mouseOnGUI && !slot.Disabled;
// Delete item from container in sub editor
if (SubEditorScreen.IsSubEditor() && PlayerInput.IsCtrlDown())
{
@@ -477,8 +477,12 @@ namespace Barotrauma
SoundPlayer.PlayUISound(GUISoundType.PickItem);
}
SubEditorScreen.BulkItemBufferInUse = true;
SubEditorScreen.BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { item }, true));
if (!item.Removed)
{
SubEditorScreen.BulkItemBufferInUse = SubEditorScreen.ItemRemoveMutex;
SubEditorScreen.BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { item }, true));
}
item.OwnInventory?.DeleteAllItems();
item.Remove();
}
@@ -688,6 +692,18 @@ namespace Barotrauma
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)
{
if (slots == null || isSubInventory != subInventory) return;

View File

@@ -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
@@ -93,6 +93,11 @@ namespace Barotrauma
}
}
public float GetDrawDepth()
{
return GetDrawDepth(SpriteDepth, Sprite);
}
public Color GetSpriteColor()
{
Color color = spriteColor;
@@ -239,6 +244,10 @@ namespace Barotrauma
Color color = IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange : GetSpriteColor();
//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;
float fadeInBrokenSpriteAlpha = 0.0f;
@@ -269,6 +278,7 @@ namespace Barotrauma
}
float depth = GetDrawDepth();
if (isWiringMode && isLogic && !PlayerInput.IsShiftDown()) { depth = 0.01f; }
if (activeSprite != null)
{
SpriteEffects oldEffects = activeSprite.effects;
@@ -296,6 +306,8 @@ namespace Barotrauma
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
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,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
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);
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)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
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,
rotationRad + rot, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
@@ -367,7 +386,8 @@ namespace Barotrauma
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
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 sa = (float)Math.Sin(-body.Rotation);
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; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
var (xOff, yOff) = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + xOff, -(DrawPosition.Y + yOff)), color,
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,
rotation, decorativeSprite.Scale * Scale, activeSprite.effects,
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))
{
return;
@@ -505,7 +534,19 @@ namespace Barotrauma
}
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 (!Linkable) { return; }
@@ -1341,6 +1382,10 @@ namespace Barotrauma
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;
Submarine sub = null;
int itemContainerIndex = -1;
@@ -1369,16 +1414,24 @@ namespace Barotrauma
string tags = "";
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)
{
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
};
@@ -1442,6 +1494,11 @@ namespace Barotrauma
}
if (descriptionChanged) { item.Description = itemDesc; }
if (tagsChanged) { item.Tags = tags; }
var nameTag = item.GetComponent<NameTag>();
if (nameTag != null)
{
nameTag.WrittenName = writtenName;
}
if (sub != null)
{

View File

@@ -56,6 +56,8 @@ namespace Barotrauma
public Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
public Sprite InventoryIcon;
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
[Serialize("1.0,1.0,1.0,1.0", false)]

View File

@@ -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
};
}
}
}

View File

@@ -36,8 +36,8 @@ namespace Barotrauma
Rand.Range(worldPosition.Y - size.Y, worldPosition.Y + 20.0f));
Vector2 particleVel = new Vector2(
(particlePos.X - (worldPosition.X + size.X / 2.0f)),
(float)Math.Sqrt(size.X) * Rand.Range(0.0f, 15.0f) * growModifier);
particlePos.X - (worldPosition.X + size.X / 2.0f),
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);

View File

@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.MapCreatures.Behavior;
namespace Barotrauma
{
@@ -48,7 +49,7 @@ namespace Barotrauma
{
get
{
return decals.Count > 0;
return decals.Count > 0 || BallastFlora != null;
}
}
@@ -65,6 +66,8 @@ namespace Barotrauma
public override bool IsVisible(Rectangle worldView)
{
if (BallastFlora != null) { return true; }
if (Screen.Selected != GameMain.SubEditorScreen && !GameMain.DebugDraw)
{
if (decals.Count == 0 && paintAmount < minimumPaintAmountToDraw) { return false; }
@@ -229,6 +232,7 @@ namespace Barotrauma
{
if (back && Screen.Selected != GameMain.SubEditorScreen)
{
BallastFlora?.Draw(spriteBatch);
DrawDecals(spriteBatch);
return;
}
@@ -244,7 +248,7 @@ namespace Barotrauma
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);
if ((IsSelected || IsHighlighted) && editing)
@@ -332,6 +336,7 @@ namespace Barotrauma
public void DrawSectionColors(SpriteBatch spriteBatch)
{
if (BackgroundSections == null || BackgroundSections.Count == 0) { return; }
Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
Point sectionSize = BackgroundSections[0].Rect.Size;
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)
{
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;
remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);

View File

@@ -1,30 +1,38 @@
using Microsoft.Xna.Framework;
using Barotrauma.SpriteDeformations;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma
{
class BackgroundCreature : ISteerable
{
const float MaxDepth = 100.0f;
const float MaxDepth = 10000.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 Vector3 velocity;
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 Vector2 obstacleDiff;
@@ -33,9 +41,16 @@ namespace Barotrauma
public Swarm Swarm;
Vector2 drawPosition;
public Vector2 TransformedPosition
public Vector2[,] CurrentSpriteDeformation
{
get { return drawPosition; }
get;
private set;
}
public Vector2[,] CurrentLightSpriteDeformation
{
get;
private set;
}
public Vector2 SimPosition
@@ -58,11 +73,10 @@ namespace Barotrauma
get;
set;
}
public BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position)
{
this.prefab = prefab;
this.Prefab = prefab;
this.position = position;
drawPosition = position;
@@ -70,18 +84,73 @@ namespace Barotrauma
steeringManager = new SteeringManager(this);
velocity = new Vector3(
Rand.Range(-prefab.Speed, prefab.Speed),
Rand.Range(-prefab.Speed, prefab.Speed),
Rand.Range(0.0f, prefab.WanderZAmount));
Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly),
Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly),
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)
{
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;
if (checkWallsTimer <= 0.0f && Level.Loaded != null)
@@ -134,54 +203,145 @@ namespace Barotrauma
float midPointDist = Vector2.Distance(SimPosition, midPoint) * 100.0f;
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);
}
if (prefab.WanderAmount > 0.0f)
if (Prefab.WanderAmount > 0.0f)
{
steeringManager.SteeringWander(prefab.Speed);
steeringManager.SteeringWander(Prefab.Speed);
}
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);
velocity.Z = (float)Math.Sin(wanderZPhase) * prefab.Speed;
wanderZPhase += Rand.Range(-Prefab.WanderZAmount, Prefab.WanderZAmount);
velocity.Z = (float)Math.Sin(wanderZPhase) * Prefab.Speed;
}
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)
{
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;
if (!prefab.DisableRotation)
if (!Prefab.DisableRotation)
{
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;
if (depth > 0.0f)
drawPosition = GetDrawPosition(cam);
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;
drawPosition -= camOffset * (depth / MaxDepth) * 0.05f;
drawPosition -= camOffset * depth / MaxDepth;
}
return drawPosition;
}
prefab.Sprite.Draw(spriteBatch,
new Vector2(drawPosition.X, -drawPosition.Y),
Color.Lerp(Color.White, Level.Loaded.BackgroundColor, (depth / MaxDepth) * 0.2f),
rotation, (1.0f - (depth / MaxDepth) * 0.2f) * prefab.Scale,
velocity.X > 0.0f ? SpriteEffects.None : SpriteEffects.FlipHorizontally,
(depth / MaxDepth));
public float GetScale()
{
return Math.Max(1.0f - depth / MaxDepth, 0.05f) * Prefab.Scale;
}
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);
}
}
}

View File

@@ -9,14 +9,14 @@ namespace Barotrauma
{
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 List<BackgroundCreature> activeSprites = new List<BackgroundCreature>();
private readonly List<BackgroundCreaturePrefab> prefabs = new List<BackgroundCreaturePrefab>();
private readonly List<BackgroundCreature> creatures = new List<BackgroundCreature>();
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;
if (position == null)
{
var wayPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine==null);
var wayPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine == null);
if (wayPoints.Any())
{
WayPoint wp = wayPoints[Rand.Int(wayPoints.Count, Rand.RandSync.ClientOnly)];
pos = new Vector2(wp.Rect.X, wp.Rect.Y);
pos += Rand.Vector(200.0f, Rand.RandSync.ClientOnly);
}
else
{
pos = Rand.Vector(2000.0f, Rand.RandSync.ClientOnly);
}
}
}
else
{
pos = (Vector2)position;
}
var prefab = prefabs[Rand.Int(prefabs.Count, Rand.RandSync.ClientOnly)];
var prefab = ToolBox.SelectWeightedRandom(availablePrefabs, availablePrefabs.Select(p => p.GetCommonness(level.GenerationParams)).ToList(), Rand.RandSync.ClientOnly);
if (prefab == null) { break; }
int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax, Rand.RandSync.ClientOnly);
List<BackgroundCreature> swarmMembers = new List<BackgroundCreature>();
for (int n = 0; n < amount; n++)
{
var newSprite = new BackgroundCreature(prefab, pos);
activeSprites.Add(newSprite);
swarmMembers.Add(newSprite);
var creature = new BackgroundCreature(prefab, pos + Rand.Vector(Rand.Range(0.0f, prefab.SwarmRadius, Rand.RandSync.ClientOnly), Rand.RandSync.ClientOnly));
creatures.Add(creature);
swarmMembers.Add(creature);
}
if (amount > 0)
if (amount > 1)
{
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)
{
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 &&
Math.Abs(sprite.TransformedPosition.Y - cam.WorldViewCenter.Y) < cam.WorldView.Height;
Rectangle extents = creature.GetExtents(cam);
bool wasVisible = creature.Visible;
creature.Visible =
extents.Right >= cam.WorldView.X - margin &&
extents.X <= cam.WorldView.Right + margin &&
extents.Bottom >= cam.WorldView.Y - cam.WorldView.Height - margin &&
extents.Y <= cam.WorldView.Y + margin;
}
checkActiveTimer = CheckActiveInterval;
checkVisibleTimer = VisibilityCheckInterval;
}
else
{
checkActiveTimer -= deltaTime;
checkVisibleTimer -= deltaTime;
}
foreach (BackgroundCreature sprite in activeSprites)
foreach (BackgroundCreature creature in creatures)
{
if (!sprite.Enabled) continue;
sprite.Update(deltaTime);
if (!creature.Visible) { continue; }
creature.Update(deltaTime);
}
}
public void Draw(SpriteBatch spriteBatch, Camera cam)
{
foreach (BackgroundCreature sprite in activeSprites)
foreach (BackgroundCreature creature in creatures)
{
if (!sprite.Enabled) continue;
sprite.Draw(spriteBatch, cam);
if (!creature.Visible) { continue; }
creature.Draw(spriteBatch, cam);
}
}
public void DrawLights(SpriteBatch spriteBatch, Camera cam)
{
foreach (BackgroundCreature creature in creatures)
{
if (!creature.Visible) { continue; }
creature.DrawLightSprite(spriteBatch, cam);
}
}
}

View File

@@ -1,50 +1,117 @@
using System.Xml.Linq;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma
{
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;
public readonly float SwarmRadius, SwarmCohesion;
[Serialize(0.0f, true)]
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)
{
Speed = element.GetAttributeFloat("speed", 1.0f);
Name = element.Name.ToString();
WanderAmount = element.GetAttributeFloat("wanderamount", 0.0f);
Config = element;
WanderZAmount = element.GetAttributeFloat("wanderzamount", 0.0f);
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);
SerializableProperty.DeserializeProperties(this, element);
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("sprite", System.StringComparison.OrdinalIgnoreCase)) { continue; }
Sprite = new Sprite(subElement, lazyLoad: true);
break;
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
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;
}
}
}

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
@@ -8,72 +9,82 @@ namespace Barotrauma
{
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++)
{
foreach (Vector2 vertex in triangles[i])
{
//shift the coordinates around a bit to make the texture repetition less obvious
Vector2 uvCoords = new Vector2(
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));
Vector2 uvCoords = vertex / generationParams.WallTextureSize;
vertices.Add(new VertexPositionTexture(new Vector3(vertex, zCoord), 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)
{
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)
{
if (edge.Cell1 != null && edge.Cell1.Body == null && edge.Cell1.CellType != CellType.Empty) edge.Cell1 = null;
if (edge.Cell2 != null && edge.Cell2.Body == null && edge.Cell2.CellType != CellType.Empty) edge.Cell2 = null;
if (compare.Compare(edge.Point1, edge.Point2) == -1)
{
var temp = edge.Point1;
edge.Point1 = edge.Point2;
edge.Point2 = temp;
}
circumference += Vector2.Distance(edge.Point1, edge.Point2);
minVert = new Vector2(
Math.Min(minVert.X, edge.Point1.X),
Math.Min(minVert.Y, edge.Point1.Y));
maxVert = new Vector2(
Math.Max(maxVert.X, edge.Point1.X),
Math.Max(maxVert.Y, edge.Point1.Y));
}
}
foreach (VoronoiCell cell in cells)
{
Vector2 center = (minVert + maxVert) / 2;
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 rightEdge = cell.Edges.Find(e => e != edge && (edge.Point2 == e.Point1 || edge.Point2 == e.Point2));
GraphEdge leftEdge = cell.Edges.Find(e => e != edge && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(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;
float inwardThickness1 = 100;
float inwardThickness2 = 100;
float inwardThickness1 = level.GenerationParams.WallEdgeExpandInwardsAmount;
float inwardThickness2 = level.GenerationParams.WallEdgeExpandInwardsAmount;
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.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
{
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))
{
@@ -86,7 +97,7 @@ namespace Barotrauma
if (cell.Body != null)
{
GameMain.World.Remove(cell.Body);
if (GameMain.World.BodyList.Contains(cell.Body)) { GameMain.World.Remove(cell.Body); }
cell.Body = null;
}
leftNormal = Vector2.UnitX;
@@ -95,16 +106,20 @@ namespace Barotrauma
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.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
{
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))
{
@@ -117,24 +132,23 @@ namespace Barotrauma
if (cell.Body != null)
{
GameMain.World.Remove(cell.Body);
if (GameMain.World.BodyList.Contains(cell.Body)) { GameMain.World.Remove(cell.Body); }
cell.Body = null;
}
rightNormal = Vector2.UnitX;
break;
}
float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - cell.Center));
float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - cell.Center));
float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - center));
float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - center));
//handle wrapping around 0/360
if (point1UV - point2UV > MathHelper.Pi)
{
point2UV += MathHelper.TwoPi;
if (point1UV - point2UV > MathHelper.Pi)
{
point1UV -= MathHelper.TwoPi;
}
//the texture wraps around the cell 4 times
//TODO: define the uv scale in level generation parameters?
point1UV = point1UV / MathHelper.TwoPi * 4;
point2UV = point2UV / MathHelper.TwoPi * 4;
int textureRepeatCount = (int)Math.Max(circumference / 2 / level.GenerationParams.WallEdgeTextureWidth, 1);
point1UV = point1UV / MathHelper.TwoPi * textureRepeatCount;
point2UV = point2UV / MathHelper.TwoPi * textureRepeatCount;
for (int i = 0; i < 2; i++)
{
@@ -147,9 +161,9 @@ namespace Barotrauma
verts[1] = edge.Point2 - rightNormal * outWardThickness;
verts[2] = edge.Point1 + leftNormal * inwardThickness1;
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(point1UV, 0.0f));
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), new Vector2(point2UV, 0.0f));
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(point1UV, 0.5f));
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 0.0f));
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point1UV, 1.0f));
}
else
{
@@ -158,16 +172,16 @@ namespace Barotrauma
verts[1] = edge.Point2 - rightNormal * outWardThickness;
verts[2] = edge.Point2 + rightNormal * inwardThickness2;
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(point1UV, 0.5f));
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), new Vector2(point2UV, 0.0f));
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(point2UV, 0.5f));
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 1.0f));
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point2UV, 1.0f));
}
verticeList.AddRange(vertPos);
vertices.AddRange(vertPos);
}
}
}
return verticeList.ToArray();
return vertices;
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -14,6 +14,8 @@ namespace Barotrauma
private BackgroundCreatureManager backgroundCreatureManager;
public BackgroundCreatureManager BackgroundCreatureManager => backgroundCreatureManager;
public LevelRenderer Renderer => renderer;
public void ReloadTextures()
@@ -33,14 +35,6 @@ namespace Barotrauma
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)
@@ -51,7 +45,7 @@ namespace Barotrauma
public void DrawFront(SpriteBatch spriteBatch, Camera cam)
{
if (renderer == null) return;
if (renderer == null) { return; }
renderer.Draw(spriteBatch, cam);
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
@@ -123,22 +117,34 @@ namespace Barotrauma
if (renderer == null) return;
renderer.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager);
}
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;
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)
foreach (LevelWall levelWall in ExtraWalls)
{
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);
}
}
}

View File

@@ -1,14 +1,14 @@
using Barotrauma.Lights;
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Barotrauma.Networking;
using Barotrauma.SpriteDeformations;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.SpriteDeformations;
using System.Linq;
using FarseerPhysics.Dynamics;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -74,10 +74,21 @@ namespace Barotrauma
private set;
}
public bool VisibleOnSonar
{
get;
private set;
}
public float SonarRadius
{
get;
private set;
}
partial void InitProjSpecific()
{
Sprite?.EnsureLazyLoaded();
SpecularSprite?.EnsureLazyLoaded();
Prefab.DeformableSprite?.EnsureLazyLoaded();
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];
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)
@@ -232,17 +250,21 @@ namespace Barotrauma
deformation.Update(deltaTime);
}
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)
{
if (Triggers == null) { return; }
Matrix matrix = ActivePrefab.DeformableSprite.GetTransform(
Position,
ActivePrefab.DeformableSprite.Origin,
@@ -258,7 +280,7 @@ namespace Barotrauma
Vector2 moveAmount = triggerer.WorldPosition - trigger.TriggererPosition[triggerer];
moveAmount = Vector2.Transform(moveAmount, rotationMatrix);
moveAmount /= (ActivePrefab.DeformableSprite.Size * Scale);
moveAmount /= ActivePrefab.DeformableSprite.Size * Scale;
moveAmount.Y = -moveAmount.Y;
positionalDeformation.Deform(trigger.WorldPosition, moveAmount, deltaTime, Matrix.Invert(matrix) *
@@ -269,9 +291,10 @@ namespace Barotrauma
public void ClientRead(IReadMessage msg)
{
if (Triggers == null) { return; }
for (int i = 0; i < Triggers.Count; i++)
{
if (!Triggers[i].UseNetworkSyncing) continue;
if (!Triggers[i].UseNetworkSyncing) { continue; }
Triggers[i].ClientRead(msg);
}
}

View File

@@ -12,6 +12,8 @@ namespace Barotrauma
private readonly List<LevelObject> visibleObjectsBack = 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,
//but small enough to prevent wrecking performance when zooming out very far
const int MaxVisibleObjects = 500;
@@ -38,11 +40,13 @@ namespace Barotrauma
/// <summary>
/// Checks which level objects are in camera view and adds them to the visibleObjects lists
/// </summary>
private void RefreshVisibleObjects(Rectangle currentIndices)
private void RefreshVisibleObjects(Rectangle currentIndices, float zoom)
{
visibleObjectsBack.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 y = currentIndices.Y; y <= currentIndices.Height; y++)
@@ -50,6 +54,22 @@ namespace Barotrauma
if (objectGrid[x, y] == null) { continue; }
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;
int drawOrderIndex = 0;
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;
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);
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;
@@ -113,19 +138,17 @@ namespace Barotrauma
Vector2 camDiff = new Vector2(obj.Position.X, obj.Position.Y) - cam.WorldViewCenter;
camDiff.Y = -camDiff.Y;
Sprite activeSprite = specular ? obj.SpecularSprite : obj.Sprite;
Sprite activeSprite = obj.Sprite;
activeSprite?.Draw(
spriteBatch,
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,
obj.CurrentRotation,
obj.CurrentScale,
SpriteEffects.None,
z);
if (specular) continue;
if (obj.ActivePrefab.DeformableSprite != 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);
if (obj.Triggers == null) { continue; }
foreach (LevelTrigger trigger in obj.Triggers)
{
if (trigger.PhysicsBody == null) continue;

View File

@@ -126,7 +126,6 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "childobject":
case "lightsource":
subElement.Remove();
break;
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");
SerializableProperty.SerializeProperties(lightSourceParams, lightElement);
element.Add(lightElement);
int elementIndex = 0;
bool wasSaved = false;
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)
@@ -162,7 +181,7 @@ namespace Barotrauma
foreach (XElement subElement in element.Elements())
{
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);
elementFound = true;

View File

@@ -3,10 +3,68 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using Voronoi2;
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
{
private static BasicEffect wallEdgeEffect, wallCenterEffect;
@@ -15,11 +73,11 @@ namespace Barotrauma
private Vector2 defaultDustVelocity;
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)
{
@@ -68,6 +126,7 @@ namespace Barotrauma
Vector2 currentDustVel = defaultDustVelocity;
foreach (LevelObject levelObject in level.LevelObjectManager.GetVisibleObjects())
{
if (levelObject.Triggers == null) { continue; }
//use the largest water flow velocity of all the triggers
Vector2 objectMaxFlow = Vector2.Zero;
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;
}
}
public static VertexPositionColorTexture[] GetColoredVertices(VertexPositionTexture[] vertices, Color color)
@@ -107,38 +165,27 @@ namespace Barotrauma
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);
wallVertices.SetData(GetColoredVertices(vertices, color));
var existingBuffer = vertexBuffers.Find(vb => vb.WallTexture == wallTexture && vb.EdgeTexture == edgeTexture);
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)
{
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,
public void DrawBackground(SpriteBatch spriteBatch, Camera cam,
LevelObjectManager backgroundSpriteManager = null,
BackgroundCreatureManager backgroundCreatureManager = null)
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap);
Vector2 backgroundPos = cam.WorldViewCenter;
backgroundPos.Y = -backgroundPos.Y;
backgroundPos *= 0.05f;
@@ -173,10 +220,13 @@ namespace Barotrauma
SamplerState.LinearWrap, DepthStencilState.DepthRead, null, null,
cam.Transform);
if (backgroundSpriteManager != null) backgroundSpriteManager.DrawObjects(spriteBatch, cam, drawFront: false);
if (backgroundCreatureManager != null) backgroundCreatureManager.Draw(spriteBatch, cam);
backgroundSpriteManager?.DrawObjects(spriteBatch, cam, drawFront: false);
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;
@@ -216,7 +266,7 @@ namespace Barotrauma
spriteBatch.End();
RenderWalls(GameMain.Instance.GraphicsDevice, cam, specular: false);
RenderWalls(GameMain.Instance.GraphicsDevice, cam);
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.NonPremultiplied,
@@ -243,7 +293,8 @@ namespace Barotrauma
foreach (GraphEdge edge in cell.Edges)
{
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)
@@ -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++)
{
@@ -261,7 +312,7 @@ namespace Barotrauma
new Vector2(nodeList[i].X, -nodeList[i].Y),
Color.Lerp(Color.Yellow, GUI.Style.Red, i / (float)nodeList.Count), 0, 10);
}
}
}*/
foreach (var ruin in level.Ruins)
{
@@ -270,108 +321,142 @@ namespace Barotrauma
}
Vector2 pos = new Vector2(0.0f, -level.Size.Y);
if (cam.WorldView.Y >= -pos.Y - 1024)
{
pos.X = cam.WorldView.X -1024;
int width = (int)(Math.Ceiling(cam.WorldView.Width / 1024 + 4.0f) * 1024);
int topBarrierWidth = level.GenerationParams.WallEdgeSprite.Texture.Width;
int topBarrierHeight = level.GenerationParams.WallEdgeSprite.Texture.Height;
GUI.DrawRectangle(spriteBatch,new Rectangle(
(int)(MathUtils.Round(pos.X, 1024)),
-cam.WorldView.Y,
width,
(int)(cam.WorldView.Y + pos.Y) - 30),
pos.X = cam.WorldView.X - topBarrierWidth;
int width = (int)(Math.Ceiling(cam.WorldView.Width / 1024 + 4.0f) * topBarrierWidth);
GUI.DrawRectangle(spriteBatch, new Rectangle(
(int)MathUtils.Round(pos.X, topBarrierWidth),
-cam.WorldView.Y,
width,
(int)(cam.WorldView.Y + pos.Y) - 60),
Color.Black, true);
spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture,
new Rectangle((int)(MathUtils.Round(pos.X, 1024)), (int)pos.Y-1000, width, 1024),
new Rectangle(0, 0, width, -1024),
level.BackgroundTextureColor, 0.0f,
new Rectangle((int)MathUtils.Round(pos.X, topBarrierWidth), (int)(pos.Y - topBarrierHeight + level.GenerationParams.WallEdgeExpandOutwardsAmount), width, topBarrierHeight),
new Rectangle(0, 0, width, -topBarrierHeight),
GameMain.LightManager?.LightingEnabled ?? false ? GameMain.LightManager.AmbientLight : level.WallColor, 0.0f,
Vector2.Zero,
SpriteEffects.None, 0.0f);
}
if (cam.WorldView.Y - cam.WorldView.Height < level.SeaFloorTopPos + 1024)
{
pos = new Vector2(cam.WorldView.X - 1024, -level.BottomPos);
int width = (int)(Math.Ceiling(cam.WorldView.Width / 1024 + 4.0f) * 1024);
int bottomBarrierWidth = level.GenerationParams.WallEdgeSprite.Texture.Width;
int bottomBarrierHeight = level.GenerationParams.WallEdgeSprite.Texture.Height;
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(
(int)(MathUtils.Round(pos.X, 1024)),
(int)-(level.BottomPos - 30),
width,
(int)(level.BottomPos - (cam.WorldView.Y - cam.WorldView.Height))),
(int)(MathUtils.Round(pos.X, bottomBarrierWidth)),
-(level.BottomPos - 60),
width,
level.BottomPos - (cam.WorldView.Y - cam.WorldView.Height)),
Color.Black, true);
spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture,
new Rectangle((int)(MathUtils.Round(pos.X, 1024)), (int)-level.BottomPos, width, 1024),
new Rectangle(0, 0, width, -1024),
level.BackgroundTextureColor, 0.0f,
new Rectangle((int)MathUtils.Round(pos.X, bottomBarrierWidth), -level.BottomPos - (int)level.GenerationParams.WallEdgeExpandOutwardsAmount, width, bottomBarrierHeight),
new Rectangle(0, 0, width, -bottomBarrierHeight),
GameMain.LightManager?.LightingEnabled ?? false ? GameMain.LightManager.AmbientLight : level.WallColor, 0.0f,
Vector2.Zero,
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;
bool renderSeaFloor = cam.WorldView.Y - cam.WorldView.Height < level.SeaFloorTopPos + 1024;
if (!renderLevel && !renderSeaFloor) return;
var defaultRasterizerState = graphicsDevice.RasterizerState;
Matrix transformMatrix = cam.ShaderTransform
* 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;
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;
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();
graphicsDevice.SetVertexBuffer(wallVertices);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wallVertices.VertexCount / 3.0f));
var wallList = i == 0 ? level.ExtraWalls : level.UnsyncedExtraWalls;
foreach (LevelWall wall in wallList)
{
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;
wallEdgeEffect.World = wall.GetTransform() * transformMatrix;
wallCenterEffect.Texture = vertexBuffer.WallTexture;
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();
graphicsDevice.SetVertexBuffer(wall.WallVertices);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallVertices.VertexCount / 3.0f));
graphicsDevice.SetVertexBuffer(vertexBuffer.WallEdgeBuffer);
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;
}
@@ -383,8 +468,11 @@ namespace Barotrauma
protected virtual void Dispose(bool disposing)
{
if (wallVertices != null) wallVertices.Dispose();
if (bodyVertices != null) bodyVertices.Dispose();
foreach (var vertexBuffer in vertexBuffers)
{
vertexBuffer.Dispose();
}
vertexBuffers.Clear();
}
}
}

View File

@@ -2,55 +2,44 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
partial class LevelWall : IDisposable
{
private VertexBuffer wallVertices, bodyVertices;
public LevelWallVertexBuffer VertexBuffer { get; private set; }
public VertexBuffer WallVertices
{
get { return wallVertices; }
}
public VertexBuffer WallBuffer { get { return VertexBuffer.WallBuffer; } }
public VertexBuffer BodyVertices
{
get { return bodyVertices; }
}
public VertexBuffer WallEdgeBuffer { get { return VertexBuffer.WallEdgeBuffer; } }
public virtual float Alpha => 1.0f;
public Matrix GetTransform()
{
return body.BodyType == BodyType.Static ?
Matrix.Identity :
Matrix.CreateRotationZ(body.Rotation) *
Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(body.Position), 0.0f));
return Body.FixedRotation ?
Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(Body.Position), 0.0f)) :
Matrix.CreateRotationZ(Body.Rotation) *
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);
wallVertices.SetData(LevelRenderer.GetColoredVertices(vertices, color));
if (VertexBuffer != null && !VertexBuffer.IsDisposed) { VertexBuffer.Dispose(); }
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);
bodyVertices.SetData(LevelRenderer.GetColoredVertices(vertices, color));
}
public void SetWallVertices(VertexPositionColorTexture[] vertices)
{
if (wallVertices != null && !wallVertices.IsDisposed) wallVertices.Dispose();
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
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);
float zCoord = this is DestructibleLevelWall ? Rand.Range(0.9f, 1.0f) : 0.9f;
List<VertexPositionTexture> wallVertices = CaveGenerator.GenerateWallVertices(triangles, level.GenerationParams, zCoord);
SetWallVertices(
wallVertices.ToArray(),
CaveGenerator.GenerateWallEdgeVertices(Cells, level, zCoord).ToArray(),
level.GenerationParams.WallSprite.Texture,
level.GenerationParams.WallEdgeSprite.Texture,
color);
}
}
}

View File

@@ -132,7 +132,7 @@ namespace Barotrauma.Lights
public void AddLight(LightSource light)
{
if (!lights.Contains(light)) lights.Add(light);
if (!lights.Contains(light)) { lights.Add(light); }
}
public void RemoveLight(LightSource light)
@@ -151,7 +151,16 @@ namespace Barotrauma.Lights
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; }
@@ -203,9 +212,9 @@ namespace Barotrauma.Lights
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
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
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();
@@ -215,9 +224,10 @@ namespace Barotrauma.Lights
graphics.Clear(AmbientLight);
graphics.BlendState = BlendState.Additive;
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
Level.Loaded?.BackgroundCreatureManager?.DrawLights(spriteBatch, cam);
foreach (LightSource light in activeLights)
{
if (!light.IsBackground) { continue; }
if (!light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawSprite(spriteBatch, cam);
light.DrawLightVolume(spriteBatch, lightEffect, transform);
}
@@ -262,7 +272,7 @@ namespace Barotrauma.Lights
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
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);
}
spriteBatch.End();
@@ -327,7 +337,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in activeLights)
{
if (light.IsBackground) { continue; }
if (light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawLightVolume(spriteBatch, lightEffect, transform);
}

View File

@@ -7,7 +7,6 @@ using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma.Lights
{
class LightSourceParams : ISerializableEntity
@@ -33,7 +32,7 @@ namespace Barotrauma.Lights
get { return range; }
set
{
range = MathHelper.Clamp(value, 0.0f, 2048.0f);
range = MathHelper.Clamp(value, 0.0f, 4096.0f);
TextureRange = range;
if (OverrideLightTexture != null)
{
@@ -49,7 +48,63 @@ namespace Barotrauma.Lights
[Serialize("0, 0", true), Editable(ValueStep = 1, DecimalCount = 1, MinValueFloat = -1000f, MaxValueFloat = 1000f)]
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
{
get;
@@ -88,6 +143,7 @@ namespace Barotrauma.Lights
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
case "lightsprite":
{
LightSprite = new Sprite(subElement);
float spriteAlpha = subElement.GetAttributeFloat("alpha", -1.0f);
@@ -144,6 +200,8 @@ namespace Barotrauma.Lights
private static Texture2D lightTexture;
private float blinkTimer, flickerState, pulseState;
private VertexPositionColorTexture[] vertices;
private short[] indices;
@@ -296,6 +354,12 @@ namespace Barotrauma.Lights
get { return lightSourceParams.Color; }
set { lightSourceParams.Color = value; }
}
public float CurrentBrightness
{
get;
private set;
}
public float Range
{
@@ -402,7 +466,33 @@ namespace Barotrauma.Lights
texture = LightTexture;
diffToSub = new Dictionary<Submarine, Vector2>();
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>
@@ -508,13 +598,12 @@ namespace Barotrauma.Lights
//recalculate vertices if the subs have moved > 5 px relative to each other
Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition;
Vector2 prevDiff;
if (!diffToSub.TryGetValue(sub, out prevDiff))
if (!diffToSub.TryGetValue(sub, out Vector2 prevDiff))
{
diffToSub.Add(sub, diff);
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;
NeedsRecalculation = true;
@@ -884,7 +973,12 @@ namespace Barotrauma.Lights
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
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; }
uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
}
@@ -1021,7 +1115,7 @@ namespace Barotrauma.Lights
lightVolumeBuffer.SetData<VertexPositionColorTexture>(vertices, 0, vertexCount);
lightVolumeIndexBuffer.SetData<short>(indices, 0, indexCount);
Vector2 GetUV(Vector2 vert, SpriteEffects effects)
static Vector2 GetUV(Vector2 vert, SpriteEffects effects)
{
if (effects == SpriteEffects.FlipHorizontally)
{
@@ -1098,7 +1192,7 @@ namespace Barotrauma.Lights
if (DeformableLightSprite != null)
{
Vector2 origin = DeformableLightSprite.Origin + LightSourceParams.Offset;
Vector2 origin = DeformableLightSprite.Origin + LightSourceParams.GetOffset();
Vector2 drawPos = position;
if (ParentSub != null)
{
@@ -1115,14 +1209,14 @@ namespace Barotrauma.Lights
DeformableLightSprite.Draw(
cam, new Vector3(drawPos, 0.0f),
origin, -Rotation, SpriteScale,
new Color(Color, lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f),
origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale,
new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness),
LightSpriteEffect == SpriteEffects.FlipVertically);
}
if (LightSprite != null)
{
Vector2 origin = LightSprite.Origin + LightSourceParams.Offset;
Vector2 origin = LightSprite.Origin + LightSourceParams.GetOffset();
if ((LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally)
{
origin.X = LightSprite.SourceRect.Width - origin.X;
@@ -1140,9 +1234,9 @@ namespace Barotrauma.Lights
drawPos.Y = -drawPos.Y;
LightSprite.Draw(
spriteBatch, drawPos,
new Color(Color, lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f),
origin, -Rotation, SpriteScale, LightSpriteEffect);
spriteBatch, drawPos,
new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness),
origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale, LightSpriteEffect);
}
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)
{
if (Range < 1.0f || Color.A < 1) { return; }
if (Range < 1.0f || Color.A < 1 || CurrentBrightness <= 0.0f) { return; }
if (CastShadows)
{
@@ -1207,7 +1301,7 @@ namespace Barotrauma.Lights
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
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;
}
@@ -1229,7 +1323,7 @@ namespace Barotrauma.Lights
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)
{
lightEffect.Texture = OverrideLightTexture.Texture;

View File

@@ -63,6 +63,8 @@ namespace Barotrauma
private Sprite[,] mapTiles;
private bool[,] tileDiscovered;
private Pair<Rectangle, string> connectionTooltip;
#if DEBUG
private GUIComponent editor;
@@ -410,6 +412,8 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, GUICustomComponent mapContainer)
{
connectionTooltip = null;
Rectangle rect = mapContainer.Rect;
Vector2 viewSize = new Vector2(rect.Width / zoom, rect.Height / zoom);
@@ -639,6 +643,10 @@ namespace Barotrauma
{
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
}
if (connectionTooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, connectionTooltip.Second, connectionTooltip.First);
}
spriteBatch.End();
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
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 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++)
{
var segment = connection.CrackSegments[i];
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))
{
@@ -734,6 +746,59 @@ namespace Barotrauma
connectionSprite.SourceRect, connectionColor * a, MathUtils.VectorToAngle(end - start),
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)
{

View File

@@ -114,6 +114,18 @@ namespace Barotrauma
public MapEntity ReplacedBy;
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>
/// Update the selection logic in submarine editor
@@ -329,8 +341,8 @@ namespace Barotrauma
{
var clones = Clone(selectedList).Where(c => c != null).ToList();
selectedList = clones;
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
selectedList.ForEach(c => c.Move(moveAmount));
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
}
else // move
{
@@ -897,10 +909,10 @@ namespace Barotrauma
{
if (entities.Count == 0) { return; }
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
CopyEntities(entities);
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
entities.ForEach(e => { if (!e.Removed) { e.Remove(); } });
entities.Clear();
}
@@ -913,7 +925,6 @@ namespace Barotrauma
Clone(copiedList);
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);
if (!nonWireClones.Any()) { nonWireClones = clones; }
@@ -929,6 +940,8 @@ namespace Barotrauma
clone.Move(moveAmount);
clone.Submarine = Submarine.MainSub;
}
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false, handleInventoryBehavior: false));
}
/// <summary>

View File

@@ -222,9 +222,7 @@ namespace Barotrauma
public float GetDrawDepth()
{
float depth = SpriteDepthOverrideIsSet ? SpriteOverrideDepth : prefab.sprite.Depth;
depth -= (ID % 255) * 0.000001f;
return depth;
return GetDrawDepth(SpriteDepthOverrideIsSet ? SpriteOverrideDepth : prefab.sprite.Depth, prefab.sprite);
}
private void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Effect damageEffect = null)
@@ -238,6 +236,7 @@ namespace Barotrauma
else if (HiddenInGame) { return; }
Color color = IsHighlighted ? GUI.Style.Orange : spriteColor;
if (IsSelected && editing)
{
//color = Color.Lerp(color, Color.Gold, 0.5f);
@@ -253,15 +252,19 @@ namespace Barotrauma
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;
float depth = GetDrawDepth();
Vector2 textureOffset = this.textureOffset;
if (FlippedX) textureOffset.X = -textureOffset.X;
if (FlippedY) textureOffset.Y = -textureOffset.Y;
if (FlippedX) { textureOffset.X = -textureOffset.X; }
if (FlippedY) { textureOffset.Y = -textureOffset.Y; }
if (back && damageEffect == null)
if (back && damageEffect == null && !isWiringMode)
{
if (Prefab.BackgroundSprite != null)
{
@@ -299,7 +302,7 @@ namespace Barotrauma
color: Prefab.BackgroundSpriteColor,
textureScale: TextureScale * Scale,
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)
{
@@ -324,6 +327,7 @@ namespace Barotrauma
for (int i = 0; i < Sections.Length; i++)
{
Rectangle drawSection = Sections[i].rect;
if (damageEffect != null)
{
float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / MaxHealth);
@@ -340,21 +344,30 @@ namespace Barotrauma
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(
Math.Abs(rect.Location.X - Sections[i].rect.Location.X),
Math.Abs(rect.Location.Y - Sections[i].rect.Location.Y));
Math.Abs(rect.Location.X - drawSection.Location.X),
Math.Abs(rect.Location.Y - drawSection.Location.Y));
if (FlippedX && IsHorizontal) sectionOffset.X = Sections[i].rect.Right - rect.Right;
if (FlippedY && !IsHorizontal) sectionOffset.Y = (rect.Y - rect.Height) - (Sections[i].rect.Y - Sections[i].rect.Height);
if (FlippedX && IsHorizontal) { sectionOffset.X = drawSection.Right - rect.Right; }
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.Y += MathUtils.PositiveModulo((int)-textureOffset.Y, prefab.sprite.SourceRect.Height);
prefab.sprite.DrawTiled(
spriteBatch,
new Vector2(Sections[i].rect.X + drawOffset.X, -(Sections[i].rect.Y + drawOffset.Y)),
new Vector2(Sections[i].rect.Width, Sections[i].rect.Height),
new Vector2(drawSection.X + drawOffset.X, -(drawSection.Y + drawOffset.Y)),
new Vector2(drawSection.Width, drawSection.Height),
color: color,
startOffset: sectionOffset,
depth: depth,

View File

@@ -394,12 +394,10 @@ namespace Barotrauma
float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY);
GUI.DrawLine(spriteBatch,
horizontalLine,
new Vector2(topLeft.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);
GUI.DrawLine(spriteBatch,
verticalLine,
new Vector2(topLeft.X + i * GridSize.X, -topLeft.Y + expandY),
new Vector2(topLeft.X + i * GridSize.X, -bottomRight.Y - expandY),
Color.White * (1.0f - normalizedDistX) * alpha, depth: 0.6f, width: 3);

View File

@@ -10,10 +10,7 @@ namespace Barotrauma
{
if (GameMain.Client == null) { return; }
Vector2 newVelocity = Body.LinearVelocity;
Vector2 newPosition = Body.SimPosition;
Body.CorrectPosition(positionBuffer, out newPosition, out newVelocity, out _, out _);
Body.CorrectPosition(positionBuffer, out Vector2 newPosition, out Vector2 newVelocity, out _, out _);
Vector2 moveAmount = ConvertUnits.ToDisplayUnits(newPosition - Body.SimPosition);
newVelocity = newVelocity.ClampLength(100.0f);
if (!MathUtils.IsValid(newVelocity) || moveAmount.LengthSquared() < 0.0001f)
@@ -24,12 +21,12 @@ namespace Barotrauma
List<Submarine> subsToMove = submarine.GetConnectedSubs();
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
dockedSub.SubBody.positionBuffer.Clear();
}
Submarine closestSub = null;
Submarine closestSub;
if (Character.Controlled == null)
{
closestSub = Submarine.FindClosest(GameMain.GameScreen.Cam.Position);
@@ -63,5 +60,33 @@ namespace Barotrauma
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);
}
}
}
}

View File

@@ -50,6 +50,8 @@ namespace Barotrauma.Networking
Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16());
int optionIndex = msg.ReadByte();
OrderTarget orderTargetPosition = null;
Order.OrderTargetType orderTargetType = (Order.OrderTargetType)msg.ReadByte();
int wallSectionIndex = 0;
if (msg.ReadBoolean())
{
var x = msg.ReadSingle();
@@ -57,6 +59,10 @@ namespace Barotrauma.Networking
var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull;
orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true);
}
else if(orderTargetType == Order.OrderTargetType.WallSection)
{
wallSectionIndex = msg.ReadByte();
}
Order orderPrefab;
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
@@ -78,17 +84,31 @@ namespace Barotrauma.Networking
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{
var order = orderTargetPosition == null ?
new Order(orderPrefab, targetEntity, orderPrefab.GetTargetItemComponent(targetEntity as Item), orderGiver: senderCharacter) :
new Order(orderPrefab, orderTargetPosition, orderGiver: senderCharacter);
if (order.TargetAllCharacters)
Order order = null;
switch (orderTargetType)
{
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);
}
}
}

View File

@@ -1,11 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Barotrauma.IO;
using System.Diagnostics;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Barotrauma.Networking
{
@@ -26,7 +20,15 @@ namespace Barotrauma.Networking
PrivateStart();
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;
}

View File

@@ -272,6 +272,7 @@ namespace Barotrauma.Networking
private void ConnectToServer(object endpoint, string hostName)
{
LastClientListUpdateID = 0;
foreach (var c in ConnectedClients)
{
GameMain.NetLobbyScreen.RemovePlayer(c);
@@ -361,6 +362,14 @@ namespace Barotrauma.Networking
{
GameMain.NetLobbyScreen.Select();
}
else
{
entityEventManager.ClearSelf();
foreach (Character c in Character.CharacterList)
{
c.ResetNetState();
}
}
chatBox.InputBox.Enabled = true;
if (GameMain.NetLobbyScreen?.ChatInput != null)
@@ -903,6 +912,9 @@ namespace Barotrauma.Networking
case ServerPacketHeader.CREW:
campaign?.ClientReadCrew(inc);
break;
case ServerPacketHeader.READY_CHECK:
ReadyCheck.ClientRead(inc);
break;
case ServerPacketHeader.FILE_TRANSFER:
fileReceiver.ReadMessage(inc);
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" +
" (client value #" + i + ": " + Level.Loaded.EqualityCheckValues[i] +
", server value #" + i + ": " + levelEqualityCheckValues[i].ToString("X") +
", level value count: " + levelEqualityCheckValues.Count.ToString("X") +
", level value count: " + levelEqualityCheckValues.Count +
", seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" +
", mirrored: " + Level.Loaded.Mirrored + ").";
@@ -2892,8 +2904,9 @@ namespace Barotrauma.Networking
return 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();
readyToStartMsg.Write((byte)ClientPacketHeader.RESPONSE_STARTGAME);

View File

@@ -61,6 +61,7 @@ namespace Barotrauma
CreateLabeledNumberInput(parent, 0, 20, nameof(AllowedWireDisconnectionsPerMinute));
CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, nameof(WireDisconnectionKarmaDecrease));
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
if (TextManager.ContainsTag("Karma." + nameof(DangerousItemStealKarmaDecrease)))

View File

@@ -193,7 +193,7 @@ namespace Barotrauma.Networking
continue;
}
byte msgLength = msg.ReadByte();
int msgLength = (int)msg.ReadVariableUInt32();
IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
entities.Add(entity);
@@ -300,5 +300,15 @@ namespace Barotrauma.Networking
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();
}
}
}

View File

@@ -13,7 +13,8 @@ namespace Barotrauma.Networking
msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID);
msg.Write(TargetEntity is Entity ? (TargetEntity as Entity).ID : (UInt16)0);
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(orderTarget.Position.X);
@@ -23,6 +24,10 @@ namespace Barotrauma.Networking
else
{
msg.Write(false);
if (Order.TargetType == Order.OrderTargetType.WallSection)
{
msg.Write((byte)(WallSectionIndex ?? Order.WallSectionIndex ?? 0));
}
}
}
}

View File

@@ -378,8 +378,8 @@ namespace Barotrauma.Networking
if (maxPlayersElement > NetConfig.MaxPlayers)
{
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.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);*/
maxPlayersElement = NetConfig.MaxPlayers;
}
@@ -540,5 +540,18 @@ namespace Barotrauma.Networking
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);
}
}
}

View File

@@ -3,6 +3,7 @@ using Barotrauma.Networking;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;

View File

@@ -240,7 +240,14 @@ namespace Barotrauma.Particles
{
animState += deltaTime;
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;

View File

@@ -218,9 +218,12 @@ namespace Barotrauma
sb.AppendLine("Target site: " + exception.TargetSite.ToString());
}
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.StackTrace.CleanupStackTrace());
sb.AppendLine("\n");
if (exception.StackTrace != null)
{
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.StackTrace.CleanupStackTrace());
sb.AppendLine("\n");
}
if (exception.InnerException != null)
{
@@ -229,8 +232,11 @@ namespace Barotrauma
{
sb.AppendLine("Target site: " + exception.InnerException.TargetSite.ToString());
}
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.InnerException.StackTrace.CleanupStackTrace());
if (exception.InnerException.StackTrace != null)
{
sb.AppendLine("Stack trace: ");
sb.AppendLine(exception.InnerException.StackTrace.CleanupStackTrace());
}
}
sb.AppendLine("Last debug messages:");

View File

@@ -372,6 +372,9 @@ namespace Barotrauma.CharacterEditor
{
base.Update(deltaTime);
if (Wizard.instance != null) { return; }
GameMain.LightManager?.Update((float)deltaTime);
spriteSheetRect = CalculateSpritesheetRectangle();
// Handle shortcut keys
if (PlayerInput.KeyHit(Keys.F1))
@@ -787,7 +790,7 @@ namespace Barotrauma.CharacterEditor
if (GameMain.LightManager.LightingEnabled)
{
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);
}
base.Draw(deltaTime, graphics, spriteBatch);

View File

@@ -186,7 +186,7 @@ namespace Barotrauma
spriteBatch.End();
graphics.SetRenderTarget(null);
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam, renderTarget);
GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam, renderTarget);
//------------------------------------------------------------------------
graphics.SetRenderTarget(renderTargetBackground);

View File

@@ -24,24 +24,26 @@ namespace Barotrauma
get { return cam; }
}
private GUIFrame leftPanel, rightPanel, bottomPanel, topPanel;
private readonly GUIFrame leftPanel, rightPanel, bottomPanel, topPanel;
private LevelGenerationParams selectedParams;
private LevelObjectPrefab selectedLevelObject;
private GUIListBox paramsList, ruinParamsList, outpostParamsList, levelObjectList;
private GUIListBox editorContainer;
private readonly GUIListBox paramsList, ruinParamsList, caveParamsList, outpostParamsList, levelObjectList;
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 LightSource pointerLightSource;
private readonly Color[] tunnelDebugColors = new Color[] { Color.White, Color.Cyan, Color.LightGreen, Color.Red, Color.LightYellow, Color.LightSeaGreen };
public LevelEditorScreen()
{
cam = new Camera()
@@ -78,8 +80,17 @@ namespace Barotrauma
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);
GUITextBlock.AutoScaleAndNormalize(ruinTitle, outpostTitle);
GUITextBlock.AutoScaleAndNormalize(ruinTitle, caveTitle, outpostTitle);
outpostParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform));
outpostParamsList.OnSelected += (GUIComponent component, object obj) =>
@@ -237,13 +248,17 @@ namespace Barotrauma
{
OnClicked = (btn, obj) =>
{
bool wasLevelLoaded = Level.Loaded != null;
Submarine.Unload();
GameMain.LightManager.ClearLights();
LevelData levelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
levelData.ForceOutpostGenerationParams = outpostParamsList.SelectedData as OutpostGenerationParams;
Level.Generate(levelData, mirror: false);
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)
{
param.TextColor = param.UserData == selectedParams ? GUI.Style.Green : param.Style.TextColor;
@@ -344,6 +359,7 @@ namespace Barotrauma
editingSprite = null;
UpdateParamsList();
UpdateRuinParamsList();
UpdateCaveParamsList();
UpdateOutpostParamsList();
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()
{
editorContainer.ClearChildren();
@@ -425,6 +457,7 @@ namespace Barotrauma
text: ToolBox.LimitString(levelObjPrefab.Name, GUI.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUI.SmallFont)
{
CanBeFocused = false,
ToolTip = levelObjPrefab.Name
};
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();
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, levelObjectPrefab, false, true, elementHeight: 20, titleFont: GUI.LargeFont);
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, caveGenerationParams, false, true, elementHeight: 20);
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)
{
AbsoluteSpacing = 5,
@@ -457,15 +489,60 @@ namespace Barotrauma
{
MinValueFloat = 0,
MaxValueFloat = 100,
FloatValue = levelObjectPrefab.GetCommonness(selectedParams.Identifier),
FloatValue = caveGenerationParams.GetCommonness(selectedParams),
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);
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;
if (sprite != null)
@@ -508,7 +585,7 @@ namespace Barotrauma
foreach (LevelObjectPrefab objPrefab in LevelObjectPrefab.List)
{
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) =>
{
@@ -590,7 +667,7 @@ namespace Barotrauma
foreach (GUIComponent levelObjFrame in levelObjectList.Content.Children)
{
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.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;
@@ -607,7 +684,7 @@ namespace Barotrauma
{
var levelObj1 = c1.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)
{
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam);
GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam);
}
graphics.Clear(Color.Black);
@@ -643,6 +720,72 @@ namespace Barotrauma
Submarine.DrawFront(spriteBatch);
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));
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();
if (lightingEnabled.Selected)
@@ -659,12 +802,23 @@ namespace Barotrauma
}
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);
spriteBatch.End();
}
public override void Update(double deltaTime)
{
GameMain.LightManager?.Update((float)deltaTime);
pointerLightSource.Position = cam.ScreenToWorld(PlayerInput.MousePosition);
pointerLightSource.Enabled = cursorLightEnabled.Selected;
pointerLightSource.IsBackground = true;
@@ -694,7 +848,40 @@ namespace Barotrauma
{
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())
{
foreach (XElement subElement in element.Elements())
@@ -730,7 +917,8 @@ namespace Barotrauma
{
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);
break;
}
@@ -846,7 +1034,7 @@ namespace Barotrauma
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);
GUI.AddMessage(TextManager.Get("leveleditor.levelobjnametaken"), GUI.Style.Red);
@@ -860,14 +1048,14 @@ namespace Barotrauma
return false;
}
newPrefab.Name = nameBox.Text;
newPrefab.Identifier = nameBox.Text;
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true };
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs))
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { continue; }
var newElement = new XElement(newPrefab.Name);
var newElement = new XElement(newPrefab.Identifier);
newPrefab.Save(newElement);
newElement.Add(new XElement("Sprite",
new XAttribute("texture", texturePathBox.Text),

View File

@@ -38,6 +38,9 @@ namespace Barotrauma
private GUIImage playstyleBanner;
private GUITextBlock playstyleDescription;
private GUIComponent remoteContentContainer;
private XDocument remoteContentDoc;
private Tab selectedTab;
private Sprite backgroundSprite;
@@ -62,6 +65,14 @@ namespace Barotrauma
}
CreateHostServerFields();
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)
@@ -84,6 +95,11 @@ namespace Barotrauma
RelativeSpacing = 0.02f
};
remoteContentContainer = new GUIFrame(new RectTransform(Vector2.One, parent: Frame.RectTransform), style: null)
{
CanBeFocused = false
};
#if TEST_REMOTE_CONTENT
var doc = XMLExtensions.TryLoadXml("Content/UI/MenuTextTest.xml");
@@ -91,7 +107,7 @@ namespace Barotrauma
{
foreach (XElement subElement in doc?.Root.Elements())
{
GUIComponent.FromXML(subElement, Frame.RectTransform);
GUIComponent.FromXML(subElement, remoteContentContainer.RectTransform);
}
}
#else
@@ -1402,10 +1418,10 @@ namespace Barotrauma
if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
if (!string.IsNullOrWhiteSpace(xml))
{
XElement element = XDocument.Parse(xml)?.Root;
foreach (XElement subElement in element.Elements())
remoteContentDoc = XDocument.Parse(xml);
foreach (XElement subElement in remoteContentDoc?.Root.Elements())
{
GUIComponent.FromXML(subElement, Frame.RectTransform);
GUIComponent.FromXML(subElement, remoteContentContainer.RectTransform);
}
}
}

View File

@@ -832,7 +832,7 @@ namespace Barotrauma
var modeFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), modeList.Content.RectTransform), style: null)
{
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) })
{
@@ -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;
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; }
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),
TextManager.Get("MissionType." + missionType.ToString()))
{
UserData = (int)missionType,
ToolTip = TextManager.Get("MissionTypeDescription." + missionType.ToString(), returnNull: true),
OnSelected = (tickbox) =>
{
int missionTypeOr = tickbox.Selected ? (int)tickbox.UserData : (int)MissionType.None;
@@ -934,7 +937,6 @@ namespace Barotrauma
}
};
frame.RectTransform.MinSize = missionTypeTickBoxes[index].RectTransform.MinSize;
index++;
}
clientDisabledElements.AddRange(missionTypeTickBoxes);
@@ -1485,7 +1487,7 @@ namespace Barotrauma
return true;
}
};
}
}
}
private void CreateChangesPendingText()
@@ -2509,7 +2511,7 @@ namespace Barotrauma
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)
{
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)
{
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)
{
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)
{
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)
{
if (modeIndex < 0 || modeIndex >= modeList.Content.CountChildren) { return; }
if ((GameModePreset)modeList.Content.GetChild(modeIndex).UserData != GameModePreset.MultiPlayerCampaign)
{
ToggleCampaignMode(false);
@@ -3001,16 +3003,33 @@ namespace Barotrauma
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()
{
if (GameMain.Client == null) { return; }
autoRestartBox.Parent.Visible = true;
settingsBlocker.Visible = false;
if (SelectedMode == GameModePreset.Mission)
if (SelectedMode == GameModePreset.Mission || SelectedMode == GameModePreset.PvP)
{
MissionTypeFrame.Visible = true;
CampaignFrame.Visible = CampaignSetupFrame.Visible = false;
RefreshMissionTypes();
}
else if (SelectedMode == GameModePreset.MultiPlayerCampaign)
{
@@ -3062,7 +3081,7 @@ namespace Barotrauma
RefreshEnabledElements();
if (enabled)
{
modeList.Select(2, true);
modeList.Select(GameModePreset.MultiPlayerCampaign, true);
}
}

View File

@@ -61,7 +61,7 @@ namespace Barotrauma
{
var temp = LoadException;
LoadException = null;
throw temp;
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(temp).Throw();
}
}
}

View File

@@ -696,6 +696,7 @@ namespace Barotrauma
private void ReadServerMemFromFile(string file, ref List<ServerInfo> servers)
{
if (servers == null) { servers = new List<ServerInfo>(); }
servers.Clear();
if (!File.Exists(file)) { return; }
@@ -716,11 +717,21 @@ namespace Barotrauma
return;
}
bool saveCleanup = false;
foreach (XElement element in doc.Root.Elements())
{
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)
@@ -1061,7 +1072,7 @@ namespace Barotrauma
(!filterFull.Selected || serverInfo.PlayerCount < serverInfo.MaxPlayers) &&
(!filterEmpty.Selected || serverInfo.PlayerCount > 0) &&
(!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) &&
(filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) &&
(!filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) &&
(!filterKarma.Selected || serverInfo.KarmaEnabled == true) &&
(!filterFriendlyFire.Selected || serverInfo.FriendlyFireEnabled == false) &&
(!filterTraitor.Selected || serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe) &&

View File

@@ -353,6 +353,8 @@ namespace Barotrauma
{
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("brokensprite").ForEach(s => CreateSprite(s));

View File

@@ -71,6 +71,7 @@ namespace Barotrauma
private bool entityMenuOpen = true;
private float entityMenuOpenState = 1.0f;
private string lastFilter;
public GUIComponent EntityMenu;
private GUITextBox entityFilterBox;
private GUIListBox entityList;
@@ -105,7 +106,25 @@ namespace Barotrauma
private static GUIComponent autoSaveLabel;
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<WarningType> SuppressedWarnings = new List<WarningType>();
@@ -197,7 +216,7 @@ namespace Barotrauma
{
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
{
@@ -630,35 +649,33 @@ namespace Barotrauma
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);
var lightCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), lightCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
lightCount.TextGetter = () =>
var lightCountText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), lightCountLabel.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
lightCountText.TextGetter = () =>
{
int disabledItemLightCount = 0;
int lightCount = 0;
foreach (Item item in Item.ItemList)
{
if (item.ParentInventory == null) { continue; }
disabledItemLightCount += item.GetComponents<LightComponent>().Count();
if (item.ParentInventory != null) { continue; }
lightCount += item.GetComponents<LightComponent>().Count();
}
int count = GameMain.LightManager.Lights.Count() - disabledItemLightCount;
lightCount.TextColor = ToolBox.GradientLerp(count / 250.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
return count.ToString();
lightCountText.TextColor = ToolBox.GradientLerp(lightCount / 250.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
return lightCount.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);
var shadowCastingLightCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), shadowCastingLightCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
shadowCastingLightCount.TextGetter = () =>
var shadowCastingLightCountText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), shadowCastingLightCountLabel.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
shadowCastingLightCountText.TextGetter = () =>
{
int disabledItemLightCount = 0;
int lightCount = 0;
foreach (Item item in Item.ItemList)
{
if (item.ParentInventory == null) { continue; }
disabledItemLightCount += item.GetComponents<LightComponent>().Count();
if (item.ParentInventory != null) { continue; }
lightCount += item.GetComponents<LightComponent>().Count(l => l.CastShadows);
}
int count = GameMain.LightManager.Lights.Count(l => l.CastShadows) - disabledItemLightCount;
shadowCastingLightCount.TextColor = ToolBox.GradientLerp(count / 60.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
return count.ToString();
shadowCastingLightCountText.TextColor = ToolBox.GradientLerp(lightCount / 60.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
return lightCount.ToString();
};
entityCountPanel.RectTransform.NonScaledSize =
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);
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.OnTextChanged += (textBox, text) => { FilterEntities(text); return true; };
entityFilterBox.OnTextChanged += (textBox, text) =>
{
if (text == lastFilter) { return true; }
lastFilter = text;
FilterEntities(text);
return true;
};
//spacing
new GUIFrame(new RectTransform(new Vector2(0.075f, 1.0f), entityMenuTop.RectTransform), style: null);
@@ -994,15 +1017,9 @@ namespace Barotrauma
GameMain.LightManager.AmbientLight =
Level.Loaded?.GenerationParams?.AmbientLightColor ??
LevelGenerationParams.LevelParams?.FirstOrDefault()?.AmbientLightColor ??
new Color(20, 20, 20, 255);
new Color(3, 3, 3, 3);
UpdateEntityList();
if (!wasSelectedBefore)
{
OpenEntityMenu(MapEntityCategory.Structure);
wasSelectedBefore = true;
}
isAutoSaving = false;
if (!wasSelectedBefore)
@@ -1150,7 +1167,7 @@ namespace Barotrauma
{
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";
//make space for the entity menu
@@ -1195,7 +1212,7 @@ namespace Barotrauma
CrossThread.RequestExecutionOnMainThread(() =>
{
if (AutoSaveInfo?.Root == null) { return; }
if (AutoSaveInfo?.Root == null || Submarine.MainSub?.Info == null) { return; }
int saveCount = AutoSaveInfo.Root.Elements().Count();
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"));
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));
subTypeDropdown.AddItem(TextManager.Get("submarinetype.player"), SubmarineType.Player);
subTypeDropdown.AddItem(TextManager.Get("submarinetype.outpostmodule"), SubmarineType.OutpostModule);
subTypeDropdown.AddItem(TextManager.Get("submarinetype.outpost"), SubmarineType.Outpost);
subTypeDropdown.AddItem(TextManager.Get("submarinetype.wreck"), SubmarineType.Wreck);
foreach (SubmarineType subType in Enum.GetValues(typeof(SubmarineType)))
{
subTypeDropdown.AddItem(TextManager.Get("submarinetype."+subType.ToString().ToLowerInvariant()), subType);
}
//---------------------------------------
//---------------------------------------
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,
CanBeFocused = true,
@@ -1853,6 +1870,10 @@ namespace Barotrauma
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))
{
@@ -2665,7 +2686,7 @@ namespace Barotrauma
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)
{
child.Visible = child.UserData is MapEntityPrefab item && IsItemPrefab(item);
@@ -2790,11 +2811,17 @@ namespace Barotrauma
if (PlayerInput.IsShiftDown())
{
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"
};
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),
TextManager.Get("editor.selectsame"), font: GUI.SmallFont)
{
@@ -2870,6 +2897,9 @@ namespace Barotrauma
case "bgcolor":
CreateBackgroundColorPicker();
break;
case "transparency":
TransparentWiringMode = !TransparentWiringMode;
break;
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));
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>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
@@ -3939,6 +3987,11 @@ namespace Barotrauma
CloseItem();
}
if (lightingEnabled)
{
GameMain.LightManager?.Update((float)deltaTime);
}
if (contextMenu != null)
{
Rectangle expandedRect = contextMenu.Rect;
@@ -4034,7 +4087,7 @@ namespace Barotrauma
// Deposit item from our "infinite stack" into inventory slots
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;
@@ -4090,7 +4143,7 @@ namespace Barotrauma
if (!newItem.Removed)
{
BulkItemBufferInUse = true;
BulkItemBufferInUse = ItemAddMutex;
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();
if (placedEntities.Any())
{
BulkItemBufferInUse = true;
BulkItemBufferInUse = ItemAddMutex;
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];
for (int i = 1; i < BulkItemBuffer.Count; i++)
{
AddOrDeleteCommand command = BulkItemBuffer[i];
command.MergeInto(master);
}
StoreCommand(master);
BulkItemBuffer.Clear();
BulkItemBufferInUse = false;
CommitBulkItemBuffer();
}
if (SerializableEntityEditor.PropertyChangesActive && (SerializableEntityEditor.NextCommandPush < DateTime.Now || MapEntity.EditingHUD == null))
@@ -4326,7 +4370,7 @@ namespace Barotrauma
cam.UpdateTransform();
if (lightingEnabled)
{
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam);
GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam);
}
foreach (Submarine sub in Submarine.Loaded)
@@ -4493,6 +4537,7 @@ namespace Barotrauma
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;
}
}

View File

@@ -566,8 +566,13 @@ namespace Barotrauma
{
ToolTip = toolTip
};
bool isFlagsAttribute = value.GetType().IsDefined(typeof(FlagsAttribute), false);
foreach (object enumValue in Enum.GetValues(value.GetType()))
{
if (isFlagsAttribute && !MathHelper.IsPowerOfTwo((int)enumValue)) { continue; }
enumDropDown.AddItem(enumValue.ToString(), enumValue);
if (((int)enumValue != 0 || (int)value == 0) && ((Enum)value).HasFlag((Enum)enumValue))
{

View File

@@ -625,7 +625,8 @@ namespace Barotrauma
Vector2 soundPos = new Vector2(GameMain.SoundManager.ListenerPosition.X + (fireVolumeRight[i] - fireVolumeLeft[i]) * 100, GameMain.SoundManager.ListenerPosition.Y);
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].Gain = Math.Max(fireVolumeRight[i], fireVolumeLeft[i]);
@@ -960,20 +961,18 @@ namespace Barotrauma
if (Level.IsLoadedOutpost && Character.Controlled.Submarine == Level.Loaded.StartOutpost)
{
// Only return music type for specific outpost types to not assume that
// every outpost type has an associated music track (switch-case for future tracks)
// Only return music type for location types which have music tracks defined
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;
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";
}
@@ -1026,7 +1025,8 @@ namespace Barotrauma
{
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";
}

View File

@@ -57,65 +57,9 @@ namespace Barotrauma
{
if (entity == null) { return; }
if (sounds.Count > 0 && playSound)
if (playSound)
{
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;
}
PlaySound(entity, hull, worldPosition);
}
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)
{
bool doMuffleCheck = Timing.TotalTime > LastMuffleCheckTime + 0.2;

View File

@@ -120,7 +120,8 @@ namespace Barotrauma
/// </summary>
/// <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>
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;
Receivers = receivers;
@@ -151,14 +152,17 @@ namespace Barotrauma
}
}
if (itemsToDelete.Any())
if (itemsToDelete.Any() && handleInventoryBehavior)
{
ContainedItemsCommand.Add(new AddOrDeleteCommand(itemsToDelete, true));
foreach (MapEntity item in itemsToDelete)
ContainedItemsCommand.Add(new AddOrDeleteCommand(itemsToDelete, wasDeleted));
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");
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 receiver = Receivers[i];
MapEntity clone = clones[i], receiver = Receivers[i];
if (receiver.GetReplacementOrThis() is Item item && clone is Item cloneItem)
{
@@ -252,6 +256,11 @@ namespace Barotrauma
}
receiver.GetReplacementOrThis().ReplacedBy = clone;
}
for (int i = 0; i < length; i++)
{
MapEntity clone = clones[i], receiver = Receivers[i];
if (clone is Item it)
{

View File

@@ -19,6 +19,8 @@ namespace Barotrauma
private set;
}
private static volatile bool cancelAll = false;
public static void Init(GraphicsDevice graphicsDevice, bool needsBmp = false)
{
_graphicsDevice = graphicsDevice;
@@ -36,6 +38,11 @@ namespace Barotrauma
});
}
public static void CancelAll()
{
cancelAll = true;
}
private static byte[] CompressDxt5(byte[] data, int width, int height)
{
using (System.IO.MemoryStream mstream = new System.IO.MemoryStream())
@@ -220,6 +227,7 @@ namespace Barotrauma
Texture2D tex = null;
CrossThread.RequestExecutionOnMainThread(() =>
{
if (cancelAll) { return; }
tex = new Texture2D(_graphicsDevice, width, height, mipmap, format);
tex.SetData(textureData);
});

View File

@@ -150,7 +150,7 @@ namespace Barotrauma
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]; }
float scaledT = t * (gradient.Length - 1);

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.10.6.2</Version>
<Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.10.6.2</Version>
<Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.10.6.2</Version>
<Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.10.6.2</Version>
<Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.10.6.2</Version>
<Version>0.11.0.9</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

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