Unstable 0.1400.1.0

This commit is contained in:
Markus Isberg
2021-05-20 16:12:54 +03:00
parent 92f0264af2
commit 5bc850cddb
181 changed files with 2475 additions and 1588 deletions
@@ -39,10 +39,7 @@ namespace Barotrauma
partial void DamageParticles(float deltaTime, Vector2 worldPosition) partial void DamageParticles(float deltaTime, Vector2 worldPosition)
{ {
if (particleEmitter != null) particleEmitter?.Emit(deltaTime, worldPosition);
{
particleEmitter.Emit(deltaTime, worldPosition);
}
if (sound != null) if (sound != null)
{ {
@@ -201,7 +201,7 @@ namespace Barotrauma
/// </summary> /// </summary>
public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true) public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
{ {
if (DisableControls || GUI.PauseMenuOpen || GUI.SettingsMenuOpen) if (DisableControls || GUI.InputBlockingMenuOpen)
{ {
foreach (Key key in keys) foreach (Key key in keys)
{ {
@@ -323,7 +323,7 @@ namespace Barotrauma
DoInteractionUpdate(deltaTime, mouseSimPos); DoInteractionUpdate(deltaTime, mouseSimPos);
} }
if (!GUI.PauseMenuOpen && !GUI.SettingsMenuOpen) if (!GUI.InputBlockingMenuOpen)
{ {
if (SelectedConstruction != null && if (SelectedConstruction != null &&
(SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)) || (SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)) ||
@@ -515,6 +515,7 @@ namespace Barotrauma
if (draggingItemToWorld) if (draggingItemToWorld)
{ {
if (item.OwnInventory == null || if (item.OwnInventory == null ||
!item.OwnInventory.Container.AllowDragAndDrop ||
!item.OwnInventory.CanBePut(CharacterInventory.DraggingItems.First()) || !item.OwnInventory.CanBePut(CharacterInventory.DraggingItems.First()) ||
!CanAccessInventory(item.OwnInventory)) !CanAccessInventory(item.OwnInventory))
{ {
@@ -679,7 +680,7 @@ namespace Barotrauma
else else
{ {
//Ideally it shouldn't send the character entirely if we can't see them but /shrug, this isn't the most hacker-proof game atm //Ideally it shouldn't send the character entirely if we can't see them but /shrug, this isn't the most hacker-proof game atm
hudInfoVisible = controlled.CanSeeCharacter(this, controlled.ViewTarget == null ? controlled.WorldPosition : controlled.ViewTarget.WorldPosition); hudInfoVisible = controlled.CanSeeTarget(this, controlled.ViewTarget);
} }
hudInfoTimer = Rand.Range(0.5f, 1.0f); hudInfoTimer = Rand.Range(0.5f, 1.0f);
} }
@@ -302,9 +302,9 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
//read anyway to prevent messing up reading the rest of the message //read anyway to prevent messing up reading the rest of the message
UInt16 lastEventID = msg.ReadUInt16(); _ = msg.ReadUInt16();
byte itemCount = msg.ReadByte(); byte inventoryItemCount = msg.ReadByte();
for (int i = 0; i < itemCount; i++) for (int i = 0; i < inventoryItemCount; i++)
{ {
msg.ReadUInt16(); msg.ReadUInt16();
} }
@@ -411,8 +411,11 @@ namespace Barotrauma
string option = null; string option = null;
if (orderPrefab.HasOptions) if (orderPrefab.HasOptions)
{ {
int optionIndex = msg.ReadRangedInteger(0, orderPrefab.Options.Length); int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length);
option = orderPrefab.Options[optionIndex]; if (optionIndex > -1)
{
option = orderPrefab.Options[optionIndex];
}
} }
GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option); GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option);
} }
@@ -431,9 +434,18 @@ namespace Barotrauma
break; break;
case 9: //NetEntityEvent.Type.AddToCrew case 9: //NetEntityEvent.Type.AddToCrew
GameMain.GameSession.CrewManager.AddCharacter(this); GameMain.GameSession.CrewManager.AddCharacter(this);
foreach (Item item in Inventory.AllItems) CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte();
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)
{ {
ushort itemID = msg.ReadUInt16();
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
item.AllowStealing = true; item.AllowStealing = true;
var wifiComponent = item.GetComponent<Items.Components.WifiComponent>();
if (wifiComponent != null)
{
wifiComponent.TeamID = teamID;
}
} }
break; break;
} }
@@ -676,8 +676,8 @@ namespace Barotrauma
bool inWater = Character.AnimController.InWater; bool inWater = Character.AnimController.InWater;
var drawTarget = inWater ? Particles.ParticlePrefab.DrawTargetType.Water : Particles.ParticlePrefab.DrawTargetType.Air; var drawTarget = inWater ? Particles.ParticlePrefab.DrawTargetType.Water : Particles.ParticlePrefab.DrawTargetType.Air;
var emitter = Character.BloodEmitters.FirstOrDefault(e => e.Prefab.ParticlePrefab.DrawTarget == drawTarget || e.Prefab.ParticlePrefab.DrawTarget == Particles.ParticlePrefab.DrawTargetType.Both); var emitter = Character.BloodEmitters.FirstOrDefault(e => e.Prefab.ParticlePrefab.DrawTarget == drawTarget || e.Prefab.ParticlePrefab.DrawTarget == Particles.ParticlePrefab.DrawTargetType.Both);
float particleMinScale = emitter != null ? emitter.Prefab.ScaleMin : 0.5f; float particleMinScale = emitter?.Prefab.Properties.ScaleMin ?? 0.5f;
float particleMaxScale = emitter != null ? emitter.Prefab.ScaleMax : 1; float particleMaxScale = emitter?.Prefab.Properties.ScaleMax ?? 1;
float severity = Math.Min(affliction.Strength / affliction.Prefab.MaxStrength * Character.Params.BleedParticleMultiplier, 1); float severity = Math.Min(affliction.Strength / affliction.Prefab.MaxStrength * Character.Params.BleedParticleMultiplier, 1);
float bloodParticleSize = MathHelper.Lerp(particleMinScale, particleMaxScale, severity); float bloodParticleSize = MathHelper.Lerp(particleMinScale, particleMaxScale, severity);
if (!inWater) if (!inWater)
@@ -1326,7 +1326,7 @@ namespace Barotrauma
child.Recalculate(); child.Recalculate();
} }
if (buttonToSelect != null) { buttonToSelect.OnClicked(buttonToSelect, "selectaffliction"); } buttonToSelect?.OnClicked(buttonToSelect, "selectaffliction");
afflictionIconContainer.RecalculateChildren(); afflictionIconContainer.RecalculateChildren();
@@ -1395,9 +1395,9 @@ namespace Barotrauma
// omega nesting incoming // omega nesting incoming
if (fabricationRecipe != null) if (fabricationRecipe != null)
{ {
foreach (Tuple<string, PriceInfo> itemLocationPrice in itemPrefab.GetSellPricesOver(0)) foreach (KeyValuePair<string, PriceInfo> itemLocationPrice in itemPrefab.GetSellPricesOver(0))
{ {
NewMessage(" If bought at " + itemLocationPrice.Item1 + " it costs " + itemLocationPrice.Item2.Price); NewMessage(" If bought at " + itemLocationPrice.Key + " it costs " + itemLocationPrice.Value.Price);
int totalPrice = 0; int totalPrice = 0;
int? totalBestPrice = 0; int? totalBestPrice = 0;
foreach (var ingredient in fabricationRecipe.RequiredItems) foreach (var ingredient in fabricationRecipe.RequiredItems)
@@ -1408,15 +1408,15 @@ namespace Barotrauma
totalPrice += ingredientItemPrefab.DefaultPrice.Price; totalPrice += ingredientItemPrefab.DefaultPrice.Price;
totalBestPrice += ingredientItemPrefab.GetMinPrice(); totalBestPrice += ingredientItemPrefab.GetMinPrice();
int basePrice = ingredientItemPrefab.DefaultPrice.Price; int basePrice = ingredientItemPrefab.DefaultPrice.Price;
foreach (Tuple<string, PriceInfo> ingredientItemLocationPrice in ingredientItemPrefab.GetBuyPricesUnder()) foreach (KeyValuePair<string, PriceInfo> ingredientItemLocationPrice in ingredientItemPrefab.GetBuyPricesUnder())
{ {
if (basePrice > ingredientItemLocationPrice.Item2.Price) if (basePrice > ingredientItemLocationPrice.Value.Price)
{ {
NewMessage(" Location " + ingredientItemLocationPrice.Item1 + " sells ingredient " + ingredientItemPrefab.Name + " for cheaper, " + ingredientItemLocationPrice.Item2.Price, Color.Yellow); NewMessage(" Location " + ingredientItemLocationPrice.Key + " sells ingredient " + ingredientItemPrefab.Name + " for cheaper, " + ingredientItemLocationPrice.Value.Price, Color.Yellow);
} }
else else
{ {
NewMessage(" Location " + ingredientItemLocationPrice.Item1 + " sells ingredient " + ingredientItemPrefab.Name + " for more, " + ingredientItemLocationPrice.Item2.Price, Color.Teal); NewMessage(" Location " + ingredientItemLocationPrice.Key + " sells ingredient " + ingredientItemPrefab.Name + " for more, " + ingredientItemLocationPrice.Value.Price, Color.Teal);
} }
} }
} }
@@ -1426,11 +1426,15 @@ namespace Barotrauma
if (totalBestPrice.HasValue) if (totalBestPrice.HasValue)
{ {
int? bestDifference = itemLocationPrice.Item2.Price - totalBestPrice; int? bestDifference = itemLocationPrice.Value.Price - totalBestPrice;
NewMessage(" Constructing the item from store-bought items provides " + bestDifference + " profit with best-case scenario values."); NewMessage(" Constructing the item from store-bought items provides " + bestDifference + " profit with best-case scenario values.");
} }
} }
} }
},
() =>
{
return new string[][] { ItemPrefab.Prefabs.SelectMany(p => p.Aliases).Concat(ItemPrefab.Prefabs.Select(p => p.Identifier)).ToArray() };
}, isCheat: false)); }, isCheat: false));
commands.Add(new Command("checkcraftingexploits", "checkcraftingexploits: Finds outright item exploits created by buying store-bought ingredients and constructing them into sellable items.", (string[] args) => commands.Add(new Command("checkcraftingexploits", "checkcraftingexploits: Finds outright item exploits created by buying store-bought ingredients and constructing them into sellable items.", (string[] args) =>
@@ -55,6 +55,12 @@ namespace Barotrauma
{ {
Debug.Assert(actionInstance == null || actionId == null); Debug.Assert(actionInstance == null || actionId == null);
if (GUI.InputBlockingMenuOpen)
{
if (actionId.HasValue) { SendIgnore(actionId.Value); }
return;
}
shouldFadeToBlack = fadeToBlack; shouldFadeToBlack = fadeToBlack;
if (lastMessageBox != null && !lastMessageBox.Closed && GUIMessageBox.MessageBoxes.Contains(lastMessageBox)) if (lastMessageBox != null && !lastMessageBox.Closed && GUIMessageBox.MessageBoxes.Contains(lastMessageBox))
@@ -368,6 +374,15 @@ namespace Barotrauma
GameMain.Client?.ClientPeer?.Send(outmsg, DeliveryMethod.Reliable); GameMain.Client?.ClientPeer?.Send(outmsg, DeliveryMethod.Reliable);
} }
private static void SendIgnore(UInt16 actionId)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ClientPacketHeader.EVENTMANAGER_RESPONSE);
outmsg.Write(actionId);
outmsg.Write(byte.MaxValue);
GameMain.Client?.ClientPeer?.Send(outmsg, DeliveryMethod.Reliable);
}
// Too broken, left it here if I ever want to come back to it // Too broken, left it here if I ever want to come back to it
private static List<RichTextData> GetQuoteHighlights(string text, Color color) private static List<RichTextData> GetQuoteHighlights(string text, Color color)
{ {
@@ -22,6 +22,13 @@ namespace Barotrauma
public override void ClientReadInitial(IReadMessage msg) public override void ClientReadInitial(IReadMessage msg)
{ {
ushort targetItemCount = msg.ReadUInt16();
for (int i = 0; i < targetItemCount; i++)
{
var item = Item.ReadSpawnData(msg);
items.Add(item);
}
byte characterCount = msg.ReadByte(); byte characterCount = msg.ReadByte();
for (int i = 0; i < characterCount; i++) for (int i = 0; i < characterCount; i++)
@@ -10,7 +10,12 @@ namespace Barotrauma
for (int i = 0; i < characterCount; i++) for (int i = 0; i < characterCount; i++)
{ {
characters.Add(Character.ReadSpawnData(msg)); Character character = Character.ReadSpawnData(msg);
characters.Add(character);
if (msg.ReadBoolean())
{
terroristCharacters.Add(character);
}
ushort itemCount = msg.ReadUInt16(); ushort itemCount = msg.ReadUInt16();
for (int j = 0; j < itemCount; j++) for (int j = 0; j < itemCount; j++)
{ {
@@ -1,18 +0,0 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class OutpostDestroyMission : AbandonedOutpostMission
{
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)
{
var item = Item.ReadSpawnData(msg);
items.Add(item);
}
}
}
}
@@ -208,6 +208,19 @@ namespace Barotrauma
get { return pauseMenuOpen; } get { return pauseMenuOpen; }
} }
public static bool InputBlockingMenuOpen
{
get
{
return PauseMenuOpen ||
SettingsMenuOpen ||
DebugConsole.IsOpen ||
GameSession.IsTabMenuOpen ||
(GameMain.GameSession?.GameMode?.Paused ?? false) ||
CharacterHUD.IsCampaignInterfaceOpen;
}
}
public static bool PreventPauseMenuToggle = false; public static bool PreventPauseMenuToggle = false;
public static Color ScreenOverlayColor public static Color ScreenOverlayColor
@@ -2144,7 +2157,7 @@ namespace Barotrauma
for (int j = i + 1; j < elements.Count; j++) for (int j = i + 1; j < elements.Count; j++)
{ {
Rectangle rect2 = elements[j].Rect; Rectangle rect2 = elements[j].Rect;
if (!rect1.Intersects(rect2)) continue; if (!rect1.Intersects(rect2)) { continue; }
intersections = true; intersections = true;
Point centerDiff = rect1.Center - rect2.Center; Point centerDiff = rect1.Center - rect2.Center;
@@ -2173,10 +2186,10 @@ namespace Barotrauma
elements[j].RectTransform.ScreenSpaceOffset += moveAmount2.ToPoint(); elements[j].RectTransform.ScreenSpaceOffset += moveAmount2.ToPoint();
} }
if (disallowedAreas == null) continue; if (disallowedAreas == null) { continue; }
foreach (Rectangle rect2 in disallowedAreas) foreach (Rectangle rect2 in disallowedAreas)
{ {
if (!rect1.Intersects(rect2)) continue; if (!rect1.Intersects(rect2)) { continue; }
intersections = true; intersections = true;
Point centerDiff = rect1.Center - rect2.Center; Point centerDiff = rect1.Center - rect2.Center;
@@ -2194,7 +2207,7 @@ namespace Barotrauma
iterations++; iterations++;
} }
Vector2 ClampMoveAmount(Rectangle Rect, Rectangle clampTo, Vector2 moveAmount) static Vector2 ClampMoveAmount(Rectangle Rect, Rectangle clampTo, Vector2 moveAmount)
{ {
if (Rect.Y < clampTo.Y) if (Rect.Y < clampTo.Y)
{ {
@@ -201,7 +201,7 @@ namespace Barotrauma
{ {
base.ApplyStyle(style); base.ApplyStyle(style);
if (frame != null) { frame.ApplyStyle(style); } frame?.ApplyStyle(style);
} }
public override void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectInflate = null) public override void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectInflate = null)
@@ -551,7 +551,7 @@ namespace Barotrauma
} }
if (openState >= 2.0f) if (openState >= 2.0f)
{ {
if (Parent != null) { Parent.RemoveChild(this); } Parent?.RemoveChild(this);
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); } if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
} }
} }
@@ -604,7 +604,7 @@ namespace Barotrauma
} }
else else
{ {
if (Parent != null) { Parent.RemoveChild(this); } Parent?.RemoveChild(this);
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); } if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
} }
@@ -658,10 +658,7 @@ namespace Barotrauma
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData); currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData);
} }
if (Strikethrough != null) Strikethrough?.Draw(spriteBatch, (int)Math.Ceiling(TextSize.X / 2f), pos.X, ForceUpperCase ? pos.Y : pos.Y + GUI.Scale * 2f);
{
Strikethrough.Draw(spriteBatch, (int)Math.Ceiling(TextSize.X / 2f), pos.X, ForceUpperCase ? pos.Y : pos.Y + GUI.Scale * 2f);
}
} }
if (overflowClipActive) if (overflowClipActive)
@@ -626,7 +626,6 @@ namespace Barotrauma
if (GameMain.Client == null) if (GameMain.Client == null)
{ {
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee); SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
RefreshSubmarineDisplay(true); RefreshSubmarineDisplay(true);
} }
else else
@@ -661,7 +660,6 @@ namespace Barotrauma
{ {
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine); GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0); SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
RefreshSubmarineDisplay(true); RefreshSubmarineDisplay(true);
} }
else else
@@ -228,7 +228,10 @@ namespace Barotrauma
var crewButton = createTabButton(InfoFrameTab.Crew, "crew"); var crewButton = createTabButton(InfoFrameTab.Crew, "crew");
var missionButton = createTabButton(InfoFrameTab.Mission, "mission"); if (!(GameMain.GameSession?.GameMode is TestGameMode))
{
createTabButton(InfoFrameTab.Mission, "mission");
}
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode) if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
{ {
@@ -903,44 +906,61 @@ namespace Barotrauma
infoFrame.ClearChildren(); infoFrame.ClearChildren();
GUIFrame missionFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox"); GUIFrame missionFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = (int)(0.0245f * missionFrame.Rect.Height); int padding = (int)(0.0245f * missionFrame.Rect.Height);
Location location = GameMain.GameSession.EndLocation != null ? GameMain.GameSession.EndLocation : GameMain.GameSession.StartLocation; GUIFrame missionFrameContent = new GUIFrame(new RectTransform(new Point(missionFrame.Rect.Width - padding * 2, missionFrame.Rect.Height - padding * 2), infoFrame.RectTransform, Anchor.Center), style: null);
Location location = GameMain.GameSession.EndLocation ?? GameMain.GameSession.StartLocation;
GUILayoutGroup locationInfoContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), missionFrameContent.RectTransform))
{
AbsoluteSpacing = GUI.IntScale(10)
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Name, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Type.Name, font: GUI.SubHeadingFont);
var biomeLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
TextManager.Get("Biome", fallBackTag: "location"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), biomeLabel.RectTransform), Level.Loaded.LevelData.Biome.DisplayName, textAlignment: Alignment.CenterRight);
var difficultyLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
TextManager.Get("LevelDifficulty"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), difficultyLabel.RectTransform), ((int)Level.Loaded.LevelData.Difficulty) + " %", textAlignment: Alignment.CenterRight);
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), missionFrameContent.RectTransform) { AbsoluteOffset = new Point(0, locationInfoContainer.Rect.Height + padding) }, style: "HorizontalLine")
{
CanBeFocused = false
};
int locationInfoYOffset = locationInfoContainer.Rect.Height + padding * 2;
Sprite portrait = location.Type.GetPortrait(location.PortraitId); Sprite portrait = location.Type.GetPortrait(location.PortraitId);
bool hasPortrait = portrait != null && portrait.SourceRect.Width > 0 && portrait.SourceRect.Height > 0; bool hasPortrait = portrait != null && portrait.SourceRect.Width > 0 && portrait.SourceRect.Height > 0;
int contentWidth = hasPortrait ? (int)(missionFrame.Rect.Width * 0.951f) : missionFrame.Rect.Width - padding * 2; int contentWidth = missionFrameContent.Rect.Width;
Vector2 locationNameSize = GUI.LargeFont.MeasureString(location.Name);
Vector2 locationTypeSize = GUI.SubHeadingFont.MeasureString(location.Name);
GUITextBlock locationNameText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationNameSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, padding) }, location.Name, font: GUI.LargeFont);
GUITextBlock locationTypeText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationTypeSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationNameText.Rect.Height + padding) }, location.Type.Name, font: GUI.SubHeadingFont);
int locationInfoYOffset = locationNameText.Rect.Height + locationTypeText.Rect.Height + padding * 2;
GUIListBox missionList;
if (hasPortrait) if (hasPortrait)
{ {
GUIFrame portraitHolder = new GUIFrame(new RectTransform(new Point(contentWidth, (int)(missionFrame.Rect.Height * 0.588f)), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height; float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height;
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(1.0f, 1f), portraitHolder.RectTransform), portrait, scaleToFit: true); GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(0.5f, 1f), locationInfoContainer.RectTransform, Anchor.CenterRight), portrait, scaleToFit: true)
portraitHolder.RectTransform.NonScaledSize = new Point(portraitImage.Rect.Size.X, (int)(portraitImage.Rect.Size.X / portraitAspectRatio)); {
IgnoreLayoutGroups = true
};
locationInfoContainer.Recalculate();
portraitImage.RectTransform.NonScaledSize = new Point(Math.Min((int)(portraitImage.Rect.Size.Y * portraitAspectRatio), portraitImage.Rect.Width), portraitImage.Rect.Size.Y);
}
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Bottom - portraitHolder.Rect.Bottom - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, portraitHolder.RectTransform.AbsoluteOffset.Y + portraitHolder.Rect.Height + padding) }); GUIListBox missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrameContent.Rect.Height - locationInfoYOffset), missionFrameContent.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
}
else
{
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Height - locationInfoYOffset - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
}
missionList.ContentBackground.Color = Color.Transparent; missionList.ContentBackground.Color = Color.Transparent;
missionList.Spacing = GUI.IntScale(15); missionList.Spacing = GUI.IntScale(15);
if (GameMain.GameSession?.Missions != null) if (GameMain.GameSession?.Missions != null)
{ {
int spacing = GUI.IntScale(5);
int iconSize = (int)(GUI.LargeFont.MeasureChar('T').Y + GUI.Font.MeasureChar('T').Y * 4 + spacing * 4);
foreach (Mission mission in GameMain.GameSession.Missions) foreach (Mission mission in GameMain.GameSession.Missions)
{ {
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null); GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft) GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(iconSize + spacing, 0) }, false, childAnchor: Anchor.TopLeft)
{ {
AbsoluteSpacing = GUI.IntScale(5) AbsoluteSpacing = spacing
}; };
string descriptionText = mission.Description; string descriptionText = mission.Description;
foreach (string missionMessage in mission.ShownMessages) foreach (string missionMessage in mission.ShownMessages)
@@ -974,12 +994,12 @@ namespace Barotrauma
if (mission.Prefab.Icon != null) if (mission.Prefab.Icon != null)
{ {
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height; /*float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X); int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio)); int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight); Point iconSize = new Point(iconWidth, iconHeight);*/
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) new GUIImage(new RectTransform(new Point(iconSize), missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
{ {
Color = mission.Prefab.IconColor, Color = mission.Prefab.IconColor,
HoverColor = mission.Prefab.IconColor, HoverColor = mission.Prefab.IconColor,
@@ -43,7 +43,7 @@ namespace Barotrauma
private readonly CampaignUI campaignUI; private readonly CampaignUI campaignUI;
private CampaignMode? Campaign => campaignUI.Campaign; private CampaignMode? Campaign => campaignUI.Campaign;
private int AvailableMoney => Campaign?.Money ?? 0; private int AvailableMoney => Campaign?.Money ?? 0;
private UpgradeTab selectedUpgradTab = UpgradeTab.Upgrade; private UpgradeTab selectedUpgradeTab = UpgradeTab.Upgrade;
private GUIMessageBox? currectConfirmation; private GUIMessageBox? currectConfirmation;
@@ -59,7 +59,7 @@ namespace Barotrauma
private GUIFrame? subPreviewFrame; private GUIFrame? subPreviewFrame;
private Submarine? drawnSubmarine; private Submarine? drawnSubmarine;
private readonly List<UpgradeCategory> applicableCategories = new List<UpgradeCategory>(); private readonly List<UpgradeCategory> applicableCategories = new List<UpgradeCategory>();
private Vector2[][] subHullVerticies = new Vector2[0][]; private Vector2[][] subHullVertices = new Vector2[0][];
private List<Structure> submarineWalls = new List<Structure>(); private List<Structure> submarineWalls = new List<Structure>();
public MapEntity? HoveredItem; public MapEntity? HoveredItem;
@@ -110,7 +110,7 @@ namespace Barotrauma
public void RefreshAll() public void RefreshAll()
{ {
switch (selectedUpgradTab) switch (selectedUpgradeTab)
{ {
case UpgradeTab.Repairs: case UpgradeTab.Repairs:
SelectTab(UpgradeTab.Repairs); SelectTab(UpgradeTab.Repairs);
@@ -215,7 +215,7 @@ namespace Barotrauma
*/ */
private void CreateUI(GUIComponent parent) private void CreateUI(GUIComponent parent)
{ {
selectedUpgradTab = UpgradeTab.Upgrade; selectedUpgradeTab = UpgradeTab.Upgrade;
parent.ClearChildren(); parent.ClearChildren();
ItemInfoFrame.ClearChildren(); ItemInfoFrame.ClearChildren();
@@ -259,8 +259,8 @@ namespace Barotrauma
GUIImage submarineIcon = new GUIImage(rectT(new Point(locationLayout.Rect.Height, locationLayout.Rect.Height), locationLayout), style: "SubmarineIcon", scaleToFit: true); GUIImage submarineIcon = new GUIImage(rectT(new Point(locationLayout.Rect.Height, locationLayout.Rect.Height), locationLayout), style: "SubmarineIcon", scaleToFit: true);
new GUITextBlock(rectT(1.0f - submarineIcon.RectTransform.RelativeSize.X, 1, locationLayout), TextManager.Get("UpgradeUI.Title"), font: GUI.LargeFont); new GUITextBlock(rectT(1.0f - submarineIcon.RectTransform.RelativeSize.X, 1, locationLayout), TextManager.Get("UpgradeUI.Title"), font: GUI.LargeFont);
categoryButtonLayout = new GUILayoutGroup(rectT(0.4f, 0.3f, leftLayout), isHorizontal: true) { Stretch = true }; categoryButtonLayout = new GUILayoutGroup(rectT(0.4f, 0.3f, leftLayout), isHorizontal: true) { Stretch = true };
GUIButton upgradeButton = new GUIButton(rectT(1, 1f, categoryButtonLayout), TextManager.Get("UICategory.Upgrades"), style: "GUITabButton") { UserData = UpgradeTab.Upgrade, Selected = selectedUpgradTab == UpgradeTab.Upgrade }; GUIButton upgradeButton = new GUIButton(rectT(1, 1f, categoryButtonLayout), TextManager.Get("UICategory.Upgrades"), style: "GUITabButton") { UserData = UpgradeTab.Upgrade, Selected = selectedUpgradeTab == UpgradeTab.Upgrade };
GUIButton repairButton = new GUIButton(rectT(1, 1f, categoryButtonLayout), TextManager.Get("UICategory.Maintenance"), style: "GUITabButton") { UserData = UpgradeTab.Repairs, Selected = selectedUpgradTab == UpgradeTab.Repairs }; GUIButton repairButton = new GUIButton(rectT(1, 1f, categoryButtonLayout), TextManager.Get("UICategory.Maintenance"), style: "GUITabButton") { UserData = UpgradeTab.Repairs, Selected = selectedUpgradeTab == UpgradeTab.Repairs };
/* RIGHT HEADER LAYOUT /* RIGHT HEADER LAYOUT
* |---------------------------------------------------------------------------------------------------| * |---------------------------------------------------------------------------------------------------|
@@ -282,15 +282,15 @@ namespace Barotrauma
{ {
if (o is UpgradeTab upgradeTab) if (o is UpgradeTab upgradeTab)
{ {
if (upgradeTab != selectedUpgradTab || currentStoreLayout == null || currentStoreLayout.Parent != storeLayout) if (upgradeTab != selectedUpgradeTab || currentStoreLayout == null || currentStoreLayout.Parent != storeLayout)
{ {
selectedUpgradTab = upgradeTab; selectedUpgradeTab = upgradeTab;
SelectTab(selectedUpgradTab); SelectTab(selectedUpgradeTab);
storeLayout?.Recalculate(); storeLayout?.Recalculate();
} }
repairButton.Selected = (UpgradeTab) repairButton.UserData == selectedUpgradTab; repairButton.Selected = (UpgradeTab) repairButton.UserData == selectedUpgradeTab;
upgradeButton.Selected = (UpgradeTab) upgradeButton.UserData == selectedUpgradTab; upgradeButton.Selected = (UpgradeTab) upgradeButton.UserData == selectedUpgradeTab;
return true; return true;
} }
@@ -306,6 +306,12 @@ namespace Barotrauma
SelectTab(UpgradeTab.Upgrade); SelectTab(UpgradeTab.Upgrade);
var itemSwapPreview = new GUICustomComponent(new RectTransform(new Vector2(0.27f, 0.4f), mainStoreLayout.RectTransform, Anchor.TopLeft) { RelativeOffset = new Vector2(GUI.IsFourByThree() ? 0.5f : 0.47f, 0.0f) }, DrawItemSwapPreview)
{
IgnoreLayoutGroups = true,
CanBeFocused = true
};
#if DEBUG #if DEBUG
// creates a button that re-creates the UI // creates a button that re-creates the UI
CreateRefreshButton(); CreateRefreshButton();
@@ -323,6 +329,35 @@ namespace Barotrauma
#endif #endif
} }
private void DrawItemSwapPreview(SpriteBatch spriteBatch, GUICustomComponent component)
{
var selectedItem = customizeTabOpen ?
activeItemSwapSlideDown?.UserData as Item ?? HoveredItem as Item :
HoveredItem as Item;
if (selectedItem?.Prefab.SwappableItem == null) { return; }
Sprite schematicsSprite = selectedItem.Prefab.SwappableItem.SchematicSprite;
if (schematicsSprite == null) { return; }
float schematicsScale = Math.Min(component.Rect.Width / 2 / schematicsSprite.size.X, component.Rect.Height / schematicsSprite.size.Y);
Vector2 center = new Vector2(component.Rect.Center.X, component.Rect.Center.Y);
schematicsSprite.Draw(spriteBatch, new Vector2(component.Rect.X, center.Y), GUI.Style.Green, new Vector2(0, schematicsSprite.size.Y / 2),
scale: schematicsScale);
var swappableItemList = selectedUpgradeCategoryLayout?.FindChild("prefablist", true) as GUIListBox;
var highlightedElement = swappableItemList?.Content.FindChild(c => c.UserData is ItemPrefab && c.IsParentOf(GUI.MouseOn)) ?? GUI.MouseOn;
ItemPrefab swapTo = highlightedElement?.UserData as ItemPrefab ?? selectedItem.PendingItemSwap;
if (swapTo?.SwappableItem == null) { return; }
Sprite? schematicsSprite2 = swapTo.SwappableItem?.SchematicSprite;
schematicsSprite2?.Draw(spriteBatch, new Vector2(component.Rect.Right, center.Y), GUI.Style.Orange, new Vector2(schematicsSprite2.size.X, schematicsSprite2.size.Y / 2),
scale: Math.Min(component.Rect.Width / 2 / schematicsSprite2.size.X, component.Rect.Height / schematicsSprite2.size.Y));
var arrowSprite = GUI.Style?.GetComponentStyle("GUIButtonToggleRight")?.GetDefaultSprite();
if (arrowSprite != null)
{
arrowSprite.Draw(spriteBatch, center, scale: GUI.Scale);
}
}
private void SelectTab(UpgradeTab tab) private void SelectTab(UpgradeTab tab)
{ {
if (currentStoreLayout != null) if (currentStoreLayout != null)
@@ -672,7 +707,9 @@ namespace Barotrauma
GUIFrame paddedFrame = new GUIFrame(rectT(0.93f, 0.9f, frame, Anchor.Center), style: null); GUIFrame paddedFrame = new GUIFrame(rectT(0.93f, 0.9f, frame, Anchor.Center), style: null);
bool hasSwappableItems = Submarine.MainSub.GetItems(true).Any(i => bool hasSwappableItems = Submarine.MainSub.GetItems(true).Any(i =>
i.Prefab.SwappableItem != null && (i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) && i.Prefab.SwappableItem != null &&
!i.HiddenInGame && i.AllowSwapping &&
(i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) &&
Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t))); Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t)));
float listHeight = hasSwappableItems ? 0.9f : 1.0f; float listHeight = hasSwappableItems ? 0.9f : 1.0f;
@@ -746,7 +783,7 @@ namespace Barotrauma
p is ItemPrefab itemPrefab && p is ItemPrefab itemPrefab &&
category.ItemTags.Any(t => itemPrefab.Tags.Contains(t)) && category.ItemTags.Any(t => itemPrefab.Tags.Contains(t)) &&
(itemPrefab.SwappableItem?.CanBeBought ?? false)).Cast<ItemPrefab>(); (itemPrefab.SwappableItem?.CanBeBought ?? false)).Cast<ItemPrefab>();
var entitiesOnSub = submarine.GetItems(true).Where(i => submarine.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t))).ToList(); var entitiesOnSub = submarine.GetItems(true).Where(i => submarine.IsEntityFoundOnThisSub(i, true) && !i.HiddenInGame && i.AllowSwapping && category.ItemTags.Any(t => i.HasTag(t))).ToList();
int slotIndex = 0; int slotIndex = 0;
foreach (Item item in entitiesOnSub) foreach (Item item in entitiesOnSub)
@@ -836,7 +873,7 @@ namespace Barotrauma
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation); int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation);
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description, frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
price, null, price, replacement,
addBuyButton: true, addBuyButton: true,
addProgressBar: false, addProgressBar: false,
buttonStyle: isPurchased ? "UpgradeBuyButton" : "StoreAddToCrateButton")); buttonStyle: isPurchased ? "UpgradeBuyButton" : "StoreAddToCrateButton"));
@@ -1191,7 +1228,7 @@ namespace Barotrauma
{ {
if (HoveredItem != item) { CreateItemTooltip(item); } if (HoveredItem != item) { CreateItemTooltip(item); }
HoveredItem = item; HoveredItem = item;
if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradTab == UpgradeTab.Upgrade && currentStoreLayout != null) if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradeTab == UpgradeTab.Upgrade && currentStoreLayout != null)
{ {
if (customizeTabOpen) if (customizeTabOpen)
{ {
@@ -1221,14 +1258,14 @@ namespace Barotrauma
// Every wall should have the same upgrades so we can just display the first one in the tooltip // Every wall should have the same upgrades so we can just display the first one in the tooltip
Structure? firstStructure = submarineWalls.FirstOrDefault(); Structure? firstStructure = submarineWalls.FirstOrDefault();
// use pnpoly algorithm to detect if our mouse is within any of the hull polygons // use pnpoly algorithm to detect if our mouse is within any of the hull polygons
if (subHullVerticies.Any(hullVertex => ToolBox.PointIntersectsWithPolygon(PlayerInput.MousePosition, hullVertex))) if (subHullVertices.Any(hullVertex => ToolBox.PointIntersectsWithPolygon(PlayerInput.MousePosition, hullVertex)))
{ {
if (HoveredItem != firstStructure && !(firstStructure is null)) { CreateItemTooltip(firstStructure); } if (HoveredItem != firstStructure && !(firstStructure is null)) { CreateItemTooltip(firstStructure); }
HoveredItem = firstStructure; HoveredItem = firstStructure;
isMouseOnStructure = true; isMouseOnStructure = true;
GUI.MouseCursor = CursorState.Hand; GUI.MouseCursor = CursorState.Hand;
if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradTab == UpgradeTab.Upgrade && currentStoreLayout != null) if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradeTab == UpgradeTab.Upgrade && currentStoreLayout != null)
{ {
ScrollToCategory(data => data.Category.IsWallUpgrade); ScrollToCategory(data => data.Category.IsWallUpgrade);
} }
@@ -1353,7 +1390,7 @@ namespace Barotrauma
Vector2 offset = (sub.WorldPosition - new Vector2(dockedBorders.Center.X, dockedBorders.Y - dockedBorders.Height / 2)) * scale; Vector2 offset = (sub.WorldPosition - new Vector2(dockedBorders.Center.X, dockedBorders.Y - dockedBorders.Height / 2)) * scale;
Vector2 center = parent.Rect.Center.ToVector2(); Vector2 center = parent.Rect.Center.ToVector2();
subHullVerticies = new Vector2[sub.HullVertices.Count][]; subHullVertices = new Vector2[sub.HullVertices.Count][];
for (int i = 0; i < sub.HullVertices.Count; i++) for (int i = 0; i < sub.HullVertices.Count; i++)
{ {
@@ -1367,7 +1404,7 @@ namespace Barotrauma
float angle = (float)Math.Atan2(edge.Y, edge.X); float angle = (float)Math.Atan2(edge.Y, edge.X);
Matrix rotate = Matrix.CreateRotationZ(angle); Matrix rotate = Matrix.CreateRotationZ(angle);
subHullVerticies[i] = new[] subHullVertices[i] = new[]
{ {
center + start + Vector2.Transform(new Vector2(length, -lineWidth), rotate), center + start + Vector2.Transform(new Vector2(length, -lineWidth), rotate),
center + end + Vector2.Transform(new Vector2(-length, -lineWidth), rotate), center + end + Vector2.Transform(new Vector2(-length, -lineWidth), rotate),
@@ -1379,7 +1416,7 @@ namespace Barotrauma
private void DrawSubmarine(SpriteBatch spriteBatch, GUICustomComponent component) private void DrawSubmarine(SpriteBatch spriteBatch, GUICustomComponent component)
{ {
foreach (Vector2[] hullVertex in subHullVerticies) foreach (Vector2[] hullVertex in subHullVertices)
{ {
// calculate the center point so we can draw a line from X to Y instead of drawing a rotated rectangle that is filled // calculate the center point so we can draw a line from X to Y instead of drawing a rotated rectangle that is filled
Vector2 point1 = hullVertex[1] + (hullVertex[2] - hullVertex[1]) / 2; Vector2 point1 = hullVertex[1] + (hullVertex[2] - hullVertex[1]) / 2;
@@ -951,10 +951,7 @@ namespace Barotrauma
Screen.Selected.AddToGUIUpdateList(); Screen.Selected.AddToGUIUpdateList();
if (Client != null) Client?.AddToGUIUpdateList();
{
Client.AddToGUIUpdateList();
}
SubmarinePreview.AddToGUIUpdateList(); SubmarinePreview.AddToGUIUpdateList();
@@ -984,10 +981,7 @@ namespace Barotrauma
} }
} }
if (NetworkMember != null) NetworkMember?.Update((float)Timing.Step);
{
NetworkMember.Update((float)Timing.Step);
}
GUI.Update((float)Timing.Step); GUI.Update((float)Timing.Step);
} }
@@ -1279,10 +1279,7 @@ namespace Barotrauma
//make the previously selected character wait in place for some time //make the previously selected character wait in place for some time
//(so they don't immediately start idling and walking away from their station) //(so they don't immediately start idling and walking away from their station)
var aiController = Character.Controlled?.AIController; var aiController = Character.Controlled?.AIController;
if (aiController != null) aiController?.Reset();
{
aiController.Reset();
}
DisableCommandUI(); DisableCommandUI();
Character.Controlled = character; Character.Controlled = character;
HintManager.OnChangeCharacter(); HintManager.OnChangeCharacter();
@@ -2009,7 +2006,10 @@ namespace Barotrauma
{ {
nodeConnectors = new GUICustomComponent( nodeConnectors = new GUICustomComponent(
new RectTransform(Vector2.One, commandFrame.RectTransform), new RectTransform(Vector2.One, commandFrame.RectTransform),
onDraw: DrawNodeConnectors); onDraw: DrawNodeConnectors)
{
CanBeFocused = false
};
nodeConnectors.SetAsFirstChild(); nodeConnectors.SetAsFirstChild();
background.SetAsFirstChild(); background.SetAsFirstChild();
} }
@@ -2018,10 +2018,27 @@ namespace Barotrauma
{ {
if (centerNode == null || optionNodes == null) { return; } if (centerNode == null || optionNodes == null) { return; }
var startNodePos = centerNode.Rect.Center.ToVector2(); var startNodePos = centerNode.Rect.Center.ToVector2();
// Don't draw connectors for mini map options or assignment nodes // Don't draw connectors for assignment nodes
if ((targetFrame == null || !targetFrame.Visible) && !(optionNodes.FirstOrDefault()?.Item1.UserData is Character)) if (!(optionNodes.FirstOrDefault()?.Item1.UserData is Character))
{ {
optionNodes.ForEach(n => DrawNodeConnector(startNodePos, centerNodeMargin, n.Item1, optionNodeMargin, spriteBatch)); // Regular option nodes
if (targetFrame == null || !targetFrame.Visible)
{
optionNodes.ForEach(n => DrawNodeConnector(startNodePos, centerNodeMargin, n.Item1, optionNodeMargin, spriteBatch));
}
// Minimap item nodes for single-option orders
else if(optionNodes.FirstOrDefault()?.Item1?.UserData is Tuple<Order, string> userData && string.IsNullOrEmpty(userData.Item2))
{
foreach (var node in optionNodes)
{
float iconRadius = 0.5f * optionNodeMargin;
Vector2 itemPosition = node.Item1.Parent.Rect.Center.ToVector2();
if (Vector2.Distance(node.Item1.Center, itemPosition) <= iconRadius) { continue; }
DrawNodeConnector(itemPosition, 0.0f, node.Item1, iconRadius, spriteBatch, widthMultiplier: 0.5f);
GUI.DrawFilledRectangle(spriteBatch, itemPosition - Vector2.One, new Vector2(3),
node.Item1.GetChildByUserData("colorsource")?.Color ?? Color.White);
}
}
} }
DrawNodeConnector(startNodePos, centerNodeMargin, returnNode, returnNodeMargin, spriteBatch); DrawNodeConnector(startNodePos, centerNodeMargin, returnNode, returnNodeMargin, spriteBatch);
if (shortcutCenterNode == null || !shortcutCenterNode.Visible) { return; } if (shortcutCenterNode == null || !shortcutCenterNode.Visible) { return; }
@@ -2030,7 +2047,7 @@ namespace Barotrauma
shortcutNodes.ForEach(n => DrawNodeConnector(startNodePos, shortcutCenterNodeMargin, n, shortcutNodeMargin, spriteBatch)); shortcutNodes.ForEach(n => DrawNodeConnector(startNodePos, shortcutCenterNodeMargin, n, shortcutNodeMargin, spriteBatch));
} }
private void DrawNodeConnector(Vector2 startNodePos, float startNodeMargin, GUIComponent endNode, float endNodeMargin, SpriteBatch spriteBatch) private void DrawNodeConnector(Vector2 startNodePos, float startNodeMargin, GUIComponent endNode, float endNodeMargin, SpriteBatch spriteBatch, float widthMultiplier = 1.0f)
{ {
if (endNode == null || !endNode.Visible) { return; } if (endNode == null || !endNode.Visible) { return; }
var endNodePos = endNode.Rect.Center.ToVector2(); var endNodePos = endNode.Rect.Center.ToVector2();
@@ -2041,11 +2058,11 @@ namespace Barotrauma
if ((selectedNode == null && endNode != shortcutCenterNode && GUI.IsMouseOn(endNode)) || if ((selectedNode == null && endNode != shortcutCenterNode && GUI.IsMouseOn(endNode)) ||
(isSelectionHighlighted && (endNode == selectedNode || (endNode == shortcutCenterNode && shortcutNodes.Any(n => GUI.IsMouseOn(n)))))) (isSelectionHighlighted && (endNode == selectedNode || (endNode == shortcutCenterNode && shortcutNodes.Any(n => GUI.IsMouseOn(n))))))
{ {
GUI.DrawLine(spriteBatch, start, end, colorSource != null ? colorSource.HoverColor : Color.White, width: 4); GUI.DrawLine(spriteBatch, start, end, colorSource?.HoverColor ?? Color.White, width: Math.Max(widthMultiplier * 4.0f, 1.0f));
} }
else else
{ {
GUI.DrawLine(spriteBatch, start, end, colorSource != null ? colorSource.Color : Color.White * nodeColorMultiplier, width: 2); GUI.DrawLine(spriteBatch, start, end, colorSource?.Color ?? Color.White * nodeColorMultiplier, width: Math.Max(widthMultiplier * 2.0f, 1.0f));
} }
} }
@@ -2122,7 +2139,12 @@ namespace Barotrauma
{ {
if (commandFrame == null) { return false; } if (commandFrame == null) { return false; }
RemoveOptionNodes(); RemoveOptionNodes();
if (targetFrame != null) { targetFrame.Visible = false; } if (targetFrame != null)
{
targetFrame.Visible = false;
nodeConnectors.RectTransform.Parent = commandFrame.RectTransform;
nodeConnectors.RectTransform.RepositionChildInHierarchy(1);
}
// TODO: Center node could move to option node instead of being removed // TODO: Center node could move to option node instead of being removed
commandFrame.RemoveChild(centerNode); commandFrame.RemoveChild(centerNode);
SetCenterNode(node); SetCenterNode(node);
@@ -2731,20 +2753,22 @@ namespace Barotrauma
if (itemTargetFrame == null) { continue; } if (itemTargetFrame == null) { continue; }
var anchor = Anchor.TopLeft; var anchor = Anchor.TopLeft;
if (itemTargetFrame.RectTransform.RelativeOffset.X < 0.5f && itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f) if (itemTargetFrame.RectTransform.RelativeOffset.X < 0.5f)
{ {
anchor = Anchor.BottomRight; if (itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
{
anchor = Anchor.BottomRight;
}
else
{
anchor = Anchor.TopRight;
}
} }
else if (itemTargetFrame.RectTransform.RelativeOffset.X > 0.5f && itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f) else if (itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
{ {
anchor = Anchor.BottomLeft; anchor = Anchor.BottomLeft;
} }
else if (itemTargetFrame.RectTransform.RelativeOffset.X < 0.5f && itemTargetFrame.RectTransform.RelativeOffset.Y > 0.5f)
{
anchor = Anchor.TopRight;
}
GUIComponent optionElement; GUIComponent optionElement;
if (order.Options.Length > 1) if (order.Options.Length > 1)
{ {
@@ -2807,7 +2831,6 @@ namespace Barotrauma
{ {
UserData = userData, UserData = userData,
Font = GUI.SmallFont, Font = GUI.SmallFont,
ToolTip = item?.Name ?? GetOrderNameBasedOnContextuality(order),
OnClicked = (_, userData) => OnClicked = (_, userData) =>
{ {
if (!CanIssueOrders) { return false; } if (!CanIssueOrders) { return false; }
@@ -2824,12 +2847,28 @@ namespace Barotrauma
var colorMultiplier = characters.Any(c => c.CurrentOrders.Any(o => o.Order != null && var colorMultiplier = characters.Any(c => c.CurrentOrders.Any(o => o.Order != null &&
o.Order.Identifier == userData.Item1.Identifier && o.Order.Identifier == userData.Item1.Identifier &&
o.Order.TargetEntity == userData.Item1.TargetEntity)) ? 0.5f : 1f; o.Order.TargetEntity == userData.Item1.TargetEntity)) ? 0.5f : 1f;
CreateNodeIcon(Vector2.One, optionElement.RectTransform, item.Prefab.MinimapIcon ?? order.SymbolSprite, order.Color * colorMultiplier); CreateNodeIcon(Vector2.One, optionElement.RectTransform, item.Prefab.MinimapIcon ?? order.SymbolSprite, order.Color * colorMultiplier, tooltip: item.Name);
optionNodes.Add(new Tuple<GUIComponent, Keys>(optionElement, Keys.None)); optionNodes.Add(new Tuple<GUIComponent, Keys>(optionElement, Keys.None));
} }
optionElements.Add(optionElement); optionElements.Add(optionElement);
} }
GUI.PreventElementOverlap(optionElements, clampArea: new Rectangle(10, 10, GameMain.GraphicsWidth - 20, GameMain.GraphicsHeight - 20));
Rectangle clampArea = new Rectangle(10, 10, GameMain.GraphicsWidth - 20, GameMain.GraphicsHeight - 20);
if (order.Identifier == "operateweapons")
{
Rectangle disallowedArea = targetFrame.GetChild<GUIFrame>().Rect;
Point originalSize = disallowedArea.Size;
disallowedArea.Size = disallowedArea.MultiplySize(0.9f);
disallowedArea.X += (originalSize.X - disallowedArea.Size.X) / 2;
disallowedArea.Y += (originalSize.Y - disallowedArea.Size.Y) / 2;
GUI.PreventElementOverlap(optionElements, new List<Rectangle>() { disallowedArea }, clampArea);
nodeConnectors.RectTransform.Parent = targetFrame.RectTransform;
nodeConnectors.RectTransform.SetAsFirstChild();
}
else
{
GUI.PreventElementOverlap(optionElements, clampArea: clampArea);
}
var shadow = new GUIFrame( var shadow = new GUIFrame(
new RectTransform(targetFrame.Rect.Size + new Point((int)(200 * GUI.Scale)), targetFrame.RectTransform, anchor: Anchor.Center), new RectTransform(targetFrame.Rect.Size + new Point((int)(200 * GUI.Scale)), targetFrame.RectTransform, anchor: Anchor.Center),
@@ -2901,6 +2940,12 @@ namespace Barotrauma
private bool CreateAssignmentNodes(GUIComponent node) private bool CreateAssignmentNodes(GUIComponent node)
{ {
if (centerNode == null)
{
DisableCommandUI();
return false;
}
var order = (node.UserData is Order) ? var order = (node.UserData is Order) ?
new Tuple<Order, string>(node.UserData as Order, null) : new Tuple<Order, string>(node.UserData as Order, null) :
node.UserData as Tuple<Order, string>; node.UserData as Tuple<Order, string>;
@@ -2947,6 +2992,8 @@ namespace Barotrauma
node = null; node = null;
} }
targetFrame.Visible = false; targetFrame.Visible = false;
nodeConnectors.RectTransform.Parent = commandFrame.RectTransform;
nodeConnectors.RectTransform.RepositionChildInHierarchy(1);
} }
if (shortcutCenterNode != null) if (shortcutCenterNode != null)
{ {
@@ -211,10 +211,7 @@ namespace Barotrauma
{ {
Character.Controlled = null; Character.Controlled = null;
if (prevControlled != null) prevControlled?.ClearInputs();
{
prevControlled.ClearInputs();
}
overlayColor = Color.LightGray; overlayColor = Color.LightGray;
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId); overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
@@ -535,7 +532,13 @@ namespace Barotrauma
msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex); msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex); msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
msg.Write(map.SelectedMissionIndex == -1 ? byte.MaxValue : (byte)map.SelectedMissionIndex);
var selectedMissionIndices = map.GetSelectedMissionIndices();
msg.Write((byte)selectedMissionIndices.Count());
foreach (int selectedMissionIndex in selectedMissionIndices)
{
msg.Write((byte)selectedMissionIndex);
}
msg.Write(PurchasedHullRepairs); msg.Write(PurchasedHullRepairs);
msg.Write(PurchasedItemRepairs); msg.Write(PurchasedItemRepairs);
msg.Write(PurchasedLostShuttles); msg.Write(PurchasedLostShuttles);
@@ -589,8 +592,15 @@ namespace Barotrauma
string mapSeed = msg.ReadString(); string mapSeed = msg.ReadString();
UInt16 currentLocIndex = msg.ReadUInt16(); UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16(); UInt16 selectedLocIndex = msg.ReadUInt16();
byte selectedMissionIndex = msg.ReadByte();
bool allowDebugTeleport = msg.ReadBoolean(); byte selectedMissionCount = msg.ReadByte();
List<int> selectedMissionIndices = new List<int>();
for (int i = 0; i < selectedMissionCount; i++)
{
selectedMissionIndices.Add(msg.ReadByte());
}
bool allowDebugTeleport = msg.ReadBoolean();
float? reputation = null; float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); } if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
@@ -717,7 +727,7 @@ namespace Barotrauma
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex); campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndex); campaign.Map.SelectMission(selectedMissionIndices);
campaign.Map.AllowDebugTeleport = allowDebugTeleport; campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems); campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.CargoManager.SetPurchasedItems(purchasedItems); campaign.CargoManager.SetPurchasedItems(purchasedItems);
@@ -266,10 +266,7 @@ namespace Barotrauma
prevControlled.AIController.Enabled = false; prevControlled.AIController.Enabled = false;
} }
Character.Controlled = null; Character.Controlled = null;
if (prevControlled != null) prevControlled?.ClearInputs();
{
prevControlled.ClearInputs();
}
GUI.DisableHUD = true; GUI.DisableHUD = true;
while (GameMain.Instance.LoadingScreenOpen) while (GameMain.Instance.LoadingScreenOpen)
@@ -305,7 +302,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration); overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration); timer = Math.Min(timer + CoroutineManager.UnscaledDeltaTime, textDuration);
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
var outpost = GameMain.GameSession.Level.StartOutpost; var outpost = GameMain.GameSession.Level.StartOutpost;
@@ -333,7 +330,7 @@ namespace Barotrauma
while (timer < fadeInDuration) while (timer < fadeInDuration)
{ {
overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration); overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
timer += CoroutineManager.DeltaTime; timer += CoroutineManager.UnscaledDeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
overlayColor = Color.Transparent; overlayColor = Color.Transparent;
@@ -224,10 +224,7 @@ namespace Barotrauma.Tutorials
public virtual void Update(float deltaTime) public virtual void Update(float deltaTime)
{ {
if (videoPlayer != null) videoPlayer?.Update();
{
videoPlayer.Update();
}
if (activeObjectives != null) if (activeObjectives != null)
{ {
@@ -218,12 +218,15 @@ namespace Barotrauma
}; };
List<Mission> missionsToDisplay = new List<Mission>(selectedMissions); List<Mission> missionsToDisplay = new List<Mission>(selectedMissions);
if (!selectedMissions.Any() && startLocation?.SelectedMission != null) if (!selectedMissions.Any() && startLocation != null)
{ {
if (startLocation.SelectedMission.Locations[0] == startLocation.SelectedMission.Locations[1] || foreach (Mission mission in startLocation.SelectedMissions)
startLocation.SelectedMission.Locations.Contains(campaignMode?.Map.SelectedLocation))
{ {
missionsToDisplay.Add(startLocation.SelectedMission); if (mission.Locations[0] == mission.Locations[1] ||
mission.Locations.Contains(campaignMode?.Map.SelectedLocation))
{
missionsToDisplay.Add(mission);
}
} }
} }
@@ -284,10 +287,11 @@ namespace Barotrauma
new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true); new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true);
} }
var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), missionContentHorizontal.RectTransform)) var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), missionContentHorizontal.RectTransform))
{ {
RelativeSpacing = 0.05f AbsoluteSpacing = GUI.IntScale(5)
}; };
missionContentHorizontal.Recalculate();
var missionNameTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), var missionNameTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
displayedMission.Name, font: GUI.SubHeadingFont); displayedMission.Name, font: GUI.SubHeadingFont);
if (displayedMission.Difficulty.HasValue) if (displayedMission.Difficulty.HasValue)
@@ -309,7 +313,7 @@ namespace Barotrauma
}; };
} }
} }
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
missionMessage, wrap: true, parseRichText: true); missionMessage, wrap: true, parseRichText: true);
int reward = displayedMission.GetReward(Submarine.MainSub); int reward = displayedMission.GetReward(Submarine.MainSub);
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && reward > 0) if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && reward > 0)
@@ -323,6 +327,13 @@ namespace Barotrauma
var spacing = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), missionList.Content.RectTransform) { MaxSize = new Point(int.MaxValue, GUI.IntScale(15)) }, style: null); var spacing = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), missionList.Content.RectTransform) { MaxSize = new Point(int.MaxValue, GUI.IntScale(15)) }, style: null);
new GUIFrame(new RectTransform(new Vector2(0.8f, 1.0f), spacing.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.1f, 0.0f) }, "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(0.8f, 1.0f), spacing.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.1f, 0.0f) }, "HorizontalLine");
} }
foreach (GUIComponent child in missionTextContent.Children)
{
child.RectTransform.IsFixedSize = true;
}
missionTextContent.RectTransform.MinSize = new Point(0, missionTextContent.Children.Sum(c => c.Rect.Height + missionTextContent.AbsoluteSpacing));
missionContentHorizontal.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Rect.Height / missionTextContent.RectTransform.RelativeSize.Y));
} }
if (!missionsToDisplay.Any()) if (!missionsToDisplay.Any())
@@ -39,43 +39,5 @@ namespace Barotrauma
} }
} }
} }
/// <summary>
/// Server has notified us that upgrades were reset.
/// </summary>
/// <param name="inc"></param>
/// <see cref="UpgradeManager.SendUpgradeResetMessage"/>
public void ClientRead(IReadMessage inc)
{
bool shouldReset = inc.ReadBoolean();
int money = inc.ReadInt32();
// uint length = inc.ReadUInt32();
//
// for (int i = 0; i < length; i++)
// {
// string key = inc.ReadString();
// byte value = inc.ReadByte();
// Metadata.SetValue(key, value);
// }
Campaign.Money = money;
if (shouldReset)
{
ResetUpgrades();
}
// spentMoney is local, so this message box should only appear for those who have spent money on upgrades
if (spentMoney > 0)
{
GUIMessageBox msgBox = new GUIMessageBox(TextManager.Get("UpgradeRefundTitle"), TextManager.Get("UpgradeRefundBody"), new [] { TextManager.Get("Ok") });
msgBox.Buttons[0].OnClicked += msgBox.Close;
}
spentMoney = 0;
PendingUpgrades.Clear();
PurchasedUpgrades.Clear();
CanUpgrade = false;
}
} }
} }
@@ -1326,24 +1326,41 @@ namespace Barotrauma
} }
}; };
GUITickBox disableInGameHintsBox = new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform), new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform), TextManager.Get("DisableInGameHints"))
TextManager.Get("DisableInGameHints"))
{ {
Selected = DisableInGameHints, Selected = DisableInGameHints,
ToolTip = TextManager.Get("DisableInGameHintsToolTip"), ToolTip = TextManager.Get("DisableInGameHintsToolTip"),
OnSelected = (tickBox) => OnSelected = (tickBox) =>
{ {
DisableInGameHints = tickBox.Selected; DisableInGameHints = tickBox.Selected;
if (!DisableInGameHints && GameMain.Config?.IgnoredHints != null)
{
// Reset the ignored hints when the hints are re-enabled (to-be-replaced by a separate button)
GameMain.Config.IgnoredHints.Clear();
}
UnsavedSettings = true; UnsavedSettings = true;
return true; return true;
} }
}; };
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
text: TextManager.Get("ResetInGameHints"),
style: "GUIButtonSmall")
{
OnClicked = (button, userData) =>
{
var msgBox = new GUIMessageBox(TextManager.Get("ResetInGameHints"),
TextManager.Get("ResetInGameHintsTooltip"),
new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
{
UserData = "verificationprompt"
};
msgBox.Buttons[0].OnClicked = (button, userData) =>
{
GameMain.Config.IgnoredHints.Clear();
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
return false;
}
};
GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true); GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true);
GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
style: "GUISlider", barSize: 0.1f) style: "GUISlider", barSize: 0.1f)
@@ -281,10 +281,10 @@ namespace Barotrauma.Items.Components
foreach (ParticleEmitter particleEmitter in particleEmitters) foreach (ParticleEmitter particleEmitter in particleEmitters)
{ {
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi); float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
float particleRange = particleEmitter.Prefab.VelocityMax * particleEmitter.Prefab.ParticlePrefab.LifeTime; float particleRange = particleEmitter.Prefab.Properties.VelocityMax * particleEmitter.Prefab.ParticlePrefab.LifeTime;
particleEmitter.Emit( particleEmitter.Emit(
deltaTime, particleStartPos, deltaTime, particleStartPos,
item.CurrentHull, particleAngle, particleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0, velocityMultiplier: dist / particleRange * 1.5f, item.CurrentHull, particleAngle, particleEmitter.Prefab.Properties.CopyEntityAngle ? -particleAngle : 0, velocityMultiplier: dist / particleRange * 1.5f,
colorMultiplier: new Color(color.R, color.G, color.B, (byte)255)); colorMultiplier: new Color(color.R, color.G, color.B, (byte)255));
} }
} }
@@ -16,44 +16,43 @@ namespace Barotrauma.Items.Components
public Vector2 DrawSize public Vector2 DrawSize
{ {
get { return new Vector2(light.Range * 2, light.Range * 2); } get { return new Vector2(Light.Range * 2, Light.Range * 2); }
} }
private LightSource light; public LightSource Light { get; }
public LightSource Light
{
get { return light; }
}
public override void OnScaleChanged() public override void OnScaleChanged()
{ {
light.SpriteScale = Vector2.One * item.Scale; Light.SpriteScale = Vector2.One * item.Scale;
light.Position = ParentBody != null ? ParentBody.Position : item.Position; Light.Position = ParentBody != null ? ParentBody.Position : item.Position;
} }
partial void SetLightSourceState(bool enabled, float brightness) partial void SetLightSourceState(bool enabled, float brightness)
{ {
if (light == null) { return; } if (Light == null) { return; }
light.Enabled = enabled; Light.Enabled = enabled;
light.Color = LightColor.Multiply(brightness); if (enabled)
{
Light.Color = LightColor.Multiply(brightness);
}
} }
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{ {
if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn) if (Light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn)
{ {
Vector2 origin = light.LightSprite.Origin; Vector2 origin = Light.LightSprite.Origin;
if ((light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = light.LightSprite.SourceRect.Width - origin.X; } if ((Light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = Light.LightSprite.SourceRect.Width - origin.X; }
if ((light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = light.LightSprite.SourceRect.Height - origin.Y; } if ((Light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = Light.LightSprite.SourceRect.Height - origin.Y; }
light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, origin, -light.Rotation, item.Scale, light.LightSpriteEffect, itemDepth - 0.0001f); Light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, origin, -Light.Rotation, item.Scale, Light.LightSpriteEffect, itemDepth - 0.0001f);
} }
} }
public override void FlipX(bool relativeToSub) public override void FlipX(bool relativeToSub)
{ {
if (light?.LightSprite != null && item.Prefab.CanSpriteFlipX && item.body == null) if (Light?.LightSprite != null && item.Prefab.CanSpriteFlipX && item.body == null)
{ {
light.LightSpriteEffect = light.LightSpriteEffect == SpriteEffects.None ? Light.LightSpriteEffect = Light.LightSpriteEffect == SpriteEffects.None ?
SpriteEffects.FlipHorizontally : SpriteEffects.None; SpriteEffects.FlipHorizontally : SpriteEffects.None;
} }
} }
@@ -93,7 +92,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific() protected override void RemoveComponentSpecific()
{ {
base.RemoveComponentSpecific(); base.RemoveComponentSpecific();
light.Remove(); Light.Remove();
} }
} }
} }
@@ -178,26 +178,26 @@ namespace Barotrauma.Items.Components
ToolTip = fi.TargetItem.Description ToolTip = fi.TargetItem.Description
}; };
var container = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform), var container = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform),
childAnchor: Anchor.CenterLeft, isHorizontal: true) { RelativeSpacing = 0.02f }; childAnchor: Anchor.CenterLeft, isHorizontal: true) { RelativeSpacing = 0.02f };
var itemIcon = fi.TargetItem.InventoryIcon ?? fi.TargetItem.sprite; var itemIcon = fi.TargetItem.InventoryIcon ?? fi.TargetItem.sprite;
if (itemIcon != null) if (itemIcon != null)
{ {
new GUIImage(new RectTransform(new Point(frame.Rect.Height,frame.Rect.Height), container.RectTransform), new GUIImage(new RectTransform(new Point(frame.Rect.Height,frame.Rect.Height), container.RectTransform),
itemIcon, scaleToFit: true) itemIcon, scaleToFit: true)
{ {
Color = fi.TargetItem.InventoryIconColor, Color = fi.TargetItem.InventoryIconColor,
ToolTip = fi.TargetItem.Description ToolTip = fi.TargetItem.Description
}; };
} }
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), container.RectTransform), GetRecipeNameAndAmount(fi)) new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), container.RectTransform), GetRecipeNameAndAmount(fi))
{ {
Padding = Vector4.Zero, Padding = Vector4.Zero,
AutoScaleVertical = true, AutoScaleVertical = true,
ToolTip = fi.TargetItem.Description ToolTip = fi.TargetItem.Description
}; };
} }
} }
@@ -367,6 +367,10 @@ namespace Barotrauma.Items.Components
{ {
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%"; toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
} }
else if (requiredItem.MaxCondition <= 0.0f)
{
toolTipText = TextManager.GetWithVariable("displayname.emptyitem", "[itemname]", toolTipText);
}
if (!string.IsNullOrEmpty(requiredItem.ItemPrefabs.First().Description)) if (!string.IsNullOrEmpty(requiredItem.ItemPrefabs.First().Description))
{ {
toolTipText += '\n' + requiredItem.ItemPrefabs.First().Description; toolTipText += '\n' + requiredItem.ItemPrefabs.First().Description;
@@ -33,10 +33,7 @@ namespace Barotrauma.Items.Components
base.Update(deltaTime, cam); base.Update(deltaTime, cam);
if (selectionUI != null) selectionUI?.Update();
{
selectionUI.Update();
}
} }
} }
} }
@@ -436,7 +436,9 @@ namespace Barotrauma.Items.Components
connectedSubUpdateTimer = ConnectedSubUpdateInterval; connectedSubUpdateTimer = ConnectedSubUpdateInterval;
} }
if (sonarView.Rect.Contains(PlayerInput.MousePosition)) Steering steering = item.GetComponent<Steering>();
if (sonarView.Rect.Contains(PlayerInput.MousePosition) &&
(GUI.MouseOn == null || GUI.MouseOn == sonarView || sonarView.IsParentOf(GUI.MouseOn) || GUI.MouseOn == steering?.GuiFrame || (steering?.GuiFrame?.IsParentOf(GUI.MouseOn) ?? false)))
{ {
float scrollSpeed = PlayerInput.ScrollWheelSpeed / 1000.0f; float scrollSpeed = PlayerInput.ScrollWheelSpeed / 1000.0f;
if (Math.Abs(scrollSpeed) > 0.0001f) if (Math.Abs(scrollSpeed) > 0.0001f)
@@ -613,7 +615,6 @@ namespace Barotrauma.Items.Components
} }
} }
Steering steering = item.GetComponent<Steering>();
if (steering != null && steering.DockingModeEnabled && steering.ActiveDockingSource != null) if (steering != null && steering.DockingModeEnabled && steering.ActiveDockingSource != null)
{ {
float dockingDist = Vector2.Distance(steering.ActiveDockingSource.Item.WorldPosition, steering.DockingTarget.Item.WorldPosition); float dockingDist = Vector2.Distance(steering.ActiveDockingSource.Item.WorldPosition, steering.DockingTarget.Item.WorldPosition);
@@ -780,10 +781,7 @@ namespace Barotrauma.Items.Components
DisplayRadius = (rect.Width / 2.0f) * (1.0f - displayBorderSize); DisplayRadius = (rect.Width / 2.0f) * (1.0f - displayBorderSize);
displayScale = DisplayRadius / range * zoom; displayScale = DisplayRadius / range * zoom;
if (screenBackground != null) screenBackground?.Draw(spriteBatch, center, 0.0f, rect.Width / screenBackground.size.X);
{
screenBackground.Draw(spriteBatch, center, 0.0f, rect.Width / screenBackground.size.X);
}
if (useDirectionalPing) if (useDirectionalPing)
{ {
@@ -871,10 +869,7 @@ namespace Barotrauma.Items.Components
GUI.DrawString(spriteBatch, rect.Location.ToVector2(), sonarBlips.Count.ToString(), Color.White); GUI.DrawString(spriteBatch, rect.Location.ToVector2(), sonarBlips.Count.ToString(), Color.White);
} }
if (screenOverlay != null) screenOverlay?.Draw(spriteBatch, center, 0.0f, rect.Width / screenOverlay.size.X);
{
screenOverlay.Draw(spriteBatch, center, 0.0f, rect.Width / screenOverlay.size.X);
}
if (signalStrength <= 0.5f) if (signalStrength <= 0.5f)
{ {
@@ -938,20 +933,24 @@ namespace Barotrauma.Items.Components
displayScale, center, DisplayRadius); displayScale, center, DisplayRadius);
} }
int missionIndex = 0;
foreach (Mission mission in GameMain.GameSession.Missions) foreach (Mission mission in GameMain.GameSession.Missions)
{ {
if (!string.IsNullOrWhiteSpace(mission.SonarLabel)) if (!string.IsNullOrWhiteSpace(mission.SonarLabel))
{ {
int i = 0;
foreach (Vector2 sonarPosition in mission.SonarPositions) foreach (Vector2 sonarPosition in mission.SonarPositions)
{ {
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
mission.SonarLabel, mission.SonarLabel,
mission.SonarIconIdentifier, mission.SonarIconIdentifier,
mission, "mission" + missionIndex + ":" + i,
sonarPosition, transducerCenter, sonarPosition, transducerCenter,
displayScale, center, DisplayRadius * 0.95f); displayScale, center, DisplayRadius * 0.95f);
i++;
} }
} }
missionIndex++;
} }
if (AllowUsingMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null) if (AllowUsingMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null)
@@ -964,7 +963,7 @@ namespace Barotrauma.Items.Components
var i = unobtainedMinerals.FirstOrDefault(); var i = unobtainedMinerals.FirstOrDefault();
if (i == null) { continue; } if (i == null) { continue; }
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
i.Name, "mineral", i, i.Name, "mineral", "mineralcluster" + i,
c.center, transducerCenter, c.center, transducerCenter,
displayScale, center, DisplayRadius * 0.95f, displayScale, center, DisplayRadius * 0.95f,
onlyShowTextOnMouseOver: true); onlyShowTextOnMouseOver: true);
@@ -977,14 +976,14 @@ namespace Barotrauma.Items.Components
if (connectedSubs.Contains(sub)) { continue; } if (connectedSubs.Contains(sub)) { continue; }
if (sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; } if (sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
if (item.Submarine != null) if (item.Submarine != null || Character.Controlled != null)
{ {
//hide enemy team //hide enemy team
if (sub.TeamID == CharacterTeamType.Team1 && (item.Submarine.TeamID == CharacterTeamType.Team2 || Character.Controlled?.TeamID == CharacterTeamType.Team2)) if (sub.TeamID == CharacterTeamType.Team1 && (item.Submarine?.TeamID == CharacterTeamType.Team2 || Character.Controlled?.TeamID == CharacterTeamType.Team2))
{ {
continue; continue;
} }
else if (sub.TeamID == CharacterTeamType.Team2 && (item.Submarine.TeamID == CharacterTeamType.Team1 || Character.Controlled?.TeamID == CharacterTeamType.Team1)) else if (sub.TeamID == CharacterTeamType.Team2 && (item.Submarine?.TeamID == CharacterTeamType.Team1 || Character.Controlled?.TeamID == CharacterTeamType.Team1))
{ {
continue; continue;
} }
@@ -1095,7 +1094,7 @@ namespace Barotrauma.Items.Components
if (!dockingPort.Item.Submarine.ShowSonarMarker && dockingPort.Item.Submarine != item.Submarine && !dockingPort.Item.Submarine.Info.IsOutpost) { continue; } if (!dockingPort.Item.Submarine.ShowSonarMarker && dockingPort.Item.Submarine != item.Submarine && !dockingPort.Item.Submarine.Info.IsOutpost) { continue; }
//don't show the docking ports of the opposing team on the sonar //don't show the docking ports of the opposing team on the sonar
if (item.Submarine != null) if (item.Submarine != null && item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle && dockingPort.Item.Submarine.Info.Type != SubmarineType.Outpost)
{ {
// specifically checking for friendlyNPC seems more logical here // specifically checking for friendlyNPC seems more logical here
if (dockingPort.Item.Submarine.TeamID != item.Submarine.TeamID && dockingPort.Item.Submarine.TeamID != CharacterTeamType.FriendlyNPC) { continue; } if (dockingPort.Item.Submarine.TeamID != item.Submarine.TeamID && dockingPort.Item.Submarine.TeamID != CharacterTeamType.FriendlyNPC) { continue; }
@@ -111,7 +111,7 @@ namespace Barotrauma.Items.Components
Tuple<Vector2, Vector2> tracerPoints = new Tuple<Vector2, Vector2>(startLocation, endLocation); Tuple<Vector2, Vector2> tracerPoints = new Tuple<Vector2, Vector2>(startLocation, endLocation);
foreach (ParticleEmitter emitter in particleEmitters) foreach (ParticleEmitter emitter in particleEmitters)
{ {
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: rotation, particleRotation: rotation, colorMultiplier: emitter.Prefab.ColorMultiplier, tracerPoints: tracerPoints); emitter.Emit(1.0f, particlePos, hullGuess: null, angle: rotation, particleRotation: rotation, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier, tracerPoints: tracerPoints);
} }
} }
@@ -73,7 +73,7 @@ namespace Barotrauma.Items.Components
} }
particleEmitter.Emit( particleEmitter.Emit(
deltaTime, ConvertUnits.ToDisplayUnits(raystart), deltaTime, ConvertUnits.ToDisplayUnits(raystart),
item.CurrentHull, particleAngle, particleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0); item.CurrentHull, particleAngle, particleEmitter.Prefab.Properties.CopyEntityAngle ? -particleAngle : 0);
} }
} }
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
Wire equippedWire = null; Wire equippedWire = null;
bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring || panel.AlwaysAllowRewiring; bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring || panel.AlwaysAllowRewiring;
if (allowRewiring && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen)) if (allowRewiring && (!panel.Locked && !panel.TemporarilyLocked || Screen.Selected == GameMain.SubEditorScreen))
{ {
//if the Character using the panel has a wire item equipped //if the Character using the panel has a wire item equipped
//and the wire hasn't been connected yet, draw it on the panel //and the wire hasn't been connected yet, draw it on the panel
@@ -365,7 +365,7 @@ namespace Barotrauma.Items.Components
ConnectionPanel.HighlightedWire = wire; ConnectionPanel.HighlightedWire = wire;
bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring || panel.AlwaysAllowRewiring; bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring || panel.AlwaysAllowRewiring;
if (allowRewiring && (!wire.Locked && !panel.Locked || Screen.Selected == GameMain.SubEditorScreen)) if (allowRewiring && (!wire.Locked && !panel.Locked && !panel.TemporarilyLocked || Screen.Selected == GameMain.SubEditorScreen))
{ {
//start dragging the wire //start dragging the wire
if (PlayerInput.PrimaryMouseButtonHeld()) { DraggingConnected = wire; } if (PlayerInput.PrimaryMouseButtonHeld()) { DraggingConnected = wire; }
@@ -46,6 +46,8 @@ namespace Barotrauma.Items.Components
private Character equipper; private Character equipper;
private bool isEquippable;
public IEnumerable<Character> VisibleCharacters public IEnumerable<Character> VisibleCharacters
{ {
get get
@@ -55,14 +57,28 @@ namespace Barotrauma.Items.Components
} }
} }
public override void OnItemLoaded()
{
isEquippable = item.GetComponent<Pickable>() != null;
if (!isEquippable) { IsActive = true; }
}
public override void Update(float deltaTime, Camera cam) public override void Update(float deltaTime, Camera cam)
{ {
base.Update(deltaTime, cam); base.Update(deltaTime, cam);
if (equipper == null || equipper.Removed) Entity refEntity = equipper;
if (isEquippable)
{ {
IsActive = false; if (equipper == null || equipper.Removed)
return; {
IsActive = false;
return;
}
}
else
{
refEntity = item;
} }
if (updateTimer > 0.0f) if (updateTimer > 0.0f)
@@ -76,11 +92,11 @@ namespace Barotrauma.Items.Components
{ {
if (c == equipper || !c.Enabled || c.Removed) { continue; } if (c == equipper || !c.Enabled || c.Removed) { continue; }
float dist = Vector2.DistanceSquared(equipper.WorldPosition, c.WorldPosition); float dist = Vector2.DistanceSquared(refEntity.WorldPosition, c.WorldPosition);
if (dist < Range * Range) if (dist < Range * Range)
{ {
Vector2 diff = c.WorldPosition - equipper.WorldPosition; Vector2 diff = c.WorldPosition - refEntity.WorldPosition;
if (Submarine.CheckVisibility(equipper.SimPosition, equipper.SimPosition + ConvertUnits.ToSimUnits(diff)) == null) if (Submarine.CheckVisibility(refEntity.SimPosition, refEntity.SimPosition + ConvertUnits.ToSimUnits(diff)) == null)
{ {
visibleCharacters.Add(c); visibleCharacters.Add(c);
} }
@@ -254,7 +254,7 @@ namespace Barotrauma.Items.Components
foreach (ParticleEmitter emitter in particleEmitterCharges) foreach (ParticleEmitter emitter in particleEmitterCharges)
{ {
// color is currently not connected to ammo type, should be updated when ammo is changed // color is currently not connected to ammo type, should be updated when ammo is changed
emitter.Emit(deltaTime, particlePos, hullGuess: null, angle: -rotation, particleRotation: rotation, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.ColorMultiplier); emitter.Emit(deltaTime, particlePos, hullGuess: null, angle: -rotation, particleRotation: rotation, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier);
} }
if (chargeSoundChannel == null || !chargeSoundChannel.IsPlaying) if (chargeSoundChannel == null || !chargeSoundChannel.IsPlaying)
@@ -1125,9 +1125,10 @@ namespace Barotrauma
if (selectedSlot == null) if (selectedSlot == null)
{ {
if (DraggingItemToWorld && if (DraggingItemToWorld &&
Character.Controlled.FocusedItem?.OwnInventory != null && Character.Controlled.FocusedItem is { OwnInventory: { } inventory } item && item.GetComponent<ItemContainer>() is { } container &&
(Character.Controlled.FocusedItem.GetComponent<ItemContainer>()?.HasRequiredItems(Character.Controlled, addMessage: false) ?? false) && container.HasRequiredItems(Character.Controlled, addMessage: false) &&
Character.Controlled.FocusedItem.OwnInventory.CanBePut(DraggingItems.FirstOrDefault())) container.AllowDragAndDrop &&
inventory.CanBePut(DraggingItems.FirstOrDefault()))
{ {
bool anySuccess = false; bool anySuccess = false;
foreach (Item it in DraggingItems) foreach (Item it in DraggingItems)
@@ -462,10 +462,7 @@ namespace Barotrauma
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
if (body != null) body?.DebugDraw(spriteBatch, Color.White);
{
body.DebugDraw(spriteBatch, Color.White);
}
} }
if (editing && IsSelected && PlayerInput.KeyDown(Keys.Space)) if (editing && IsSelected && PlayerInput.KeyDown(Keys.Space))
@@ -722,7 +719,7 @@ namespace Barotrauma
reloadTextureButton.OnClicked += (button, data) => reloadTextureButton.OnClicked += (button, data) =>
{ {
Sprite.ReloadXML(); Sprite.ReloadXML();
Sprite.ReloadTexture(); Sprite.ReloadTexture(updateAllSprites: true);
return true; return true;
}; };
} }
@@ -768,7 +765,7 @@ namespace Barotrauma
{ {
if (!ic.AllowInGameEditing) { continue; } if (!ic.AllowInGameEditing) { continue; }
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0 && if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0 &&
!SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable())) !SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable(ic)))
{ {
continue; continue;
} }
@@ -1133,31 +1130,35 @@ namespace Barotrauma
texts.Add(new ColoredText(nameText, GUI.Style.TextColor, false, false)); texts.Add(new ColoredText(nameText, GUI.Style.TextColor, false, false));
bool noComponentText = true; bool noComponentText = true;
foreach (ItemComponent ic in components)
{
if (string.IsNullOrEmpty(ic.DisplayMsg)) { continue; }
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
Color color = Color.Gray; if (CampaignInteractionType != CampaignMode.InteractionType.None)
if (ic.HasRequiredItems(character, false))
{
if (ic is Repairable)
{
if (!IsFullCondition) { color = Color.Cyan; }
}
else
{
color = Color.Cyan;
}
}
texts.Add(new ColoredText(ic.DisplayMsg, color, false, false));
noComponentText = false;
}
if (noComponentText && CampaignInteractionType != CampaignMode.InteractionType.None)
{ {
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameMain.Config.KeyBindText(InputType.Use)), Color.Cyan, false, false)); texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameMain.Config.KeyBindText(InputType.Use)), Color.Cyan, false, false));
} }
else
{
foreach (ItemComponent ic in components)
{
if (string.IsNullOrEmpty(ic.DisplayMsg)) { continue; }
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
Color color = Color.Gray;
if (ic.HasRequiredItems(character, false))
{
if (ic is Repairable)
{
if (!IsFullCondition) { color = Color.Cyan; }
}
else
{
color = Color.Cyan;
}
}
texts.Add(new ColoredText(ic.DisplayMsg, color, false, false));
noComponentText = false;
}
}
if (PlayerInput.IsShiftDown() && CrewManager.DoesItemHaveContextualOrders(this)) if (PlayerInput.IsShiftDown() && CrewManager.DoesItemHaveContextualOrders(this))
{ {
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")), Color.Cyan, false, false)); texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")), Color.Cyan, false, false));
@@ -77,9 +77,9 @@ namespace Barotrauma
if (flash) if (flash)
{ {
float displayRange = flashRange.HasValue ? flashRange.Value : Attack.Range; float displayRange = flashRange ?? Attack.Range;
if (displayRange < 0.1f) { return; } if (displayRange < 0.1f) { return; }
var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null); var light = new LightSource(worldPosition, displayRange, flashColor, null);
CoroutineManager.StartCoroutine(DimLight(light)); CoroutineManager.StartCoroutine(DimLight(light));
} }
} }
@@ -629,9 +629,9 @@ namespace Barotrauma
PowerConsumptionTimer = message.ReadSingle() PowerConsumptionTimer = message.ReadSingle()
}; };
} }
else if (BallastFlora != null) else
{ {
BallastFlora.ClientRead(message, header); BallastFlora?.ClientRead(message, header);
} }
return; return;
} }
@@ -210,7 +210,7 @@ namespace Barotrauma
if (ParticleEmitterTriggers[i] != null && !ParticleEmitterTriggers[i].IsTriggered) { continue; } if (ParticleEmitterTriggers[i] != null && !ParticleEmitterTriggers[i].IsTriggered) { continue; }
Vector2 emitterPos = LocalToWorld(Prefab.EmitterPositions[i]); Vector2 emitterPos = LocalToWorld(Prefab.EmitterPositions[i]);
ParticleEmitters[i].Emit(deltaTime, emitterPos, hullGuess: null, ParticleEmitters[i].Emit(deltaTime, emitterPos, hullGuess: null,
angle: ParticleEmitters[i].Prefab.CopyEntityAngle ? -CurrentRotation + MathHelper.PiOver2 : 0.0f); angle: ParticleEmitters[i].Prefab.Properties.CopyEntityAngle ? -CurrentRotation + MathHelper.PiOver2 : 0.0f);
} }
} }
@@ -293,6 +293,12 @@ namespace Barotrauma
public void ClientRead(IReadMessage msg) public void ClientRead(IReadMessage msg)
{ {
if (Triggers == null) { return; } if (Triggers == null) { return; }
if (Prefab.TakeLevelWallDamage)
{
float newHealth = msg.ReadRangedSingle(0.0f, Prefab.Health, 8);
AddDamage(Health - newHealth, 1.0f, null, isNetworkEvent: true);
}
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; }
@@ -21,6 +21,8 @@ namespace Barotrauma
private Rectangle currentGridIndices; private Rectangle currentGridIndices;
public bool ForceRefreshVisibleObjects;
partial void UpdateProjSpecific(float deltaTime) partial void UpdateProjSpecific(float deltaTime)
{ {
foreach (LevelObject obj in visibleObjectsBack) foreach (LevelObject obj in visibleObjectsBack)
@@ -60,6 +62,8 @@ 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 (obj.Prefab.HideWhenBroken && obj.Health <= 0.0f) { continue; }
if (zoom < 0.05f) if (zoom < 0.05f)
{ {
//hide if the sprite is very small when zoomed this far out //hide if the sprite is very small when zoomed this far out
@@ -154,9 +158,10 @@ 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 && Timing.TotalTime > NextRefreshTime) if (ForceRefreshVisibleObjects || (currentGridIndices != indices && Timing.TotalTime > NextRefreshTime))
{ {
RefreshVisibleObjects(indices, cam.Zoom); RefreshVisibleObjects(indices, cam.Zoom);
ForceRefreshVisibleObjects = false;
if (cam.Zoom < 0.1f) if (cam.Zoom < 0.1f)
{ {
//when zoomed very far out, refresh a little less often //when zoomed very far out, refresh a little less often
@@ -361,6 +361,7 @@ namespace Barotrauma.Lights
void DrawHalo(Character character) void DrawHalo(Character character)
{ {
if (character == null || character.Removed) { return; }
Vector2 haloDrawPos = character.DrawPosition; Vector2 haloDrawPos = character.DrawPosition;
haloDrawPos.Y = -haloDrawPos.Y; haloDrawPos.Y = -haloDrawPos.Y;
@@ -404,7 +405,7 @@ namespace Barotrauma.Lights
} }
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (item.IsHighlighted && !highlightedEntities.Contains(item)) if ((item.IsHighlighted || item.IconStyle != null) && !highlightedEntities.Contains(item))
{ {
highlightedEntities.Add(item); highlightedEntities.Add(item);
} }
@@ -425,7 +426,14 @@ namespace Barotrauma.Lights
{ {
if (highlighted is Item item) if (highlighted is Item item)
{ {
item.Draw(spriteBatch, false, true); if (item.IconStyle != null && (item != Character.Controlled.FocusedItem || Character.Controlled.FocusedItem == null))
{
//wait until next pass
}
else
{
item.Draw(spriteBatch, false, true);
}
} }
else if (highlighted is Character character) else if (highlighted is Character character)
{ {
@@ -434,6 +442,22 @@ namespace Barotrauma.Lights
} }
spriteBatch.End(); spriteBatch.End();
//draw items with iconstyles in the style's color
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive, samplerState: SamplerState.LinearWrap, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
foreach (Entity highlighted in highlightedEntities)
{
if (highlighted is Item item)
{
if (item.IconStyle != null && (item != Character.Controlled.FocusedItem || Character.Controlled.FocusedItem == null))
{
SolidColorEffect.Parameters["color"].SetValue(item.IconStyle.Color.ToVector4());
SolidColorEffect.CurrentTechnique.Passes[0].Apply();
item.Draw(spriteBatch, false, true);
}
}
}
spriteBatch.End();
//draw characters in black with a bit of blur, leaving the white edges visible //draw characters in black with a bit of blur, leaving the white edges visible
float phase = (float)(Math.Sin(Timing.TotalTime * 3.0f) + 1.0f) / 2.0f; //phase oscillates between 0 and 1 float phase = (float)(Math.Sin(Timing.TotalTime * 3.0f) + 1.0f) / 2.0f; //phase oscillates between 0 and 1
Vector4 overlayColor = Color.Black.ToVector4() * MathHelper.Lerp(0.5f, 0.9f, phase); Vector4 overlayColor = Color.Black.ToVector4() * MathHelper.Lerp(0.5f, 0.9f, phase);
@@ -607,15 +607,18 @@ namespace Barotrauma
tooltip = (new Rectangle(typeChangeIconPos.ToPoint(), new Point(30)), location.LastTypeChangeMessage); tooltip = (new Rectangle(typeChangeIconPos.ToPoint(), new Point(30)), location.LastTypeChangeMessage);
} }
} }
if (location != CurrentLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location)) && generationParams.MissionIcon != null) if (location != CurrentLocation && generationParams.MissionIcon != null)
{ {
Vector2 missionIconPos = pos + new Vector2(1.35f, 0.35f) * generationParams.LocationIconSize * 0.5f * zoom; if ((CurrentLocation == currentDisplayLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location))) || location.AvailableMissions.Any(m => m.Prefab.Type == MissionType.GoTo))
float missionIconScale = 18.0f / generationParams.MissionIcon.SourceRect.Width;
generationParams.MissionIcon.Draw(spriteBatch, missionIconPos, generationParams.IndicatorColor, scale: missionIconScale * zoom);
if (Vector2.Distance(PlayerInput.MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom && IsPreferredTooltip(missionIconPos))
{ {
var availableMissions = CurrentLocation.AvailableMissions.Where(m => m.Locations.Contains(location)); Vector2 missionIconPos = pos + new Vector2(1.35f, 0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
tooltip = (new Rectangle(missionIconPos.ToPoint(), new Point(30)), TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name))); float missionIconScale = 18.0f / generationParams.MissionIcon.SourceRect.Width;
generationParams.MissionIcon.Draw(spriteBatch, missionIconPos, generationParams.IndicatorColor, scale: missionIconScale * zoom);
if (Vector2.Distance(PlayerInput.MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom && IsPreferredTooltip(missionIconPos))
{
var availableMissions = CurrentLocation.AvailableMissions.Where(m => m.Locations.Contains(location)).Concat(location.AvailableMissions.Where(m => m.Prefab.Type == MissionType.GoTo)).Distinct();
tooltip = (new Rectangle(missionIconPos.ToPoint(), new Point(30)), TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name)));
}
} }
} }
@@ -153,7 +153,7 @@ namespace Barotrauma
OnClicked = (button, data) => OnClicked = (button, data) =>
{ {
Sprite.ReloadXML(); Sprite.ReloadXML();
Sprite.ReloadTexture(); Sprite.ReloadTexture(updateAllSprites: true);
return true; return true;
} }
}; };
@@ -89,9 +89,9 @@ namespace Barotrauma.Networking
var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null; var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null;
GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime); GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
} }
else if (orderMessageInfo.TargetCharacter != null) else
{ {
orderMessageInfo.TargetCharacter.SetOrder(order, orderOption, orderMessageInfo.Priority, senderCharacter); orderMessageInfo.TargetCharacter?.SetOrder(order, orderOption, orderMessageInfo.Priority, senderCharacter);
} }
} }
} }
@@ -643,7 +643,7 @@ namespace Barotrauma.Networking
DebugConsole.ThrowError("Error while reading a message from server.", e); DebugConsole.ThrowError("Error while reading a message from server.", e);
new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", new string[2] { "[message]", "[targetsite]" }, new string[2] { e.Message, e.TargetSite.ToString() })); new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", new string[2] { "[message]", "[targetsite]" }, new string[2] { e.Message, e.TargetSite.ToString() }));
Disconnect(); Disconnect();
GameMain.MainMenuScreen.Select(); GameMain.ServerListScreen.Select();
return; return;
} }
@@ -659,10 +659,7 @@ namespace Barotrauma.Networking
{ {
EndVoteTickBox.Visible = serverSettings.Voting.AllowEndVoting && HasSpawned && !(GameMain.GameSession?.GameMode is CampaignMode); EndVoteTickBox.Visible = serverSettings.Voting.AllowEndVoting && HasSpawned && !(GameMain.GameSession?.GameMode is CampaignMode);
if (respawnManager != null) respawnManager?.Update(deltaTime);
{
respawnManager.Update(deltaTime);
}
if (updateTimer <= DateTime.Now) if (updateTimer <= DateTime.Now)
{ {
@@ -936,9 +933,6 @@ namespace Barotrauma.Networking
} }
} }
break; break;
case ServerPacketHeader.RESET_UPGRADES:
campaign?.UpgradeManager.ClientRead(inc);
break;
case ServerPacketHeader.CREW: case ServerPacketHeader.CREW:
campaign?.ClientReadCrew(inc); campaign?.ClientReadCrew(inc);
break; break;
@@ -1313,10 +1307,7 @@ namespace Barotrauma.Networking
Client.ReadPermissions(inc, out permissions, out permittedCommands); Client.ReadPermissions(inc, out permissions, out permittedCommands);
Client targetClient = ConnectedClients.Find(c => c.ID == clientID); Client targetClient = ConnectedClients.Find(c => c.ID == clientID);
if (targetClient != null) targetClient?.SetPermissions(permissions, permittedCommands);
{
targetClient.SetPermissions(permissions, permittedCommands);
}
if (clientID == myID) if (clientID == myID)
{ {
SetMyPermissions(permissions, permittedCommands.Select(command => command.names[0])); SetMyPermissions(permissions, permittedCommands.Select(command => command.names[0]));
@@ -1427,7 +1418,7 @@ namespace Barotrauma.Networking
while (CoroutineManager.IsCoroutineRunning("EndGame")) while (CoroutineManager.IsCoroutineRunning("EndGame"))
{ {
if (EndCinematic != null) { EndCinematic.Stop(); } EndCinematic?.Stop();
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
@@ -2759,6 +2750,8 @@ namespace Barotrauma.Networking
public void Vote(VoteType voteType, object data) public void Vote(VoteType voteType, object data)
{ {
if (clientPeer == null) return;
IWriteMessage msg = new WriteOnlyMessage(); IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.UPDATE_LOBBY); msg.Write((byte)ClientPacketHeader.UPDATE_LOBBY);
msg.Write((byte)ClientNetObject.VOTE); msg.Write((byte)ClientNetObject.VOTE);
@@ -7,7 +7,102 @@ namespace Barotrauma.Particles
{ {
class ParticleEmitterProperties : ISerializableEntity class ParticleEmitterProperties : ISerializableEntity
{ {
public string Name => nameof(ParticleEmitterProperties); private const float MinValue = int.MinValue,
MaxValue = int.MaxValue;
public string Name => nameof(ParticleEmitter);
private float angleMin, angleMax;
public float AngleMinRad { get; private set; }
public float AngleMaxRad { get; private set; }
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 360, MinValueFloat = -360f), Serialize(0f, true)]
public float AngleMin
{
get => angleMin;
set
{
angleMin = value;
AngleMinRad = MathHelper.ToRadians(MathHelper.Clamp(value, -360.0f, 360.0f));
}
}
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 360, MinValueFloat = -360f), Serialize(0f, true)]
public float AngleMax
{
get => angleMax;
set
{
angleMax = value;
AngleMaxRad = MathHelper.ToRadians(MathHelper.Clamp(value, -360.0f, 360.0f));
}
}
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(0f, true)]
public float DistanceMin { get; set; }
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(0f, true)]
public float DistanceMax { get; set; }
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(0f, true)]
public float VelocityMin { get; set; }
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(0f, true)]
public float VelocityMax { get; set; }
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(1f, true)]
public float ScaleMin { get; set; }
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(1f, true)]
public float ScaleMax { get; set; }
[Editable(), Serialize("1,1", true)]
public Vector2 ScaleMultiplier { get; set; }
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(0f, true)]
public float EmitInterval { get; set; }
[Editable, Serialize(0, true)]
public int ParticleAmount { get; set; }
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = 0), Serialize(0f, true)]
public float ParticlesPerSecond { get; set; }
[Editable, Serialize(false, true)]
public bool HighQualityCollisionDetection { get; set; }
[Editable, Serialize(false, true)]
public bool CopyEntityAngle { get; set; }
[Editable, Serialize("1,1,1,1", true)]
public Color ColorMultiplier { get; set; }
[Editable, Serialize(false, true)]
public bool DrawOnTop { get; set; }
[Serialize(0f, true)]
public float Angle
{
get => AngleMin;
set => AngleMin = AngleMax = value;
}
[Serialize(0f, true)]
public float Distance
{
get => DistanceMin;
set => DistanceMin = DistanceMax = value;
}
[Serialize(0f, true)]
public float Velocity
{
get => VelocityMin;
set => VelocityMin = VelocityMax = value;
}
public Dictionary<string, SerializableProperty> SerializableProperties { get; } public Dictionary<string, SerializableProperty> SerializableProperties { get; }
public ParticleEmitterProperties(XElement element) public ParticleEmitterProperties(XElement element)
@@ -39,9 +134,9 @@ namespace Barotrauma.Particles
emitTimer += deltaTime * amountMultiplier; emitTimer += deltaTime * amountMultiplier;
burstEmitTimer -= deltaTime; burstEmitTimer -= deltaTime;
if (Prefab.ParticlesPerSecond > 0) if (Prefab.Properties.ParticlesPerSecond > 0)
{ {
float emitInterval = 1.0f / Prefab.ParticlesPerSecond; float emitInterval = 1.0f / Prefab.Properties.ParticlesPerSecond;
while (emitTimer > emitInterval) while (emitTimer > emitInterval)
{ {
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: tracerPoints); Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: tracerPoints);
@@ -51,8 +146,8 @@ namespace Barotrauma.Particles
if (burstEmitTimer > 0.0f) { return; } if (burstEmitTimer > 0.0f) { return; }
burstEmitTimer = Prefab.EmitInterval; burstEmitTimer = Prefab.Properties.EmitInterval;
for (int i = 0; i < Prefab.ParticleAmount * amountMultiplier; i++) for (int i = 0; i < Prefab.Properties.ParticleAmount * amountMultiplier; i++)
{ {
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: tracerPoints); Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: tracerPoints);
} }
@@ -60,26 +155,29 @@ namespace Barotrauma.Particles
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, Tuple<Vector2, Vector2> tracerPoints = null) private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, Tuple<Vector2, Vector2> tracerPoints = null)
{ {
angle += Rand.Range(Prefab.AngleMin, Prefab.AngleMax); var particlePrefab = overrideParticle ?? Prefab.ParticlePrefab;
if (particlePrefab == null) { return; }
angle += Rand.Range(Prefab.Properties.AngleMinRad, Prefab.Properties.AngleMaxRad);
Vector2 dir = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)); Vector2 dir = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
Vector2 velocity = dir * Rand.Range(Prefab.VelocityMin, Prefab.VelocityMax) * velocityMultiplier; Vector2 velocity = dir * Rand.Range(Prefab.Properties.VelocityMin, Prefab.Properties.VelocityMax) * velocityMultiplier;
position += dir * Rand.Range(Prefab.DistanceMin, Prefab.DistanceMax); position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax);
var particle = GameMain.ParticleManager.CreateParticle(overrideParticle ?? Prefab.ParticlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, tracerPoints: tracerPoints); var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, tracerPoints: tracerPoints);
if (particle != null) if (particle != null)
{ {
particle.Size *= Rand.Range(Prefab.ScaleMin, Prefab.ScaleMax) * sizeMultiplier; particle.Size *= Rand.Range(Prefab.Properties.ScaleMin, Prefab.Properties.ScaleMax) * sizeMultiplier;
particle.Size *= Prefab.ScaleMultiplier; particle.Size *= Prefab.Properties.ScaleMultiplier;
particle.HighQualityCollisionDetection = Prefab.HighQualityCollisionDetection; particle.HighQualityCollisionDetection = Prefab.Properties.HighQualityCollisionDetection;
if (colorMultiplier.HasValue) if (colorMultiplier.HasValue)
{ {
particle.ColorMultiplier = colorMultiplier.Value.ToVector4(); particle.ColorMultiplier = colorMultiplier.Value.ToVector4();
} }
else if (Prefab.ColorMultiplier != Color.White) else if (Prefab.Properties.ColorMultiplier != Color.White)
{ {
particle.ColorMultiplier = Prefab.ColorMultiplier.ToVector4(); particle.ColorMultiplier = Prefab.Properties.ColorMultiplier.ToVector4();
} }
} }
} }
@@ -88,9 +186,9 @@ namespace Barotrauma.Particles
{ {
Rectangle bounds = new Rectangle((int)startPosition.X, (int)startPosition.Y, (int)startPosition.X, (int)startPosition.Y); Rectangle bounds = new Rectangle((int)startPosition.X, (int)startPosition.Y, (int)startPosition.X, (int)startPosition.Y);
for (float angle = Prefab.AngleMin; angle <= Prefab.AngleMax; angle += 0.1f) for (float angle = Prefab.Properties.AngleMinRad; angle <= Prefab.Properties.AngleMaxRad; angle += 0.1f)
{ {
Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Prefab.VelocityMax; Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Prefab.Properties.VelocityMax;
Vector2 endPosition = Prefab.ParticlePrefab.CalculateEndPosition(startPosition, velocity); Vector2 endPosition = Prefab.ParticlePrefab.CalculateEndPosition(startPosition, velocity);
Vector2 endSize = Prefab.ParticlePrefab.CalculateEndSize(); Vector2 endSize = Prefab.ParticlePrefab.CalculateEndSize();
@@ -108,10 +206,10 @@ namespace Barotrauma.Particles
} }
bounds = new Rectangle( bounds = new Rectangle(
(int)Math.Min(bounds.X, endPosition.X - Prefab.DistanceMax - spriteExtent / 2), (int)Math.Min(bounds.X, endPosition.X - Prefab.Properties.DistanceMax - spriteExtent / 2),
(int)Math.Min(bounds.Y, endPosition.Y - Prefab.DistanceMax - spriteExtent / 2), (int)Math.Min(bounds.Y, endPosition.Y - Prefab.Properties.DistanceMax - spriteExtent / 2),
(int)Math.Max(bounds.X, endPosition.X + Prefab.DistanceMax + spriteExtent / 2), (int)Math.Max(bounds.X, endPosition.X + Prefab.Properties.DistanceMax + spriteExtent / 2),
(int)Math.Max(bounds.Y, endPosition.Y + Prefab.DistanceMax + spriteExtent / 2)); (int)Math.Max(bounds.Y, endPosition.Y + Prefab.Properties.DistanceMax + spriteExtent / 2));
} }
bounds = new Rectangle(bounds.X, bounds.Y, bounds.Width - bounds.X, bounds.Height - bounds.Y); bounds = new Rectangle(bounds.X, bounds.Y, bounds.Width - bounds.X, bounds.Height - bounds.Y);
@@ -122,8 +220,6 @@ namespace Barotrauma.Particles
class ParticleEmitterPrefab class ParticleEmitterPrefab
{ {
public readonly string Name;
private string particlePrefabName; private string particlePrefabName;
private ParticlePrefab particlePrefab; private ParticlePrefab particlePrefab;
@@ -134,105 +230,30 @@ namespace Barotrauma.Particles
if (particlePrefab == null && particlePrefabName != null) if (particlePrefab == null && particlePrefabName != null)
{ {
particlePrefab = GameMain.ParticleManager?.FindPrefab(particlePrefabName); particlePrefab = GameMain.ParticleManager?.FindPrefab(particlePrefabName);
if (particlePrefab == null) { particlePrefabName = null; } if (particlePrefab == null)
{
DebugConsole.ThrowError($"Failed to find particle prefab \"{particlePrefabName}\".");
particlePrefabName = null;
}
} }
return particlePrefab; return particlePrefab;
} }
} }
public readonly float AngleMin, AngleMax; public readonly ParticleEmitterProperties Properties;
public readonly float DistanceMin, DistanceMax; public bool DrawOnTop => Properties.DrawOnTop || ParticlePrefab.DrawOnTop;
public readonly float VelocityMin, VelocityMax;
public readonly float ScaleMin, ScaleMax;
public readonly Vector2 ScaleMultiplier;
public readonly float EmitInterval;
public readonly int ParticleAmount;
public readonly float ParticlesPerSecond;
public readonly bool HighQualityCollisionDetection;
public readonly bool CopyEntityAngle;
public readonly Color ColorMultiplier;
public bool DrawOnTop => forceDrawOnTop || ParticlePrefab.DrawOnTop;
private readonly bool forceDrawOnTop;
public ParticleEmitterPrefab(XElement element) public ParticleEmitterPrefab(XElement element)
{ {
Name = element.Name.ToString(); Properties = new ParticleEmitterProperties(element);
particlePrefabName = element.GetAttributeString("particle", ""); particlePrefabName = element.GetAttributeString("particle", "");
}
if (element.Attribute("startrotation") == null) public ParticleEmitterPrefab(ParticlePrefab prefab, ParticleEmitterProperties properties)
{ {
AngleMin = element.GetAttributeFloat("anglemin", 0.0f); Properties = properties;
AngleMax = element.GetAttributeFloat("anglemax", 0.0f); particlePrefab = prefab;
}
else
{
AngleMin = element.GetAttributeFloat("angle", 0.0f);
AngleMax = AngleMin;
}
AngleMin = MathHelper.ToRadians(MathHelper.Clamp(AngleMin, -360.0f, 360.0f));
AngleMax = MathHelper.ToRadians(MathHelper.Clamp(AngleMax, -360.0f, 360.0f));
if (element.Attribute("scalemin") == null)
{
ScaleMin = 1.0f;
ScaleMax = 1.0f;
}
else
{
ScaleMin = element.GetAttributeFloat("scalemin", 1.0f);
ScaleMax = Math.Max(ScaleMin, element.GetAttributeFloat("scalemax", 1.0f));
}
ScaleMultiplier = element.GetAttributeVector2("scalemultiplier", Vector2.One);
if (element.Attribute("distance") == null)
{
DistanceMin = element.GetAttributeFloat("distancemin", 0.0f);
DistanceMax = element.GetAttributeFloat("distancemax", 0.0f);
}
else
{
DistanceMin = DistanceMax = element.GetAttributeFloat("distance", 0.0f);
}
if (DistanceMax < DistanceMin)
{
var temp = DistanceMin;
DistanceMin = DistanceMax;
DistanceMax = temp;
}
if (element.Attribute("velocity") == null)
{
VelocityMin = element.GetAttributeFloat("velocitymin", 0.0f);
VelocityMax = element.GetAttributeFloat("velocitymax", 0.0f);
}
else
{
VelocityMin = VelocityMax = element.GetAttributeFloat("velocity", 0.0f);
}
if (VelocityMax < VelocityMin)
{
var temp = VelocityMin;
VelocityMin = VelocityMax;
VelocityMax = temp;
}
EmitInterval = element.GetAttributeFloat("emitinterval", 0.0f);
ParticlesPerSecond = element.GetAttributeFloat("particlespersecond", 0);
ParticleAmount = element.GetAttributeInt("particleamount", 0);
HighQualityCollisionDetection = element.GetAttributeBool("highqualitycollisiondetection", false);
CopyEntityAngle = element.GetAttributeBool("copyentityangle", false);
forceDrawOnTop = element.GetAttributeBool("drawontop", false);
ColorMultiplier = element.GetAttributeColor("colormultiplier", Color.White);
} }
} }
} }
@@ -51,9 +51,18 @@ namespace Barotrauma
CreateUI(container); CreateUI(container);
campaign.Map.OnLocationSelected += SelectLocation; campaign.Map.OnLocationSelected += SelectLocation;
campaign.Map.OnMissionSelected += (connection, mission) => campaign.Map.OnMissionsSelected += (connection, missions) =>
{ {
missionList?.Select(mission); if (missionList?.Content != null)
{
foreach (GUIComponent missionElement in missionList.Content.Children)
{
if (missionElement.FindChild(c => c is GUITickBox, recursive: true) is GUITickBox tickBox)
{
tickBox.Selected = missions.Contains(tickBox.UserData as Mission);
}
}
}
}; };
} }
@@ -436,6 +445,16 @@ namespace Barotrauma
{ {
Spacing = (int)(5 * GUI.yScale) Spacing = (int)(5 * GUI.yScale)
}; };
missionList.OnSelected = (GUIComponent selected, object userdata) =>
{
var tickBox = selected.FindChild(c => c is GUITickBox, recursive: true) as GUITickBox;
if (GUI.MouseOn == tickBox) { return false; }
if (tickBox != null)
{
tickBox.Selected = !tickBox.Selected;
}
return true;
};
SelectedLevel = connection?.LevelData; SelectedLevel = connection?.LevelData;
Location currentDisplayLocation = Campaign.GetCurrentDisplayLocation(); Location currentDisplayLocation = Campaign.GetCurrentDisplayLocation();
@@ -444,9 +463,6 @@ namespace Barotrauma
List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).ToList(); List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).ToList();
if (!availableMissions.Contains(null)) { availableMissions.Insert(0, null); } if (!availableMissions.Contains(null)) { availableMissions.Insert(0, null); }
Mission selectedMission = currentDisplayLocation.SelectedMission != null && availableMissions.Contains(currentDisplayLocation.SelectedMission) ?
currentDisplayLocation.SelectedMission : null;
missionList.Content.ClearChildren(); missionList.Content.ClearChildren();
foreach (Mission mission in availableMissions) foreach (Mission mission in availableMissions)
@@ -458,53 +474,74 @@ namespace Barotrauma
var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), missionPanel.RectTransform, Anchor.Center)) var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), missionPanel.RectTransform, Anchor.Center))
{ {
Stretch = true, Stretch = true,
CanBeFocused = true CanBeFocused = true,
AbsoluteSpacing = GUI.IntScale(5)
}; };
var missionName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission?.Name ?? TextManager.Get("NoMission"), font: GUI.SubHeadingFont, wrap: true); var missionName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission?.Name ?? TextManager.Get("NoMission"), font: GUI.SubHeadingFont, wrap: true);
// missionName.RectTransform.MinSize = new Point(0, (int)(missionName.Rect.Height * 1.5f));
if (mission != null) if (mission != null)
{ {
if (MapGenerationParams.Instance?.MissionIcon != null) var tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) }, label: string.Empty)
{ {
var icon = new GUIImage(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) }, UserData = mission,
MapGenerationParams.Instance.MissionIcon, scaleToFit: true) Selected = Campaign.Map.CurrentLocation?.SelectedMissions.Contains(mission) ?? false
};
tickBox.RectTransform.MinSize = new Point(tickBox.Rect.Height, 0);
tickBox.RectTransform.IsFixedSize = true;
if (Campaign.AllowedToManageCampaign())
{
tickBox.OnSelected += (GUITickBox tb) =>
{ {
Color = MapGenerationParams.Instance.IndicatorColor * 0.5f, if (tb.Selected)
SelectedColor = MapGenerationParams.Instance.IndicatorColor,
HoverColor = Color.Lerp(MapGenerationParams.Instance.IndicatorColor, Color.White, 0.5f)
};
icon.RectTransform.IsFixedSize = true;
GUILayoutGroup difficultyIndicatorGroup = null;
if (mission.Difficulty.HasValue)
{
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
isHorizontal: true, childAnchor: Anchor.CenterRight)
{ {
AbsoluteSpacing = 1, Campaign.Map.CurrentLocation.SelectMission(mission);
UserData = "difficulty"
};
var difficultyColor = mission.GetDifficultyColor();
for (int i = 0; i < mission.Difficulty; i++)
{
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
{
Color = difficultyColor * 0.5f,
SelectedColor = difficultyColor,
HoverColor = Color.Lerp(difficultyColor, Color.White, 0.5f)
};
} }
} else
{
float extraPadding = 0.5f * icon.Rect.Width; Campaign.Map.CurrentLocation.DeselectMission(mission);
float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0; }
missionName.Padding = new Vector4(missionName.Padding.X + icon.Rect.Width + extraPadding, if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
missionName.Padding.Y, Campaign.AllowedToManageCampaign())
missionName.Padding.Z + extraZPadding + extraPadding, {
missionName.Padding.W); GameMain.Client?.SendCampaignState();
missionName.CalculateHeightFromText(); }
return true;
};
} }
GUILayoutGroup difficultyIndicatorGroup = null;
if (mission.Difficulty.HasValue)
{
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
isHorizontal: true, childAnchor: Anchor.CenterRight)
{
AbsoluteSpacing = 1,
UserData = "difficulty"
};
var difficultyColor = mission.GetDifficultyColor();
for (int i = 0; i < mission.Difficulty; i++)
{
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
{
Color = difficultyColor,
SelectedColor = difficultyColor,
HoverColor = difficultyColor
};
}
}
float extraPadding = 0;// 0.8f * tickBox.Rect.Width;
float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0;
missionName.Padding = new Vector4(missionName.Padding.X + tickBox.Rect.Width * 1.2f + extraPadding,
missionName.Padding.Y,
missionName.Padding.Z + extraZPadding + extraPadding,
missionName.Padding.W);
missionName.CalculateHeightFromText();
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(10)) }, style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.GetMissionRewardText(Submarine.MainSub), wrap: true, parseRichText: true); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.GetMissionRewardText(Submarine.MainSub), wrap: true, parseRichText: true);
string reputationText = mission.GetReputationRewardText(mission.Locations[0]); string reputationText = mission.GetReputationRewardText(mission.Locations[0]);
@@ -512,14 +549,14 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, wrap: true, parseRichText: true); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, wrap: true, parseRichText: true);
} }
missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(20)); missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height + missionTextContent.AbsoluteSpacing) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(0));
foreach (GUIComponent child in missionTextContent.Children) foreach (GUIComponent child in missionTextContent.Children)
{ {
var textBlock = child as GUITextBlock; if (child is GUITextBlock textBlock)
textBlock.Color = textBlock.SelectedColor = textBlock.HoverColor = Color.Transparent; {
textBlock.SelectedTextColor = textBlock.TextColor; textBlock.Color = textBlock.SelectedColor = textBlock.HoverColor = Color.Transparent;
textBlock.TextColor *= 0.5f; textBlock.SelectedTextColor = textBlock.HoverTextColor = textBlock.TextColor;
textBlock.HoverTextColor = textBlock.TextColor; }
} }
missionPanel.OnAddedToGUIUpdateList = (c) => missionPanel.OnAddedToGUIUpdateList = (c) =>
{ {
@@ -538,28 +575,10 @@ namespace Barotrauma
}; };
} }
} }
missionList.Select(selectedMission);
if (prevSelectedLocation == selectedLocation) if (prevSelectedLocation == selectedLocation)
{ {
missionList.BarScroll = prevMissionListScroll; missionList.BarScroll = prevMissionListScroll;
} }
if (Campaign.AllowedToManageCampaign())
{
missionList.OnSelected = (component, userdata) =>
{
Mission mission = userdata as Mission;
if (Campaign.Map.CurrentLocation.SelectedMission == mission) { return false; }
Campaign.Map.CurrentLocation.SelectedMission = mission;
//RefreshMissionInfo(mission);
if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
Campaign.AllowedToManageCampaign())
{
GameMain.Client?.SendCampaignState();
}
return true;
};
}
} }
StartButton = new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), content.RectTransform), StartButton = new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), content.RectTransform),
@@ -567,7 +586,8 @@ namespace Barotrauma
{ {
OnClicked = (GUIButton btn, object obj) => OnClicked = (GUIButton btn, object obj) =>
{ {
if (missionList.Content.Children.Any(c => c.UserData is Mission) && !(missionList.SelectedData is Mission)) if (missionList.Content.FindChild(c => c is GUITickBox tickBox && tickBox.Selected, recursive: true) == null &&
missionList.Content.Children.Any(c => c.UserData is Mission))
{ {
var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), new string[] { TextManager.Get("yes"), TextManager.Get("no") }); var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), new string[] { TextManager.Get("yes"), TextManager.Get("no") });
noMissionVerification.Buttons[0].OnClicked = (btn, userdata) => noMissionVerification.Buttons[0].OnClicked = (btn, userdata) =>
@@ -969,6 +969,16 @@ namespace Barotrauma
UserData = missionType, UserData = missionType,
}; };
if (MissionPrefab.HiddenMissionClasses.Contains(missionType))
{
missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform), string.Empty)
{
UserData = (int)missionType,
Visible = false,
CanBeFocused = false
};
continue;
}
missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform), missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform),
TextManager.Get("MissionType." + missionType.ToString())) TextManager.Get("MissionType." + missionType.ToString()))
{ {
@@ -3214,7 +3224,12 @@ namespace Barotrauma
{ {
for (int i = 0; i < missionTypeTickBoxes.Length; i++) for (int i = 0; i < missionTypeTickBoxes.Length; i++)
{ {
MissionType missionType = (MissionType)((int)missionTypeTickBoxes[i].UserData); MissionType missionType = (MissionType)(int)missionTypeTickBoxes[i].UserData;
if (MissionPrefab.HiddenMissionClasses.Contains(missionType))
{
missionTypeTickBoxes[i].Parent.Visible = false;
continue;
}
if (SelectedMode == GameModePreset.Mission) if (SelectedMode == GameModePreset.Mission)
{ {
missionTypeTickBoxes[i].Parent.Visible = MissionPrefab.CoOpMissionClasses.ContainsKey(missionType); missionTypeTickBoxes[i].Parent.Visible = MissionPrefab.CoOpMissionClasses.ContainsKey(missionType);
@@ -18,55 +18,6 @@ namespace Barotrauma
{ {
class ParticleEditorScreen : EditorScreen class ParticleEditorScreen : EditorScreen
{ {
class Emitter : ISerializableEntity
{
public float EmitTimer;
public float BurstTimer;
[Editable, Serialize("0.0,360.0", false)]
public Vector2 AngleRange { get; private set; }
[Editable, Serialize("0.0,0.0", false)]
public Vector2 VelocityRange { get; private set; }
[Editable, Serialize("1.0,1.0", false)]
public Vector2 ScaleRange { get; private set; }
[Editable, Serialize(0, false)]
public int ParticleBurstAmount { get; private set; }
[Editable, Serialize(1.0f, false)]
public float ParticleBurstInterval { get; private set; }
[Editable, Serialize(1.0f, false)]
public float ParticlesPerSecond { get; private set; }
public string Name
{
get
{
return TextManager.Get("particleeditor.emitter");
}
}
public Dictionary<string, SerializableProperty> SerializableProperties
{
get;
private set;
}
public Emitter()
{
ScaleRange = Vector2.One;
AngleRange = new Vector2(0.0f, 360.0f);
ParticleBurstAmount = 1;
ParticleBurstInterval = 1.0f;
SerializableProperties = SerializableProperty.GetProperties(this);
}
}
private GUIComponent rightPanel, leftPanel; private GUIComponent rightPanel, leftPanel;
private GUIListBox prefabList; private GUIListBox prefabList;
private GUITextBox filterBox; private GUITextBox filterBox;
@@ -74,7 +25,17 @@ namespace Barotrauma
private ParticlePrefab selectedPrefab; private ParticlePrefab selectedPrefab;
private Emitter emitter; private readonly ParticleEmitterProperties emitterProperties = new ParticleEmitterProperties(null)
{
ScaleMax = 1f,
ScaleMin = 1f,
AngleMax = 360f,
AngleMin = 0,
ParticlesPerSecond = 1f
};
private ParticleEmitterPrefab emitterPrefab;
private ParticleEmitter emitter;
private readonly Camera cam; private readonly Camera cam;
@@ -128,8 +89,8 @@ namespace Barotrauma
} }
}; };
var serializeToClipBoardButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform), new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform),
TextManager.Get("editor.copytoclipboard")) TextManager.Get("ParticleEditor.CopyPrefabToClipboard"))
{ {
OnClicked = (btn, obj) => OnClicked = (btn, obj) =>
{ {
@@ -138,11 +99,18 @@ namespace Barotrauma
} }
}; };
emitter = new Emitter(); new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform),
var emitterEditorContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.25f), paddedRightPanel.RectTransform), style: null); TextManager.Get("ParticleEditor.CopyEmitterToClipboard"))
var emitterEditor = new SerializableEntityEditor(emitterEditorContainer.RectTransform, emitter, false, true, elementHeight: 20, titleFont: GUI.SubHeadingFont); {
emitterEditor.RectTransform.RelativeSize = Vector2.One; OnClicked = (btn, obj) =>
emitterEditorContainer.RectTransform.Resize(new Point(emitterEditorContainer.RectTransform.NonScaledSize.X, emitterEditor.ContentHeight), false); {
SerializeEmitterToClipboard();
return true;
}
};
var emitterListBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.25f), paddedRightPanel.RectTransform));
new SerializableEntityEditor(emitterListBox.Content.RectTransform, emitterProperties, false, true, elementHeight: 20, titleFont: GUI.SubHeadingFont);
var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.6f), paddedRightPanel.RectTransform)); var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.6f), paddedRightPanel.RectTransform));
@@ -163,7 +131,10 @@ namespace Barotrauma
prefabList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), paddedLeftPanel.RectTransform)); prefabList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), paddedLeftPanel.RectTransform));
prefabList.OnSelected += (GUIComponent component, object obj) => prefabList.OnSelected += (GUIComponent component, object obj) =>
{ {
cam.Position = Vector2.Zero;
selectedPrefab = obj as ParticlePrefab; selectedPrefab = obj as ParticlePrefab;
emitterPrefab = new ParticleEmitterPrefab(selectedPrefab, emitterProperties);
emitter = new ParticleEmitter(emitterPrefab);
listBox.ClearChildren(); listBox.ClearChildren();
new SerializableEntityEditor(listBox.Content.RectTransform, selectedPrefab, false, true, elementHeight: 20, titleFont: GUI.SubHeadingFont); new SerializableEntityEditor(listBox.Content.RectTransform, selectedPrefab, false, true, elementHeight: 20, titleFont: GUI.SubHeadingFont);
//listBox.Content.RectTransform.NonScaledSize = particlePrefabEditor.RectTransform.NonScaledSize; //listBox.Content.RectTransform.NonScaledSize = particlePrefabEditor.RectTransform.NonScaledSize;
@@ -204,19 +175,6 @@ namespace Barotrauma
} }
} }
private void Emit(Vector2 position)
{
float angle = MathHelper.ToRadians(Rand.Range(emitter.AngleRange.X, emitter.AngleRange.Y));
Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Rand.Range(emitter.VelocityRange.X, emitter.VelocityRange.Y);
var particle = GameMain.ParticleManager.CreateParticle(selectedPrefab, position, velocity, 0.0f);
if (particle != null)
{
particle.Size *= Rand.Range(emitter.ScaleRange.X, emitter.ScaleRange.Y);
}
}
private void FilterEmitters(string text) private void FilterEmitters(string text)
{ {
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
@@ -269,9 +227,34 @@ namespace Barotrauma
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
} }
private void SerializeEmitterToClipboard()
{
XElement element = new XElement(nameof(ParticleEmitter));
if (selectedPrefab is { } prefab)
{
element.Add(new XAttribute("particle", prefab.Identifier));
}
SerializableProperty.SerializeProperties(emitterProperties, element, saveIfDefault: false);
StringBuilder sb = new StringBuilder();
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
{
OmitXmlDeclaration = true
};
using (var writer = System.Xml.XmlWriter.Create(sb, settings))
{
element.WriteTo(writer);
writer.Flush();
}
Clipboard.SetText(sb.ToString());
}
private void SerializeToClipboard(ParticlePrefab prefab) private void SerializeToClipboard(ParticlePrefab prefab)
{ {
#if WINDOWS
if (prefab == null) { return; } if (prefab == null) { return; }
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
@@ -314,7 +297,6 @@ namespace Barotrauma
} }
Clipboard.SetText(sb.ToString()); Clipboard.SetText(sb.ToString());
#endif
} }
public override void Update(double deltaTime) public override void Update(double deltaTime)
@@ -331,31 +313,9 @@ namespace Barotrauma
CreateContextMenu(); CreateContextMenu();
} }
if (selectedPrefab != null) if (selectedPrefab != null && emitter != null)
{ {
emitter.EmitTimer += (float)deltaTime; emitter.Emit((float) deltaTime, Vector2.Zero);
emitter.BurstTimer += (float)deltaTime;
if (emitter.ParticlesPerSecond > 0)
{
float emitInterval = 1.0f / emitter.ParticlesPerSecond;
while (emitter.EmitTimer > emitInterval)
{
Emit(Vector2.Zero);
emitter.EmitTimer -= emitInterval;
}
}
if (emitter.BurstTimer > emitter.ParticleBurstInterval)
{
for (int i = 0; i < emitter.ParticleBurstAmount; i++)
{
Emit(Vector2.Zero);
}
emitter.BurstTimer = 0.0f;
}
} }
GameMain.ParticleManager.Update((float)deltaTime); GameMain.ParticleManager.Update((float)deltaTime);
@@ -365,7 +365,8 @@ namespace Barotrauma
"FlowerSprite", "FlowerSprite",
"DecorativeSprite", "DecorativeSprite",
"BarrelSprite", "BarrelSprite",
"RailSprite" "RailSprite",
"SchematicSprite"
}; };
foreach (string spriteElementName in spriteElementNames) foreach (string spriteElementName in spriteElementNames)
@@ -3200,6 +3200,8 @@ namespace Barotrauma
Color newColor = SetColor(null); Color newColor = SetColor(null);
if (!IsSubEditor()) { return true; }
Dictionary<object, List<ISerializableEntity>> oldProperties = new Dictionary<object, List<ISerializableEntity>>(); Dictionary<object, List<ISerializableEntity>> oldProperties = new Dictionary<object, List<ISerializableEntity>>();
foreach (var (sEntity, color, _) in entities) foreach (var (sEntity, color, _) in entities)
@@ -3969,9 +3971,9 @@ namespace Barotrauma
{ {
loadFrame.AddToGUIUpdateList(); loadFrame.AddToGUIUpdateList();
} }
else if (saveFrame != null) else
{ {
saveFrame.AddToGUIUpdateList(); saveFrame?.AddToGUIUpdateList();
} }
} }
@@ -273,8 +273,8 @@ namespace Barotrauma
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, bool inGame, bool showName, string style = "", int elementHeight = 24, ScalableFont titleFont = null) public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, bool inGame, bool showName, string style = "", int elementHeight = 24, ScalableFont titleFont = null)
: this(parent, entity, inGame ? : this(parent, entity, inGame ?
SerializableProperty.GetProperties<InGameEditable>(entity).Union(SerializableProperty.GetProperties<ConditionallyEditable>(entity).Where(p => p.GetAttribute<ConditionallyEditable>()?.IsEditable() ?? false)) SerializableProperty.GetProperties<InGameEditable>(entity).Union(SerializableProperty.GetProperties<ConditionallyEditable>(entity).Where(p => p.GetAttribute<ConditionallyEditable>()?.IsEditable(entity) ?? false))
: SerializableProperty.GetProperties<Editable>(entity), showName, style, elementHeight, titleFont) : SerializableProperty.GetProperties<Editable>(entity).Where(p => p.GetAttribute<ConditionallyEditable>()?.IsEditable(entity) ?? true), showName, style, elementHeight, titleFont)
{ {
} }
@@ -826,27 +826,48 @@ namespace Barotrauma
IEnumerable<BackgroundMusic> suitableMusic = GetSuitableMusicClips(currentMusicType, currentIntensity); IEnumerable<BackgroundMusic> suitableMusic = GetSuitableMusicClips(currentMusicType, currentIntensity);
int mainTrackIndex = 0;
if (suitableMusic.Count() == 0) if (suitableMusic.Count() == 0)
{ {
targetMusic[0] = null; targetMusic[mainTrackIndex] = null;
} }
//switch the music if nothing playing atm or the currently playing clip is not suitable anymore //switch the music if nothing playing atm or the currently playing clip is not suitable anymore
else if (targetMusic[0] == null || currentMusic[0] == null || !currentMusic[0].IsPlaying() || !suitableMusic.Any(m => m.File == currentMusic[0].Filename)) else if (targetMusic[mainTrackIndex] == null || currentMusic[mainTrackIndex] == null || !currentMusic[mainTrackIndex].IsPlaying() || !suitableMusic.Any(m => m.File == currentMusic[mainTrackIndex].Filename))
{ {
if (currentMusicType == "default") if (currentMusicType == "default")
{ {
if (previousDefaultMusic == null) if (previousDefaultMusic == null)
{ {
targetMusic[0] = previousDefaultMusic = suitableMusic.GetRandom(); targetMusic[mainTrackIndex] = previousDefaultMusic = suitableMusic.GetRandom();
} }
else else
{ {
targetMusic[0] = previousDefaultMusic; targetMusic[mainTrackIndex] = previousDefaultMusic;
} }
} }
else else
{ {
targetMusic[0] = suitableMusic.GetRandom(); targetMusic[mainTrackIndex] = suitableMusic.GetRandom();
}
}
if (Level.Loaded?.Type == LevelData.LevelType.LocationConnection)
{
// Find background noise loop for the current biome
IEnumerable<BackgroundMusic> suitableNoiseLoops = Screen.Selected == GameMain.GameScreen ?
GetSuitableMusicClips(Level.Loaded.LevelData?.Biome?.Identifier, currentIntensity) :
Enumerable.Empty<BackgroundMusic>();
int noiseLoopIndex = 1;
if (suitableNoiseLoops.Count() == 0)
{
targetMusic[noiseLoopIndex] = null;
}
// Switch the noise loop if nothing playing atm or the currently playing clip is not suitable anymore
else if (targetMusic[noiseLoopIndex] == null || currentMusic[noiseLoopIndex] == null || !suitableNoiseLoops.Any(m => m.File == currentMusic[noiseLoopIndex].Filename))
{
targetMusic[noiseLoopIndex] = suitableNoiseLoops.GetRandom();
} }
} }
@@ -855,7 +876,8 @@ namespace Barotrauma
GetSuitableMusicClips("intensity", currentIntensity) : GetSuitableMusicClips("intensity", currentIntensity) :
Enumerable.Empty<BackgroundMusic>(); Enumerable.Empty<BackgroundMusic>();
for (int i = 1; i < MaxMusicChannels; i++) int intensityTrackStartIndex = 2;
for (int i = intensityTrackStartIndex; i < MaxMusicChannels; i++)
{ {
//disable targetmusics that aren't suitable anymore //disable targetmusics that aren't suitable anymore
if (targetMusic[i] != null && !suitableIntensityMusic.Any(m => m.File == targetMusic[i].File)) if (targetMusic[i] != null && !suitableIntensityMusic.Any(m => m.File == targetMusic[i].File))
@@ -869,7 +891,7 @@ namespace Barotrauma
//already playing, do nothing //already playing, do nothing
if (targetMusic.Any(m => m != null && m.File == intensityMusic.File)) { continue; } if (targetMusic.Any(m => m != null && m.File == intensityMusic.File)) { continue; }
for (int i = 1; i < MaxMusicChannels; i++) for (int i = intensityTrackStartIndex; i < MaxMusicChannels; i++)
{ {
if (targetMusic[i] == null) if (targetMusic[i] == null)
{ {
@@ -114,10 +114,7 @@ namespace Barotrauma.Sounds
{ {
lock (mutex) lock (mutex)
{ {
if (soundChannel != null) soundChannel?.Dispose();
{
soundChannel.Dispose();
}
base.Dispose(); base.Dispose();
} }
} }
@@ -121,7 +121,7 @@ namespace Barotrauma
} }
} }
public void ReloadTexture(bool updateAllSprites = false) => ReloadTexture(updateAllSprites ? LoadedSprites.Where(s => s.Texture == texture) : new Sprite[] { this }); public void ReloadTexture(bool updateAllSprites = false) => ReloadTexture(updateAllSprites ? LoadedSprites.Where(s => s.texture == texture).ToList() : new List<Sprite>() { this });
public void ReloadTexture(IEnumerable<Sprite> spritesToUpdate) public void ReloadTexture(IEnumerable<Sprite> spritesToUpdate)
{ {
@@ -55,8 +55,6 @@ namespace Barotrauma
partial void ApplyProjSpecific(float deltaTime, Entity entity, IEnumerable<ISerializableEntity> targets, Hull hull, Vector2 worldPosition, bool playSound) partial void ApplyProjSpecific(float deltaTime, Entity entity, IEnumerable<ISerializableEntity> targets, Hull hull, Vector2 worldPosition, bool playSound)
{ {
if (entity == null) { return; }
if (playSound) if (playSound)
{ {
PlaySound(entity, hull, worldPosition); PlaySound(entity, hull, worldPosition);
@@ -66,7 +64,7 @@ namespace Barotrauma
{ {
float angle = 0.0f; float angle = 0.0f;
float particleRotation = 0.0f; float particleRotation = 0.0f;
if (emitter.Prefab.CopyEntityAngle) if (emitter.Prefab.Properties.CopyEntityAngle)
{ {
Limb targetLimb = null; Limb targetLimb = null;
if (entity is Item item && item.body != null) if (entity is Item item && item.body != null)
@@ -215,15 +215,10 @@ namespace Barotrauma
} }
else else
{ {
DebugConsole.NewMessage($"Could not compress a texture because the dimensions aren't a multiple of 4 (path: {path ?? "null"}, size: {width}x{height})", Color.Orange); DebugConsole.AddWarning($"Could not compress a texture because the dimensions aren't a multiple of 4 (path: {path ?? "null"}, size: {width}x{height})");
} }
} }
if (((width & 0x03) != 0) || ((height & 0x03) != 0))
{
DebugConsole.AddWarning($"Cannot compress a texture because the dimensions are not a multiple of 4 (path: {path ?? "null"}, size: {width}x{height})");
}
Texture2D tex = null; Texture2D tex = null;
CrossThread.RequestExecutionOnMainThread(() => CrossThread.RequestExecutionOnMainThread(() =>
{ {
@@ -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.1400.0.0</Version> <Version>0.1400.1.0</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.1400.0.0</Version> <Version>0.1400.1.0</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.1400.0.0</Version> <Version>0.1400.1.0</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.1400.0.0</Version> <Version>0.1400.1.0</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.1400.0.0</Version> <Version>0.1400.1.0</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>
@@ -356,8 +356,12 @@ namespace Barotrauma
int orderIndex = Order.PrefabList.IndexOf(orderPrefab); int orderIndex = Order.PrefabList.IndexOf(orderPrefab);
msg.WriteRangedInteger(orderIndex, 0, Order.PrefabList.Count); msg.WriteRangedInteger(orderIndex, 0, Order.PrefabList.Count);
if (!orderPrefab.HasOptions) { break; } if (!orderPrefab.HasOptions) { break; }
int optionIndex = orderPrefab.Options.IndexOf(currentOrderInfo.Value.OrderOption); int optionIndex = orderPrefab.AllOptions.IndexOf(currentOrderInfo.Value.OrderOption);
msg.WriteRangedInteger(optionIndex, 0, orderPrefab.Options.Length); if (optionIndex == -1)
{
DebugConsole.AddWarning($"Error while writing order data. Order option \"{(currentOrderInfo.Value.OrderOption ?? null)}\" not found in the order prefab \"{orderPrefab.Name}\".");
}
msg.WriteRangedInteger(optionIndex, -1, orderPrefab.AllOptions.Length);
} }
else if (type == 2) else if (type == 2)
{ {
@@ -381,6 +385,13 @@ namespace Barotrauma
break; break;
case NetEntityEvent.Type.AddToCrew: case NetEntityEvent.Type.AddToCrew:
msg.WriteRangedInteger(9, min, max); msg.WriteRangedInteger(9, min, max);
msg.Write((byte)(CharacterTeamType)extraData[1]); // team id
ushort[] inventoryItemIDs = (ushort[])extraData[2];
msg.Write((ushort)inventoryItemIDs.Length);
for (int i = 0; i < inventoryItemIDs.Length; i++)
{
msg.Write(inventoryItemIDs[i]);
}
break; break;
default: default:
DebugConsole.ThrowError("Invalid NetworkEvent type for entity " + ToString() + " (" + (NetEntityEvent.Type)extraData[0] + ")"); DebugConsole.ThrowError("Invalid NetworkEvent type for entity " + ToString() + " (" + (NetEntityEvent.Type)extraData[0] + ")");
@@ -1,5 +1,7 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -15,9 +17,45 @@ namespace Barotrauma
private static readonly Dictionary<Client, ConversationAction> lastActiveAction = new Dictionary<Client, ConversationAction>(); private static readonly Dictionary<Client, ConversationAction> lastActiveAction = new Dictionary<Client, ConversationAction>();
private readonly HashSet<Client> targetClients = new HashSet<Client>(); private readonly HashSet<Client> targetClients = new HashSet<Client>();
private readonly Dictionary<Client, DateTime> ignoredClients = new Dictionary<Client, DateTime>();
public IEnumerable<Client> TargetClients public IEnumerable<Client> TargetClients
{ {
get { return targetClients; } get
{
UpdateIgnoredClients();
return targetClients.Where(c => !ignoredClients.ContainsKey(c));
}
}
private void UpdateIgnoredClients()
{
if (ignoredClients.Any())
{
HashSet<Client> clientsToRemove = null;
foreach (var k in ignoredClients.Keys)
{
if (ignoredClients[k] < DateTime.Now)
{
clientsToRemove ??= new HashSet<Client>();
clientsToRemove.Add(k);
}
}
if (!(clientsToRemove is null))
{
foreach (var k in clientsToRemove)
{
ignoredClients.Remove(k);
}
}
}
}
public void IgnoreClient(Client c, float seconds)
{
if (!ignoredClients.ContainsKey(c)) { ignoredClients.Add(c, DateTime.Now); }
ignoredClients[c] = DateTime.Now + TimeSpan.FromSeconds(seconds);
Reset();
} }
private bool IsBlockedByAnotherConversation(IEnumerable<Entity> targets) private bool IsBlockedByAnotherConversation(IEnumerable<Entity> targets)
@@ -28,7 +28,14 @@ namespace Barotrauma
continue; continue;
} }
convAction.SelectedOption = selectedOption; if (selectedOption == byte.MaxValue)
{
convAction.IgnoreClient(sender, 3f);
}
else
{
convAction.SelectedOption = selectedOption;
}
return; return;
} }
} }
@@ -1,16 +1,20 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
partial class AbandonedOutpostMission : Mission partial class AbandonedOutpostMission : Mission
{ {
private readonly List<Item> spawnedItems = new List<Item>();
public override void ServerWriteInitial(IWriteMessage msg, Client c) public override void ServerWriteInitial(IWriteMessage msg, Client c)
{ {
if (characters.Count == 0) msg.Write((ushort)spawnedItems.Count);
foreach (Item item in spawnedItems)
{ {
throw new InvalidOperationException("Server attempted to write AbandonedOutpostMission data when no characters had been spawned."); item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0);
} }
msg.Write((byte)characters.Count); msg.Write((byte)characters.Count);
@@ -21,7 +21,7 @@ namespace Barotrauma
} }
} }
public override void Update(float deltaTime) protected override void UpdateMissionSpecific(float deltaTime)
{ {
if (!initialized) if (!initialized)
{ {
@@ -18,6 +18,7 @@ namespace Barotrauma
foreach (Character character in characters) foreach (Character character in characters)
{ {
character.WriteSpawnData(msg, character.ID, restrictMessageSize: false); character.WriteSpawnData(msg, character.ID, restrictMessageSize: false);
msg.Write(terroristCharacters.Contains(character));
List<Item> characterItems = characterDictionary[character]; List<Item> characterItems = characterDictionary[character];
// items must be written in a specific sequence so that child items aren't written before their parents // items must be written in a specific sequence so that child items aren't written before their parents
msg.Write((ushort)characterItems.Count()); msg.Write((ushort)characterItems.Count());
@@ -6,10 +6,12 @@ namespace Barotrauma
{ {
partial void ShowMessageProjSpecific(int missionState) partial void ShowMessageProjSpecific(int missionState)
{ {
if (missionState >= Headers.Count && missionState >= Messages.Count) return; int messageIndex = missionState - 1;
if (messageIndex >= Headers.Count && messageIndex >= Messages.Count) { return; }
if (messageIndex < 0) { return; }
string header = missionState < Headers.Count ? Headers[missionState] : ""; string header = messageIndex < Headers.Count ? Headers[messageIndex] : "";
string message = missionState < Messages.Count ? Messages[missionState] : ""; string message = messageIndex < Messages.Count ? Messages[messageIndex] : "";
GameServer.Log(TextManager.Get("MissionInfo") + ": " + header + " - " + message, ServerLog.MessageType.ServerMessage); GameServer.Log(TextManager.Get("MissionInfo") + ": " + header + " - " + message, ServerLog.MessageType.ServerMessage);
} }
@@ -1,19 +0,0 @@
using Barotrauma.Networking;
using System.Collections.Generic;
namespace Barotrauma
{
partial class OutpostDestroyMission : AbandonedOutpostMission
{
private readonly List<Item> spawnedItems = new List<Item>();
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
base.ServerWriteInitial(msg, c);
msg.Write((ushort)spawnedItems.Count);
foreach (Item item in spawnedItems)
{
item.WriteSpawnData(msg, item.ID, Entity.NullEntityID, 0);
}
}
}
}
@@ -35,6 +35,8 @@ namespace Barotrauma
private set; private set;
} }
public static Thread MainThread { get; private set; }
//only screens the server implements //only screens the server implements
public static GameScreen GameScreen; public static GameScreen GameScreen;
public static NetLobbyScreen NetLobbyScreen; public static NetLobbyScreen NetLobbyScreen;
@@ -91,6 +93,8 @@ namespace Barotrauma
Console.WriteLine("Initializing GameScreen"); Console.WriteLine("Initializing GameScreen");
GameScreen = new GameScreen(); GameScreen = new GameScreen();
MainThread = Thread.CurrentThread;
} }
public void Init() public void Init()
@@ -388,6 +392,8 @@ namespace Barotrauma
if (GameSettings.SaveDebugConsoleLogs) { DebugConsole.SaveLogs(); } if (GameSettings.SaveDebugConsoleLogs) { DebugConsole.SaveLogs(); }
if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); } if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); }
MainThread = null;
} }
public static void ResetFrameTime() public static void ResetFrameTime()
@@ -214,6 +214,7 @@ namespace Barotrauma
c.CharacterHealth.Save(c.Info.HealthData); c.CharacterHealth.Save(c.Info.HealthData);
c.Info.InventoryData = new XElement("inventory"); c.Info.InventoryData = new XElement("inventory");
c.SaveInventory(); c.SaveInventory();
c.Info.SaveOrderData();
} }
c.Inventory.DeleteAllItems(); c.Inventory.DeleteAllItems();
@@ -332,7 +333,7 @@ namespace Barotrauma
CargoManager.OnSoldItemsChanged += () => { LastUpdateID++; }; CargoManager.OnSoldItemsChanged += () => { LastUpdateID++; };
UpgradeManager.OnUpgradesChanged += () => { LastUpdateID++; }; UpgradeManager.OnUpgradesChanged += () => { LastUpdateID++; };
Map.OnLocationSelected += (loc, connection) => { LastUpdateID++; }; Map.OnLocationSelected += (loc, connection) => { LastUpdateID++; };
Map.OnMissionSelected += (loc, mission) => { LastUpdateID++; }; Map.OnMissionsSelected += (loc, mission) => { LastUpdateID++; };
Reputation.OnAnyReputationValueChanged += () => { LastUpdateID++; }; Reputation.OnAnyReputationValueChanged += () => { LastUpdateID++; };
} }
//increment save ID so clients know they're lacking the most up-to-date save file //increment save ID so clients know they're lacking the most up-to-date save file
@@ -432,7 +433,14 @@ namespace Barotrauma
msg.Write(map.Seed); msg.Write(map.Seed);
msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex); msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex); msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
msg.Write(map.SelectedMissionIndex == -1 ? byte.MaxValue : (byte)map.SelectedMissionIndex);
var selectedMissionIndices = map.GetSelectedMissionIndices();
msg.Write((byte)selectedMissionIndices.Count());
foreach (int selectedMissionIndex in selectedMissionIndices)
{
msg.Write((byte)selectedMissionIndex);
}
msg.Write(map.AllowDebugTeleport); msg.Write(map.AllowDebugTeleport);
msg.Write(reputation != null); msg.Write(reputation != null);
if (reputation != null) { msg.Write(reputation.Value); } if (reputation != null) { msg.Write(reputation.Value); }
@@ -535,7 +543,14 @@ namespace Barotrauma
{ {
UInt16 currentLocIndex = msg.ReadUInt16(); UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16(); UInt16 selectedLocIndex = msg.ReadUInt16();
byte selectedMissionIndex = msg.ReadByte();
byte selectedMissionCount = msg.ReadByte();
List<int> selectedMissionIndices = new List<int>();
for (int i = 0; i < selectedMissionCount; i++)
{
selectedMissionIndices.Add(msg.ReadByte());
}
bool purchasedHullRepairs = msg.ReadBoolean(); bool purchasedHullRepairs = msg.ReadBoolean();
bool purchasedItemRepairs = msg.ReadBoolean(); bool purchasedItemRepairs = msg.ReadBoolean();
bool purchasedLostShuttles = msg.ReadBoolean(); bool purchasedLostShuttles = msg.ReadBoolean();
@@ -663,7 +678,7 @@ namespace Barotrauma
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); } if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); }
if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndex); } if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndices); }
List<PurchasedItem> currentBuyCrateItems = new List<PurchasedItem>(CargoManager.ItemsInBuyCrate); List<PurchasedItem> currentBuyCrateItems = new List<PurchasedItem>(CargoManager.ItemsInBuyCrate);
currentBuyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, -i.Quantity)); currentBuyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, -i.Quantity));
@@ -20,33 +20,5 @@ namespace Barotrauma
} }
} }
} }
/// <summary>
/// Sends a message to all clients telling them that all upgrades on the submarine were reset.
/// </summary>
/// <remarks>
/// <param name="newUpgrades"/> is supposed to have a list of reloaded metadata but seeing as
/// this method is currently only used when switching submarines and that disables the repair NPC
/// until the next round so currently there's no need for it as we get the new values from the save
/// file anyways.
/// </remarks>
/// <see cref="UpgradeManager.ClientRead"/>
private void SendUpgradeResetMessage(Dictionary<string, int> newUpgrades)
{
foreach (Client c in GameMain.Server.ConnectedClients)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ServerPacketHeader.RESET_UPGRADES);
outmsg.Write(true);
outmsg.Write(Campaign.Money);
// outmsg.Write((uint)newUpgrades.Count);
// foreach (var (key, value) in newUpgrades)
// {
// outmsg.Write(key);
// outmsg.Write((byte)value);
// }
GameMain.Server?.ServerPeer?.Send(outmsg, c.Connection, DeliveryMethod.Reliable);
}
}
} }
} }
@@ -41,7 +41,7 @@ namespace Barotrauma.Items.Components
} }
//don't allow rewiring locked panels //don't allow rewiring locked panels
if (Locked || !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return; } if (Locked || TemporarilyLocked || !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return; }
item.CreateServerEvent(this); item.CreateServerEvent(this);
@@ -378,7 +378,7 @@ namespace Barotrauma.Networking
if (gameStarted) if (gameStarted)
{ {
if (respawnManager != null) { respawnManager.Update(deltaTime); } respawnManager?.Update(deltaTime);
entityEventManager.Update(connectedClients); entityEventManager.Update(connectedClients);
@@ -406,10 +406,7 @@ namespace Barotrauma.Networking
} }
} }
if (TraitorManager != null) TraitorManager?.Update(deltaTime);
{
TraitorManager.Update(deltaTime);
}
if (serverSettings.Voting.VoteRunning) if (serverSettings.Voting.VoteRunning)
{ {
@@ -433,7 +430,7 @@ namespace Barotrauma.Networking
connectedClients.All(c => c.Character == null || c.Character.IsDead || c.Character.IsIncapacitated); connectedClients.All(c => c.Character == null || c.Character.IsDead || c.Character.IsIncapacitated);
bool subAtLevelEnd = false; bool subAtLevelEnd = false;
if (Submarine.MainSub != null && Submarine.MainSubs[1] == null) if (Submarine.MainSub != null && !(GameMain.GameSession.GameMode is PvPMode))
{ {
if (Level.Loaded?.EndOutpost != null) if (Level.Loaded?.EndOutpost != null)
{ {
@@ -488,8 +485,10 @@ namespace Barotrauma.Networking
} }
else if (isCrewDead && (GameMain.GameSession?.GameMode is CampaignMode)) else if (isCrewDead && (GameMain.GameSession?.GameMode is CampaignMode))
{ {
#if !DEBUG
endRoundDelay = 1.0f; endRoundDelay = 1.0f;
endRoundTimer += deltaTime; endRoundTimer += deltaTime;
#endif
} }
else else
{ {
@@ -3281,7 +3280,6 @@ namespace Barotrauma.Networking
if (voteType != VoteType.PurchaseSub) if (voteType != VoteType.PurchaseSub)
{ {
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(targetSubmarine, deliveryFee); SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(targetSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub, true);
} }
serverSettings.Voting.StopSubmarineVote(true); serverSettings.Voting.StopSubmarineVote(true);
@@ -306,7 +306,8 @@ namespace Barotrauma.Networking
{ {
if (Enum.TryParse(missionTypeName, out MissionType missionType)) if (Enum.TryParse(missionTypeName, out MissionType missionType))
{ {
if (missionType == Barotrauma.MissionType.None) continue; if (missionType == Barotrauma.MissionType.None) { continue; }
if (MissionPrefab.HiddenMissionClasses.Contains(missionType)) { continue; }
AllowedRandomMissionTypes.Add(missionType); AllowedRandomMissionTypes.Add(missionType);
} }
} }
@@ -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.1400.0.0</Version> <Version>0.1400.1.0</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>
@@ -257,5 +257,7 @@
<OutpostModule file="Content/Map/Outposts/MineModule_04.sub" /> <OutpostModule file="Content/Map/Outposts/MineModule_04.sub" />
<OutpostModule file="Content/Map/Outposts/HallModuleHorizontal_Abandoned.sub" /> <OutpostModule file="Content/Map/Outposts/HallModuleHorizontal_Abandoned.sub" />
<OutpostModule file="Content/Map/Outposts/HallModuleVertical_Abandoned.sub" /> <OutpostModule file="Content/Map/Outposts/HallModuleVertical_Abandoned.sub" />
<EnemySubmarine file="Content/Map/EnemySubmarines/DugongPirate.sub"/>
<EnemySubmarine file="Content/Map/EnemySubmarines/HumpbackPirate.sub"/> <EnemySubmarine file="Content/Map/EnemySubmarines/HumpbackPirate.sub"/>
<EnemySubmarine file="Content/Map/EnemySubmarines/Typhon2Pirate.sub"/>
</contentpackage> </contentpackage>
@@ -66,7 +66,7 @@ namespace Barotrauma
private IEnumerable<object> Update(ISpatialEntity targetEntity, Camera cam) private IEnumerable<object> Update(ISpatialEntity targetEntity, Camera cam)
{ {
if (targetEntity == null) { yield return CoroutineStatus.Success; } if (targetEntity == null || (targetEntity is Entity e && e.Removed)) { yield return CoroutineStatus.Success; }
prevControlled = Character.Controlled; prevControlled = Character.Controlled;
if (RemoveControlFromCharacter) if (RemoveControlFromCharacter)
@@ -1475,7 +1475,9 @@ namespace Barotrauma
Character thief = character; Character thief = character;
bool someoneSpoke = false; bool someoneSpoke = false;
if (item.SpawnedInOutpost && !item.AllowStealing && thief.TeamID != CharacterTeamType.FriendlyNPC && !item.HasTag("handlocker")) bool stolenItemsInside = item.OwnInventory?.FindAllItems(it => it.SpawnedInOutpost && !it.AllowStealing, recursive: true).Any() ?? false;
if ((item.SpawnedInOutpost && !item.AllowStealing || stolenItemsInside) && thief.TeamID != CharacterTeamType.FriendlyNPC && !item.HasTag("handlocker"))
{ {
foreach (Character otherCharacter in Character.CharacterList) foreach (Character otherCharacter in Character.CharacterList)
{ {
@@ -56,8 +56,23 @@ namespace Barotrauma
public float BasePriority { get; set; } public float BasePriority { get; set; }
public float PriorityModifier { get; private set; } = 1; public float PriorityModifier { get; private set; } = 1;
// For forcing the highest priority temporarily. Will reset after each priority calculation, so it will need to be kept alive by something. private float resetPriorityTimer;
public bool ForceHighestPriority { get; set; } private readonly float resetPriorityTime = 1;
private bool _forceHighestPriority;
// For forcing the highest priority temporarily. Will reset automatically after one second, unless kept alive by something.
public bool ForceHighestPriority
{
get { return _forceHighestPriority; }
set
{
if (_forceHighestPriority == value) { return; }
_forceHighestPriority = value;
if (_forceHighestPriority)
{
resetPriorityTimer = resetPriorityTime;
}
}
}
// For temporarily forcing walking. Will reset after each priority calculation, so it will need to be kept alive by something. // For temporarily forcing walking. Will reset after each priority calculation, so it will need to be kept alive by something.
// The intention of this boolean to allow walking even when the priority is higher than AIObjectiveManager.RunPriority. // The intention of this boolean to allow walking even when the priority is higher than AIObjectiveManager.RunPriority.
@@ -171,7 +186,6 @@ namespace Barotrauma
Act(deltaTime); Act(deltaTime);
} }
// TODO: check turret aioperate
public void AddSubObjective(AIObjective objective, bool addFirst = false) public void AddSubObjective(AIObjective objective, bool addFirst = false)
{ {
var type = objective.GetType(); var type = objective.GetType();
@@ -294,6 +308,14 @@ namespace Barotrauma
public virtual void Update(float deltaTime) public virtual void Update(float deltaTime)
{ {
if (resetPriorityTimer > 0)
{
resetPriorityTimer -= deltaTime;
}
else
{
ForceHighestPriority = false;
}
if (!objectiveManager.IsOrder(this) && objectiveManager.WaitTimer <= 0) if (!objectiveManager.IsOrder(this) && objectiveManager.WaitTimer <= 0)
{ {
UpdateDevotion(deltaTime); UpdateDevotion(deltaTime);
@@ -91,7 +91,7 @@ namespace Barotrauma
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
if (container == null || (container.Item != null && container.Item.IsThisOrAnyContainerIgnoredByAI())) if (container?.Item == null || container.Item.Removed || container.Item.IsThisOrAnyContainerIgnoredByAI())
{ {
Abandon = true; Abandon = true;
return; return;
@@ -146,7 +146,10 @@ namespace Barotrauma
{ {
DialogueIdentifier = "dialogcannotreachtarget", DialogueIdentifier = "dialogcannotreachtarget",
TargetName = container.Item.Name, TargetName = container.Item.Name,
AbortCondition = obj => !ItemToContain.IsOwnedBy(character), AbortCondition = obj =>
container?.Item == null || container.Item.Removed || container.Item.IsThisOrAnyContainerIgnoredByAI() ||
ItemToContain == null || ItemToContain.Removed ||
!ItemToContain.IsOwnedBy(character) || container.Item.GetRootInventoryOwner() is Character c && c != character,
SpeakIfFails = !objectiveManager.IsCurrentOrder<AIObjectiveCleanupItems>() SpeakIfFails = !objectiveManager.IsCurrentOrder<AIObjectiveCleanupItems>()
}, },
onAbandon: () => Abandon = true, onAbandon: () => Abandon = true,
@@ -614,7 +614,11 @@ namespace Barotrauma
/// <summary> /// <summary>
/// Returns all active objectives of the specific type. Creates a new collection -> don't use too frequently. /// Returns all active objectives of the specific type. Creates a new collection -> don't use too frequently.
/// </summary> /// </summary>
public IEnumerable<T> GetActiveObjectives<T>() where T : AIObjective => CurrentObjective?.GetSubObjectivesRecursive(includingSelf: true).Where(so => so is T).Select(so => so as T); public IEnumerable<T> GetActiveObjectives<T>() where T : AIObjective
{
if (CurrentObjective == null) { return Enumerable.Empty<T>(); }
return CurrentObjective.GetSubObjectivesRecursive(includingSelf: true).Where(so => so is T).Select(so => so as T);
}
public bool HasActiveObjective<T>() where T : AIObjective => CurrentObjective is T || CurrentObjective != null && CurrentObjective.GetSubObjectivesRecursive().Any(so => so is T); public bool HasActiveObjective<T>() where T : AIObjective => CurrentObjective is T || CurrentObjective != null && CurrentObjective.GetSubObjectivesRecursive().Any(so => so is T);
@@ -46,7 +46,7 @@ namespace Barotrauma
Abandon = !isOrder; Abandon = !isOrder;
return Priority; return Priority;
} }
if (component.Item.ConditionPercentage <= 0) if (!isOrder && component.Item.ConditionPercentage <= 0)
{ {
Priority = 0; Priority = 0;
} }
@@ -1,6 +1,7 @@
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.Extensions; using Barotrauma.Extensions;
@@ -43,7 +44,6 @@ namespace Barotrauma
} }
return Priority; return Priority;
} }
// TODO: priority list?
// Ignore items that are being repaired by someone else. // Ignore items that are being repaired by someone else.
if (Item.Repairables.Any(r => r.CurrentFixer != null && r.CurrentFixer != character)) if (Item.Repairables.Any(r => r.CurrentFixer != null && r.CurrentFixer != character))
{ {
@@ -66,7 +66,20 @@ namespace Barotrauma
float devotion = (CumulatedDevotion + selectedBonus) / 100; float devotion = (CumulatedDevotion + selectedBonus) / 100;
float reduction = isPriority ? 1 : isSelected ? 2 : 3; float reduction = isPriority ? 1 : isSelected ? 2 : 3;
float max = AIObjectiveManager.LowestOrderPriority - reduction; float max = AIObjectiveManager.LowestOrderPriority - reduction;
Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1)); float highestWeight = -1;
foreach (string tag in Item.Prefab.Tags)
{
if (JobPrefab.ItemRepairPriorities.TryGetValue(tag, out float weight) && weight > highestWeight)
{
highestWeight = weight;
}
}
if (highestWeight == -1)
{
// Predefined weight not found.
highestWeight = 1;
}
Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (severity * distanceFactor * highestWeight * PriorityModifier), 0, 1));
} }
return Priority; return Priority;
} }
@@ -150,6 +150,8 @@ namespace Barotrauma
//legacy support //legacy support
public readonly string[] AppropriateJobs; public readonly string[] AppropriateJobs;
public readonly string[] Options; public readonly string[] Options;
public readonly string[] HiddenOptions;
public readonly string[] AllOptions;
private readonly Dictionary<string, string> OptionNames; private readonly Dictionary<string, string> OptionNames;
public readonly Dictionary<string, Sprite> OptionSprites; public readonly Dictionary<string, Sprite> OptionSprites;
@@ -310,6 +312,8 @@ namespace Barotrauma
TargetAllCharacters = orderElement.GetAttributeBool("targetallcharacters", false); TargetAllCharacters = orderElement.GetAttributeBool("targetallcharacters", false);
AppropriateJobs = orderElement.GetAttributeStringArray("appropriatejobs", new string[0]); AppropriateJobs = orderElement.GetAttributeStringArray("appropriatejobs", new string[0]);
Options = orderElement.GetAttributeStringArray("options", new string[0]); Options = orderElement.GetAttributeStringArray("options", new string[0]);
HiddenOptions = orderElement.GetAttributeStringArray("hiddenoptions", new string[0]);
AllOptions = Options.Concat(HiddenOptions).ToArray();
var category = orderElement.GetAttributeString("category", null); var category = orderElement.GetAttributeString("category", null);
if (!string.IsNullOrWhiteSpace(category)) { this.Category = (OrderCategory)Enum.Parse(typeof(OrderCategory), category, true); } if (!string.IsNullOrWhiteSpace(category)) { this.Category = (OrderCategory)Enum.Parse(typeof(OrderCategory), category, true); }
MustSetTarget = orderElement.GetAttributeBool("mustsettarget", false); MustSetTarget = orderElement.GetAttributeBool("mustsettarget", false);
@@ -400,7 +400,7 @@ namespace Barotrauma
spawnPos = spawnPoint?.WorldPosition ?? Submarine.MainSub.WorldPosition; spawnPos = spawnPoint?.WorldPosition ?? Submarine.MainSub.WorldPosition;
} }
var pet = Character.Create(speciesName, spawnPos, seed); var pet = Character.Create(speciesName, spawnPos, seed);
var petBehavior = (pet.AIController as EnemyAIController)?.PetBehavior; var petBehavior = (pet?.AIController as EnemyAIController)?.PetBehavior;
if (petBehavior != null) if (petBehavior != null)
{ {
petBehavior.Owner = owner; petBehavior.Owner = owner;
@@ -113,7 +113,7 @@ namespace Barotrauma
if (CurrentOrder != null && OrderedCharacter.GetCurrentOrderWithTopPriority()?.Order != CurrentOrder) if (CurrentOrder != null && OrderedCharacter.GetCurrentOrderWithTopPriority()?.Order != CurrentOrder)
{ {
#if DEBUG #if DEBUG
DebugConsole.NewMessage($"Order {CurrentOrder.Name} did not match current order for character {OrderedCharacter} in {this}"); ShipCommandManager.ShipCommandLog($"Order {CurrentOrder.Name} did not match current order for character {OrderedCharacter} in {this}");
#endif #endif
return false; return false;
} }
@@ -121,7 +121,7 @@ namespace Barotrauma
if (!shipCommandManager.AbleToTakeOrder(OrderedCharacter)) if (!shipCommandManager.AbleToTakeOrder(OrderedCharacter))
{ {
#if DEBUG #if DEBUG
DebugConsole.NewMessage(OrderedCharacter + " was unable to perform assigned order in " + this); ShipCommandManager.ShipCommandLog(OrderedCharacter + " was unable to perform assigned order in " + this);
#endif #endif
return false; return false;
} }
@@ -93,7 +93,7 @@ namespace Barotrauma
} }
} }
static void ShipCommandLog(string text) public static void ShipCommandLog(string text)
{ {
if (GameSettings.VerboseLogging) if (GameSettings.VerboseLogging)
{ {
@@ -74,7 +74,7 @@ namespace Barotrauma
} }
} }
public bool HasMultipleLimbsOfSameType => Limbs.Length > limbDictionary.Count; public bool HasMultipleLimbsOfSameType => limbs == null ? false : Limbs.Length > limbDictionary.Count;
private bool frozen; private bool frozen;
public bool Frozen public bool Frozen
@@ -416,10 +416,7 @@ namespace Barotrauma
protected void CreateColliders() protected void CreateColliders()
{ {
if (collider != null) collider?.ForEach(c => c.Remove());
{
collider.ForEach(c => c.Remove());
}
DebugConsole.Log($"Creating colliders from {RagdollParams.Name}."); DebugConsole.Log($"Creating colliders from {RagdollParams.Name}.");
collider = new List<PhysicsBody>(); collider = new List<PhysicsBody>();
foreach (var cParams in RagdollParams.Colliders) foreach (var cParams in RagdollParams.Colliders)
@@ -479,10 +476,7 @@ namespace Barotrauma
protected void CreateLimbs() protected void CreateLimbs()
{ {
if (limbs != null) limbs?.ForEach(l => l.Remove());
{
limbs.ForEach(l => l.Remove());
}
DebugConsole.Log($"Creating limbs from {RagdollParams.Name}."); DebugConsole.Log($"Creating limbs from {RagdollParams.Name}.");
limbDictionary = new Dictionary<LimbType, Limb>(); limbDictionary = new Dictionary<LimbType, Limb>();
limbs = new Limb[RagdollParams.Limbs.Count]; limbs = new Limb[RagdollParams.Limbs.Count];
@@ -395,14 +395,12 @@ namespace Barotrauma
Affliction affliction; Affliction affliction;
string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant(); string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, System.StringComparison.OrdinalIgnoreCase)); afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, System.StringComparison.OrdinalIgnoreCase));
if (afflictionPrefab != null) if (afflictionPrefab == null)
{ {
affliction = afflictionPrefab.Instantiate(0.0f); DebugConsole.ThrowError($"Couldn't find the affliction with the identifier {afflictionIdentifier} referenced in {element.Document.ParseContentPathFromUri()}");
} continue;
else
{
affliction = new Affliction(null, 0);
} }
affliction = afflictionPrefab.Instantiate(0.0f);
affliction.Deserialize(subElement); affliction.Deserialize(subElement);
//backwards compatibility //backwards compatibility
if (subElement.Attribute("amount") != null && subElement.Attribute("strength") == null) if (subElement.Attribute("amount") != null && subElement.Attribute("strength") == null)
@@ -1523,6 +1523,16 @@ namespace Barotrauma
greatestNegativeHealthMultiplier = 1f; greatestNegativeHealthMultiplier = 1f;
} }
/// <summary>
/// Can be used to modify a character's health for runtime session. Change with AddHealthMultiplier
/// </summary>
public float StaticHealthMultiplier { get; private set; } = 1;
public void AddStaticHealthMultiplier(float newMultiplier)
{
StaticHealthMultiplier *= newMultiplier;
}
/// <summary> /// <summary>
/// Speed reduction from the current limb specific damage. Min 0, max 1. /// Speed reduction from the current limb specific damage. Min 0, max 1.
/// </summary> /// </summary>
@@ -1916,19 +1926,19 @@ namespace Barotrauma
return AnimController.GetLimb(LimbType.Head) ?? AnimController.GetLimb(LimbType.Torso) ?? AnimController.MainLimb; return AnimController.GetLimb(LimbType.Head) ?? AnimController.GetLimb(LimbType.Torso) ?? AnimController.MainLimb;
} }
public bool CanSeeTarget(ISpatialEntity target, Limb seeingLimb = null) public bool CanSeeTarget(ISpatialEntity target, ISpatialEntity seeingEntity = null)
{ {
seeingLimb ??= GetSeeingLimb(); seeingEntity ??= AnimController.SimplePhysicsEnabled ? this as ISpatialEntity : GetSeeingLimb() as ISpatialEntity;
if (seeingLimb == null) { return false; } if (seeingEntity == null) { return false; }
ISpatialEntity seeingEntity = AnimController.SimplePhysicsEnabled ? this : seeingLimb as ISpatialEntity; ISpatialEntity sourceEntity = seeingEntity ;
// TODO: Could we just use the method below? If not, let's refactor it so that we can. // TODO: Could we just use the method below? If not, let's refactor it so that we can.
Vector2 diff = ConvertUnits.ToSimUnits(target.WorldPosition - seeingEntity.WorldPosition); Vector2 diff = ConvertUnits.ToSimUnits(target.WorldPosition - sourceEntity.WorldPosition);
Body closestBody; Body closestBody;
//both inside the same sub (or both outside) //both inside the same sub (or both outside)
//OR the we're inside, the other character outside //OR the we're inside, the other character outside
if (target.Submarine == Submarine || target.Submarine == null) if (target.Submarine == Submarine || target.Submarine == null)
{ {
closestBody = Submarine.CheckVisibility(seeingEntity.SimPosition, seeingEntity.SimPosition + diff); closestBody = Submarine.CheckVisibility(sourceEntity.SimPosition, sourceEntity.SimPosition + diff);
} }
//we're outside, the other character inside //we're outside, the other character inside
else if (Submarine == null) else if (Submarine == null)
@@ -1938,7 +1948,7 @@ namespace Barotrauma
//both inside different subs //both inside different subs
else else
{ {
closestBody = Submarine.CheckVisibility(seeingEntity.SimPosition, seeingEntity.SimPosition + diff); closestBody = Submarine.CheckVisibility(sourceEntity.SimPosition, sourceEntity.SimPosition + diff);
if (!IsBlocking(closestBody)) if (!IsBlocking(closestBody))
{ {
closestBody = Submarine.CheckVisibility(target.SimPosition, target.SimPosition - diff); closestBody = Submarine.CheckVisibility(target.SimPosition, target.SimPosition - diff);
@@ -1966,29 +1976,6 @@ namespace Barotrauma
} }
} }
/// <summary>
/// TODO: ensure that works. CheckVisibility takes positions in sim space, but this method uses world positions
/// </summary>
public bool CanSeeCharacter(Character target, Vector2 sourceWorldPos)
{
Vector2 diff = ConvertUnits.ToSimUnits(target.WorldPosition - sourceWorldPos);
Body closestBody;
if (target.Submarine == null)
{
closestBody = Submarine.CheckVisibility(sourceWorldPos, sourceWorldPos + diff);
if (closestBody == null) { return true; }
}
else
{
closestBody = Submarine.CheckVisibility(target.WorldPosition, target.WorldPosition - diff);
if (closestBody == null) { return true; }
}
Structure wall = closestBody.UserData as Structure;
Item item = closestBody.UserData as Item;
Door door = item?.GetComponent<Door>();
return (wall == null || !wall.CastShadow) && (door == null || door.CanBeTraversed);
}
/// <summary> /// <summary>
/// A simple check if the character Dir is towards the target or not. Uses the world coordinates. /// A simple check if the character Dir is towards the target or not. Uses the world coordinates.
/// </summary> /// </summary>
@@ -2233,7 +2220,10 @@ namespace Barotrauma
Rectangle itemDisplayRect = new Rectangle(item.InteractionRect.X, item.InteractionRect.Y - item.InteractionRect.Height, item.InteractionRect.Width, item.InteractionRect.Height); Rectangle itemDisplayRect = new Rectangle(item.InteractionRect.X, item.InteractionRect.Y - item.InteractionRect.Height, item.InteractionRect.Width, item.InteractionRect.Height);
// Get the point along the line between lowerBodyPosition and upperBodyPosition which is closest to the center of itemDisplayRect // Get the point along the line between lowerBodyPosition and upperBodyPosition which is closest to the center of itemDisplayRect
Vector2 playerDistanceCheckPosition = Vector2.Clamp(itemDisplayRect.Center.ToVector2(), lowerBodyPosition, upperBodyPosition); Vector2 playerDistanceCheckPosition =
lowerBodyPosition.Y < upperBodyPosition.Y ?
Vector2.Clamp(itemDisplayRect.Center.ToVector2(), lowerBodyPosition, upperBodyPosition) :
Vector2.Clamp(itemDisplayRect.Center.ToVector2(), upperBodyPosition, lowerBodyPosition);
// If playerDistanceCheckPosition is inside the itemDisplayRect then we consider the character to within 0 distance of the item // If playerDistanceCheckPosition is inside the itemDisplayRect then we consider the character to within 0 distance of the item
if (itemDisplayRect.Contains(playerDistanceCheckPosition)) if (itemDisplayRect.Contains(playerDistanceCheckPosition))
@@ -2271,7 +2261,7 @@ namespace Barotrauma
itemPosition -= Submarine.SimPosition; itemPosition -= Submarine.SimPosition;
} }
var body = Submarine.CheckVisibility(SimPosition, itemPosition, ignoreLevel: true); var body = Submarine.CheckVisibility(SimPosition, itemPosition, ignoreLevel: true);
if (body != null && body.UserData as Item != item) { return false; } if (body != null && body.UserData as Item != item && Submarine.LastPickedFixture?.UserData as Item != item) { return false; }
} }
return true; return true;
@@ -3657,7 +3647,11 @@ namespace Barotrauma
// OnDamaged is called only for the limb that is hit. // OnDamaged is called only for the limb that is hit.
AnimController.Limbs.ForEach(l => l.ApplyStatusEffects(actionType, deltaTime)); AnimController.Limbs.ForEach(l => l.ApplyStatusEffects(actionType, deltaTime));
} }
CharacterHealth.ApplyAfflictionStatusEffects(actionType); //OnActive effects are handled by the afflictions themselves
if (actionType != ActionType.OnActive)
{
CharacterHealth.ApplyAfflictionStatusEffects(actionType);
}
} }
private void Implode(bool isNetworkMessage = false) private void Implode(bool isNetworkMessage = false)
@@ -3801,10 +3795,7 @@ namespace Barotrauma
return; return;
} }
if (aiTarget != null) aiTarget?.Remove();
{
aiTarget.Remove();
}
aiTarget = new AITarget(this); aiTarget = new AITarget(this);
CharacterHealth.RemoveAllAfflictions(); CharacterHealth.RemoveAllAfflictions();
@@ -456,7 +456,7 @@ namespace Barotrauma
public bool IsAttachmentsLoaded => HairIndex > -1 && BeardIndex > -1 && MoustacheIndex > -1 && FaceAttachmentIndex > -1; public bool IsAttachmentsLoaded => HairIndex > -1 && BeardIndex > -1 && MoustacheIndex > -1 && FaceAttachmentIndex > -1;
// Used for creating the data // Used for creating the data
public CharacterInfo(string speciesName, string name = "", string originalName = "", JobPrefab jobPrefab = null, string ragdollFileName = null, int variant = 0, Rand.RandSync randSync = Rand.RandSync.Unsynced) public CharacterInfo(string speciesName, string name = "", string originalName = "", JobPrefab jobPrefab = null, string ragdollFileName = null, int variant = 0, Rand.RandSync randSync = Rand.RandSync.Unsynced, string npcIdentifier = "")
{ {
if (speciesName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)) if (speciesName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{ {
@@ -485,6 +485,10 @@ namespace Barotrauma
{ {
Name = name; Name = name;
} }
else if (!string.IsNullOrEmpty(npcIdentifier) && TextManager.Get("npctitle." + npcIdentifier, true) is string npcTitle)
{
Name = npcTitle;
}
else else
{ {
name = ""; name = "";
@@ -150,12 +150,9 @@ namespace Barotrauma
{ {
max += Character.Info.Job.Prefab.VitalityModifier; max += Character.Info.Job.Prefab.VitalityModifier;
} }
max *= Character.StaticHealthMultiplier;
return max * Character.HealthMultiplier; return max * Character.HealthMultiplier;
} }
set
{
maxVitality = Math.Max(0, value);
}
} }
public float MinVitality public float MinVitality
@@ -121,11 +121,12 @@ namespace Barotrauma
public void InitializeCharacter(Character npc, ISpatialEntity positionToStayIn = null) public void InitializeCharacter(Character npc, ISpatialEntity positionToStayIn = null)
{ {
npc.CharacterHealth.MaxVitality *= HealthMultiplier; npc.AddStaticHealthMultiplier(HealthMultiplier);
if (GameMain.NetworkMember != null) if (GameMain.NetworkMember != null)
{ {
npc.CharacterHealth.MaxVitality *= HealthMultiplierInMultiplayer; npc.AddStaticHealthMultiplier(HealthMultiplierInMultiplayer);
} }
var humanAI = npc.AIController as HumanAIController; var humanAI = npc.AIController as HumanAIController;
if (humanAI != null) if (humanAI != null)
{ {
@@ -227,10 +228,7 @@ namespace Barotrauma
} }
IdCard idCardComponent = item.GetComponent<IdCard>(); IdCard idCardComponent = item.GetComponent<IdCard>();
if (idCardComponent != null) idCardComponent?.Initialize(character.Info);
{
idCardComponent.Initialize(character.Info);
}
var idCardTags = itemElement.GetAttributeStringArray("tags", new string[0]); var idCardTags = itemElement.GetAttributeStringArray("tags", new string[0]);
foreach (string tag in idCardTags) foreach (string tag in idCardTags)
@@ -243,10 +241,7 @@ namespace Barotrauma
{ {
wifiComponent.TeamID = character.TeamID; wifiComponent.TeamID = character.TeamID;
} }
if (parentItem != null) parentItem?.Combine(item, user: null);
{
parentItem.Combine(item, user: null);
}
foreach (XElement childItemElement in itemElement.Elements()) foreach (XElement childItemElement in itemElement.Elements())
{ {
InitializeItem(character, childItemElement, submarine, humanPrefab, item, createNetworkEvents); InitializeItem(character, childItemElement, submarine, humanPrefab, item, createNetworkEvents);
@@ -204,10 +204,7 @@ namespace Barotrauma
item.AddTag("job:" + Name); item.AddTag("job:" + Name);
IdCard idCardComponent = item.GetComponent<IdCard>(); IdCard idCardComponent = item.GetComponent<IdCard>();
if (idCardComponent != null) idCardComponent?.Initialize(character.Info);
{
idCardComponent.Initialize(character.Info);
}
} }
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>()) foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
@@ -1,9 +1,9 @@
using Microsoft.Xna.Framework; using Barotrauma.Extensions;
using System.Collections.Generic; using Microsoft.Xna.Framework;
using System.Xml.Linq;
using Barotrauma.Extensions;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -43,6 +43,12 @@ namespace Barotrauma
Prefabs.Remove(this); Prefabs.Remove(this);
} }
private static readonly Dictionary<string, float> _itemRepairPriorities = new Dictionary<string, float>();
/// <summary>
/// Tag -> priority.
/// </summary>
public static IReadOnlyDictionary<string, float> ItemRepairPriorities => _itemRepairPriorities;
public static XElement NoJobElement; public static XElement NoJobElement;
public static JobPrefab Get(string identifier) public static JobPrefab Get(string identifier)
{ {
@@ -294,7 +300,7 @@ namespace Barotrauma
} }
foreach (XElement element in mainElement.Elements()) foreach (XElement element in mainElement.Elements())
{ {
if (element.Name.ToString().Equals("nojob", StringComparison.OrdinalIgnoreCase)) { continue; } if (!element.Name.ToString().Equals("job", StringComparison.OrdinalIgnoreCase)) { continue; }
if (element.IsOverride()) if (element.IsOverride())
{ {
var job = new JobPrefab(element.FirstElement(), file.Path) var job = new JobPrefab(element.FirstElement(), file.Path)
@@ -312,8 +318,31 @@ namespace Barotrauma
Prefabs.Add(job, false); Prefabs.Add(job, false);
} }
} }
NoJobElement = NoJobElement ?? mainElement.Element("NoJob"); NoJobElement ??= mainElement.GetChildElement("nojob");
NoJobElement = NoJobElement ?? mainElement.Element("nojob"); var itemRepairPrioritiesElement = mainElement.GetChildElement("ItemRepairPriorities");
if (itemRepairPrioritiesElement != null)
{
foreach (var subElement in itemRepairPrioritiesElement.Elements())
{
string tag = subElement.GetAttributeString("tag", null);
if (tag != null)
{
float priority = subElement.GetAttributeFloat("priority", -1f);
if (priority >= 0)
{
_itemRepairPriorities.TryAdd(tag, priority);
}
else
{
DebugConsole.AddWarning($"The 'priority' attribute is missing from the the item repair priorities definition in {subElement} of {file.Path}.");
}
}
else
{
DebugConsole.AddWarning($"The 'tag' attribute is missing from the the item repair priorities definition in {subElement} of {file.Path}.");
}
}
}
} }
public static void RemoveByFile(string filePath) public static void RemoveByFile(string filePath)
@@ -837,10 +837,7 @@ namespace Barotrauma
} }
} }
if (attack != null) attack?.UpdateCoolDown(deltaTime);
{
attack.UpdateCoolDown(deltaTime);
}
} }
private float reEnableTimer = -1; private float reEnableTimer = -1;
@@ -194,13 +194,13 @@ namespace Barotrauma
isCorePackage = value; isCorePackage = value;
if (isCorePackage && regularPackages.Contains(this)) if (isCorePackage && regularPackages.Contains(this))
{ {
corePackages.Add(this); corePackages.AddOnMainThread(this);
regularPackages.Remove(this); regularPackages.RemoveOnMainThread(this);
} }
else if (!isCorePackage && corePackages.Contains(this)) else if (!isCorePackage && corePackages.Contains(this))
{ {
regularPackages.Add(this); regularPackages.AddOnMainThread(this);
corePackages.Remove(this); corePackages.RemoveOnMainThread(this);
} }
} }
} }
@@ -411,6 +411,7 @@ namespace Barotrauma
case ContentType.Submarine: case ContentType.Submarine:
case ContentType.Wreck: case ContentType.Wreck:
case ContentType.BeaconStation: case ContentType.BeaconStation:
case ContentType.EnemySubmarine:
break; break;
default: default:
try try
@@ -528,7 +529,7 @@ namespace Barotrauma
{ {
refreshFiles = true; refreshFiles = true;
} }
corePackages.Remove(p); corePackages.RemoveOnMainThread(p);
} }
else else
{ {
@@ -536,16 +537,16 @@ namespace Barotrauma
{ {
refreshFiles = true; refreshFiles = true;
} }
regularPackages.Remove(p); regularPackages.RemoveOnMainThread(p);
} }
} }
if (IsCorePackage) if (IsCorePackage)
{ {
corePackages.Add(this); corePackages.AddOnMainThread(this);
} }
else else
{ {
regularPackages.Add(this); regularPackages.AddOnMainThread(this);
} }
if (refreshFiles) if (refreshFiles)
@@ -743,18 +744,18 @@ namespace Barotrauma
} }
if (newPackage.IsCorePackage) if (newPackage.IsCorePackage)
{ {
corePackages.Add(newPackage); corePackages.AddOnMainThread(newPackage);
} }
else else
{ {
regularPackages.Add(newPackage); regularPackages.AddOnMainThread(newPackage);
} }
} }
public static void RemovePackage(ContentPackage package) public static void RemovePackage(ContentPackage package)
{ {
if (package.IsCorePackage) { corePackages.Remove(package); } if (package.IsCorePackage) { corePackages.RemoveOnMainThread(package); }
else { regularPackages.Remove(package); } else { regularPackages.RemoveOnMainThread(package); }
} }
public static void LoadAll() public static void LoadAll()
@@ -775,9 +776,9 @@ namespace Barotrauma
IEnumerable<string> files = Directory.GetFiles(folder, "*.xml"); IEnumerable<string> files = Directory.GetFiles(folder, "*.xml");
corePackages.Clear(); corePackages.ClearOnMainThread();
var prevRegularPackages = regularPackages.Select(p => p.Name.ToLowerInvariant()).ToList(); var prevRegularPackages = regularPackages.Select(p => p.Name.ToLowerInvariant()).ToList();
regularPackages.Clear(); regularPackages.ClearOnMainThread();
foreach (string filePath in files) foreach (string filePath in files)
{ {
@@ -815,7 +816,7 @@ namespace Barotrauma
.OrderBy(p => order(p)) .OrderBy(p => order(p))
.ThenBy(p => regularPackages.IndexOf(p)) .ThenBy(p => regularPackages.IndexOf(p))
.ToList(); .ToList();
regularPackages.Clear(); regularPackages.AddRange(ordered); regularPackages.ClearOnMainThread(); regularPackages.AddRangeOnMainThread(ordered);
(config ?? GameMain.Config)?.SortContentPackages(refreshAll); (config ?? GameMain.Config)?.SortContentPackages(refreshAll);
} }
@@ -825,12 +826,12 @@ namespace Barotrauma
{ {
if (IsCorePackage) if (IsCorePackage)
{ {
corePackages.Remove(this); corePackages.RemoveOnMainThread(this);
if (GameMain.Config.CurrentCorePackage == this) { GameMain.Config.AutoSelectCorePackage(null); } if (GameMain.Config.CurrentCorePackage == this) { GameMain.Config.AutoSelectCorePackage(null); }
} }
else else
{ {
regularPackages.Remove(this); regularPackages.RemoveOnMainThread(this);
if (GameMain.Config.EnabledRegularPackages.Contains(this)) { GameMain.Config.DisableRegularPackage(this); } if (GameMain.Config.EnabledRegularPackages.Contains(this)) { GameMain.Config.DisableRegularPackage(this); }
} }
GameMain.Config.SaveNewPlayerConfig(); GameMain.Config.SaveNewPlayerConfig();
@@ -303,9 +303,15 @@ namespace Barotrauma
private bool IsValidTarget(Entity e) private bool IsValidTarget(Entity e)
{ {
return bool isValid = e is Character character && !character.Removed && !character.IsDead && !character.IsIncapacitated &&
e is Character character && !character.Removed && !character.IsDead && !character.IsIncapacitated &&
(e == Character.Controlled || character.IsRemotePlayer); (e == Character.Controlled || character.IsRemotePlayer);
#if SERVER
UpdateIgnoredClients();
isValid &= !ignoredClients.Keys.Any(c => c.Character == e);
#elif CLIENT
isValid &= (e != Character.Controlled || !GUI.InputBlockingMenuOpen);
#endif
return isValid;
} }
private void TryStartConversation(Character speaker, Character targetCharacter = null) private void TryStartConversation(Character speaker, Character targetCharacter = null)

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