Unstable 0.16.1.0

This commit is contained in:
Markus Isberg
2022-01-27 00:30:32 +09:00
parent 7d6421a548
commit b259af5911
161 changed files with 1913 additions and 638 deletions

View File

@@ -1,7 +1,11 @@
namespace Barotrauma
using Microsoft.Xna.Framework;
namespace Barotrauma
{
abstract partial class AIObjective
{
public static Color ObjectiveIconColor => Color.LightGray;
public static Sprite GetSprite(string identifier, string option, Entity targetEntity)
{
if (string.IsNullOrEmpty(identifier))

View File

@@ -154,7 +154,7 @@ namespace Barotrauma
public bool PlaySound;
public GUIMessage(string rawText, Color color, float delay, string identifier = null, int? value = null)
public GUIMessage(string rawText, Color color, float delay, string identifier = null, int? value = null, float lifeTime = 3.0f)
{
RawText = Text = rawText;
if (value.HasValue)
@@ -166,7 +166,7 @@ namespace Barotrauma
Size = GUI.Font.MeasureString(Text);
Color = color;
Identifier = identifier;
Lifetime = 3.0f;
Lifetime = lifeTime;
}
}
@@ -997,7 +997,7 @@ namespace Barotrauma
return nameColor;
}
public void AddMessage(string rawText, Color color, bool playSound, string identifier = null, int? value = null)
public void AddMessage(string rawText, Color color, bool playSound, string identifier = null, int? value = null, float lifetime = 3.0f)
{
GUIMessage existingMessage = null;
@@ -1026,7 +1026,7 @@ namespace Barotrauma
}
if (existingMessage == null || !value.HasValue)
{
var newMessage = new GUIMessage(rawText, color, delay, identifier, value);
var newMessage = new GUIMessage(rawText, color, delay, identifier, value, lifetime);
guiMessages.Insert(0, newMessage);
if (playSound)
{

View File

@@ -113,7 +113,7 @@ namespace Barotrauma
return
character?.Inventory != null &&
character.AllowInput &&
!character.Removed && !character.IsKnockedDown &&
(controller?.User != character || !controller.HideHUD) &&
!IsCampaignInterfaceOpen &&
!ConversationAction.FadeScreenToBlack;

View File

@@ -16,6 +16,7 @@ namespace Barotrauma
private static Sprite infoAreaPortraitBG;
public bool LastControlled;
public int CrewListIndex { get; set; } = -1;
#warning TODO: Refactor
private Sprite disguisedPortrait;
@@ -831,7 +832,7 @@ namespace Barotrauma
};
new GUIFrame(
new RectTransform(new Vector2(1.25f, 1.25f), HeadSelectionList.RectTransform, Anchor.Center),
new RectTransform(new Vector2(1.25f, 1.25f), HeadSelectionList.ContentBackground.RectTransform, Anchor.Center),
style: "OuterGlow", color: Color.Black)
{
UserData = "outerglow",
@@ -966,10 +967,15 @@ namespace Barotrauma
foreach (Sprite sprite in characterSprites) { sprite.Remove(); }
characterSprites.Clear();
}
public void Dispose()
{
ClearSprites();
if (HeadSelectionList != null)
{
HeadSelectionList.RectTransform.Parent = null;
HeadSelectionList = null;
}
}
~AppearanceCustomizationMenu()

View File

@@ -143,7 +143,7 @@ namespace Barotrauma
Hull fireHull = Hull.hullList.GetRandom(h => h.Submarine == character.Submarine);
if (fireHull != null)
{
var fakeFire = new DummyFireSource(Vector2.One * 500.0f, new Vector2(Rand.Range(fireHull.WorldRect.X, fireHull.WorldRect.Right), fireHull.WorldPosition.Y), fireHull, isNetworkMessage: true)
var fakeFire = new DummyFireSource(Vector2.One * 500.0f, new Vector2(Rand.Range(fireHull.WorldRect.X, fireHull.WorldRect.Right), fireHull.WorldPosition.Y + 1), fireHull, isNetworkMessage: true)
{
CausedByPsychosis = true,
DamagesItems = false,

View File

@@ -2018,6 +2018,27 @@ namespace Barotrauma
limbIndicatorOverlay?.Remove();
limbIndicatorOverlay = null;
if (healthWindow != null)
{
healthWindow.RectTransform.Parent = null;
healthWindow = null;
}
if (healthBarHolder != null)
{
healthBarHolder.RectTransform.Parent = null;
healthBarHolder = null;
}
if (SuicideButton != null)
{
SuicideButton.RectTransform.Parent = null;
SuicideButton = null;
}
if (afflictionTooltip != null)
{
afflictionTooltip.RectTransform.Parent = null;
afflictionTooltip = null;
}
}
}
}

View File

@@ -389,6 +389,12 @@ namespace Barotrauma
DrawString(spriteBatch, new Vector2(10, 10),
"FPS: " + Math.Round(GameMain.PerformanceCounter.AverageFramesPerSecond),
Color.White, Color.Black * 0.5f, 0, SmallFont);
if (GameMain.GameSession != null && Timing.TotalTime > GameMain.GameSession.RoundStartTime + 1.0)
{
DrawString(spriteBatch, new Vector2(10, 25),
$"Physics: {GameMain.CurrentUpdateRate}",
(GameMain.CurrentUpdateRate < Timing.FixedUpdateRate) ? Color.Red : Color.White, Color.Black * 0.5f, 0, SmallFont);
}
}
if (GameMain.ShowPerf)
@@ -397,7 +403,7 @@ namespace Barotrauma
DrawString(spriteBatch, new Vector2(300, y),
"Draw - Avg: " + GameMain.PerformanceCounter.DrawTimeGraph.Average().ToString("0.00") + " ms" +
" Max: " + GameMain.PerformanceCounter.DrawTimeGraph.LargestValue().ToString("0.00") + " ms",
GUI.Style.Green, Color.Black * 0.8f, font: SmallFont);
Style.Green, Color.Black * 0.8f, font: SmallFont);
y += 15;
GameMain.PerformanceCounter.DrawTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), color: Style.Green);
y += 50;
@@ -408,7 +414,6 @@ namespace Barotrauma
Color.LightBlue, Color.Black * 0.8f, font: SmallFont);
y += 15;
GameMain.PerformanceCounter.UpdateTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), color: Color.LightBlue);
GameMain.PerformanceCounter.UpdateIterationsGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), maxValue: 20, color: Style.Red);
y += 50;
foreach (string key in GameMain.PerformanceCounter.GetSavedIdentifiers)
{
@@ -431,7 +436,7 @@ namespace Barotrauma
}
}
if (GameMain.DebugDraw)
if (GameMain.DebugDraw && !Submarine.Unloading && !(Screen.Selected is RoundSummaryScreen))
{
DrawString(spriteBatch, new Vector2(10, 25),
"Physics: " + GameMain.World.UpdateTime,
@@ -2435,6 +2440,11 @@ namespace Barotrauma
private static bool TogglePauseMenu(GUIButton button, object obj)
{
pauseMenuOpen = !pauseMenuOpen;
if (!pauseMenuOpen && PauseMenu != null)
{
PauseMenu.RectTransform.Parent = null;
PauseMenu = null;
}
return true;
}

View File

@@ -22,13 +22,14 @@ namespace Barotrauma
{
GameMain.Instance.ResolutionChanged += RecalculateSize;
}
_instance.ItemComponentHolder = new GUIFrame(new RectTransform(Vector2.One, _instance, Anchor.Center)).RectTransform;
_instance.ChildrenChanged += OnChildrenChanged;
}
return _instance;
}
}
public RectTransform ItemComponentHolder;
//GUICanvas stores the children as weak references, to allow elements that we no longer need to get garbage collected
private readonly List<WeakReference<RectTransform>> childrenWeakRef = new List<WeakReference<RectTransform>>();
private static Vector2 size => new Vector2(GameMain.GraphicsWidth / (float)GUI.UIWidth, 1f);
@@ -36,16 +37,41 @@ namespace Barotrauma
private enum ResizeAxis { Both = 0, X = 1, Y = 2 }
private static void OnChildrenChanged(RectTransform _)
{
//add weak reference if we don't have one yet
foreach (var child in _instance.Children)
{
if (!_instance.childrenWeakRef.Any(c => c.TryGetTarget(out var existingChild) && existingChild == child))
{
_instance.childrenWeakRef.Add(new WeakReference<RectTransform>(child));
}
}
//get rid of strong references
_instance.children.Clear();
//remove dead children
for (int i = _instance.childrenWeakRef.Count - 2; i >= 0; i--)
{
if (!_instance.childrenWeakRef[i].TryGetTarget(out var child) || child.Parent != _instance)
{
_instance.childrenWeakRef.RemoveAt(i);
}
}
}
// Turn public, if there is a need to call this manually.
private static void RecalculateSize()
{
Vector2 recalculatedSize = size;
// Scale children that are supposed to encompass the whole screen so that they are properly scaled on ultrawide as well
for (int i = 0; i < Instance.Children.Count(); i++)
for (int i = 0; i < Instance.childrenWeakRef.Count; i++)
{
RectTransform target = Instance.GetChild(i);
if (target == null || target.RelativeSize.X < 1 && target.RelativeSize.Y < 1) continue;
if (!_instance.childrenWeakRef[i].TryGetTarget(out RectTransform target) || target == null) { continue; };
_instance.children.Add(target);
if (target.RelativeSize.X < 1 && target.RelativeSize.Y < 1) { continue; }
ResizeAxis axis;
@@ -80,6 +106,7 @@ namespace Barotrauma
Instance.Resize(size, resizeChildren: true);
Instance.GetAllChildren().Select(c => c.GUIComponent as GUITextBlock).ForEach(t => t?.SetTextPos());
_instance.children.Clear();
}
}
}

View File

@@ -362,6 +362,14 @@ namespace Barotrauma
RectTransform.ScaleChanged += () => dimensionsNeedsRecalculation = true;
RectTransform.SizeChanged += () => dimensionsNeedsRecalculation = true;
UpdateDimensions();
rectT.ChildrenChanged += CheckForChildren;
}
private void CheckForChildren(RectTransform rectT)
{
if (rectT == ScrollBar.RectTransform || rectT == Content.RectTransform || rectT == ContentBackground.RectTransform) { return; }
throw new InvalidOperationException($"Children were added to {nameof(GUIListBox)}, Add them to {nameof(GUIListBox)}.{nameof(Content)} instead.");
}
public void UpdateDimensions()
@@ -827,13 +835,6 @@ namespace Barotrauma
protected override void Update(float deltaTime)
{
foreach (GUIComponent child in Children)
{
if (child == ScrollBar || child == Content || child == ContentBackground) { continue; }
throw new InvalidOperationException($"Children were found in {nameof(GUIListBox)}, Add them to {nameof(GUIListBox)}.{nameof(Content)} instead.");
}
if (!Visible) { return; }
UpdateChildrenRect();

View File

@@ -618,6 +618,7 @@ namespace Barotrauma
public bool Close(GUIButton button, object obj)
{
RectTransform.Parent = null;
Close();
return true;
}

View File

@@ -347,7 +347,7 @@ namespace Barotrauma
// sum up all the afflictions and their strengths
Dictionary<AfflictionPrefab, float> afflictionAndStrength = new Dictionary<AfflictionPrefab, float>();
foreach (Affliction affliction in health.GetAllAfflictions().Where(a => !a.Prefab.IsBuff && a.Strength > 0))
foreach (Affliction affliction in health.GetAllAfflictions().Where(a => MedicalClinic.IsHealable(a)))
{
if (afflictionAndStrength.TryGetValue(affliction.Prefab, out float strength))
{
@@ -581,7 +581,6 @@ namespace Barotrauma
OnClicked = (button, _) =>
{
button.Enabled = false;
ClosePopup();
medicalClinic.HealAllButtonAction(request =>
{
switch (request.HealResult)
@@ -595,7 +594,9 @@ namespace Barotrauma
}
button.Enabled = true;
ClosePopup();
});
ClosePopup();
return true;
}
};
@@ -993,7 +994,7 @@ namespace Barotrauma
}
}
private void ClosePopup()
public void ClosePopup()
{
if (selectedCrewElement is { } popup)
{

View File

@@ -58,7 +58,7 @@ namespace Barotrauma
}
}
private readonly List<RectTransform> children = new List<RectTransform>();
protected readonly List<RectTransform> children = new List<RectTransform>();
public IEnumerable<RectTransform> Children => children;
public int CountChildren => children.Count;

View File

@@ -20,7 +20,7 @@ namespace Barotrauma
private static Sprite ownerIcon, moderatorIcon;
public enum InfoFrameTab { Crew, Mission, Reputation, Traitor, Submarine, Talents };
public static InfoFrameTab selectedTab;
public static InfoFrameTab SelectedTab { get; private set; }
private GUIFrame infoFrame, contentFrame;
private readonly List<GUIButton> tabButtons = new List<GUIButton>();
@@ -130,8 +130,8 @@ namespace Barotrauma
{
if (!initialized) { Initialize(); }
CreateInfoFrame(selectedTab);
SelectInfoFrameTab(null, selectedTab);
CreateInfoFrame(SelectedTab);
SelectInfoFrameTab(SelectedTab);
}
public void Update()
@@ -147,8 +147,8 @@ namespace Barotrauma
}
}
if (selectedTab != InfoFrameTab.Crew) return;
if (linkedGUIList == null) return;
if (SelectedTab != InfoFrameTab.Crew) { return; }
if (linkedGUIList == null) { return; }
if (GameMain.IsMultiplayer)
{
@@ -226,7 +226,7 @@ namespace Barotrauma
{
UserData = tab,
ToolTip = TextManager.Get(textTag),
OnClicked = SelectInfoFrameTab
OnClicked = (btn, userData) => { SelectInfoFrameTab((InfoFrameTab)userData); return true; }
};
tabButtons.Add(newButton);
return newButton;
@@ -277,16 +277,16 @@ namespace Barotrauma
talentsButton.Enabled = Character.Controlled?.Info != null;
if (!talentsButton.Enabled && selectedTab == InfoFrameTab.Talents)
{
SelectInfoFrameTab(null, InfoFrameTab.Crew);
SelectInfoFrameTab(InfoFrameTab.Crew);
}
};
talentPointNotification = GameSession.CreateTalentIconNotification(talentsButton);
}
private bool SelectInfoFrameTab(GUIButton button, object userData)
public void SelectInfoFrameTab(InfoFrameTab selectedTab)
{
selectedTab = (InfoFrameTab)userData;
SelectedTab = selectedTab;
CreateInfoFrame(selectedTab);
tabButtons.ForEach(tb => tb.Selected = (InfoFrameTab)tb.UserData == selectedTab);
@@ -310,7 +310,7 @@ namespace Barotrauma
case InfoFrameTab.Traitor:
TraitorMissionPrefab traitorMission = GameMain.Client.TraitorMission;
Character traitor = GameMain.Client.Character;
if (traitor == null || traitorMission == null) return false;
if (traitor == null || traitorMission == null) { return; }
CreateTraitorInfo(infoFrameHolder, traitorMission, traitor);
break;
case InfoFrameTab.Submarine:
@@ -320,8 +320,6 @@ namespace Barotrauma
CreateTalentInfo(infoFrameHolder);
break;
}
return true;
}
private const float jobColumnWidthPercentage = 0.138f;
@@ -859,7 +857,7 @@ namespace Barotrauma
string msg = ChatMessage.GetTimeStamp() + message.TextWithSender;
storedMessages.Add(new Pair<string, PlayerConnectionChangeType>(msg, message.ChangeType));
if (GameSession.IsTabMenuOpen && selectedTab == InfoFrameTab.Crew)
if (GameSession.IsTabMenuOpen && SelectedTab == InfoFrameTab.Crew)
{
TabMenu instance = GameSession.TabMenuInstance;
instance.AddLineToLog(msg, message.ChangeType);

View File

@@ -433,6 +433,7 @@ namespace Barotrauma
if (AvailableMoney >= hullRepairCost)
{
Campaign.Money -= hullRepairCost;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
Campaign.PurchasedHullRepairs = true;
button.Enabled = false;
SelectTab(UpgradeTab.Repairs);
@@ -467,6 +468,7 @@ namespace Barotrauma
if (AvailableMoney >= itemRepairCost && !Campaign.PurchasedItemRepairs)
{
Campaign.Money -= itemRepairCost;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
Campaign.PurchasedItemRepairs = true;
button.Enabled = false;
SelectTab(UpgradeTab.Repairs);
@@ -512,6 +514,7 @@ namespace Barotrauma
if (AvailableMoney >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
{
Campaign.Money -= shuttleRetrieveCost;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
Campaign.PurchasedLostShuttles = true;
button.Enabled = false;
SelectTab(UpgradeTab.Repairs);

View File

@@ -5,7 +5,7 @@ using System.Linq;
namespace Barotrauma
{
public static partial class GameAnalyticsManager
static partial class GameAnalyticsManager
{
static partial void CreateConsentPrompt()
{

View File

@@ -30,6 +30,13 @@ namespace Barotrauma
public static PerformanceCounter PerformanceCounter;
private static Stopwatch performanceCounterTimer;
private static int updateCount = 0;
public static int CurrentUpdateRate
{
get; private set;
}
public static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
public static string[] ConsoleArguments;
@@ -122,6 +129,12 @@ namespace Barotrauma
private bool exiting;
public static bool IsFirstLaunch
{
get;
private set;
}
public static GameMain Instance
{
get;
@@ -349,6 +362,8 @@ namespace Barotrauma
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Item));
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Items.Components.ItemComponent));
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Hull));
performanceCounterTimer = Stopwatch.StartNew();
}
/// <summary>
@@ -579,6 +594,18 @@ namespace Barotrauma
{
Steamworks.SteamFriends.OnGameRichPresenceJoinRequested += OnInvitedToGame;
Steamworks.SteamFriends.OnGameLobbyJoinRequested += OnLobbyJoinRequested;
if (SteamManager.TryGetUnlockedAchievements(out List<Steamworks.Data.Achievement> achievements))
{
//check the achievements too, so we don't consider people who've played the game before this "gamelaunchcount" stat was added as being 1st-time-players
//(people who have played previous versions, but not unlocked any achievements, will be incorrectly considered 1st-time-players, but that should be a small enough group to not skew the statistics)
if (!achievements.Any() && SteamManager.GetStatInt("gamelaunchcount") <= 0)
{
IsFirstLaunch = true;
GameAnalyticsManager.AddDesignEvent("FirstLaunch");
}
}
SteamManager.IncrementStat("gamelaunchcount", 1);
}
#endif
@@ -696,11 +723,10 @@ namespace Barotrauma
protected override void Update(GameTime gameTime)
{
Timing.Accumulator += gameTime.ElapsedGameTime.TotalSeconds;
int updateIterations = (int)Math.Floor(Timing.Accumulator / Timing.Step);
if (Timing.Accumulator > Timing.Step * 6.0)
if (Timing.Accumulator > Timing.AccumulatorMax)
{
//if the game's running too slowly then we have no choice
//but to skip a bunch of steps
//prevent spiral of death:
//if the game's running too slowly then we have no choice but to skip a bunch of steps
//otherwise it snowballs and becomes unplayable
Timing.Accumulator = Timing.Step;
}
@@ -736,7 +762,6 @@ namespace Barotrauma
PlayerInput.Update(Timing.Step);
if (loadingScreenOpen)
{
//reset accumulator if loading
@@ -975,13 +1000,21 @@ namespace Barotrauma
Timing.Accumulator -= Timing.Step;
updateCount++;
sw.Stop();
PerformanceCounter.AddElapsedTicks("Update total", sw.ElapsedTicks);
PerformanceCounter.UpdateTimeGraph.Update(sw.ElapsedTicks * 1000.0f / (float)Stopwatch.Frequency);
PerformanceCounter.UpdateIterationsGraph.Update(updateIterations);
}
if (!Paused) Timing.Alpha = Timing.Accumulator / Timing.Step;
if (!Paused) { Timing.Alpha = Timing.Accumulator / Timing.Step; }
if (performanceCounterTimer.ElapsedMilliseconds > 1000)
{
CurrentUpdateRate = (int)Math.Round(updateCount / (double)(performanceCounterTimer.ElapsedMilliseconds / 1000.0));
performanceCounterTimer.Restart();
updateCount = 0;
}
}
public static void ResetFrameTime()
@@ -1086,8 +1119,15 @@ namespace Barotrauma
{
double roundDuration = Timing.TotalTime - GameSession.RoundStartTime;
GameAnalyticsManager.AddProgressionEvent(GameAnalyticsManager.ProgressionStatus.Fail,
GameSession.GameMode?.Name ?? "none",
GameSession.GameMode?.Preset.Identifier ?? "none",
roundDuration);
string eventId = "QuitRound:" + (GameSession.GameMode?.Preset.Identifier ?? "none") + ":";
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:CurrentIntensity", GameSession.EventManager.CurrentIntensity);
foreach (var activeEvent in GameSession.EventManager.ActiveEvents)
{
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:ActiveEvents:" + activeEvent.ToString());
}
GameSession.LogEndRoundStats(eventId);
if (Tutorial.Initialized)
{
((TutorialMode)GameSession.GameMode).Tutorial?.Stop();

View File

@@ -232,6 +232,8 @@ namespace Barotrauma
Location.StoreCurrentBalance -= itemValue;
campaign.Money += itemValue;
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier);
// Remove from the sell crate
// TODO: Simplify duplicate logic?
if (sellingMode == Store.StoreTab.Sell && ItemsInSellCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } inventoryItem)

View File

@@ -83,7 +83,7 @@ namespace Barotrauma
partial void InitProjectSpecific()
{
guiFrame = new GUIFrame(new RectTransform(Vector2.One, GUICanvas.Instance), null, Color.Transparent)
guiFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), null, Color.Transparent)
{
CanBeFocused = false
};
@@ -302,7 +302,7 @@ namespace Barotrauma
/// </summary>
/// <param name="character">The character to remove</param>
/// <param name="removeInfo">If the character info is also removed, the character will not be visible in the round summary.</param>
public void RemoveCharacter(Character character, bool removeInfo = false)
public void RemoveCharacter(Character character, bool removeInfo = false, bool resetCrewListIndex = true)
{
if (character == null)
{
@@ -311,14 +311,15 @@ namespace Barotrauma
}
characters.Remove(character);
if (removeInfo) { characterInfos.Remove(character.Info); }
if (resetCrewListIndex) { ResetCrewListIndex(character); }
}
/// <summary>
/// Add character to the list without actually adding it to the crew
/// </summary>
public void AddCharacterToCrewList(Character character)
public GUIComponent AddCharacterToCrewList(Character character)
{
if (character == null) { return; }
if (character == null) { return null; }
var background = new GUIFrame(
new RectTransform(crewListEntrySize, parent: crewList.Content.RectTransform, anchor: Anchor.TopRight),
@@ -510,6 +511,8 @@ namespace Barotrauma
return true;
}
};
return background;
}
private void SetCharacterComponentTooltip(GUIComponent characterComponent)
@@ -549,13 +552,13 @@ namespace Barotrauma
if (characterInfos.Contains(revivedCharacter.Info)) { AddCharacter(revivedCharacter); }
}
public void KillCharacter(Character killedCharacter)
public void KillCharacter(Character killedCharacter, bool resetCrewListIndex = true)
{
if (crewList.Content.GetChildByUserData(killedCharacter) is GUIComponent characterComponent)
{
CoroutineManager.StartCoroutine(KillCharacterAnim(characterComponent));
}
RemoveCharacter(killedCharacter);
RemoveCharacter(killedCharacter, resetCrewListIndex: resetCrewListIndex);
}
private IEnumerable<CoroutineStatus> KillCharacterAnim(GUIComponent component)
@@ -601,9 +604,53 @@ namespace Barotrauma
{
if (crewList != this.crewList) { return; }
if (!(draggedElementData is Character)) { return; }
if (crewList.HasDraggedElementIndexChanged) { return; }
if (!IsSinglePlayer) { return; }
CharacterClicked(crewList.DraggedElement, draggedElementData);
if (crewList.HasDraggedElementIndexChanged)
{
UpdateCrewListIndices();
}
else
{
CharacterClicked(crewList.DraggedElement, draggedElementData);
}
}
private void ResetCrewListIndex(Character c)
{
if (c?.Info == null) { return; }
c.Info.CrewListIndex = -1;
UpdateCrewListIndices();
}
private void UpdateCrewListIndices()
{
if (crewList == null) { return; }
for (int i = 0; i < crewList.Content.CountChildren; i++)
{
var characterComponent = crewList.Content.GetChild(i);
if (!(characterComponent?.UserData is Character c)) { continue; }
if (c.Info == null) { continue; }
c.Info.CrewListIndex = i;
}
}
private void SortCrewList()
{
if (crewList == null) { return; }
crewList.Content.RectTransform.SortChildren((x, y) =>
{
var infoX = (x.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
var infoY = (y.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
if (infoX.HasValue)
{
return infoY.HasValue ? infoX.Value.CompareTo(infoY.Value) : -1;
}
else
{
return infoY.HasValue ? 1 : 0;
}
});
UpdateCrewListIndices();
}
#endregion
@@ -1077,7 +1124,13 @@ namespace Barotrauma
private string CreateOrderTooltip(Order orderPrefab, string option, Entity targetEntity)
{
if (orderPrefab == null) { return ""; }
if (!string.IsNullOrEmpty(option))
if (orderPrefab.DisplayGiverInTooltip && orderPrefab.OrderGiver != null)
{
return TextManager.GetWithVariables("crewlistordericontooltip",
new string[2] { "[ordername]", "[orderoption]" },
new string[2] { orderPrefab.Name, orderPrefab.OrderGiver.DisplayName });
}
else if (!string.IsNullOrEmpty(option))
{
return TextManager.GetWithVariables("crewlistordericontooltip",
new string[2] { "[ordername]", "[orderoption]" },
@@ -1210,6 +1263,10 @@ namespace Barotrauma
DisableCommandUI();
Character.Controlled = character;
HintManager.OnChangeCharacter();
if (GameSession.TabMenuInstance != null && TabMenu.SelectedTab == TabMenu.InfoFrameTab.Talents)
{
GameSession.TabMenuInstance.SelectInfoFrameTab(TabMenu.SelectedTab);
}
}
private int TryAdjustIndex(int amount)
@@ -1566,10 +1623,28 @@ namespace Barotrauma
{
crewList.Select(character, force: true);
}
// Icon colors might change based on the target so we check if they need to be updated
if (GetCurrentOrderIconList(characterComponent) is GUIListBox currentOrderIconList)
{
foreach (var orderIcon in currentOrderIconList.Content.Children)
{
if (!(orderIcon.UserData is OrderInfo orderInfo)) { continue; }
if (!(orderInfo.Order is Order order)) { continue; }
if (order.ColoredWhenControllingGiver && order.OrderGiver != Character.Controlled)
{
orderIcon.Color = AIObjective.ObjectiveIconColor;
}
else
{
orderIcon.Color = order.Color;
}
}
}
// Only update the order highlights and objective icons here in singleplayer
// The server will let the clients know when they need to update in multiplayer
if (GameMain.IsSingleplayer && character.IsBot && character.AIController is HumanAIController controller &&
controller.ObjectiveManager is AIObjectiveManager objectiveManager)
{
// In multiplayer, these are set through character networking (the server lets the clients now when these are updated)
if (objectiveManager.CurrentObjective is AIObjective currentObjective)
{
if (objectiveManager.IsOrder(currentObjective))
@@ -1638,8 +1713,8 @@ namespace Barotrauma
bool foundMatch = false;
foreach (var orderIcon in currentOrderIconList.Content.Children)
{
var glowComponent = orderIcon.GetChildByUserData("glow");
if (glowComponent == null) { continue; }
if (!(orderIcon.GetChildByUserData("glow") is GUIComponent glowComponent)) { continue; }
glowComponent.Color = orderIcon.Color;
if (foundMatch)
{
glowComponent.Visible = false;
@@ -1684,11 +1759,11 @@ namespace Barotrauma
objectiveIconFrame.ClearChildren();
if (sprite != null)
{
var objectiveIcon = CreateNodeIcon(Vector2.One, objectiveIconFrame.RectTransform, sprite, Color.LightGray, tooltip: tooltip);
var objectiveIcon = CreateNodeIcon(Vector2.One, objectiveIconFrame.RectTransform, sprite, AIObjective.ObjectiveIconColor, tooltip: tooltip);
new GUIFrame(new RectTransform(new Vector2(1.5f), objectiveIcon.RectTransform, anchor: Anchor.Center), style: "OuterGlowCircular")
{
CanBeFocused = false,
Color = Color.LightGray
Color = AIObjective.ObjectiveIconColor
};
objectiveIconFrame.Visible = true;
}
@@ -1909,7 +1984,7 @@ namespace Barotrauma
ScaleCommandUI();
commandFrame = new GUIFrame(
new RectTransform(Vector2.One, GUICanvas.Instance, anchor: Anchor.Center),
new RectTransform(Vector2.One, GUI.Canvas, anchor: Anchor.Center),
style: null,
color: Color.Transparent);
background = new GUIImage(
@@ -3530,12 +3605,14 @@ namespace Barotrauma
public void Save(XElement parentElement)
{
XElement element = new XElement("crew");
foreach (CharacterInfo ci in characterInfos)
for (int i = 0; i < characterInfos.Count; i++)
{
var ci = characterInfos[i];
var infoElement = ci.Save(element);
if (ci.InventoryData != null) { infoElement.Add(ci.InventoryData); }
if (ci.HealthData != null) { infoElement.Add(ci.HealthData); }
if (ci.OrderData != null) { infoElement.Add(ci.OrderData); }
infoElement.Add(new XAttribute("crewlistindex", ci.CrewListIndex));
if (ci.LastControlled) { infoElement.Add(new XAttribute("lastcontrolled", true)); }
}
SaveActiveOrders(element);

View File

@@ -97,7 +97,7 @@ namespace Barotrauma
partial void InitProjSpecific()
{
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUICanvas.Instance),
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUI.Canvas),
isHorizontal: true, childAnchor: Anchor.CenterRight)
{
CanBeFocused = false
@@ -108,7 +108,7 @@ namespace Barotrauma
buttonCenter = buttonHeight / 2,
screenMiddle = GameMain.GraphicsWidth / 2;
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle - buttonWidth / 2, HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, buttonWidth, buttonHeight), GUICanvas.Instance),
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle - buttonWidth / 2, HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, buttonWidth, buttonHeight), GUI.Canvas),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
{
Pulse = true,
@@ -145,7 +145,7 @@ namespace Barotrauma
int readyButtonHeight = buttonHeight;
int readyButtonWidth = (int) (GUI.Scale * 50);
ReadyCheckButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle + (buttonWidth / 2) + GUI.IntScale(16), HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, readyButtonWidth, readyButtonHeight), GUICanvas.Instance),
ReadyCheckButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle + (buttonWidth / 2) + GUI.IntScale(16), HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, readyButtonWidth, readyButtonHeight), GUI.Canvas),
style: "RepairBuyButton")
{
ToolTip = TextManager.Get("ReadyCheck.Tooltip"),

View File

@@ -108,6 +108,9 @@ namespace Barotrauma
case "pets":
petsElement = subElement;
break;
case "stats":
LoadStats(subElement);
break;
}
}
@@ -167,7 +170,7 @@ namespace Barotrauma
int buttonHeight = (int)(GUI.Scale * 40);
int buttonWidth = GUI.IntScale(450);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUICanvas.Instance),
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUI.Canvas),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
{
Pulse = true,
@@ -412,6 +415,10 @@ namespace Barotrauma
break;
case TransitionType.ProgressToNextLocation:
Map.MoveToNextLocation();
TotalPassedLevels++;
break;
case TransitionType.ProgressToNextEmptyLocation:
TotalPassedLevels++;
break;
}
@@ -728,6 +735,7 @@ namespace Barotrauma
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
new XAttribute("cheatsenabled", CheatsEnabled));
modeElement.Add(Settings.Save());
modeElement.Add(SaveStats());
//save and remove all items that are in someone's inventory so they don't get included in the sub file as well
foreach (Character c in Character.CharacterList)

View File

@@ -205,6 +205,11 @@ namespace Barotrauma
return;
}
if (campaign?.CampaignUI?.MedicalClinic is { } ui)
{
ui.ClosePopup();
}
healAllRequests.Add(new RequestAction<HealRequest>(onReceived, GetTimeout()));
ClientSend(null, NetworkHeader.HEAL_PENDING, DeliveryMethod.Reliable);
}

View File

@@ -600,6 +600,10 @@ namespace Barotrauma
OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; }
};
tabButtons[(int)tab].Text = ToolBox.LimitString(buttonText, tabButtons[(int)tab].Font, (int)(0.75f * tabWidth * tabButtonHolder.Rect.Width));
if (tabButtons[(int)tab].Text != buttonText)
{
tabButtons[(int)tab].ToolTip = buttonText;
}
}
/// Graphics tab --------------------------------------------------------------

View File

@@ -796,7 +796,7 @@ namespace Barotrauma
if (quickUseAction != QuickUseAction.Drop)
{
slot.QuickUseButtonToolTip = quickUseAction == QuickUseAction.None ?
"" : TextManager.GetWithVariable("QuickUseAction." + quickUseAction.ToString(), "[equippeditem]", item?.Name);
"" : TextManager.GetWithVariable("QuickUseAction." + quickUseAction.ToString(), "[equippeditem]", character.HeldItems.FirstOrDefault()?.Name ?? item?.Name);
if (PlayerInput.PrimaryMouseButtonDown()) { slot.EquipButtonState = GUIComponent.ComponentState.Pressed; }
if (PlayerInput.PrimaryMouseButtonClicked())
{
@@ -970,7 +970,9 @@ namespace Barotrauma
{
return QuickUseAction.TakeFromCharacter;
}
else if (character.HeldItems.Any(i => i.OwnInventory != null && i.OwnInventory.CanBePut(item)) && allowInventorySwap)
else if (character.HeldItems.Any(i =>
i.OwnInventory != null &&
(i.OwnInventory.CanBePut(item) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
{
return QuickUseAction.PutToEquippedItem;
}
@@ -1136,7 +1138,8 @@ namespace Barotrauma
foreach (Item heldItem in character.HeldItems)
{
if (heldItem.OwnInventory != null &&
heldItem.OwnInventory.TryPutItem(item, Character.Controlled))
heldItem.OwnInventory.TryPutItem(item, Character.Controlled) ||
(heldItem.OwnInventory.Capacity == 1 && heldItem.OwnInventory.TryPutItem(item, 0, allowSwapping: true, allowCombine: false, user: Character.Controlled)))
{
success = true;
for (int j = 0; j < capacity; j++)

View File

@@ -256,6 +256,10 @@ namespace Barotrauma.Items.Components
{
targetHull.IncreaseSectionColorOrStrength(targetSections[i], color, sizeAdjustedSprayStrength * deltaTime, true, false);
}
if (GameMain.GameSession != null)
{
GameMain.GameSession.TimeSpentCleaning += deltaTime;
}
}
else
{
@@ -263,6 +267,10 @@ namespace Barotrauma.Items.Components
{
targetHull.CleanSection(targetSections[i], -sizeAdjustedSprayStrength * deltaTime, true);
}
if (GameMain.GameSession != null)
{
GameMain.GameSession.TimeSpentPainting += deltaTime;
}
}
Vector2 particleStartPos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);

View File

@@ -143,7 +143,7 @@ namespace Barotrauma.Items.Components
}
}
public GUIFrame GuiFrame { get; protected set; }
public GUIFrame GuiFrame { get; set; }
[Serialize(false, false)]
public bool AllowUIOverlap
@@ -554,7 +554,7 @@ namespace Barotrauma.Items.Components
color = GuiFrameSource.GetAttributeColor("color", Color.White);
}
string style = GuiFrameSource.Attribute("style") == null ? null : GuiFrameSource.GetAttributeString("style", "");
GuiFrame = new GUIFrame(RectTransform.Load(GuiFrameSource, GUI.Canvas.ItemComponentHolder, Anchor.Center), style, color);
GuiFrame = new GUIFrame(RectTransform.Load(GuiFrameSource, GUI.Canvas, Anchor.Center), style, color);
DefaultLayout = GUILayoutSettings.Load(GuiFrameSource);
if (GuiFrame != null)
{

View File

@@ -76,6 +76,9 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, false, description: "If true, the contained state indicator calculates how full the item is based on the total amount of items that can be stacked inside it, as opposed to how many of the inventory slots are occupied.")]
public bool ShowTotalStackCapacityInContainedStateIndicator { get; set; }
[Serialize(false, false, description: "Should the inventory of this item be kept open when the item is equipped by a character.")]
public bool KeepOpenWhenEquipped { get; set; }

View File

@@ -63,7 +63,7 @@ namespace Barotrauma.Items.Components
Stretch = true,
RelativeSpacing = 0.05f
};
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, inputLabelArea.RectTransform), TextManager.Get("uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, inputLabelArea.RectTransform), TextManager.Get("deconstructor.input", fallBackTag: "uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, inputLabelArea.RectTransform), style: "HorizontalLine");

View File

@@ -132,7 +132,7 @@ namespace Barotrauma.Items.Components
Stretch = true,
RelativeSpacing = 0.03f
};
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, separatorArea.RectTransform), TextManager.Get("uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, separatorArea.RectTransform), TextManager.Get("fabricator.input", fallBackTag: "uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, separatorArea.RectTransform), style: "HorizontalLine");

View File

@@ -507,7 +507,7 @@ namespace Barotrauma.Items.Components
{
Vector2 origin = weaponSprite.Origin;
float scale = parentWidth / Math.Max(weaponSprite.size.X, weaponSprite.size.Y);
Color color = !hasPower ? NoPowerColor : turret.ActiveUser is null ? GUI.Style.Red : GUI.Style.Green;
Color color = !hasPower ? NoPowerColor : turret.ActiveUser is null ? Color.DimGray : GUI.Style.Green;
weaponSprite.Draw(batch, center, color, origin, rotation, scale, it.SpriteEffects);
}
});
@@ -1335,7 +1335,7 @@ namespace Barotrauma.Items.Components
RectangleF entityRect = ScaleRectToUI(structure, parent, border);
Vector2 spriteScale = new Vector2(entityRect.Size.X / sprite.size.X, entityRect.Size.Y / sprite.size.Y);
sprite.Draw(spriteBatch, new Vector2(entityRect.Location.X + inflate, entityRect.Location.Y + inflate), structure.SpriteColor, Vector2.Zero, 0f, spriteScale, structure.SpriteEffects);
sprite.Draw(spriteBatch, new Vector2(entityRect.Location.X + inflate, entityRect.Location.Y + inflate), structure.SpriteColor, Vector2.Zero, 0f, spriteScale, sprite.effects ^ structure.SpriteEffects);
}
private static RectangleF ScaleRectToUI(MapEntity entity, RectangleF parentRect, RectangleF worldBorders)
@@ -1718,5 +1718,20 @@ namespace Barotrauma.Items.Components
return new MiniMapHullData(scaledPolygon, worldRect, parentRect.Size, snappedRectangles, hullRefs.ToImmutableArray());
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
if (searchAutoComplete != null)
{
searchAutoComplete.RectTransform.Parent = null;
searchAutoComplete = null;
}
if (hullInfoFrame != null)
{
hullInfoFrame.RectTransform.Parent = null;
hullInfoFrame = null;
}
}
}
}

View File

@@ -14,7 +14,7 @@ namespace Barotrauma.Items.Components
if (selectionUI == null)
{
selectionUI = new SubmarineSelection(true, null, GUICanvas.Instance.ItemComponentHolder);
selectionUI = new SubmarineSelection(true, null, GUI.Canvas);
}
GuiFrame = selectionUI.GuiFrame;
@@ -35,5 +35,15 @@ namespace Barotrauma.Items.Components
selectionUI?.Update();
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
if (selectionUI != null)
{
selectionUI.GuiFrame.RectTransform.Parent = null;
selectionUI = null;
}
}
}
}

View File

@@ -348,6 +348,27 @@ namespace Barotrauma.Items.Components
}
}
private Vector2 GetTransducerPos()
{
if (!UseTransducers || connectedTransducers.Count == 0)
{
//use the position of the sub if the item is static (no body) and inside a sub
return item.Submarine != null && item.body == null ? item.Submarine.WorldPosition : item.WorldPosition;
}
Vector2 transducerPosSum = Vector2.Zero;
foreach (ConnectedTransducer transducer in connectedTransducers)
{
if (transducer.Transducer.Item.Submarine != null && !CenterOnTransducers)
{
return transducer.Transducer.Item.Submarine.WorldPosition;
}
transducerPosSum += transducer.Transducer.Item.WorldPosition;
}
return transducerPosSum / connectedTransducers.Count;
}
public override void OnItemLoaded()
{
base.OnItemLoaded();

View File

@@ -10,6 +10,12 @@ namespace Barotrauma.Items.Components
private GUIProgressBar chargeIndicator;
private GUIScrollBar rechargeSpeedSlider;
[Serialize(0.0f, true)]
public float RechargeWarningIndicatorLow { get; set; }
[Serialize(0.0f, true)]
public float RechargeWarningIndicatorHigh { get; set; }
public Vector2 DrawSize
{
//use the extents of the item as the draw size
@@ -28,19 +34,38 @@ namespace Barotrauma.Items.Components
var upperArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), paddedFrame.RectTransform, Anchor.TopCenter), style: null);
var lowerArea = new GUIFrame(new RectTransform(new Vector2(1, 0.6f), paddedFrame.RectTransform, Anchor.BottomCenter), style: null);
string rechargeStr = TextManager.Get("PowerContainerRechargeRate");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), upperArea.RectTransform, Anchor.TopCenter),
"RechargeRate", textColor: GUI.Style.TextColor, font: GUI.SubHeadingFont, textAlignment: Alignment.Center)
var rechargeRateContainer = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), upperArea.RectTransform), style: null);
var rechargeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.0f), rechargeRateContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get("rechargerate"), textColor: GUI.Style.TextColor, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
string kW = TextManager.Get("kilowatt");
var rechargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), rechargeRateContainer.RectTransform, Anchor.CenterRight),
"", textColor: GUI.Style.TextColor, font: GUI.Font, textAlignment: Alignment.CenterRight)
{
TextGetter = () =>
{
return rechargeStr.Replace("[rate]", ((int)((rechargeSpeed / maxRechargeSpeed) * 100.0f)).ToString());
}
TextGetter = () => $"{(int)MathF.Round(currPowerConsumption)} {kW} ({(int)MathF.Round(RechargeRatio * 100)} %)"
};
if (rechargeText.TextSize.X > rechargeText.Rect.Width) { rechargeText.Font = GUI.SmallFont; }
rechargeSpeedSlider = new GUIScrollBar(new RectTransform(new Vector2(0.9f, 0.4f), upperArea.RectTransform, Anchor.BottomCenter),
barSize: 0.15f, style: "DeviceSlider")
var rechargeSliderContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.4f), upperArea.RectTransform, Anchor.BottomCenter));
if (RechargeWarningIndicatorLow > 0.0f || RechargeWarningIndicatorHigh > 0.0f)
{
var rechargeSliderFill = new GUICustomComponent(new RectTransform(new Vector2(0.95f, 0.9f), rechargeSliderContainer.RectTransform, Anchor.Center), (SpriteBatch sb, GUICustomComponent c) =>
{
if (RechargeWarningIndicatorLow > 0.0f)
{
float warningLow = c.Rect.Width * RechargeWarningIndicatorLow;
GUI.DrawRectangle(sb, new Vector2(c.Rect.X + warningLow, c.Rect.Y), new Vector2(c.Rect.Width - warningLow, c.Rect.Height), GUI.Style.Orange, isFilled: true);
}
if (RechargeWarningIndicatorHigh > 0.0f)
{
float warningHigh = c.Rect.Width * RechargeWarningIndicatorHigh;
GUI.DrawRectangle(sb, new Vector2(c.Rect.X + warningHigh, c.Rect.Y), new Vector2(c.Rect.Width - warningHigh, c.Rect.Height), GUI.Style.Red, isFilled: true);
}
});
}
rechargeSpeedSlider = new GUIScrollBar(new RectTransform(Vector2.One, rechargeSliderContainer.RectTransform, Anchor.Center),
barSize: 0.15f, style: "DeviceSliderSeeThrough")
{
Step = 0.1f,
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
@@ -61,17 +86,17 @@ namespace Barotrauma.Items.Components
// lower area --------------------------
var textArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), lowerArea.RectTransform), style: null);
var chargeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.0f), textArea.RectTransform, Anchor.CenterLeft),
var chargeTextContainer = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), lowerArea.RectTransform), style: null);
var chargeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.0f), chargeTextContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get("charge"), textColor: GUI.Style.TextColor, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
{
ToolTip = TextManager.Get("PowerTransferTipPower")
};
string kWmin = TextManager.Get("kilowattminute");
var chargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), textArea.RectTransform, Anchor.CenterRight),
var chargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), chargeTextContainer.RectTransform, Anchor.CenterRight),
"", textColor: GUI.Style.TextColor, font: GUI.Font, textAlignment: Alignment.CenterRight)
{
TextGetter = () => $"{(int)Math.Round(charge)}/{(int)capacity} {kWmin} ({(int)Math.Round(MathUtils.Percentage(charge, capacity))} %)"
TextGetter = () => $"{(int)MathF.Round(charge)}/{(int)capacity} {kWmin} ({(int)MathF.Round(MathUtils.Percentage(charge, capacity))} %)"
};
if (chargeText.TextSize.X > chargeText.Rect.Width) { chargeText.Font = GUI.SmallFont; }

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
@@ -19,5 +20,10 @@ namespace Barotrauma.Items.Components
ShapeExtensions.DrawLine(spriteBatch, pos + Vector2.UnitX * range, pos - Vector2.UnitX * range, Color.Cyan * 0.5f, 2);
ShapeExtensions.DrawCircle(spriteBatch, pos, range, 32, Color.Cyan * 0.5f, 3);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
Channel = msg.ReadRangedInteger(MinChannel, MaxChannel);
}
}
}

View File

@@ -1584,6 +1584,10 @@ namespace Barotrauma
{
containedState = item.Condition / item.MaxCondition;
}
else if (itemContainer.ShowTotalStackCapacityInContainedStateIndicator)
{
containedState = itemContainer.Inventory.AllItems.Count() / (float)(itemContainer.GetMaxStackSize(0) * itemContainer.Capacity);
}
else
{
var containedItem = itemContainer.Inventory.slots[Math.Max(itemContainer.ContainedStateIndicatorSlot, 0)].FirstOrDefault();

View File

@@ -315,7 +315,7 @@ namespace Barotrauma.MapCreatures.Behavior
}
else
{
RemoveClaim(itemId);
RemoveClaim(item);
}
}
else

View File

@@ -128,13 +128,13 @@ namespace Barotrauma
{
//no flow particles between linked hulls (= rooms consisting of multiple hulls)
if (hull1.linkedTo.Contains(hull2)) { return; }
foreach (Hull h in hull1.linkedTo)
foreach (var linkedEntity in hull1.linkedTo)
{
if (h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2)) { return; }
if (linkedEntity is Hull h && h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2)) { return; }
}
foreach (Hull h in hull2.linkedTo)
foreach (var linkedEntity in hull2.linkedTo)
{
if (h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2)) { return; }
if (linkedEntity is Hull h && h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2)) { return; }
}
}
@@ -244,7 +244,7 @@ namespace Barotrauma
float emitInterval = 1.0f / particlesPerSec;
while (particleTimer > emitInterval)
{
pos.X = Rand.Range(rect.X, rect.X + rect.Width);
pos.X = Rand.Range(rect.X, rect.X + rect.Width + 1);
Vector2 velocity = new Vector2(
lerpedFlowForce.X * Rand.Range(0.5f, 0.7f),
MathHelper.Clamp(lerpedFlowForce.Y, -500.0f, 1000.0f) * Rand.Range(0.5f, 0.7f));

View File

@@ -95,7 +95,7 @@ namespace Barotrauma
var prefab = ToolBox.SelectWeightedRandom(availablePrefabs, availablePrefabs.Select(p => p.GetCommonness(level.GenerationParams)).ToList(), Rand.RandSync.ClientOnly);
if (prefab == null) { break; }
int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax, Rand.RandSync.ClientOnly);
int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax + 1, Rand.RandSync.ClientOnly);
List<BackgroundCreature> swarmMembers = new List<BackgroundCreature>();
for (int n = 0; n < amount; n++)
{

View File

@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Lights;
@@ -36,9 +37,6 @@ namespace Barotrauma
private static List<MapEntity> highlightedList = new List<MapEntity>();
// Test feature. Not yet saved.
public static Dictionary<MapEntity, HashSet<MapEntity>> SelectionGroups { get; private set; } = new Dictionary<MapEntity, HashSet<MapEntity>>();
private static float highlightTimer;
private static GUIListBox highlightedListBox;
@@ -197,7 +195,7 @@ namespace Barotrauma
{
Paste(cam.ScreenToWorld(PlayerInput.MousePosition));
}
else if (PlayerInput.KeyHit(Keys.G))
/*else if (PlayerInput.KeyHit(Keys.G))
{
if (SelectedList.Any())
{
@@ -217,7 +215,7 @@ namespace Barotrauma
}
}
}
}
}*/
}
}
@@ -360,14 +358,15 @@ namespace Barotrauma
{
if (highLightedEntity != null)
{
if (SelectionGroups.TryGetValue(highLightedEntity, out HashSet<MapEntity> group))
if (SubEditorScreen.IsLayerLinked(highLightedEntity)/*SelectionGroups.TryGetValue(highLightedEntity, out HashSet<MapEntity> group)*/)
{
foreach (MapEntity entity in group.Where(e => !newSelection.Contains(e)))
ImmutableHashSet<MapEntity> entitiesInSameLayer = SubEditorScreen.GetEntitiesInSameLayer(highLightedEntity);
foreach (MapEntity entity in entitiesInSameLayer.Where(e => !newSelection.Contains(e)))
{
newSelection.Add(entity);
}
foreach (MapEntity entity in group)
foreach (MapEntity entity in entitiesInSameLayer)
{
entity.IsIncludedInSelection = true;
}
@@ -1197,14 +1196,24 @@ namespace Barotrauma
Rectangle selectionRect = Submarine.AbsRect(pos, size);
foreach (MapEntity e in mapEntityList)
foreach (MapEntity entity in mapEntityList)
{
if (!e.SelectableInEditor) continue;
if (!entity.SelectableInEditor) { continue; }
if (Submarine.RectsOverlap(selectionRect, e.rect))
if (Submarine.RectsOverlap(selectionRect, entity.rect))
{
foundEntities.Add(e);
e.IsIncludedInSelection = true;
foundEntities.Add(entity);
entity.IsIncludedInSelection = true;
if (SubEditorScreen.IsLayerLinked(entity))
{
ImmutableHashSet<MapEntity> entitiesInSameLayer = SubEditorScreen.GetEntitiesInSameLayer(entity);
foreach (MapEntity layerEntity in entitiesInSameLayer.Where(e => !foundEntities.Contains(e)))
{
foundEntities.Add(layerEntity);
layerEntity.IsIncludedInSelection = true;
}
}
}
}

View File

@@ -651,7 +651,11 @@ namespace Barotrauma
public void Dispose()
{
previewFrame = null;
if (previewFrame != null)
{
previewFrame.RectTransform.Parent = null;
previewFrame = null;
}
spriteRecorder?.Dispose();
isDisposed = true;
}

View File

@@ -46,6 +46,13 @@ namespace Barotrauma.Networking
senderName = senderCharacter.Name;
}
}
Color? textColor = null;
if (msg.ReadBoolean())
{
textColor = msg.ReadColorR8G8B8A8();
}
msg.ReadPadBits();
switch (type)
@@ -135,14 +142,18 @@ namespace Barotrauma.Networking
//only show the message box if the text differs from the text in the currently visible box
if ((GUIMessageBox.VisibleBox as GUIMessageBox)?.Text?.Text != txt)
{
new GUIMessageBox("", txt);
GUIMessageBox messageBox = new GUIMessageBox("", txt);
if (textColor != null) { messageBox.Text.TextColor = textColor.Value; }
}
break;
case ChatMessageType.ServerMessageBoxInGame:
new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting);
{
GUIMessageBox messageBox = new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting);
if (textColor != null) { messageBox.Text.TextColor = textColor.Value; }
}
break;
case ChatMessageType.Console:
DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
DebugConsole.NewMessage(txt, textColor == null ? MessageColor[(int)ChatMessageType.Console] : textColor.Value);
break;
case ChatMessageType.ServerLog:
if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType))
@@ -152,7 +163,7 @@ namespace Barotrauma.Networking
GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
break;
default:
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType);
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType, textColor: textColor);
break;
}
LastID = id;

View File

@@ -857,7 +857,7 @@ namespace Barotrauma.Networking
}
break;
case ServerPacketHeader.STARTGAMEFINALIZE:
DebugConsole.Log("Received STARTGAMEFINALIZE packet.");
DebugConsole.NewMessage("Received STARTGAMEFINALIZE packet. Round init status: " + roundInitStatus);
if (roundInitStatus == RoundInitStatus.WaitingForStartGameFinalize)
{
//waiting for a save file

View File

@@ -1,13 +1,10 @@
using Barotrauma.Tutorials;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
using System.Globalization;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using System.Linq;
namespace Barotrauma
{
@@ -64,6 +61,7 @@ namespace Barotrauma
maxMissionCount = MathHelper.Clamp(maxMissionCount,
CampaignSettings.MinMissionCountLimit,
CampaignSettings.MaxMissionCountLimit);
maxMissionCountText.Text = maxMissionCount.ToString(CultureInfo.InvariantCulture);
}
maxMissionCountButtons[1]

View File

@@ -151,6 +151,7 @@ namespace Barotrauma
if (Campaign.Money >= CampaignMode.HullRepairCost)
{
Campaign.Money -= CampaignMode.HullRepairCost;
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.HullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
Campaign.PurchasedHullRepairs = true;
}
}
@@ -196,6 +197,7 @@ namespace Barotrauma
if (Campaign.Money >= CampaignMode.ItemRepairCost)
{
Campaign.Money -= CampaignMode.ItemRepairCost;
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ItemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
Campaign.PurchasedItemRepairs = true;
}
}
@@ -248,6 +250,7 @@ namespace Barotrauma
if (Campaign.Money >= CampaignMode.ShuttleReplaceCost)
{
Campaign.Money -= CampaignMode.ShuttleReplaceCost;
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ShuttleReplaceCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
Campaign.PurchasedLostShuttles = true;
}
}

View File

@@ -54,7 +54,7 @@ namespace Barotrauma
private void CreateGUI()
{
GuiFrame = new GUIFrame(new RectTransform(new Vector2(0.2f, 0.4f), GUICanvas.Instance) { MinSize = new Point(300, 400) });
GuiFrame = new GUIFrame(new RectTransform(new Vector2(0.2f, 0.4f), GUI.Canvas) { MinSize = new Point(300, 400) });
GUILayoutGroup layoutGroup = new GUILayoutGroup(RectTransform(0.9f, 0.9f, GuiFrame, Anchor.Center)) { Stretch = true };
// === BUTTONS === //

View File

@@ -501,8 +501,6 @@ namespace Barotrauma
ResetButtonStates(null);
GameAnalyticsManager.SetCustomDimension01("");
if (GameMain.SteamWorkshopScreen != null)
{
CoroutineManager.StartCoroutine(GameMain.SteamWorkshopScreen.RefreshDownloadState());
@@ -1019,7 +1017,10 @@ namespace Barotrauma
if (backgroundSprite == null)
{
backgroundSprite = (LocationType.List.Where(l => l.UseInMainMenu).GetRandom())?.GetPortrait(0);
#if UNSTABLE
backgroundSprite = new Sprite("Content/UnstableBackground.png", sourceRectangle: null);
#endif
backgroundSprite ??= LocationType.List.Where(l => l.UseInMainMenu).GetRandom()?.GetPortrait(0);
}
if (backgroundSprite != null)
@@ -1159,7 +1160,7 @@ namespace Barotrauma
//GameMain.LobbyScreen.Select();
}
#region UI Methods
#region UI Methods
private void CreateCampaignSetupUI()
{
menuTabs[(int)Tab.NewGame].ClearChildren();
@@ -1432,7 +1433,7 @@ namespace Barotrauma
playstyleDescription.TextAlignment = playstyleDescription.WrappedText.Contains('\n') ?
Alignment.CenterLeft : Alignment.Center;
}
#endregion
#endregion
private void FetchRemoteContent()
{

View File

@@ -14,7 +14,7 @@ namespace Barotrauma
{
if (frame == null)
{
frame = new GUIFrame(new RectTransform(GUICanvas.Instance.RelativeSize, GUICanvas.Instance), style: null)
frame = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas), style: null)
{
CanBeFocused = false
};

View File

@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
@@ -21,6 +22,38 @@ namespace Barotrauma
{
class SubEditorScreen : EditorScreen
{
private enum LayerVisibility
{
Visible,
Invisible
}
private enum LayerLinkage
{
Unlinked,
Linked
}
private readonly struct LayerData
{
public readonly LayerVisibility Visible;
public readonly LayerLinkage Linkage;
public static readonly LayerData Default = new LayerData(LayerVisibility.Visible, LayerLinkage.Unlinked);
public LayerData(LayerVisibility visible, LayerLinkage linkage)
{
Visible = visible;
Linkage = linkage;
}
public void Deconstruct(out LayerVisibility isvisible, out LayerLinkage islinked)
{
isvisible = Visible;
islinked = Linkage;
}
}
private static readonly string[] crewExperienceLevels =
{
"CrewExperienceLow",
@@ -94,6 +127,7 @@ namespace Barotrauma
private GUIFrame hullVolumeFrame;
private GUIFrame saveAssemblyFrame;
private GUIFrame snapToGridFrame;
const int PreviouslyUsedCount = 10;
private GUIFrame previouslyUsedPanel;
@@ -238,7 +272,7 @@ namespace Barotrauma
public bool WiringMode => mode == Mode.Wiring;
public static readonly Dictionary<string, bool> Layers = new Dictionary<string, bool>();
private static readonly Dictionary<string, LayerData> Layers = new Dictionary<string, LayerData>();
public SubEditorScreen()
{
@@ -507,7 +541,7 @@ namespace Barotrauma
//-----------------------------------------------
layerPanel = new GUIFrame(new RectTransform(new Vector2(0.175f, 0.4f), GUI.Canvas))
layerPanel = new GUIFrame(new RectTransform(new Vector2(0.2f, 0.4f), GUI.Canvas))
{
Visible = false
};
@@ -520,6 +554,7 @@ namespace Barotrauma
AutoHideScrollBar = false,
OnSelected = (component, o) =>
{
if (GUI.MouseOn is GUITickBox) { return false; } // lol
if (!(o is string layer)) { return false; }
MapEntity.SelectedList.Clear();
@@ -847,6 +882,19 @@ namespace Barotrauma
};
saveAssemblyFrame.RectTransform.MinSize = new Point(saveAssemblyFrame.Rect.Width, (int)(saveAssemblyButton.Rect.Height / saveAssemblyButton.RectTransform.RelativeSize.Y));
snapToGridFrame = new GUIFrame(new RectTransform(new Vector2(0.08f, 0.5f), TopPanel.RectTransform, Anchor.BottomLeft, Pivot.TopLeft)
{ MinSize = new Point((int)(250 * GUI.Scale), (int)(80 * GUI.Scale)), AbsoluteOffset = new Point((int)(10 * GUI.Scale), -saveAssemblyFrame.Rect.Height - entityCountPanel.Rect.Height - (int)(10 * GUI.Scale)) }, "InnerFrame")
{
Visible = false
};
var saveStampButton = new GUIButton(new RectTransform(new Vector2(0.9f, 0.8f), snapToGridFrame.RectTransform, Anchor.Center), TextManager.Get("subeditor.snaptogrid", fallBackTag: "spriteeditor.snaptogrid"));
saveStampButton.TextBlock.AutoScaleHorizontal = true;
saveStampButton.OnClicked += (btn, userdata) =>
{
SnapToGrid();
return true;
};
snapToGridFrame.RectTransform.MinSize = new Point(snapToGridFrame.Rect.Width, (int)(saveStampButton.Rect.Height / saveStampButton.RectTransform.RelativeSize.Y));
//Entity menu
//------------------------------------------------
@@ -941,7 +989,7 @@ namespace Barotrauma
};
paddedTab.Recalculate();
UpdateLayerPanel();
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
@@ -1161,6 +1209,7 @@ namespace Barotrauma
{
CanBeFocused = false,
LoadAsynchronously = true,
SpriteEffects = icon.effects,
Color = legacy ? iconColor * 0.6f : iconColor
};
}
@@ -1344,8 +1393,8 @@ namespace Barotrauma
}
ImageManager.OnEditorSelected();
ReconstructLayers();
GameAnalyticsManager.SetCustomDimension01("editor");
if (!GameMain.Config.EditorDisclaimerShown)
{
GameMain.Instance.ShowEditorDisclaimer();
@@ -1455,7 +1504,6 @@ namespace Barotrauma
loadFrame = null;
MapEntity.DeselectAll();
MapEntity.SelectionGroups.Clear();
ClearUndoBuffer();
SetMode(Mode.Default);
@@ -2205,7 +2253,7 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), priceGroup.RectTransform),
TextManager.Get("subeditor.price"), textAlignment: Alignment.CenterLeft, wrap: true);
int basePrice = GameMain.DebugDraw ? 0 : Submarine.MainSub?.CalculateBasePrice() ?? 1000;
int basePrice = (GameMain.DebugDraw ? 0 : Submarine.MainSub?.CalculateBasePrice()) ?? 1000;
new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), priceGroup.RectTransform), GUINumberInput.NumberType.Int, hidePlusMinusButtons: true)
{
IntValue = Math.Max(Submarine.MainSub?.Info?.Price ?? basePrice, basePrice),
@@ -2682,6 +2730,38 @@ namespace Barotrauma
return false;
}
private void SnapToGrid()
{
// First move components
foreach (Item item in MapEntity.SelectedList.Where(entity => entity is Item).Cast<Item>())
{
var wire = item.GetComponent<Wire>();
if (wire == null)
{
// Items snap to centre of nearest grid square
Vector2 offset = item.Position;
offset = new Vector2((MathF.Floor(offset.X / Submarine.GridSize.X) + .5f) * Submarine.GridSize.X - offset.X, (MathF.Floor(offset.Y / Submarine.GridSize.Y) + .5f) * Submarine.GridSize.Y - offset.Y);
item.Move(offset);
}
}
// Then move wires, separated as moving components also moves the start and end node of wires
foreach (Item item in MapEntity.SelectedList.Where(entity => entity is Item).Cast<Item>())
{
var wire = item.GetComponent<Wire>();
if (wire != null)
{
for (int i = 0; i < wire.GetNodes().Count; i++)
{
// Items wire nodes to centre of nearest grid square
Vector2 offset = wire.GetNodes()[i] + Submarine.MainSub.HiddenSubPosition;
offset = new Vector2((MathF.Floor(offset.X / Submarine.GridSize.X) + .5f) * Submarine.GridSize.X - offset.X, (MathF.Floor(offset.Y / Submarine.GridSize.Y) + .5f) * Submarine.GridSize.Y - offset.Y);
wire.MoveNode(i, offset);
}
}
}
}
private void CreateLoadScreen()
{
CloseItem();
@@ -3232,7 +3312,6 @@ namespace Barotrauma
}),
new ContextMenuOption("editor.layer.openlayermenu", isEnabled: true, onSelected: () =>
{
if (visibilityButton is null) { return; }
previouslyUsedPanel.Visible = false;
undoBufferPanel.Visible = false;
showEntitiesPanel.Visible = false;
@@ -3289,7 +3368,7 @@ namespace Barotrauma
MoveToLayer(name, content);
}
Layers.Add(name, true);
Layers.Add(name, LayerData.Default);
UpdateLayerPanel();
}
@@ -3304,7 +3383,7 @@ namespace Barotrauma
if (!string.IsNullOrWhiteSpace(newName))
{
Layers.TryAdd(newName, true);
Layers.TryAdd(newName, LayerData.Default);
}
UpdateLayerPanel();
}
@@ -3316,7 +3395,7 @@ namespace Barotrauma
{
if (!string.IsNullOrWhiteSpace(entity.Layer))
{
Layers.TryAdd(entity.Layer, true);
Layers.TryAdd(entity.Layer, LayerData.Default);
}
}
UpdateLayerPanel();
@@ -4352,8 +4431,13 @@ namespace Barotrauma
layerList.Content.ClearChildren();
layerList.Deselect();
GUILayoutGroup buttonHeaders = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.075f), layerList.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.BottomLeft);
foreach (var (layer, isVisible) in Layers)
new GUIButton(new RectTransform(new Vector2(0.25f, 1f), buttonHeaders.RectTransform), TextManager.Get("editor.layer.headervisible"), style: "GUIButtonSmallFreeScale") { CanBeFocused = false, ForceUpperCase = true };
new GUIButton(new RectTransform(new Vector2(0.15f, 1f), buttonHeaders.RectTransform), TextManager.Get("editor.layer.headerlink"), style: "GUIButtonSmallFreeScale") { CanBeFocused = false, ForceUpperCase = true };
new GUIButton(new RectTransform(new Vector2(0.65f, 1f), buttonHeaders.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale") { CanBeFocused = false, ForceUpperCase = true };
foreach (var (layer, (visibility, linkage)) in Layers)
{
GUIFrame parent = new GUIFrame(new RectTransform(new Vector2(1f, 0.1f), layerList.Content.RectTransform), style: "ListBoxElement")
{
@@ -4362,33 +4446,54 @@ namespace Barotrauma
GUILayoutGroup layerGroup = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
GUITickBox layerVisibleButton = new GUITickBox(new RectTransform(Vector2.One, layerGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), string.Empty)
GUILayoutGroup layerVisibilityLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1f), layerGroup.RectTransform), childAnchor: Anchor.Center);
GUITickBox layerVisibleButton = new GUITickBox(new RectTransform(Vector2.One, layerVisibilityLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), string.Empty)
{
Selected = isVisible,
Selected = visibility == LayerVisibility.Visible,
OnSelected = box =>
{
if (!Layers.TryGetValue(layer, out bool _))
if (!Layers.TryGetValue(layer, out LayerData data))
{
UpdateLayerPanel();
return false;
}
Layers[layer] = box.Selected;
Layers[layer] = new LayerData(box.Selected ? LayerVisibility.Visible : LayerVisibility.Invisible, data.Linkage);
return true;
}
};
GUILayoutGroup layerChainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.15f, 1f), layerGroup.RectTransform), childAnchor: Anchor.Center);
GUITickBox layerChainButton = new GUITickBox(new RectTransform(Vector2.One, layerChainLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), string.Empty)
{
Selected = linkage == LayerLinkage.Linked,
OnSelected = box =>
{
if (!Layers.TryGetValue(layer, out LayerData data))
{
UpdateLayerPanel();
return false;
}
Layers[layer] = new LayerData(data.Visible, box.Selected ? LayerLinkage.Linked : LayerLinkage.Unlinked);
return true;
}
};
layerGroup.Recalculate();
new GUITextBlock(new RectTransform(new Vector2(1.0f - layerVisibleButton.RectTransform.RelativeSize.X, 1f), layerGroup.RectTransform), layer, textAlignment: Alignment.CenterLeft)
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), layerGroup.RectTransform), layer, textAlignment: Alignment.CenterLeft)
{
CanBeFocused = false
};
layerGroup.Recalculate();
layerChainLayout.Recalculate();
layerVisibilityLayout.Recalculate();
}
layerList.RecalculateChildren();
buttonHeaders.Recalculate();
}
public void UpdateUndoHistoryPanel()
@@ -4454,6 +4559,7 @@ namespace Barotrauma
saveFrame = null;
loadFrame = null;
saveAssemblyFrame = null;
snapToGridFrame = null;
CreateUI();
UpdateEntityList();
}
@@ -4501,6 +4607,7 @@ namespace Barotrauma
hullVolumeFrame.Visible = MapEntity.SelectedList.Any(s => s is Hull);
hullVolumeFrame.RectTransform.AbsoluteOffset = new Point(Math.Max(showEntitiesPanel.Rect.Right, previouslyUsedPanel.Rect.Right), 0);
saveAssemblyFrame.Visible = MapEntity.SelectedList.Count > 0;
snapToGridFrame.Visible = MapEntity.SelectedList.Count > 0;
var offset = cam.WorldView.Top - cam.ScreenToWorld(new Vector2(0, GameMain.GraphicsHeight - EntityMenu.Rect.Top)).Y;
@@ -4962,7 +5069,8 @@ namespace Barotrauma
MouseDragStart = Vector2.Zero;
}
if (!saveAssemblyFrame.Rect.Contains(PlayerInput.MousePosition) && dummyCharacter?.SelectedConstruction == null && !WiringMode && GUI.MouseOn == null)
if (!saveAssemblyFrame.Rect.Contains(PlayerInput.MousePosition) && !snapToGridFrame.Rect.Contains(PlayerInput.MousePosition) &&
dummyCharacter?.SelectedConstruction == null && !WiringMode && GUI.MouseOn == null)
{
if (layerList is { Visible: true } && GUI.KeyboardDispatcher.Subscriber == layerList)
{
@@ -5338,17 +5446,34 @@ namespace Barotrauma
public static bool IsLayerVisible(MapEntity entity)
{
if (!IsSubEditor()) { return true; }
if (!IsSubEditor() || string.IsNullOrWhiteSpace(entity.Layer)) { return true; }
if (string.IsNullOrWhiteSpace(entity.Layer)) { return true; }
if (!Layers.TryGetValue(entity.Layer, out bool isVisible))
if (!Layers.TryGetValue(entity.Layer, out LayerData data))
{
Layers.TryAdd(entity.Layer, true);
Layers.TryAdd(entity.Layer, LayerData.Default);
return true;
}
return isVisible;
return data.Visible == LayerVisibility.Visible;
}
public static bool IsLayerLinked(MapEntity entity)
{
if (!IsSubEditor() || string.IsNullOrWhiteSpace(entity.Layer)) { return false; }
if (!Layers.TryGetValue(entity.Layer, out LayerData data))
{
Layers.TryAdd(entity.Layer, LayerData.Default);
return true;
}
return data.Linkage == LayerLinkage.Linked;
}
public static ImmutableHashSet<MapEntity> GetEntitiesInSameLayer(MapEntity entity)
{
if (string.IsNullOrWhiteSpace(entity.Layer)) { return ImmutableHashSet<MapEntity>.Empty; }
return MapEntity.mapEntityList.Where(me => me.Layer == entity.Layer).ToImmutableHashSet();
}
}
}

View File

@@ -65,7 +65,6 @@ namespace Barotrauma
Character.Controlled = dummyCharacter;
GameMain.World.ProcessChanges();
TabMenu.selectedTab = TabMenu.InfoFrameTab.Talents;
tabMenu = new TabMenu();
}

View File

@@ -1157,7 +1157,7 @@ namespace Barotrauma
public static void PlaySplashSound(Vector2 worldPosition, float strength)
{
if (SplashSounds.Count == 0) { return; }
int splashIndex = MathHelper.Clamp((int)(strength + Rand.Range(-2, 2)), 0, SplashSounds.Count - 1);
int splashIndex = MathHelper.Clamp((int)(strength + Rand.Range(-2.0f, 2.0f)), 0, SplashSounds.Count - 1);
float range = 800.0f;
var channel = SplashSounds[splashIndex].Play(1.0f, range, worldPosition, muffle: ShouldMuffleSound(Character.Controlled, worldPosition, range, null));
}

View File

@@ -148,14 +148,8 @@ namespace Barotrauma
return t;
}
string fullPath = Path.GetFullPath(file);
foreach (Sprite s in LoadedSprites)
{
if (s.FullPath == fullPath && s.texture != null && !s.texture.IsDisposed)
{
reusedSprite = s;
return s.texture;
}
}
reusedSprite = FindMatchingSprite(fullPath, requireTexture: true);
if (reusedSprite != null) { return reusedSprite.texture; }
if (File.Exists(file))
{
@@ -176,6 +170,22 @@ namespace Barotrauma
return null;
}
private static Sprite FindMatchingSprite(string fullPath, bool requireTexture)
{
lock (list)
{
foreach (var wRef in list)
{
if (wRef.TryGetTarget(out Sprite sprite))
{
bool hasTexture = sprite.texture != null && !sprite.texture.IsDisposed;
if (sprite.FullPath == fullPath && (hasTexture || !requireTexture)) { return sprite; }
}
}
}
return null;
}
public void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None)
{
this.Draw(spriteBatch, pos, Color.White, rotate, scale, spriteEffect);
@@ -371,15 +381,9 @@ namespace Barotrauma
//check if another sprite is using the same texture
if (!string.IsNullOrEmpty(FilePath)) //file can be empty if the sprite is created directly from a Texture2D instance
{
lock (list)
{
foreach (Sprite s in LoadedSprites)
{
if (s.FullPath == FullPath) { return; }
}
}
if (FindMatchingSprite(FullPath, requireTexture: false) != null) { return; }
}
//if not, free the texture
if (texture != null)
{

View File

@@ -89,7 +89,17 @@ namespace Barotrauma
for (int j = 0; j < infoTextFiles.Count; j++)
{
List<string> xmlContent = ConvertInfoTextToXML(File.ReadAllLines(infoTextFiles[j], Encoding.UTF8), language);
List<string> xmlContent = null;
try
{
xmlContent = ConvertInfoTextToXML(File.ReadAllLines(infoTextFiles[j], Encoding.UTF8), language);
}
catch (Exception e)
{
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + infoTextFiles[j], e);
continue;
}
if (xmlContent == null)
{
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + infoTextFiles[j]);

View File

@@ -122,7 +122,7 @@ namespace Barotrauma
{
int width = 4096; int height = 4096;
Rectangle subDimensions = sub.Borders;
Rectangle subDimensions = sub.CalculateDimensions(false);
Vector2 viewPos = subDimensions.Center.ToVector2();
float scale = Math.Min(width / (float)subDimensions.Width, height / (float)subDimensions.Height);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -28,11 +28,11 @@ namespace Barotrauma
if (!CheatsEnabled && IsCheat)
{
NewMessage("Client \"" + client.Name + "\" attempted to use the command \"" + names[0] + "\". Cheats must be enabled using \"enablecheats\" before the command can be used.", Color.Red);
GameMain.Server.SendConsoleMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", client);
GameMain.Server.SendConsoleMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", client, Color.Red);
#if USE_STEAM
NewMessage("Enabling cheats will disable Steam achievements during this play session.", Color.Red);
GameMain.Server.SendConsoleMessage("Enabling cheats will disable Steam achievements during this play session.", client);
GameMain.Server.SendConsoleMessage("Enabling cheats will disable Steam achievements during this play session.", client, Color.Red);
#endif
return;
@@ -367,7 +367,7 @@ namespace Barotrauma
}
else
{
GameMain.Server.SendConsoleMessage("\"" + args[0] + "\" is not a valid bot spawn mode. (Valid modes are Fill and Normal)", client);
GameMain.Server.SendConsoleMessage("\"" + args[0] + "\" is not a valid bot spawn mode. (Valid modes are Fill and Normal)", client, Color.Red);
}
});
@@ -1046,12 +1046,12 @@ namespace Barotrauma
}));
AssignOnClientRequestExecute("clientlist", (Client client, Vector2 cursorWorldPos, string[] args) =>
{
GameMain.Server.SendConsoleMessage("***************", client);
GameMain.Server.SendConsoleMessage("***************", client, Color.Cyan);
foreach (Client c in GameMain.Server.ConnectedClients)
{
GameMain.Server.SendConsoleMessage("- " + c.ID.ToString() + ": " + c.Name + ", " + c.Connection.EndPointString + $", ping {c.Ping} ms", client);
GameMain.Server.SendConsoleMessage("- " + c.ID.ToString() + ": " + c.Name + ", " + c.Connection.EndPointString + $", ping {c.Ping} ms", client, Color.Cyan);
}
GameMain.Server.SendConsoleMessage("***************", client);
GameMain.Server.SendConsoleMessage("***************", client, Color.Cyan);
});
commands.Add(new Command("enablecheats", "enablecheats: Enables cheat commands and disables Steam achievements during this play session.", (string[] args) =>
@@ -1164,13 +1164,13 @@ namespace Barotrauma
if (GameMain.Server == null || args.Length == 0) return;
if (!int.TryParse(args[0], out int maxPlayers))
{
GameMain.Server.SendConsoleMessage(args[0] + " is not a valid player count.", client);
GameMain.Server.SendConsoleMessage(args[0] + " is not a valid player count.", client, Color.Red);
}
else
{
if (maxPlayers > NetConfig.MaxPlayers)
{
GameMain.Server.SendConsoleMessage($"Setting the maximum amount of players to {maxPlayers} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", client);
GameMain.Server.SendConsoleMessage($"Setting the maximum amount of players to {maxPlayers} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", client, Color.Red);
maxPlayers = NetConfig.MaxPlayers;
}
@@ -1455,7 +1455,7 @@ namespace Barotrauma
}
else
{
GameMain.Server.SendConsoleMessage("\"" + args[1] + "\" is not a valid ban duration.", client);
GameMain.Server.SendConsoleMessage("\"" + args[1] + "\" is not a valid ban duration.", client, Color.Red);
return;
}
}
@@ -1575,7 +1575,7 @@ namespace Barotrauma
}
else
{
GameMain.Server.SendConsoleMessage("\"" + args[0] + "\" is not a valid bot spawn mode. (Valid modes are Fill and Normal)", client);
GameMain.Server.SendConsoleMessage("\"" + args[0] + "\" is not a valid bot spawn mode. (Valid modes are Fill and Normal)", client, Color.Red);
}
}
);
@@ -1599,7 +1599,7 @@ namespace Barotrauma
if (Submarine.MainSub == null || Level.Loaded == null) return;
if (Level.Loaded.Type == LevelData.LevelType.Outpost)
{
GameMain.Server.SendConsoleMessage("The teleportsub command is unavailable in outpost levels!", client);
GameMain.Server.SendConsoleMessage("The teleportsub command is unavailable in outpost levels!", client, Color.Red);
return;
}
@@ -1623,7 +1623,7 @@ namespace Barotrauma
{
if (!(GameMain.GameSession?.Campaign is MultiPlayerCampaign mpCampaign))
{
GameMain.Server.SendConsoleMessage("No campaign active.", client);
GameMain.Server.SendConsoleMessage("No campaign active.", client, Color.Red);
return;
}
mpCampaign.LastUpdateID++;
@@ -1671,13 +1671,13 @@ namespace Barotrauma
a.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase));
if (afflictionPrefab == null)
{
GameMain.Server.SendConsoleMessage("Affliction \"" + args[0] + "\" not found.", client);
GameMain.Server.SendConsoleMessage("Affliction \"" + args[0] + "\" not found.", client, Color.Red);
return;
}
if (!float.TryParse(args[1], out float afflictionStrength))
{
GameMain.Server.SendConsoleMessage("\"" + args[1] + "\" is not a valid affliction strength.", client);
GameMain.Server.SendConsoleMessage("\"" + args[1] + "\" is not a valid affliction strength.", client, Color.Red);
return;
}
@@ -1760,7 +1760,7 @@ namespace Barotrauma
c.DisplayName.Equals(args[0], StringComparison.OrdinalIgnoreCase));
if (talentPrefab == null)
{
GameMain.Server.SendConsoleMessage("Couldn't find the talent \"" + args[0] + "\".", client);
GameMain.Server.SendConsoleMessage("Couldn't find the talent \"" + args[0] + "\".", client, Color.Red);
return;
}
targetCharacter.GiveTalent(talentPrefab);
@@ -1786,12 +1786,12 @@ namespace Barotrauma
var job = JobPrefab.Prefabs.Find(jp => jp.Name != null && jp.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase));
if (job == null)
{
GameMain.Server.SendConsoleMessage($"Failed to find the job \"{args[0]}\".", client);
GameMain.Server.SendConsoleMessage($"Failed to find the job \"{args[0]}\".", client, Color.Red);
return;
}
if (!TalentTree.JobTalentTrees.TryGetValue(job.Identifier, out TalentTree talentTree))
{
GameMain.Server.SendConsoleMessage($"No talents configured for the job \"{args[0]}\".", client);
GameMain.Server.SendConsoleMessage($"No talents configured for the job \"{args[0]}\".", client, Color.Red);
return;
}
talentTrees.Add(talentTree);
@@ -1858,6 +1858,10 @@ namespace Barotrauma
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
Character killedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (killedCharacter == null)
{
GameMain.Server.SendConsoleMessage("Could not find the specified character.", client, Color.Red);
}
killedCharacter?.SetAllDamage(200.0f, 0.0f, 0.0f);
}
);
@@ -1873,6 +1877,10 @@ namespace Barotrauma
GameMain.Server.SetClientCharacter(client, character);
client.SpectateOnly = false;
}
else
{
GameMain.Server.SendConsoleMessage("Could not find the specified character.", client, Color.Red);
}
}
);
@@ -1899,7 +1907,7 @@ namespace Barotrauma
}
else
{
GameMain.Server.SendConsoleMessage(args[0] + " is not a valid difficulty setting (enter a value between 0-100)", client);
GameMain.Server.SendConsoleMessage(args[0] + " is not a valid difficulty setting (enter a value between 0-100)", client, Color.Red);
NewMessage(args[0] + " is not a valid difficulty setting (enter a value between 0-100)", Color.Red);
}
}
@@ -1923,7 +1931,7 @@ namespace Barotrauma
ClientPermissions permission = ClientPermissions.None;
if (!Enum.TryParse(perm, true, out permission))
{
GameMain.Server.SendConsoleMessage(perm + " is not a valid permission!", senderClient);
GameMain.Server.SendConsoleMessage(perm + " is not a valid permission!", senderClient, Color.Red);
return;
}
@@ -1955,7 +1963,7 @@ namespace Barotrauma
}
if (client.Connection == GameMain.Server.OwnerConnection)
{
GameMain.Server.SendConsoleMessage("Cannot revoke permissions from the server owner!", senderClient);
GameMain.Server.SendConsoleMessage("Cannot revoke permissions from the server owner!", senderClient, Color.Red);
return;
}
@@ -1964,7 +1972,7 @@ namespace Barotrauma
ClientPermissions permission = ClientPermissions.None;
if (!Enum.TryParse(perm, true, out permission))
{
GameMain.Server.SendConsoleMessage(perm + " is not a valid permission!", senderClient);
GameMain.Server.SendConsoleMessage(perm + " is not a valid permission!", senderClient, Color.Red);
return;
}
client.RemovePermission(permission);
@@ -1988,7 +1996,7 @@ namespace Barotrauma
}
if (client.Connection == GameMain.Server.OwnerConnection)
{
GameMain.Server.SendConsoleMessage("Cannot modify the rank of the server owner!", senderClient);
GameMain.Server.SendConsoleMessage("Cannot modify the rank of the server owner!", senderClient, Color.Red);
return;
}
@@ -1996,7 +2004,7 @@ namespace Barotrauma
PermissionPreset preset = PermissionPreset.List.Find(p => p.Name.Equals(rank, StringComparison.OrdinalIgnoreCase));
if (preset == null)
{
GameMain.Server.SendConsoleMessage("Rank \"" + rank + "\" not found.", senderClient);
GameMain.Server.SendConsoleMessage("Rank \"" + rank + "\" not found.", senderClient, Color.Red);
return;
}
@@ -2016,12 +2024,12 @@ namespace Barotrauma
var client = FindClient(args[0]);
if (client == null)
{
GameMain.Server.SendConsoleMessage("Client \"" + args[0] + "\" not found.", senderClient);
GameMain.Server.SendConsoleMessage("Client \"" + args[0] + "\" not found.", senderClient, Color.Red);
return;
}
if (client.Connection == GameMain.Server.OwnerConnection)
{
GameMain.Server.SendConsoleMessage("Cannot modify the command permissions of the server owner!", senderClient);
GameMain.Server.SendConsoleMessage("Cannot modify the command permissions of the server owner!", senderClient, Color.Red);
return;
}
@@ -2040,7 +2048,7 @@ namespace Barotrauma
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i]));
if (matchingCommand == null)
{
GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient);
GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient, Color.Red);
}
else
{
@@ -2078,7 +2086,7 @@ namespace Barotrauma
}
if (client.Connection == GameMain.Server.OwnerConnection)
{
GameMain.Server.SendConsoleMessage("Cannot revoke command permissions from the server owner!", senderClient);
GameMain.Server.SendConsoleMessage("Cannot revoke command permissions from the server owner!", senderClient, Color.Red);
return;
}
List<Command> revokedCommands = new List<Command>();
@@ -2096,7 +2104,7 @@ namespace Barotrauma
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i]));
if (matchingCommand == null)
{
GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient);
GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient, Color.Red);
}
else
{
@@ -2176,7 +2184,7 @@ namespace Barotrauma
{
if (args.Length < 2)
{
GameMain.Server.SendConsoleMessage("Invalid parameters. The command should be formatted as \"setclientcharacter [client] [character]\". If the names consist of multiple words, you should surround them with quotation marks.", senderClient);
GameMain.Server.SendConsoleMessage("Invalid parameters. The command should be formatted as \"setclientcharacter [client] [character]\". If the names consist of multiple words, you should surround them with quotation marks.", senderClient, Color.Red);
return;
}
@@ -2189,7 +2197,7 @@ namespace Barotrauma
var client = GameMain.Server.ConnectedClients.Find(c => c.Name == args[0]);
if (client == null)
{
GameMain.Server.SendConsoleMessage("Client \"" + args[0] + "\" not found.", senderClient);
GameMain.Server.SendConsoleMessage("Client \"" + args[0] + "\" not found.", senderClient, Color.Red);
return;
}
@@ -2206,17 +2214,18 @@ namespace Barotrauma
if (args.Length == 0) { return; }
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign))
{
GameMain.Server.SendConsoleMessage("No campaign active!", senderClient);
GameMain.Server.SendConsoleMessage("No campaign active!", senderClient, Color.Red);
return;
}
if (int.TryParse(args[0], out int money))
{
campaign.Money += money;
GameAnalyticsManager.AddMoneyGainedEvent(money, GameAnalyticsManager.MoneySource.Cheat, "console");
campaign.LastUpdateID++;
}
else
{
GameMain.Server.SendConsoleMessage($"\"{args[0]}\" is not a valid numeric value.", senderClient);
GameMain.Server.SendConsoleMessage($"\"{args[0]}\" is not a valid numeric value.", senderClient, Color.Red);
}
}
);
@@ -2226,7 +2235,7 @@ namespace Barotrauma
{
if (!(GameMain.GameSession?.GameMode is CampaignMode campaign))
{
GameMain.Server.SendConsoleMessage("No campaign active!", senderClient);
GameMain.Server.SendConsoleMessage("No campaign active!", senderClient, Color.Red);
return;
}
@@ -2234,7 +2243,7 @@ namespace Barotrauma
if (args.Length < 1 || !int.TryParse(args[0], out destinationIndex)) return;
if (destinationIndex < 0 || destinationIndex >= campaign.Map.CurrentLocation.Connections.Count)
{
GameMain.Server.SendConsoleMessage("Index out of bounds!", senderClient);
GameMain.Server.SendConsoleMessage("Index out of bounds!", senderClient, Color.Red);
return;
}
Location location = campaign.Map.CurrentLocation.Connections[destinationIndex].OtherLocation(campaign.Map.CurrentLocation);
@@ -2251,14 +2260,41 @@ namespace Barotrauma
NewMessage(tag, Color.Yellow);
}
}));
commands.Add(new Command("sendchatmessage", "Sends a chat message with specified type and color.", (string[] args) =>
{
if (args.Length < 2) { return; }
ChatMessageType chatMessageType = ChatMessageType.Default;
Color? chatMessageColor = null;
if (args.Length >= 3 && int.TryParse(args[2], out int result))
{
chatMessageType = (ChatMessageType)result;
}
if (args.Length >= 7 &&
int.TryParse(args[3], out int r) &&
int.TryParse(args[4], out int g) &&
int.TryParse(args[5], out int b) &&
int.TryParse(args[6], out int a))
{
chatMessageColor = new Color(r, g, b, a);
}
foreach (var client in GameMain.Server.ConnectedClients)
{
GameMain.Server.SendDirectChatMessage(ChatMessage.Create(args[0], args[1], chatMessageType, null, null, textColor: chatMessageColor), client);
}
}));
AssignOnClientRequestExecute(
"setskill",
(senderClient, cursorWorldPos, args) =>
{
if (args.Length < 2)
{
GameMain.Server.SendConsoleMessage($"Missing arguments. Expected at least 2 but got {args.Length} (skill, level, name)", senderClient);
GameMain.Server.SendConsoleMessage($"Missing arguments. Expected at least 2 but got {args.Length} (skill, level, name)", senderClient, Color.Red);
return;
}
@@ -2268,7 +2304,7 @@ namespace Barotrauma
if (character?.Info?.Job == null)
{
GameMain.Server.SendConsoleMessage("Character is not valid.", senderClient);
GameMain.Server.SendConsoleMessage("Character is not valid.", senderClient, Color.Red);
return;
}
@@ -2295,7 +2331,7 @@ namespace Barotrauma
}
else
{
GameMain.Server.SendConsoleMessage($"{levelString} is not a valid level. Expected number or \"max\".", senderClient);
GameMain.Server.SendConsoleMessage($"{levelString} is not a valid level. Expected number or \"max\".", senderClient, Color.Red);
}
}
);
@@ -2389,7 +2425,7 @@ namespace Barotrauma
if (string.IsNullOrWhiteSpace(command)) return;
if (!client.HasPermission(ClientPermissions.ConsoleCommands) && client.Connection != GameMain.Server.OwnerConnection)
{
GameMain.Server.SendConsoleMessage("You are not permitted to use console commands!", client);
GameMain.Server.SendConsoleMessage("You are not permitted to use console commands!", client, Color.Red);
GameServer.Log(GameServer.ClientLogName(client) + " attempted to execute the console command \"" + command + "\" without a permission to use console commands.", ServerLog.MessageType.ConsoleUsage);
return;
}
@@ -2398,19 +2434,19 @@ namespace Barotrauma
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommand[0].ToLowerInvariant()));
if (matchingCommand != null && !client.PermittedConsoleCommands.Contains(matchingCommand) && client.Connection != GameMain.Server.OwnerConnection)
{
GameMain.Server.SendConsoleMessage("You are not permitted to use the command\"" + matchingCommand.names[0] + "\"!", client);
GameMain.Server.SendConsoleMessage("You are not permitted to use the command\"" + matchingCommand.names[0] + "\"!", client, Color.Red);
GameServer.Log(GameServer.ClientLogName(client) + " attempted to execute the console command \"" + command + "\" without a permission to use the command.", ServerLog.MessageType.ConsoleUsage);
return;
}
else if (matchingCommand == null)
{
GameMain.Server.SendConsoleMessage("Command \"" + splitCommand[0] + "\" not found.", client);
GameMain.Server.SendConsoleMessage("Command \"" + splitCommand[0] + "\" not found.", client, Color.Red);
return;
}
if (!MathUtils.IsValid(cursorWorldPos))
{
GameMain.Server.SendConsoleMessage("Could not execute command \"" + command + "\" - invalid cursor position.", client);
GameMain.Server.SendConsoleMessage("Could not execute command \"" + command + "\" - invalid cursor position.", client, Color.Red);
NewMessage(GameServer.ClientLogName(client) + " attempted to execute the console command \"" + command + "\" with invalid cursor position.", Color.White);
return;
}

View File

@@ -36,7 +36,7 @@ namespace Barotrauma
public static GameServer Server;
public static NetworkMember NetworkMember
{
get { return Server as NetworkMember; }
get { return Server; }
}
public static GameSession GameSession;
@@ -64,6 +64,9 @@ namespace Barotrauma
private static Stopwatch stopwatch;
private static Queue<int> prevUpdateRates = new Queue<int>();
private static int updateCount = 0;
private static ContentPackage vanillaContent;
public static ContentPackage VanillaContent
{
@@ -364,6 +367,8 @@ namespace Barotrauma
DebugConsole.NewMessage("WARNING: Stopwatch frequency under 1500 ticks per second. Expect significant syncing accuracy issues.", Color.Yellow);
}
Stopwatch performanceCounterTimer = Stopwatch.StartNew();
stopwatch = Stopwatch.StartNew();
long prevTicks = stopwatch.ElapsedTicks;
while (ShouldRun)
@@ -371,9 +376,11 @@ namespace Barotrauma
long currTicks = stopwatch.ElapsedTicks;
double elapsedTime = Math.Max(currTicks - prevTicks, 0) / frequency;
Timing.Accumulator += elapsedTime;
if (Timing.Accumulator > 1.0)
if (Timing.Accumulator > Timing.AccumulatorMax)
{
//prevent spiral of death
//prevent spiral of death:
//if the game's running too slowly then we have no choice but to skip a bunch of steps
//otherwise it snowballs and becomes unplayable
Timing.Accumulator = Timing.Step;
}
prevTicks = currTicks;
@@ -392,6 +399,7 @@ namespace Barotrauma
CoroutineManager.Update((float)Timing.Step, (float)Timing.Step);
Timing.Accumulator -= Timing.Step;
updateCount++;
}
#if !DEBUG
@@ -407,10 +415,34 @@ namespace Barotrauma
DebugConsole.UpdateCommandLine((int)(Timing.Accumulator * 800));
#endif
int frameTime = (int)(((double)(stopwatch.ElapsedTicks - prevTicks) / frequency) * 1000.0);
int frameTime = (int)((stopwatch.ElapsedTicks - prevTicks) / frequency * 1000.0);
frameTime = Math.Max(0, frameTime);
Thread.Sleep(Math.Max(((int)(Timing.Step * 1000.0) - frameTime) / 2, 0));
if (performanceCounterTimer.ElapsedMilliseconds > 1000)
{
int updateRate = (int)Math.Round(updateCount / (double)(performanceCounterTimer.ElapsedMilliseconds / 1000.0));
prevUpdateRates.Enqueue(updateRate);
if (prevUpdateRates.Count >= 10)
{
int avgUpdateRate = (int)prevUpdateRates.Average();
if (avgUpdateRate < Timing.FixedUpdateRate * 0.98 && GameSession != null && Timing.TotalTime > GameSession.RoundStartTime + 1.0)
{
DebugConsole.AddWarning($"Running slowly ({avgUpdateRate} updates/s)!");
foreach (Client c in Server.ConnectedClients)
{
if (c.Connection == Server.OwnerConnection || c.Permissions != ClientPermissions.None)
{
Server.SendConsoleMessage($"Server running slowly ({avgUpdateRate} updates/s)!", c, Color.Orange);
}
}
}
prevUpdateRates.Clear();
}
performanceCounterTimer.Restart();
updateCount = 0;
}
}
stopwatch.Stop();
@@ -429,8 +461,9 @@ namespace Barotrauma
public static void ResetFrameTime()
{
Timing.Accumulator = 0.0f;
stopwatch?.Reset();
stopwatch?.Start();
stopwatch?.Restart();
prevUpdateRates.Clear();
updateCount = 0;
}
public CoroutineHandle ShowLoading(IEnumerable<CoroutineStatus> loader, bool waitKeyHit = true)

View File

@@ -55,6 +55,7 @@ namespace Barotrauma
SoldItems.Add(item);
Location.StoreCurrentBalance -= itemValue;
campaign.Money += itemValue;
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier);
}
OnSoldItemsChanged?.Invoke();
}

View File

@@ -285,11 +285,15 @@ namespace Barotrauma
break;
case TransitionType.ProgressToNextLocation:
Map.MoveToNextLocation();
TotalPassedLevels++;
break;
case TransitionType.End:
EndCampaign();
IsFirstRound = true;
break;
case TransitionType.ProgressToNextEmptyLocation:
TotalPassedLevels++;
break;
}
Map.ProgressWorld(transitionType, (float)(Timing.TotalTime - GameMain.GameSession.RoundStartTime));
@@ -697,6 +701,7 @@ namespace Barotrauma
{
this.PurchasedHullRepairs = true;
Money -= hullRepairCost;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
}
else if (!purchasedHullRepairs)
{
@@ -710,6 +715,7 @@ namespace Barotrauma
{
this.PurchasedItemRepairs = true;
Money -= itemRepairCost;
GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
}
else if (!purchasedItemRepairs)
{
@@ -728,6 +734,7 @@ namespace Barotrauma
{
this.PurchasedLostShuttles = true;
Money -= shuttleRetrieveCost;
GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
}
else if (!purchasedItemRepairs)
{
@@ -998,7 +1005,9 @@ namespace Barotrauma
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
new XAttribute("cheatsenabled", CheatsEnabled));
modeElement.Add(Settings.Save());
modeElement.Add(SaveStats());
CampaignMetadata?.Save(modeElement);
Map.Save(modeElement);
CargoManager?.SavePurchasedItems(modeElement);

View File

@@ -1,13 +1,14 @@
using System;
using System.ComponentModel;
using System.Linq;
using Barotrauma.Networking;
using System;
using System.Xml.Linq;
using Barotrauma.Networking;
namespace Barotrauma.Items.Components
{
internal partial class Growable
{
private const int serverHealthUpdateDelay = 10;
private int serverHealthUpdateTimer;
partial void LoadVines(XElement element)
{
foreach (XElement subElement in element.Elements())

View File

@@ -0,0 +1,12 @@
using Barotrauma.Networking;
namespace Barotrauma.Items.Components
{
partial class WifiComponent
{
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.WriteRangedInteger(Channel, MinChannel, MaxChannel);
}
}
}

View File

@@ -212,6 +212,11 @@ namespace Barotrauma.Networking
{
msg.Write(Sender.ID);
}
msg.Write(customTextColor != null);
if (customTextColor != null)
{
msg.WriteColorR8G8B8A8(customTextColor.Value);
}
msg.WritePadBits();
if (Type == ChatMessageType.ServerMessageBoxInGame)
{

View File

@@ -726,7 +726,15 @@ namespace Barotrauma.Networking
}
break;
case ClientPacketHeader.REQUEST_STARTGAMEFINALIZE:
if (gameStarted && connectedClient != null)
if (connectedClient == null)
{
DebugConsole.AddWarning("Received a REQUEST_STARTGAMEFINALIZE message. Client not connected, ignoring the message.");
}
else if (!gameStarted)
{
DebugConsole.AddWarning("Received a REQUEST_STARTGAMEFINALIZE message. Game not started, ignoring the message.");
}
else
{
SendRoundStartFinalize(connectedClient);
}
@@ -746,7 +754,7 @@ namespace Barotrauma.Networking
string seed = inc.ReadString();
string subName = inc.ReadString();
string subHash = inc.ReadString();
CampaignSettings settings = new CampaignSettings(inc);
CampaignSettings settings = new CampaignSettings(inc);
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash);
@@ -769,6 +777,7 @@ namespace Barotrauma.Networking
{
ServerSettings.RadiationEnabled = settings.RadiationEnabled;
ServerSettings.MaxMissionCount = settings.MaxMissionCount;
ServerSettings.SaveSettings();
MultiPlayerCampaign.StartNewCampaign(localSavePath, matchingSub.FilePath, seed, settings);
}
}
@@ -1904,7 +1913,7 @@ namespace Barotrauma.Networking
int chatMessageBytes = outmsg.LengthBytes;
WriteChatMessages(outmsg, c);
chatMessageBytes = outmsg.LengthBytes - outmsg.LengthBytes;
chatMessageBytes = outmsg.LengthBytes - chatMessageBytes;
outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE);
@@ -1927,7 +1936,11 @@ namespace Barotrauma.Networking
warningMsg +=
" Settings buffer size: " + settingsBuf.LengthBytes + " bytes\n";
}
if (GameSettings.VerboseLogging) { DebugConsole.AddWarning(warningMsg); }
#if DEBUG || UNSTABLE
DebugConsole.ThrowError(warningMsg);
#else
if (GameSettings.VerboseLogging) { DebugConsole.AddWarning(warningMsg); }
#endif
GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:ClientWriteLobby" + outmsg.LengthBytes, GameAnalyticsManager.ErrorSeverity.Warning, warningMsg);
}
@@ -1943,12 +1956,16 @@ namespace Barotrauma.Networking
//these large initial messages until the client acknowledges receiving them
c.LastRecvLobbyUpdate++;
SendVoteStatus(new List<Client>() { c });
}
else
{
serverPeer.Send(outmsg, c.Connection, DeliveryMethod.Unreliable);
}
if (isInitialUpdate)
{
SendVoteStatus(new List<Client>() { c });
}
}
private void WriteChatMessages(IWriteMessage outmsg, Client c)
@@ -2893,9 +2910,9 @@ namespace Barotrauma.Networking
SendDirectChatMessage(msg, recipient);
}
public void SendConsoleMessage(string txt, Client recipient)
public void SendConsoleMessage(string txt, Client recipient, Color? color = null)
{
ChatMessage msg = ChatMessage.Create("", txt, ChatMessageType.Console, null);
ChatMessage msg = ChatMessage.Create("", txt, ChatMessageType.Console, sender: null, textColor: color);
SendDirectChatMessage(msg, recipient);
}

View File

@@ -115,10 +115,14 @@ namespace Barotrauma.Networking
return ShouldStartRespawnCountdown(characterToRespawnCount);
}
private int GetMinCharactersToRespawn()
{
return Math.Max((int)(GameMain.Server.ConnectedClients.Count * GameMain.Server.ServerSettings.MinRespawnRatio), 1);
}
private bool ShouldStartRespawnCountdown(int characterToRespawnCount)
{
int totalCharacterCount = GameMain.Server.ConnectedClients.Count;
return (float)characterToRespawnCount >= Math.Max((float)totalCharacterCount * GameMain.Server.ServerSettings.MinRespawnRatio, 1.0f);
return characterToRespawnCount >= GetMinCharactersToRespawn();
}
partial void UpdateWaiting(float deltaTime)
@@ -129,7 +133,7 @@ namespace Barotrauma.Networking
}
pendingRespawnCount = GetClientsToRespawn().Count();
requiredRespawnCount = (int)Math.Max((float)GameMain.Server.ConnectedClients.Count * GameMain.Server.ServerSettings.MinRespawnRatio, 1.0f);
requiredRespawnCount = GetMinCharactersToRespawn();
if (pendingRespawnCount != prevPendingRespawnCount ||
requiredRespawnCount != prevRequiredRespawnCount)
{

View File

@@ -223,11 +223,6 @@ namespace Barotrauma.Networking
AutoRestart = autoRestart;
}
RadiationEnabled = incMsg.ReadBoolean();
int maxMissionCount = MaxMissionCount + incMsg.ReadByte() - 1;
MaxMissionCount = MathHelper.Clamp(maxMissionCount, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit);
changed |= true;
UpdateFlag(NetFlags.Misc);
}

View File

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

View File

@@ -340,7 +340,7 @@ namespace Barotrauma
{
targetingTag = "dead";
}
else if (AIParams.TryGetTarget(targetCharacter.CharacterHealth.GetActiveAfflictionTags(), out CharacterParams.TargetParams tp) && tp.Threshold > Character.GetDamageDoneByAttacker(targetCharacter))
else if (AIParams.TryGetTarget(targetCharacter.CharacterHealth.GetActiveAfflictionTags(), out CharacterParams.TargetParams tp) && tp.Threshold >= Character.GetDamageDoneByAttacker(targetCharacter))
{
targetingTag = tp.Tag;
}
@@ -678,7 +678,10 @@ namespace Barotrauma
return a.Damage >= selectedTargetingParams.Threshold;
}
Character attacker = targetCharacter.LastAttackers.LastOrDefault(IsValid)?.Character;
if (attacker != null)
//if the attacker has the same targeting tag as the character we're protecting, we can't change the TargetState
//otherwise e.g. a pet that's set to follow humans would start attacking all humans (and other pets, since they're considered part of the same group) when a hostile human attacks it
//TODO: a way for pets to differentiate hostile and friendly humans?
if (attacker?.AiTarget != null && !targetCharacter.SpeciesName.Equals(GetTargetingTag(attacker.AiTarget), StringComparison.OrdinalIgnoreCase))
{
// Attack the character that attacked the target we are protecting
ChangeTargetState(attacker, AIState.Attack, selectedTargetingParams.Priority * 2);
@@ -1598,7 +1601,7 @@ namespace Barotrauma
}
else
{
sweepTimer = Rand.Range(-1000, 1000) * selectedTargetingParams.SweepSpeed;
sweepTimer = Rand.Range(-1000f, 1000f) * selectedTargetingParams.SweepSpeed;
}
}
break;
@@ -2305,7 +2308,7 @@ namespace Barotrauma
if (item.Condition <= 0.0f)
{
if (!wasBroken) { PetBehavior?.OnEat(item.GetTags(), 1.0f); }
if (!wasBroken) { PetBehavior?.OnEat(item); }
Entity.Spawner.AddToRemoveQueue(item);
}
}

View File

@@ -565,7 +565,7 @@ namespace Barotrauma
Character.AnimController.HeadInWater ||
Character.Submarine == null ||
(Character.Submarine.TeamID != Character.TeamID && !Character.IsEscorted) ||
ObjectiveManager.CurrentOrders.Any(o => o.Objective.KeepDivingGearOn) ||
!ObjectiveManager.IsCurrentObjective<AIObjectiveIdle>() && ObjectiveManager.CurrentOrders.Any(o => o.Objective.KeepDivingGearOn) ||
ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn) ||
Character.CurrentHull.OxygenPercentage < HULL_LOW_OXYGEN_PERCENTAGE + 10;
bool IsOrderedToWait() => Character.IsOnPlayerTeam && ObjectiveManager.CurrentOrder is AIObjectiveGoTo goTo && goTo.Target == Character;
@@ -1090,7 +1090,6 @@ namespace Barotrauma
private void RespondToAttack(Character attacker, AttackResult attackResult)
{
float minorDamageThreshold = 10;
float healAmount = 0.0f;
if (attacker != null)
{
@@ -1099,7 +1098,7 @@ namespace Barotrauma
// excluding poisons etc
float realDamage = attackResult.Damage - healAmount;
// including poisons etc
float totalDamage = realDamage - healAmount;
float totalDamage = realDamage;
if (attackResult.Afflictions != null)
{
foreach (Affliction affliction in attackResult.Afflictions)
@@ -1140,6 +1139,13 @@ namespace Barotrauma
}
bool isAttackerInfected = false;
bool isAttackerFightingEnemy = false;
float minorDamageThreshold = 1;
float majorDamageThreshold = 20;
if (attacker.TeamID == Character.TeamID)
{
minorDamageThreshold = 10;
majorDamageThreshold = 40;
}
if (IsFriendly(attacker))
{
if (attacker.AnimController.Anim == Barotrauma.AnimController.Animation.CPR && attacker.SelectedCharacter == Character)
@@ -1148,11 +1154,11 @@ namespace Barotrauma
// Should not cancel any existing ai objectives (so that if the character attacked you and then helped, we still would want to retaliate).
return;
}
float cumulativeDamage = Character.GetDamageDoneByAttacker(attacker);
float cumulativeDamage = realDamage + Character.GetDamageDoneByAttacker(attacker);
bool isAccidental = attacker.IsBot && !IsMentallyUnstable && !attacker.AIController.IsMentallyUnstable && Character.CombatAction == null;
if (isAccidental)
{
if (!Character.IsSecurity && cumulativeDamage > 1)
if (!Character.IsSecurity && cumulativeDamage > minorDamageThreshold)
{
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, attacker);
}
@@ -1161,7 +1167,7 @@ namespace Barotrauma
{
isAttackerInfected = attacker.CharacterHealth.GetAfflictionStrength("alieninfection") > 0;
// Inform other NPCs
if (isAttackerInfected || cumulativeDamage > 1 || totalDamage >= minorDamageThreshold)
if (isAttackerInfected || cumulativeDamage > minorDamageThreshold || totalDamage > minorDamageThreshold)
{
if (GameMain.IsMultiplayer || !attacker.IsPlayer || Character.TeamID != attacker.TeamID)
{
@@ -1170,27 +1176,36 @@ namespace Barotrauma
}
if (Character.IsBot)
{
if (ObjectiveManager.CurrentObjective is AIObjectiveFightIntruders) { return; }
if (attacker.IsPlayer)
var combatMode = DetermineCombatMode(Character, cumulativeDamage);
if (attacker.IsPlayer && !Character.IsInstigator && !ObjectiveManager.IsCurrentObjective<AIObjectiveCombat>())
{
if (Character.IsSecurity)
switch (combatMode)
{
if (attacker.TeamID != Character.TeamID && cumulativeDamage > 1 || cumulativeDamage > minorDamageThreshold)
{
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityarrest"), null, 0.50f, "attackedbyfriendlysecurityarrest", minDurationBetweenSimilar: 30.0f);
}
else
{
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityresponse"), null, 0.50f, "attackedbyfriendlysecurityresponse", minDurationBetweenSimilar: 30.0f);
}
}
else if (!Character.IsInstigator && cumulativeDamage > 1)
{
Character.Speak(TextManager.Get("DialogAttackedByFriendly"), null, 0.50f, "attackedbyfriendly", minDurationBetweenSimilar: 30.0f);
case AIObjectiveCombat.CombatMode.Defensive:
case AIObjectiveCombat.CombatMode.Retreat:
if (Character.IsSecurity)
{
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityresponse"), null, 0.5f, "attackedbyfriendlysecurityresponse", minDurationBetweenSimilar: 10.0f);
}
else
{
Character.Speak(TextManager.Get("DialogAttackedByFriendly"), null, 0.5f, "attackedbyfriendly", minDurationBetweenSimilar: 10.0f);
}
break;
case AIObjectiveCombat.CombatMode.Offensive:
case AIObjectiveCombat.CombatMode.Arrest:
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityarrest"), null, 0.5f, "attackedbyfriendlysecurityarrest", minDurationBetweenSimilar: 10.0f);
break;
case AIObjectiveCombat.CombatMode.None:
if (Character.IsSecurity && realDamage > 1)
{
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityresponse"), null, 0.5f, "attackedbyfriendlysecurityresponse", minDurationBetweenSimilar: 10.0f);
}
break;
}
}
// If the attacker is using a low damage and high frequency weapon like a repair tool, we shouldn't use any delay.
AddCombatObjective(DetermineCombatMode(Character, cumulativeDamage), attacker, delay: realDamage > 1 ? GetReactionTime() : 0);
AddCombatObjective(combatMode, attacker, delay: realDamage > 1 ? GetReactionTime() : 0);
}
if (!isAttackerFightingEnemy)
{
@@ -1203,15 +1218,15 @@ namespace Barotrauma
if (Character.Submarine != null && Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
{
// Non-friendly
InformOtherNPCs(Character.GetDamageDoneByAttacker(attacker));
InformOtherNPCs();
}
if (Character.IsBot)
{
AddCombatObjective(DetermineCombatMode(Character, cumulativeDamage: realDamage), attacker);
AddCombatObjective(DetermineCombatMode(Character), attacker);
}
}
void InformOtherNPCs(float cumulativeDamage)
void InformOtherNPCs(float cumulativeDamage = 0)
{
foreach (Character otherCharacter in Character.CharacterList)
{
@@ -1238,7 +1253,7 @@ namespace Barotrauma
}
}
AIObjectiveCombat.CombatMode DetermineCombatMode(Character c, float cumulativeDamage, bool isWitnessing = false)
AIObjectiveCombat.CombatMode DetermineCombatMode(Character c, float cumulativeDamage = 0, bool isWitnessing = false)
{
if (!IsFriendly(attacker))
{
@@ -1258,7 +1273,6 @@ namespace Barotrauma
}
else
{
float dmgThreshold = attacker.TeamID == Character.TeamID ? 50 : minorDamageThreshold;
if (isAttackerInfected)
{
cumulativeDamage = 100;
@@ -1266,8 +1280,7 @@ namespace Barotrauma
if (GameMain.IsSingleplayer && attacker.IsPlayer && Character.TeamID == attacker.TeamID)
{
// Bots in the player team never act aggressively in single player when attacked by the player
dmgThreshold = minorDamageThreshold;
return cumulativeDamage > dmgThreshold ? AIObjectiveCombat.CombatMode.Retreat : AIObjectiveCombat.CombatMode.None;
return cumulativeDamage > minorDamageThreshold ? AIObjectiveCombat.CombatMode.Retreat : AIObjectiveCombat.CombatMode.None;
}
if (Character.Submarine == null || !Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
{
@@ -1308,21 +1321,25 @@ namespace Barotrauma
// Already targeting the attacker -> treat as a more serious threat.
cumulativeDamage *= 2;
}
if (cumulativeDamage > dmgThreshold)
if (cumulativeDamage > majorDamageThreshold)
{
if (c.IsSecurity)
{
return c.IsSecurity ? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Arrest;
return AIObjectiveCombat.CombatMode.Offensive;
}
else
{
return c == Character ? AIObjectiveCombat.CombatMode.Defensive : AIObjectiveCombat.CombatMode.Retreat;
}
}
else
else if (cumulativeDamage > minorDamageThreshold)
{
return c.IsSecurity ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Retreat;
}
else
{
return AIObjectiveCombat.CombatMode.None;
}
}
Character FindInstigator()

View File

@@ -30,7 +30,7 @@ namespace Barotrauma
private float holdFireTimer;
private bool hasAimed;
private bool isLethalWeapon;
private bool AllowCoolDown => !IsOffensiveOrArrest || Mode != initialMode;
private bool AllowCoolDown => !IsOffensiveOrArrest || Mode != initialMode || character.TeamID == Enemy.TeamID;
public Character Enemy { get; private set; }
public bool HoldPosition { get; set; }
@@ -143,7 +143,7 @@ namespace Barotrauma
{
Mode = CombatMode.Retreat;
}
spreadTimer = Rand.Range(-10, 10);
spreadTimer = Rand.Range(-10f, 10f);
HumanAIController.SortTimer = 0;
}
@@ -1177,7 +1177,7 @@ namespace Barotrauma
}
private void SpeakNoWeapons() => Speak("dialogcombatnoweapons", delay: 0, minDuration: 30);
private void AskHelp() => Speak("dialogcombatretreating", delay: Rand.Range(0, 1), minDuration: 20);
private void AskHelp() => Speak("dialogcombatretreating", delay: Rand.Range(0f, 1f), minDuration: 20);
private void Speak(string textIdentifier, float delay, float minDuration)
{

View File

@@ -43,7 +43,6 @@ namespace Barotrauma
private readonly float minDistance = 50;
private readonly float seekGapsInterval = 1;
private float seekGapsTimer;
private bool cannotFollow;
/// <summary>
/// Display units
@@ -52,6 +51,11 @@ namespace Barotrauma
{
get
{
if (IsFollowOrderObjective && Target is Character targetCharacter && (targetCharacter.CurrentHull == null) != (character.CurrentHull == null))
{
// Keep close when the target is going inside/outside
return minDistance;
}
float dist = _closeEnough * CloseEnoughMultiplier;
float extraMultiplier = Math.Clamp(CloseEnoughMultiplier * 0.6f, 1, 3);
if (character.AnimController.InWater)
@@ -288,28 +292,16 @@ namespace Barotrauma
{
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: false, objectiveManager),
onAbandon: () => Abandon = true,
onCompleted: () =>
{
cannotFollow = false;
RemoveSubObjective(ref findDivingGear);
});
onCompleted: () => RemoveSubObjective(ref findDivingGear));
}
else
{
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager),
onAbandon: () => Abandon = true,
onCompleted: () =>
{
cannotFollow = false;
RemoveSubObjective(ref findDivingGear);
});
onCompleted: () => RemoveSubObjective(ref findDivingGear));
}
return;
}
else
{
cannotFollow = false;
}
}
if (repeat)
{
@@ -735,7 +727,6 @@ namespace Barotrauma
findDivingGear = null;
seekGapsTimer = 0;
TargetGap = null;
cannotFollow = false;
}
}
}

View File

@@ -408,7 +408,7 @@ namespace Barotrauma
if (orderGiver == null) { return null; }
newObjective = new AIObjectiveGoTo(orderGiver, character, this, repeat: true, priorityModifier: priorityModifier)
{
CloseEnough = Rand.Range(80, 100),
CloseEnough = Rand.Range(80f, 100f),
CloseEnoughMultiplier = Math.Min(1 + HumanAIController.CountCrew(c => c.ObjectiveManager.HasOrder<AIObjectiveGoTo>(o => o.Target == orderGiver), onlyBots: true) * Rand.Range(0.8f, 1f), 4),
ExtraDistanceOutsideSub = 100,
ExtraDistanceWhileSwimming = 100,

View File

@@ -221,6 +221,9 @@ namespace Barotrauma
/// </summary>
public int AssignmentPriority { get; }
public bool ColoredWhenControllingGiver { get; }
public bool DisplayGiverInTooltip { get; }
public static void Init()
{
Prefabs = new Dictionary<string, Order>();
@@ -406,6 +409,8 @@ namespace Barotrauma
DrawIconWhenContained = orderElement.GetAttributeBool("displayiconwhencontained", false);
AutoDismiss = orderElement.GetAttributeBool("autodismiss", Category == OrderCategory.Movement);
AssignmentPriority = Math.Clamp(orderElement.GetAttributeInt("assignmentpriority", 100), 0, 100);
ColoredWhenControllingGiver = orderElement.GetAttributeBool("coloredwhencontrollinggiver", false);
DisplayGiverInTooltip = orderElement.GetAttributeBool("displaygiverintooltip", false);
}
/// <summary>
@@ -441,6 +446,8 @@ namespace Barotrauma
Hidden = prefab.Hidden;
IgnoreAtOutpost = prefab.IgnoreAtOutpost;
AssignmentPriority = prefab.AssignmentPriority;
ColoredWhenControllingGiver = prefab.ColoredWhenControllingGiver;
DisplayGiverInTooltip = prefab.DisplayGiverInTooltip;
OrderGiver = orderGiver;
TargetEntity = targetEntity;

View File

@@ -134,6 +134,7 @@ namespace Barotrauma
aggregate += Items[i].Commonness;
if (aggregate >= r && Items[i].Prefab != null)
{
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":PetProducedItem:" + pet.AiController.Character.SpeciesName + ":" + Items[i].Prefab.Identifier);
Entity.Spawner.AddToSpawnQueue(Items[i].Prefab, pet.AiController.Character.WorldPosition);
break;
}
@@ -200,6 +201,8 @@ namespace Barotrauma
break;
}
}
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":PetSpawned:" + aiController.Character.SpeciesName);
}
public StatusIndicatorType GetCurrentStatusIndicatorType()
@@ -210,23 +213,44 @@ namespace Barotrauma
return StatusIndicatorType.None;
}
public bool OnEat(IEnumerable<string> tags, float amount)
public bool OnEat(Item item)
{
bool success = OnEat(item.GetTags());
if (success)
{
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":PetEat:" + AiController.Character.SpeciesName + ":" + item.prefab.Identifier);
}
return success;
}
public bool OnEat(Character character)
{
if (character == null || !character.IsDead) { return false; }
bool success = OnEat("dead");
if (success)
{
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":PetEat:" + AiController.Character.SpeciesName + ":" + character.SpeciesName);
}
return success;
}
private bool OnEat(IEnumerable<string> tags)
{
foreach (string tag in tags)
{
if (OnEat(tag, amount)) { return true; }
if (OnEat(tag)) { return true; }
}
return false;
}
public bool OnEat(string tag, float amount)
private bool OnEat(string tag)
{
for (int i = 0; i < foods.Count; i++)
{
if (tag.Equals(foods[i].Tag, System.StringComparison.OrdinalIgnoreCase))
{
Hunger += foods[i].Hunger * amount;
Happiness += foods[i].Happiness * amount;
Hunger += foods[i].Hunger;
Happiness += foods[i].Happiness;
#if CLIENT
AiController.Character.PlaySound(CharacterSound.SoundType.Happy, 0.5f);
#endif

View File

@@ -22,7 +22,13 @@ namespace Barotrauma
public override void CalculateImportanceSpecific()
{
if (TargetItemComponent is Turret turret && !turret.HasPowerToShoot()) { return; }
if (TargetItemComponent is Turret turret && !turret.HasPowerToShoot())
{
//operate (= recharge the turrets) with low priority if they're out of power
//if something else (issues with reactor or the electrical grid) is preventing them from being charged, fixing those issues should take priority
Importance = ShipCommandManager.MinimumIssueThreshold * 1.05f;
return;
}
targetingImportances.Clear();
foreach (Character character in shipCommandManager.EnemyCharacters)

View File

@@ -51,7 +51,7 @@ namespace Barotrauma
private const float RamTimerMax = 17.5f;
public readonly List<ShipIssueWorker> ShipIssueWorkers = new List<ShipIssueWorker>();
private const float MinimumIssueThreshold = 10f;
public const float MinimumIssueThreshold = 10f;
private const float IssueDevotionBuffer = 5f;
private float decisionTimer = 6f;

View File

@@ -254,7 +254,7 @@ namespace Barotrauma
private void SpawnInitialCells()
{
int brainRoomCells = Rand.Range(MinCellsPerBrainRoom, MaxCellsPerRoom);
int brainRoomCells = Rand.Range(MinCellsPerBrainRoom, MaxCellsPerRoom + 1);
if (brain.CurrentHull?.WaterPercentage >= MinWaterLevel)
{
for (int i = 0; i < brainRoomCells; i++)
@@ -262,12 +262,12 @@ namespace Barotrauma
if (!TrySpawnCell(out _, brain.CurrentHull)) { break; }
}
}
int cellsInside = Rand.Range(MinCellsInside, MaxCellsInside);
int cellsInside = Rand.Range(MinCellsInside, MaxCellsInside + 1);
for (int i = 0; i < cellsInside; i++)
{
if (!TrySpawnCell(out _)) { break; }
}
int cellsOutside = Rand.Range(MinCellsOutside, MaxCellsOutside);
int cellsOutside = Rand.Range(MinCellsOutside, MaxCellsOutside + 1);
// If we failed to spawn some of the cells in the brainroom/inside, spawn some extra cells outside.
cellsOutside = Math.Clamp(cellsOutside + brainRoomCells + cellsInside - protectiveCells.Count, cellsOutside, MaxCellsOutside);
for (int i = 0; i < cellsOutside; i++)

View File

@@ -420,7 +420,7 @@ namespace Barotrauma
if (Character.AIController is EnemyAIController enemyAi)
{
enemyAi.PetBehavior?.OnEat("dead", 1.0f);
enemyAi.PetBehavior?.OnEat(target);
}
character.SelectedCharacter = null;

View File

@@ -533,6 +533,8 @@ namespace Barotrauma
bool onSlope = Math.Abs(movement.X) > 0.01f && Math.Abs(floorNormal.X) > 0.1f && Math.Sign(floorNormal.X) != Math.Sign(movement.X);
bool movingHorizontally = !MathUtils.NearlyEqual(targetMovement.X, 0.0f);
if (Stairs != null || onSlope)
{
torso.PullJointWorldAnchorB = new Vector2(
@@ -562,10 +564,8 @@ namespace Barotrauma
if (!torso.Disabled)
{
if (TorsoPosition.HasValue)
{
y += TorsoPosition.Value;
}
if (TorsoPosition.HasValue) { y += TorsoPosition.Value; }
if (Crouching && !movingHorizontally) { y -= HumanCrouchParams.MoveDownAmountWhenStationary; }
torso.PullJointWorldAnchorB =
MathUtils.SmoothStep(torso.SimPosition,
new Vector2(footMid + movement.X * TorsoLeanAmount, y), getUpForce);
@@ -574,10 +574,8 @@ namespace Barotrauma
if (!head.Disabled)
{
y = colliderPos.Y + stepLift * CurrentGroundedParams.StepLiftHeadMultiplier;
if (HeadPosition.HasValue)
{
y += HeadPosition.Value;
}
if (HeadPosition.HasValue) { y += HeadPosition.Value; }
if (Crouching && !movingHorizontally) { y -= HumanCrouchParams.MoveDownAmountWhenStationary; }
head.PullJointWorldAnchorB =
MathUtils.SmoothStep(head.SimPosition,
new Vector2(footMid + movement.X * HeadLeanAmount, y), getUpForce * 1.2f);
@@ -593,12 +591,15 @@ namespace Barotrauma
{
float torsoAngle = TorsoAngle.Value;
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
if (Crouching && !movingHorizontally) { torsoAngle -= HumanCrouchParams.ExtraTorsoAngleWhenStationary; }
torsoAngle -= herpesStrength / 150.0f;
torso.body.SmoothRotate(torsoAngle * Dir, CurrentGroundedParams.TorsoTorque);
}
if (HeadAngle.HasValue)
{
head.body.SmoothRotate(HeadAngle.Value * Dir, CurrentGroundedParams.HeadTorque);
float headAngle = HeadAngle.Value;
if (Crouching && !movingHorizontally) { headAngle -= HumanCrouchParams.ExtraHeadAngleWhenStationary; }
head.body.SmoothRotate(headAngle * Dir, CurrentGroundedParams.HeadTorque);
}
if (!onGround)
@@ -616,8 +617,7 @@ namespace Barotrauma
Vector2 waistPos = waist != null ? waist.SimPosition : torso.SimPosition;
//moving horizontally
if (TargetMovement.X != 0.0f)
if (movingHorizontally)
{
//progress the walking animation
WalkPos -= MathHelper.ToRadians(CurrentAnimationParams.CycleSpeed) * walkCycleMultiplier * movement.X;

View File

@@ -261,9 +261,16 @@ namespace Barotrauma
public AttackResult LastDamage;
public Dictionary<ItemPrefab, double> ItemSelectedDurations
{
get { return itemSelectedDurations; }
}
private readonly Dictionary<ItemPrefab, double> itemSelectedDurations = new Dictionary<ItemPrefab, double>();
private double itemSelectedTime;
public float InvisibleTimer;
private CharacterPrefab prefab;
private readonly CharacterPrefab prefab;
public readonly CharacterParams Params;
public string SpeciesName => Params?.SpeciesName ?? "null";
@@ -700,7 +707,7 @@ namespace Barotrauma
{
get
{
if (!CanSpeak || IsUnconscious || Stun > 0.0f || IsDead) { return 100.0f; }
if (!CanSpeak || IsUnconscious || IsKnockedDown) { return 100.0f; }
return speechImpediment;
}
set
@@ -737,9 +744,7 @@ namespace Barotrauma
get => _selectedConstruction;
set
{
#if CLIENT
var prevSelectedConstruction = _selectedConstruction;
#endif
_selectedConstruction = value;
#if CLIENT
HintManager.OnSetSelectedConstruction(this, prevSelectedConstruction, _selectedConstruction);
@@ -755,6 +760,19 @@ namespace Barotrauma
}
}
#endif
if (prevSelectedConstruction == null && _selectedConstruction != null)
{
itemSelectedTime = Timing.TotalTime;
}
else if (prevSelectedConstruction != null && _selectedConstruction == null && itemSelectedTime > 0)
{
if (!itemSelectedDurations.ContainsKey(prevSelectedConstruction.Prefab))
{
itemSelectedDurations.Add(prevSelectedConstruction.Prefab, 0);
}
itemSelectedDurations[prevSelectedConstruction.Prefab] += Timing.TotalTime - itemSelectedTime;
itemSelectedTime = 0;
}
}
}
@@ -3950,27 +3968,44 @@ namespace Barotrauma
AnimController.Frozen = false;
if (GameAnalyticsManager.SendUserStatistics)
{
string characterType = "Unknown";
if (this == Controlled)
characterType = "Player";
else if (IsRemotePlayer)
characterType = "RemotePlayer";
else if (AIController is EnemyAIController)
characterType = "Enemy";
else if (AIController is HumanAIController)
characterType = "AICrew";
string causeOfDeathStr = causeOfDeathAffliction == null ?
causeOfDeath.ToString() : causeOfDeathAffliction.Prefab.Name.Replace(" ", "");
GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":" + SpeciesName + ":" + causeOfDeathStr);
}
CauseOfDeath = new CauseOfDeath(
causeOfDeath, causeOfDeathAffliction?.Prefab,
causeOfDeathAffliction?.Source ?? LastAttacker, LastDamageSource);
causeOfDeathAffliction?.Source, LastDamageSource);
if (GameAnalyticsManager.SendUserStatistics)
{
string causeOfDeathStr = causeOfDeathAffliction == null ?
causeOfDeath.ToString() : causeOfDeathAffliction.Prefab.Identifier.Replace(" ", "");
string characterType = GetCharacterType(this);
GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":" + causeOfDeathStr);
if (CauseOfDeath.Killer != null)
{
GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":Killer:" + GetCharacterType(CauseOfDeath.Killer));
}
if (CauseOfDeath.DamageSource != null)
{
string damageSourceStr = CauseOfDeath.DamageSource.ToString();
if (CauseOfDeath.DamageSource is Item damageSourceItem) { damageSourceStr = damageSourceItem.ToString(); }
GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":DamageSource:" + damageSourceStr);
}
static string GetCharacterType(Character character)
{
if (character.IsPlayer)
return "Player";
else if (character.AIController is EnemyAIController)
return "Enemy" + character.SpeciesName;
else if (character.AIController is HumanAIController && character.TeamID == CharacterTeamType.Team2)
return "EnemyHuman";
else if (character.Info != null && character.TeamID == CharacterTeamType.Team1)
return "AICrew";
else if (character.Info != null && character.TeamID == CharacterTeamType.FriendlyNPC)
return "FriendlyNPC";
return "Unknown";
}
}
OnDeath?.Invoke(this, CauseOfDeath);
var abilityCharacterKiller = new AbilityCharacterKiller(CauseOfDeath.Killer);
@@ -4097,7 +4132,7 @@ namespace Barotrauma
info?.Remove();
#if CLIENT
GameMain.GameSession?.CrewManager?.KillCharacter(this);
GameMain.GameSession?.CrewManager?.KillCharacter(this, resetCrewListIndex: false);
#endif
CharacterList.Remove(this);
@@ -4112,6 +4147,8 @@ namespace Barotrauma
}
}
itemSelectedDurations.Clear();
DisposeProjSpecific();
aiTarget?.Remove();

View File

@@ -1527,7 +1527,7 @@ namespace Barotrauma
orderTargetElement.Add(new XAttribute("hullid", (uint)ot.Hull.ID));
position -= ot.Hull.WorldPosition;
}
orderTargetElement.Add(new XAttribute("position", $"{position.X},{position.Y}"));
orderTargetElement.Add(new XAttribute("position", XMLExtensions.Vector2ToString(position)));
orderElement.Add(orderTargetElement);
break;
case Order.OrderTargetType.WallSection when targetAvailableInNextLevel && order.TargetEntity is Structure s && order.WallSectionIndex.HasValue:

View File

@@ -106,7 +106,7 @@ namespace Barotrauma
{
if (State != InfectionState.Active && stun)
{
character.SetStun(Rand.Range(2, 4));
character.SetStun(Rand.Range(2f, 3f));
}
State = InfectionState.Active;
ActivateHusk();

View File

@@ -363,7 +363,9 @@ namespace Barotrauma
public readonly string Name, Description;
public readonly string TranslationOverride;
public readonly bool IsBuff;
public readonly bool HealableInMedicalClinic;
public readonly float HealCostMultiplier;
public readonly int BaseHealCost;
public readonly string CauseOfDeathDescription, SelfCauseOfDeathDescription;
@@ -656,7 +658,13 @@ namespace Barotrauma
Name = TextManager.Get("AfflictionName." + translationId, true) ?? element.GetAttributeString("name", "");
Description = TextManager.Get("AfflictionDescription." + translationId, true) ?? element.GetAttributeString("description", "");
IsBuff = element.GetAttributeBool("isbuff", false);
HealableInMedicalClinic = element.GetAttributeBool("healableinmedicalclinic",
!IsBuff &&
!AfflictionType.Equals("geneticmaterialbuff", StringComparison.OrdinalIgnoreCase) &&
!AfflictionType.Equals("geneticmaterialdebuff", StringComparison.OrdinalIgnoreCase));
HealCostMultiplier = element.GetAttributeFloat(nameof(HealCostMultiplier).ToLowerInvariant(), 1f);
BaseHealCost = element.GetAttributeInt(nameof(BaseHealCost).ToLowerInvariant(), 0);
if (element.Attribute("nameidentifier") != null)
{

View File

@@ -764,6 +764,7 @@ namespace Barotrauma
if (applyAffliction)
{
afflictionsCopy.Add(newAffliction);
newAffliction.Source ??= attacker;
}
appliedDamageModifiers.AddRange(tempModifiers);
}

View File

@@ -26,6 +26,15 @@ namespace Barotrauma
class HumanCrouchParams : HumanGroundedParams
{
[Serialize(0.0f, true, description: "How much lower the character's head and torso move when stationary."), Editable(MinValueFloat = 0, MaxValueFloat = 2, DecimalCount = 2)]
public float MoveDownAmountWhenStationary { get; set; }
[Serialize(0.0f, true), Editable(-360f, 360f)]
public float ExtraHeadAngleWhenStationary { get; set; }
[Serialize(0.0f, true), Editable(-360f, 360f)]
public float ExtraTorsoAngleWhenStationary { get; set; }
public static HumanCrouchParams GetDefaultAnimParams(Character character) => GetDefaultAnimParams<HumanCrouchParams>(character, AnimationType.Crouch);
public static HumanCrouchParams GetAnimParams(Character character, string fileName = null)
{

View File

@@ -23,7 +23,9 @@ namespace Barotrauma.Abilities
multiplier = 0 + Character.Info.GetSavedStatValue(StatTypes.None, scalingStatIdentifier);
}
targetCharacter.GiveMoney((int)(multiplier * amount));
int totalAmount = (int)(multiplier * amount);
targetCharacter.GiveMoney(totalAmount);
GameAnalyticsManager.AddMoneyGainedEvent(totalAmount, GameAnalyticsManager.MoneySource.Ability, CharacterTalent.Prefab.Identifier);
}
protected override void ApplyEffect(AbilityObject abilityObject)

View File

@@ -16,7 +16,9 @@ namespace Barotrauma.Abilities
{
if ((abilityObject as IAbilityCharacter)?.Character is Character character)
{
Character.GiveMoney((int)(vitalityPercentage * character.MaxVitality));
int totalAmount = (int)(vitalityPercentage * character.MaxVitality);
Character.GiveMoney(totalAmount);
GameAnalyticsManager.AddMoneyGainedEvent(totalAmount, GameAnalyticsManager.MoneySource.Ability, CharacterTalent.Prefab.Identifier);
}
}
}

View File

@@ -30,6 +30,7 @@ namespace Barotrauma.Abilities
if (!enemyCharacter.LockHands) { continue; }
if (timesGiven > max) { continue; }
Character.GiveMoney(moneyAmount);
GameAnalyticsManager.AddMoneyGainedEvent(moneyAmount, GameAnalyticsManager.MoneySource.Ability, CharacterTalent.Prefab.Identifier);
foreach (Character character in Character.GetFriendlyCrew(Character))
{
character.Info?.GiveExperience(experienceAmount);

View File

@@ -12,8 +12,6 @@ namespace Barotrauma.Abilities
private readonly int moneyPerMission;
private static List<Client> clientsAlreadyUsed = new List<Client>();
public CharacterAbilityInsurancePolicy(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
moneyPerMission = abilityElement.GetAttributeInt("moneypermission", 0);
@@ -23,7 +21,9 @@ namespace Barotrauma.Abilities
{
if (Character?.Info is CharacterInfo info)
{
Character.GiveMoney(moneyPerMission * info.MissionsCompletedSinceDeath);
int totalAmount = moneyPerMission * info.MissionsCompletedSinceDeath;
Character.GiveMoney(totalAmount);
GameAnalyticsManager.AddMoneyGainedEvent(totalAmount, GameAnalyticsManager.MoneySource.Ability, CharacterTalent.Prefab.Identifier);
}
}
}

View File

@@ -1534,6 +1534,7 @@ namespace Barotrauma
if (int.TryParse(args[0], out int money))
{
campaign.Money += money;
GameAnalyticsManager.AddMoneyGainedEvent(money, GameAnalyticsManager.MoneySource.Cheat, "console");
}
else
{

View File

@@ -28,6 +28,7 @@ namespace Barotrauma
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{
campaign.Money += Amount;
GameAnalyticsManager.AddMoneyGainedEvent(Amount, GameAnalyticsManager.MoneySource.Event, ParentEvent.Prefab.Identifier);
#if SERVER
(campaign as MultiPlayerCampaign).LastUpdateID++;
#endif

View File

@@ -152,7 +152,7 @@ namespace Barotrauma
if (spawnPoint is WayPoint wp && wp.CurrentHull != null && wp.CurrentHull.Rect.Width > 100)
{
spawnPos = new Vector2(
MathHelper.Clamp(wp.WorldPosition.X + Rand.Range(-200, 200), wp.CurrentHull.WorldRect.X + 50, wp.CurrentHull.WorldRect.Right - 50),
MathHelper.Clamp(wp.WorldPosition.X + Rand.Range(-200, 201), wp.CurrentHull.WorldRect.X + 50, wp.CurrentHull.WorldRect.Right - 50),
wp.CurrentHull.WorldRect.Y - wp.CurrentHull.Rect.Height + 16.0f);
}
var item = new Item(itemPrefab, spawnPos, null);

View File

@@ -319,6 +319,19 @@ namespace Barotrauma
}
}
foreach (Character character in characters)
{
if (character.Inventory == null) { continue; }
foreach (Item item in character.Inventory.AllItemsMod)
{
//item didn't spawn with the characters -> drop it
if (!characterItems.Any(c => c.Value.Contains(item)))
{
item.Drop(character);
}
}
}
// characters that survived will take their items with them, in case players tried to be crafty and steal them
// this needs to run here in case players abort the mission by going back home
// TODO: I think this might feel like a bug.

View File

@@ -378,7 +378,10 @@ namespace Barotrauma
crewCharacters.ForEach(c => c.CheckTalents(AbilityEffectType.OnGainMissionMoney, missionMoneyGainMultiplier));
crewCharacters.ForEach(c => missionMoneyGainMultiplier.Value += c.GetStatValue(StatTypes.MissionMoneyGainMultiplier));
campaign.Money += (int)(reward * missionMoneyGainMultiplier.Value);
int totalReward = (int)(reward * missionMoneyGainMultiplier.Value);
campaign.Money += totalReward;
GameAnalyticsManager.AddMoneyGainedEvent(totalReward, GameAnalyticsManager.MoneySource.MissionReward, Prefab.Identifier);
foreach (Character character in crewCharacters)
{

View File

@@ -328,7 +328,7 @@ namespace Barotrauma
}
else
{
dir = new Vector2(1, Rand.Range(-1, 1));
dir = new Vector2(1, Rand.Range(-1f, 1f));
}
Vector2 targetPos = spawnPos.Value + dir * offset;
var targetWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, targetPos)).FirstOrDefault();
@@ -475,6 +475,7 @@ namespace Barotrauma
{
scatterAmount = 0;
}
for (int i = 0; i < amount; i++)
{
string seed = Level.Loaded.Seed + i.ToString();
@@ -540,6 +541,13 @@ namespace Barotrauma
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
DebugConsole.NewMessage($"Spawned: {ToString()}. Strength: {StringFormatter.FormatZeroDecimal(monsters.Sum(m => m.Params.AI.CombatStrength))}.", Color.LightBlue, debugOnly: true);
}
if (GameMain.GameSession != null)
{
GameAnalyticsManager.AddDesignEvent(
$"MonsterSpawn:{GameMain.GameSession.GameMode?.Preset?.Identifier ?? "none"}:{Level.Loaded?.LevelData?.Biome?.Identifier ?? "none"}:{SpawnPosType}:{speciesName}",
value: Timing.TotalTime - GameMain.GameSession.RoundStartTime);
}
}, delayBetweenSpawns * i);
}
}

View File

@@ -1,12 +1,11 @@
using System;
using Barotrauma.Steam;
using RestSharp;
using System;
using System.Net;
using System.Threading.Tasks;
namespace Barotrauma
{
public static partial class GameAnalyticsManager
static partial class GameAnalyticsManager
{
public enum Consent
{

View File

@@ -1,5 +1,6 @@
#nullable enable
using Barotrauma.IO;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,7 +10,7 @@ using System.Text;
namespace Barotrauma
{
public static partial class GameAnalyticsManager
static partial class GameAnalyticsManager
{
public enum ErrorSeverity
{
@@ -29,6 +30,61 @@ namespace Barotrauma
Fail = 3
}
public enum CustomDimensions01
{
Vanilla,
Modded
}
public enum CustomDimensions02
{
None,
Difficulty0to10,
Difficulty10to20,
Difficulty20to30,
Difficulty30to40,
Difficulty40to50,
Difficulty50to60,
Difficulty60to70,
Difficulty70to80,
Difficulty80to90,
Difficulty90to100,
}
public enum ResourceCurrency
{
Money
}
public enum ResourceFlowType
{
Undefined = 0,
Source = 1,
Sink = 2
}
public enum MoneySource
{
Unknown,
MissionReward,
Store,
Event,
Ability,
Cheat
}
public enum MoneySink
{
Unknown,
Store,
Service,
Crew,
SubmarineUpgrade,
SubmarineWeapon,
SubmarinePurchase,
SubmarineSwitch
}
private readonly static HashSet<string> sentEventIdentifiers = new HashSet<string>();
private class Implementation : IDisposable
@@ -69,13 +125,29 @@ namespace Barotrauma
internal void AddProgressionEvent(ProgressionStatus status, string progression01, string progression02, string progression03)
=> addProgressionEvent03(status, progression01, progression02, progression03);
private readonly Action<ResourceFlowType, string, float, string, string> addResourceEvent;
internal void AddResourceEvent(ResourceFlowType flowType, string currency, float amount, string itemType, string itemId)
=> addResourceEvent(flowType, currency, amount, itemType, itemId);
private readonly Action<string> setCustomDimension01;
internal void SetCustomDimension01(string dimension01)
=> setCustomDimension01(dimension01);
private readonly Action<string[]> configureAvailableCustomDimensions01;
internal void ConfigureAvailableCustomDimensions01(params string[] customDimensions)
=> configureAvailableCustomDimensions01(customDimensions);
internal void ConfigureAvailableCustomDimensions01(params CustomDimensions01[] customDimensions)
=> configureAvailableCustomDimensions01(customDimensions.Select(d => d.ToString()).ToArray());
private readonly Action<string> setCustomDimension02;
internal void SetCustomDimension02(string dimension02)
=> setCustomDimension02(dimension02);
private readonly Action<string[]> configureAvailableCustomDimensions02;
internal void ConfigureAvailableCustomDimensions02(params CustomDimensions02[] customDimensions)
=> configureAvailableCustomDimensions02(customDimensions.Select(d => d.ToString()).ToArray());
private readonly Action<string[]> configureAvailableResourceCurrencies;
internal void ConfigureAvailableResourceCurrencies(params ResourceCurrency[] customDimensions)
=> configureAvailableResourceCurrencies(customDimensions.Select(d => d.ToString()).ToArray());
private readonly Action<bool> setEnabledInfoLog;
internal void SetEnabledInfoLog(bool enabled)
@@ -94,6 +166,7 @@ namespace Barotrauma
private readonly object?[] args2 = new object?[2];
private readonly object?[] args3 = new object?[3];
private readonly object?[] args4 = new object?[4];
private readonly object?[] args5 = new object?[5];
private Action Call(MethodInfo methodInfo)
=> () => methodInfo?.Invoke(null, null);
@@ -131,6 +204,17 @@ namespace Barotrauma
args4[3] = arg4;
methodInfo.Invoke(null, args4);
};
private Action<T1, T2, T3, T4, T5> Call<T1, T2, T3, T4, T5>(MethodInfo methodInfo)
=> (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) =>
{
args5[0] = arg1;
args5[1] = arg2;
args5[2] = arg3;
args5[3] = arg4;
args5[4] = arg5;
methodInfo.Invoke(null, args5);
};
#endregion
private AssemblyLoadContext? loadContext;
@@ -165,9 +249,15 @@ namespace Barotrauma
var mainClass = getType(MainClass);
var errorSeverityEnumType = getType($"{EnumPrefix}{nameof(ErrorSeverity)}");
var progressionStatusEnumType = getType($"{EnumPrefix}{nameof(ProgressionStatus)}");
var resourceFlowTypeEnumType = getType($"{EnumPrefix}{nameof(ResourceFlowType)}");
MethodInfo getMethod(string name, Type[] types)
{
foreach (var me in mainClass.GetMethods())
{
var aksjdnakjsdnf = me;
}
return mainClass?.GetMethod(name, BindingFlags.Public | BindingFlags.Static, binder: null, types: types, modifiers: null)
?? throw new Exception($"Could not find method \"{name}\" with types {string.Join(',', types.Select(t => t.Name))}");
}
@@ -190,10 +280,20 @@ namespace Barotrauma
new Type[] { progressionStatusEnumType, typeof(string), typeof(string) }));
addProgressionEvent03 = Call<ProgressionStatus, string, string, string>(getMethod(nameof(AddProgressionEvent),
new Type[] { progressionStatusEnumType, typeof(string), typeof(string), typeof(string) }));
setCustomDimension01 = Call<string>(getMethod(nameof(SetCustomDimension01),
new Type[] { typeof(string) }));
configureAvailableCustomDimensions01 = Call<string[]>(getMethod(nameof(ConfigureAvailableCustomDimensions01),
new Type[] { typeof(string[]) }));
setCustomDimension02 = Call<string>(getMethod(nameof(SetCustomDimension02),
new Type[] { typeof(string) }));
configureAvailableCustomDimensions02 = Call<string[]>(getMethod(nameof(ConfigureAvailableCustomDimensions02),
new Type[] { typeof(string[]) }));
configureAvailableResourceCurrencies = Call<string[]>(getMethod(nameof(ConfigureAvailableResourceCurrencies),
new Type[] { typeof(string[]) }));
addResourceEvent = Call<ResourceFlowType, string, float, string, string>(getMethod(nameof(AddResourceEvent),
new Type[] { resourceFlowTypeEnumType, typeof(string), typeof(float), typeof(string), typeof(string) }));
setEnabledInfoLog = Call<bool>(getMethod(nameof(SetEnabledInfoLog),
new Type[] { typeof(bool) }));
@@ -204,8 +304,7 @@ namespace Barotrauma
private void OnQuit()
{
try
{
{
if (assembly != null) { onQuit?.Invoke(); }
}
catch (Exception e)
@@ -298,10 +397,40 @@ namespace Barotrauma
loadedImplementation?.AddProgressionEvent(progressionStatus, progression01, progression02, progression03);
}
public static void SetCustomDimension01(string dimension)
public static void SetCustomDimension01(CustomDimensions01 dimension)
{
if (!SendUserStatistics) { return; }
loadedImplementation?.SetCustomDimension01(dimension);
loadedImplementation?.SetCustomDimension01(dimension.ToString());
}
public static void SetCurrentLevel(LevelData levelData)
{
if (!SendUserStatistics) { return; }
CustomDimensions02 customDimension = CustomDimensions02.None;
if (levelData != null)
{
float levelDifficulty = levelData.Difficulty;
customDimension = (CustomDimensions02)MathHelper.Clamp((int)(levelDifficulty / 10) + 1, 0, Enum.GetValues(typeof(CustomDimensions02)).Length - 1);
}
loadedImplementation?.SetCustomDimension02(customDimension.ToString());
}
public static void AddMoneyGainedEvent(int amount, MoneySource moneySource, string eventId)
{
AddResourceEvent(ResourceFlowType.Source, ResourceCurrency.Money, amount, moneySource.ToString(), eventId);
}
public static void AddMoneySpentEvent(int amount, MoneySink moneySink, string eventId)
{
AddResourceEvent(ResourceFlowType.Sink, ResourceCurrency.Money, amount, moneySink.ToString(), eventId);
}
private static void AddResourceEvent(ResourceFlowType flowType, ResourceCurrency currency, float amount, string eventType, string eventId)
{
if (!SendUserStatistics) { return; }
loadedImplementation?.AddResourceEvent(flowType, currency.ToString(), amount, eventType, eventId);
}
private static void Init()
@@ -359,7 +488,8 @@ namespace Barotrauma
+ exeName + ":"
+ AssemblyInfo.GitRevision + ":"
+ buildConfiguration);
loadedImplementation?.ConfigureAvailableCustomDimensions01("singleplayer", "multiplayer", "editor");
loadedImplementation?.ConfigureAvailableCustomDimensions01(Enum.GetValues(typeof(CustomDimensions01)).Cast<CustomDimensions01>().ToArray());
loadedImplementation?.ConfigureAvailableResourceCurrencies(Enum.GetValues(typeof(ResourceCurrency)).Cast<ResourceCurrency>().ToArray());
InitKeys();
@@ -380,15 +510,16 @@ namespace Barotrauma
var allPackages = GameMain.Config?.AllEnabledPackages.ToList();
if (allPackages?.Count > 0)
{
StringBuilder sb = new StringBuilder("ContentPackage: ");
int i = 0;
List<string> packageNames = new List<string>();
foreach (ContentPackage cp in allPackages)
{
string trimmedName = cp.Name.Replace(":", "").Replace(" ", "");
sb.Append(trimmedName.Substring(0, Math.Min(32, trimmedName.Length)));
if (i < allPackages.Count - 1) { sb.Append(" "); }
string sanitizedName = cp.Name.Replace(":", "").Replace(" ", "");
sanitizedName = sanitizedName.Substring(0, Math.Min(32, sanitizedName.Length));
packageNames.Add(sanitizedName);
loadedImplementation?.AddDesignEvent("ContentPackage:" + sanitizedName);
}
loadedImplementation?.AddDesignEvent(sb.ToString());
packageNames.Sort();
loadedImplementation?.AddDesignEvent("AllContentPackages:" + string.Join(", ", packageNames));
}
}

View File

@@ -132,6 +132,7 @@ namespace Barotrauma
// Exchange money
var itemValue = item.Quantity * buyValues[item.ItemPrefab];
campaign.Money -= itemValue;
GameAnalyticsManager.AddMoneySpentEvent(itemValue, GameAnalyticsManager.MoneySink.Store, item.ItemPrefab.Identifier);
Location.StoreCurrentBalance += itemValue;
if (removeFromCrate)
@@ -291,7 +292,7 @@ namespace Barotrauma
float floorPos = hull.Rect.Y - hull.Rect.Height;
Vector2 position = new Vector2(
hull.Rect.Width > 40 ? Rand.Range(hull.Rect.X + 20, hull.Rect.Right - 20) : hull.Rect.Center.X,
hull.Rect.Width > 40 ? Rand.Range(hull.Rect.X + 20f, hull.Rect.Right - 20f) : hull.Rect.Center.X,
floorPos);
//check where the actual floor structure is in case the bottom of the hull extends below it

View File

@@ -37,7 +37,6 @@ namespace Barotrauma
{
IsSinglePlayer = isSinglePlayer;
conversationTimer = 5.0f;
InitProjectSpecific();
}
@@ -100,10 +99,10 @@ namespace Barotrauma
foreach (XElement characterElement in element.Elements())
{
if (!characterElement.Name.ToString().Equals("character", StringComparison.OrdinalIgnoreCase)) { continue; }
CharacterInfo characterInfo = new CharacterInfo(characterElement);
#if CLIENT
if (characterElement.GetAttributeBool("lastcontrolled", false)) { characterInfo.LastControlled = true; }
characterInfo.CrewListIndex = characterElement.GetAttributeInt("crewlistindex", -1);
#endif
characterInfos.Add(characterInfo);
foreach (XElement subElement in characterElement.Elements())
@@ -133,7 +132,7 @@ namespace Barotrauma
characterInfos.Remove(characterInfo);
}
public void AddCharacter(Character character)
public void AddCharacter(Character character, bool sortCrewList = true)
{
if (character.Removed)
{
@@ -155,7 +154,11 @@ namespace Barotrauma
characterInfos.Add(character.Info);
}
#if CLIENT
AddCharacterToCrewList(character);
var characterComponent = AddCharacterToCrewList(character);
if (sortCrewList)
{
SortCrewList();
}
if (character.CurrentOrders != null)
{
foreach (var order in character.CurrentOrders)
@@ -254,12 +257,16 @@ namespace Barotrauma
}
}
AddCharacter(character);
AddCharacter(character, sortCrewList: false);
#if CLIENT
if (IsSinglePlayer && (Character.Controlled == null || character.Info.LastControlled)) { Character.Controlled = character; }
#endif
}
#if CLIENT
if (IsSinglePlayer) { SortCrewList(); }
#endif
//longer delay in multiplayer to prevent the server from triggering NPC conversations while the players are still loading the round
conversationTimer = IsSinglePlayer ? Rand.Range(5.0f, 10.0f) : Rand.Range(45.0f, 60.0f);
}

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