Unstable v0.1100.0.4 (November 11th 2020)

This commit is contained in:
Joonas Rikkonen
2020-11-06 20:12:15 +02:00
parent 6b36bf809d
commit b772654326
297 changed files with 12502 additions and 4277 deletions
@@ -328,9 +328,9 @@ namespace Barotrauma
{ {
previousOffset = offset; previousOffset = offset;
} }
//how much to zoom out (zoom completely out when offset is 1000) //how much to zoom out (zoom completely out when offset is 1000)
float zoomOutAmount = Math.Min(offset.Length() / 1000.0f, 1.0f); float zoomOutAmount = GetZoomAmount(offset);
//zoom amount when resolution is not taken into account //zoom amount when resolution is not taken into account
float unscaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount); float unscaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount);
//zoom with resolution taken into account (zoom further out on smaller resolutions) //zoom with resolution taken into account (zoom further out on smaller resolutions)
@@ -409,5 +409,15 @@ namespace Barotrauma
//Vector2 screenCoords = Vector2.Transform(coords, transform); //Vector2 screenCoords = Vector2.Transform(coords, transform);
return Vector2.Transform(coords, transform); return Vector2.Transform(coords, transform);
} }
private float GetZoomAmount(Vector2 offset)
{
return Math.Min(offset.Length() / 1000.0f, 1.0f);
}
public float GetZoomAmountFromPrevious()
{
return GetZoomAmount(previousOffset);
}
} }
} }
@@ -580,14 +580,23 @@ namespace Barotrauma
soundTimer -= deltaTime; soundTimer -= deltaTime;
} }
else if (AIController != null) else if (AIController != null)
{ {
switch (AIController.State) switch (AIController.State)
{ {
case AIState.Attack: case AIState.Attack:
PlaySound(CharacterSound.SoundType.Attack); PlaySound(CharacterSound.SoundType.Attack);
break; break;
default: default:
PlaySound(CharacterSound.SoundType.Idle); var petBehavior = (AIController as EnemyAIController)?.PetBehavior;
if (petBehavior != null && petBehavior.Happiness < petBehavior.MaxHappiness * 0.25f)
{
PlaySound(CharacterSound.SoundType.Unhappy);
}
else
{
PlaySound(CharacterSound.SoundType.Idle);
}
break; break;
} }
} }
@@ -801,7 +810,7 @@ namespace Barotrauma
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType); var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
if (iconStyle != null) if (iconStyle != null)
{ {
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f; Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos; Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y; iconPos.Y = -iconPos.Y;
nameColor = iconStyle.Color; nameColor = iconStyle.Color;
@@ -826,7 +835,7 @@ namespace Barotrauma
var iconStyle = GUI.Style.GetComponentStyle("PetIcon." + petStatus); var iconStyle = GUI.Style.GetComponentStyle("PetIcon." + petStatus);
if (iconStyle != null) if (iconStyle != null)
{ {
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f; Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.body?.DrawPosition ?? DrawPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos; Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y; iconPos.Y = -iconPos.Y;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First(); var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
@@ -877,11 +886,12 @@ namespace Barotrauma
private readonly List<CharacterSound> matchingSounds = new List<CharacterSound>(); private readonly List<CharacterSound> matchingSounds = new List<CharacterSound>();
private SoundChannel soundChannel; private SoundChannel soundChannel;
public void PlaySound(CharacterSound.SoundType soundType) public void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor = 1.0f)
{ {
if (sounds == null || sounds.Count == 0) { return; } if (sounds == null || sounds.Count == 0) { return; }
if (soundChannel != null && soundChannel.IsPlaying) { return; } if (soundChannel != null && soundChannel.IsPlaying) { return; }
if (GameMain.SoundManager?.Disabled ?? true) { return; } if (GameMain.SoundManager?.Disabled ?? true) { return; }
if (soundTimer > soundInterval * soundIntervalFactor) { return; }
matchingSounds.Clear(); matchingSounds.Clear();
foreach (var s in sounds) foreach (var s in sounds)
{ {
@@ -11,15 +11,15 @@ namespace Barotrauma
{ {
class CharacterHUD class CharacterHUD
{ {
private static Dictionary<ISpatialEntity, int> orderIndicatorCount = new Dictionary<ISpatialEntity, int>(); private static readonly Dictionary<ISpatialEntity, int> orderIndicatorCount = new Dictionary<ISpatialEntity, int>();
const float ItemOverlayDelay = 1.0f; const float ItemOverlayDelay = 1.0f;
private static Item focusedItem; private static Item focusedItem;
private static float focusedItemOverlayTimer; private static float focusedItemOverlayTimer;
private static List<Item> brokenItems = new List<Item>(); private static readonly List<Item> brokenItems = new List<Item>();
private static float brokenItemsCheckTimer; private static float brokenItemsCheckTimer;
private static Dictionary<string, string> cachedHudTexts = new Dictionary<string, string>(); private static readonly Dictionary<string, string> cachedHudTexts = new Dictionary<string, string>();
private static GUIFrame hudFrame; private static GUIFrame hudFrame;
public static GUIFrame HUDFrame public static GUIFrame HUDFrame
@@ -126,6 +126,11 @@ namespace Barotrauma
{ {
character.Inventory.Update(deltaTime, cam); character.Inventory.Update(deltaTime, cam);
} }
else
{
character.Inventory.ClearSubInventories();
}
for (int i = 0; i < character.Inventory.Items.Length - 1; i++) for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
{ {
var item = character.Inventory.Items[i]; var item = character.Inventory.Items[i];
@@ -199,9 +204,18 @@ namespace Barotrauma
if (GameMain.GameSession?.CrewManager != null) if (GameMain.GameSession?.CrewManager != null)
{ {
orderIndicatorCount.Clear(); orderIndicatorCount.Clear();
foreach (Pair<Order, float> timedOrder in GameMain.GameSession.CrewManager.ActiveOrders) foreach (Pair<Order, float?> activeOrder in GameMain.GameSession.CrewManager.ActiveOrders)
{ {
DrawOrderIndicator(spriteBatch, cam, character, timedOrder.First, MathHelper.Clamp(timedOrder.Second / 10.0f, 0.2f, 1.0f)); if (activeOrder.Second.HasValue)
{
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First, iconAlpha: MathHelper.Clamp(activeOrder.Second.Value / 10.0f, 0.2f, 1.0f));
}
else
{
float iconAlpha = GetDistanceBasedIconAlpha(activeOrder.First.TargetSpatialEntity, maxDistance: 350.0f);
if (iconAlpha <= 0.0f) { continue; }
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First, iconAlpha: iconAlpha, createOffset: false, scaleMultiplier: 0.5f);
}
} }
if (character.CurrentOrder != null) if (character.CurrentOrder != null)
@@ -218,14 +232,18 @@ namespace Barotrauma
foreach (Item brokenItem in brokenItems) foreach (Item brokenItem in brokenItems)
{ {
if (brokenItem.NonInteractable) { continue; } if (brokenItem.NonInteractable) { continue; }
float dist = Vector2.Distance(character.WorldPosition, brokenItem.WorldPosition); float alpha = GetDistanceBasedIconAlpha(brokenItem);
Vector2 drawPos = brokenItem.DrawPosition;
float alpha = Math.Min((1000.0f - dist) / 1000.0f * 2.0f, 1.0f);
if (alpha <= 0.0f) continue; if (alpha <= 0.0f) continue;
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, GUI.BrokenIcon, GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUI.BrokenIcon,
Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha); Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
} }
float GetDistanceBasedIconAlpha(ISpatialEntity target, float maxDistance = 1000.0f)
{
float dist = Vector2.Distance(character.WorldPosition, target.WorldPosition);
return Math.Min((maxDistance - dist) / maxDistance * 2.0f, 1.0f);
}
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() == null))) if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() == null)))
{ {
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected) if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
@@ -362,8 +380,8 @@ namespace Barotrauma
(int)(HUDLayoutSettings.BottomRightInfoArea.X + HUDLayoutSettings.BottomRightInfoArea.Width * 0.05f), (int)(HUDLayoutSettings.BottomRightInfoArea.X + HUDLayoutSettings.BottomRightInfoArea.Width * 0.05f),
(int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f), (int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f),
(int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2), (int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2),
(int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f))); (int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f)), character.Info.IsDisguisedAsAnother);
character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true); character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true, character.Info.IsDisguisedAsAnother);
} }
mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud(); mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud();
if (mouseOnPortrait) if (mouseOnPortrait)
@@ -426,7 +444,7 @@ namespace Barotrauma
Vector2 startPos = character.DrawPosition + (character.FocusedCharacter.DrawPosition - character.DrawPosition) * 0.7f; Vector2 startPos = character.DrawPosition + (character.FocusedCharacter.DrawPosition - character.DrawPosition) * 0.7f;
startPos = cam.WorldToScreen(startPos); startPos = cam.WorldToScreen(startPos);
string focusName = character.FocusedCharacter.DisplayName; string focusName = character.FocusedCharacter.Info == null ? character.FocusedCharacter.DisplayName : character.FocusedCharacter.Info.DisplayName;
Vector2 textPos = startPos; Vector2 textPos = startPos;
Vector2 textSize = GUI.Font.MeasureString(focusName); Vector2 textSize = GUI.Font.MeasureString(focusName);
Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusName); Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusName);
@@ -475,19 +493,12 @@ namespace Barotrauma
return character.ShouldLockHud(); return character.ShouldLockHud();
} }
private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f) private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f, bool createOffset = true, float scaleMultiplier = 1.0f)
{ {
if (order?.SymbolSprite == null) { return; } if (order?.SymbolSprite == null) { return; }
if (order.IsReport && order.OrderGiver != character && !order.HasAppropriateJob(character)) { return; }
if (order.TargetAllCharacters) ISpatialEntity target = order.ConnectedController?.Item ?? order.TargetSpatialEntity;
{
if (order.OrderGiver != character && !order.HasAppropriateJob(character))
{
return;
}
}
ISpatialEntity target = order.ConnectedController != null ? order.ConnectedController.Item : order.TargetSpatialEntity;
if (target == null) { return; } if (target == null) { return; }
//don't show the indicator if far away and not inside the same sub //don't show the indicator if far away and not inside the same sub
@@ -503,7 +514,7 @@ namespace Barotrauma
Vector2 drawPos = target is Entity ? (target as Entity).DrawPosition : Vector2 drawPos = target is Entity ? (target as Entity).DrawPosition :
target.Submarine == null ? target.Position : target.Position + target.Submarine.DrawPosition; target.Submarine == null ? target.Position : target.Position + target.Submarine.DrawPosition;
drawPos += Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target]; drawPos += Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target];
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha); GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha, createOffset: createOffset, scaleMultiplier: scaleMultiplier);
orderIndicatorCount[target] = orderIndicatorCount[target] + 1; orderIndicatorCount[target] = orderIndicatorCount[target] + 1;
} }
@@ -6,6 +6,8 @@ using System.Collections.Generic;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq; using System.Xml.Linq;
using System.IO;
using Barotrauma.Items.Components;
namespace Barotrauma namespace Barotrauma
{ {
@@ -15,6 +17,12 @@ namespace Barotrauma
public bool LastControlled; public bool LastControlled;
private Sprite disguisedPortrait;
private List<WearableSprite> disguisedAttachmentSprites;
private Vector2? disguisedSheetIndex;
private Sprite disguisedJobIcon;
private Color disguisedJobColor;
public static void Init() public static void Init()
{ {
infoAreaPortraitBG = GUI.Style.GetComponentStyle("InfoAreaPortraitBG")?.GetDefaultSprite(); infoAreaPortraitBG = GUI.Style.GetComponentStyle("InfoAreaPortraitBG")?.GetDefaultSprite();
@@ -175,6 +183,195 @@ namespace Barotrauma
} }
} }
private void GetDisguisedSprites(IdCard idCard)
{
if (idCard.StoredJobPrefab == null || idCard.StoredPortrait == null)
{
string[] readTags = idCard.Item.Tags.Split(',');
if (idCard.StoredJobPrefab == null)
{
string jobIdTag = readTags.First(s => s.StartsWith("jobid:"));
if (jobIdTag != string.Empty && jobIdTag.Length > 6)
{
string jobId = jobIdTag.Substring(6);
if (jobId != string.Empty)
{
idCard.StoredJobPrefab = JobPrefab.Get(jobId);
}
}
}
if (idCard.StoredPortrait == null)
{
string disguisedGender = string.Empty;
string disguisedRace = string.Empty;
string disguisedHeadSpriteId = string.Empty;
int disguisedHairIndex = -1;
int disguisedBeardIndex = -1;
int disguisedMoustacheIndex = -1;
int disguisedFaceAttachmentIndex = -1;
foreach (string tag in readTags)
{
string[] s = tag.Split(':');
switch (s[0])
{
case "gender":
disguisedGender = s[1];
break;
case "race":
disguisedRace = s[1];
break;
case "headspriteid":
disguisedHeadSpriteId = s[1];
break;
case "hairindex":
disguisedHairIndex = int.Parse(s[1]);
break;
case "beardindex":
disguisedBeardIndex = int.Parse(s[1]);
break;
case "moustacheindex":
disguisedMoustacheIndex = int.Parse(s[1]);
break;
case "faceattachmentindex":
disguisedFaceAttachmentIndex = int.Parse(s[1]);
break;
case "sheetindex":
string[] vectorValues = s[1].Split(";");
idCard.StoredSheetIndex = new Vector2(float.Parse(vectorValues[0]), float.Parse(vectorValues[1]));
break;
}
}
if (disguisedGender == string.Empty || disguisedRace == string.Empty || disguisedHeadSpriteId == string.Empty)
{
idCard.StoredPortrait = null;
idCard.StoredAttachments = null;
return;
}
foreach (XElement limbElement in Ragdoll.MainElement.Elements())
{
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
XElement spriteElement = limbElement.Element("sprite");
if (spriteElement == null) { continue; }
string spritePath = spriteElement.Attribute("texture").Value;
spritePath = spritePath.Replace("[GENDER]", disguisedGender);
spritePath = spritePath.Replace("[RACE]", disguisedRace.ToLowerInvariant());
spritePath = spritePath.Replace("[HEADID]", disguisedHeadSpriteId);
string fileName = Path.GetFileNameWithoutExtension(spritePath);
//go through the files in the directory to find a matching sprite
foreach (string file in Directory.GetFiles(Path.GetDirectoryName(spritePath)))
{
if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
{
continue;
}
string fileWithoutTags = Path.GetFileNameWithoutExtension(file);
fileWithoutTags = fileWithoutTags.Split('[', ']').First();
if (fileWithoutTags != fileName) { continue; }
idCard.StoredPortrait = new Sprite(spriteElement, "", file) { RelativeOrigin = Vector2.Zero };
break;
}
break;
}
if (Wearables != null)
{
XElement disguisedHairElement, disguisedBeardElement, disguisedMoustacheElement, disguisedFaceAttachmentElement;
List<XElement> disguisedHairs, disguisedBeards, disguisedMoustaches, disguisedFaceAttachments;
Gender disguisedGenderEnum = disguisedGender == "female" ? Gender.Female : Gender.Male;
Race disguisedRaceEnum = (Race)Enum.Parse(typeof(Race), disguisedRace);
int headSpriteId = int.Parse(disguisedHeadSpriteId);
float commonness = disguisedGenderEnum == Gender.Female ? 0.05f : 0.2f;
disguisedHairs = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Hair, headSpriteId), WearableType.Hair, commonness);
disguisedBeards = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Beard, headSpriteId), WearableType.Beard);
disguisedMoustaches = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Moustache, headSpriteId), WearableType.Moustache);
disguisedFaceAttachments = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.FaceAttachment, headSpriteId), WearableType.FaceAttachment);
if (IsValidIndex(disguisedHairIndex, disguisedHairs))
{
disguisedHairElement = disguisedHairs[disguisedHairIndex];
}
else
{
disguisedHairElement = GetRandomElement(disguisedHairs);
}
if (IsValidIndex(disguisedBeardIndex, disguisedBeards))
{
disguisedBeardElement = disguisedBeards[disguisedBeardIndex];
}
else
{
disguisedBeardElement = GetRandomElement(disguisedBeards);
}
if (IsValidIndex(disguisedMoustacheIndex, disguisedMoustaches))
{
disguisedMoustacheElement = disguisedMoustaches[disguisedMoustacheIndex];
}
else
{
disguisedMoustacheElement = GetRandomElement(disguisedMoustaches);
}
if (IsValidIndex(disguisedFaceAttachmentIndex, disguisedFaceAttachments))
{
disguisedFaceAttachmentElement = disguisedFaceAttachments[disguisedFaceAttachmentIndex];
}
else
{
disguisedFaceAttachmentElement = GetRandomElement(disguisedFaceAttachments);
}
idCard.StoredAttachments = new List<WearableSprite>();
disguisedFaceAttachmentElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.FaceAttachment)));
disguisedBeardElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Beard)));
disguisedMoustacheElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Moustache)));
disguisedHairElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Hair)));
if (OmitJobInPortraitClothing)
{
JobPrefab.NoJobElement?.Element("PortraitClothing")?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
else
{
idCard.StoredJobPrefab?.ClothingElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
}
}
}
if (idCard.StoredJobPrefab != null)
{
disguisedJobIcon = idCard.StoredJobPrefab.Icon;
disguisedJobColor = idCard.StoredJobPrefab.UIColor;
}
disguisedPortrait = idCard.StoredPortrait;
disguisedSheetIndex = idCard.StoredSheetIndex;
disguisedAttachmentSprites = idCard.StoredAttachments;
}
partial void LoadAttachmentSprites(bool omitJob) partial void LoadAttachmentSprites(bool omitJob)
{ {
if (attachmentSprites == null) if (attachmentSprites == null)
@@ -219,23 +416,42 @@ namespace Barotrauma
HUDLayoutSettings.BottomRightInfoArea.Height / (float)infoAreaPortraitBG.SourceRect.Height)); HUDLayoutSettings.BottomRightInfoArea.Height / (float)infoAreaPortraitBG.SourceRect.Height));
} }
public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false) public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false, bool evaluateDisguise = false)
{ {
if (Portrait != null) if (evaluateDisguise && IsDisguised) return;
Vector2? sheetIndex;
Sprite portraitToDraw;
List<WearableSprite> attachmentsToDraw;
if (!IsDisguisedAsAnother || !evaluateDisguise)
{
sheetIndex = Head.SheetIndex;
portraitToDraw = Portrait;
attachmentsToDraw = AttachmentSprites;
}
else
{
sheetIndex = disguisedSheetIndex;
portraitToDraw = disguisedPortrait;
attachmentsToDraw = disguisedAttachmentSprites;
}
if (portraitToDraw != null)
{ {
// Scale down the head sprite 10% // Scale down the head sprite 10%
float scale = targetWidth * 0.9f / Portrait.size.X; float scale = targetWidth * 0.9f / Portrait.size.X;
if (Head.SheetIndex.HasValue) if (sheetIndex.HasValue)
{ {
Portrait.SourceRect = new Rectangle(CalculateOffset(Portrait, Head.SheetIndex.Value.ToPoint()), Portrait.SourceRect.Size); portraitToDraw.SourceRect = new Rectangle(CalculateOffset(portraitToDraw, sheetIndex.Value.ToPoint()), portraitToDraw.SourceRect.Size);
} }
Portrait.Draw(spriteBatch, screenPos + offset, Color.White, Portrait.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None); portraitToDraw.Draw(spriteBatch, screenPos + offset, Color.White, portraitToDraw.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
if (AttachmentSprites != null) if (attachmentsToDraw != null)
{ {
float depthStep = 0.000001f; float depthStep = 0.000001f;
foreach (var attachment in AttachmentSprites) foreach (var attachment in attachmentsToDraw)
{ {
DrawAttachmentSprite(spriteBatch, attachment, Portrait, screenPos + offset, scale, depthStep, flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None); DrawAttachmentSprite(spriteBatch, attachment, portraitToDraw, sheetIndex, screenPos + offset, scale, depthStep, flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
depthStep += depthStep; depthStep += depthStep;
} }
} }
@@ -258,30 +474,34 @@ namespace Barotrauma
float depthStep = 0.000001f; float depthStep = 0.000001f;
foreach (var attachment in AttachmentSprites) foreach (var attachment in AttachmentSprites)
{ {
DrawAttachmentSprite(spriteBatch, attachment, headSprite, screenPos, scale, depthStep); DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep);
depthStep += depthStep; depthStep += depthStep;
} }
} }
} }
} }
public void DrawJobIcon(SpriteBatch spriteBatch, Vector2 pos, float scale = 1.0f) public void DrawJobIcon(SpriteBatch spriteBatch, Vector2 pos, float scale = 1.0f, bool evaluateDisguise = false)
{ {
var icon = Job?.Prefab?.Icon; if (evaluateDisguise && IsDisguised) return;
var icon = !IsDisguisedAsAnother || !evaluateDisguise ? Job?.Prefab?.Icon : disguisedJobIcon;
if (icon == null) { return; } if (icon == null) { return; }
icon.Draw(spriteBatch, pos, Job.Prefab.UIColor, scale: scale); Color iconColor = !IsDisguisedAsAnother || !evaluateDisguise ? Job.Prefab.UIColor : disguisedJobColor;
}
public void DrawJobIcon(SpriteBatch spriteBatch, Rectangle area) icon.Draw(spriteBatch, pos, iconColor, scale: scale);
{
var icon = Job?.Prefab?.Icon;
if (icon == null) { return; }
icon.Draw(spriteBatch,
area.Center.ToVector2(),
Job.Prefab.UIColor,
scale: Math.Min(area.Width / (float)icon.SourceRect.Width, area.Height / (float)icon.SourceRect.Height));
} }
private void DrawAttachmentSprite(SpriteBatch spriteBatch, WearableSprite attachment, Sprite head, Vector2 drawPos, float scale, float depthStep, SpriteEffects spriteEffects = SpriteEffects.None) public void DrawJobIcon(SpriteBatch spriteBatch, Rectangle area, bool evaluateDisguise = false)
{
if (evaluateDisguise && IsDisguised) return;
var icon = !IsDisguisedAsAnother || !evaluateDisguise ? Job?.Prefab?.Icon : disguisedJobIcon;
if (icon == null) { return; }
Color iconColor = !IsDisguisedAsAnother || !evaluateDisguise ? Job.Prefab.UIColor : disguisedJobColor;
icon.Draw(spriteBatch, area.Center.ToVector2(), iconColor, scale: Math.Min(area.Width / (float)icon.SourceRect.Width, area.Height / (float)icon.SourceRect.Height));
}
private void DrawAttachmentSprite(SpriteBatch spriteBatch, WearableSprite attachment, Sprite head, Vector2? sheetIndex, Vector2 drawPos, float scale, float depthStep, SpriteEffects spriteEffects = SpriteEffects.None)
{ {
if (attachment.InheritSourceRect) if (attachment.InheritSourceRect)
{ {
@@ -289,9 +509,9 @@ namespace Barotrauma
{ {
attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, attachment.SheetIndex.Value), head.SourceRect.Size); attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, attachment.SheetIndex.Value), head.SourceRect.Size);
} }
else if (Head.SheetIndex.HasValue) else if (sheetIndex.HasValue)
{ {
attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, Head.SheetIndex.Value.ToPoint()), head.SourceRect.Size); attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, sheetIndex.Value.ToPoint()), head.SourceRect.Size);
} }
else else
{ {
@@ -84,6 +84,10 @@ namespace Barotrauma
{ {
newMem.interact = FocusedCharacter.ID; newMem.interact = FocusedCharacter.ID;
} }
else if (newMem.states.HasFlag(InputNetFlags.Use) && (FocusedCharacter?.IsPet ?? false))
{
newMem.interact = FocusedCharacter.ID;
}
else if (focusedItem != null && !CharacterInventory.DraggingItemToWorld && else if (focusedItem != null && !CharacterInventory.DraggingItemToWorld &&
!newMem.states.HasFlag(InputNetFlags.Grab) && !newMem.states.HasFlag(InputNetFlags.Health)) !newMem.states.HasFlag(InputNetFlags.Grab) && !newMem.states.HasFlag(InputNetFlags.Health))
{ {
@@ -254,6 +258,7 @@ namespace Barotrauma
if (readStatus) if (readStatus)
{ {
ReadStatus(msg); ReadStatus(msg);
(AIController as EnemyAIController)?.PetBehavior?.ClientRead(msg);
} }
msg.ReadPadBits(); msg.ReadPadBits();
@@ -411,8 +416,7 @@ namespace Barotrauma
Character character = null; Character character = null;
if (noInfo) if (noInfo)
{ {
character = Create(speciesName, position, seed, null, false); character = Create(speciesName, position, seed, characterInfo: null, id: id, isRemotePlayer: false);
character.ID = id;
bool containsStatusData = inc.ReadBoolean(); bool containsStatusData = inc.ReadBoolean();
if (containsStatusData) if (containsStatusData)
{ {
@@ -429,8 +433,7 @@ namespace Barotrauma
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc); CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
character = Create(speciesName, position, seed, info, ownerId > 0 && GameMain.Client.ID != ownerId, hasAi); character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.ID != ownerId, hasAi: hasAi);
character.ID = id;
character.TeamID = (TeamType)teamID; character.TeamID = (TeamType)teamID;
character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte(); character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte();
if (character.CampaignInteractionType != CampaignMode.InteractionType.None) if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
@@ -6,7 +6,7 @@ namespace Barotrauma
{ {
public enum SoundType public enum SoundType
{ {
Idle, Attack, Die, Damage Idle, Attack, Die, Damage, Happy, Unhappy
} }
private readonly RoundSound roundSound; private readonly RoundSound roundSound;
@@ -53,7 +53,7 @@ namespace Barotrauma
private GUIFrame healthWindow; private GUIFrame healthWindow;
private GUIComponent deadIndicator; private GUITextBlock deadIndicator;
private GUIComponent lowSkillIndicator; private GUIComponent lowSkillIndicator;
@@ -225,7 +225,16 @@ namespace Barotrauma
Character.Controlled.ResetInteract = true; Character.Controlled.ResetInteract = true;
if (openHealthWindow != null) if (openHealthWindow != null)
{ {
openHealthWindow.characterName.Text = value.Character.Name; 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) if (Character.Controlled.SelectedConstruction != null && Character.Controlled.SelectedConstruction.GetComponent<Ladder>() == null)
{ {
Character.Controlled.SelectedConstruction = null; Character.Controlled.SelectedConstruction = null;
@@ -322,11 +331,19 @@ namespace Barotrauma
} }
); );
deadIndicator = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.1f), limbSelection.RectTransform, Anchor.Center), deadIndicator = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.1f), limbSelection.RectTransform, Anchor.Center),
text: TextManager.Get("Deceased"), font: GUI.LargeFont, textAlignment: Alignment.Center, wrap: true, style: "GUIToolTip") text: TextManager.Get("Deceased"), font: GUI.LargeFont, textAlignment: Alignment.Center, style: "GUIToolTip")
{ {
Visible = false, Visible = false,
CanBeFocused = false CanBeFocused = false
}; };
if (deadIndicator.Text.Contains(' '))
{
deadIndicator.Wrap = true;
}
else
{
deadIndicator.AutoScaleHorizontal = true;
}
var rightSide = new GUIFrame(new RectTransform(new Vector2(0.4f, 1.0f), paddedHealthWindow.RectTransform), style: null); var rightSide = new GUIFrame(new RectTransform(new Vector2(0.4f, 1.0f), paddedHealthWindow.RectTransform), style: null);
@@ -397,15 +414,17 @@ namespace Barotrauma
CanBeFocused = true CanBeFocused = true
}; };
textLayout.RectTransform.RelativeOffset = new Vector2(0, 0.025f);
var nameContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), textLayout.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true) var nameContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), textLayout.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true)
{ {
Stretch = true Stretch = true
}; };
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform), new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform, Anchor.CenterLeft),
onDraw: (spriteBatch, component) => onDraw: (spriteBatch, component) =>
{ {
character.Info.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), Vector2.Zero, component.Rect.Width); character.Info.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), Vector2.Zero, component.Rect.Width, false, openHealthWindow?.Character != Character.Controlled);
}); });
characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont)
{ {
@@ -414,7 +433,7 @@ namespace Barotrauma
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform), new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform),
onDraw: (spriteBatch, component) => onDraw: (spriteBatch, component) =>
{ {
character.Info.DrawJobIcon(spriteBatch, component.Rect); character.Info.DrawJobIcon(spriteBatch, component.Rect, openHealthWindow?.Character != Character.Controlled);
}); });
@@ -943,6 +962,28 @@ namespace Barotrauma
SuicideButton.Visible = Character == Character.Controlled && !Character.IsDead && Character.IsIncapacitated; SuicideButton.Visible = Character == Character.Controlled && !Character.IsDead && Character.IsIncapacitated;
if (GameMain.GameSession?.Campaign is { } campaign)
{
RectTransform endRoundButton = campaign?.EndRoundButton?.RectTransform;
RectTransform readyCheckButton = campaign?.ReadyCheckButton?.RectTransform;
if (endRoundButton != null)
{
if (SuicideButton.Visible)
{
Point offset = new Point(0, SuicideButton.Rect.Height);
endRoundButton.ScreenSpaceOffset = offset;
}
else if (endRoundButton.ScreenSpaceOffset != Point.Zero)
{
endRoundButton.ScreenSpaceOffset = Point.Zero;
}
if (readyCheckButton != null)
{
readyCheckButton.ScreenSpaceOffset = endRoundButton.ScreenSpaceOffset;
}
}
}
cprButton.Visible = cprButton.Visible =
Character == Character.Controlled?.SelectedCharacter Character == Character.Controlled?.SelectedCharacter
&& (Character.IsUnconscious || Character.Stun > 0.0f) && (Character.IsUnconscious || Character.Stun > 0.0f)
@@ -328,8 +328,7 @@ namespace Barotrauma
deformation = ragdoll.Limbs deformation = ragdoll.Limbs
.Where(l => l != null) .Where(l => l != null)
.SelectMany(l => l.Deformations) .SelectMany(l => l.Deformations)
.Where(d => d.TypeName == typeName && d.Sync == sync) .FirstOrDefault(d => d.TypeName == typeName && d.Sync == sync);
.FirstOrDefault();
} }
if (deformation == null) if (deformation == null)
{ {
@@ -971,11 +970,11 @@ namespace Barotrauma
XElement element; XElement element;
if (random) if (random)
{ {
element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), type)?.GetRandom(Rand.RandSync.ClientOnly); element = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Gender, info.Race), type, info.Head.HeadSpriteId)?.GetRandom(Rand.RandSync.ClientOnly);
} }
else else
{ {
element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), type)?.FirstOrDefault(); element = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Gender, info.Race), type, info.Head.HeadSpriteId)?.FirstOrDefault();
} }
if (element != null) if (element != null)
{ {
@@ -491,7 +491,11 @@ namespace Barotrauma
{ {
SteamManager.NetworkingDebugLog = !SteamManager.NetworkingDebugLog; SteamManager.NetworkingDebugLog = !SteamManager.NetworkingDebugLog;
SteamManager.SetSteamworksNetworkingDebugLog(SteamManager.NetworkingDebugLog); SteamManager.SetSteamworksNetworkingDebugLog(SteamManager.NetworkingDebugLog);
}));
commands.Add(new Command("readycheck", "Commence a ready check in multiplayer.", (string[] args) =>
{
NewMessage("Ready checks can only be commenced in multiplayer.", Color.Red);
})); }));
AssignRelayToServer("kick", false); AssignRelayToServer("kick", false);
@@ -527,6 +531,7 @@ namespace Barotrauma
AssignRelayToServer("traitorlist", true); AssignRelayToServer("traitorlist", true);
AssignRelayToServer("money", true); AssignRelayToServer("money", true);
AssignRelayToServer("setskill", true); AssignRelayToServer("setskill", true);
AssignRelayToServer("readycheck", true);
AssignOnExecute("control", (string[] args) => AssignOnExecute("control", (string[] args) =>
{ {
@@ -0,0 +1,15 @@
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma
{
partial class BeaconMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
return;
}
}
}
@@ -2,7 +2,7 @@
namespace Barotrauma namespace Barotrauma
{ {
partial class CombatMission partial class CombatMission : Mission
{ {
public override string Description public override string Description
{ {
@@ -0,0 +1,56 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class MineralMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
for (int i = 0; i < ResourceClusters.Count; i++)
{
var amount = msg.ReadByte();
var rotation = msg.ReadSingle();
for (int j = 0; j < amount; j++)
{
var item = Item.ReadSpawnData(msg);
if (item.GetComponent<Holdable>() is Holdable h)
{
h.AttachToWall();
item.Rotation = rotation;
}
if (SpawnedResources.TryGetValue(item.Prefab.Identifier, out var resources))
{
resources.Add(item);
}
else
{
SpawnedResources.Add(item.Prefab.Identifier, new List<Item>() { item });
}
}
}
CalculateMissionClusterPositions();
for(int i = 0; i < ResourceClusters.Count; i++)
{
var identifier = msg.ReadString();
var count = msg.ReadByte();
var resources = new Item[count];
for (int j = 0; j < count; j++)
{
var id = msg.ReadUInt16();
var entity = Entity.FindEntityByID(id);
if (!(entity is Item item)) { continue; }
resources[j] = item;
}
RelevantLevelResources.Add(identifier, resources);
}
}
}
}
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
{ {
partial class Mission abstract partial class Mission
{ {
partial void ShowMessageProjSpecific(int missionState) partial void ShowMessageProjSpecific(int missionState)
{ {
@@ -3,7 +3,7 @@ using System;
namespace Barotrauma namespace Barotrauma
{ {
partial class MissionMode : GameMode abstract partial class MissionMode : GameMode
{ {
public override void ShowStartMessage() public override void ShowStartMessage()
{ {
@@ -0,0 +1,26 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
partial class NestMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
nestPosition = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)
{
var item = Item.ReadSpawnData(msg);
items.Add(item);
if (item.body != null)
{
item.body.FarseerBody.BodyType = BodyType.Kinematic;
}
}
}
}
}
@@ -1256,17 +1256,26 @@ namespace Barotrauma
#region Element drawing #region Element drawing
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color)
/// <param name="createOffset">Should the indicator move based on the camera position?</param>
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color, bool createOffset = true, float scaleMultiplier = 1.0f)
{ {
Vector2 diff = worldPosition - cam.WorldViewCenter; Vector2 diff = worldPosition - cam.WorldViewCenter;
float dist = diff.Length(); float dist = diff.Length();
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f); float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier;
if (dist > hideDist) if (dist > hideDist)
{ {
float alpha = Math.Min((dist - hideDist) / 100.0f, 1.0f); float alpha = Math.Min((dist - hideDist) / 100.0f, 1.0f);
Vector2 targetScreenPos = cam.WorldToScreen(worldPosition); Vector2 targetScreenPos = cam.WorldToScreen(worldPosition);
if (!createOffset)
{
sprite.Draw(spriteBatch, targetScreenPos, color * alpha, rotate: 0.0f, scale: symbolScale);
return;
}
float screenDist = Vector2.Distance(cam.WorldToScreen(cam.WorldViewCenter), targetScreenPos); float screenDist = Vector2.Distance(cam.WorldToScreen(cam.WorldViewCenter), targetScreenPos);
float angle = MathUtils.VectorToAngle(diff); float angle = MathUtils.VectorToAngle(diff);
@@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -218,6 +219,12 @@ namespace Barotrauma
GUI.Style.ButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent)); GUI.Style.ButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
} }
if (UserData is string s && s == "ReadyCheckButton" && ReadyCheck.lastReadyCheck > DateTime.Now)
{
float progress = (ReadyCheck.lastReadyCheck - DateTime.Now).Seconds / 60.0f;
Frame.Color = ToolBox.GradientLerp(progress, Color.White, GUI.Style.Red);
}
} }
protected override void Update(float deltaTime) protected override void Update(float deltaTime)
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma namespace Barotrauma
{ {
@@ -20,7 +21,8 @@ namespace Barotrauma
public enum Type public enum Type
{ {
Default, Default,
InGame InGame,
Vote
} }
public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>(); public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
@@ -47,6 +49,9 @@ namespace Barotrauma
Icon.Color = value; Icon.Color = value;
} }
} }
public bool Draggable { get; set; }
public Vector2 DraggingPosition = Vector2.Zero;
public GUIImage BackgroundIcon { get; private set; } public GUIImage BackgroundIcon { get; private set; }
private GUIImage newBackgroundIcon; private GUIImage newBackgroundIcon;
@@ -60,7 +65,7 @@ namespace Barotrauma
private bool iconSwitching; private bool iconSwitching;
private bool closing; private bool closing;
private Type type; private readonly Type type;
public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault(); public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
@@ -97,12 +102,25 @@ namespace Barotrauma
}; };
} }
InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, type == Type.InGame ? Anchor.TopCenter : Anchor.Center) { IsFixedSize = false }, style: null); Anchor anchor = type switch
{
Type.InGame => Anchor.TopCenter,
Type.Vote => Anchor.TopRight,
_ => Anchor.Center
};
InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, anchor) { IsFixedSize = false }, style: null);
if (type == Type.Vote)
{
int offset = GUI.IntScale(64);
InnerFrame.RectTransform.ScreenSpaceOffset = new Point(-offset, offset);
CanBeFocused = false;
}
GUI.Style.Apply(InnerFrame, "", this); GUI.Style.Apply(InnerFrame, "", this);
this.type = type; this.type = type;
Tag = tag; Tag = tag;
if (type == Type.Default) if (type == Type.Default || type == Type.Vote)
{ {
Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 }; Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 };
@@ -271,92 +289,114 @@ namespace Barotrauma
protected override void Update(float deltaTime) protected override void Update(float deltaTime)
{ {
if (type != Type.InGame) { return; } if (Draggable)
Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
if (!closing)
{ {
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint(); if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton))
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{ {
BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2); GUI.MouseCursor = CursorState.Move;
if (!MathUtils.NearlyEqual(openState, 1.0f)) if (PlayerInput.PrimaryMouseButtonDown())
{ {
BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White); DraggingPosition = RectTransform.ScreenSpaceOffset.ToVector2() - PlayerInput.MousePosition;
} }
} }
if (!(Screen.Selected is RoundSummaryScreen) && !MessageBoxes.Any(mb => mb.UserData is RoundSummary))
{
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
}
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose) if (PlayerInput.PrimaryMouseButtonHeld() && DraggingPosition != Vector2.Zero)
{ {
inGameCloseTimer += deltaTime; GUI.MouseCursor = CursorState.Dragging;
} RectTransform.ScreenSpaceOffset = (PlayerInput.MousePosition + DraggingPosition).ToPoint();
if (inGameCloseTimer >= inGameCloseTime)
{
Close();
}
}
else
{
openState += deltaTime * 2.0f;
Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
}
if (openState >= 2.0f)
{
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
}
if (newBackgroundIcon != null)
{
if (!iconSwitching)
{
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
if (BackgroundIcon.Color.A == 0)
{
BackgroundIcon = null;
iconSwitching = true;
RemoveChild(BackgroundIcon);
}
}
else
{
iconSwitching = true;
}
iconState = 0;
} }
else else
{ {
newBackgroundIcon.SetAsFirstChild(); DraggingPosition = Vector2.Zero;
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2); }
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White); }
if (newBackgroundIcon.Color.A == 255)
{ if (type == Type.InGame)
BackgroundIcon = newBackgroundIcon; {
BackgroundIcon.SetAsFirstChild(); Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
newBackgroundIcon = null; Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
iconSwitching = false; Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
}
if (!closing)
iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f); {
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
if (!MathUtils.NearlyEqual(openState, 1.0f))
{
BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
}
}
if (!(Screen.Selected is RoundSummaryScreen) && !MessageBoxes.Any(mb => mb.UserData is RoundSummary))
{
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
}
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
{
inGameCloseTimer += deltaTime;
}
if (inGameCloseTimer >= inGameCloseTime)
{
Close();
}
}
else
{
openState += deltaTime * 2.0f;
Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
}
if (openState >= 2.0f)
{
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
}
if (newBackgroundIcon != null)
{
if (!iconSwitching)
{
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
if (BackgroundIcon.Color.A == 0)
{
BackgroundIcon = null;
iconSwitching = true;
RemoveChild(BackgroundIcon);
}
}
else
{
iconSwitching = true;
}
iconState = 0;
}
else
{
newBackgroundIcon.SetAsFirstChild();
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White);
if (newBackgroundIcon.Color.A == 255)
{
BackgroundIcon = newBackgroundIcon;
BackgroundIcon.SetAsFirstChild();
newBackgroundIcon = null;
iconSwitching = false;
}
iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
}
} }
} }
} }
@@ -707,6 +707,8 @@ namespace Barotrauma
private void SortItems(GUIListBox list, SortingMethod sortingMethod) private void SortItems(GUIListBox list, SortingMethod sortingMethod)
{ {
if (CurrentLocation == null) { return; }
if (sortingMethod == SortingMethod.AlphabeticalAsc || sortingMethod == SortingMethod.AlphabeticalDesc) if (sortingMethod == SortingMethod.AlphabeticalAsc || sortingMethod == SortingMethod.AlphabeticalDesc)
{ {
list.Content.RectTransform.SortChildren( list.Content.RectTransform.SortChildren(
@@ -868,13 +870,13 @@ namespace Barotrauma
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f), TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
UserData = "price" UserData = "price"
}; };
if(listBox == storeSellList || listBox == shoppingCrateSellList) if (listBox == storeSellList || listBox == shoppingCrateSellList)
{ {
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation.GetAdjustedItemSellPrice(priceInfo)); priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(priceInfo) ?? 0);
} }
else else
{ {
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation.GetAdjustedItemBuyPrice(priceInfo)); priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(priceInfo) ?? 0);
} }
if (listBox == storeDealsList || listBox == storeBuyList || listBox == storeSellList) if (listBox == storeDealsList || listBox == storeBuyList || listBox == storeSellList)
@@ -525,6 +525,7 @@ namespace Barotrauma
Tutorials.Tutorial.Init(); Tutorials.Tutorial.Init();
MapGenerationParams.Init(); MapGenerationParams.Init();
LevelGenerationParams.LoadPresets(); LevelGenerationParams.LoadPresets();
CaveGenerationParams.LoadPresets();
OutpostGenerationParams.LoadPresets(); OutpostGenerationParams.LoadPresets();
WreckAIConfig.LoadAll(); WreckAIConfig.LoadAll();
EventSet.LoadPrefabs(); EventSet.LoadPrefabs();
@@ -533,6 +534,7 @@ namespace Barotrauma
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings)); SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
Order.Init(); Order.Init();
EventManagerSettings.Init(); EventManagerSettings.Init();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
TitleScreen.LoadState = 50.0f; TitleScreen.LoadState = 50.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -232,7 +232,7 @@ namespace Barotrauma
}; };
} }
var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null); var reports = Order.PrefabList.FindAll(o => o.IsReport && o.SymbolSprite != null);
if (reports.None()) if (reports.None())
{ {
DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined."); DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined.");
@@ -252,7 +252,7 @@ namespace Barotrauma
//report buttons //report buttons
foreach (Order order in reports) foreach (Order order in reports)
{ {
if (!order.TargetAllCharacters || order.SymbolSprite == null) { continue; } if (!order.IsReport || order.SymbolSprite == null) { continue; }
var btn = new GUIButton(new RectTransform(new Point(ReportButtonFrame.Rect.Width), ReportButtonFrame.RectTransform), style: null) var btn = new GUIButton(new RectTransform(new Point(ReportButtonFrame.Rect.Width), ReportButtonFrame.RectTransform), style: null)
{ {
OnClicked = (GUIButton button, object userData) => OnClicked = (GUIButton button, object userData) =>
@@ -667,6 +667,8 @@ namespace Barotrauma
#region Crew List Order Displayment #region Crew List Order Displayment
// TODO: CHECK ALL THE ORDER CONSTUCTOR CALLS
/// <summary> /// <summary>
/// Sets the character's current order (if it's close enough to receive messages from orderGiver) and /// Sets the character's current order (if it's close enough to receive messages from orderGiver) and
/// displays the order in the crew UI /// displays the order in the crew UI
@@ -675,16 +677,69 @@ namespace Barotrauma
{ {
if (order != null && order.TargetAllCharacters) if (order != null && order.TargetAllCharacters)
{ {
if (orderGiver == null || orderGiver.CurrentHull == null) { return; } Hull hull = null;
var hull = orderGiver.CurrentHull; if (order.IsReport)
AddOrder(new Order(order.Prefab ?? order, hull, null, orderGiver), order.FadeOutTime); {
if (orderGiver?.CurrentHull == null) { return; }
hull = orderGiver.CurrentHull;
AddOrder(new Order(order.Prefab ?? order, hull, null, orderGiver), order.FadeOutTime);
}
else if(order.IsIgnoreOrder)
{
WallSection ws = null;
if (order.TargetType == Order.OrderTargetType.Entity && order.TargetEntity is MapEntity me)
{
if (order.Identifier == "ignorethis")
{
me.SetIgnoreByAI(true);
AddOrder(new Order(order.Prefab ?? order, order.TargetEntity, order.TargetItemComponent, orderGiver), null);
}
else
{
me.SetIgnoreByAI(false);
ActiveOrders.RemoveAll(p => p.First.Identifier == "ignorethis" && p.First.TargetEntity == order.TargetEntity);
}
}
else if (order.TargetType == Order.OrderTargetType.WallSection && order.TargetEntity is Structure s)
{
var wallSectionIndex = order.WallSectionIndex ?? s.Sections.IndexOf(wallContext);
ws = s.GetSection(wallSectionIndex);
if (ws != null)
{
if (order.Identifier == "ignorethis")
{
ws.SetIgnoreByAI(true);
AddOrder(new Order(order.Prefab ?? order, s, wallSectionIndex, orderGiver), null);
}
else
{
ws.SetIgnoreByAI(false);
ActiveOrders.RemoveAll(p => p.First.Identifier == "ignorethis" && p.First.TargetEntity == s && p.First.WallSectionIndex == wallSectionIndex);
}
}
}
if (ws != null)
{
hull = Hull.FindHull(ws.WorldPosition);
}
else if (order.TargetEntity is Item i)
{
hull = i.CurrentHull;
}
else if (order.TargetEntity is ISpatialEntity se)
{
hull = Hull.FindHull(se.WorldPosition);
}
}
if (IsSinglePlayer) if (IsSinglePlayer)
{ {
orderGiver.Speak(order.GetChatMessage("", hull.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order); orderGiver.Speak(order.GetChatMessage("", hull?.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order);
} }
else else
{ {
OrderChatMessage msg = new OrderChatMessage(order, "", hull, null, orderGiver); OrderChatMessage msg = new OrderChatMessage(order, "", order.IsReport ? hull : order.TargetEntity, null, orderGiver);
GameMain.Client?.SendChatMessage(msg); GameMain.Client?.SendChatMessage(msg);
} }
} }
@@ -696,7 +751,7 @@ namespace Barotrauma
if (IsSinglePlayer) if (IsSinglePlayer)
{ {
character.SetOrder(order, option, orderGiver, speak: orderGiver != character); character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
orderGiver?.Speak(order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null); orderGiver?.Speak(order?.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option));
} }
else if (orderGiver != null) else if (orderGiver != null)
{ {
@@ -1591,6 +1646,7 @@ namespace Barotrauma
private Character characterContext; private Character characterContext;
private Item itemContext; private Item itemContext;
private Hull hullContext; private Hull hullContext;
private WallSection wallContext;
private bool isContextual; private bool isContextual;
private readonly List<Order> contextualOrders = new List<Order>(); private readonly List<Order> contextualOrders = new List<Order>();
private Point shorcutCenterNodeOffset; private Point shorcutCenterNodeOffset;
@@ -1640,7 +1696,7 @@ namespace Barotrauma
} }
} }
else if (TryGetBreachedHullAtHoveredWall(out Hull breachedHull)) else if (TryGetBreachedHullAtHoveredWall(out Hull breachedHull, out wallContext))
{ {
return breachedHull; return breachedHull;
} }
@@ -1662,16 +1718,24 @@ namespace Barotrauma
if (entityContext is Character character) if (entityContext is Character character)
{ {
characterContext = character; characterContext = character;
itemContext = null;
hullContext = null;
wallContext = null;
isContextual = false; isContextual = false;
} }
else if (entityContext is Item item) else if (entityContext is Item item)
{ {
itemContext = item; itemContext = item;
characterContext = null;
hullContext = null;
wallContext = null;
isContextual = true; isContextual = true;
} }
else if (entityContext is Hull hull) else if (entityContext is Hull hull)
{ {
hullContext = hull; hullContext = hull;
characterContext = null;
itemContext = null;
isContextual = true; isContextual = true;
} }
@@ -1785,7 +1849,7 @@ namespace Barotrauma
availableCategories = new List<OrderCategory>(); availableCategories = new List<OrderCategory>();
foreach (OrderCategory category in Enum.GetValues(typeof(OrderCategory))) foreach (OrderCategory category in Enum.GetValues(typeof(OrderCategory)))
{ {
if (Order.PrefabList.Any(o => o.Category == category && !o.TargetAllCharacters)) if (Order.PrefabList.Any(o => o.Category == category && !o.IsReport))
{ {
availableCategories.Add(category); availableCategories.Add(category);
} }
@@ -2129,7 +2193,7 @@ namespace Barotrauma
if (Order.GetPrefab(orderIdentifier) is Order orderPrefab && if (Order.GetPrefab(orderIdentifier) is Order orderPrefab &&
shortcutNodes.None(n => (n.UserData is Order order && order.Identifier == orderIdentifier) || shortcutNodes.None(n => (n.UserData is Order order && order.Identifier == orderIdentifier) ||
(n.UserData is Tuple<Order, string> orderWithOption && orderWithOption.Item1.Identifier == orderIdentifier)) && (n.UserData is Tuple<Order, string> orderWithOption && orderWithOption.Item1.Identifier == orderIdentifier)) &&
!orderPrefab.TargetAllCharacters && orderPrefab.Category != null) !orderPrefab.IsReport && orderPrefab.Category != null)
{ {
if (!orderPrefab.MustSetTarget || orderPrefab.GetMatchingItems(sub, true).Any()) if (!orderPrefab.MustSetTarget || orderPrefab.GetMatchingItems(sub, true).Any())
{ {
@@ -2172,7 +2236,7 @@ namespace Barotrauma
private void CreateOrderNodes(OrderCategory orderCategory) private void CreateOrderNodes(OrderCategory orderCategory)
{ {
var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.TargetAllCharacters); var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.IsReport);
Order order; Order order;
bool disableNode; bool disableNode;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
@@ -2255,29 +2319,33 @@ namespace Barotrauma
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled)); contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled));
} }
} }
AddIgnoreOrder(itemContext);
} }
else if (hullContext != null) else if (hullContext != null)
{ {
contextualOrders.Add(new Order(Order.GetPrefab("fixleaks"), hullContext, targetItem: null, Character.Controlled)); contextualOrders.Add(new Order(Order.GetPrefab("fixleaks"), hullContext, targetItem: null, Character.Controlled));
if (wallContext != null)
{
AddIgnoreOrder(wallContext);
}
} }
if (contextualOrders.None(o => o.Category != OrderCategory.Movement)) orderIdentifier = "wait";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{ {
orderIdentifier = "wait"; Vector2 position = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
Hull hull = Hull.FindHull(position, guess: Character.Controlled?.CurrentHull);
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), new OrderTarget(position, hull), Character.Controlled));
}
if (contextualOrders.None(o => o.Category != OrderCategory.Movement) && characters.Any(c => c != Character.Controlled))
{
orderIdentifier = "follow";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier))) if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{ {
Vector2 position = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); contextualOrders.Add(Order.GetPrefab(orderIdentifier));
Hull hull = Hull.FindHull(position, guess: Character.Controlled?.CurrentHull);
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), new OrderTarget(position, hull), Character.Controlled));
}
if (characters.Any(c => c != Character.Controlled))
{
orderIdentifier = "follow";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
contextualOrders.Add(Order.GetPrefab(orderIdentifier));
}
} }
} }
@@ -2298,6 +2366,35 @@ namespace Barotrauma
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false), CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
!disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None)); !disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None));
} }
void AddIgnoreOrder(ISpatialEntity target)
{
var orderIdentifier = "ignorethis";
if (!target.IgnoreByAI && contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
AddOrder(orderIdentifier, target);
}
else
{
orderIdentifier = "unignorethis";
if (target.IgnoreByAI && contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
AddOrder(orderIdentifier, target);
}
}
void AddOrder(string id, ISpatialEntity target)
{
if (target is WallSection ws)
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), ws.Wall, ws.Wall.Sections.IndexOf(ws), orderGiver: Character.Controlled));
}
else
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), target as Entity, null, Character.Controlled));
}
}
}
} }
// TODO: there's duplicate logic here and above -> would be better to refactor so that the conditions are only defined in one place // TODO: there's duplicate logic here and above -> would be better to refactor so that the conditions are only defined in one place
@@ -2367,11 +2464,13 @@ namespace Barotrauma
{ {
o = new Order(o.Prefab, orderTargetEntity, orderTargetEntity.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: order.OrderGiver); o = new Order(o.Prefab, orderTargetEntity, orderTargetEntity.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: order.OrderGiver);
} }
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o), o, null, Character.Controlled); var character = !o.TargetAllCharacters ? characterContext ?? GetCharacterForQuickAssignment(o) : null;
SetCharacterOrder(character, o, null, Character.Controlled);
DisableCommandUI(); DisableCommandUI();
} }
return true; return true;
}; };
// TODO: Might need to edit the tooltip
var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color, var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color,
tooltip: mustSetOptionOrTarget || characterContext != null ? order.Name : order.Name + tooltip: mustSetOptionOrTarget || characterContext != null ? order.Name : order.Name +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") + "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") +
@@ -2948,9 +3047,10 @@ namespace Barotrauma
return (degrees < 0) ? (degrees + 360) : degrees; return (degrees < 0) ? (degrees + 360) : degrees;
} }
private bool TryGetBreachedHullAtHoveredWall(out Hull breachedHull) private bool TryGetBreachedHullAtHoveredWall(out Hull breachedHull, out WallSection hoveredWall)
{ {
breachedHull = null; breachedHull = null;
hoveredWall = null;
// Based on the IsValidTarget() method of AIObjectiveFixLeaks class // Based on the IsValidTarget() method of AIObjectiveFixLeaks class
List<Gap> leaks = Gap.GapList.FindAll(g => List<Gap> leaks = Gap.GapList.FindAll(g =>
g != null && g.ConnectedWall != null && g.ConnectedDoor == null && g.Open > 0 && g.linkedTo.Any(l => l != null) && g != null && g.ConnectedWall != null && g.ConnectedDoor == null && g.Open > 0 && g.linkedTo.Any(l => l != null) &&
@@ -2962,6 +3062,15 @@ namespace Barotrauma
if (Submarine.RectContains(leak.ConnectedWall.WorldRect, mouseWorldPosition)) if (Submarine.RectContains(leak.ConnectedWall.WorldRect, mouseWorldPosition))
{ {
breachedHull = leak.FlowTargetHull; breachedHull = leak.FlowTargetHull;
foreach (var section in leak.ConnectedWall.Sections)
{
if (Submarine.RectContains(section.WorldRect, mouseWorldPosition))
{
hoveredWall = section;
break;
}
}
return true; return true;
} }
} }
@@ -20,6 +20,7 @@ namespace Barotrauma
protected GUIButton endRoundButton; protected GUIButton endRoundButton;
public GUIButton ReadyCheckButton;
public GUIButton EndRoundButton => endRoundButton; public GUIButton EndRoundButton => endRoundButton;
protected GUIFrame campaignUIContainer; protected GUIFrame campaignUIContainer;
@@ -142,7 +143,8 @@ namespace Barotrauma
if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition")) if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"))
{ {
endRoundButton.Visible = false; endRoundButton.Visible = false;
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = false; }
return; return;
} }
if (Submarine.MainSub == null) { return; } if (Submarine.MainSub == null) { return; }
@@ -188,6 +190,8 @@ namespace Barotrauma
break; break;
} }
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = endRoundButton.Visible; }
if (endRoundButton.Visible) if (endRoundButton.Visible)
{ {
if (!AllowedToEndRound()) { buttonText = TextManager.Get("map"); } if (!AllowedToEndRound()) { buttonText = TextManager.Get("map"); }
@@ -211,6 +215,10 @@ namespace Barotrauma
} }
} }
endRoundButton.DrawManually(spriteBatch); endRoundButton.DrawManually(spriteBatch);
if (this is MultiPlayerCampaign)
{
ReadyCheckButton?.DrawManually(spriteBatch);
}
} }
public Task SelectSummaryScreen(RoundSummary roundSummary, LevelData newLevel, bool mirror, Action action) public Task SelectSummaryScreen(RoundSummary roundSummary, LevelData newLevel, bool mirror, Action action)
@@ -279,6 +287,7 @@ namespace Barotrauma
base.AddToGUIUpdateList(); base.AddToGUIUpdateList();
CrewManager.AddToGUIUpdateList(); CrewManager.AddToGUIUpdateList();
endRoundButton.AddToGUIUpdateList(); endRoundButton.AddToGUIUpdateList();
ReadyCheckButton?.AddToGUIUpdateList();
} }
public override void Update(float deltaTime) public override void Update(float deltaTime)
@@ -34,7 +34,6 @@ namespace Barotrauma
} }
} }
public static void StartCampaignSetup(IEnumerable<string> saveFiles) public static void StartCampaignSetup(IEnumerable<string> saveFiles)
{ {
var parent = GameMain.NetLobbyScreen.CampaignSetupFrame; var parent = GameMain.NetLobbyScreen.CampaignSetupFrame;
@@ -103,11 +102,13 @@ namespace Barotrauma
{ {
CanBeFocused = false CanBeFocused = false
}; };
int buttonHeight = (int)(GUI.Scale * 40);
int buttonWidth = GUI.IntScale(450);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUICanvas.Instance), int buttonHeight = (int) (GUI.Scale * 40),
buttonWidth = GUI.IntScale(450),
buttonCenter = buttonHeight / 2,
screenMiddle = GameMain.GraphicsWidth / 2;
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle - buttonWidth / 2, HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, buttonWidth, buttonHeight), GUICanvas.Instance),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton") TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
{ {
Pulse = true, Pulse = true,
@@ -140,6 +141,25 @@ namespace Barotrauma
return true; return true;
} }
}; };
int readyButtonHeight = buttonHeight;
int readyButtonWidth = (int) (GUI.Scale * 50);
ReadyCheckButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle + (buttonWidth / 2) + GUI.IntScale(16), HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, readyButtonWidth, readyButtonHeight), GUICanvas.Instance),
style: "RepairBuyButton")
{
ToolTip = TextManager.Get("ReadyCheck.Tooltip"),
OnClicked = delegate
{
if (CrewManager != null && CrewManager.ActiveReadyCheck == null)
{
ReadyCheck.CreateReadyCheck();
}
return true;
},
UserData = "ReadyCheckButton"
};
buttonContainer.Recalculate(); buttonContainer.Recalculate();
} }
@@ -378,6 +398,7 @@ namespace Barotrauma
if (!GUI.DisableHUD && !GUI.DisableUpperHUD) if (!GUI.DisableHUD && !GUI.DisableUpperHUD)
{ {
endRoundButton.UpdateManually(deltaTime); endRoundButton.UpdateManually(deltaTime);
ReadyCheckButton?.UpdateManually(deltaTime);
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || ForceMapUI) { return; } if (CoroutineManager.IsCoroutineRunning("LevelTransition") || ForceMapUI) { return; }
} }
@@ -368,6 +368,9 @@ namespace Barotrauma
SoundPlayer.OverrideMusicDuration = 18.0f; SoundPlayer.OverrideMusicDuration = 18.0f;
crewDead = false; crewDead = false;
LevelData lvlData = GameMain.GameSession.LevelData;
bool beaconActive = GameMain.GameSession.Level.CheckBeaconActive();
GameMain.GameSession.EndRound("", traitorResults, transitionType); GameMain.GameSession.EndRound("", traitorResults, transitionType);
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton; var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
RoundSummary roundSummary = null; RoundSummary roundSummary = null;
@@ -452,6 +455,8 @@ namespace Barotrauma
} }
} }
lvlData.IsBeaconActive = beaconActive;
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
} }
else else
@@ -712,7 +717,7 @@ namespace Barotrauma
} }
} }
XElement petsElement = new XElement("pets"); petsElement = new XElement("pets");
PetBehavior.SavePets(petsElement); PetBehavior.SavePets(petsElement);
modeElement.Add(petsElement); modeElement.Add(petsElement);
@@ -121,7 +121,7 @@ namespace Barotrauma.Tutorials
return; return;
} }
character = Character.Create(charInfo, wayPoint.WorldPosition, "", false, false); character = Character.Create(charInfo, wayPoint.WorldPosition, "", isRemotePlayer: false, hasAi: false);
character.TeamID = Character.TeamType.Team1; character.TeamID = Character.TeamType.Team1;
Character.Controlled = character; Character.Controlled = character;
character.GiveJobItems(null); character.GiveJobItems(null);
@@ -0,0 +1,266 @@
#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, status) 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) { return; }
switch (state)
{
case ReadyCheckState.Start:
bool isOwn = false;
float duration = inc.ReadSingle();
string author = inc.ReadString();
bool hasAuthor = inc.ReadBoolean();
if (hasAuthor)
{
isOwn = inc.ReadByte() == 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);
}
break;
case ReadyCheckState.Update:
crewManager.ActiveReadyCheck.time = inc.ReadSingle();
ReadyStatus newState = (ReadyStatus) inc.ReadByte();
byte targetId = inc.ReadByte();
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()
{
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;
};
}
}
}
@@ -1335,7 +1335,7 @@ namespace Barotrauma
}; };
msgBox.Buttons[0].OnClicked = (yesButton, obj) => msgBox.Buttons[0].OnClicked = (yesButton, obj) =>
{ {
LoadDefaultConfig(setLanguage: false); LoadDefaultConfig(setLanguage: false, loadContentPackages: Screen.Selected != GameMain.GameScreen);
CheckBindings(true); CheckBindings(true);
RefreshItemMessages(); RefreshItemMessages();
ApplySettings(); ApplySettings();
@@ -291,6 +291,7 @@ namespace Barotrauma.Items.Components
bool broken = msg.ReadBoolean(); bool broken = msg.ReadBoolean();
bool forcedOpen = msg.ReadBoolean(); bool forcedOpen = msg.ReadBoolean();
bool isStuck = msg.ReadBoolean(); bool isStuck = msg.ReadBoolean();
bool isJammed = msg.ReadBoolean();
SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen); SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen);
stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8); stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
UInt16 lastUserID = msg.ReadUInt16(); UInt16 lastUserID = msg.ReadUInt16();
@@ -301,6 +302,7 @@ namespace Barotrauma.Items.Components
toggleCooldownTimer = ToggleCoolDown; toggleCooldownTimer = ToggleCoolDown;
} }
this.isStuck = isStuck; this.isStuck = isStuck;
this.isJammed = isJammed;
if (isStuck) { OpenState = 0.0f; } if (isStuck) { OpenState = 0.0f; }
IsBroken = broken; IsBroken = broken;
PredictedState = null; PredictedState = null;
@@ -8,46 +8,6 @@ using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
internal partial class VineTile
{
public void Draw(SpriteBatch spriteBatch, Vector2 position, float depth, float leafDepth)
{
Vector2 pos = position + Position;
pos.Y = -pos.Y;
VineSprite vineSprite = Parent.VineSprites[Type];
Color color = Parent.Decayed ? Parent.DeadTint : Parent.VineTint;
float layer1 = depth + 0.01f, // flowers
layer2 = depth + 0.02f, // decay atlas
layer3 = depth + 0.03f; // branches and leaves
float scale = Parent.VineScale * VineStep;
if (Parent.VineAtlas != null)
{
spriteBatch.Draw(Parent.VineAtlas.Texture, pos + offset, vineSprite.SourceRect, color, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer3);
}
if (Parent.DecayAtlas != null)
{
spriteBatch.Draw(Parent.DecayAtlas.Texture, pos, vineSprite.SourceRect, HealthColor, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer2);
}
if (FlowerConfig.Variant >= 0 && !Parent.Decayed)
{
Sprite flowerSprite = Parent.FlowerSprites[FlowerConfig.Variant];
flowerSprite.Draw(spriteBatch, pos, Parent.FlowerTint, flowerSprite.Origin, scale: Parent.BaseFlowerScale * FlowerConfig.Scale * FlowerStep, rotate: FlowerConfig.Rotation, depth: layer1);
}
if (LeafConfig.Variant >= 0)
{
Sprite leafSprite = Parent.LeafSprites[LeafConfig.Variant];
leafSprite.Draw(spriteBatch, pos, Parent.Decayed ? Parent.DeadTint : Parent.LeafTint, leafSprite.Origin, scale: Parent.BaseLeafScale * LeafConfig.Scale * FlowerStep, rotate: LeafConfig.Rotation, depth: layer3 + leafDepth);
}
}
}
internal class VineSprite internal class VineSprite
{ {
[Serialize("0,0,0,0", false)] [Serialize("0,0,0,0", false)]
@@ -97,7 +57,7 @@ namespace Barotrauma.Items.Components
foreach (VineTile vine in Vines) foreach (VineTile vine in Vines)
{ {
leafDepth += zStep; leafDepth += zStep;
vine.Draw(spriteBatch, planter.Item.DrawPosition + offset, depth, leafDepth); DrawBranch(vine, spriteBatch, planter.Item.DrawPosition + offset, depth, leafDepth);
} }
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
@@ -111,6 +71,43 @@ namespace Barotrauma.Items.Components
} }
} }
} }
private void DrawBranch(VineTile vine, SpriteBatch spriteBatch, Vector2 position, float depth, float leafDepth)
{
Vector2 pos = position + vine.Position;
pos.Y = -pos.Y;
VineSprite vineSprite = VineSprites[vine.Type];
Color color = Decayed ? DeadTint : VineTint;
float layer1 = depth + 0.01f, // flowers
layer2 = depth + 0.02f, // decay atlas
layer3 = depth + 0.03f; // branches and leaves
float scale = VineScale * vine.VineStep;
if (VineAtlas != null)
{
spriteBatch.Draw(VineAtlas.Texture, pos + vine.offset, vineSprite.SourceRect, color, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer3);
}
if (DecayAtlas != null)
{
spriteBatch.Draw(DecayAtlas.Texture, pos, vineSprite.SourceRect, vine.HealthColor, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer2);
}
if (vine.FlowerConfig.Variant >= 0 && !Decayed)
{
Sprite flowerSprite = FlowerSprites[vine.FlowerConfig.Variant];
flowerSprite.Draw(spriteBatch, pos, FlowerTint, flowerSprite.Origin, scale: BaseFlowerScale * vine.FlowerConfig.Scale * vine.FlowerStep, rotate: vine.FlowerConfig.Rotation, depth: layer1);
}
if (vine.LeafConfig.Variant >= 0)
{
Sprite leafSprite = LeafSprites[vine.LeafConfig.Variant];
leafSprite.Draw(spriteBatch, pos, Decayed ? DeadTint : LeafTint, leafSprite.Origin, scale: BaseLeafScale * vine.LeafConfig.Scale * vine.FlowerStep, rotate: vine.LeafConfig.Rotation, depth: layer3 + leafDepth);
}
}
partial void LoadVines(XElement element) partial void LoadVines(XElement element)
{ {
@@ -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;
}
}
@@ -257,6 +257,8 @@ namespace Barotrauma.Items.Components
spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically; spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
} }
bool isWiringMode = SubEditorScreen.IsWiringMode();
int i = 0; int i = 0;
foreach (Item containedItem in Inventory.Items) foreach (Item containedItem in Inventory.Items)
{ {
@@ -278,7 +280,7 @@ namespace Barotrauma.Items.Components
containedItem.Sprite.Draw( containedItem.Sprite.Draw(
spriteBatch, spriteBatch,
new Vector2(currentItemPos.X, -currentItemPos.Y), new Vector2(currentItemPos.X, -currentItemPos.Y),
containedItem.GetSpriteColor(), isWiringMode ? containedItem.GetSpriteColor() * 0.15f : containedItem.GetSpriteColor(),
origin, origin,
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation + MathHelper.ToRadians(-item.Rotation)), -(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation + MathHelper.ToRadians(-item.Rotation)),
containedItem.Scale, containedItem.Scale,
@@ -95,23 +95,21 @@ namespace Barotrauma.Items.Components
// TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental. // TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental.
// Only do this if there's only one linked component. If you link more containers then may // Only do this if there's only one linked component. If you link more containers then may
// GUI.PreventElementOverlap have mercy on your HUD layout // GUI.PreventElementOverlap have mercy on your HUD layout
if (item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1) if (GuiFrame != null && item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1)
{ {
foreach (MapEntity linkedTo in item.linkedTo) foreach (MapEntity linkedTo in item.linkedTo)
{ {
if (!(linkedTo is Item linkedItem)) continue; if (!(linkedTo is Item linkedItem) || !linkedItem.DisplaySideBySideWhenLinked) { continue; }
if (!linkedItem.Components.Any()) continue; if (!linkedItem.Components.Any()) { continue; }
var itemContainer = linkedItem.Components.First(); var itemContainer = linkedItem.GetComponent<ItemContainer>();
if (itemContainer == null) { continue; } if (itemContainer?.GuiFrame == null || itemContainer.AllowUIOverlap) { continue; }
if (!itemContainer.Item.DisplaySideBySideWhenLinked) continue;
// how much spacing do we want between the components // how much spacing do we want between the components
var padding = (int) (8 * GUI.Scale); var padding = (int) (8 * GUI.Scale);
// Move the linked container to the right and move the fabricator to the left // Move the linked container to the right and move the deconstructor to the left
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(GuiFrame.Rect.Width / -2 - padding, 0); itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(100, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0); GuiFrame.RectTransform.AbsoluteOffset = new Point(-100, 0);
} }
} }
return base.Select(character); return base.Select(character);
@@ -212,23 +212,21 @@ namespace Barotrauma.Items.Components
// TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental. // TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental.
// Only do this if there's only one linked component. If you link more containers then may // Only do this if there's only one linked component. If you link more containers then may
// GUI.PreventElementOverlap have mercy on your HUD layout // GUI.PreventElementOverlap have mercy on your HUD layout
if (item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1) if (GuiFrame != null && item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1)
{ {
foreach (MapEntity linkedTo in item.linkedTo) foreach (MapEntity linkedTo in item.linkedTo)
{ {
if (!(linkedTo is Item linkedItem)) continue; if (!(linkedTo is Item linkedItem) || !linkedItem.DisplaySideBySideWhenLinked) { continue; }
if (!linkedItem.Components.Any()) continue; if (!linkedItem.Components.Any()) { continue; }
var itemContainer = linkedItem.Components.First();
if (itemContainer == null) { continue; }
if (!itemContainer.Item.DisplaySideBySideWhenLinked) continue; var itemContainer = linkedItem.GetComponent<ItemContainer>();
if (itemContainer?.GuiFrame == null || itemContainer.AllowUIOverlap) { continue; }
// how much spacing do we want between the components // how much spacing do we want between the components
var padding = (int) (8 * GUI.Scale); var padding = (int) (8 * GUI.Scale);
// Move the linked container to the right and move the fabricator to the left // Move the linked container to the right and move the fabricator to the left
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(GuiFrame.Rect.Width / -2 - padding, 0); itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(-100, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0); GuiFrame.RectTransform.AbsoluteOffset = new Point(100, 0);
} }
} }
@@ -73,7 +73,7 @@ namespace Barotrauma.Items.Components
{ {
OnClicked = (button, data) => OnClicked = (button, data) =>
{ {
targetLevel = null; TargetLevel = null;
IsActive = !IsActive; IsActive = !IsActive;
if (GameMain.Client != null) if (GameMain.Client != null)
{ {
@@ -112,7 +112,7 @@ namespace Barotrauma.Items.Components
{ {
if (pumpSpeedLockTimer <= 0.0f) if (pumpSpeedLockTimer <= 0.0f)
{ {
targetLevel = null; TargetLevel = null;
} }
float newValue = barScroll * 200.0f - 100.0f; float newValue = barScroll * 200.0f - 100.0f;
if (Math.Abs(newValue - FlowPercentage) < 0.1f) { return false; } if (Math.Abs(newValue - FlowPercentage) < 0.1f) { return false; }
@@ -223,6 +223,7 @@ namespace Barotrauma.Items.Components
FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f; FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
IsActive = msg.ReadBoolean(); IsActive = msg.ReadBoolean();
Hijacked = msg.ReadBoolean();
} }
} }
} }
@@ -1,4 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Extensions;
using Barotrauma.Networking;
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -28,11 +29,18 @@ namespace Barotrauma.Items.Components
private GUITickBox activeTickBox, passiveTickBox; private GUITickBox activeTickBox, passiveTickBox;
private GUITextBlock signalWarningText; private GUITextBlock signalWarningText;
private GUIFrame lowerAreaFrame;
private GUIScrollBar zoomSlider; private GUIScrollBar zoomSlider;
private GUIButton directionalModeSwitch; private GUIButton directionalModeSwitch;
private Vector2? pingDragDirection = null; private Vector2? pingDragDirection = null;
/// <summary>
/// Can be null if the property HasMineralScanner is false
/// </summary>
private GUIButton mineralScannerSwitch;
private GUIFrame controlContainer; private GUIFrame controlContainer;
private GUICustomComponent sonarView; private GUICustomComponent sonarView;
@@ -60,8 +68,6 @@ namespace Barotrauma.Items.Components
private const float DisruptionUpdateInterval = 0.2f; private const float DisruptionUpdateInterval = 0.2f;
private float disruptionUpdateTimer; private float disruptionUpdateTimer;
private float zoomSqrt;
private float showDirectionalIndicatorTimer; private float showDirectionalIndicatorTimer;
//Vector2 = vector from the ping source to the position of the disruption //Vector2 = vector from the ping source to the position of the disruption
@@ -114,6 +120,10 @@ namespace Barotrauma.Items.Components
public static Vector2 GUISizeCalculation => Vector2.One * Math.Min(GUI.RelativeHorizontalAspectRatio, 1f) * sonarAreaSize; public static Vector2 GUISizeCalculation => Vector2.One * Math.Min(GUI.RelativeHorizontalAspectRatio, 1f) * sonarAreaSize;
private List<Tuple<Vector2, List<Item>>> MineralClusters { get; set; }
private readonly List<GUITextBlock> textBlocksToScaleAndNormalize = new List<GUITextBlock>();
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
{ {
System.Diagnostics.Debug.Assert(Enum.GetValues(typeof(BlipType)).Cast<BlipType>().All(t => blipColorGradient.ContainsKey(t))); System.Diagnostics.Debug.Assert(Enum.GetValues(typeof(BlipType)).Cast<BlipType>().All(t => blipColorGradient.ContainsKey(t)));
@@ -214,11 +224,14 @@ namespace Barotrauma.Items.Components
}; };
passiveTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor); passiveTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor);
activeTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor); activeTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor);
textBlocksToScaleAndNormalize.Add(passiveTickBox.TextBlock);
textBlocksToScaleAndNormalize.Add(activeTickBox.TextBlock);
var lowerArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.BottomCenter), style: null); 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), lowerArea.RectTransform, Anchor.TopCenter), style: null); var zoomContainer = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.TopCenter), style: null);
var zoomText = new GUITextBlock(new RectTransform(new Vector2(0.3f, 0.6f), zoomContainer.RectTransform, Anchor.CenterLeft), var zoomText = new GUITextBlock(new RectTransform(new Vector2(0.3f, 0.6f), zoomContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get("SonarZoom"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight); TextManager.Get("SonarZoom"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight);
textBlocksToScaleAndNormalize.Add(zoomText);
zoomSlider = new GUIScrollBar(new RectTransform(new Vector2(0.5f, 0.8f), zoomContainer.RectTransform, Anchor.CenterLeft) zoomSlider = new GUIScrollBar(new RectTransform(new Vector2(0.5f, 0.8f), zoomContainer.RectTransform, Anchor.CenterLeft)
{ {
RelativeOffset = new Vector2(0.35f, 0) RelativeOffset = new Vector2(0.35f, 0)
@@ -238,7 +251,7 @@ 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");
var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerArea.RectTransform, Anchor.BottomCenter), style: null); var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null);
directionalModeSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), directionalModeFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal") directionalModeSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), directionalModeFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
{ {
OnClicked = (button, data) => OnClicked = (button, data) =>
@@ -255,11 +268,11 @@ namespace Barotrauma.Items.Components
}; };
var directionalModeSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), directionalModeFrame.RectTransform, Anchor.CenterRight), var directionalModeSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), directionalModeFrame.RectTransform, Anchor.CenterRight),
TextManager.Get("SonarDirectionalPing"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft); TextManager.Get("SonarDirectionalPing"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft);
textBlocksToScaleAndNormalize.Add(directionalModeSwitchText);
GuiFrame.CanBeFocused = false; GuiFrame.CanBeFocused = false;
GUITextBlock.AutoScaleAndNormalize(passiveTickBox.TextBlock, activeTickBox.TextBlock, zoomText, directionalModeSwitchText); GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
sonarView = new GUICustomComponent(new RectTransform(Vector2.One * 0.7f, GuiFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight), sonarView = new GUICustomComponent(new RectTransform(Vector2.One * 0.7f, GuiFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight),
(spriteBatch, guiCustomComponent) => { DrawSonar(spriteBatch, guiCustomComponent.Rect); }, null); (spriteBatch, guiCustomComponent) => { DrawSonar(spriteBatch, guiCustomComponent.Rect); }, null);
@@ -275,7 +288,7 @@ namespace Barotrauma.Items.Components
sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest; sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest;
sonarView.RectTransform.SetPosition(Anchor.CenterRight); sonarView.RectTransform.SetPosition(Anchor.CenterRight);
sonarView.RectTransform.Resize(GUISizeCalculation); sonarView.RectTransform.Resize(GUISizeCalculation);
GUITextBlock.AutoScaleAndNormalize(passiveTickBox.TextBlock, activeTickBox.TextBlock, zoomText, directionalModeSwitchText); GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
} }
} }
@@ -293,10 +306,40 @@ namespace Barotrauma.Items.Components
{ {
base.OnItemLoaded(); base.OnItemLoaded();
zoomSlider.BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom); zoomSlider.BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom);
if (HasMineralScanner) { AddMineralScannerSwitchToGUI(); }
//make the sonarView customcomponent render the steering view so it gets drawn in front of the sonar //make the sonarView customcomponent render the steering view so it gets drawn in front of the sonar
item.GetComponent<Steering>()?.AttachToSonarHUD(sonarView); item.GetComponent<Steering>()?.AttachToSonarHUD(sonarView);
} }
private void AddMineralScannerSwitchToGUI()
{
// First adjust other elements of the lower area
zoomSlider.Parent.RectTransform.RelativeSize = new Vector2(1.0f, 0.3f);
directionalModeSwitch.Parent.RectTransform.RelativeSize = new Vector2(1.0f, 0.3f);
directionalModeSwitch.Parent.RectTransform.SetPosition(Anchor.Center);
// Then add the scanner switch
var mineralScannerFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.3f), 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);
GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{ {
showDirectionalIndicatorTimer -= deltaTime; showDirectionalIndicatorTimer -= deltaTime;
@@ -339,6 +382,32 @@ namespace Barotrauma.Items.Components
Vector2.DistanceSquared(sonarView.Rect.Center.ToVector2(), PlayerInput.MousePosition) < Vector2.DistanceSquared(sonarView.Rect.Center.ToVector2(), PlayerInput.MousePosition) <
(sonarView.Rect.Width / 2 * sonarView.Rect.Width / 2); (sonarView.Rect.Width / 2 * sonarView.Rect.Width / 2);
if (HasMineralScanner && Level.Loaded != null && !Level.Loaded.Generating)
{
if (MineralClusters == null)
{
MineralClusters = new List<Tuple<Vector2, List<Item>>>();
foreach (var p in Level.Loaded.PathPoints)
{
foreach (var c in p.ClusterLocations)
{
if (c.Resources.None(i => i != null && !i.Removed && i.Tags.Contains("ore"))) { continue; }
var pos = Vector2.Zero;
foreach (var r in c.Resources)
{
pos += r.WorldPosition;
}
pos /= c.Resources.Count;
MineralClusters.Add(new Tuple<Vector2, List<Item>>(pos, c.Resources));
}
}
}
else
{
MineralClusters.RemoveAll(t => t.Item2 == null || t.Item2.None() || t.Item2.All(i => i == null || i.Removed));
}
}
if (UseTransducers && connectedTransducers.Count == 0) if (UseTransducers && connectedTransducers.Count == 0)
{ {
return; return;
@@ -348,12 +417,16 @@ namespace Barotrauma.Items.Components
if (Level.Loaded != null) if (Level.Loaded != null)
{ {
List<LevelTrigger> ballastFloraSpores = new List<LevelTrigger>();
Dictionary<LevelTrigger, Vector2> levelTriggerFlows = new Dictionary<LevelTrigger, Vector2>(); Dictionary<LevelTrigger, Vector2> levelTriggerFlows = new Dictionary<LevelTrigger, Vector2>();
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex) for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{ {
var activePing = activePings[pingIndex]; var activePing = activePings[pingIndex];
foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(transducerCenter, range * activePing.State / zoom)) LevelObjectManager objManager = Level.Loaded.LevelObjectManager;
float pingRange = range * activePing.State / zoom;
foreach (LevelObject levelObject in objManager.GetAllObjects(transducerCenter, pingRange))
{ {
if (levelObject.Triggers == null) { continue; }
//gather all nearby triggers that are causing the water to flow into the dictionary //gather all nearby triggers that are causing the water to flow into the dictionary
foreach (LevelTrigger trigger in levelObject.Triggers) foreach (LevelTrigger trigger in levelObject.Triggers)
{ {
@@ -363,6 +436,10 @@ namespace Barotrauma.Items.Components
{ {
levelTriggerFlows.Add(trigger, flow); levelTriggerFlows.Add(trigger, flow);
} }
if (!string.IsNullOrWhiteSpace(trigger.InfectIdentifier) && Vector2.DistanceSquared(transducerCenter, trigger.WorldPosition) < pingRange / 2 * pingRange / 2)
{
ballastFloraSpores.Add(trigger);
}
} }
} }
} }
@@ -400,6 +477,19 @@ namespace Barotrauma.Items.Components
} }
} }
foreach (LevelTrigger spore in ballastFloraSpores)
{
Vector2 blipPos = spore.WorldPosition + Rand.Vector(spore.ColliderRadius * Rand.Range(0.0f, 1.0f));
SonarBlip sporeBlip = new SonarBlip(blipPos, Rand.Range(0.1f, 0.5f), 0.5f)
{
Rotation = Rand.Range(-MathHelper.TwoPi, MathHelper.TwoPi),
BlipType = BlipType.Default,
Velocity = Rand.Vector(100f, Rand.RandSync.Unsynced)
};
sonarBlips.Add(sporeBlip);
}
float outsideLevelFlow = 0.0f; float outsideLevelFlow = 0.0f;
if (transducerCenter.X < 0.0f) if (transducerCenter.X < 0.0f)
{ {
@@ -721,6 +811,24 @@ namespace Barotrauma.Items.Components
} }
} }
if (HasMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null)
{
var maxMineralScanRangeSquared = Range * Range;
foreach (var t in MineralClusters)
{
var unobtainedMinerals = t.Item2.Where(i => i != null && i.GetRootInventoryOwner() == i);
if (unobtainedMinerals.None()) { continue; }
if (Vector2.DistanceSquared(transducerCenter, t.Item1) > maxMineralScanRangeSquared) { continue; }
var i = unobtainedMinerals.FirstOrDefault();
if (i == null) { continue; }
DrawMarker(spriteBatch,
i.Name, null, i,
t.Item1, transducerCenter,
displayScale, center, DisplayRadius * 0.95f,
onlyShowTextOnMouseOver: true);
}
}
foreach (Submarine sub in Submarine.Loaded) foreach (Submarine sub in Submarine.Loaded)
{ {
if (!sub.ShowSonarMarker) { continue; } if (!sub.ShowSonarMarker) { continue; }
@@ -1334,7 +1442,8 @@ namespace Barotrauma.Items.Components
sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale, SpriteEffects.None, 0); sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale, SpriteEffects.None, 0);
} }
private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius) private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius,
bool onlyShowTextOnMouseOver = false)
{ {
float linearDist = Vector2.Distance(worldPosition, transducerPosition); float linearDist = Vector2.Distance(worldPosition, transducerPosition);
float dist = linearDist; float dist = linearDist;
@@ -1391,16 +1500,27 @@ namespace Barotrauma.Items.Components
markerPos.Y = (int)markerPos.Y; markerPos.Y = (int)markerPos.Y;
float alpha = 1.0f; float alpha = 1.0f;
if (linearDist * scale < radius) if (!onlyShowTextOnMouseOver)
{ {
float normalizedDist = linearDist * scale / radius; if (linearDist * scale < radius)
alpha = Math.Max(normalizedDist - 0.4f, 0.0f);
float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos);
float hoverThreshold = 150.0f;
if (mouseDist < hoverThreshold)
{ {
alpha += (hoverThreshold - mouseDist) / hoverThreshold; float normalizedDist = linearDist * scale / radius;
alpha = Math.Max(normalizedDist - 0.4f, 0.0f);
float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos);
float hoverThreshold = 150.0f;
if (mouseDist < hoverThreshold)
{
alpha += (hoverThreshold - mouseDist) / hoverThreshold;
}
}
}
else
{
float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos);
if (mouseDist > 5)
{
alpha = 0.0f;
} }
} }
@@ -1446,6 +1566,8 @@ namespace Barotrauma.Items.Components
sprite.Remove(); sprite.Remove();
} }
targetIcons.Clear(); targetIcons.Clear();
MineralClusters = null;
} }
public void ClientWrite(IWriteMessage msg, object[] extraData = null) public void ClientWrite(IWriteMessage msg, object[] extraData = null)
@@ -1460,6 +1582,7 @@ namespace Barotrauma.Items.Components
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection)); float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8); msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
} }
msg.Write(useMineralScanner);
} }
} }
@@ -1471,6 +1594,7 @@ namespace Barotrauma.Items.Components
float zoomT = 1.0f; float zoomT = 1.0f;
bool directionalPing = useDirectionalPing; bool directionalPing = useDirectionalPing;
float directionT = 0.0f; float directionT = 0.0f;
bool mineralScanner = useMineralScanner;
if (isActive) if (isActive)
{ {
zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8); zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
@@ -1479,6 +1603,7 @@ namespace Barotrauma.Items.Components
{ {
directionT = msg.ReadRangedSingle(0.0f, 1.0f, 8); directionT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
} }
mineralScanner = msg.ReadBoolean();
} }
if (correctionTimer > 0.0f) if (correctionTimer > 0.0f)
@@ -1500,6 +1625,11 @@ namespace Barotrauma.Items.Components
pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle)); pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle));
} }
useDirectionalPing = directionalModeSwitch.Selected = directionalPing; useDirectionalPing = directionalModeSwitch.Selected = directionalPing;
useMineralScanner = mineralScanner;
if (mineralScannerSwitch != null)
{
mineralScannerSwitch.Selected = mineralScanner;
}
} }
} }
@@ -1510,6 +1640,10 @@ namespace Barotrauma.Items.Components
passiveTickBox.Selected = !isActive; passiveTickBox.Selected = !isActive;
activeTickBox.Selected = isActive; activeTickBox.Selected = isActive;
directionalModeSwitch.Selected = useDirectionalPing; directionalModeSwitch.Selected = useDirectionalPing;
if (mineralScannerSwitch != null)
{
mineralScannerSwitch.Selected = useMineralScanner;
}
} }
} }
@@ -319,8 +319,7 @@ namespace Barotrauma.Items.Components
centerText = $"({TextManager.Get("Meter")})"; centerText = $"({TextManager.Get("Meter")})";
rightTextGetter = () => rightTextGetter = () =>
{ {
Vector2 pos = controlledSub == null ? Vector2.Zero : controlledSub.Position; float realWorldDepth = controlledSub == null ? -1000.0f : controlledSub.RealWorldDepth;
float realWorldDepth = Level.Loaded == null ? 0.0f : Math.Abs(pos.Y - Level.Loaded.Size.Y) * Physics.DisplayToRealWorldRatio;
return ((int)realWorldDepth).ToString(); return ((int)realWorldDepth).ToString();
}; };
break; break;
@@ -1,8 +1,7 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Xml.Linq; using System.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -10,6 +9,23 @@ namespace Barotrauma.Items.Components
{ {
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
bool launch = msg.ReadBoolean();
if (launch)
{
ushort userId = msg.ReadUInt16();
User = Entity.FindEntityByID(userId) as Character;
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
float rotation = msg.ReadSingle();
if (User != null)
{
Shoot(User, simPosition, simPosition, rotation, ignoredBodies: User.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList(), createNetworkEvent: false);
}
else
{
Launch(User, simPosition, rotation);
}
}
bool isStuck = msg.ReadBoolean(); bool isStuck = msg.ReadBoolean();
if (isStuck) if (isStuck)
{ {
@@ -56,7 +72,15 @@ namespace Barotrauma.Items.Components
else if (entity is Item item) else if (entity is Item item)
{ {
if (item.Removed) { return; } if (item.Removed) { return; }
StickToTarget(item.body.FarseerBody, axis); var door = item.GetComponent<Door>();
if (door != null)
{
StickToTarget(door.Body.FarseerBody, axis);
}
else if (item.body != null)
{
StickToTarget(item.body.FarseerBody, axis);
}
} }
else if (entity is Submarine sub) else if (entity is Submarine sub)
{ {
@@ -1,6 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -70,7 +69,13 @@ namespace Barotrauma.Items.Components
partial void ShowOnDisplay(string input) partial void ShowOnDisplay(string input)
{ {
while (historyBox.Content.CountChildren > 60) messageHistory.Add(input);
while (messageHistory.Count > MaxMessages)
{
messageHistory.RemoveAt(0);
}
while (historyBox.Content.CountChildren > MaxMessages)
{ {
historyBox.RemoveChild(historyBox.Content.Children.First()); historyBox.RemoveChild(historyBox.Content.Children.First());
} }
@@ -114,11 +119,6 @@ namespace Barotrauma.Items.Components
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
base.AddToGUIUpdateList(); base.AddToGUIUpdateList();
if (!string.IsNullOrEmpty(DisplayedWelcomeMessage))
{
ShowOnDisplay(DisplayedWelcomeMessage);
DisplayedWelcomeMessage = "";
}
if (shouldSelectInputBox) if (shouldSelectInputBox)
{ {
inputBox.Select(); inputBox.Select();
@@ -156,7 +156,7 @@ namespace Barotrauma.Items.Components
drawOffset = sub.DrawPosition + sub.HiddenSubPosition; drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
} }
float depth = item.IsSelected ? 0.0f : Screen.Selected is SubEditorScreen editor && editor.WiringMode ? 0.00002f : wireSprite.Depth + ((item.ID % 100) * 0.00001f); float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : wireSprite.Depth + ((item.ID % 100) * 0.00001f);
if (item.IsHighlighted) if (item.IsHighlighted)
{ {
@@ -261,7 +261,7 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-3, -3), new Vector2(6, 6), item.Color, true, 0.0f); GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-3, -3), new Vector2(6, 6), item.Color, true, 0.015f);
} }
} }
} }
@@ -288,72 +288,77 @@ namespace Barotrauma.Items.Components
if (!editing || GUI.DisableHUD || !item.IsSelected) { return; } if (!editing || GUI.DisableHUD || !item.IsSelected) { return; }
float widgetRadius = 60.0f; const float widgetRadius = 60.0f;
GUI.DrawLine(spriteBatch,
drawPos,
drawPos + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * widgetRadius,
GUI.Style.Green);
GUI.DrawLine(spriteBatch,
drawPos,
drawPos + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * widgetRadius,
GUI.Style.Green);
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
drawPos, drawPos,
drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * widgetRadius, drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * widgetRadius,
Color.LightGreen); Color.LightGreen);
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);
}
Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: 10, initMethod: (widget) => Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: 10, initMethod: (widget) =>
{ {
widget.Selected += () => widget.Selected += () =>
{ {
oldRotation = RotationLimits; oldRotation = RotationLimits;
}; };
widget.MouseDown += () => widget.MouseDown += () =>
{ {
widget.color = GUI.Style.Green; widget.color = GUI.Style.Green;
prevAngle = minRotation; prevAngle = minRotation;
}; };
widget.Deselected += () => widget.Deselected += () =>
{ {
widget.color = Color.Yellow; widget.color = Color.Yellow;
item.CreateEditingHUD(); item.CreateEditingHUD();
if (SubEditorScreen.IsSubEditor()) if (SubEditorScreen.IsSubEditor())
{ {
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation)); SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
} }
}; };
widget.MouseHeld += (deltaTime) => widget.MouseHeld += (deltaTime) =>
{ {
minRotation = GetRotationAngle(GetDrawPos()); minRotation = GetRotationAngle(GetDrawPos());
if (minRotation > maxRotation) if (minRotation > maxRotation)
{ {
float temp = minRotation; float temp = minRotation;
minRotation = maxRotation; minRotation = maxRotation;
maxRotation = temp; maxRotation = temp;
} }
MapEntity.DisableSelect = true; RotationLimits = RotationLimits;
}; MapEntity.DisableSelect = true;
widget.PreUpdate += (deltaTime) => };
{ widget.PreUpdate += (deltaTime) =>
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y); {
widget.DrawPos = Screen.Selected.Cam.WorldToScreen(widget.DrawPos); widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
}; widget.DrawPos = Screen.Selected.Cam.WorldToScreen(widget.DrawPos);
widget.PostUpdate += (deltaTime) => };
{ widget.PostUpdate += (deltaTime) =>
widget.DrawPos = Screen.Selected.Cam.ScreenToWorld(widget.DrawPos); {
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y); widget.DrawPos = Screen.Selected.Cam.ScreenToWorld(widget.DrawPos);
}; widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
widget.PreDraw += (sprtBtch, deltaTime) => };
{ 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.tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
widget.Update(deltaTime); 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: 10, initMethod: (widget) => Widget maxRotationWidget = GetWidget("maxrotation", spriteBatch, size: 10, initMethod: (widget) =>
{ {
widget.Selected += () => widget.Selected += () =>
@@ -383,6 +388,7 @@ namespace Barotrauma.Items.Components
minRotation = maxRotation; minRotation = maxRotation;
maxRotation = temp; maxRotation = temp;
} }
RotationLimits = RotationLimits;
MapEntity.DisableSelect = true; MapEntity.DisableSelect = true;
}; };
widget.PreUpdate += (deltaTime) => widget.PreUpdate += (deltaTime) =>
@@ -398,7 +404,7 @@ namespace Barotrauma.Items.Components
widget.PreDraw += (sprtBtch, deltaTime) => widget.PreDraw += (sprtBtch, deltaTime) =>
{ {
widget.tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation); widget.tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation);
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * widgetRadius; widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
widget.Update(deltaTime); widget.Update(deltaTime);
}; };
}); });
@@ -580,7 +586,6 @@ namespace Barotrauma.Items.Components
} }
Launch(projectile, launchRotation: newTargetRotation); Launch(projectile, launchRotation: newTargetRotation);
} }
} }
} }
} }
@@ -439,7 +439,7 @@ namespace Barotrauma
protected virtual void ControlInput(Camera cam) protected virtual void ControlInput(Camera cam)
{ {
// Note that these targets are static. Therefore the outcome is the same if this method is called multiple times or only once. // Note that these targets are static. Therefore the outcome is the same if this method is called multiple times or only once.
if (selectedSlot != null && !DraggingItemToWorld) if (selectedSlot != null && !DraggingItemToWorld && cam.GetZoomAmountFromPrevious() <= 0.25f)
{ {
cam.Freeze = true; cam.Freeze = true;
} }
@@ -461,7 +461,7 @@ namespace Barotrauma
}*/ }*/
bool mouseOn = interactRect.Contains(PlayerInput.MousePosition) && !Locked && !mouseOnGUI && !slot.Disabled; bool mouseOn = interactRect.Contains(PlayerInput.MousePosition) && !Locked && !mouseOnGUI && !slot.Disabled;
// Delete item from container in sub editor // Delete item from container in sub editor
if (SubEditorScreen.IsSubEditor() && PlayerInput.IsCtrlDown()) if (SubEditorScreen.IsSubEditor() && PlayerInput.IsCtrlDown())
{ {
@@ -477,8 +477,12 @@ namespace Barotrauma
SoundPlayer.PlayUISound(GUISoundType.PickItem); SoundPlayer.PlayUISound(GUISoundType.PickItem);
} }
SubEditorScreen.BulkItemBufferInUse = true; if (!item.Removed)
SubEditorScreen.BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { item }, true)); {
SubEditorScreen.BulkItemBufferInUse = SubEditorScreen.ItemRemoveMutex;
SubEditorScreen.BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { item }, true));
}
item.OwnInventory?.DeleteAllItems(); item.OwnInventory?.DeleteAllItems();
item.Remove(); item.Remove();
} }
@@ -688,6 +692,18 @@ namespace Barotrauma
subInventory.Update(deltaTime, cam, true); subInventory.Update(deltaTime, cam, true);
} }
public void ClearSubInventories()
{
if (highlightedSubInventorySlots.Count == 0) return;
foreach (SlotReference highlightedSubInventorySlot in highlightedSubInventorySlots)
{
highlightedSubInventorySlot.Inventory.HideTimer = 0.0f;
}
highlightedSubInventorySlots.Clear();
}
public virtual void Draw(SpriteBatch spriteBatch, bool subInventory = false) public virtual void Draw(SpriteBatch spriteBatch, bool subInventory = false)
{ {
if (slots == null || isSubInventory != subInventory) return; if (slots == null || isSubInventory != subInventory) return;
@@ -62,9 +62,9 @@ namespace Barotrauma
} }
} }
public override bool DrawBelowWater => (!(Screen.Selected is SubEditorScreen editor) || !editor.WiringMode || !isWire) && (base.DrawBelowWater || ParentInventory is CharacterInventory); public override bool DrawBelowWater => (!(Screen.Selected is SubEditorScreen editor) || !editor.WiringMode || !isWire || !isLogic) && (base.DrawBelowWater || ParentInventory is CharacterInventory);
public override bool DrawOverWater => base.DrawOverWater || (IsSelected || Screen.Selected is SubEditorScreen editor && editor.WiringMode) && isWire; public override bool DrawOverWater => base.DrawOverWater || (IsSelected || Screen.Selected is SubEditorScreen editor && editor.WiringMode) && (isWire || isLogic);
private GUITextBlock itemInUseWarning; private GUITextBlock itemInUseWarning;
private GUITextBlock ItemInUseWarning private GUITextBlock ItemInUseWarning
@@ -239,6 +239,10 @@ namespace Barotrauma
Color color = IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange : GetSpriteColor(); Color color = IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange : GetSpriteColor();
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f); //if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
bool isWiringMode = editing && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
if (renderTransparent) { color *= 0.15f; }
BrokenItemSprite fadeInBrokenSprite = null; BrokenItemSprite fadeInBrokenSprite = null;
float fadeInBrokenSpriteAlpha = 0.0f; float fadeInBrokenSpriteAlpha = 0.0f;
@@ -269,6 +273,7 @@ namespace Barotrauma
} }
float depth = GetDrawDepth(); float depth = GetDrawDepth();
if (isWiringMode && isLogic && !PlayerInput.IsShiftDown()) { depth = 0.01f; }
if (activeSprite != null) if (activeSprite != null)
{ {
SpriteEffects oldEffects = activeSprite.effects; SpriteEffects oldEffects = activeSprite.effects;
@@ -296,6 +301,8 @@ namespace Barotrauma
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.DrawTiled(spriteBatch, decorativeSprite.Sprite.DrawTiled(spriteBatch,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)), new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
size, color: color, size, color: color,
@@ -316,11 +323,18 @@ namespace Barotrauma
} }
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth); activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f); fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f);
if (Infector != null && Infector.ParentBallastFlora.HasBrokenThrough)
{
Prefab.InfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, Prefab.InfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.001f);
Prefab.DamagedInfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, Infector.HealthColor, Prefab.DamagedInfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.002f);
}
foreach (var decorativeSprite in Prefab.DecorativeSprites) foreach (var decorativeSprite in Prefab.DecorativeSprites)
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color, decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotationRad + rot, decorativeSprite.Scale * Scale, activeSprite.effects, rotationRad + rot, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f)); depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
@@ -367,7 +381,8 @@ namespace Barotrauma
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
var ca = (float)Math.Cos(-body.Rotation); var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation); var sa = (float)Math.Sin(-body.Rotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y); Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
@@ -386,9 +401,10 @@ namespace Barotrauma
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
var (xOff, yOff) = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + xOff, -(DrawPosition.Y + yOff)), color, if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotation, decorativeSprite.Scale * Scale, activeSprite.effects, rotation, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth)); depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
} }
@@ -417,6 +433,14 @@ namespace Barotrauma
} }
} }
if (editing && IsSelected && PlayerInput.KeyDown(Keys.Space))
{
if (GetComponent<ElectricalDischarger>() is { } discharger)
{
discharger.DrawElectricity(spriteBatch);
}
}
if (!editing || (body != null && !body.Enabled)) if (!editing || (body != null && !body.Enabled))
{ {
return; return;
@@ -505,7 +529,19 @@ namespace Barotrauma
} }
if (Screen.Selected != GameMain.SubEditorScreen) { return; } if (Screen.Selected != GameMain.SubEditorScreen) { return; }
if (GetComponent<ElectricalDischarger>() is { } discharger)
{
if (PlayerInput.KeyDown(Keys.Space))
{
discharger.FindNodes(WorldPosition, discharger.Range);
}
else
{
discharger.IsActive = false;
}
}
if (Character.Controlled == null) { activeHUDs.Clear(); } if (Character.Controlled == null) { activeHUDs.Clear(); }
if (!Linkable) { return; } if (!Linkable) { return; }
@@ -1341,6 +1377,10 @@ namespace Barotrauma
DebugConsole.Log($"Received entity spawn message for item \"{itemName}\" (identifier: {itemIdentifier}, id: {itemId})"); DebugConsole.Log($"Received entity spawn message for item \"{itemName}\" (identifier: {itemIdentifier}, id: {itemId})");
var itemPrefab = string.IsNullOrEmpty(itemIdentifier) ?
MapEntityPrefab.Find(itemName, null, showErrorMessages: false) as ItemPrefab :
MapEntityPrefab.Find(itemName, itemIdentifier, showErrorMessages: false) as ItemPrefab;
Vector2 pos = Vector2.Zero; Vector2 pos = Vector2.Zero;
Submarine sub = null; Submarine sub = null;
int itemContainerIndex = -1; int itemContainerIndex = -1;
@@ -1369,16 +1409,24 @@ namespace Barotrauma
string tags = ""; string tags = "";
if (tagsChanged) if (tagsChanged)
{ {
tags = msg.ReadString(); string[] addedTags = msg.ReadString().Split(',');
string[] removedTags = msg.ReadString().Split(',');
if (itemPrefab != null)
{
tags = string.Join(',',itemPrefab.Tags.Where(t => !removedTags.Contains(t)).Concat(addedTags));
}
}
bool isNameTag = msg.ReadBoolean();
string writtenName = "";
if (isNameTag)
{
writtenName = msg.ReadString();
} }
if (!spawn) return null; if (!spawn) { return null; }
//---------------------------------------- //----------------------------------------
var itemPrefab = string.IsNullOrEmpty(itemIdentifier) ?
MapEntityPrefab.Find(itemName, null, showErrorMessages: false) as ItemPrefab :
MapEntityPrefab.Find(itemName, itemIdentifier, showErrorMessages: false) as ItemPrefab;
if (itemPrefab == null) if (itemPrefab == null)
{ {
string errorMsg = "Failed to spawn item, prefab not found (name: " + (itemName ?? "null") + ", identifier: " + (itemIdentifier ?? "null") + ")"; string errorMsg = "Failed to spawn item, prefab not found (name: " + (itemName ?? "null") + ", identifier: " + (itemIdentifier ?? "null") + ")";
@@ -1425,9 +1473,8 @@ namespace Barotrauma
} }
} }
var item = new Item(itemPrefab, pos, sub) var item = new Item(itemPrefab, pos, sub, id: itemId)
{ {
ID = itemId,
SpawnedInOutpost = spawnedInOutpost SpawnedInOutpost = spawnedInOutpost
}; };
@@ -1442,6 +1489,11 @@ namespace Barotrauma
} }
if (descriptionChanged) { item.Description = itemDesc; } if (descriptionChanged) { item.Description = itemDesc; }
if (tagsChanged) { item.Tags = tags; } if (tagsChanged) { item.Tags = tags; }
var nameTag = item.GetComponent<NameTag>();
if (nameTag != null)
{
nameTag.WrittenName = writtenName;
}
if (sub != null) if (sub != null)
{ {
@@ -56,6 +56,8 @@ namespace Barotrauma
public Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>(); public Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
public Sprite InventoryIcon; public Sprite InventoryIcon;
public Sprite MinimapIcon; public Sprite MinimapIcon;
public Sprite InfectedSprite;
public Sprite DamagedInfectedSprite;
//only used to display correct color in the sub editor, item instances have their own property that can be edited on a per-item basis //only used to display correct color in the sub editor, item instances have their own property that can be edited on a per-item basis
[Serialize("1.0,1.0,1.0,1.0", false)] [Serialize("1.0,1.0,1.0,1.0", false)]
@@ -0,0 +1,333 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
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; }
private float RandRotation() => Rand.Range(MinRotation, MaxRotation);
private float RandVelocity() => Rand.Range(MinVelocity, MaxVelocity);
public void Emit(Vector2 pos)
{
GameMain.ParticleManager.CreateParticle(Identifier, pos, RandRotation(), RandVelocity());
}
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>();
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 "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 static readonly Color DarkColor = new Color(25, 25, 25);
public void Draw(SpriteBatch spriteBatch)
{
const float zStep = 0.00001f;
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)
{
spriteBatch.Draw(branchAtlas.Texture, pos + branch.offset, branchSprite.SourceRect, branchColor, 0f, branchSprite.AbsoluteOrigin, BaseBranchScale * branch.VineStep, SpriteEffects.None, layer2);
}
if (decayAtlas != null && isDamaged)
{
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 (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;
}
}
}
public void ClientRead(IReadMessage msg, NetworkHeader header)
{
switch (header)
{
case NetworkHeader.Infect:
ushort itemId = msg.ReadUInt16();
bool infect = msg.ReadBoolean();
if (Entity.FindEntityByID(itemId) is Item item)
{
if (infect)
{
ClaimTarget(item, null);
}
else
{
RemoveClaim(itemId);
}
}
break;
case NetworkHeader.BranchCreate:
int parentId = msg.ReadInt32();
BallastFloraBranch branch = ReadBranch(msg);
UpdateConnections(branch, Branches.FirstOrDefault(b => b.ID == parentId));
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); }
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;
}
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,
Sides = (TileSide) sides
};
}
}
}
@@ -36,8 +36,8 @@ namespace Barotrauma
Rand.Range(worldPosition.Y - size.Y, worldPosition.Y + 20.0f)); Rand.Range(worldPosition.Y - size.Y, worldPosition.Y + 20.0f));
Vector2 particleVel = new Vector2( Vector2 particleVel = new Vector2(
(particlePos.X - (worldPosition.X + size.X / 2.0f)), particlePos.X - (worldPosition.X + size.X / 2.0f),
(float)Math.Sqrt(size.X) * Rand.Range(0.0f, 15.0f) * growModifier); Math.Max((float)Math.Sqrt(size.X) * Rand.Range(0.0f, 15.0f) * growModifier, 0.0f));
particleVel.X = MathHelper.Clamp(particleVel.X, -200.0f, 200.0f); particleVel.X = MathHelper.Clamp(particleVel.X, -200.0f, 200.0f);
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.MapCreatures.Behavior;
namespace Barotrauma namespace Barotrauma
{ {
@@ -48,7 +49,7 @@ namespace Barotrauma
{ {
get get
{ {
return decals.Count > 0; return decals.Count > 0 || BallastFlora != null;
} }
} }
@@ -65,6 +66,8 @@ namespace Barotrauma
public override bool IsVisible(Rectangle worldView) public override bool IsVisible(Rectangle worldView)
{ {
if (BallastFlora != null) { return true; }
if (Screen.Selected != GameMain.SubEditorScreen && !GameMain.DebugDraw) if (Screen.Selected != GameMain.SubEditorScreen && !GameMain.DebugDraw)
{ {
if (decals.Count == 0 && paintAmount < minimumPaintAmountToDraw) { return false; } if (decals.Count == 0 && paintAmount < minimumPaintAmountToDraw) { return false; }
@@ -229,6 +232,7 @@ namespace Barotrauma
{ {
if (back && Screen.Selected != GameMain.SubEditorScreen) if (back && Screen.Selected != GameMain.SubEditorScreen)
{ {
BallastFlora?.Draw(spriteBatch);
DrawDecals(spriteBatch); DrawDecals(spriteBatch);
return; return;
} }
@@ -244,7 +248,7 @@ namespace Barotrauma
alpha = Math.Min((float)(Timing.TotalTime - lastAmbientLightEditTime) / hideTimeAfterEdit - 1.0f, 1.0f); alpha = Math.Min((float)(Timing.TotalTime - lastAmbientLightEditTime) / hideTimeAfterEdit - 1.0f, 1.0f);
} }
Rectangle drawRect = Rectangle drawRect =
Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height); Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height);
if ((IsSelected || IsHighlighted) && editing) if ((IsSelected || IsHighlighted) && editing)
@@ -607,6 +611,26 @@ namespace Barotrauma
public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime)
{ {
bool isBallastFloraUpdate = message.ReadBoolean();
if (isBallastFloraUpdate)
{
BallastFloraBehavior.NetworkHeader header = (BallastFloraBehavior.NetworkHeader) message.ReadByte();
if (header == BallastFloraBehavior.NetworkHeader.Spawn)
{
string identifier = message.ReadString();
float x = message.ReadSingle();
float y = message.ReadSingle();
BallastFlora = new BallastFloraBehavior(this, BallastFloraPrefab.Find(identifier), new Vector2(x, y), firstGrowth: true)
{
PowerConsumptionTimer = message.ReadSingle()
};
}
else if (BallastFlora != null)
{
BallastFlora.ClientRead(message, header);
}
return;
}
remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
@@ -1,30 +1,38 @@
using Microsoft.Xna.Framework; using Barotrauma.SpriteDeformations;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class BackgroundCreature : ISteerable class BackgroundCreature : ISteerable
{ {
const float MaxDepth = 100.0f; const float MaxDepth = 10000.0f;
const float CheckWallsInterval = 5.0f; const float CheckWallsInterval = 5.0f;
public bool Enabled; public bool Visible;
private BackgroundCreaturePrefab prefab; public readonly BackgroundCreaturePrefab Prefab;
private readonly List<SpriteDeformation> uniqueSpriteDeformations = new List<SpriteDeformation>();
private readonly List<SpriteDeformation> spriteDeformations = new List<SpriteDeformation>();
private readonly List<SpriteDeformation> lightSpriteDeformations = new List<SpriteDeformation>();
private Vector2 position; private Vector2 position;
private Vector3 velocity; private Vector3 velocity;
private float depth; private float depth;
private SteeringManager steeringManager;
private float checkWallsTimer; private float alpha = 1.0f;
private readonly SteeringManager steeringManager;
private float checkWallsTimer, flashTimer;
private float wanderZPhase; private float wanderZPhase;
private Vector2 obstacleDiff; private Vector2 obstacleDiff;
@@ -33,9 +41,16 @@ namespace Barotrauma
public Swarm Swarm; public Swarm Swarm;
Vector2 drawPosition; Vector2 drawPosition;
public Vector2 TransformedPosition
public Vector2[,] CurrentSpriteDeformation
{ {
get { return drawPosition; } get;
private set;
}
public Vector2[,] CurrentLightSpriteDeformation
{
get;
private set;
} }
public Vector2 SimPosition public Vector2 SimPosition
@@ -58,11 +73,10 @@ namespace Barotrauma
get; get;
set; set;
} }
public BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position) public BackgroundCreature(BackgroundCreaturePrefab prefab, Vector2 position)
{ {
this.prefab = prefab; this.Prefab = prefab;
this.position = position; this.position = position;
drawPosition = position; drawPosition = position;
@@ -70,18 +84,73 @@ namespace Barotrauma
steeringManager = new SteeringManager(this); steeringManager = new SteeringManager(this);
velocity = new Vector3( velocity = new Vector3(
Rand.Range(-prefab.Speed, prefab.Speed), Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly),
Rand.Range(-prefab.Speed, prefab.Speed), Rand.Range(-prefab.Speed, prefab.Speed, Rand.RandSync.ClientOnly),
Rand.Range(0.0f, prefab.WanderZAmount)); Rand.Range(0.0f, prefab.WanderZAmount, Rand.RandSync.ClientOnly));
checkWallsTimer = Rand.Range(0.0f, CheckWallsInterval); checkWallsTimer = Rand.Range(0.0f, CheckWallsInterval, Rand.RandSync.ClientOnly);
foreach (XElement subElement in prefab.Config.Elements())
{
List<SpriteDeformation> deformationList = null;
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "deformablesprite":
deformationList = spriteDeformations;
break;
case "deformablelightsprite":
deformationList = lightSpriteDeformations;
break;
default:
continue;
}
foreach (XElement animationElement in subElement.Elements())
{
SpriteDeformation deformation = null;
int sync = animationElement.GetAttributeInt("sync", -1);
if (sync > -1)
{
string typeName = animationElement.GetAttributeString("type", "").ToLowerInvariant();
deformation = uniqueSpriteDeformations.Find(d => d.TypeName == typeName && d.Sync == sync);
}
if (deformation == null)
{
deformation = SpriteDeformation.Load(animationElement, prefab.Name);
if (deformation != null)
{
uniqueSpriteDeformations.Add(deformation);
}
}
if (deformation != null)
{
deformationList.Add(deformation);
}
}
}
} }
public void Update(float deltaTime) public void Update(float deltaTime)
{ {
position += new Vector2(velocity.X, velocity.Y) * deltaTime; position += new Vector2(velocity.X, velocity.Y) * deltaTime;
depth = MathHelper.Clamp(depth + velocity.Z * deltaTime, 0.0f, MaxDepth); depth = MathHelper.Clamp(depth + velocity.Z * deltaTime, Prefab.MinDepth, Prefab.MaxDepth * 10);
if (Prefab.FlashInterval > 0.0f)
{
flashTimer -= deltaTime;
if (flashTimer > 0.0f)
{
alpha = 0.0f;
}
else
{
//value goes from 0 to 1 and back to 0 during the flash
alpha = (float)Math.Sin(-flashTimer / Prefab.FlashDuration * MathHelper.Pi) * PerlinNoise.GetPerlin((float)Timing.TotalTime * 0.1f, (float)Timing.TotalTime * 0.2f);
if (flashTimer < -Prefab.FlashDuration)
{
flashTimer = Prefab.FlashInterval;
}
}
}
checkWallsTimer -= deltaTime; checkWallsTimer -= deltaTime;
if (checkWallsTimer <= 0.0f && Level.Loaded != null) if (checkWallsTimer <= 0.0f && Level.Loaded != null)
@@ -134,54 +203,145 @@ namespace Barotrauma
float midPointDist = Vector2.Distance(SimPosition, midPoint) * 100.0f; float midPointDist = Vector2.Distance(SimPosition, midPoint) * 100.0f;
if (midPointDist > Swarm.MaxDistance) if (midPointDist > Swarm.MaxDistance)
{ {
steeringManager.SteeringSeek(midPoint, ((midPointDist / Swarm.MaxDistance) - 1.0f) * prefab.Speed); steeringManager.SteeringSeek(midPoint, ((midPointDist / Swarm.MaxDistance) - 1.0f) * Prefab.Speed);
} }
steeringManager.SteeringManual(deltaTime, Swarm.AvgVelocity() * Swarm.Cohesion); steeringManager.SteeringManual(deltaTime, Swarm.AvgVelocity() * Swarm.Cohesion);
} }
if (prefab.WanderAmount > 0.0f) if (Prefab.WanderAmount > 0.0f)
{ {
steeringManager.SteeringWander(prefab.Speed); steeringManager.SteeringWander(Prefab.Speed);
} }
if (obstacleDiff != Vector2.Zero) if (obstacleDiff != Vector2.Zero)
{ {
steeringManager.SteeringManual(deltaTime, -obstacleDiff * (1.0f - obstacleDist / 5000.0f) * prefab.Speed); steeringManager.SteeringManual(deltaTime, -obstacleDiff * (1.0f - obstacleDist / 5000.0f) * Prefab.Speed);
} }
steeringManager.Update(prefab.Speed); steeringManager.Update(Prefab.Speed);
if (prefab.WanderZAmount > 0.0f) if (Prefab.WanderZAmount > 0.0f)
{ {
wanderZPhase += Rand.Range(-prefab.WanderZAmount, prefab.WanderZAmount); wanderZPhase += Rand.Range(-Prefab.WanderZAmount, Prefab.WanderZAmount);
velocity.Z = (float)Math.Sin(wanderZPhase) * prefab.Speed; velocity.Z = (float)Math.Sin(wanderZPhase) * Prefab.Speed;
} }
velocity = Vector3.Lerp(velocity, new Vector3(Steering.X, Steering.Y, velocity.Z), deltaTime); velocity = Vector3.Lerp(velocity, new Vector3(Steering.X, Steering.Y, velocity.Z), deltaTime);
UpdateDeformations(deltaTime);
}
public void DrawLightSprite(SpriteBatch spriteBatch, Camera cam)
{
Draw(spriteBatch, cam, Prefab.LightSprite, Prefab.DeformableLightSprite, CurrentLightSpriteDeformation, Color.White * alpha);
} }
public void Draw(SpriteBatch spriteBatch, Camera cam) public void Draw(SpriteBatch spriteBatch, Camera cam)
{ {
Draw(spriteBatch,
cam,
Prefab.Sprite,
Prefab.DeformableSprite,
CurrentSpriteDeformation,
Color.Lerp(Color.White, Level.Loaded.BackgroundColor, depth / Math.Max(MaxDepth, Prefab.MaxDepth)) * alpha);
}
private void Draw(SpriteBatch spriteBatch, Camera cam, Sprite sprite, DeformableSprite deformableSprite, Vector2[,] currentSpriteDeformation, Color color)
{
if (sprite == null && deformableSprite == null) { return; }
if (color.A == 0) { return; }
float rotation = 0.0f; float rotation = 0.0f;
if (!prefab.DisableRotation) if (!Prefab.DisableRotation)
{ {
rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y)); rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y));
if (velocity.X < 0.0f) rotation -= MathHelper.Pi; if (velocity.X < 0.0f) { rotation -= MathHelper.Pi; }
} }
drawPosition = position; drawPosition = GetDrawPosition(cam);
if (depth > 0.0f)
float scale = GetScale();
sprite?.Draw(spriteBatch,
new Vector2(drawPosition.X, -drawPosition.Y),
color,
rotation,
scale,
Prefab.DisableFlipping || velocity.X > 0.0f ? SpriteEffects.None : SpriteEffects.FlipHorizontally,
Math.Min(depth / MaxDepth, 1.0f));
if (deformableSprite != null)
{
if (currentSpriteDeformation != null)
{
deformableSprite.Deform(currentSpriteDeformation);
}
else
{
deformableSprite.Reset();
}
deformableSprite?.Draw(cam,
new Vector3(drawPosition.X, drawPosition.Y, Math.Min(depth / 10000.0f, 1.0f)),
deformableSprite.Origin,
rotation,
Vector2.One * scale,
color,
mirror: Prefab.DisableFlipping || velocity.X <= 0.0f);
}
}
public Vector2 GetDrawPosition(Camera cam)
{
Vector2 drawPosition = WorldPosition;
if (depth >= 0)
{ {
Vector2 camOffset = drawPosition - cam.WorldViewCenter; Vector2 camOffset = drawPosition - cam.WorldViewCenter;
drawPosition -= camOffset * (depth / MaxDepth) * 0.05f; drawPosition -= camOffset * depth / MaxDepth;
} }
return drawPosition;
}
prefab.Sprite.Draw(spriteBatch, public float GetScale()
new Vector2(drawPosition.X, -drawPosition.Y), {
Color.Lerp(Color.White, Level.Loaded.BackgroundColor, (depth / MaxDepth) * 0.2f), return Math.Max(1.0f - depth / MaxDepth, 0.05f) * Prefab.Scale;
rotation, (1.0f - (depth / MaxDepth) * 0.2f) * prefab.Scale, }
velocity.X > 0.0f ? SpriteEffects.None : SpriteEffects.FlipHorizontally,
(depth / MaxDepth)); public Rectangle GetExtents(Camera cam)
{
Vector2 min = GetDrawPosition(cam);
Vector2 max = min;
float scale = GetScale();
GetSpriteExtents(Prefab.Sprite, ref min, ref max);
GetSpriteExtents(Prefab.LightSprite, ref min, ref max);
GetSpriteExtents(Prefab.DeformableSprite?.Sprite, ref min, ref max);
GetSpriteExtents(Prefab.DeformableLightSprite?.Sprite, ref min, ref max);
return new Rectangle(min.ToPoint(), (max - min).ToPoint());
void GetSpriteExtents(Sprite sprite, ref Vector2 min, ref Vector2 max)
{
if (sprite == null) { return; }
min.X = Math.Min(min.X, min.X - sprite.size.X * sprite.RelativeOrigin.X * scale);
min.Y = Math.Min(min.Y, min.Y - sprite.size.Y * sprite.RelativeOrigin.Y * scale);
max.X = Math.Max(max.X, max.X + sprite.size.X * (1.0f - sprite.RelativeOrigin.X) * scale);
max.Y = Math.Max(max.Y, max.Y + sprite.size.Y * (1.0f - sprite.RelativeOrigin.Y) * scale);
}
}
private void UpdateDeformations(float deltaTime)
{
foreach (SpriteDeformation deformation in uniqueSpriteDeformations)
{
deformation.Update(deltaTime);
}
if (spriteDeformations.Count > 0)
{
CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, Prefab.DeformableSprite.Size);
}
if (lightSpriteDeformations.Count > 0)
{
CurrentLightSpriteDeformation = SpriteDeformation.GetDeformation(lightSpriteDeformations, Prefab.DeformableLightSprite.Size);
}
} }
} }
@@ -9,14 +9,14 @@ namespace Barotrauma
{ {
class BackgroundCreatureManager class BackgroundCreatureManager
{ {
const int MaxSprites = 100; const int MaxCreatures = 100;
const float CheckActiveInterval = 1.0f; const float VisibilityCheckInterval = 1.0f;
private float checkActiveTimer; private float checkVisibleTimer;
private List<BackgroundCreaturePrefab> prefabs = new List<BackgroundCreaturePrefab>(); private readonly List<BackgroundCreaturePrefab> prefabs = new List<BackgroundCreaturePrefab>();
private List<BackgroundCreature> activeSprites = new List<BackgroundCreature>(); private readonly List<BackgroundCreature> creatures = new List<BackgroundCreature>();
public BackgroundCreatureManager(string configPath) public BackgroundCreatureManager(string configPath)
{ {
@@ -60,92 +60,111 @@ namespace Barotrauma
} }
} }
public void SpawnSprites(int count, Vector2? position = null) public void SpawnCreatures(Level level, int count, Vector2? position = null)
{ {
activeSprites.Clear(); creatures.Clear();
if (prefabs.Count == 0) return; if (prefabs.Count == 0) { return; }
count = Math.Min(count, MaxSprites); count = Math.Min(count, MaxCreatures);
for (int i = 0; i < count; i++ ) List<BackgroundCreaturePrefab> availablePrefabs = new List<BackgroundCreaturePrefab>(prefabs);
for (int i = 0; i < count; i++)
{ {
Vector2 pos = Vector2.Zero; Vector2 pos = Vector2.Zero;
if (position == null) if (position == null)
{ {
var wayPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine==null); var wayPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine == null);
if (wayPoints.Any()) if (wayPoints.Any())
{ {
WayPoint wp = wayPoints[Rand.Int(wayPoints.Count, Rand.RandSync.ClientOnly)]; WayPoint wp = wayPoints[Rand.Int(wayPoints.Count, Rand.RandSync.ClientOnly)];
pos = new Vector2(wp.Rect.X, wp.Rect.Y); pos = new Vector2(wp.Rect.X, wp.Rect.Y);
pos += Rand.Vector(200.0f, Rand.RandSync.ClientOnly); pos += Rand.Vector(200.0f, Rand.RandSync.ClientOnly);
} }
else else
{ {
pos = Rand.Vector(2000.0f, Rand.RandSync.ClientOnly); pos = Rand.Vector(2000.0f, Rand.RandSync.ClientOnly);
} }
} }
else else
{ {
pos = (Vector2)position; pos = (Vector2)position;
} }
var prefab = ToolBox.SelectWeightedRandom(availablePrefabs, availablePrefabs.Select(p => p.GetCommonness(level.GenerationParams)).ToList(), Rand.RandSync.ClientOnly);
var prefab = prefabs[Rand.Int(prefabs.Count, Rand.RandSync.ClientOnly)]; if (prefab == null) { break; }
int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax, Rand.RandSync.ClientOnly); int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax, Rand.RandSync.ClientOnly);
List<BackgroundCreature> swarmMembers = new List<BackgroundCreature>(); List<BackgroundCreature> swarmMembers = new List<BackgroundCreature>();
for (int n = 0; n < amount; n++) for (int n = 0; n < amount; n++)
{ {
var newSprite = new BackgroundCreature(prefab, pos); var creature = new BackgroundCreature(prefab, pos + Rand.Vector(Rand.Range(0.0f, prefab.SwarmRadius, Rand.RandSync.ClientOnly), Rand.RandSync.ClientOnly));
activeSprites.Add(newSprite); creatures.Add(creature);
swarmMembers.Add(newSprite); swarmMembers.Add(creature);
} }
if (amount > 0) if (amount > 1)
{ {
new Swarm(swarmMembers, prefab.SwarmRadius, prefab.SwarmCohesion); new Swarm(swarmMembers, prefab.SwarmRadius, prefab.SwarmCohesion);
} }
if (creatures.Count(c => c.Prefab == prefab) > prefab.MaxCount)
{
availablePrefabs.Remove(prefab);
if (availablePrefabs.Count <= 0) { break; }
}
} }
} }
public void ClearSprites() public void Clear()
{ {
activeSprites.Clear(); creatures.Clear();
} }
public void Update(float deltaTime, Camera cam) public void Update(float deltaTime, Camera cam)
{ {
if (checkActiveTimer < 0.0f) if (checkVisibleTimer < 0.0f)
{ {
foreach (BackgroundCreature sprite in activeSprites) int margin = 500;
foreach (BackgroundCreature creature in creatures)
{ {
sprite.Enabled = Math.Abs(sprite.TransformedPosition.X - cam.WorldViewCenter.X) < cam.WorldView.Width && Rectangle extents = creature.GetExtents(cam);
Math.Abs(sprite.TransformedPosition.Y - cam.WorldViewCenter.Y) < cam.WorldView.Height; bool wasVisible = creature.Visible;
creature.Visible =
extents.Right >= cam.WorldView.X - margin &&
extents.X <= cam.WorldView.Right + margin &&
extents.Bottom >= cam.WorldView.Y - cam.WorldView.Height - margin &&
extents.Y <= cam.WorldView.Y + margin;
} }
checkActiveTimer = CheckActiveInterval; checkVisibleTimer = VisibilityCheckInterval;
} }
else else
{ {
checkActiveTimer -= deltaTime; checkVisibleTimer -= deltaTime;
} }
foreach (BackgroundCreature sprite in activeSprites) foreach (BackgroundCreature creature in creatures)
{ {
if (!sprite.Enabled) continue; if (!creature.Visible) { continue; }
sprite.Update(deltaTime); creature.Update(deltaTime);
} }
} }
public void Draw(SpriteBatch spriteBatch, Camera cam) public void Draw(SpriteBatch spriteBatch, Camera cam)
{ {
foreach (BackgroundCreature sprite in activeSprites) foreach (BackgroundCreature creature in creatures)
{ {
if (!sprite.Enabled) continue; if (!creature.Visible) { continue; }
sprite.Draw(spriteBatch, cam); creature.Draw(spriteBatch, cam);
}
}
public void DrawLights(SpriteBatch spriteBatch, Camera cam)
{
foreach (BackgroundCreature creature in creatures)
{
if (!creature.Visible) { continue; }
creature.DrawLightSprite(spriteBatch, cam);
} }
} }
} }
@@ -1,50 +1,117 @@
using System.Xml.Linq; using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class BackgroundCreaturePrefab class BackgroundCreaturePrefab
{ {
public readonly Sprite Sprite; public readonly Sprite Sprite, LightSprite;
public readonly DeformableSprite DeformableSprite, DeformableLightSprite;
public readonly float Speed; public readonly string Name;
public readonly float WanderAmount; public readonly XElement Config;
public readonly float WanderZAmount; [Serialize(1.0f, true)]
public float Speed { get; private set; }
public readonly int SwarmMin, SwarmMax; [Serialize(0.0f, true)]
public readonly float SwarmRadius, SwarmCohesion; public float WanderAmount { get; private set; }
public readonly bool DisableRotation; [Serialize(0.0f, true)]
public float WanderZAmount { get; private set; }
[Serialize(1, true)]
public int SwarmMin { get; private set; }
[Serialize(1, true)]
public int SwarmMax { get; private set; }
[Serialize(200.0f, true)]
public float SwarmRadius { get; private set; }
[Serialize(0.2f, true)]
public float SwarmCohesion { get; private set; }
[Serialize(10.0f, true)]
public float MinDepth { get; private set; }
[Serialize(1000.0f, true)]
public float MaxDepth { get; private set; }
[Serialize(false, true)]
public bool DisableRotation { get; private set; }
[Serialize(false, true)]
public bool DisableFlipping { get; private set; }
[Serialize(1.0f, true)]
public float Scale { get; private set; }
[Serialize(1.0f, true)]
public float Commonness { get; private set; }
[Serialize(1000, true)]
public int MaxCount { get; private set; }
[Serialize(0.0f, true)]
public float FlashInterval { get; private set; }
[Serialize(0.0f, true)]
public float FlashDuration { get; private set; }
/// <summary>
/// Overrides the commonness of the object in a specific level type.
/// Key = name of the level type, value = commonness in that level type.
/// </summary>
public Dictionary<string, float> OverrideCommonness = new Dictionary<string, float>();
public readonly float Scale;
public BackgroundCreaturePrefab(XElement element) public BackgroundCreaturePrefab(XElement element)
{ {
Speed = element.GetAttributeFloat("speed", 1.0f); Name = element.Name.ToString();
WanderAmount = element.GetAttributeFloat("wanderamount", 0.0f); Config = element;
WanderZAmount = element.GetAttributeFloat("wanderzamount", 0.0f); SerializableProperty.DeserializeProperties(this, element);
SwarmMin = element.GetAttributeInt("swarmmin", 1);
SwarmMax = element.GetAttributeInt("swarmmax", 1);
SwarmRadius = element.GetAttributeFloat("swarmradius", 200.0f);
SwarmCohesion = element.GetAttributeFloat("swarmcohesion", 0.2f);
DisableRotation = element.GetAttributeBool("disablerotation", false);
Scale = element.GetAttributeFloat("scale", 1.0f);
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (!subElement.Name.ToString().Equals("sprite", System.StringComparison.OrdinalIgnoreCase)) { continue; } switch (subElement.Name.ToString().ToLowerInvariant())
{
Sprite = new Sprite(subElement, lazyLoad: true); case "sprite":
break; Sprite = new Sprite(subElement, lazyLoad: true);
break;
case "deformablesprite":
DeformableSprite = new DeformableSprite(subElement, lazyLoad: true);
break;
case "lightsprite":
LightSprite = new Sprite(subElement, lazyLoad: true);
break;
case "deformablelightsprite":
DeformableLightSprite = new DeformableSprite(subElement, lazyLoad: true);
break;
case "overridecommonness":
string levelType = subElement.GetAttributeString("leveltype", "").ToLowerInvariant();
if (!OverrideCommonness.ContainsKey(levelType))
{
OverrideCommonness.Add(levelType, subElement.GetAttributeFloat("commonness", 1.0f));
}
break;
}
} }
} }
public float GetCommonness(LevelGenerationParams generationParams)
{
if (generationParams?.Identifier != null &&
(OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness) ||
(generationParams.OldIdentifier != null && OverrideCommonness.TryGetValue(generationParams.OldIdentifier, out commonness))))
{
return commonness;
}
return Commonness;
}
} }
} }
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework; using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -8,72 +9,73 @@ namespace Barotrauma
{ {
static partial class CaveGenerator static partial class CaveGenerator
{ {
public static List<VertexPositionTexture> GenerateRenderVerticeList(List<Vector2[]> triangles) public static List<VertexPositionTexture> GenerateWallVertices(List<Vector2[]> triangles, LevelGenerationParams generationParams, float zCoord)
{ {
var verticeList = new List<VertexPositionTexture>(); var vertices = new List<VertexPositionTexture>();
for (int i = 0; i < triangles.Count; i++) for (int i = 0; i < triangles.Count; i++)
{ {
foreach (Vector2 vertex in triangles[i]) foreach (Vector2 vertex in triangles[i])
{ {
//shift the coordinates around a bit to make the texture repetition less obvious Vector2 uvCoords = vertex / generationParams.WallTextureSize;
Vector2 uvCoords = new Vector2( vertices.Add(new VertexPositionTexture(new Vector3(vertex, zCoord), uvCoords));
vertex.X / 2000.0f + (float)Math.Sin(vertex.X / 500.0f) * 0.15f,
vertex.Y / 2000.0f + (float)Math.Sin(vertex.Y / 700.0f) * 0.15f);
verticeList.Add(new VertexPositionTexture(new Vector3(vertex, 1.0f), uvCoords));
} }
} }
return verticeList; return vertices;
} }
public static VertexPositionTexture[] GenerateWallShapes(List<VoronoiCell> cells, Level level) public static List<VertexPositionTexture> GenerateWallEdgeVertices(List<VoronoiCell> cells, Level level, float zCoord)
{ {
float outWardThickness = 30.0f; float outWardThickness = level.GenerationParams.WallEdgeExpandOutwardsAmount;
List<VertexPositionTexture> verticeList = new List<VertexPositionTexture>(); List<VertexPositionTexture> vertices = new List<VertexPositionTexture>();
foreach (VoronoiCell cell in cells) foreach (VoronoiCell cell in cells)
{ {
CompareCCW compare = new CompareCCW(cell.Center); float circumference = 0.0f;
foreach (GraphEdge edge in cell.Edges) foreach (GraphEdge edge in cell.Edges)
{ {
if (edge.Cell1 != null && edge.Cell1.Body == null && edge.Cell1.CellType != CellType.Empty) edge.Cell1 = null; circumference += Vector2.Distance(edge.Point1, edge.Point2);
if (edge.Cell2 != null && edge.Cell2.Body == null && edge.Cell2.CellType != CellType.Empty) edge.Cell2 = null;
if (compare.Compare(edge.Point1, edge.Point2) == -1)
{
var temp = edge.Point1;
edge.Point1 = edge.Point2;
edge.Point2 = temp;
}
} }
}
foreach (VoronoiCell cell in cells)
{
foreach (GraphEdge edge in cell.Edges) foreach (GraphEdge edge in cell.Edges)
{ {
if (!edge.IsSolid) continue; if (!edge.IsSolid) { continue; }
GraphEdge leftEdge = cell.Edges.Find(e => e != edge && (edge.Point1 == e.Point1 || edge.Point1 == e.Point2)); GraphEdge leftEdge = cell.Edges.Find(e => e != edge && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
GraphEdge rightEdge = cell.Edges.Find(e => e != edge && (edge.Point2 == e.Point1 || edge.Point2 == e.Point2)); var leftAdjacentCell = leftEdge?.AdjacentCell(cell);
if (leftAdjacentCell != null)
{
var adjEdge = leftAdjacentCell.Edges.Find(e => e != leftEdge && e.IsSolid && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
if (adjEdge != null) { leftEdge = adjEdge; }
}
GraphEdge rightEdge = cell.Edges.Find(e => e != edge && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
var rightAdjacentCell = rightEdge?.AdjacentCell(cell);
if (rightAdjacentCell != null)
{
var adjEdge = rightAdjacentCell.Edges.Find(e => e != rightEdge && e.IsSolid && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
if (adjEdge != null) { rightEdge = adjEdge; }
}
Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero; Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero;
float inwardThickness1 = 100; float inwardThickness1 = level.GenerationParams.WallEdgeExpandInwardsAmount;
float inwardThickness2 = 100; float inwardThickness2 = level.GenerationParams.WallEdgeExpandInwardsAmount;
if (leftEdge != null && !leftEdge.IsSolid) if (leftEdge != null && !leftEdge.IsSolid)
{ {
leftNormal = edge.Point1 == leftEdge.Point1 ? leftNormal = edge.Point1.NearlyEquals(leftEdge.Point1) ?
Vector2.Normalize(leftEdge.Point2 - leftEdge.Point1) : Vector2.Normalize(leftEdge.Point2 - leftEdge.Point1) :
Vector2.Normalize(leftEdge.Point1 - leftEdge.Point2); Vector2.Normalize(leftEdge.Point1 - leftEdge.Point2);
inwardThickness1 = Vector2.Distance(leftEdge.Point1, leftEdge.Point2) / 2; }
else if (leftEdge != null)
{
leftNormal = -Vector2.Normalize(edge.GetNormal(cell) + leftEdge.GetNormal(leftAdjacentCell ?? cell));
if (!MathUtils.IsValid(leftNormal)) { leftNormal = -edge.GetNormal(cell); }
} }
else else
{ {
leftNormal = Vector2.Normalize(cell.Center - edge.Point1); leftNormal = Vector2.Normalize(cell.Center - edge.Point1);
inwardThickness1 = Vector2.Distance(edge.Point1, cell.Center) / 2;
} }
inwardThickness1 = Math.Min(Vector2.Distance(edge.Point1, cell.Center), inwardThickness1);
if (!MathUtils.IsValid(leftNormal)) if (!MathUtils.IsValid(leftNormal))
{ {
@@ -86,7 +88,7 @@ namespace Barotrauma
if (cell.Body != null) if (cell.Body != null)
{ {
GameMain.World.Remove(cell.Body); if (GameMain.World.BodyList.Contains(cell.Body)) { GameMain.World.Remove(cell.Body); }
cell.Body = null; cell.Body = null;
} }
leftNormal = Vector2.UnitX; leftNormal = Vector2.UnitX;
@@ -95,16 +97,20 @@ namespace Barotrauma
if (rightEdge != null && !rightEdge.IsSolid) if (rightEdge != null && !rightEdge.IsSolid)
{ {
rightNormal = edge.Point2 == rightEdge.Point1 ? rightNormal = edge.Point2.NearlyEquals(rightEdge.Point1) ?
Vector2.Normalize(rightEdge.Point2 - rightEdge.Point1) : Vector2.Normalize(rightEdge.Point2 - rightEdge.Point1) :
Vector2.Normalize(rightEdge.Point1 - rightEdge.Point2); Vector2.Normalize(rightEdge.Point1 - rightEdge.Point2);
inwardThickness2 = Vector2.Distance(rightEdge.Point1, rightEdge.Point2) / 2; }
else if (rightEdge != null)
{
rightNormal = -Vector2.Normalize(edge.GetNormal(cell) + rightEdge.GetNormal(rightAdjacentCell ?? cell));
if (!MathUtils.IsValid(rightNormal)) { rightNormal = -edge.GetNormal(cell); }
} }
else else
{ {
rightNormal = Vector2.Normalize(cell.Center - edge.Point2); rightNormal = Vector2.Normalize(cell.Center - edge.Point2);
inwardThickness2 = Vector2.Distance(edge.Point2, cell.Center) / 2;
} }
inwardThickness2 = Math.Min(Vector2.Distance(edge.Point2, cell.Center), inwardThickness2);
if (!MathUtils.IsValid(rightNormal)) if (!MathUtils.IsValid(rightNormal))
{ {
@@ -117,7 +123,7 @@ namespace Barotrauma
if (cell.Body != null) if (cell.Body != null)
{ {
GameMain.World.Remove(cell.Body); if (GameMain.World.BodyList.Contains(cell.Body)) { GameMain.World.Remove(cell.Body); }
cell.Body = null; cell.Body = null;
} }
rightNormal = Vector2.UnitX; rightNormal = Vector2.UnitX;
@@ -127,14 +133,13 @@ namespace Barotrauma
float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - cell.Center)); float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - cell.Center));
float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - cell.Center)); float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - cell.Center));
//handle wrapping around 0/360 //handle wrapping around 0/360
if (point1UV - point2UV > MathHelper.Pi) if (point1UV - point2UV > MathHelper.Pi)
{ {
point2UV += MathHelper.TwoPi; point1UV -= MathHelper.TwoPi;
} }
//the texture wraps around the cell 4 times int textureRepeatCount = (int)Math.Max(circumference / 2 / level.GenerationParams.WallEdgeTextureWidth, 1);
//TODO: define the uv scale in level generation parameters? point1UV = point1UV / MathHelper.TwoPi * textureRepeatCount;
point1UV = point1UV / MathHelper.TwoPi * 4; point2UV = point2UV / MathHelper.TwoPi * textureRepeatCount;
point2UV = point2UV / MathHelper.TwoPi * 4;
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
@@ -147,9 +152,9 @@ namespace Barotrauma
verts[1] = edge.Point2 - rightNormal * outWardThickness; verts[1] = edge.Point2 - rightNormal * outWardThickness;
verts[2] = edge.Point1 + leftNormal * inwardThickness1; verts[2] = edge.Point1 + leftNormal * inwardThickness1;
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(point1UV, 0.0f)); vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 0.0f));
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), new Vector2(point2UV, 0.0f)); vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(point1UV, 0.5f)); vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point1UV, 1.0f));
} }
else else
{ {
@@ -158,16 +163,16 @@ namespace Barotrauma
verts[1] = edge.Point2 - rightNormal * outWardThickness; verts[1] = edge.Point2 - rightNormal * outWardThickness;
verts[2] = edge.Point2 + rightNormal * inwardThickness2; verts[2] = edge.Point2 + rightNormal * inwardThickness2;
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(point1UV, 0.5f)); vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 1.0f));
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), new Vector2(point2UV, 0.0f)); vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(point2UV, 0.5f)); vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point2UV, 1.0f));
} }
verticeList.AddRange(vertPos); vertices.AddRange(vertPos);
} }
} }
} }
return verticeList.ToArray(); return vertices;
} }
} }
} }
@@ -0,0 +1,62 @@
using Microsoft.Xna.Framework;
using System;
using System.Linq;
namespace Barotrauma
{
partial class DestructibleLevelWall : LevelWall, IDamageable
{
public override float Alpha
{
get
{
if (FadeOutDuration <= 0.0f || FadeOutTimer < FadeOutDuration - 1.0f) { return 1.0f; }
return MathHelper.Clamp(FadeOutDuration - FadeOutTimer, 0.0f, 1.0f);
}
}
partial void AddDamageProjSpecific(float damage, Vector2 worldPosition)
{
if (damage <= 0.0f) { return; }
Vector2 particlePos = worldPosition;
if (!Cells.Any(c => c.IsPointInside(particlePos)))
{
bool intersectionFound = false;
foreach (var cell in Cells)
{
foreach (var edge in cell.Edges)
{
if (MathUtils.GetLineIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
{
intersectionFound = true;
particlePos = intersection;
break;
}
}
if (intersectionFound) { break; }
}
}
Vector2 particleDir = particlePos - WorldPosition;
if (particleDir.LengthSquared() > 0.0001f) { particleDir = Vector2.Normalize(particleDir); }
int particleAmount = MathHelper.Clamp((int)damage, 1, 10);
for (int i = 0; i < particleAmount; i++)
{
var particle = GameMain.ParticleManager.CreateParticle("iceshards",
particlePos + Rand.Vector(5.0f),
particleDir * Rand.Range(200.0f, 500.0f) + Rand.Vector(100.0f));
}
}
public void SetDamage(float damage)
{
Damage = damage;
if (Damage >= MaxHealth && !Destroyed)
{
CreateFragments();
Destroy();
}
}
}
}
@@ -14,6 +14,8 @@ namespace Barotrauma
private BackgroundCreatureManager backgroundCreatureManager; private BackgroundCreatureManager backgroundCreatureManager;
public BackgroundCreatureManager BackgroundCreatureManager => backgroundCreatureManager;
public LevelRenderer Renderer => renderer; public LevelRenderer Renderer => renderer;
public void ReloadTextures() public void ReloadTextures()
@@ -33,14 +35,6 @@ namespace Barotrauma
uniqueSprites.Add(sprite); uniqueSprites.Add(sprite);
} }
} }
foreach (Sprite specularSprite in levelObj.Prefab.SpecularSprites)
{
if (!uniqueTextures.Contains(specularSprite.Texture))
{
uniqueTextures.Add(specularSprite.Texture);
uniqueSprites.Add(specularSprite);
}
}
} }
foreach (Sprite sprite in uniqueSprites) foreach (Sprite sprite in uniqueSprites)
@@ -51,7 +45,7 @@ namespace Barotrauma
public void DrawFront(SpriteBatch spriteBatch, Camera cam) public void DrawFront(SpriteBatch spriteBatch, Camera cam)
{ {
if (renderer == null) return; if (renderer == null) { return; }
renderer.Draw(spriteBatch, cam); renderer.Draw(spriteBatch, cam);
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f) if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
@@ -123,22 +117,34 @@ namespace Barotrauma
if (renderer == null) return; if (renderer == null) return;
renderer.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager); renderer.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager);
} }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
foreach (LevelWall levelWall in ExtraWalls) bool isGlobalUpdate = msg.ReadBoolean();
if (isGlobalUpdate)
{ {
if (levelWall.Body.BodyType == BodyType.Static) continue; foreach (LevelWall levelWall in ExtraWalls)
Vector2 bodyPos = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
levelWall.MoveState = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 16);
if (Vector2.DistanceSquared(bodyPos, levelWall.Body.Position) > 0.5f)
{ {
levelWall.Body.SetTransformIgnoreContacts(ref bodyPos, levelWall.Body.Rotation); if (levelWall.Body.BodyType == BodyType.Static) { continue; }
Vector2 bodyPos = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
levelWall.MoveState = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 16);
DestructibleLevelWall destructibleWall = levelWall as DestructibleLevelWall;
if (Vector2.DistanceSquared(bodyPos, levelWall.Body.Position) > 0.5f && (destructibleWall == null || !destructibleWall.Destroyed))
{
levelWall.Body.SetTransformIgnoreContacts(ref bodyPos, levelWall.Body.Rotation);
}
}
}
else
{
int index = msg.ReadUInt16();
byte damageByte = msg.ReadByte();
if (index < ExtraWalls.Count && ExtraWalls[index] is DestructibleLevelWall destructibleWall)
{
destructibleWall.SetDamage(destructibleWall.MaxHealth * damageByte / 255.0f);
} }
} }
} }
@@ -1,14 +1,12 @@
using Barotrauma.Lights; using Barotrauma.Lights;
using Barotrauma.Networking;
using Barotrauma.Particles; using Barotrauma.Particles;
using Barotrauma.Sounds; using Barotrauma.Sounds;
using Barotrauma.Networking; using Barotrauma.SpriteDeformations;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.SpriteDeformations;
using System.Linq;
using FarseerPhysics.Dynamics;
namespace Barotrauma namespace Barotrauma
{ {
@@ -77,7 +75,6 @@ namespace Barotrauma
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
Sprite?.EnsureLazyLoaded(); Sprite?.EnsureLazyLoaded();
SpecularSprite?.EnsureLazyLoaded();
Prefab.DeformableSprite?.EnsureLazyLoaded(); Prefab.DeformableSprite?.EnsureLazyLoaded();
CurrentSwingAmount = Prefab.SwingAmountRad; CurrentSwingAmount = Prefab.SwingAmountRad;
@@ -98,7 +95,7 @@ namespace Barotrauma
} }
} }
if (Prefab.LightSourceParams != null) if (Prefab.LightSourceParams != null && Prefab.LightSourceParams.Count > 0)
{ {
LightSources = new LightSource[Prefab.LightSourceParams.Count]; LightSources = new LightSource[Prefab.LightSourceParams.Count];
LightSourceTriggers = new LevelTrigger[Prefab.LightSourceParams.Count]; LightSourceTriggers = new LevelTrigger[Prefab.LightSourceParams.Count];
@@ -232,17 +229,21 @@ namespace Barotrauma
deformation.Update(deltaTime); deformation.Update(deltaTime);
} }
CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, ActivePrefab.DeformableSprite.Size); CurrentSpriteDeformation = SpriteDeformation.GetDeformation(spriteDeformations, ActivePrefab.DeformableSprite.Size);
foreach (LightSource lightSource in LightSources) if (LightSources != null)
{ {
if (lightSource?.DeformableLightSprite != null) foreach (LightSource lightSource in LightSources)
{ {
lightSource.DeformableLightSprite.Deform(CurrentSpriteDeformation); if (lightSource?.DeformableLightSprite != null)
{
lightSource.DeformableLightSprite.Deform(CurrentSpriteDeformation);
}
} }
} }
} }
private void UpdatePositionalDeformation(PositionalDeformation positionalDeformation, float deltaTime) private void UpdatePositionalDeformation(PositionalDeformation positionalDeformation, float deltaTime)
{ {
if (Triggers == null) { return; }
Matrix matrix = ActivePrefab.DeformableSprite.GetTransform( Matrix matrix = ActivePrefab.DeformableSprite.GetTransform(
Position, Position,
ActivePrefab.DeformableSprite.Origin, ActivePrefab.DeformableSprite.Origin,
@@ -258,7 +259,7 @@ namespace Barotrauma
Vector2 moveAmount = triggerer.WorldPosition - trigger.TriggererPosition[triggerer]; Vector2 moveAmount = triggerer.WorldPosition - trigger.TriggererPosition[triggerer];
moveAmount = Vector2.Transform(moveAmount, rotationMatrix); moveAmount = Vector2.Transform(moveAmount, rotationMatrix);
moveAmount /= (ActivePrefab.DeformableSprite.Size * Scale); moveAmount /= ActivePrefab.DeformableSprite.Size * Scale;
moveAmount.Y = -moveAmount.Y; moveAmount.Y = -moveAmount.Y;
positionalDeformation.Deform(trigger.WorldPosition, moveAmount, deltaTime, Matrix.Invert(matrix) * positionalDeformation.Deform(trigger.WorldPosition, moveAmount, deltaTime, Matrix.Invert(matrix) *
@@ -269,9 +270,10 @@ namespace Barotrauma
public void ClientRead(IReadMessage msg) public void ClientRead(IReadMessage msg)
{ {
if (Triggers == null) { return; }
for (int i = 0; i < Triggers.Count; i++) for (int i = 0; i < Triggers.Count; i++)
{ {
if (!Triggers[i].UseNetworkSyncing) continue; if (!Triggers[i].UseNetworkSyncing) { continue; }
Triggers[i].ClientRead(msg); Triggers[i].ClientRead(msg);
} }
} }
@@ -12,6 +12,8 @@ namespace Barotrauma
private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>(); private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>();
private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>(); private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>();
private double NextRefreshTime;
//Maximum number of visible objects drawn at once. Should be large enough to not have an effect during normal gameplay, //Maximum number of visible objects drawn at once. Should be large enough to not have an effect during normal gameplay,
//but small enough to prevent wrecking performance when zooming out very far //but small enough to prevent wrecking performance when zooming out very far
const int MaxVisibleObjects = 500; const int MaxVisibleObjects = 500;
@@ -38,11 +40,13 @@ namespace Barotrauma
/// <summary> /// <summary>
/// Checks which level objects are in camera view and adds them to the visibleObjects lists /// Checks which level objects are in camera view and adds them to the visibleObjects lists
/// </summary> /// </summary>
private void RefreshVisibleObjects(Rectangle currentIndices) private void RefreshVisibleObjects(Rectangle currentIndices, float zoom)
{ {
visibleObjectsBack.Clear(); visibleObjectsBack.Clear();
visibleObjectsFront.Clear(); visibleObjectsFront.Clear();
float minSizeToDraw = MathHelper.Lerp(10.0f, 5.0f, Math.Min(zoom * 20.0f, 1.0f));
for (int x = currentIndices.X; x <= currentIndices.Width; x++) for (int x = currentIndices.X; x <= currentIndices.Width; x++)
{ {
for (int y = currentIndices.Y; y <= currentIndices.Height; y++) for (int y = currentIndices.Y; y <= currentIndices.Height; y++)
@@ -50,6 +54,22 @@ namespace Barotrauma
if (objectGrid[x, y] == null) { continue; } if (objectGrid[x, y] == null) { continue; }
foreach (LevelObject obj in objectGrid[x, y]) foreach (LevelObject obj in objectGrid[x, y])
{ {
if (zoom < 0.05f)
{
//hide if the sprite is very small when zoomed this far out
if ((obj.Sprite != null && Math.Min(obj.Sprite.size.X * zoom, obj.Sprite.size.Y * zoom) < 5.0f) ||
(obj.ActivePrefab?.DeformableSprite != null && Math.Min(obj.ActivePrefab.DeformableSprite.Sprite.size.X * zoom, obj.ActivePrefab.DeformableSprite.Sprite.size.Y * zoom) < minSizeToDraw))
{
continue;
}
float zCutoff = MathHelper.Lerp(5000.0f, 500.0f, (0.05f - zoom) * 20.0f);
if (obj.Position.Z > zCutoff)
{
continue;
}
}
var objectList = obj.Position.Z >= 0 ? visibleObjectsBack : visibleObjectsFront; var objectList = obj.Position.Z >= 0 ? visibleObjectsBack : visibleObjectsFront;
int drawOrderIndex = 0; int drawOrderIndex = 0;
for (int i = 0; i < objectList.Count; i++) for (int i = 0; i < objectList.Count; i++)
@@ -83,7 +103,7 @@ namespace Barotrauma
} }
public void DrawObjects(SpriteBatch spriteBatch, Camera cam, bool drawFront, bool specular = false) public void DrawObjects(SpriteBatch spriteBatch, Camera cam, bool drawFront)
{ {
Rectangle indices = Rectangle.Empty; Rectangle indices = Rectangle.Empty;
indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize); indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize);
@@ -102,9 +122,14 @@ namespace Barotrauma
indices.Height = Math.Min(indices.Height, objectGrid.GetLength(1) - 1); indices.Height = Math.Min(indices.Height, objectGrid.GetLength(1) - 1);
float z = 0.0f; float z = 0.0f;
if (currentGridIndices != indices) if (currentGridIndices != indices && Timing.TotalTime > NextRefreshTime)
{ {
RefreshVisibleObjects(indices); RefreshVisibleObjects(indices, cam.Zoom);
if (cam.Zoom < 0.1f)
{
//when zoomed very far out, refresh a little less often
NextRefreshTime = Timing.TotalTime + MathHelper.Lerp(1.0f, 0.0f, cam.Zoom * 10.0f);
}
} }
var objectList = drawFront ? visibleObjectsFront : visibleObjectsBack; var objectList = drawFront ? visibleObjectsFront : visibleObjectsBack;
@@ -113,19 +138,17 @@ namespace Barotrauma
Vector2 camDiff = new Vector2(obj.Position.X, obj.Position.Y) - cam.WorldViewCenter; Vector2 camDiff = new Vector2(obj.Position.X, obj.Position.Y) - cam.WorldViewCenter;
camDiff.Y = -camDiff.Y; camDiff.Y = -camDiff.Y;
Sprite activeSprite = specular ? obj.SpecularSprite : obj.Sprite; Sprite activeSprite = obj.Sprite;
activeSprite?.Draw( activeSprite?.Draw(
spriteBatch, spriteBatch,
new Vector2(obj.Position.X, -obj.Position.Y) - camDiff * obj.Position.Z / 10000.0f, new Vector2(obj.Position.X, -obj.Position.Y) - camDiff * obj.Position.Z / 10000.0f,
Color.Lerp(Color.White, Level.Loaded.BackgroundTextureColor, obj.Position.Z / 5000.0f), Color.Lerp(Color.White, Level.Loaded.BackgroundTextureColor, obj.Position.Z / 3000.0f),
activeSprite.Origin, activeSprite.Origin,
obj.CurrentRotation, obj.CurrentRotation,
obj.CurrentScale, obj.CurrentScale,
SpriteEffects.None, SpriteEffects.None,
z); z);
if (specular) continue;
if (obj.ActivePrefab.DeformableSprite != null) if (obj.ActivePrefab.DeformableSprite != null)
{ {
if (obj.CurrentSpriteDeformation != null) if (obj.CurrentSpriteDeformation != null)
@@ -149,6 +172,7 @@ namespace Barotrauma
{ {
GUI.DrawRectangle(spriteBatch, new Vector2(obj.Position.X, -obj.Position.Y), new Vector2(10.0f, 10.0f), GUI.Style.Red, true); GUI.DrawRectangle(spriteBatch, new Vector2(obj.Position.X, -obj.Position.Y), new Vector2(10.0f, 10.0f), GUI.Style.Red, true);
if (obj.Triggers == null) { continue; }
foreach (LevelTrigger trigger in obj.Triggers) foreach (LevelTrigger trigger in obj.Triggers)
{ {
if (trigger.PhysicsBody == null) continue; if (trigger.PhysicsBody == null) continue;
@@ -126,7 +126,6 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
case "childobject": case "childobject":
case "lightsource":
subElement.Remove(); subElement.Remove();
break; break;
case "deformablesprite": case "deformablesprite":
@@ -141,11 +140,31 @@ namespace Barotrauma
} }
} }
foreach (LightSourceParams lightSourceParams in LightSourceParams) for (int i = 0; i < LightSourceParams.Count; i++)
{ {
var lightElement = new XElement("LightSource"); int elementIndex = 0;
SerializableProperty.SerializeProperties(lightSourceParams, lightElement); bool wasSaved = false;
element.Add(lightElement); foreach (XElement subElement in element.Elements().ToList())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "lightsource":
if (elementIndex == i)
{
SerializableProperty.SerializeProperties(LightSourceParams[i], subElement);
wasSaved = true;
break;
}
elementIndex++;
break;
}
}
if (!wasSaved)
{
var lightElement = new XElement("LightSource");
SerializableProperty.SerializeProperties(LightSourceParams[i], lightElement);
element.Add(lightElement);
}
} }
foreach (ChildObject childObj in ChildObjects) foreach (ChildObject childObj in ChildObjects)
@@ -162,7 +181,7 @@ namespace Barotrauma
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().Equals("overridecommonness", System.StringComparison.OrdinalIgnoreCase) if (subElement.Name.ToString().Equals("overridecommonness", System.StringComparison.OrdinalIgnoreCase)
&& subElement.GetAttributeString("leveltype", "") == overrideCommonness.Key) && subElement.GetAttributeString("leveltype", "").Equals(overrideCommonness.Key, System.StringComparison.OrdinalIgnoreCase))
{ {
subElement.Attribute("commonness").Value = overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture); subElement.Attribute("commonness").Value = overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture);
elementFound = true; elementFound = true;
@@ -3,10 +3,68 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Voronoi2; using Voronoi2;
namespace Barotrauma namespace Barotrauma
{ {
class LevelWallVertexBuffer : IDisposable
{
public VertexBuffer WallEdgeBuffer, WallBuffer;
public readonly Texture2D WallTexture, EdgeTexture;
private VertexPositionColorTexture[] wallVertices;
private VertexPositionColorTexture[] wallEdgeVertices;
public bool IsDisposed
{
get;
private set;
}
public LevelWallVertexBuffer(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
{
this.wallVertices = LevelRenderer.GetColoredVertices(wallVertices, color);
WallBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, wallVertices.Length, BufferUsage.WriteOnly);
WallBuffer.SetData(this.wallVertices);
WallTexture = wallTexture;
this.wallEdgeVertices = LevelRenderer.GetColoredVertices(wallEdgeVertices, color);
WallEdgeBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, wallEdgeVertices.Length, BufferUsage.WriteOnly);
WallEdgeBuffer.SetData(this.wallEdgeVertices);
EdgeTexture = edgeTexture;
}
public void Append(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Color color)
{
WallBuffer.Dispose();
WallBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, this.wallVertices.Length + wallVertices.Length, BufferUsage.WriteOnly);
int originalWallVertexCount = this.wallVertices.Length;
Array.Resize(ref this.wallVertices, originalWallVertexCount + wallVertices.Length);
Array.Copy(LevelRenderer.GetColoredVertices(wallVertices, color), 0, this.wallVertices, originalWallVertexCount, wallVertices.Length);
WallBuffer.SetData(this.wallVertices);
WallEdgeBuffer.Dispose();
WallEdgeBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, this.wallEdgeVertices.Length + wallEdgeVertices.Length, BufferUsage.WriteOnly);
int originalWallEdgeVertexCount = this.wallEdgeVertices.Length;
Array.Resize(ref this.wallEdgeVertices, originalWallEdgeVertexCount + wallEdgeVertices.Length);
Array.Copy(LevelRenderer.GetColoredVertices(wallEdgeVertices, color), 0, this.wallEdgeVertices, originalWallEdgeVertexCount, wallEdgeVertices.Length);
WallEdgeBuffer.SetData(this.wallEdgeVertices);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
IsDisposed = true;
WallEdgeBuffer?.Dispose();
WallBuffer?.Dispose();
}
}
class LevelRenderer : IDisposable class LevelRenderer : IDisposable
{ {
private static BasicEffect wallEdgeEffect, wallCenterEffect; private static BasicEffect wallEdgeEffect, wallCenterEffect;
@@ -15,11 +73,11 @@ namespace Barotrauma
private Vector2 defaultDustVelocity; private Vector2 defaultDustVelocity;
private Vector2 dustVelocity; private Vector2 dustVelocity;
private RasterizerState cullNone; private readonly RasterizerState cullNone;
private Level level; private readonly Level level;
private VertexBuffer wallVertices, bodyVertices; private readonly List<LevelWallVertexBuffer> vertexBuffers = new List<LevelWallVertexBuffer>();
public LevelRenderer(Level level) public LevelRenderer(Level level)
{ {
@@ -68,6 +126,7 @@ namespace Barotrauma
Vector2 currentDustVel = defaultDustVelocity; Vector2 currentDustVel = defaultDustVelocity;
foreach (LevelObject levelObject in level.LevelObjectManager.GetVisibleObjects()) foreach (LevelObject levelObject in level.LevelObjectManager.GetVisibleObjects())
{ {
if (levelObject.Triggers == null) { continue; }
//use the largest water flow velocity of all the triggers //use the largest water flow velocity of all the triggers
Vector2 objectMaxFlow = Vector2.Zero; Vector2 objectMaxFlow = Vector2.Zero;
foreach (LevelTrigger trigger in levelObject.Triggers) foreach (LevelTrigger trigger in levelObject.Triggers)
@@ -94,7 +153,6 @@ namespace Barotrauma
while (dustOffset.Y <= -waterTextureSize.Y) dustOffset.Y += waterTextureSize.Y; while (dustOffset.Y <= -waterTextureSize.Y) dustOffset.Y += waterTextureSize.Y;
while (dustOffset.Y >= waterTextureSize.Y) dustOffset.Y -= waterTextureSize.Y; while (dustOffset.Y >= waterTextureSize.Y) dustOffset.Y -= waterTextureSize.Y;
} }
} }
public static VertexPositionColorTexture[] GetColoredVertices(VertexPositionTexture[] vertices, Color color) public static VertexPositionColorTexture[] GetColoredVertices(VertexPositionTexture[] vertices, Color color)
@@ -107,38 +165,27 @@ namespace Barotrauma
return verts; return verts;
} }
public void SetWallVertices(VertexPositionTexture[] vertices, Color color) public void SetVertices(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
{ {
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); var existingBuffer = vertexBuffers.Find(vb => vb.WallTexture == wallTexture && vb.EdgeTexture == edgeTexture);
wallVertices.SetData(GetColoredVertices(vertices, color)); if (existingBuffer != null)
{
existingBuffer.Append(wallVertices, wallEdgeVertices,color);
}
else
{
vertexBuffers.Add(new LevelWallVertexBuffer(wallVertices, wallEdgeVertices, wallTexture, edgeTexture, color));
}
} }
public void SetBodyVertices(VertexPositionTexture[] vertices, Color color) public void DrawBackground(SpriteBatch spriteBatch, Camera cam,
{ LevelObjectManager backgroundSpriteManager = null,
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
bodyVertices.SetData(GetColoredVertices(vertices, color));
}
public void SetWallVertices(VertexPositionColorTexture[] vertices)
{
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length,BufferUsage.WriteOnly);
wallVertices.SetData(vertices);
}
public void SetBodyVertices(VertexPositionColorTexture[] vertices)
{
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
bodyVertices.SetData(vertices);
}
public void DrawBackground(SpriteBatch spriteBatch, Camera cam,
LevelObjectManager backgroundSpriteManager = null,
BackgroundCreatureManager backgroundCreatureManager = null) BackgroundCreatureManager backgroundCreatureManager = null)
{ {
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap);
Vector2 backgroundPos = cam.WorldViewCenter; Vector2 backgroundPos = cam.WorldViewCenter;
backgroundPos.Y = -backgroundPos.Y; backgroundPos.Y = -backgroundPos.Y;
backgroundPos *= 0.05f; backgroundPos *= 0.05f;
@@ -173,10 +220,13 @@ namespace Barotrauma
SamplerState.LinearWrap, DepthStencilState.DepthRead, null, null, SamplerState.LinearWrap, DepthStencilState.DepthRead, null, null,
cam.Transform); cam.Transform);
if (backgroundSpriteManager != null) backgroundSpriteManager.DrawObjects(spriteBatch, cam, drawFront: false); backgroundSpriteManager?.DrawObjects(spriteBatch, cam, drawFront: false);
if (backgroundCreatureManager != null) backgroundCreatureManager.Draw(spriteBatch, cam); if (cam.Zoom > 0.05f)
{
backgroundCreatureManager?.Draw(spriteBatch, cam);
}
if (level.GenerationParams.WaterParticles != null) if (level.GenerationParams.WaterParticles != null && cam.Zoom > 0.05f)
{ {
float textureScale = level.GenerationParams.WaterParticleScale; float textureScale = level.GenerationParams.WaterParticleScale;
@@ -216,7 +266,7 @@ namespace Barotrauma
spriteBatch.End(); spriteBatch.End();
RenderWalls(GameMain.Instance.GraphicsDevice, cam, specular: false); RenderWalls(GameMain.Instance.GraphicsDevice, cam);
spriteBatch.Begin(SpriteSortMode.Deferred, spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.NonPremultiplied, BlendState.NonPremultiplied,
@@ -243,7 +293,8 @@ namespace Barotrauma
foreach (GraphEdge edge in cell.Edges) foreach (GraphEdge edge in cell.Edges)
{ {
GUI.DrawLine(spriteBatch, new Vector2(edge.Point1.X + cell.Translation.X, -(edge.Point1.Y + cell.Translation.Y)), GUI.DrawLine(spriteBatch, new Vector2(edge.Point1.X + cell.Translation.X, -(edge.Point1.Y + cell.Translation.Y)),
new Vector2(edge.Point2.X + cell.Translation.X, -(edge.Point2.Y + cell.Translation.Y)), cell.Body == null ? Color.Cyan * 0.5f : Color.White); new Vector2(edge.Point2.X + cell.Translation.X, -(edge.Point2.Y + cell.Translation.Y)), edge.NextToCave ? Color.Red : (cell.Body == null ? Color.Cyan * 0.5f : (edge.IsSolid ? Color.White : Color.Gray)),
width: edge.NextToCave ? 8 :1);
} }
foreach (Vector2 point in cell.BodyVertices) foreach (Vector2 point in cell.BodyVertices)
@@ -252,7 +303,7 @@ namespace Barotrauma
} }
} }
foreach (List<Point> nodeList in level.SmallTunnels) /*foreach (List<Point> nodeList in level.SmallTunnels)
{ {
for (int i = 1; i < nodeList.Count; i++) for (int i = 1; i < nodeList.Count; i++)
{ {
@@ -261,7 +312,7 @@ namespace Barotrauma
new Vector2(nodeList[i].X, -nodeList[i].Y), new Vector2(nodeList[i].X, -nodeList[i].Y),
Color.Lerp(Color.Yellow, GUI.Style.Red, i / (float)nodeList.Count), 0, 10); Color.Lerp(Color.Yellow, GUI.Style.Red, i / (float)nodeList.Count), 0, 10);
} }
} }*/
foreach (var ruin in level.Ruins) foreach (var ruin in level.Ruins)
{ {
@@ -270,108 +321,142 @@ namespace Barotrauma
} }
Vector2 pos = new Vector2(0.0f, -level.Size.Y); Vector2 pos = new Vector2(0.0f, -level.Size.Y);
if (cam.WorldView.Y >= -pos.Y - 1024) if (cam.WorldView.Y >= -pos.Y - 1024)
{ {
pos.X = cam.WorldView.X -1024; int topBarrierWidth = level.GenerationParams.WallEdgeSprite.Texture.Width;
int width = (int)(Math.Ceiling(cam.WorldView.Width / 1024 + 4.0f) * 1024); int topBarrierHeight = level.GenerationParams.WallEdgeSprite.Texture.Height;
GUI.DrawRectangle(spriteBatch,new Rectangle( pos.X = cam.WorldView.X - topBarrierWidth;
(int)(MathUtils.Round(pos.X, 1024)), int width = (int)(Math.Ceiling(cam.WorldView.Width / 1024 + 4.0f) * topBarrierWidth);
-cam.WorldView.Y,
width, GUI.DrawRectangle(spriteBatch, new Rectangle(
(int)(cam.WorldView.Y + pos.Y) - 30), (int)MathUtils.Round(pos.X, topBarrierWidth),
-cam.WorldView.Y,
width,
(int)(cam.WorldView.Y + pos.Y) - 60),
Color.Black, true); Color.Black, true);
spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture, spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture,
new Rectangle((int)(MathUtils.Round(pos.X, 1024)), (int)pos.Y-1000, width, 1024), new Rectangle((int)MathUtils.Round(pos.X, topBarrierWidth), (int)(pos.Y - topBarrierHeight + level.GenerationParams.WallEdgeExpandOutwardsAmount), width, topBarrierHeight),
new Rectangle(0, 0, width, -1024), new Rectangle(0, 0, width, -topBarrierHeight),
level.BackgroundTextureColor, 0.0f, GameMain.LightManager?.LightingEnabled ?? false ? GameMain.LightManager.AmbientLight : level.WallColor, 0.0f,
Vector2.Zero, Vector2.Zero,
SpriteEffects.None, 0.0f); SpriteEffects.None, 0.0f);
} }
if (cam.WorldView.Y - cam.WorldView.Height < level.SeaFloorTopPos + 1024) if (cam.WorldView.Y - cam.WorldView.Height < level.SeaFloorTopPos + 1024)
{ {
pos = new Vector2(cam.WorldView.X - 1024, -level.BottomPos); int bottomBarrierWidth = level.GenerationParams.WallEdgeSprite.Texture.Width;
int bottomBarrierHeight = level.GenerationParams.WallEdgeSprite.Texture.Height;
int width = (int)(Math.Ceiling(cam.WorldView.Width / 1024 + 4.0f) * 1024); pos = new Vector2(cam.WorldView.X - bottomBarrierWidth, -level.BottomPos);
int width = (int)(Math.Ceiling(cam.WorldView.Width / bottomBarrierWidth + 4.0f) * bottomBarrierWidth);
GUI.DrawRectangle(spriteBatch, new Rectangle( GUI.DrawRectangle(spriteBatch, new Rectangle(
(int)(MathUtils.Round(pos.X, 1024)), (int)(MathUtils.Round(pos.X, bottomBarrierWidth)),
(int)-(level.BottomPos - 30), -(level.BottomPos - 60),
width, width,
(int)(level.BottomPos - (cam.WorldView.Y - cam.WorldView.Height))), level.BottomPos - (cam.WorldView.Y - cam.WorldView.Height)),
Color.Black, true); Color.Black, true);
spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture, spriteBatch.Draw(level.GenerationParams.WallEdgeSprite.Texture,
new Rectangle((int)(MathUtils.Round(pos.X, 1024)), (int)-level.BottomPos, width, 1024), new Rectangle((int)MathUtils.Round(pos.X, bottomBarrierWidth), -level.BottomPos - (int)level.GenerationParams.WallEdgeExpandOutwardsAmount, width, bottomBarrierHeight),
new Rectangle(0, 0, width, -1024), new Rectangle(0, 0, width, -bottomBarrierHeight),
level.BackgroundTextureColor, 0.0f, GameMain.LightManager?.LightingEnabled ?? false ? GameMain.LightManager.AmbientLight : level.WallColor, 0.0f,
Vector2.Zero, Vector2.Zero,
SpriteEffects.FlipVertically, 0.0f); SpriteEffects.FlipVertically, 0.0f);
} }
} }
public void RenderWalls(GraphicsDevice graphicsDevice, Camera cam, bool specular) public void RenderWalls(GraphicsDevice graphicsDevice, Camera cam)
{ {
if (wallVertices == null) return; if (!vertexBuffers.Any()) { return; }
bool renderLevel = cam.WorldView.Y >= 0.0f; var defaultRasterizerState = graphicsDevice.RasterizerState;
bool renderSeaFloor = cam.WorldView.Y - cam.WorldView.Height < level.SeaFloorTopPos + 1024;
if (!renderLevel && !renderSeaFloor) return;
Matrix transformMatrix = cam.ShaderTransform Matrix transformMatrix = cam.ShaderTransform
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 100) * 0.5f; * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 100) * 0.5f;
wallEdgeEffect.Texture = specular && level.GenerationParams.WallEdgeSpriteSpecular != null ?
level.GenerationParams.WallEdgeSpriteSpecular.Texture :
level.GenerationParams.WallEdgeSprite.Texture;
wallEdgeEffect.World = transformMatrix;
wallCenterEffect.Texture = specular && level.GenerationParams.WallSpriteSpecular != null ?
level.GenerationParams.WallSpriteSpecular.Texture :
level.GenerationParams.WallSprite.Texture;
wallCenterEffect.World = transformMatrix;
graphicsDevice.SamplerStates[0] = SamplerState.LinearWrap; graphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
if (renderLevel)
{
graphicsDevice.SetVertexBuffer(bodyVertices);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(bodyVertices.VertexCount / 3.0f));
}
foreach (LevelWall wall in level.ExtraWalls)
{
if (!renderSeaFloor && wall == level.SeaFloor) continue;
wallCenterEffect.World = wall.GetTransform() * transformMatrix;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.BodyVertices);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.BodyVertices.VertexCount / 3.0f));
}
var defaultRasterizerState = graphicsDevice.RasterizerState;
graphicsDevice.RasterizerState = cullNone; graphicsDevice.RasterizerState = cullNone;
wallEdgeEffect.World = transformMatrix;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
if (renderLevel) //render destructible walls
for (int i = 0; i < 2; i++)
{ {
wallEdgeEffect.CurrentTechnique.Passes[0].Apply(); var wallList = i == 0 ? level.ExtraWalls : level.UnsyncedExtraWalls;
graphicsDevice.SetVertexBuffer(wallVertices); foreach (LevelWall wall in wallList)
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wallVertices.VertexCount / 3.0f)); {
if (!(wall is DestructibleLevelWall destructibleWall) || destructibleWall.Destroyed) { continue; }
wallCenterEffect.Texture = level.GenerationParams.DestructibleWallSprite?.Texture ?? level.GenerationParams.WallSprite.Texture;
wallCenterEffect.World = wall.GetTransform() * transformMatrix;
wallCenterEffect.Alpha = wall.Alpha;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallBuffer.VertexCount / 3.0f));
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));
if (destructibleWall.Damage <= 0.0f) { continue; }
wallEdgeEffect.Texture = level.GenerationParams.WallSpriteDestroyed.Texture;
wallEdgeEffect.Alpha = MathHelper.Lerp(0.2f, 1.0f, destructibleWall.Damage / destructibleWall.MaxHealth) * wall.Alpha;
wallEdgeEffect.World = wall.GetTransform() * transformMatrix;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallEdgeBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallEdgeBuffer.VertexCount / 3.0f));
}
} }
foreach (LevelWall wall in level.ExtraWalls)
wallEdgeEffect.Alpha = 1.0f;
wallCenterEffect.Alpha = 1.0f;
wallCenterEffect.World = transformMatrix;
wallEdgeEffect.World = transformMatrix;
//render static walls
foreach (var vertexBuffer in vertexBuffers)
{ {
if (!renderSeaFloor && wall == level.SeaFloor) continue; wallCenterEffect.Texture = vertexBuffer.WallTexture;
wallEdgeEffect.World = wall.GetTransform() * transformMatrix; wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(vertexBuffer.WallBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(vertexBuffer.WallBuffer.VertexCount / 3.0f));
wallEdgeEffect.Texture = vertexBuffer.EdgeTexture;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply(); wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallVertices); graphicsDevice.SetVertexBuffer(vertexBuffer.WallEdgeBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallVertices.VertexCount / 3.0f)); graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(vertexBuffer.WallEdgeBuffer.VertexCount / 3.0f));
} }
wallCenterEffect.Texture = level.GenerationParams.WallSprite.Texture;
wallEdgeEffect.Texture = level.GenerationParams.WallEdgeSprite.Texture;
//render non-destructible extra walls
for (int i = 0; i < 2; i++)
{
var wallList = i == 0 ? level.ExtraWalls : level.UnsyncedExtraWalls;
foreach (LevelWall wall in wallList)
{
if (wall is DestructibleLevelWall) { continue; }
//TODO: use LevelWallVertexBuffers for extra walls as well
wallCenterEffect.World = wall.GetTransform() * transformMatrix;
wallCenterEffect.Alpha = wall.Alpha;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallBuffer.VertexCount / 3.0f));
wallEdgeEffect.World = wall.GetTransform() * transformMatrix;
wallEdgeEffect.Alpha = wall.Alpha;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallEdgeBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallEdgeBuffer.VertexCount / 3.0f));
}
}
graphicsDevice.RasterizerState = defaultRasterizerState; graphicsDevice.RasterizerState = defaultRasterizerState;
} }
@@ -383,8 +468,11 @@ namespace Barotrauma
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (wallVertices != null) wallVertices.Dispose(); foreach (var vertexBuffer in vertexBuffers)
if (bodyVertices != null) bodyVertices.Dispose(); {
vertexBuffer.Dispose();
}
vertexBuffers.Clear();
} }
} }
} }
@@ -2,55 +2,44 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
{ {
partial class LevelWall : IDisposable partial class LevelWall : IDisposable
{ {
private VertexBuffer wallVertices, bodyVertices; public LevelWallVertexBuffer VertexBuffer { get; private set; }
public VertexBuffer WallVertices public VertexBuffer WallBuffer { get { return VertexBuffer.WallBuffer; } }
{
get { return wallVertices; }
}
public VertexBuffer BodyVertices public VertexBuffer WallEdgeBuffer { get { return VertexBuffer.WallEdgeBuffer; } }
{
get { return bodyVertices; } public virtual float Alpha => 1.0f;
}
public Matrix GetTransform() public Matrix GetTransform()
{ {
return body.BodyType == BodyType.Static ? return Body.FixedRotation ?
Matrix.Identity : Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(Body.Position), 0.0f)) :
Matrix.CreateRotationZ(body.Rotation) * Matrix.CreateRotationZ(Body.Rotation) *
Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(body.Position), 0.0f)); Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(Body.Position), 0.0f));
} }
public void SetWallVertices(VertexPositionTexture[] vertices, Color color) public void SetWallVertices(VertexPositionTexture[] wallVertices, VertexPositionTexture[] wallEdgeVertices, Texture2D wallTexture, Texture2D edgeTexture, Color color)
{ {
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); if (VertexBuffer != null && !VertexBuffer.IsDisposed) { VertexBuffer.Dispose(); }
wallVertices.SetData(LevelRenderer.GetColoredVertices(vertices, color)); VertexBuffer = new LevelWallVertexBuffer(wallVertices, wallEdgeVertices, wallTexture, edgeTexture, color);
} }
public void SetBodyVertices(VertexPositionTexture[] vertices, Color color) public void GenerateVertices()
{ {
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); float zCoord = this is DestructibleLevelWall ? Rand.Range(0.9f, 1.0f) : 0.9f;
bodyVertices.SetData(LevelRenderer.GetColoredVertices(vertices, color)); List<VertexPositionTexture> wallVertices = CaveGenerator.GenerateWallVertices(triangles, level.GenerationParams, zCoord);
} SetWallVertices(
wallVertices.ToArray(),
public void SetWallVertices(VertexPositionColorTexture[] vertices) CaveGenerator.GenerateWallEdgeVertices(Cells, level, zCoord).ToArray(),
{ level.GenerationParams.WallSprite.Texture,
if (wallVertices != null && !wallVertices.IsDisposed) wallVertices.Dispose(); level.GenerationParams.WallEdgeSprite.Texture,
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); color);
wallVertices.SetData(vertices);
}
public void SetBodyVertices(VertexPositionColorTexture[] vertices)
{
if (bodyVertices != null && !bodyVertices.IsDisposed) bodyVertices.Dispose();
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
bodyVertices.SetData(vertices);
} }
} }
} }
@@ -151,7 +151,16 @@ namespace Barotrauma.Lights
private readonly List<LightSource> activeLights = new List<LightSource>(capacity: 100); private readonly List<LightSource> activeLights = new List<LightSource>(capacity: 100);
public void UpdateLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null) public void Update(float deltaTime)
{
foreach (LightSource light in lights)
{
if (!light.Enabled) { continue; }
light.Update(deltaTime);
}
}
public void RenderLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null)
{ {
if (!LightingEnabled) { return; } if (!LightingEnabled) { return; }
@@ -174,7 +183,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in lights) foreach (LightSource light in lights)
{ {
if (!light.Enabled) { continue; } if (!light.Enabled) { continue; }
if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; } if ((light.Color.A < 1 || light.Range < 1.0f || light.CurrentBrightness <= 0.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; }
if (light.ParentBody != null) if (light.ParentBody != null)
{ {
light.Position = light.ParentBody.DrawPosition; light.Position = light.ParentBody.DrawPosition;
@@ -205,7 +214,7 @@ namespace Barotrauma.Lights
{ {
if (light.IsBackground) { continue; } if (light.IsBackground) { continue; }
//draw limb lights at this point, because they were skipped over previously to prevent them from being obstructed //draw limb lights at this point, because they were skipped over previously to prevent them from being obstructed
if (light.ParentBody?.UserData is Limb) { light.DrawSprite(spriteBatch, cam); } if (light.ParentBody?.UserData is Limb limb && !limb.Hide) { light.DrawSprite(spriteBatch, cam); }
} }
spriteBatch.End(); spriteBatch.End();
@@ -215,6 +224,7 @@ namespace Barotrauma.Lights
graphics.Clear(AmbientLight); graphics.Clear(AmbientLight);
graphics.BlendState = BlendState.Additive; graphics.BlendState = BlendState.Additive;
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
Level.Loaded?.BackgroundCreatureManager?.DrawLights(spriteBatch, cam);
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
if (!light.IsBackground) { continue; } if (!light.IsBackground) { continue; }
@@ -7,7 +7,6 @@ using System.Linq;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
namespace Barotrauma.Lights namespace Barotrauma.Lights
{ {
class LightSourceParams : ISerializableEntity class LightSourceParams : ISerializableEntity
@@ -33,7 +32,7 @@ namespace Barotrauma.Lights
get { return range; } get { return range; }
set set
{ {
range = MathHelper.Clamp(value, 0.0f, 2048.0f); range = MathHelper.Clamp(value, 0.0f, 4096.0f);
TextureRange = range; TextureRange = range;
if (OverrideLightTexture != null) if (OverrideLightTexture != null)
{ {
@@ -49,7 +48,63 @@ namespace Barotrauma.Lights
[Serialize("0, 0", true), Editable(ValueStep = 1, DecimalCount = 1, MinValueFloat = -1000f, MaxValueFloat = 1000f)] [Serialize("0, 0", true), Editable(ValueStep = 1, DecimalCount = 1, MinValueFloat = -1000f, MaxValueFloat = 1000f)]
public Vector2 Offset { get; set; } public Vector2 Offset { get; set; }
[Serialize(0f, true), Editable(MinValueFloat = -360, MaxValueFloat = 360, ValueStep = 1, DecimalCount = 0)]
public float Rotation { get; set; }
public Vector2 GetOffset() => Vector2.Transform(Offset, Matrix.CreateRotationZ(Rotation));
private float flicker;
[Editable, Serialize(0.0f, false, description: "How heavily the light flickers. 0 = no flickering, 1 = the light will alternate between completely dark and full brightness.")]
public float Flicker
{
get { return flicker; }
set
{
flicker = MathHelper.Clamp(value, 0.0f, 1.0f);
}
}
[Editable, Serialize(1.0f, false, description: "How fast the light flickers.")]
public float FlickerSpeed
{
get;
set;
}
private float pulseFrequency;
[Editable, Serialize(0.0f, true, description: "How rapidly the light pulsates (in Hz). 0 = no blinking.")]
public float PulseFrequency
{
get { return pulseFrequency; }
set
{
pulseFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
}
}
private float pulseAmount;
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.0f, true, description: "How much light pulsates (in Hz). 0 = not at all, 1 = alternates between full brightness and off.")]
public float PulseAmount
{
get { return pulseAmount; }
set
{
pulseAmount = MathHelper.Clamp(value, 0.0f, 1.0f);
}
}
private float blinkFrequency;
[Editable, Serialize(0.0f, true, description: "How rapidly the light blinks on and off (in Hz). 0 = no blinking.")]
public float BlinkFrequency
{
get { return blinkFrequency; }
set
{
blinkFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
}
}
public float TextureRange public float TextureRange
{ {
get; get;
@@ -88,6 +143,7 @@ namespace Barotrauma.Lights
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
case "sprite": case "sprite":
case "lightsprite":
{ {
LightSprite = new Sprite(subElement); LightSprite = new Sprite(subElement);
float spriteAlpha = subElement.GetAttributeFloat("alpha", -1.0f); float spriteAlpha = subElement.GetAttributeFloat("alpha", -1.0f);
@@ -144,6 +200,8 @@ namespace Barotrauma.Lights
private static Texture2D lightTexture; private static Texture2D lightTexture;
private float blinkTimer, flickerState, pulseState;
private VertexPositionColorTexture[] vertices; private VertexPositionColorTexture[] vertices;
private short[] indices; private short[] indices;
@@ -296,6 +354,12 @@ namespace Barotrauma.Lights
get { return lightSourceParams.Color; } get { return lightSourceParams.Color; }
set { lightSourceParams.Color = value; } set { lightSourceParams.Color = value; }
} }
public float CurrentBrightness
{
get;
private set;
}
public float Range public float Range
{ {
@@ -402,7 +466,41 @@ namespace Barotrauma.Lights
texture = LightTexture; texture = LightTexture;
diffToSub = new Dictionary<Submarine, Vector2>(); diffToSub = new Dictionary<Submarine, Vector2>();
if (addLight) { GameMain.LightManager.AddLight(this); } if (addLight) { GameMain.LightManager.AddLight(this); }
}
public void Update(float deltaTime)
{
if (lightSourceParams.BlinkFrequency > 0.0f)
{
blinkTimer = (blinkTimer + deltaTime * lightSourceParams.BlinkFrequency) % 1.0f;
}
if (lightSourceParams.PulseFrequency > 0.0f)
{
pulseState = (pulseState + deltaTime * lightSourceParams.PulseFrequency) % 1.0f;
}
if (blinkTimer > 0.5f)
{
CurrentBrightness = 0.0f;
}
else
{
float flicker = 0.0f;
float pulse = 0.0f;
if (lightSourceParams.Flicker > 0.0f)
{
flickerState += deltaTime * lightSourceParams.FlickerSpeed;
flickerState %= 255;
flicker = PerlinNoise.GetPerlin(flickerState, flickerState * 0.5f) * lightSourceParams.Flicker;
}
if (lightSourceParams.PulseFrequency > 0.0f && lightSourceParams.PulseAmount > 0.0f)
{
//oscillate between 0-1
pulse = (float)(Math.Sin(pulseState * MathHelper.TwoPi) + 1.0f) / 2.0f * lightSourceParams.PulseAmount;
}
CurrentBrightness = (1.0f - flicker) * (1.0f - pulse);
}
} }
/// <summary> /// <summary>
@@ -508,13 +606,12 @@ namespace Barotrauma.Lights
//recalculate vertices if the subs have moved > 5 px relative to each other //recalculate vertices if the subs have moved > 5 px relative to each other
Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition; Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition;
Vector2 prevDiff; if (!diffToSub.TryGetValue(sub, out Vector2 prevDiff))
if (!diffToSub.TryGetValue(sub, out prevDiff))
{ {
diffToSub.Add(sub, diff); diffToSub.Add(sub, diff);
NeedsRecalculation = true; NeedsRecalculation = true;
} }
else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f*5.0f) else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f * 5.0f)
{ {
diffToSub[sub] = diff; diffToSub[sub] = diff;
NeedsRecalculation = true; NeedsRecalculation = true;
@@ -884,7 +981,12 @@ namespace Barotrauma.Lights
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height); overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTextureOrigin; Vector2 origin = OverrideLightTextureOrigin;
if (LightSpriteEffect == SpriteEffects.FlipHorizontally) { origin.X = OverrideLightTexture.SourceRect.Width - origin.X; } if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
{
origin.X = OverrideLightTexture.SourceRect.Width - origin.X;
cosAngle = -cosAngle;
sinAngle = -sinAngle;
}
if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; } if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; }
uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f); uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
} }
@@ -1021,7 +1123,7 @@ namespace Barotrauma.Lights
lightVolumeBuffer.SetData<VertexPositionColorTexture>(vertices, 0, vertexCount); lightVolumeBuffer.SetData<VertexPositionColorTexture>(vertices, 0, vertexCount);
lightVolumeIndexBuffer.SetData<short>(indices, 0, indexCount); lightVolumeIndexBuffer.SetData<short>(indices, 0, indexCount);
Vector2 GetUV(Vector2 vert, SpriteEffects effects) static Vector2 GetUV(Vector2 vert, SpriteEffects effects)
{ {
if (effects == SpriteEffects.FlipHorizontally) if (effects == SpriteEffects.FlipHorizontally)
{ {
@@ -1098,7 +1200,7 @@ namespace Barotrauma.Lights
if (DeformableLightSprite != null) if (DeformableLightSprite != null)
{ {
Vector2 origin = DeformableLightSprite.Origin + LightSourceParams.Offset; Vector2 origin = DeformableLightSprite.Origin + LightSourceParams.GetOffset();
Vector2 drawPos = position; Vector2 drawPos = position;
if (ParentSub != null) if (ParentSub != null)
{ {
@@ -1115,14 +1217,14 @@ namespace Barotrauma.Lights
DeformableLightSprite.Draw( DeformableLightSprite.Draw(
cam, new Vector3(drawPos, 0.0f), cam, new Vector3(drawPos, 0.0f),
origin, -Rotation, SpriteScale, origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale,
new Color(Color, lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f), new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness),
LightSpriteEffect == SpriteEffects.FlipVertically); LightSpriteEffect == SpriteEffects.FlipVertically);
} }
if (LightSprite != null) if (LightSprite != null)
{ {
Vector2 origin = LightSprite.Origin + LightSourceParams.Offset; Vector2 origin = LightSprite.Origin + LightSourceParams.GetOffset();
if ((LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) if ((LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally)
{ {
origin.X = LightSprite.SourceRect.Width - origin.X; origin.X = LightSprite.SourceRect.Width - origin.X;
@@ -1140,9 +1242,9 @@ namespace Barotrauma.Lights
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
LightSprite.Draw( LightSprite.Draw(
spriteBatch, drawPos, spriteBatch, drawPos,
new Color(Color, lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f), new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness),
origin, -Rotation, SpriteScale, LightSpriteEffect); origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale, LightSpriteEffect);
} }
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f) if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
@@ -1185,7 +1287,7 @@ namespace Barotrauma.Lights
public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform) public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform)
{ {
if (Range < 1.0f || Color.A < 1) { return; } if (Range < 1.0f || Color.A < 1 || CurrentBrightness <= 0.0f) { return; }
if (CastShadows) if (CastShadows)
{ {
@@ -1207,7 +1309,7 @@ namespace Barotrauma.Lights
if (ParentSub != null) drawPos += ParentSub.DrawPosition; if (ParentSub != null) drawPos += ParentSub.DrawPosition;
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
spriteBatch.Draw(currentTexture, drawPos, null, Color, -rotation, center, scale, SpriteEffects.None, 1); spriteBatch.Draw(currentTexture, drawPos, null, Color.Multiply(CurrentBrightness), -rotation, center, scale, SpriteEffects.None, 1);
return; return;
} }
@@ -1229,7 +1331,7 @@ namespace Barotrauma.Lights
if (vertexCount == 0) { return; } if (vertexCount == 0) { return; }
lightEffect.DiffuseColor = (new Vector3(Color.R, Color.G, Color.B) * (Color.A / 255.0f)) / 255.0f; lightEffect.DiffuseColor = (new Vector3(Color.R, Color.G, Color.B) * (Color.A / 255.0f * CurrentBrightness)) / 255.0f;
if (OverrideLightTexture != null) if (OverrideLightTexture != null)
{ {
lightEffect.Texture = OverrideLightTexture.Texture; lightEffect.Texture = OverrideLightTexture.Texture;
@@ -63,6 +63,8 @@ namespace Barotrauma
private Sprite[,] mapTiles; private Sprite[,] mapTiles;
private bool[,] tileDiscovered; private bool[,] tileDiscovered;
private Pair<Rectangle, string> connectionTooltip;
#if DEBUG #if DEBUG
private GUIComponent editor; private GUIComponent editor;
@@ -410,6 +412,8 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, GUICustomComponent mapContainer) public void Draw(SpriteBatch spriteBatch, GUICustomComponent mapContainer)
{ {
connectionTooltip = null;
Rectangle rect = mapContainer.Rect; Rectangle rect = mapContainer.Rect;
Vector2 viewSize = new Vector2(rect.Width / zoom, rect.Height / zoom); Vector2 viewSize = new Vector2(rect.Width / zoom, rect.Height / zoom);
@@ -639,6 +643,10 @@ namespace Barotrauma
{ {
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First); GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
} }
if (connectionTooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, connectionTooltip.Second, connectionTooltip.First);
}
spriteBatch.End(); spriteBatch.End();
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect; GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
@@ -689,12 +697,16 @@ namespace Barotrauma
int startIndex = connection.CrackSegments.Count > 2 ? 1 : 0; int startIndex = connection.CrackSegments.Count > 2 ? 1 : 0;
int endIndex = connection.CrackSegments.Count > 2 ? connection.CrackSegments.Count - 1 : connection.CrackSegments.Count; int endIndex = connection.CrackSegments.Count > 2 ? connection.CrackSegments.Count - 1 : connection.CrackSegments.Count;
Vector2? connectionStart = null;
Vector2? connectionEnd = null;
for (int i = startIndex; i < endIndex; i++) for (int i = startIndex; i < endIndex; i++)
{ {
var segment = connection.CrackSegments[i]; var segment = connection.CrackSegments[i];
Vector2 start = rectCenter + (segment[0] + viewOffset) * zoom; Vector2 start = rectCenter + (segment[0] + viewOffset) * zoom;
Vector2 end = rectCenter + (segment[1] + viewOffset) * zoom; if (!connectionStart.HasValue) { connectionStart = start; }
Vector2 end = rectCenter + (segment[1] + viewOffset) * zoom;
connectionEnd = end;
if (!viewArea.Contains(start) && !viewArea.Contains(end)) if (!viewArea.Contains(start) && !viewArea.Contains(end))
{ {
@@ -734,6 +746,40 @@ namespace Barotrauma
connectionSprite.SourceRect, connectionColor * a, MathUtils.VectorToAngle(end - start), connectionSprite.SourceRect, connectionColor * a, MathUtils.VectorToAngle(end - start),
new Vector2(0, connectionSprite.size.Y / 2), SpriteEffects.None, 0.01f); new Vector2(0, connectionSprite.size.Y / 2), SpriteEffects.None, 0.01f);
} }
if (connectionStart.HasValue && connectionEnd.HasValue)
{
GUIComponentStyle crushDepthWarningIconStyle = null;
string tooltip = null;
if (connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio > connection.LevelData.RealWorldCrushDepth)
{
crushDepthWarningIconStyle = GUI.Style.GetComponentStyle("CrushDepthWarningHighIcon");
tooltip = "crushdepthwarninghigh";
}
else if ((connection.LevelData.InitialDepth + connection.LevelData.Size.Y) * Physics.DisplayToRealWorldRatio > connection.LevelData.RealWorldCrushDepth)
{
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)(Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth)).ToString()));
}
}
}
if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames) if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames)
{ {
@@ -329,8 +329,8 @@ namespace Barotrauma
{ {
var clones = Clone(selectedList).Where(c => c != null).ToList(); var clones = Clone(selectedList).Where(c => c != null).ToList();
selectedList = clones; selectedList = clones;
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
selectedList.ForEach(c => c.Move(moveAmount)); selectedList.ForEach(c => c.Move(moveAmount));
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
} }
else // move else // move
{ {
@@ -897,10 +897,10 @@ namespace Barotrauma
{ {
if (entities.Count == 0) { return; } if (entities.Count == 0) { return; }
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
CopyEntities(entities); CopyEntities(entities);
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
entities.ForEach(e => { if (!e.Removed) { e.Remove(); } }); entities.ForEach(e => { if (!e.Removed) { e.Remove(); } });
entities.Clear(); entities.Clear();
} }
@@ -913,7 +913,6 @@ namespace Barotrauma
Clone(copiedList); Clone(copiedList);
var clones = mapEntityList.Except(prevEntities).ToList(); var clones = mapEntityList.Except(prevEntities).ToList();
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
var nonWireClones = clones.Where(c => !(c is Item item) || item.GetComponent<Wire>() == null); var nonWireClones = clones.Where(c => !(c is Item item) || item.GetComponent<Wire>() == null);
if (!nonWireClones.Any()) { nonWireClones = clones; } if (!nonWireClones.Any()) { nonWireClones = clones; }
@@ -929,6 +928,8 @@ namespace Barotrauma
clone.Move(moveAmount); clone.Move(moveAmount);
clone.Submarine = Submarine.MainSub; clone.Submarine = Submarine.MainSub;
} }
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false, handleInventoryBehavior: false));
} }
/// <summary> /// <summary>
@@ -238,6 +238,7 @@ namespace Barotrauma
else if (HiddenInGame) { return; } else if (HiddenInGame) { return; }
Color color = IsHighlighted ? GUI.Style.Orange : spriteColor; Color color = IsHighlighted ? GUI.Style.Orange : spriteColor;
if (IsSelected && editing) if (IsSelected && editing)
{ {
//color = Color.Lerp(color, Color.Gold, 0.5f); //color = Color.Lerp(color, Color.Gold, 0.5f);
@@ -253,6 +254,10 @@ namespace Barotrauma
thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom))); thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
} }
bool isWiringMode = editing && SubEditorScreen.IsWiringMode();
if (isWiringMode) { color *= 0.15f; }
Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition; Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
float depth = GetDrawDepth(); float depth = GetDrawDepth();
@@ -261,7 +266,7 @@ namespace Barotrauma
if (FlippedX) textureOffset.X = -textureOffset.X; if (FlippedX) textureOffset.X = -textureOffset.X;
if (FlippedY) textureOffset.Y = -textureOffset.Y; if (FlippedY) textureOffset.Y = -textureOffset.Y;
if (back && damageEffect == null) if (back && damageEffect == null && !isWiringMode)
{ {
if (Prefab.BackgroundSprite != null) if (Prefab.BackgroundSprite != null)
{ {
@@ -394,12 +394,10 @@ namespace Barotrauma
float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY); float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY);
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
horizontalLine,
new Vector2(topLeft.X - expandX, -bottomRight.Y + i * GridSize.Y), new Vector2(topLeft.X - expandX, -bottomRight.Y + i * GridSize.Y),
new Vector2(bottomRight.X + expandX, -bottomRight.Y + i * GridSize.Y), new Vector2(bottomRight.X + expandX, -bottomRight.Y + i * GridSize.Y),
Color.White * (1.0f - normalizedDistY) * alpha, depth: 0.6f, width: 3); Color.White * (1.0f - normalizedDistY) * alpha, depth: 0.6f, width: 3);
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
verticalLine,
new Vector2(topLeft.X + i * GridSize.X, -topLeft.Y + expandY), new Vector2(topLeft.X + i * GridSize.X, -topLeft.Y + expandY),
new Vector2(topLeft.X + i * GridSize.X, -bottomRight.Y - expandY), new Vector2(topLeft.X + i * GridSize.X, -bottomRight.Y - expandY),
Color.White * (1.0f - normalizedDistX) * alpha, depth: 0.6f, width: 3); Color.White * (1.0f - normalizedDistX) * alpha, depth: 0.6f, width: 3);
@@ -10,10 +10,7 @@ namespace Barotrauma
{ {
if (GameMain.Client == null) { return; } if (GameMain.Client == null) { return; }
Vector2 newVelocity = Body.LinearVelocity; Body.CorrectPosition(positionBuffer, out Vector2 newPosition, out Vector2 newVelocity, out _, out _);
Vector2 newPosition = Body.SimPosition;
Body.CorrectPosition(positionBuffer, out newPosition, out newVelocity, out _, out _);
Vector2 moveAmount = ConvertUnits.ToDisplayUnits(newPosition - Body.SimPosition); Vector2 moveAmount = ConvertUnits.ToDisplayUnits(newPosition - Body.SimPosition);
newVelocity = newVelocity.ClampLength(100.0f); newVelocity = newVelocity.ClampLength(100.0f);
if (!MathUtils.IsValid(newVelocity) || moveAmount.LengthSquared() < 0.0001f) if (!MathUtils.IsValid(newVelocity) || moveAmount.LengthSquared() < 0.0001f)
@@ -24,12 +21,12 @@ namespace Barotrauma
List<Submarine> subsToMove = submarine.GetConnectedSubs(); List<Submarine> subsToMove = submarine.GetConnectedSubs();
foreach (Submarine dockedSub in subsToMove) foreach (Submarine dockedSub in subsToMove)
{ {
if (dockedSub == submarine) continue; if (dockedSub == submarine) { continue; }
//clear the position buffer of the docked subs to prevent unnecessary position corrections //clear the position buffer of the docked subs to prevent unnecessary position corrections
dockedSub.SubBody.positionBuffer.Clear(); dockedSub.SubBody.positionBuffer.Clear();
} }
Submarine closestSub = null; Submarine closestSub;
if (Character.Controlled == null) if (Character.Controlled == null)
{ {
closestSub = Submarine.FindClosest(GameMain.GameScreen.Cam.Position); closestSub = Submarine.FindClosest(GameMain.GameScreen.Cam.Position);
@@ -63,5 +60,33 @@ namespace Barotrauma
if (Character.Controlled != null) Character.Controlled.CursorPosition += moveAmount; if (Character.Controlled != null) Character.Controlled.CursorPosition += moveAmount;
} }
} }
private void PlayDamageSounds(Dictionary<Structure, float> damagedStructures, Vector2 impactSimPos, float impact, string soundTag)
{
if (impact < MinCollisionImpact) { return; }
//play a damage sound for the structure that took the most damage
float maxDamage = 0.0f;
Structure maxDamageStructure = null;
foreach (KeyValuePair<Structure, float> structureDamage in damagedStructures)
{
if (maxDamageStructure == null || structureDamage.Value > maxDamage)
{
maxDamage = structureDamage.Value;
maxDamageStructure = structureDamage.Key;
}
}
if (maxDamageStructure != null)
{
SoundPlayer.PlayDamageSound(
soundTag,
impact * 10.0f,
ConvertUnits.ToDisplayUnits(impactSimPos),
MathHelper.Lerp(2000.0f, 10000.0f, (impact - MinCollisionImpact) / 2.0f),
maxDamageStructure.Tags);
}
}
} }
} }
@@ -50,6 +50,8 @@ namespace Barotrauma.Networking
Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16()); Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16());
int optionIndex = msg.ReadByte(); int optionIndex = msg.ReadByte();
OrderTarget orderTargetPosition = null; OrderTarget orderTargetPosition = null;
Order.OrderTargetType orderTargetType = (Order.OrderTargetType)msg.ReadByte();
int wallSectionIndex = 0;
if (msg.ReadBoolean()) if (msg.ReadBoolean())
{ {
var x = msg.ReadSingle(); var x = msg.ReadSingle();
@@ -57,6 +59,10 @@ namespace Barotrauma.Networking
var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull; var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull;
orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true); orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true);
} }
else if(orderTargetType == Order.OrderTargetType.WallSection)
{
wallSectionIndex = msg.ReadByte();
}
Order orderPrefab; Order orderPrefab;
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count) if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
@@ -78,17 +84,31 @@ namespace Barotrauma.Networking
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen) if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{ {
var order = orderTargetPosition == null ? Order order = null;
new Order(orderPrefab, targetEntity, orderPrefab.GetTargetItemComponent(targetEntity as Item), orderGiver: senderCharacter) : switch (orderTargetType)
new Order(orderPrefab, orderTargetPosition, orderGiver: senderCharacter);
if (order.TargetAllCharacters)
{ {
GameMain.GameSession?.CrewManager?.AddOrder(order, orderPrefab.FadeOutTime); case Order.OrderTargetType.Entity:
order = new Order(orderPrefab, targetEntity, orderPrefab.GetTargetItemComponent(targetEntity as Item), orderGiver: senderCharacter);
break;
case Order.OrderTargetType.Position:
order = new Order(orderPrefab, orderTargetPosition, orderGiver: senderCharacter);
break;
case Order.OrderTargetType.WallSection:
order = new Order(orderPrefab, targetEntity as Structure, wallSectionIndex, orderGiver: senderCharacter);
break;
} }
else if (targetCharacter != null)
if (order != null)
{ {
targetCharacter.SetOrder(order, orderOption, senderCharacter); if (order.TargetAllCharacters)
{
var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null;
GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
}
else if (targetCharacter != null)
{
targetCharacter.SetOrder(order, orderOption, senderCharacter);
}
} }
} }
@@ -1,11 +1,5 @@
using System; using System.Diagnostics;
using System.Collections.Generic;
using System.Diagnostics;
using Barotrauma.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -26,7 +20,15 @@ namespace Barotrauma.Networking
PrivateStart(); PrivateStart();
processInfo.Arguments += " -pipes " + writePipe.GetClientHandleAsString() + " " + readPipe.GetClientHandleAsString(); processInfo.Arguments += " -pipes " + writePipe.GetClientHandleAsString() + " " + readPipe.GetClientHandleAsString();
Process = Process.Start(processInfo); try
{
Process = Process.Start(processInfo);
}
catch
{
DebugConsole.ThrowError($"Failed to start ChildServerRelay Process. File: {processInfo.FileName}, arguments: {processInfo.Arguments}");
throw;
}
localHandlesDisposed = false; localHandlesDisposed = false;
} }
@@ -903,6 +903,9 @@ namespace Barotrauma.Networking
case ServerPacketHeader.CREW: case ServerPacketHeader.CREW:
campaign?.ClientReadCrew(inc); campaign?.ClientReadCrew(inc);
break; break;
case ServerPacketHeader.READY_CHECK:
ReadyCheck.ClientRead(inc);
break;
case ServerPacketHeader.FILE_TRANSFER: case ServerPacketHeader.FILE_TRANSFER:
fileReceiver.ReadMessage(inc); fileReceiver.ReadMessage(inc);
break; break;
@@ -976,7 +979,7 @@ namespace Barotrauma.Networking
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" + string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" +
" (client value #" + i + ": " + Level.Loaded.EqualityCheckValues[i] + " (client value #" + i + ": " + Level.Loaded.EqualityCheckValues[i] +
", server value #" + i + ": " + levelEqualityCheckValues[i].ToString("X") + ", server value #" + i + ": " + levelEqualityCheckValues[i].ToString("X") +
", level value count: " + levelEqualityCheckValues.Count.ToString("X") + ", level value count: " + levelEqualityCheckValues.Count +
", seed: " + Level.Loaded.Seed + ", seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" + ", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" +
", mirrored: " + Level.Loaded.Mirrored + ")."; ", mirrored: " + Level.Loaded.Mirrored + ").";
@@ -61,6 +61,7 @@ namespace Barotrauma
CreateLabeledNumberInput(parent, 0, 20, nameof(AllowedWireDisconnectionsPerMinute)); CreateLabeledNumberInput(parent, 0, 20, nameof(AllowedWireDisconnectionsPerMinute));
CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, nameof(WireDisconnectionKarmaDecrease)); CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, nameof(WireDisconnectionKarmaDecrease));
CreateLabeledSlider(parent, 0.0f, 30.0f, 1.0f, nameof(SpamFilterKarmaDecrease)); CreateLabeledSlider(parent, 0.0f, 30.0f, 1.0f, nameof(SpamFilterKarmaDecrease));
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(BallastFloraKarmaIncrease));
//hide these for now if a localized text is not available //hide these for now if a localized text is not available
if (TextManager.ContainsTag("Karma." + nameof(DangerousItemStealKarmaDecrease))) if (TextManager.ContainsTag("Karma." + nameof(DangerousItemStealKarmaDecrease)))
@@ -13,7 +13,8 @@ namespace Barotrauma.Networking
msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID); msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID);
msg.Write(TargetEntity is Entity ? (TargetEntity as Entity).ID : (UInt16)0); msg.Write(TargetEntity is Entity ? (TargetEntity as Entity).ID : (UInt16)0);
msg.Write((byte)Array.IndexOf(Order.Prefab.Options, OrderOption)); msg.Write((byte)Array.IndexOf(Order.Prefab.Options, OrderOption));
if (TargetEntity is OrderTarget orderTarget) msg.Write((byte)Order.TargetType);
if (Order.TargetType == Order.OrderTargetType.Position && TargetEntity is OrderTarget orderTarget)
{ {
msg.Write(true); msg.Write(true);
msg.Write(orderTarget.Position.X); msg.Write(orderTarget.Position.X);
@@ -23,6 +24,10 @@ namespace Barotrauma.Networking
else else
{ {
msg.Write(false); msg.Write(false);
if (Order.TargetType == Order.OrderTargetType.WallSection)
{
msg.Write((byte)(WallSectionIndex ?? Order.WallSectionIndex ?? 0));
}
} }
} }
} }
@@ -1,131 +0,0 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Barotrauma.Particles
{
class Decal
{
public readonly DecalPrefab Prefab;
private Vector2 position;
public readonly Sprite Sprite;
private float fadeTimer;
public float FadeTimer
{
get { return fadeTimer; }
set { fadeTimer = MathHelper.Clamp(value, 0.0f, LifeTime); }
}
public float FadeInTime
{
get { return Prefab.FadeInTime; }
}
public float FadeOutTime
{
get { return Prefab.FadeOutTime; }
}
public float LifeTime
{
get { return Prefab.LifeTime; }
}
public Color Color
{
get;
set;
}
public Vector2 WorldPosition
{
get
{
Vector2 worldPos = position
+ clippedSourceRect.Size.ToVector2() / 2 * scale
+ hull.Rect.Location.ToVector2();
if (hull.Submarine != null) { worldPos += hull.Submarine.DrawPosition; }
return worldPos;
}
}
private Hull hull;
private float scale;
private Rectangle clippedSourceRect;
public Decal(DecalPrefab prefab, float scale, Vector2 worldPosition, Hull hull)
{
Prefab = prefab;
this.hull = hull;
//transform to hull-relative coordinates so we don't have to worry about the hull moving
position = worldPosition - hull.WorldRect.Location.ToVector2();
Vector2 drawPos = position + hull.Rect.Location.ToVector2();
Sprite = prefab.Sprites[Rand.Range(0, prefab.Sprites.Count, Rand.RandSync.Unsynced)];
Color = prefab.Color;
Rectangle drawRect = new Rectangle(
(int)(drawPos.X - Sprite.size.X / 2 * scale),
(int)(drawPos.Y + Sprite.size.Y / 2 * scale),
(int)(Sprite.size.X * scale),
(int)(Sprite.size.Y * scale));
Rectangle overFlowAmount = new Rectangle(
(int)Math.Max(hull.Rect.X - drawRect.X, 0.0f),
(int)Math.Max(drawRect.Y - hull.Rect.Y, 0.0f),
(int)Math.Max(drawRect.Right - hull.Rect.Right, 0.0f),
(int)Math.Max((hull.Rect.Y - hull.Rect.Height) - (drawRect.Y - drawRect.Height), 0.0f));
clippedSourceRect = new Rectangle(
Sprite.SourceRect.X + (int)(overFlowAmount.X / scale),
Sprite.SourceRect.Y + (int)(overFlowAmount.Y / scale),
Sprite.SourceRect.Width - (int)((overFlowAmount.X + overFlowAmount.Width) / scale),
Sprite.SourceRect.Height - (int)((overFlowAmount.Y + overFlowAmount.Height) / scale));
position -= new Vector2(Sprite.size.X / 2 * scale - overFlowAmount.X, -Sprite.size.Y / 2 * scale + overFlowAmount.Y);
this.scale = scale;
}
public void Update(float deltaTime)
{
fadeTimer += deltaTime;
}
public void StopFadeIn()
{
Color *= GetAlpha();
fadeTimer = Prefab.FadeInTime;
}
public void Draw(SpriteBatch spriteBatch, Hull hull, float depth)
{
Vector2 drawPos = position + hull.Rect.Location.ToVector2();
if (hull.Submarine != null) { drawPos += hull.Submarine.DrawPosition; }
drawPos.Y = -drawPos.Y;
spriteBatch.Draw(Sprite.Texture, drawPos, clippedSourceRect, Color * GetAlpha(), 0, Vector2.Zero, scale, SpriteEffects.None, depth);
}
private float GetAlpha()
{
if (fadeTimer < Prefab.FadeInTime)
{
return fadeTimer / Prefab.FadeInTime;
}
else if (fadeTimer > Prefab.LifeTime - Prefab.FadeOutTime)
{
return (Prefab.LifeTime - fadeTimer) / Prefab.FadeOutTime;
}
return 1.0f;
}
}
}
@@ -1,75 +0,0 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
namespace Barotrauma.Particles
{
class DecalManager
{
public PrefabCollection<DecalPrefab> Prefabs { get; private set; }
public DecalManager()
{
Prefabs = new PrefabCollection<DecalPrefab>();
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.Decals))
{
LoadFromFile(configFile);
}
}
public void LoadFromFile(ContentFile configFile)
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { return; }
bool allowOverriding = false;
var mainElement = doc.Root;
if (doc.Root.IsOverride())
{
mainElement = doc.Root.FirstElement();
allowOverriding = true;
}
foreach (XElement sourceElement in mainElement.Elements())
{
var element = sourceElement.IsOverride() ? sourceElement.FirstElement() : sourceElement;
string name = element.Name.ToString().ToLowerInvariant();
if (Prefabs.ContainsKey(name))
{
if (allowOverriding || sourceElement.IsOverride())
{
DebugConsole.NewMessage($"Overriding the existing decal prefab '{name}' using the file '{configFile.Path}'", Color.Yellow);
}
else
{
DebugConsole.ThrowError($"Error in '{configFile.Path}': Duplicate decal prefab '{name}' found in '{configFile.Path}'! Each decal prefab must have a unique name. " +
"Use <override></override> tags to override prefabs.");
continue;
}
}
Prefabs.Add(new DecalPrefab(element, configFile), allowOverriding || sourceElement.IsOverride());
}
}
public void RemoveByFile(string filePath)
{
Prefabs.RemoveByFile(filePath);
}
public Decal CreateDecal(string decalName, float scale, Vector2 worldPosition, Hull hull)
{
if (!Prefabs.ContainsKey(decalName.ToLowerInvariant()))
{
DebugConsole.ThrowError("Decal prefab " + decalName + " not found!");
return null;
}
DecalPrefab prefab = Prefabs[decalName];
return new Decal(prefab, scale, worldPosition, hull);
}
}
}
@@ -1,73 +0,0 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Particles
{
class DecalPrefab : IPrefab, IDisposable
{
public readonly string Name;
public string OriginalName { get { return Name; } }
private string _identifier;
public string Identifier
{
get
{
if (_identifier == null)
{
_identifier = Name.ToLowerInvariant();
}
return _identifier;
}
}
public string FilePath { get; private set; }
public ContentPackage ContentPackage { get; private set; }
public void Dispose()
{
foreach (Sprite spr in Sprites)
{
spr.Remove();
}
Sprites.Clear();
}
public readonly List<Sprite> Sprites;
public readonly Color Color;
public readonly float LifeTime;
public readonly float FadeOutTime;
public readonly float FadeInTime;
public DecalPrefab(XElement element, ContentFile file)
{
Name = element.Name.ToString();
FilePath = file.Path;
ContentPackage = file.ContentPackage;
Sprites = new List<Sprite>();
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().Equals("sprite", StringComparison.OrdinalIgnoreCase))
{
Sprites.Add(new Sprite(subElement));
}
}
Color = new Color(element.GetAttributeVector4("color", Vector4.One));
LifeTime = element.GetAttributeFloat("lifetime", 10.0f);
FadeOutTime = Math.Min(LifeTime, element.GetAttributeFloat("fadeouttime", 1.0f));
FadeInTime = Math.Min(LifeTime - FadeOutTime, element.GetAttributeFloat("fadeintime", 0.0f));
}
}
}
@@ -483,7 +483,7 @@ namespace Barotrauma.Particles
if (prefab.GrowTime > 0.0f && totalLifeTime - lifeTime < prefab.GrowTime) if (prefab.GrowTime > 0.0f && totalLifeTime - lifeTime < prefab.GrowTime)
{ {
drawSize *= ((totalLifeTime - lifeTime) / prefab.GrowTime); drawSize *= MathUtils.SmoothStep((totalLifeTime - lifeTime) / prefab.GrowTime);
} }
Color currColor = new Color(color.ToVector4() * ColorMultiplier); Color currColor = new Color(color.ToVector4() * ColorMultiplier);
@@ -372,6 +372,9 @@ namespace Barotrauma.CharacterEditor
{ {
base.Update(deltaTime); base.Update(deltaTime);
if (Wizard.instance != null) { return; } if (Wizard.instance != null) { return; }
GameMain.LightManager?.Update((float)deltaTime);
spriteSheetRect = CalculateSpritesheetRectangle(); spriteSheetRect = CalculateSpritesheetRectangle();
// Handle shortcut keys // Handle shortcut keys
if (PlayerInput.KeyHit(Keys.F1)) if (PlayerInput.KeyHit(Keys.F1))
@@ -787,7 +790,7 @@ namespace Barotrauma.CharacterEditor
if (GameMain.LightManager.LightingEnabled) if (GameMain.LightManager.LightingEnabled)
{ {
GameMain.LightManager.ObstructVision = Character.Controlled.ObstructVision; GameMain.LightManager.ObstructVision = Character.Controlled.ObstructVision;
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam); GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam);
GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition); GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition);
} }
base.Draw(deltaTime, graphics, spriteBatch); base.Draw(deltaTime, graphics, spriteBatch);
@@ -2786,9 +2789,7 @@ namespace Barotrauma.CharacterEditor
}; };
// Spacing // Spacing
new GUIFrame(new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style: null) { CanBeFocused = false }; new GUIFrame(new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style: null) { CanBeFocused = false };
Vector2 messageBoxRelSize = new Vector2(0.5f, 0.5f); Vector2 messageBoxRelSize = new Vector2(0.5f, 0.7f);
int messageBoxWidth = GameMain.GraphicsWidth / 2;
int messageBoxHeight = GameMain.GraphicsHeight / 2;
var saveRagdollButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("SaveRagdoll")); var saveRagdollButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("SaveRagdoll"));
saveRagdollButton.OnClicked += (button, userData) => saveRagdollButton.OnClicked += (button, userData) =>
{ {
@@ -2908,12 +2909,12 @@ namespace Barotrauma.CharacterEditor
{ {
var box = new GUIMessageBox(GetCharacterEditorTranslation("SaveAnimation"), string.Empty, new string[] { TextManager.Get("Cancel"), TextManager.Get("Save") }, messageBoxRelSize); var box = new GUIMessageBox(GetCharacterEditorTranslation("SaveAnimation"), string.Empty, new string[] { TextManager.Get("Cancel"), TextManager.Get("Save") }, messageBoxRelSize);
var textArea = new GUIFrame(new RectTransform(new Vector2(1, 0.1f), box.Content.RectTransform) { MinSize = new Point(350, 30) }, style: null); var textArea = new GUIFrame(new RectTransform(new Vector2(1, 0.1f), box.Content.RectTransform) { MinSize = new Point(350, 30) }, style: null);
var inputLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), textArea.RectTransform) { MinSize = new Point(250, 30) }, $"{GetCharacterEditorTranslation("ProvideFileName")}: "); var inputLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), textArea.RectTransform, Anchor.CenterLeft) { MinSize = new Point(250, 30) }, $"{GetCharacterEditorTranslation("ProvideFileName")}: ");
var inputField = new GUITextBox(new RectTransform(new Vector2(0.5f, 1), textArea.RectTransform, Anchor.TopRight) { MinSize = new Point(100, 30) }, CurrentAnimation.Name); var inputField = new GUITextBox(new RectTransform(new Vector2(0.45f, 1), textArea.RectTransform, Anchor.CenterRight) { MinSize = new Point(100, 30) }, CurrentAnimation.Name);
// Type filtering // Type filtering
var typeSelectionArea = new GUIFrame(new RectTransform(new Vector2(1f, 0.1f), box.Content.RectTransform) { MinSize = new Point(0, 30) }, style: null); var typeSelectionArea = new GUIFrame(new RectTransform(new Vector2(1f, 0.1f), box.Content.RectTransform) { MinSize = new Point(0, 30) }, style: null);
var typeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), typeSelectionArea.RectTransform, Anchor.TopCenter, Pivot.TopRight), $"{GetCharacterEditorTranslation("SelectAnimationType")}: "); var typeLabel = new GUITextBlock(new RectTransform(new Vector2(0.45f, 1), typeSelectionArea.RectTransform, Anchor.CenterLeft), $"{GetCharacterEditorTranslation("SelectAnimationType")}: ");
var typeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1), typeSelectionArea.RectTransform, Anchor.TopCenter, Pivot.TopLeft), elementCount: 4); var typeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.45f, 1), typeSelectionArea.RectTransform, Anchor.CenterRight), elementCount: 4);
foreach (object enumValue in Enum.GetValues(typeof(AnimationType))) foreach (object enumValue in Enum.GetValues(typeof(AnimationType)))
{ {
if (!(enumValue is AnimationType.NotDefined)) if (!(enumValue is AnimationType.NotDefined))
@@ -2964,8 +2965,8 @@ namespace Barotrauma.CharacterEditor
deleteButton.Enabled = false; deleteButton.Enabled = false;
// Type filtering // Type filtering
var typeSelectionArea = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.1f), loadBox.Content.RectTransform) { MinSize = new Point(0, 30) }, style: null); var typeSelectionArea = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.1f), loadBox.Content.RectTransform) { MinSize = new Point(0, 30) }, style: null);
var typeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), typeSelectionArea.RectTransform, Anchor.TopCenter, Pivot.TopRight), $"{GetCharacterEditorTranslation("SelectAnimationType")}: "); var typeLabel = new GUITextBlock(new RectTransform(new Vector2(0.45f, 1), typeSelectionArea.RectTransform, Anchor.CenterLeft), $"{GetCharacterEditorTranslation("SelectAnimationType")}: ");
var typeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1), typeSelectionArea.RectTransform, Anchor.TopCenter, Pivot.TopLeft), elementCount: 4); var typeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.45f, 1), typeSelectionArea.RectTransform, Anchor.CenterRight), elementCount: 4);
foreach (object enumValue in Enum.GetValues(typeof(AnimationType))) foreach (object enumValue in Enum.GetValues(typeof(AnimationType)))
{ {
if (!(enumValue is AnimationType.NotDefined)) if (!(enumValue is AnimationType.NotDefined))
@@ -186,7 +186,7 @@ namespace Barotrauma
spriteBatch.End(); spriteBatch.End();
graphics.SetRenderTarget(null); graphics.SetRenderTarget(null);
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam, renderTarget); GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam, renderTarget);
//------------------------------------------------------------------------ //------------------------------------------------------------------------
graphics.SetRenderTarget(renderTargetBackground); graphics.SetRenderTarget(renderTargetBackground);
@@ -24,24 +24,26 @@ namespace Barotrauma
get { return cam; } get { return cam; }
} }
private GUIFrame leftPanel, rightPanel, bottomPanel, topPanel; private readonly GUIFrame leftPanel, rightPanel, bottomPanel, topPanel;
private LevelGenerationParams selectedParams; private LevelGenerationParams selectedParams;
private LevelObjectPrefab selectedLevelObject; private LevelObjectPrefab selectedLevelObject;
private GUIListBox paramsList, ruinParamsList, outpostParamsList, levelObjectList; private readonly GUIListBox paramsList, ruinParamsList, caveParamsList, outpostParamsList, levelObjectList;
private GUIListBox editorContainer; private readonly GUIListBox editorContainer;
private GUIButton spriteEditDoneButton; private readonly GUIButton spriteEditDoneButton;
private GUITextBox seedBox; private readonly GUITextBox seedBox;
private GUITickBox lightingEnabled, cursorLightEnabled; private readonly GUITickBox lightingEnabled, cursorLightEnabled;
private Sprite editingSprite; private Sprite editingSprite;
private LightSource pointerLightSource; private LightSource pointerLightSource;
private readonly Color[] tunnelDebugColors = new Color[] { Color.White, Color.Cyan, Color.LightGreen, Color.Red, Color.LightYellow, Color.LightSeaGreen };
public LevelEditorScreen() public LevelEditorScreen()
{ {
cam = new Camera() cam = new Camera()
@@ -78,8 +80,17 @@ namespace Barotrauma
return true; return true;
}; };
var caveTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedLeftPanel.RectTransform), TextManager.Get("leveleditor.caveparams"), font: GUI.SubHeadingFont);
caveParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.1f), paddedLeftPanel.RectTransform));
caveParamsList.OnSelected += (GUIComponent component, object obj) =>
{
CreateCaveParamsEditor(obj as CaveGenerationParams);
return true;
};
var outpostTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedLeftPanel.RectTransform), TextManager.Get("leveleditor.outpostparams"), font: GUI.SubHeadingFont); var outpostTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedLeftPanel.RectTransform), TextManager.Get("leveleditor.outpostparams"), font: GUI.SubHeadingFont);
GUITextBlock.AutoScaleAndNormalize(ruinTitle, outpostTitle); GUITextBlock.AutoScaleAndNormalize(ruinTitle, caveTitle, outpostTitle);
outpostParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform)); outpostParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform));
outpostParamsList.OnSelected += (GUIComponent component, object obj) => outpostParamsList.OnSelected += (GUIComponent component, object obj) =>
@@ -237,13 +248,17 @@ namespace Barotrauma
{ {
OnClicked = (btn, obj) => OnClicked = (btn, obj) =>
{ {
bool wasLevelLoaded = Level.Loaded != null;
Submarine.Unload(); Submarine.Unload();
GameMain.LightManager.ClearLights(); GameMain.LightManager.ClearLights();
LevelData levelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams); LevelData levelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
levelData.ForceOutpostGenerationParams = outpostParamsList.SelectedData as OutpostGenerationParams; levelData.ForceOutpostGenerationParams = outpostParamsList.SelectedData as OutpostGenerationParams;
Level.Generate(levelData, mirror: false); Level.Generate(levelData, mirror: false);
GameMain.LightManager.AddLight(pointerLightSource); GameMain.LightManager.AddLight(pointerLightSource);
cam.Position = new Vector2(Level.Loaded.Size.X / 2, Level.Loaded.Size.Y / 2); if (!wasLevelLoaded || cam.Position.X < 0 || cam.Position.Y < 0 || cam.Position.Y > Level.Loaded.Size.X || cam.Position.Y > Level.Loaded.Size.Y)
{
cam.Position = new Vector2(Level.Loaded.Size.X / 2, Level.Loaded.Size.Y / 2);
}
foreach (GUITextBlock param in paramsList.Content.Children) foreach (GUITextBlock param in paramsList.Content.Children)
{ {
param.TextColor = param.UserData == selectedParams ? GUI.Style.Green : param.Style.TextColor; param.TextColor = param.UserData == selectedParams ? GUI.Style.Green : param.Style.TextColor;
@@ -344,6 +359,7 @@ namespace Barotrauma
editingSprite = null; editingSprite = null;
UpdateParamsList(); UpdateParamsList();
UpdateRuinParamsList(); UpdateRuinParamsList();
UpdateCaveParamsList();
UpdateOutpostParamsList(); UpdateOutpostParamsList();
UpdateLevelObjectsList(); UpdateLevelObjectsList();
} }
@@ -371,6 +387,22 @@ namespace Barotrauma
} }
} }
private void UpdateCaveParamsList()
{
editorContainer.ClearChildren();
caveParamsList.Content.ClearChildren();
foreach (CaveGenerationParams genParams in CaveGenerationParams.CaveParams)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), caveParamsList.Content.RectTransform) { MinSize = new Point(0, 20) },
genParams.Name)
{
Padding = Vector4.Zero,
UserData = genParams
};
}
}
private void UpdateRuinParamsList() private void UpdateRuinParamsList()
{ {
editorContainer.ClearChildren(); editorContainer.ClearChildren();
@@ -425,6 +457,7 @@ namespace Barotrauma
text: ToolBox.LimitString(levelObjPrefab.Name, GUI.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUI.SmallFont) text: ToolBox.LimitString(levelObjPrefab.Name, GUI.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUI.SmallFont)
{ {
CanBeFocused = false, CanBeFocused = false,
ToolTip = levelObjPrefab.Name
}; };
Sprite sprite = levelObjPrefab.Sprites.FirstOrDefault() ?? levelObjPrefab.DeformableSprite?.Sprite; Sprite sprite = levelObjPrefab.Sprites.FirstOrDefault() ?? levelObjPrefab.DeformableSprite?.Sprite;
@@ -437,15 +470,14 @@ namespace Barotrauma
} }
} }
private void CreateLevelObjectEditor(LevelObjectPrefab levelObjectPrefab) private void CreateCaveParamsEditor(CaveGenerationParams caveGenerationParams)
{ {
editorContainer.ClearChildren(); editorContainer.ClearChildren();
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, caveGenerationParams, false, true, elementHeight: 20);
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, levelObjectPrefab, false, true, elementHeight: 20, titleFont: GUI.LargeFont);
if (selectedParams != null) if (selectedParams != null)
{ {
var commonnessContainer = new GUILayoutGroup(new RectTransform(new Point(editor.Rect.Width, 70)) { IsFixedSize = true }, var commonnessContainer = new GUILayoutGroup(new RectTransform(new Point(editor.Rect.Width, 70)) { IsFixedSize = true },
isHorizontal: false, childAnchor: Anchor.TopCenter) isHorizontal: false, childAnchor: Anchor.TopCenter)
{ {
AbsoluteSpacing = 5, AbsoluteSpacing = 5,
@@ -457,15 +489,60 @@ namespace Barotrauma
{ {
MinValueFloat = 0, MinValueFloat = 0,
MaxValueFloat = 100, MaxValueFloat = 100,
FloatValue = levelObjectPrefab.GetCommonness(selectedParams.Identifier), FloatValue = caveGenerationParams.GetCommonness(selectedParams),
OnValueChanged = (numberInput) => OnValueChanged = (numberInput) =>
{ {
levelObjectPrefab.OverrideCommonness[selectedParams.Identifier] = numberInput.FloatValue; caveGenerationParams.OverrideCommonness[selectedParams.Identifier] = numberInput.FloatValue;
} }
}; };
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), commonnessContainer.RectTransform), style: null); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), commonnessContainer.RectTransform), style: null);
editor.AddCustomContent(commonnessContainer, 1); editor.AddCustomContent(commonnessContainer, 1);
} }
}
private void CreateLevelObjectEditor(LevelObjectPrefab levelObjectPrefab)
{
editorContainer.ClearChildren();
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, levelObjectPrefab, false, true, elementHeight: 20, titleFont: GUI.LargeFont);
if (selectedParams != null)
{
List<string> availableIdentifiers = new List<string>();
{
if (selectedParams != null) { availableIdentifiers.Add(selectedParams.Identifier); }
}
foreach (var caveParam in CaveGenerationParams.CaveParams)
{
if (selectedParams != null && caveParam.GetCommonness(selectedParams) <= 0.0f) { continue; }
availableIdentifiers.Add(caveParam.Identifier);
}
availableIdentifiers.Reverse();
foreach (string paramsId in availableIdentifiers)
{
var commonnessContainer = new GUILayoutGroup(new RectTransform(new Point(editor.Rect.Width, 70)) { IsFixedSize = true },
isHorizontal: false, childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = 5,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), commonnessContainer.RectTransform),
TextManager.GetWithVariable("leveleditor.levelobjcommonness", "[leveltype]", paramsId), textAlignment: Alignment.Center);
new GUINumberInput(new RectTransform(new Vector2(0.5f, 0.4f), commonnessContainer.RectTransform), GUINumberInput.NumberType.Float)
{
MinValueFloat = 0,
MaxValueFloat = 100,
FloatValue = selectedParams.Identifier == paramsId ? levelObjectPrefab.GetCommonness(selectedParams) : levelObjectPrefab.GetCommonness(CaveGenerationParams.CaveParams.Find(p => p.Identifier == paramsId)),
OnValueChanged = (numberInput) =>
{
levelObjectPrefab.OverrideCommonness[paramsId] = numberInput.FloatValue;
}
};
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), commonnessContainer.RectTransform), style: null);
editor.AddCustomContent(commonnessContainer, 1);
}
}
Sprite sprite = levelObjectPrefab.Sprites.FirstOrDefault() ?? levelObjectPrefab.DeformableSprite?.Sprite; Sprite sprite = levelObjectPrefab.Sprites.FirstOrDefault() ?? levelObjectPrefab.DeformableSprite?.Sprite;
if (sprite != null) if (sprite != null)
@@ -508,7 +585,7 @@ namespace Barotrauma
foreach (LevelObjectPrefab objPrefab in LevelObjectPrefab.List) foreach (LevelObjectPrefab objPrefab in LevelObjectPrefab.List)
{ {
dropdown.AddItem(objPrefab.Name, objPrefab); dropdown.AddItem(objPrefab.Name, objPrefab);
if (childObj.AllowedNames.Contains(objPrefab.Name)) dropdown.SelectItem(objPrefab); if (childObj.AllowedNames.Contains(objPrefab.Name)) { dropdown.SelectItem(objPrefab); }
} }
dropdown.OnSelected = (selected, obj) => dropdown.OnSelected = (selected, obj) =>
{ {
@@ -590,7 +667,7 @@ namespace Barotrauma
foreach (GUIComponent levelObjFrame in levelObjectList.Content.Children) foreach (GUIComponent levelObjFrame in levelObjectList.Content.Children)
{ {
var levelObj = levelObjFrame.UserData as LevelObjectPrefab; var levelObj = levelObjFrame.UserData as LevelObjectPrefab;
float commonness = levelObj.GetCommonness(selectedParams.Identifier); float commonness = levelObj.GetCommonness(selectedParams);
levelObjFrame.Color = commonness > 0.0f ? GUI.Style.Green * 0.4f : Color.Transparent; levelObjFrame.Color = commonness > 0.0f ? GUI.Style.Green * 0.4f : Color.Transparent;
levelObjFrame.SelectedColor = commonness > 0.0f ? GUI.Style.Green * 0.6f : Color.White * 0.5f; levelObjFrame.SelectedColor = commonness > 0.0f ? GUI.Style.Green * 0.6f : Color.White * 0.5f;
levelObjFrame.HoverColor = commonness > 0.0f ? GUI.Style.Green * 0.7f : Color.White * 0.6f; levelObjFrame.HoverColor = commonness > 0.0f ? GUI.Style.Green * 0.7f : Color.White * 0.6f;
@@ -607,7 +684,7 @@ namespace Barotrauma
{ {
var levelObj1 = c1.GUIComponent.UserData as LevelObjectPrefab; var levelObj1 = c1.GUIComponent.UserData as LevelObjectPrefab;
var levelObj2 = c2.GUIComponent.UserData as LevelObjectPrefab; var levelObj2 = c2.GUIComponent.UserData as LevelObjectPrefab;
return Math.Sign(levelObj2.GetCommonness(selectedParams.Identifier) - levelObj1.GetCommonness(selectedParams.Identifier)); return Math.Sign(levelObj2.GetCommonness(selectedParams) - levelObj1.GetCommonness(selectedParams));
}); });
} }
@@ -630,7 +707,7 @@ namespace Barotrauma
{ {
if (lightingEnabled.Selected) if (lightingEnabled.Selected)
{ {
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam); GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam);
} }
graphics.Clear(Color.Black); graphics.Clear(Color.Black);
@@ -643,6 +720,72 @@ namespace Barotrauma
Submarine.DrawFront(spriteBatch); Submarine.DrawFront(spriteBatch);
Submarine.DrawDamageable(spriteBatch, null); Submarine.DrawDamageable(spriteBatch, null);
GUI.DrawRectangle(spriteBatch, new Rectangle(new Point(0, -Level.Loaded.Size.Y), Level.Loaded.Size), Color.Gray, thickness: (int)(1.0f / cam.Zoom)); GUI.DrawRectangle(spriteBatch, new Rectangle(new Point(0, -Level.Loaded.Size.Y), Level.Loaded.Size), Color.Gray, thickness: (int)(1.0f / cam.Zoom));
for (int i = 0; i < Level.Loaded.Tunnels.Count; i++)
{
var tunnel = Level.Loaded.Tunnels[i];
Color tunnelColor = tunnelDebugColors[i % tunnelDebugColors.Length] * 0.2f;
for (int j = 1; j < tunnel.Nodes.Count; j++)
{
Vector2 start = new Vector2(tunnel.Nodes[j - 1].X, -tunnel.Nodes[j - 1].Y);
Vector2 end = new Vector2(tunnel.Nodes[j].X, -tunnel.Nodes[j].Y);
GUI.DrawLine(spriteBatch, start, end, tunnelColor, width: (int)(2.0f / cam.Zoom));
}
}
foreach (Level.InterestingPosition interestingPos in Level.Loaded.PositionsOfInterest)
{
if (interestingPos.Position.X < cam.WorldView.X || interestingPos.Position.X > cam.WorldView.Right ||
interestingPos.Position.Y > cam.WorldView.Y || interestingPos.Position.Y < cam.WorldView.Y - cam.WorldView.Height)
{
continue;
}
Vector2 pos = new Vector2(interestingPos.Position.X, -interestingPos.Position.Y);
spriteBatch.DrawCircle(pos, 500, 6, Color.White * 0.5f, thickness: (int)(2 / cam.Zoom));
GUI.DrawString(spriteBatch, pos, interestingPos.PositionType.ToString(), Color.White, font: GUI.LargeFont);
}
// TODO: Improve this temporary level editor debug solution (or remove it)
foreach (var pathPoint in Level.Loaded.PathPoints)
{
Vector2 pathPointPos = new Vector2(pathPoint.Position.X, -pathPoint.Position.Y);
foreach (var location in pathPoint.ClusterLocations)
{
if (location.Resources == null) { continue; }
foreach (var resource in location.Resources)
{
Vector2 resourcePos = new Vector2(resource.Position.X, -resource.Position.Y);
spriteBatch.DrawCircle(resourcePos, 100, 6, Color.DarkGreen * 0.5f, thickness: (int)(2 / cam.Zoom));
GUI.DrawString(spriteBatch, resourcePos, resource.Name, Color.DarkGreen, font: GUI.LargeFont);
var dist = Vector2.Distance(resourcePos, pathPointPos);
var lineStartPos = Vector2.Lerp(resourcePos, pathPointPos, 110 / dist);
var lineEndPos = Vector2.Lerp(pathPointPos, resourcePos, 310 / dist);
GUI.DrawLine(spriteBatch, lineStartPos, lineEndPos, Color.DarkGreen * 0.5f, width: (int)(2 / cam.Zoom));
}
}
var color = pathPoint.ShouldContainResources ? Color.DarkGreen : Color.DarkRed;
spriteBatch.DrawCircle(pathPointPos, 300, 6, color * 0.5f, thickness: (int)(2 / cam.Zoom));
GUI.DrawString(spriteBatch, pathPointPos, "Path Point\n" + pathPoint.Id, color, font: GUI.LargeFont);
}
/*for (int i = 0; i < Level.Loaded.distanceField.Count; i++)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(Level.Loaded.distanceField[i].First.X, -Level.Loaded.distanceField[i].First.Y),
Vector2.One * 5 / cam.Zoom,
ToolBox.GradientLerp((float)(Level.Loaded.distanceField[i].Second / 20000.0), Color.Red, Color.Orange, Color.Yellow, Color.LightGreen),
isFilled: true);
}*/
/*for (int i = 0; i < Level.Loaded.siteCoordsX.Count; i++)
{
GUI.DrawRectangle(spriteBatch,
new Vector2((float)Level.Loaded.siteCoordsX[i], -(float)Level.Loaded.siteCoordsY[i]),
Vector2.One * 5 / cam.Zoom,
Color.Red,
isFilled: true);
}*/
spriteBatch.End(); spriteBatch.End();
if (lightingEnabled.Selected) if (lightingEnabled.Selected)
@@ -659,12 +802,23 @@ namespace Barotrauma
} }
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
if (Level.Loaded != null)
{
float crushDepthScreen = cam.WorldToScreen(new Vector2(0.0f, -Level.Loaded.CrushDepth)).Y;
if (crushDepthScreen > 0.0f && crushDepthScreen < GameMain.GraphicsHeight)
{
GUI.DrawLine(spriteBatch, new Vector2(0, crushDepthScreen), new Vector2(GameMain.GraphicsWidth, crushDepthScreen), GUI.Style.Red * 0.25f, width: 5);
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, crushDepthScreen), "Crush depth", GUI.Style.Red, backgroundColor: Color.Black);
}
}
GUI.Draw(Cam, spriteBatch); GUI.Draw(Cam, spriteBatch);
spriteBatch.End(); spriteBatch.End();
} }
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
GameMain.LightManager?.Update((float)deltaTime);
pointerLightSource.Position = cam.ScreenToWorld(PlayerInput.MousePosition); pointerLightSource.Position = cam.ScreenToWorld(PlayerInput.MousePosition);
pointerLightSource.Enabled = cursorLightEnabled.Selected; pointerLightSource.Enabled = cursorLightEnabled.Selected;
pointerLightSource.IsBackground = true; pointerLightSource.IsBackground = true;
@@ -694,7 +848,40 @@ namespace Barotrauma
{ {
foreach (XElement element in doc.Root.Elements()) foreach (XElement element in doc.Root.Elements())
{ {
XElement levelParamElement = element; if (element.IsOverride())
{
foreach (XElement subElement in element.Elements())
{
string id = element.GetAttributeString("identifier", null) ?? element.Name.ToString();
if (!id.Equals(genParams.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
SerializableProperty.SerializeProperties(genParams, element, true);
}
}
else
{
string id = element.GetAttributeString("identifier", null) ?? element.Name.ToString();
if (!id.Equals(genParams.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
SerializableProperty.SerializeProperties(genParams, element, true);
}
break;
}
}
using (var writer = XmlWriter.Create(configFile.Path, settings))
{
doc.WriteTo(writer);
writer.Flush();
}
}
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.CaveGenerationParameters))
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { continue; }
foreach (CaveGenerationParams genParams in CaveGenerationParams.CaveParams)
{
foreach (XElement element in doc.Root.Elements())
{
if (element.IsOverride()) if (element.IsOverride())
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
@@ -730,7 +917,8 @@ namespace Barotrauma
{ {
foreach (XElement element in doc.Root.Elements()) foreach (XElement element in doc.Root.Elements())
{ {
if (!element.Name.ToString().Equals(levelObjPrefab.Name, StringComparison.OrdinalIgnoreCase)) { continue; } string identifier = element.GetAttributeString("identifier", null);
if (!identifier.Equals(levelObjPrefab.Identifier, StringComparison.OrdinalIgnoreCase)) { continue; }
levelObjPrefab.Save(element); levelObjPrefab.Save(element);
break; break;
} }
@@ -846,7 +1034,7 @@ namespace Barotrauma
return false; return false;
} }
if (LevelObjectPrefab.List.Any(obj => obj.Name.ToLower() == nameBox.Text.ToLower())) if (LevelObjectPrefab.List.Any(obj => obj.Identifier.Equals(nameBox.Text, StringComparison.OrdinalIgnoreCase)))
{ {
nameBox.Flash(GUI.Style.Red); nameBox.Flash(GUI.Style.Red);
GUI.AddMessage(TextManager.Get("leveleditor.levelobjnametaken"), GUI.Style.Red); GUI.AddMessage(TextManager.Get("leveleditor.levelobjnametaken"), GUI.Style.Red);
@@ -860,14 +1048,14 @@ namespace Barotrauma
return false; return false;
} }
newPrefab.Name = nameBox.Text; newPrefab.Identifier = nameBox.Text;
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true }; System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true };
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs)) foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.LevelObjectPrefabs))
{ {
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path); XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
if (doc == null) { continue; } if (doc == null) { continue; }
var newElement = new XElement(newPrefab.Name); var newElement = new XElement(newPrefab.Identifier);
newPrefab.Save(newElement); newPrefab.Save(newElement);
newElement.Add(new XElement("Sprite", newElement.Add(new XElement("Sprite",
new XAttribute("texture", texturePathBox.Text), new XAttribute("texture", texturePathBox.Text),
@@ -38,6 +38,9 @@ namespace Barotrauma
private GUIImage playstyleBanner; private GUIImage playstyleBanner;
private GUITextBlock playstyleDescription; private GUITextBlock playstyleDescription;
private GUIComponent remoteContentContainer;
private XDocument remoteContentDoc;
private Tab selectedTab; private Tab selectedTab;
private Sprite backgroundSprite; private Sprite backgroundSprite;
@@ -62,6 +65,14 @@ namespace Barotrauma
} }
CreateHostServerFields(); CreateHostServerFields();
CreateCampaignSetupUI(); CreateCampaignSetupUI();
if (remoteContentDoc?.Root != null)
{
remoteContentContainer.ClearChildren();
foreach (XElement subElement in remoteContentDoc.Root.Elements())
{
GUIComponent.FromXML(subElement, remoteContentContainer.RectTransform);
}
}
}; };
new GUIImage(new RectTransform(new Vector2(0.4f, 0.25f), Frame.RectTransform, Anchor.BottomRight) new GUIImage(new RectTransform(new Vector2(0.4f, 0.25f), Frame.RectTransform, Anchor.BottomRight)
@@ -84,6 +95,11 @@ namespace Barotrauma
RelativeSpacing = 0.02f RelativeSpacing = 0.02f
}; };
remoteContentContainer = new GUIFrame(new RectTransform(Vector2.One, parent: Frame.RectTransform), style: null)
{
CanBeFocused = false
};
#if TEST_REMOTE_CONTENT #if TEST_REMOTE_CONTENT
var doc = XMLExtensions.TryLoadXml("Content/UI/MenuTextTest.xml"); var doc = XMLExtensions.TryLoadXml("Content/UI/MenuTextTest.xml");
@@ -91,7 +107,7 @@ namespace Barotrauma
{ {
foreach (XElement subElement in doc?.Root.Elements()) foreach (XElement subElement in doc?.Root.Elements())
{ {
GUIComponent.FromXML(subElement, Frame.RectTransform); GUIComponent.FromXML(subElement, remoteContentContainer.RectTransform);
} }
} }
#else #else
@@ -1402,10 +1418,10 @@ namespace Barotrauma
if (index > 0) { xml = xml.Substring(index, xml.Length - index); } if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
if (!string.IsNullOrWhiteSpace(xml)) if (!string.IsNullOrWhiteSpace(xml))
{ {
XElement element = XDocument.Parse(xml)?.Root; remoteContentDoc = XDocument.Parse(xml);
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in remoteContentDoc?.Root.Elements())
{ {
GUIComponent.FromXML(subElement, Frame.RectTransform); GUIComponent.FromXML(subElement, remoteContentContainer.RectTransform);
} }
} }
} }
@@ -832,7 +832,7 @@ namespace Barotrauma
var modeFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), modeList.Content.RectTransform), style: null) var modeFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), modeList.Content.RectTransform), style: null)
{ {
UserData = mode UserData = mode
}; };
var modeContent = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.9f), modeFrame.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.02f, 0.0f) }) var modeContent = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.9f), modeFrame.RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.02f, 0.0f) })
{ {
@@ -910,15 +910,17 @@ namespace Barotrauma
} }
}; };
missionTypeTickBoxes = new GUITickBox[Enum.GetValues(typeof(MissionType)).Length - 2]; var missionTypes = (MissionType[])Enum.GetValues(typeof(MissionType));
missionTypeTickBoxes = new GUITickBox[missionTypes.Length - 2];
int index = 0; int index = 0;
foreach (MissionType missionType in Enum.GetValues(typeof(MissionType))) for (int i = 0; i < missionTypes.Length; i++)
{ {
var missionType = missionTypes[i];
if (missionType == MissionType.None || missionType == MissionType.All) { continue; } if (missionType == MissionType.None || missionType == MissionType.All) { continue; }
GUIFrame frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), missionTypeList.Content.RectTransform) { MinSize = new Point(0, (int)(30 * GUI.Scale)) }, style: "ListBoxElement") GUIFrame frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), missionTypeList.Content.RectTransform) { MinSize = new Point(0, (int)(30 * GUI.Scale)) }, style: "ListBoxElement")
{ {
UserData = index, UserData = missionType,
}; };
missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform), missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform),
@@ -934,7 +936,6 @@ namespace Barotrauma
} }
}; };
frame.RectTransform.MinSize = missionTypeTickBoxes[index].RectTransform.MinSize; frame.RectTransform.MinSize = missionTypeTickBoxes[index].RectTransform.MinSize;
index++; index++;
} }
clientDisabledElements.AddRange(missionTypeTickBoxes); clientDisabledElements.AddRange(missionTypeTickBoxes);
@@ -1485,7 +1486,7 @@ namespace Barotrauma
return true; return true;
} }
}; };
} }
} }
private void CreateChangesPendingText() private void CreateChangesPendingText()
@@ -2509,7 +2510,7 @@ namespace Barotrauma
Selected = info.Gender == Gender.Female Selected = info.Gender == Gender.Female
}; };
int hairCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables), WearableType.Hair).Count(); int hairCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race), WearableType.Hair, info.HeadSpriteId).Count();
if (hairCount > 0) if (hairCount > 0)
{ {
var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Hair"), font: GUI.SubHeadingFont); var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Hair"), font: GUI.SubHeadingFont);
@@ -2524,7 +2525,7 @@ namespace Barotrauma
}; };
} }
int beardCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables), WearableType.Beard).Count(); int beardCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race), WearableType.Beard, info.HeadSpriteId).Count();
if (beardCount > 0) if (beardCount > 0)
{ {
var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Beard"), font: GUI.SubHeadingFont); var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Beard"), font: GUI.SubHeadingFont);
@@ -2539,7 +2540,7 @@ namespace Barotrauma
}; };
} }
int moustacheCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables), WearableType.Moustache).Count(); int moustacheCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race), WearableType.Moustache, info.HeadSpriteId).Count();
if (moustacheCount > 0) if (moustacheCount > 0)
{ {
var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Moustache"), font: GUI.SubHeadingFont); var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Moustache"), font: GUI.SubHeadingFont);
@@ -2554,7 +2555,7 @@ namespace Barotrauma
}; };
} }
int faceAttachmentCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables), WearableType.FaceAttachment).Count(); int faceAttachmentCount = info.FilterByTypeAndHeadID(info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race), WearableType.FaceAttachment, info.HeadSpriteId).Count();
if (faceAttachmentCount > 0) if (faceAttachmentCount > 0)
{ {
var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Accessories"), font: GUI.SubHeadingFont); var label = new GUITextBlock(new RectTransform(elementSize, content.RectTransform), TextManager.Get("FaceAttachment.Accessories"), font: GUI.SubHeadingFont);
@@ -2976,7 +2977,7 @@ namespace Barotrauma
public void SelectMode(int modeIndex) public void SelectMode(int modeIndex)
{ {
if (modeIndex < 0 || modeIndex >= modeList.Content.CountChildren) { return; } if (modeIndex < 0 || modeIndex >= modeList.Content.CountChildren) { return; }
if ((GameModePreset)modeList.Content.GetChild(modeIndex).UserData != GameModePreset.MultiPlayerCampaign) if ((GameModePreset)modeList.Content.GetChild(modeIndex).UserData != GameModePreset.MultiPlayerCampaign)
{ {
ToggleCampaignMode(false); ToggleCampaignMode(false);
@@ -3001,16 +3002,33 @@ namespace Barotrauma
RefreshGameModeContent(); RefreshGameModeContent();
} }
private void RefreshMissionTypes()
{
for (int i = 0; i < missionTypeTickBoxes.Length; i++)
{
MissionType missionType = (MissionType)((int)missionTypeTickBoxes[i].UserData);
if (SelectedMode == GameModePreset.Mission)
{
missionTypeTickBoxes[i].Parent.Visible = MissionPrefab.CoOpMissionClasses.ContainsKey(missionType);
}
else if (SelectedMode == GameModePreset.PvP)
{
missionTypeTickBoxes[i].Parent.Visible = MissionPrefab.PvPMissionClasses.ContainsKey(missionType);
}
}
}
private void RefreshGameModeContent() private void RefreshGameModeContent()
{ {
if (GameMain.Client == null) { return; } if (GameMain.Client == null) { return; }
autoRestartBox.Parent.Visible = true; autoRestartBox.Parent.Visible = true;
settingsBlocker.Visible = false; settingsBlocker.Visible = false;
if (SelectedMode == GameModePreset.Mission) if (SelectedMode == GameModePreset.Mission || SelectedMode == GameModePreset.PvP)
{ {
MissionTypeFrame.Visible = true; MissionTypeFrame.Visible = true;
CampaignFrame.Visible = CampaignSetupFrame.Visible = false; CampaignFrame.Visible = CampaignSetupFrame.Visible = false;
RefreshMissionTypes();
} }
else if (SelectedMode == GameModePreset.MultiPlayerCampaign) else if (SelectedMode == GameModePreset.MultiPlayerCampaign)
{ {
@@ -3062,7 +3080,7 @@ namespace Barotrauma
RefreshEnabledElements(); RefreshEnabledElements();
if (enabled) if (enabled)
{ {
modeList.Select(2, true); modeList.Select(GameModePreset.MultiPlayerCampaign, true);
} }
} }
@@ -61,7 +61,7 @@ namespace Barotrauma
{ {
var temp = LoadException; var temp = LoadException;
LoadException = null; LoadException = null;
throw temp; System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(temp).Throw();
} }
} }
} }
@@ -1061,7 +1061,7 @@ namespace Barotrauma
(!filterFull.Selected || serverInfo.PlayerCount < serverInfo.MaxPlayers) && (!filterFull.Selected || serverInfo.PlayerCount < serverInfo.MaxPlayers) &&
(!filterEmpty.Selected || serverInfo.PlayerCount > 0) && (!filterEmpty.Selected || serverInfo.PlayerCount > 0) &&
(!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) && (!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) &&
(filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) && (!filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) &&
(!filterKarma.Selected || serverInfo.KarmaEnabled == true) && (!filterKarma.Selected || serverInfo.KarmaEnabled == true) &&
(!filterFriendlyFire.Selected || serverInfo.FriendlyFireEnabled == false) && (!filterFriendlyFire.Selected || serverInfo.FriendlyFireEnabled == false) &&
(!filterTraitor.Selected || serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe) && (!filterTraitor.Selected || serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe) &&
@@ -71,6 +71,7 @@ namespace Barotrauma
private bool entityMenuOpen = true; private bool entityMenuOpen = true;
private float entityMenuOpenState = 1.0f; private float entityMenuOpenState = 1.0f;
private string lastFilter;
public GUIComponent EntityMenu; public GUIComponent EntityMenu;
private GUITextBox entityFilterBox; private GUITextBox entityFilterBox;
private GUIListBox entityList; private GUIListBox entityList;
@@ -105,7 +106,23 @@ namespace Barotrauma
private static GUIComponent autoSaveLabel; private static GUIComponent autoSaveLabel;
private static int maxAutoSaves = GameSettings.MaximumAutoSaves; private static int maxAutoSaves = GameSettings.MaximumAutoSaves;
public static bool BulkItemBufferInUse; public static readonly object ItemAddMutex = new object(), ItemRemoveMutex = new object();
private static object bulkItemBufferinUse;
public static object BulkItemBufferInUse
{
get => bulkItemBufferinUse;
set
{
if (value != bulkItemBufferinUse && bulkItemBufferinUse != null)
{
CommitBulkItemBuffer();
}
bulkItemBufferinUse = value;
}
}
public static List<AddOrDeleteCommand> BulkItemBuffer = new List<AddOrDeleteCommand>(); public static List<AddOrDeleteCommand> BulkItemBuffer = new List<AddOrDeleteCommand>();
public static List<WarningType> SuppressedWarnings = new List<WarningType>(); public static List<WarningType> SuppressedWarnings = new List<WarningType>();
@@ -630,35 +647,33 @@ namespace Barotrauma
return Structure.WallList.Count.ToString(); return Structure.WallList.Count.ToString();
}; };
var lightCountText = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("SubEditorLights"), var lightCountLabel = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("SubEditorLights"),
textAlignment: Alignment.CenterLeft, font: GUI.SmallFont); textAlignment: Alignment.CenterLeft, font: GUI.SmallFont);
var lightCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), lightCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight); var lightCountText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), lightCountLabel.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
lightCount.TextGetter = () => lightCountText.TextGetter = () =>
{ {
int disabledItemLightCount = 0; int lightCount = 0;
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (item.ParentInventory == null) { continue; } if (item.ParentInventory != null) { continue; }
disabledItemLightCount += item.GetComponents<LightComponent>().Count(); lightCount += item.GetComponents<LightComponent>().Count();
} }
int count = GameMain.LightManager.Lights.Count() - disabledItemLightCount; lightCountText.TextColor = ToolBox.GradientLerp(lightCount / 250.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
lightCount.TextColor = ToolBox.GradientLerp(count / 250.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red); return lightCount.ToString();
return count.ToString();
}; };
var shadowCastingLightCountText = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("SubEditorShadowCastingLights"), var shadowCastingLightCountLabel = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("SubEditorShadowCastingLights"),
textAlignment: Alignment.CenterLeft, font: GUI.SmallFont, wrap: true); textAlignment: Alignment.CenterLeft, font: GUI.SmallFont, wrap: true);
var shadowCastingLightCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), shadowCastingLightCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight); var shadowCastingLightCountText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), shadowCastingLightCountLabel.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
shadowCastingLightCount.TextGetter = () => shadowCastingLightCountText.TextGetter = () =>
{ {
int disabledItemLightCount = 0; int lightCount = 0;
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (item.ParentInventory == null) { continue; } if (item.ParentInventory != null) { continue; }
disabledItemLightCount += item.GetComponents<LightComponent>().Count(); lightCount += item.GetComponents<LightComponent>().Count(l => l.CastShadows);
} }
int count = GameMain.LightManager.Lights.Count(l => l.CastShadows) - disabledItemLightCount; shadowCastingLightCountText.TextColor = ToolBox.GradientLerp(lightCount / 60.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
shadowCastingLightCount.TextColor = ToolBox.GradientLerp(count / 60.0f, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red); return lightCount.ToString();
return count.ToString();
}; };
entityCountPanel.RectTransform.NonScaledSize = entityCountPanel.RectTransform.NonScaledSize =
new Point( new Point(
@@ -737,7 +752,13 @@ namespace Barotrauma
var filterText = new GUITextBlock(new RectTransform(new Vector2(0.1f, 1.0f), entityMenuTop.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.SubHeadingFont); var filterText = new GUITextBlock(new RectTransform(new Vector2(0.1f, 1.0f), entityMenuTop.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.SubHeadingFont);
filterText.RectTransform.MaxSize = new Point((int)(filterText.TextSize.X * 1.5f), int.MaxValue); filterText.RectTransform.MaxSize = new Point((int)(filterText.TextSize.X * 1.5f), int.MaxValue);
entityFilterBox = new GUITextBox(new RectTransform(new Vector2(0.17f, 1.0f), entityMenuTop.RectTransform), font: GUI.Font, createClearButton: true); entityFilterBox = new GUITextBox(new RectTransform(new Vector2(0.17f, 1.0f), entityMenuTop.RectTransform), font: GUI.Font, createClearButton: true);
entityFilterBox.OnTextChanged += (textBox, text) => { FilterEntities(text); return true; }; entityFilterBox.OnTextChanged += (textBox, text) =>
{
if (text == lastFilter) { return true; }
lastFilter = text;
FilterEntities(text);
return true;
};
//spacing //spacing
new GUIFrame(new RectTransform(new Vector2(0.075f, 1.0f), entityMenuTop.RectTransform), style: null); new GUIFrame(new RectTransform(new Vector2(0.075f, 1.0f), entityMenuTop.RectTransform), style: null);
@@ -994,8 +1015,7 @@ namespace Barotrauma
GameMain.LightManager.AmbientLight = GameMain.LightManager.AmbientLight =
Level.Loaded?.GenerationParams?.AmbientLightColor ?? Level.Loaded?.GenerationParams?.AmbientLightColor ??
LevelGenerationParams.LevelParams?.FirstOrDefault()?.AmbientLightColor ?? new Color(3, 3, 3, 3);
new Color(20, 20, 20, 255);
UpdateEntityList(); UpdateEntityList();
if (!wasSelectedBefore) if (!wasSelectedBefore)
@@ -1150,7 +1170,7 @@ namespace Barotrauma
{ {
if (dummyCharacter != null) RemoveDummyCharacter(); if (dummyCharacter != null) RemoveDummyCharacter();
dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", hasAi: false); dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.RespawnManagerID, hasAi: false);
dummyCharacter.Info.Name = "Galldren"; dummyCharacter.Info.Name = "Galldren";
//make space for the entity menu //make space for the entity menu
@@ -1853,6 +1873,10 @@ namespace Barotrauma
Submarine.MainSub.Info.Price = numberInput.IntValue; Submarine.MainSub.Info.Price = numberInput.IntValue;
} }
}; };
if (Submarine.MainSub?.Info != null)
{
Submarine.MainSub.Info.Price = Math.Max(Submarine.MainSub.Info.Price, basePrice);
}
if (!Submarine.MainSub.Info.HasTag(SubmarineTag.Shuttle)) if (!Submarine.MainSub.Info.HasTag(SubmarineTag.Shuttle))
{ {
@@ -2665,7 +2689,7 @@ namespace Barotrauma
foreach (GUIComponent child in entityList.Content.Children) foreach (GUIComponent child in entityList.Content.Children)
{ {
child.Visible = !entityCategory.HasValue || ((MapEntityPrefab) child.UserData).Category == entityCategory; child.Visible = !entityCategory.HasValue || ((MapEntityPrefab) child.UserData).Category.HasFlag(entityCategory);
if (child.Visible && dummyCharacter?.SelectedConstruction?.OwnInventory != null) if (child.Visible && dummyCharacter?.SelectedConstruction?.OwnInventory != null)
{ {
child.Visible = child.UserData is MapEntityPrefab item && IsItemPrefab(item); child.Visible = child.UserData is MapEntityPrefab item && IsItemPrefab(item);
@@ -3710,6 +3734,24 @@ namespace Barotrauma
} }
} }
private static void CommitBulkItemBuffer()
{
if (BulkItemBuffer.Any())
{
AddOrDeleteCommand master = BulkItemBuffer[0];
for (int i = 1; i < BulkItemBuffer.Count; i++)
{
AddOrDeleteCommand command = BulkItemBuffer[i];
command.MergeInto(master);
}
StoreCommand(master);
BulkItemBuffer.Clear();
}
bulkItemBufferinUse = null;
}
/// <summary> /// <summary>
/// Allows the game to run logic such as updating the world, /// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio. /// checking for collisions, gathering input, and playing audio.
@@ -3939,6 +3981,11 @@ namespace Barotrauma
CloseItem(); CloseItem();
} }
if (lightingEnabled)
{
GameMain.LightManager?.Update((float)deltaTime);
}
if (contextMenu != null) if (contextMenu != null)
{ {
Rectangle expandedRect = contextMenu.Rect; Rectangle expandedRect = contextMenu.Rect;
@@ -4034,7 +4081,7 @@ namespace Barotrauma
// Deposit item from our "infinite stack" into inventory slots // Deposit item from our "infinite stack" into inventory slots
var inv = dummyCharacter?.SelectedConstruction?.OwnInventory; var inv = dummyCharacter?.SelectedConstruction?.OwnInventory;
if (inv?.slots != null) if (inv?.slots != null && !PlayerInput.IsCtrlDown())
{ {
var dragginMouse = MouseDragStart != Vector2.Zero && Vector2.Distance(PlayerInput.MousePosition, MouseDragStart) >= GUI.Scale * 20; var dragginMouse = MouseDragStart != Vector2.Zero && Vector2.Distance(PlayerInput.MousePosition, MouseDragStart) >= GUI.Scale * 20;
@@ -4090,7 +4137,7 @@ namespace Barotrauma
if (!newItem.Removed) if (!newItem.Removed)
{ {
BulkItemBufferInUse = true; BulkItemBufferInUse = ItemAddMutex;
BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { newItem }, false)); BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { newItem }, false));
} }
@@ -4162,7 +4209,7 @@ namespace Barotrauma
List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<MapEntity>().ToList(); List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<MapEntity>().ToList();
if (placedEntities.Any()) if (placedEntities.Any())
{ {
BulkItemBufferInUse = true; BulkItemBufferInUse = ItemAddMutex;
BulkItemBuffer.Add(new AddOrDeleteCommand(placedEntities, false)); BulkItemBuffer.Add(new AddOrDeleteCommand(placedEntities, false));
} }
} }
@@ -4174,18 +4221,9 @@ namespace Barotrauma
} }
} }
if (BulkItemBufferInUse && PlayerInput.PrimaryMouseButtonReleased() && BulkItemBuffer.Any()) if (PlayerInput.PrimaryMouseButtonReleased() && BulkItemBufferInUse != null)
{ {
AddOrDeleteCommand master = BulkItemBuffer[0]; CommitBulkItemBuffer();
for (int i = 1; i < BulkItemBuffer.Count; i++)
{
AddOrDeleteCommand command = BulkItemBuffer[i];
command.MergeInto(master);
}
StoreCommand(master);
BulkItemBuffer.Clear();
BulkItemBufferInUse = false;
} }
if (SerializableEntityEditor.PropertyChangesActive && (SerializableEntityEditor.NextCommandPush < DateTime.Now || MapEntity.EditingHUD == null)) if (SerializableEntityEditor.PropertyChangesActive && (SerializableEntityEditor.NextCommandPush < DateTime.Now || MapEntity.EditingHUD == null))
@@ -4326,7 +4364,7 @@ namespace Barotrauma
cam.UpdateTransform(); cam.UpdateTransform();
if (lightingEnabled) if (lightingEnabled)
{ {
GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam); GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam);
} }
foreach (Submarine sub in Submarine.Loaded) foreach (Submarine sub in Submarine.Loaded)
@@ -4493,6 +4531,7 @@ namespace Barotrauma
stream.Dispose(); stream.Dispose();
} }
public static bool IsSubEditor() { return Screen.Selected is SubEditorScreen && !Submarine.Unloading; } public static bool IsSubEditor() => Screen.Selected is SubEditorScreen && !Submarine.Unloading;
public static bool IsWiringMode() => Screen.Selected == GameMain.SubEditorScreen && GameMain.SubEditorScreen.WiringMode && !Submarine.Unloading;
} }
} }
@@ -566,8 +566,13 @@ namespace Barotrauma
{ {
ToolTip = toolTip ToolTip = toolTip
}; };
bool isFlagsAttribute = value.GetType().IsDefined(typeof(FlagsAttribute), false);
foreach (object enumValue in Enum.GetValues(value.GetType())) foreach (object enumValue in Enum.GetValues(value.GetType()))
{ {
if (isFlagsAttribute && !MathHelper.IsPowerOfTwo((int)enumValue)) { continue; }
enumDropDown.AddItem(enumValue.ToString(), enumValue); enumDropDown.AddItem(enumValue.ToString(), enumValue);
if (((int)enumValue != 0 || (int)value == 0) && ((Enum)value).HasFlag((Enum)enumValue)) if (((int)enumValue != 0 || (int)value == 0) && ((Enum)value).HasFlag((Enum)enumValue))
{ {
@@ -730,6 +730,7 @@ namespace Barotrauma.Sounds
else else
{ {
playingChannels[i][j].Dispose(); playingChannels[i][j].Dispose();
playingChannels[i][j] = null;
} }
} }
else if (playingChannels[i][j].FadingOutAndDisposing) else if (playingChannels[i][j].FadingOutAndDisposing)
@@ -738,6 +739,7 @@ namespace Barotrauma.Sounds
if (playingChannels[i][j].Gain <= 0.0f) if (playingChannels[i][j].Gain <= 0.0f)
{ {
playingChannels[i][j].Dispose(); playingChannels[i][j].Dispose();
playingChannels[i][j] = null;
} }
} }
} }
@@ -499,6 +499,12 @@ namespace Barotrauma
private static void UpdateWaterFlowSounds(float deltaTime) private static void UpdateWaterFlowSounds(float deltaTime)
{ {
if (FlowSounds.Count == 0) { return; } if (FlowSounds.Count == 0) { return; }
for (int i = 0; i < targetFlowLeft.Length; i++)
{
targetFlowLeft[i] = 0.0f;
targetFlowRight[i] = 0.0f;
}
Vector2 listenerPos = new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y); Vector2 listenerPos = new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y);
foreach (Gap gap in Gap.GapList) foreach (Gap gap in Gap.GapList)
@@ -619,7 +625,8 @@ namespace Barotrauma
Vector2 soundPos = new Vector2(GameMain.SoundManager.ListenerPosition.X + (fireVolumeRight[i] - fireVolumeLeft[i]) * 100, GameMain.SoundManager.ListenerPosition.Y); Vector2 soundPos = new Vector2(GameMain.SoundManager.ListenerPosition.X + (fireVolumeRight[i] - fireVolumeLeft[i]) * 100, GameMain.SoundManager.ListenerPosition.Y);
if (fireSoundChannels[i] == null || !fireSoundChannels[i].IsPlaying) if (fireSoundChannels[i] == null || !fireSoundChannels[i].IsPlaying)
{ {
fireSoundChannels[i] = GetSound(fireSoundTags[i]).Play(1.0f, FlowSoundRange, soundPos); fireSoundChannels[i] = GetSound(fireSoundTags[i])?.Play(1.0f, FlowSoundRange, soundPos);
if (fireSoundChannels[i] == null) { continue; }
fireSoundChannels[i].Looping = true; fireSoundChannels[i].Looping = true;
} }
fireSoundChannels[i].Gain = Math.Max(fireVolumeRight[i], fireVolumeLeft[i]); fireSoundChannels[i].Gain = Math.Max(fireVolumeRight[i], fireVolumeLeft[i]);
@@ -954,20 +961,18 @@ namespace Barotrauma
if (Level.IsLoadedOutpost && Character.Controlled.Submarine == Level.Loaded.StartOutpost) if (Level.IsLoadedOutpost && Character.Controlled.Submarine == Level.Loaded.StartOutpost)
{ {
// Only return music type for specific outpost types to not assume that // Only return music type for location types which have music tracks defined
// every outpost type has an associated music track (switch-case for future tracks)
var locationType = Level.Loaded.StartLocation?.Type?.Identifier?.ToLowerInvariant(); var locationType = Level.Loaded.StartLocation?.Type?.Identifier?.ToLowerInvariant();
switch (locationType) if (!string.IsNullOrEmpty(locationType) && musicClips.Any(c => c.Type == locationType))
{ {
case "research": return locationType;
return locationType;
} }
} }
} }
Submarine targetSubmarine = Character.Controlled?.Submarine; Submarine targetSubmarine = Character.Controlled?.Submarine;
if ((targetSubmarine != null && targetSubmarine.AtDamageDepth) || if ((targetSubmarine != null && targetSubmarine.AtDamageDepth) ||
(GameMain.GameScreen != null && Screen.Selected == GameMain.GameScreen && GameMain.GameScreen.Cam.Position.Y < SubmarineBody.DamageDepth)) (GameMain.GameScreen != null && Screen.Selected == GameMain.GameScreen && Level.Loaded != null && Level.Loaded.GetRealWorldDepth(GameMain.GameScreen.Cam.Position.Y) > Level.Loaded.RealWorldCrushDepth))
{ {
return "deep"; return "deep";
} }
@@ -57,65 +57,9 @@ namespace Barotrauma
{ {
if (entity == null) { return; } if (entity == null) { return; }
if (sounds.Count > 0 && playSound) if (playSound)
{ {
if (soundChannel == null || !soundChannel.IsPlaying) PlaySound(entity, hull, worldPosition);
{
if (soundSelectionMode == SoundSelectionMode.All)
{
foreach (RoundSound sound in sounds)
{
if (sound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
soundChannel = SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, hullGuess: hull);
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
}
else
{
int selectedSoundIndex;
if (soundSelectionMode == SoundSelectionMode.ItemSpecific && entity is Item item)
{
selectedSoundIndex = item.ID % sounds.Count;
}
else if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && entity is Character user)
{
selectedSoundIndex = user.ID % sounds.Count;
}
else
{
selectedSoundIndex = Rand.Int(sounds.Count);
}
var selectedSound = sounds[selectedSoundIndex];
if (selectedSound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
if (selectedSound.Sound.Disposed)
{
Submarine.ReloadRoundSound(selectedSound);
}
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, worldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: hull);
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
}
else
{
soundChannel.Position = new Vector3(worldPosition, 0.0f);
}
if (soundChannel != null && soundChannel.Looping)
{
ActiveLoopingSounds.Add(this);
soundEmitter = entity;
loopStartTime = Timing.TotalTime;
}
} }
foreach (ParticleEmitter emitter in particleEmitters) foreach (ParticleEmitter emitter in particleEmitters)
@@ -151,6 +95,69 @@ namespace Barotrauma
} }
} }
private void PlaySound(Entity entity, Hull hull, Vector2 worldPosition)
{
if (sounds.Count == 0) return;
if (soundChannel == null || !soundChannel.IsPlaying)
{
if (soundSelectionMode == SoundSelectionMode.All)
{
foreach (RoundSound sound in sounds)
{
if (sound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
soundChannel = SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, hullGuess: hull);
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
}
else
{
int selectedSoundIndex;
if (soundSelectionMode == SoundSelectionMode.ItemSpecific && entity is Item item)
{
selectedSoundIndex = item.ID % sounds.Count;
}
else if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && entity is Character user)
{
selectedSoundIndex = user.ID % sounds.Count;
}
else
{
selectedSoundIndex = Rand.Int(sounds.Count);
}
var selectedSound = sounds[selectedSoundIndex];
if (selectedSound?.Sound == null)
{
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
if (selectedSound.Sound.Disposed)
{
Submarine.ReloadRoundSound(selectedSound);
}
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, worldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: hull);
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
}
else
{
soundChannel.Position = new Vector3(worldPosition, 0.0f);
}
if (soundChannel != null && soundChannel.Looping)
{
ActiveLoopingSounds.Add(this);
soundEmitter = entity;
loopStartTime = Timing.TotalTime;
}
}
static partial void UpdateAllProjSpecific(float deltaTime) static partial void UpdateAllProjSpecific(float deltaTime)
{ {
bool doMuffleCheck = Timing.TotalTime > LastMuffleCheckTime + 0.2; bool doMuffleCheck = Timing.TotalTime > LastMuffleCheckTime + 0.2;
@@ -120,7 +120,8 @@ namespace Barotrauma
/// </summary> /// </summary>
/// <param name="receivers">Entities that were deleted or added</param> /// <param name="receivers">Entities that were deleted or added</param>
/// <param name="wasDeleted">Whether or not all entities are or are going to be deleted</param> /// <param name="wasDeleted">Whether or not all entities are or are going to be deleted</param>
public AddOrDeleteCommand(List<MapEntity> receivers, bool wasDeleted) /// <param name="handleInventoryBehavior">Ignore item inventories when set to false, workaround for pasting</param>
public AddOrDeleteCommand(List<MapEntity> receivers, bool wasDeleted, bool handleInventoryBehavior = true)
{ {
WasDeleted = wasDeleted; WasDeleted = wasDeleted;
Receivers = receivers; Receivers = receivers;
@@ -151,14 +152,17 @@ namespace Barotrauma
} }
} }
if (itemsToDelete.Any()) if (itemsToDelete.Any() && handleInventoryBehavior)
{ {
ContainedItemsCommand.Add(new AddOrDeleteCommand(itemsToDelete, true)); ContainedItemsCommand.Add(new AddOrDeleteCommand(itemsToDelete, wasDeleted));
foreach (MapEntity item in itemsToDelete) if (wasDeleted)
{ {
if (item != null && !item.Removed) foreach (MapEntity item in itemsToDelete)
{ {
item.Remove(); if (item != null && !item.Removed)
{
item.Remove();
}
} }
} }
} }
@@ -226,10 +230,10 @@ namespace Barotrauma
Debug.Assert(Receivers.All(entity => entity.GetReplacementOrThis().Removed), "Tried to redo a deletion but some items were not deleted"); Debug.Assert(Receivers.All(entity => entity.GetReplacementOrThis().Removed), "Tried to redo a deletion but some items were not deleted");
List<MapEntity> clones = MapEntity.Clone(CloneList); List<MapEntity> clones = MapEntity.Clone(CloneList);
for (int i = 0; i < Math.Min(Receivers.Count, clones.Count); i++) int length = Math.Min(Receivers.Count, clones.Count);
for (int i = 0; i < length; i++)
{ {
MapEntity clone = clones[i]; MapEntity clone = clones[i], receiver = Receivers[i];
MapEntity receiver = Receivers[i];
if (receiver.GetReplacementOrThis() is Item item && clone is Item cloneItem) if (receiver.GetReplacementOrThis() is Item item && clone is Item cloneItem)
{ {
@@ -252,6 +256,11 @@ namespace Barotrauma
} }
receiver.GetReplacementOrThis().ReplacedBy = clone; receiver.GetReplacementOrThis().ReplacedBy = clone;
}
for (int i = 0; i < length; i++)
{
MapEntity clone = clones[i], receiver = Receivers[i];
if (clone is Item it) if (clone is Item it)
{ {
@@ -1,773 +0,0 @@
//
// System.Web.HttpUtility
//
// Authors:
// Patrik Torstensson (Patrik.Torstensson@labs2.com)
// Wictor Wilén (decode/encode functions) (wictor@ibizkit.se)
// Tim Coleman (tim@timcoleman.com)
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using Barotrauma.IO;
using System.Security.Permissions;
using System.Text;
namespace RestSharp.Contrib
{
//#if !MONOTOUCH
// // CAS - no InheritanceDemand here as the class is sealed
// [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
//#endif
public sealed class HttpUtility
{
sealed class HttpQSCollection : NameValueCollection
{
public override string ToString()
{
int count = Count;
if (count == 0)
return "";
StringBuilder sb = new StringBuilder();
string[] keys = AllKeys;
for (int i = 0; i < count; i++)
{
sb.AppendFormat("{0}={1}&", keys[i], this[keys[i]]);
}
if (sb.Length > 0)
sb.Length--;
return sb.ToString();
}
}
#region Constructors
public HttpUtility()
{
}
#endregion // Constructors
#region Methods
/*
public static void HtmlAttributeEncode(string s, TextWriter output)
{
if (output == null)
{
#if NET_4_0
throw new ArgumentNullException ("output");
#else
throw new NullReferenceException(".NET emulation");
#endif
}
#if NET_4_0
HttpEncoder.Current.HtmlAttributeEncode (s, output);
#else
output.Write(HttpEncoder.HtmlAttributeEncode(s));
#endif
}
*/
public static string HtmlAttributeEncode(string s)
{
#if NET_4_0
if (s == null)
return null;
using (var sw = new StringWriter ()) {
HttpEncoder.Current.HtmlAttributeEncode (s, sw);
return sw.ToString ();
}
#else
return HttpEncoder.HtmlAttributeEncode(s);
#endif
}
public static string UrlDecode(string str)
{
return UrlDecode(str, Encoding.UTF8);
}
static char[] GetChars(System.IO.MemoryStream b, Encoding e)
{
return e.GetChars(b.GetBuffer(), 0, (int)b.Length);
}
static void WriteCharBytes(IList buf, char ch, Encoding e)
{
if (ch > 255)
{
foreach (byte b in e.GetBytes(new char[] { ch }))
buf.Add(b);
}
else
buf.Add((byte)ch);
}
public static string UrlDecode(string s, Encoding e)
{
if (null == s)
return null;
if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
return s;
if (e == null)
e = Encoding.UTF8;
long len = s.Length;
var bytes = new List<byte>();
int xchar;
char ch;
for (int i = 0; i < len; i++)
{
ch = s[i];
if (ch == '%' && i + 2 < len && s[i + 1] != '%')
{
if (s[i + 1] == 'u' && i + 5 < len)
{
// unicode hex sequence
xchar = GetChar(s, i + 2, 4);
if (xchar != -1)
{
WriteCharBytes(bytes, (char)xchar, e);
i += 5;
}
else
WriteCharBytes(bytes, '%', e);
}
else if ((xchar = GetChar(s, i + 1, 2)) != -1)
{
WriteCharBytes(bytes, (char)xchar, e);
i += 2;
}
else
{
WriteCharBytes(bytes, '%', e);
}
continue;
}
if (ch == '+')
WriteCharBytes(bytes, ' ', e);
else
WriteCharBytes(bytes, ch, e);
}
byte[] buf = bytes.ToArray();
bytes = null;
return e.GetString(buf);
}
public static string UrlDecode(byte[] bytes, Encoding e)
{
if (bytes == null)
return null;
return UrlDecode(bytes, 0, bytes.Length, e);
}
static int GetInt(byte b)
{
char c = (char)b;
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static int GetChar(byte[] bytes, int offset, int length)
{
int value = 0;
int end = length + offset;
for (int i = offset; i < end; i++)
{
int current = GetInt(bytes[i]);
if (current == -1)
return -1;
value = (value << 4) + current;
}
return value;
}
static int GetChar(string str, int offset, int length)
{
int val = 0;
int end = length + offset;
for (int i = offset; i < end; i++)
{
char c = str[i];
if (c > 127)
return -1;
int current = GetInt((byte)c);
if (current == -1)
return -1;
val = (val << 4) + current;
}
return val;
}
public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e)
{
if (bytes == null)
return null;
if (count == 0)
return String.Empty;
if (bytes == null)
throw new ArgumentNullException("bytes");
if (offset < 0 || offset > bytes.Length)
throw new ArgumentOutOfRangeException("offset");
if (count < 0 || offset + count > bytes.Length)
throw new ArgumentOutOfRangeException("count");
StringBuilder output = new StringBuilder();
System.IO.MemoryStream acc = new System.IO.MemoryStream();
int end = count + offset;
int xchar;
for (int i = offset; i < end; i++)
{
if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%')
{
if (bytes[i + 1] == (byte)'u' && i + 5 < end)
{
if (acc.Length > 0)
{
output.Append(GetChars(acc, e));
acc.SetLength(0);
}
xchar = GetChar(bytes, i + 2, 4);
if (xchar != -1)
{
output.Append((char)xchar);
i += 5;
continue;
}
}
else if ((xchar = GetChar(bytes, i + 1, 2)) != -1)
{
acc.WriteByte((byte)xchar);
i += 2;
continue;
}
}
if (acc.Length > 0)
{
output.Append(GetChars(acc, e));
acc.SetLength(0);
}
if (bytes[i] == '+')
{
output.Append(' ');
}
else
{
output.Append((char)bytes[i]);
}
}
if (acc.Length > 0)
{
output.Append(GetChars(acc, e));
}
acc = null;
return output.ToString();
}
public static byte[] UrlDecodeToBytes(byte[] bytes)
{
if (bytes == null)
return null;
return UrlDecodeToBytes(bytes, 0, bytes.Length);
}
public static byte[] UrlDecodeToBytes(string str)
{
return UrlDecodeToBytes(str, Encoding.UTF8);
}
public static byte[] UrlDecodeToBytes(string str, Encoding e)
{
if (str == null)
return null;
if (e == null)
throw new ArgumentNullException("e");
return UrlDecodeToBytes(e.GetBytes(str));
}
public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count)
{
if (bytes == null)
return null;
if (count == 0)
return new byte[0];
int len = bytes.Length;
if (offset < 0 || offset >= len)
throw new ArgumentOutOfRangeException("offset");
if (count < 0 || offset > len - count)
throw new ArgumentOutOfRangeException("count");
System.IO.MemoryStream result = new System.IO.MemoryStream();
int end = offset + count;
for (int i = offset; i < end; i++)
{
char c = (char)bytes[i];
if (c == '+')
{
c = ' ';
}
else if (c == '%' && i < end - 2)
{
int xchar = GetChar(bytes, i + 1, 2);
if (xchar != -1)
{
c = (char)xchar;
i += 2;
}
}
result.WriteByte((byte)c);
}
return result.ToArray();
}
public static string UrlEncode(string str)
{
return UrlEncode(str, Encoding.UTF8);
}
public static string UrlEncode(string s, Encoding Enc)
{
if (s == null)
return null;
if (s == String.Empty)
return String.Empty;
bool needEncode = false;
int len = s.Length;
for (int i = 0; i < len; i++)
{
char c = s[i];
if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z'))
{
if (HttpEncoder.NotEncoded(c))
continue;
needEncode = true;
break;
}
}
if (!needEncode)
return s;
// avoided GetByteCount call
byte[] bytes = new byte[Enc.GetMaxByteCount(s.Length)];
int realLen = Enc.GetBytes(s, 0, s.Length, bytes, 0);
return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, realLen));
}
public static string UrlEncode(byte[] bytes)
{
if (bytes == null)
return null;
if (bytes.Length == 0)
return String.Empty;
return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, bytes.Length));
}
public static string UrlEncode(byte[] bytes, int offset, int count)
{
if (bytes == null)
return null;
if (bytes.Length == 0)
return String.Empty;
return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, offset, count));
}
public static byte[] UrlEncodeToBytes(string str)
{
return UrlEncodeToBytes(str, Encoding.UTF8);
}
public static byte[] UrlEncodeToBytes(string str, Encoding e)
{
if (str == null)
return null;
if (str.Length == 0)
return new byte[0];
byte[] bytes = e.GetBytes(str);
return UrlEncodeToBytes(bytes, 0, bytes.Length);
}
public static byte[] UrlEncodeToBytes(byte[] bytes)
{
if (bytes == null)
return null;
if (bytes.Length == 0)
return new byte[0];
return UrlEncodeToBytes(bytes, 0, bytes.Length);
}
public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count)
{
if (bytes == null)
return null;
#if NET_4_0
return HttpEncoder.Current.UrlEncode (bytes, offset, count);
#else
return HttpEncoder.UrlEncodeToBytes(bytes, offset, count);
#endif
}
public static string UrlEncodeUnicode(string str)
{
if (str == null)
return null;
return Encoding.ASCII.GetString(UrlEncodeUnicodeToBytes(str));
}
public static byte[] UrlEncodeUnicodeToBytes(string str)
{
if (str == null)
return null;
if (str.Length == 0)
return new byte[0];
System.IO.MemoryStream result = new System.IO.MemoryStream(str.Length);
foreach (char c in str)
{
HttpEncoder.UrlEncodeChar(c, result, true);
}
return result.ToArray();
}
/// <summary>
/// Decodes an HTML-encoded string and returns the decoded string.
/// </summary>
/// <param name="s">The HTML string to decode. </param>
/// <returns>The decoded text.</returns>
public static string HtmlDecode(string s)
{
#if NET_4_0
if (s == null)
return null;
using (var sw = new StringWriter ()) {
HttpEncoder.Current.HtmlDecode (s, sw);
return sw.ToString ();
}
#else
return HttpEncoder.HtmlDecode(s);
#endif
}
/// <summary>
/// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream.
/// </summary>
/// <param name="s">The HTML string to decode</param>
/// <param name="output">The TextWriter output stream containing the decoded string. </param>
/*
public static void HtmlDecode(string s, TextWriter output)
{
if (output == null)
{
#if NET_4_0
throw new ArgumentNullException ("output");
#else
throw new NullReferenceException(".NET emulation");
#endif
}
if (!String.IsNullOrEmpty(s))
{
#if NET_4_0
HttpEncoder.Current.HtmlDecode (s, output);
#else
output.Write(HttpEncoder.HtmlDecode(s));
#endif
}
}
*/
public static string HtmlEncode(string s)
{
#if NET_4_0
if (s == null)
return null;
using (var sw = new StringWriter ()) {
HttpEncoder.Current.HtmlEncode (s, sw);
return sw.ToString ();
}
#else
return HttpEncoder.HtmlEncode(s);
#endif
}
/// <summary>
/// HTML-encodes a string and sends the resulting output to a TextWriter output stream.
/// </summary>
/// <param name="s">The string to encode. </param>
/// <param name="output">The TextWriter output stream containing the encoded string. </param>
/*
public static void HtmlEncode(string s, TextWriter output)
{
if (output == null)
{
#if NET_4_0
throw new ArgumentNullException ("output");
#else
throw new NullReferenceException(".NET emulation");
#endif
}
if (!String.IsNullOrEmpty(s))
{
#if NET_4_0
HttpEncoder.Current.HtmlEncode (s, output);
#else
output.Write(HttpEncoder.HtmlEncode(s));
#endif
}
}
*/
#if NET_4_0
public static string HtmlEncode (object value)
{
if (value == null)
return null;
IHtmlString htmlString = value as IHtmlString;
if (htmlString != null)
return htmlString.ToHtmlString ();
return HtmlEncode (value.ToString ());
}
public static string JavaScriptStringEncode (string value)
{
return JavaScriptStringEncode (value, false);
}
public static string JavaScriptStringEncode (string value, bool addDoubleQuotes)
{
if (String.IsNullOrEmpty (value))
return addDoubleQuotes ? "\"\"" : String.Empty;
int len = value.Length;
bool needEncode = false;
char c;
for (int i = 0; i < len; i++) {
c = value [i];
if (c >= 0 && c <= 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92) {
needEncode = true;
break;
}
}
if (!needEncode)
return addDoubleQuotes ? "\"" + value + "\"" : value;
var sb = new StringBuilder ();
if (addDoubleQuotes)
sb.Append ('"');
for (int i = 0; i < len; i++) {
c = value [i];
if (c >= 0 && c <= 7 || c == 11 || c >= 14 && c <= 31 || c == 39 || c == 60 || c == 62)
sb.AppendFormat ("\\u{0:x4}", (int)c);
else switch ((int)c) {
case 8:
sb.Append ("\\b");
break;
case 9:
sb.Append ("\\t");
break;
case 10:
sb.Append ("\\n");
break;
case 12:
sb.Append ("\\f");
break;
case 13:
sb.Append ("\\r");
break;
case 34:
sb.Append ("\\\"");
break;
case 92:
sb.Append ("\\\\");
break;
default:
sb.Append (c);
break;
}
}
if (addDoubleQuotes)
sb.Append ('"');
return sb.ToString ();
}
#endif
public static string UrlPathEncode(string s)
{
#if NET_4_0
return HttpEncoder.Current.UrlPathEncode (s);
#else
return HttpEncoder.UrlPathEncode(s);
#endif
}
public static NameValueCollection ParseQueryString(string query)
{
return ParseQueryString(query, Encoding.UTF8);
}
public static NameValueCollection ParseQueryString(string query, Encoding encoding)
{
if (query == null)
throw new ArgumentNullException("query");
if (encoding == null)
throw new ArgumentNullException("encoding");
if (query.Length == 0 || (query.Length == 1 && query[0] == '?'))
return new NameValueCollection();
if (query[0] == '?')
query = query.Substring(1);
NameValueCollection result = new HttpQSCollection();
ParseQueryString(query, encoding, result);
return result;
}
internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result)
{
if (query.Length == 0)
return;
string decoded = HtmlDecode(query);
int decodedLength = decoded.Length;
int namePos = 0;
bool first = true;
while (namePos <= decodedLength)
{
int valuePos = -1, valueEnd = -1;
for (int q = namePos; q < decodedLength; q++)
{
if (valuePos == -1 && decoded[q] == '=')
{
valuePos = q + 1;
}
else if (decoded[q] == '&')
{
valueEnd = q;
break;
}
}
if (first)
{
first = false;
if (decoded[namePos] == '?')
namePos++;
}
string name, value;
if (valuePos == -1)
{
name = null;
valuePos = namePos;
}
else
{
name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding);
}
if (valueEnd < 0)
{
namePos = -1;
valueEnd = decoded.Length;
}
else
{
namePos = valueEnd + 1;
}
value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding);
result.Add(name, value);
if (namePos == -1)
break;
}
}
#endregion // Methods
}
}
@@ -150,7 +150,7 @@ namespace Barotrauma
return Color.Black; return Color.Black;
} }
if (t <= 0.0f) { return gradient[0]; } if (t <= 0.0f || !MathUtils.IsValid(t)) { return gradient[0]; }
if (t >= 1.0f) { return gradient[gradient.Length - 1]; } if (t >= 1.0f) { return gradient[gradient.Length - 1]; }
float scaledT = t * (gradient.Length - 1); float scaledT = t * (gradient.Length - 1);
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.10.6.0</Version> <Version>0.1100.0.4</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.10.6.0</Version> <Version>0.1100.0.4</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.10.6.0</Version> <Version>0.1100.0.4</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.10.6.0</Version> <Version>0.1100.0.4</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.10.6.0</Version> <Version>0.1100.0.4</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -420,6 +420,7 @@ namespace Barotrauma
if (writeStatus) if (writeStatus)
{ {
WriteStatus(tempBuffer); WriteStatus(tempBuffer);
(AIController as EnemyAIController)?.PetBehavior?.ServerWrite(tempBuffer);
HealthUpdatePending = false; HealthUpdatePending = false;
} }
@@ -2155,6 +2155,43 @@ namespace Barotrauma
} }
); );
commands.Add(new Command("readycheck", "Commence a ready check.", (string[] args) =>
{
if (Screen.Selected == GameMain.GameScreen && GameMain.NetworkMember != null)
{
CrewManager crewManager = GameMain.GameSession?.CrewManager;
if (crewManager != null && crewManager.ActiveReadyCheck == null)
{
ReadyCheck.StartReadyCheck("");
NewMessage("Attempted to commence a ready check.", Color.Green);
return;
}
NewMessage("A ready check is already running.", Color.Red);
return;
}
NewMessage("Ready checks cannot be commenced in the lobby.", Color.Red);
}));
AssignOnClientRequestExecute(
"readycheck",
(senderClient, cursorWorldPos, args) =>
{
if (Screen.Selected == GameMain.GameScreen && GameMain.NetworkMember != null && !(GameMain.GameSession?.GameMode?.IsSinglePlayer ?? true))
{
CrewManager crewManager = GameMain.GameSession?.CrewManager;
if (crewManager != null && crewManager.ActiveReadyCheck == null)
{
ReadyCheck.StartReadyCheck(senderClient.Name, senderClient);
GameMain.Server.SendConsoleMessage("Attempted to commence a ready check.", senderClient);
return;
}
GameMain.Server.SendConsoleMessage("A ready check is already running.", senderClient);
return;
}
GameMain.Server.SendConsoleMessage("Ready checks cannot be commenced in the lobby.", senderClient);
}
);
#if DEBUG #if DEBUG
commands.Add(new Command("spamevents", "A debug command that creates a ton of entity events.", (string[] args) => commands.Add(new Command("spamevents", "A debug command that creates a ton of entity events.", (string[] args) =>
{ {
@@ -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 ServerWriteInitial(IWriteMessage msg, Client c)
{
return;
}
}
}
@@ -10,7 +10,7 @@ namespace Barotrauma
foreach (Item item in items) foreach (Item item in items)
{ {
item.WriteSpawnData(msg, item.WriteSpawnData(msg,
item.OriginalID, item.ID,
parentInventoryIDs.ContainsKey(item) ? parentInventoryIDs[item] : Entity.NullEntityID, parentInventoryIDs.ContainsKey(item) ? parentInventoryIDs[item] : Entity.NullEntityID,
parentItemContainerIndices.ContainsKey(item) ? parentItemContainerIndices[item] : (byte)0); parentItemContainerIndices.ContainsKey(item) ? parentItemContainerIndices[item] : (byte)0);
} }
@@ -0,0 +1,31 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class MineralMission : Mission
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
foreach (var kvp in SpawnedResources)
{
msg.Write((byte)kvp.Value.Count);
var rotation = ResourceClusters[kvp.Key].Second;
msg.Write(rotation);
foreach (var r in kvp.Value)
{
r.WriteSpawnData(msg, r.ID, Entity.NullEntityID, 0);
}
}
foreach (var kvp in RelevantLevelResources)
{
msg.Write(kvp.Key);
msg.Write((byte)kvp.Value.Length);
foreach (var i in kvp.Value)
{
msg.Write(i.ID);
}
}
}
}
}
@@ -15,7 +15,7 @@ namespace Barotrauma
msg.Write((byte)monsters.Count); msg.Write((byte)monsters.Count);
foreach (Character monster in monsters) foreach (Character monster in monsters)
{ {
monster.WriteSpawnData(msg, monster.OriginalID, restrictMessageSize: false); monster.WriteSpawnData(msg, monster.ID, restrictMessageSize: false);
} }
} }
} }
@@ -0,0 +1,18 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class NestMission : Mission
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
msg.Write(nestPosition.X);
msg.Write(nestPosition.Y);
msg.Write((ushort)items.Count);
foreach (Item item in items)
{
item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0);
}
}
}
}

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