0.1500.3.0 (🗿 edition)

This commit is contained in:
Markus Isberg
2021-09-17 22:47:21 +09:00
parent 1231170fce
commit 5a6bbcc79e
75 changed files with 1145 additions and 441 deletions

View File

@@ -801,7 +801,11 @@ namespace Barotrauma
{
var treatmentButton = component.GetChild<GUIButton>();
if (!(treatmentButton?.UserData is ItemPrefab itemPrefab)) { continue; }
treatmentButton.Enabled = Character.Controlled.Inventory.AllItems.Any(it => it.prefab == itemPrefab);
treatmentButton.Enabled = Character.Controlled.Inventory.AllItems.Any(it => it.prefab == itemPrefab);
foreach (GUIComponent child in treatmentButton.Children)
{
child.Enabled = treatmentButton.Enabled;
}
}
}
@@ -1155,7 +1159,10 @@ namespace Barotrauma
//key = item identifier
//float = suitability
Dictionary<string, float> treatmentSuitability = new Dictionary<string, float>();
GetSuitableTreatments(treatmentSuitability, normalize: true, limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex));
GetSuitableTreatments(treatmentSuitability,
normalize: true,
ignoreHiddenAfflictions: true,
limb: selectedLimbIndex == -1 ? null : Character.AnimController.Limbs.Find(l => l.HealthIndex == selectedLimbIndex));
foreach (string treatment in treatmentSuitability.Keys.ToList())
{
@@ -1260,10 +1267,11 @@ namespace Barotrauma
UserData = item
};
var innerFrame = new GUIButton(new RectTransform(Vector2.One, itemSlot.RectTransform, Anchor.Center, Pivot.Center, scaleBasis: ScaleBasis.Smallest), style: "GUIButtonRound")
var innerFrame = new GUIButton(new RectTransform(Vector2.One, itemSlot.RectTransform, Anchor.Center, Pivot.Center, scaleBasis: ScaleBasis.Smallest), style: "SubtreeHeader")
{
UserData = item,
ToolTip = $"‖color:255,255,255,255‖{item.Name}‖color:end‖" + '\n' + item.Description,
DisabledColor = Color.White * 0.1f,
OnClicked = (btn, userdata) =>
{
if (!(userdata is ItemPrefab itemPrefab)) { return false; }
@@ -1274,6 +1282,17 @@ namespace Barotrauma
return true;
}
};
new GUIImage(new RectTransform(Vector2.One, innerFrame.RectTransform, Anchor.Center), style: "TalentBackgroundGlow")
{
CanBeFocused = false,
Color = Color.White * 0.7f,
HoverColor = Color.White,
PressedColor = Color.DarkGray,
SelectedColor = Color.Transparent,
DisabledColor = Color.Transparent
};
Sprite itemSprite = item.InventoryIcon ?? item.sprite;
Color itemColor = itemSprite == item.sprite ? item.SpriteColor : item.InventoryIconColor;
var itemIcon = new GUIImage(new RectTransform(new Vector2(0.8f, 0.8f), innerFrame.RectTransform, Anchor.Center),
@@ -1283,7 +1302,7 @@ namespace Barotrauma
Color = itemColor * 0.9f,
HoverColor = itemColor,
SelectedColor = itemColor,
DisabledColor = itemColor * 0.7f
DisabledColor = itemColor * 0.8f
};
}

View File

@@ -1697,13 +1697,14 @@ namespace Barotrauma
//check missing mission texts
foreach (var missionPrefab in MissionPrefab.List)
{
string nameIdentifier = "missionname." + missionPrefab.Identifier;
string missionId = (missionPrefab.ConfigElement.Attribute("textidentifier") == null ? missionPrefab.Identifier : missionPrefab.ConfigElement.GetAttributeString("textidentifier", string.Empty));
string nameIdentifier = "missionname." + missionId;
if (!tags[language].Contains(nameIdentifier))
{
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<string>(); }
missingTags[nameIdentifier].Add(language);
}
string descriptionIdentifier = "missiondescription." + missionPrefab.Identifier;
string descriptionIdentifier = "missiondescription." + missionId;
if (!tags[language].Contains(descriptionIdentifier))
{
if (!missingTags.ContainsKey(descriptionIdentifier)) { missingTags[descriptionIdentifier] = new HashSet<string>(); }
@@ -1713,6 +1714,7 @@ namespace Barotrauma
foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{
if (sub.Type != SubmarineType.Player) { continue; }
string nameIdentifier = "submarine.name." + sub.Name.ToLowerInvariant();
if (!tags[language].Contains(nameIdentifier))
{
@@ -1729,14 +1731,23 @@ namespace Barotrauma
foreach (AfflictionPrefab affliction in AfflictionPrefab.List)
{
string nameIdentifier = "afflictionname." + affliction.Identifier;
if (affliction.ShowIconThreshold > affliction.MaxStrength &&
affliction.ShowIconToOthersThreshold > affliction.MaxStrength &&
affliction.ShowInHealthScannerThreshold > affliction.MaxStrength)
{
//hidden affliction, no need for localization
continue;
}
string afflictionId = affliction.TranslationOverride ?? affliction.Identifier;
string nameIdentifier = "afflictionname." + afflictionId;
if (!tags[language].Contains(nameIdentifier))
{
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<string>(); }
missingTags[nameIdentifier].Add(language);
}
string descriptionIdentifier = "afflictiondescription." + affliction.Identifier;
string descriptionIdentifier = "afflictiondescription." + afflictionId;
if (!tags[language].Contains(descriptionIdentifier))
{
if (!missingTags.ContainsKey(descriptionIdentifier)) { missingTags[descriptionIdentifier] = new HashSet<string>(); }
@@ -1744,6 +1755,29 @@ namespace Barotrauma
}
}
foreach (var talentTree in TalentTree.JobTalentTrees)
{
foreach (var talentSubTree in talentTree.Value.TalentSubTrees)
{
string nameIdentifier = "talenttree." + talentSubTree.Identifier;
if (!tags[language].Contains(nameIdentifier))
{
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<string>(); }
missingTags[nameIdentifier].Add(language);
}
}
}
foreach (var talent in TalentPrefab.TalentPrefabs)
{
string nameIdentifier = "talentname." + talent.Identifier;
if (!tags[language].Contains(nameIdentifier))
{
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<string>(); }
missingTags[nameIdentifier].Add(language);
}
}
//check missing entity names
foreach (MapEntityPrefab me in MapEntityPrefab.List)
{

View File

@@ -41,11 +41,14 @@ namespace Barotrauma
public SpriteSheet SavingIndicator { get; private set; }
public UISprite UIGlow { get; private set; }
public UISprite TalentGlow { get; private set; }
public UISprite PingCircle { get; private set; }
public UISprite UIGlowCircular { get; private set; }
public UISprite UIGlowSolidCircular { get; private set; }
public UISprite ButtonPulse { get; private set; }
public SpriteSheet FocusIndicator { get; private set; }
@@ -88,6 +91,12 @@ namespace Barotrauma
public Color TextColorDark { get; private set; } = Color.Black * 0.9f;
public Color TextColorDim { get; private set; } = Color.White * 0.6f;
public Color ItemQualityColorPoor { get; private set; } = Color.Gray;
public Color ItemQualityColorNormal { get; private set; } = Color.White;
public Color ItemQualityColorGood { get; private set; } = Color.LightGreen;
public Color ItemQualityColorExcellent { get; private set; } = Color.LightBlue;
public Color ItemQualityColorMasterwork { get; private set; } = Color.MediumPurple;
public Color ColorReputationVeryLow { get; private set; } = Color.Red;
public Color ColorReputationLow { get; private set; } = Color.Orange;
public Color ColorReputationNeutral { get; private set; } = Color.White * 0.8f;
@@ -241,6 +250,9 @@ namespace Barotrauma
case "uiglow":
UIGlow = new UISprite(subElement);
break;
case "talentglow":
TalentGlow = new UISprite(subElement);
break;
case "pingcircle":
PingCircle = new UISprite(subElement);
break;
@@ -253,6 +265,9 @@ namespace Barotrauma
case "uiglowcircular":
UIGlowCircular = new UISprite(subElement);
break;
case "uiglowsolidcircular":
UIGlowSolidCircular = new UISprite(subElement);
break;
case "endroundbuttonpulse":
ButtonPulse = new UISprite(subElement);
break;

View File

@@ -81,7 +81,7 @@ namespace Barotrauma
private readonly object loadMutex = new object();
private float? loadState;
public float? LoadState
{
get
@@ -90,8 +90,8 @@ namespace Barotrauma
{
return loadState;
}
}
set
}
set
{
lock (loadMutex)
{
@@ -141,7 +141,7 @@ namespace Barotrauma
GameMain.Config.EnableSplashScreen = false;
}
}
var titleStyle = GUI.Style?.GetComponentStyle("TitleText");
Sprite titleSprite = null;
if (!WaitForLanguageSelection && titleStyle != null && titleStyle.Sprites.ContainsKey(GUIComponent.ComponentState.None))
@@ -177,8 +177,8 @@ namespace Barotrauma
color: Color.White * noiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale);
titleSprite?.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth * 0.05f, GameMain.GraphicsHeight * 0.125f),
Color.White, origin: new Vector2(0.0f, titleSprite.SourceRect.Height / 2.0f),
titleSprite?.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth * 0.05f, GameMain.GraphicsHeight * 0.125f),
Color.White, origin: new Vector2(0.0f, titleSprite.SourceRect.Height / 2.0f),
scale: GameMain.GraphicsHeight / 2000.0f);
if (WaitForLanguageSelection)
@@ -211,6 +211,13 @@ namespace Barotrauma
if (LoadState != null)
{
loadText += " " + (int)LoadState + " %";
#if DEBUG
if (GameMain.FirstLoad && GameMain.CancelQuickStart)
{
loadText += " (Quickstart aborted)";
}
#endif
}
}
if (GUI.LargeFont != null)
@@ -265,7 +272,7 @@ namespace Barotrauma
decorativeGraph.Draw(spriteBatch, (int)(decorativeGraph.FrameCount * noiseVal),
new Vector2(GameMain.GraphicsWidth * 0.001f, GameMain.GraphicsHeight * 0.24f),
Color.White, Vector2.Zero, 0.0f, decorativeScale, SpriteEffects.FlipVertically);
decorativeMap.Draw(spriteBatch, (int)(decorativeMap.FrameCount * noiseVal),
new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.66f),
Color.White, decorativeMap.FrameSize.ToVector2(), 0.0f, decorativeScale);
@@ -281,9 +288,9 @@ namespace Barotrauma
}
else if (noiseVal < 0.5f)
{
randText =
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
randText =
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0');
}
@@ -299,12 +306,12 @@ namespace Barotrauma
{
if (languageSelectionFont == null)
{
languageSelectionFont = new ScalableFont("Content/Fonts/NotoSans/NotoSans-Bold.ttf",
languageSelectionFont = new ScalableFont("Content/Fonts/NotoSans/NotoSans-Bold.ttf",
(uint)(30 * (GameMain.GraphicsHeight / 1080.0f)), graphicsDevice);
}
if (languageSelectionFontCJK == null)
{
languageSelectionFontCJK = new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf",
languageSelectionFontCJK = new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf",
(uint)(30 * (GameMain.GraphicsHeight / 1080.0f)), graphicsDevice, dynamicLoading: true);
}
if (languageSelectionCursor == null)
@@ -320,11 +327,11 @@ namespace Barotrauma
var font = TextManager.IsCJK(localizedLanguageName) ? languageSelectionFontCJK : languageSelectionFont;
Vector2 textSize = font.MeasureString(localizedLanguageName);
bool hover =
Math.Abs(PlayerInput.MousePosition.X - textPos.X) < textSize.X / 2 &&
bool hover =
Math.Abs(PlayerInput.MousePosition.X - textPos.X) < textSize.X / 2 &&
Math.Abs(PlayerInput.MousePosition.Y - textPos.Y) < textSpacing.Y / 2;
font.DrawString(spriteBatch, localizedLanguageName, textPos - textSize / 2,
font.DrawString(spriteBatch, localizedLanguageName, textPos - textSize / 2,
hover ? Color.White : Color.White * 0.6f);
if (hover && PlayerInput.PrimaryMouseButtonClicked())
{
@@ -394,14 +401,14 @@ namespace Barotrauma
LoadState = null;
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
while (!drawn)
{
yield return CoroutineStatus.Running;
}
CoroutineManager.StartCoroutine(loader);
yield return CoroutineStatus.Running;
while (CoroutineManager.IsCoroutineRunning(loader.ToString()))

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
@@ -18,8 +19,8 @@ namespace Barotrauma
private static UISprite spectateIcon, disconnectedIcon;
private static Sprite ownerIcon, moderatorIcon;
private enum InfoFrameTab { Crew, Mission, Reputation, MyCharacter, Traitor, Submarine, Talents };
private static InfoFrameTab selectedTab;
public enum InfoFrameTab { Crew, Mission, Reputation, MyCharacter, Traitor, Submarine, Talents };
public static InfoFrameTab selectedTab;
private GUIFrame infoFrame, contentFrame;
private readonly List<GUIButton> tabButtons = new List<GUIButton>();
@@ -48,7 +49,7 @@ namespace Barotrauma
private readonly GUIFrame frame;
public LinkedGUI(Client client, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock)
{
{
this.client = client;
this.textBlock = textBlock;
this.frame = frame;
@@ -262,7 +263,11 @@ namespace Barotrauma
var talentsButton = createTabButton(InfoFrameTab.Talents, "tabmenu.talents");
talentsButton.OnAddedToGUIUpdateList += (GUIComponent component) =>
{
talentsButton.Enabled = Character.Controlled?.Info != null && GameMain.GameSession?.Campaign != null;
talentsButton.Enabled = Character.Controlled?.Info != null && (GameMain.GameSession?.Campaign != null || Screen.Selected == GameMain.TestScreen || GameMain.GameSession.GameMode is TestGameMode);
if (!talentsButton.Enabled && selectedTab == InfoFrameTab.Talents)
{
SelectInfoFrameTab(null, InfoFrameTab.Crew);
}
};
}
@@ -417,7 +422,7 @@ namespace Barotrauma
if (GameMain.IsMultiplayer)
{
CreateMultiPlayerList(false);
CreateMultiPlayerLogContent(crewFrame);
CreateMultiPlayerLogContent(crewFrame);
}
else
{
@@ -608,7 +613,7 @@ namespace Barotrauma
AbsoluteSpacing = 2
};
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center),
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center),
onDraw: (sb, component) => DrawNotInGameIcon(sb, component.Rect, client))
{
CanBeFocused = false,
@@ -837,7 +842,7 @@ namespace Barotrauma
}
private static readonly List<Pair<string, PlayerConnectionChangeType>> storedMessages = new List<Pair<string, PlayerConnectionChangeType>>();
public static void StorePlayerConnectionChangeMessage(ChatMessage message)
{
if (!GameMain.GameSession?.IsRunning ?? true) { return; }
@@ -850,7 +855,7 @@ namespace Barotrauma
TabMenu instance = GameSession.TabMenuInstance;
instance.AddLineToLog(msg, message.ChangeType);
instance.RemoveCurrentElements();
instance.CreateMultiPlayerList(true);
instance.CreateMultiPlayerList(true);
}
}
@@ -969,7 +974,7 @@ namespace Barotrauma
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(iconSize + spacing, 0) }, false, childAnchor: Anchor.TopLeft)
{
AbsoluteSpacing = spacing
AbsoluteSpacing = spacing
};
string descriptionText = mission.Description;
foreach (string missionMessage in mission.ShownMessages)
@@ -997,7 +1002,7 @@ namespace Barotrauma
float ySize = missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y + missionReputationSize.Y + missionTextGroup.AbsoluteSpacing * 4;
bool displayDifficulty = mission.Difficulty.HasValue;
if (displayDifficulty) { ySize += missionRewardSize.Y; }
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)ySize);
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
@@ -1008,8 +1013,8 @@ namespace Barotrauma
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight);*/
new GUIImage(new RectTransform(new Point(iconSize), missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
{
new GUIImage(new RectTransform(new Point(iconSize), missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
{
Color = mission.Prefab.IconColor,
HoverColor = mission.Prefab.IconColor,
SelectedColor = mission.Prefab.IconColor,
@@ -1174,19 +1179,38 @@ namespace Barotrauma
private Color unselectableColor = new Color(100, 100, 100, 225);
private Color pressedColor = new Color(60, 60, 60, 225);
private readonly List<(GUIButton button, GUIImage background, GUIImage icon)> talentButtons = new List<(GUIButton button, GUIImage background, GUIImage icon)>();
private readonly List<(GUIButton button, GUIComponent icon, GUIImage glow)> talentButtons = new List<(GUIButton button, GUIComponent icon, GUIImage glow)>();
private readonly List<(string talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)> talentCornerIcons = new List<(string talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)>();
private List<string> selectedTalents = new List<string>();
private GUITextBlock talentTitleText;
private GUITextBlock talentDescriptionText;
private GUITextBlock talentPointsText;
private GUITextBlock experienceText;
private GUIProgressBar experienceBar;
private GUITextBlock talentPointText;
private GUIListBox skillListBox;
private readonly ImmutableDictionary<TalentTree.TalentTreeStageState, GUIComponentStyle> talentStageStyles = new Dictionary<TalentTree.TalentTreeStageState, GUIComponentStyle>
{
{ TalentTree.TalentTreeStageState.Invalid, GUI.Style.GetComponentStyle("TalentTreeLocked") },
{ TalentTree.TalentTreeStageState.Locked, GUI.Style.GetComponentStyle("TalentTreeLocked") },
{ TalentTree.TalentTreeStageState.Unlocked, GUI.Style.GetComponentStyle("TalentTreePurchased") },
{ TalentTree.TalentTreeStageState.Available, GUI.Style.GetComponentStyle("TalentTreeUnlocked") },
{ TalentTree.TalentTreeStageState.Highlighted, GUI.Style.GetComponentStyle("TalentTreeAvailable") },
}.ToImmutableDictionary();
private readonly ImmutableDictionary<TalentTree.TalentTreeStageState, Color> talentStageBackgroundColors = new Dictionary<TalentTree.TalentTreeStageState, Color>
{
{ TalentTree.TalentTreeStageState.Invalid, new Color(48,48,48,255) },
{ TalentTree.TalentTreeStageState.Locked, new Color(48,48,48,255) },
{ TalentTree.TalentTreeStageState.Unlocked, new Color(24,37,31,255) },
{ TalentTree.TalentTreeStageState.Available, new Color(50,47,33,255) },
{ TalentTree.TalentTreeStageState.Highlighted, new Color(50,47,33,255) },
}.ToImmutableDictionary();
private void CreateTalentInfo(GUIFrame infoFrame)
{
infoFrame.ClearChildren();
talentButtons.Clear();
talentCornerIcons.Clear();
Character controlledCharacter = Character.Controlled;
if (controlledCharacter == null) { return; }
@@ -1195,6 +1219,8 @@ namespace Barotrauma
int padding = GUI.IntScale(15);
GUIFrame talentFrameContent = new GUIFrame(new RectTransform(new Point(talentFrameBackground.Rect.Width - padding, talentFrameBackground.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
GUIFrame paddedTalentFrame = new GUIFrame(new RectTransform(new Vector2(0.9f), talentFrameContent.RectTransform, Anchor.Center), style: null);
if (controlledCharacter.Info == null)
{
DebugConsole.ThrowError("No character info found for talent UI");
@@ -1203,87 +1229,103 @@ namespace Barotrauma
selectedTalents = controlledCharacter.Info.UnlockedTalents.ToList();
GUILayoutGroup talentFrameLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), talentFrameContent.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
GUILayoutGroup talentFrameLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), paddedTalentFrame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = GUI.IntScale(5)
};
GUILayoutGroup talentInfoLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.325f), talentFrameLayoutGroup.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter);
GUILayoutGroup talentInfoLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), talentFrameLayoutGroup.RectTransform, Anchor.Center), isHorizontal: true);
GUIFrame talentTitleFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.25f), talentInfoLayoutGroup.RectTransform, Anchor.TopCenter), style: null);
CharacterInfo info = controlledCharacter.Info;
Job job = info.Job;
talentTitleText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), talentTitleFrame.RectTransform, Anchor.TopLeft), "", font: GUI.LargeFont);
talentPointsText = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1.0f), talentTitleFrame.RectTransform, Anchor.TopRight), "", font: GUI.Font, textAlignment: Alignment.Center);
GUIFrame talentDescriptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.4f), talentInfoLayoutGroup.RectTransform, Anchor.TopCenter), style: null);
talentDescriptionText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), talentDescriptionFrame.RectTransform, Anchor.TopLeft), "", font: GUI.Font, textAlignment: Alignment.TopLeft, wrap: true);
GUIFrame characterInfoFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.3f), talentInfoLayoutGroup.RectTransform, Anchor.TopLeft), style: null);
GUILayoutGroup characterInfoColumn = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), characterInfoFrame.RectTransform, anchor: Anchor.TopLeft), childAnchor: Anchor.TopLeft, isHorizontal: true);
// move to a different tab menu
if (GameSettings.VerboseLogging)
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), talentInfoLayoutGroup.RectTransform), onDraw: (batch, component) =>
{
CreateCharacterSheet(characterInfoColumn);
}
float posY = component.Rect.Bottom - component.Rect.Width;
info.DrawPortrait(batch, new Vector2(component.Rect.X, posY), Vector2.Zero, component.Rect.Width, false, false);
});
GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.375f, 1f), talentInfoLayoutGroup.RectTransform));
Vector2 nameSize = GUI.SubHeadingFont.MeasureString(info.Name);
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), info.Name, font: GUI.SubHeadingFont) { TextColor = job.Prefab.UIColor };
nameBlock.RectTransform.NonScaledSize = nameSize.Pad(nameBlock.Padding).ToPoint();
Vector2 jobSize = GUI.SmallFont.MeasureString(job.Name);
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), job.Name, font: GUI.SmallFont) { TextColor = job.Prefab.UIColor };
jobBlock.RectTransform.NonScaledSize = jobSize.Pad(jobBlock.Padding).ToPoint();
string traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + info.PersonalityTrait.Name.Replace(" ", "")));
Vector2 traitSize = GUI.SmallFont.MeasureString(traitString);
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUI.SmallFont);
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
GUILayoutGroup skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.375f, 1f), talentInfoLayoutGroup.RectTransform)) { Stretch = true };
string skillString = TextManager.Get("skills");
Vector2 skillSize = GUI.SubHeadingFont.MeasureString(skillString);
GUITextBlock skillBlock = new GUITextBlock(new RectTransform(Vector2.One, skillLayout.RectTransform), skillString, font: GUI.SubHeadingFont);
skillBlock.RectTransform.NonScaledSize = skillSize.Pad(skillBlock.Padding).ToPoint();
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
CreateTalentSkillList(controlledCharacter, skillListBox);
if (!TalentTree.JobTalentTrees.TryGetValue(controlledCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.6f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
new GUIFrame(new RectTransform(new Vector2(1f, 1f), talentFrameLayoutGroup.RectTransform), style: "HorizontalLine");
int spacing = GUI.IntScale(5);
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
foreach (var subTree in talentTree.TalentSubTrees)
{
GUIFrame subTreeFrame = new GUIFrame(new RectTransform(new Vector2(0.333f, 1f), talentTreeListBox.Content.RectTransform, anchor: Anchor.TopLeft), style: null);
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), subTreeFrame.RectTransform, Anchor.Center), false, childAnchor: Anchor.TopCenter);
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.111f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: "SubtreeHeader");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), subtreeTitleFrame.RectTransform, anchor: Anchor.TopCenter), subTree.Identifier, font: GUI.LargeFont, textAlignment: Alignment.Center);
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.111f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
int elementPadding = GUI.IntScale(8);
Point headerSize = subtreeTitleFrame.RectTransform.NonScaledSize;
GUIFrame subTreeTitleBackground = new GUIFrame(new RectTransform(new Point(headerSize.X - elementPadding, headerSize.Y), subtreeTitleFrame.RectTransform, anchor: Anchor.Center), style: "SubtreeHeader");
new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUI.LargeFont, textAlignment: Alignment.Center);
for (int i = 0; i < 4; i++)
{
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.222f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: "TalentOptionFrame");
GUIImage talentBackground = new GUIImage(new RectTransform(Vector2.One, talentOptionFrame.RectTransform, anchor: Anchor.Center), style: "TalentBackground")
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.222f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
GUIFrame talentBackground = new GUIFrame(new RectTransform(new Point(talentFrameSize.X - elementPadding, talentFrameSize.Y - elementPadding), talentOptionFrame.RectTransform, anchor: Anchor.Center), style: "TalentBackground");
GUIFrame talentBackgroundHighlight = new GUIFrame(new RectTransform(Vector2.One, talentBackground.RectTransform, anchor: Anchor.Center), style: "TalentBackgroundGlow") { Visible = false };
GUIImage cornerIcon = new GUIImage(new RectTransform(new Vector2(0.25f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight), style: null)
{
CanBeFocused = false,
Color = unselectableColor,
CanBeFocused = false
};
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
if (subTree.TalentOptionStages.Count > i)
{
TalentOption talentOption = subTree.TalentOptionStages[i];
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), talentOptionFrame.RectTransform, Anchor.CenterLeft), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
};
GUILayoutGroup talentOptionCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.7f), talentOptionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, talentOptionCenterGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
foreach (TalentPrefab talent in talentOption.Talents)
{
int optionPadding = GUI.IntScale(10);
GUIFrame talentFrame = new GUIFrame(new RectTransform(new Point(talentOptionFrame.Rect.Width, talentOptionFrame.Rect.Height - optionPadding), talentOptionLayoutGroup.RectTransform), style: null)
GUIFrame talentFrame = new GUIFrame(new RectTransform(Vector2.One, talentOptionLayoutGroup.RectTransform), style: null)
{
CanBeFocused = false,
};
new GUIImage(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center), style: "TalentFrameBackground")
{
CanBeFocused = false,
};
GUIImage iconImage = null;
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center), style: "TalentFrame")
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center), style: null)
{
ToolTip = $"{talent.DisplayName}\n\n{talent.Description}",
UserData = talent.Identifier,
PressedColor = pressedColor,
OnClicked = (button, userData) =>
{
talentTitleText.Text = talent.DisplayName;
talentDescriptionText.Text = talent.Description;
// deselect other buttons in tier by removing their selected talents from pool
foreach (GUIButton guiButton in talentOptionLayoutGroup.GetAllChildren<GUIButton>())
{
@@ -1314,61 +1356,129 @@ namespace Barotrauma
},
};
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = Color.Transparent;
int iconPadding = GUI.IntScale(15);
iconImage = new GUIImage(new RectTransform(new Point(talentFrame.Rect.Width - iconPadding, talentFrame.Rect.Height - iconPadding), talentFrame.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
GUIComponent iconImage;
if (talent.Icon is null)
{
PressedColor = unselectableColor,
CanBeFocused = false,
};
iconImage = new GUITextBlock(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), text: "???", font: GUI.LargeFont, textAlignment: Alignment.Center, style: null)
{
OutlineColor = GUI.Style.Red,
TextColor = GUI.Style.Red,
PressedColor = unselectableColor,
CanBeFocused = false,
};
}
else
{
iconImage = new GUIImage(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
{
PressedColor = unselectableColor,
CanBeFocused = false,
};
}
talentButtons.Add((talentButton, talentBackground, iconImage));
GUIImage iconGlow = new GUIImage(new RectTransform(Vector2.One, iconImage.RectTransform, anchor: Anchor.Center), sprite: GUI.Style.TalentGlow.Sprite, scaleToFit: true) { Visible = false };
talentButtons.Add((talentButton, iconImage, iconGlow));
}
talentCornerIcons.Add((subTree.Identifier, i, cornerIcon, talentBackground, talentBackgroundHighlight));
}
}
}
GUIFrame talentBottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), style: null);
GUILayoutGroup talentBottomFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true) { RelativeSpacing = 0.01f };
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(0.775f, 0.75f), talentBottomFrame.RectTransform, Anchor.TopCenter), style: null);
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.775f, 1f), talentBottomFrame.RectTransform));
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
barSize: controlledCharacter.Info.GetProgressTowardsNextLevel(), color: Color.White, style: "ExperienceBar")
barSize: controlledCharacter.Info.GetProgressTowardsNextLevel(), color: GUI.Style.Green)
{
IsHorizontal = true
};
GUIImage experienceTextBackground = new GUIImage(new RectTransform(new Vector2(0.2f, 0.475f), experienceBarFrame.RectTransform, anchor: Anchor.Center), style: "ExperienceTextBackground");
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUI.Font, textAlignment: Alignment.CenterRight);
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceTextBackground.RectTransform, anchor: Anchor.Center), "", font: GUI.Font, textColor: Color.White, textAlignment: Alignment.Center);
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUI.SubHeadingFont, parseRichText: true, textAlignment: Alignment.CenterRight);
new GUIButton(new RectTransform(new Vector2(0.1f, 0.3f), talentBottomFrame.RectTransform, anchor: Anchor.TopRight), text: TextManager.Get("applysettingsbutton"))
{
OnClicked = ApplyTalentSelection,
};
new GUIButton(new RectTransform(new Vector2(0.1f, 0.3f), talentBottomFrame.RectTransform, anchor: Anchor.TopLeft), text: TextManager.Get("reset"))
new GUIButton(new RectTransform(new Vector2(0.1f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("reset"), style: "GUICharacterInfoButton")
{
OnClicked = ResetTalentSelection,
};
new GUIButton(new RectTransform(new Vector2(0.1f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUICharacterInfoButton")
{
OnClicked = ApplyTalentSelection,
};
UpdateTalentButtons();
}
private void CreateTalentSkillList(Character character, GUIListBox parent)
{
parent.Content.ClearChildren();
foreach (Skill skill in character.Info.Job.Skills)
{
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(0.7f, 1f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}", returnNull: true) ?? skill.Identifier);
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), Math.Floor(skill.Level).ToString("F0"), textAlignment: Alignment.CenterRight) { Padding = new Vector4(0, 0, 4, 0) };
float modifiedSkillLevel = character.GetSkillLevel(skill.Identifier);
if (!MathUtils.NearlyEqual(MathF.Floor(modifiedSkillLevel), MathF.Floor(skill.Level)))
{
int skillChange = (int)MathF.Floor(modifiedSkillLevel - skill.Level);
string stringColor = true switch
{
true when skillChange > 0 => XMLExtensions.ColorToString(GUI.Style.Green),
true when skillChange < 0 => XMLExtensions.ColorToString(GUI.Style.Red),
_ => XMLExtensions.ColorToString(GUI.Style.TextColor)
};
string changeText = $"(‖color:{stringColor}‖{(skillChange > 0 ? "+" : string.Empty) + skillChange}‖color:end‖)";
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), changeText, parseRichText: true) { Padding = Vector4.Zero };
}
skillContainer.Recalculate();
}
parent.RecalculateChildren();
}
private void UpdateTalentButtons()
{
Character controlledCharacter = Character.Controlled;
talentPointsText.Text = $"{TextManager.Get("talentpointsleft")}{controlledCharacter.Info.GetAvailableTalentPoints()}";
experienceText.Text = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}";
experienceBar.BarSize = controlledCharacter.Info.GetProgressTowardsNextLevel();
//experienceBar.ToolTip = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}";
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
foreach (var talentButton in talentButtons)
string pointsLeft = controlledCharacter.Info.GetAvailableTalentPoints().ToString();
int talentCount = selectedTalents.Count - controlledCharacter.Info.UnlockedTalents.Count;
if (talentCount > 0)
{
talentButton.background.Color = unselectableColor;
string pointsUsed = $"‖color:{XMLExtensions.ColorToString(GUI.Style.Red)}‖{-talentCount}‖color:end‖";
string localizedString = TextManager.GetWithVariables("talentmenu.points.spending", new []{ "[amount]", "[used]" }, new []{ pointsLeft, pointsUsed});
talentPointText.SetRichText(localizedString);
}
else
{
talentPointText.SetRichText(TextManager.GetWithVariable("talentmenu.points", "[amount]", pointsLeft));
}
foreach (var (talentTree, index, icon, frame, glow) in talentCornerIcons)
{
TalentTree.TalentTreeStageState state = TalentTree.GetTalentOptionStageState(controlledCharacter, talentTree, index, selectedTalents);
GUIComponentStyle newStyle = talentStageStyles[state];
icon.ApplyStyle(newStyle);
icon.Color = newStyle.Color;
frame.Color = talentStageBackgroundColors[state];
glow.Visible = state == TalentTree.TalentTreeStageState.Highlighted;
}
foreach (var talentButton in talentButtons)
@@ -1377,30 +1487,23 @@ namespace Barotrauma
bool unselectable = !TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents) || controlledCharacter.HasTalent(talentIdentifier);
Color newTalentColor = unselectable ? unselectableColor : unselectedColor;
talentButton.glow.Visible = false;
if (controlledCharacter.HasTalent(talentIdentifier))
{
newTalentColor = ownedColor;
newTalentColor = new Color(140,225,140,255);
}
else if (selectedTalents.Contains(talentIdentifier))
{
newTalentColor = selectedColor;
newTalentColor = new Color(174,164,124,255);
talentButton.glow.Visible = true;
}
talentButton.button.Color = newTalentColor;
talentButton.button.SelectedColor = newTalentColor;
talentButton.button.HoverColor = newTalentColor;
talentButton.button.DisabledColor = newTalentColor;
talentButton.icon.Color = newTalentColor;
// update background color as well, if not defined yet
if (talentButton.background.Color == unselectableColor)
{
talentButton.background.Color = newTalentColor;
}
}
}
CreateTalentSkillList(controlledCharacter, skillListBox);
}
private void ApplyTalents(Character controlledCharacter)
{

View File

@@ -194,6 +194,8 @@ namespace Barotrauma
#if DEBUG
public static bool FirstLoad = true;
public static bool CancelQuickStart;
#endif
public GameMain(string[] args)
@@ -309,7 +311,7 @@ namespace Barotrauma
GraphicsDeviceManager.PreferredBackBufferWidth = GraphicsWidth;
GraphicsDeviceManager.PreferredBackBufferHeight = GraphicsHeight;
GraphicsDeviceManager.ApplyChanges();
if (windowMode == WindowMode.BorderlessWindowed)
@@ -588,7 +590,7 @@ namespace Barotrauma
StructurePrefab.LoadAll(GetFilesOfType(ContentType.Structure));
TitleScreen.LoadState = 55.0f;
yield return CoroutineStatus.Running;
UpgradePrefab.LoadAll(GetFilesOfType(ContentType.UpgradeModules));
TitleScreen.LoadState = 56.0f;
yield return CoroutineStatus.Running;
@@ -601,7 +603,7 @@ namespace Barotrauma
ItemAssemblyPrefab.LoadAll();
TitleScreen.LoadState = 60.0f;
yield return CoroutineStatus.Running;
GameModePreset.Init();
SaveUtil.DeleteDownloadedSubs();
@@ -654,7 +656,7 @@ namespace Barotrauma
ParticleManager.LoadPrefabs();
TitleScreen.LoadState = 88.0f;
LevelObjectPrefab.LoadAll();
TitleScreen.LoadState = 90.0f;
yield return CoroutineStatus.Running;
@@ -804,7 +806,9 @@ namespace Barotrauma
}
#if DEBUG
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled || Config.TestScreenEnabled) && FirstLoad && !PlayerInput.KeyDown(Keys.LeftShift))
CancelQuickStart |= PlayerInput.KeyDown(Keys.LeftShift);
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled || Config.TestScreenEnabled) && FirstLoad && !CancelQuickStart)
{
loadingScreenOpen = false;
FirstLoad = false;
@@ -812,7 +816,7 @@ namespace Barotrauma
if (Config.TestScreenEnabled)
{
TestScreen.Select();
}
}
else if (Config.AutomaticQuickStartEnabled)
{
MainMenuScreen.QuickStart();
@@ -930,8 +934,8 @@ namespace Barotrauma
static bool itemHudActive()
{
if (Character.Controlled?.SelectedConstruction == null) { return false; }
return
Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) ||
return
Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) ||
((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false);
}
}
@@ -1095,7 +1099,7 @@ namespace Barotrauma
if (save)
{
GUI.SetSavingIndicatorState(true);
if (GameSession.Submarine != null && !GameSession.Submarine.Removed)
{
GameSession.SubmarineInfo = new SubmarineInfo(GameSession.Submarine);
@@ -1267,7 +1271,7 @@ namespace Barotrauma
string text = TextManager.GetWithVariable("openlinkinbrowserprompt", "[link]", url);
string extensionText = TextManager.Get(promptExtensionTag, returnNull: true, useEnglishAsFallBack: false);
if (!string.IsNullOrEmpty(extensionText))
{
{
text += $"\n\n{extensionText}";
}

View File

@@ -208,7 +208,7 @@ namespace Barotrauma
ReportButtonFrame.RectTransform.AbsoluteOffset = new Point(0, -chatBox.ToggleButton.Rect.Height);
CreateReports(this, ReportButtonFrame, reports, false);
CreateReportButtons(this, ReportButtonFrame, reports, false);
#endregion
@@ -218,7 +218,7 @@ namespace Barotrauma
dismissedOrderPrefab ??= Order.GetPrefab("dismissed");
}
public static void CreateReports(CrewManager crewManager, GUIComponent parent, List<Order> reports, bool isHorizontal)
public static void CreateReportButtons(CrewManager crewManager, GUIComponent parent, List<Order> reports, bool isHorizontal)
{
//report buttons
foreach (Order order in reports)
@@ -228,22 +228,21 @@ namespace Barotrauma
{
OnClicked = (button, userData) =>
{
if (!CanIssueOrders) { return false; }
if (!CanIssueOrders || crewManager?.DraggedOrder != null) { return false; }
var sub = Character.Controlled.Submarine;
if (sub == null || sub.TeamID != Character.Controlled.TeamID || sub.Info.IsWreck) { return false; }
if (crewManager != null)
{
crewManager.SetCharacterOrder(null, order, null, CharacterInfo.HighestManualOrderPriority, Character.Controlled);
if (crewManager.IsSinglePlayer) { HumanAIController.ReportProblem(Character.Controlled, order); }
}
return true;
},
UserData = order,
ToolTip = order.Name,
ClampMouseRectToParent = false
};
btn.ToolTip = $"‖color:{XMLExtensions.ColorToString(order.Prefab.Color)}‖{order.Name}‖color:end‖\n{TextManager.Get("draganddropreports")}";
if (crewManager != null)
{
@@ -272,8 +271,9 @@ namespace Barotrauma
{
Color = order.Color,
HoverColor = Color.Lerp(order.Color, Color.White, 0.5f),
ToolTip = order.Name,
SpriteEffects = SpriteEffects.FlipHorizontally
ToolTip = btn.RawToolTip,
SpriteEffects = SpriteEffects.FlipHorizontally,
UserData = order
};
}
}
@@ -717,7 +717,7 @@ namespace Barotrauma
hull ??= orderGiver.CurrentHull;
AddOrder(new Order(order.Prefab ?? order, hull, null, orderGiver), order.FadeOutTime);
}
else if(order.IsIgnoreOrder)
else if (order.IsIgnoreOrder)
{
WallSection ws = null;
if (order.TargetType == Order.OrderTargetType.Entity && order.TargetEntity is IIgnorable ignorable)

View File

@@ -1232,7 +1232,7 @@ namespace Barotrauma
highlightedQuickUseSlot = visualSlots[i];
}
if (!slots[i].First().AllowedSlots.Any(a => a == InvSlotType.Any) || SlotTypes[i] == InvSlotType.HealthInterface)
if (slots[i].First().AllowedSlots.Count() == 1 || SlotTypes[i] == InvSlotType.HealthInterface)
{
continue;
}

View File

@@ -8,6 +8,7 @@ using Barotrauma.IO;
using System.Text;
using System.Xml.Linq;
using Barotrauma.Sounds;
using System.Linq;
namespace Barotrauma.Items.Components
{
@@ -145,7 +146,9 @@ namespace Barotrauma.Items.Components
if (character == null || !character.IsKeyDown(InputType.Aim)) { return; }
//camera focused on some other item/device, don't draw the crosshair
if (character.ViewTarget != null && (character.ViewTarget is Item item) && item.Prefab.FocusOnSelected) { return; }
if (character.ViewTarget != null && (character.ViewTarget is Item viewTargetItem) && viewTargetItem.Prefab.FocusOnSelected) { return; }
//don't draw the crosshair if the item is in some other type of equip slot than hands (e.g. assault rifle in the bag slot)
if (!character.HeldItems.Contains(item)) { return; }
GUI.HideCursor = (crosshairSprite != null || crosshairPointerSprite != null) &&
GUI.MouseOn == null && !Inventory.IsMouseOnInventory && !GameMain.Instance.Paused;

View File

@@ -214,7 +214,7 @@ namespace Barotrauma.Items.Components
if (UILabel == string.Empty) { return string.Empty; }
if (UILabel != null)
{
return TextManager.Get("UILabel." + UILabel);
return TextManager.Get("UILabel." + UILabel, returnNull: true) ?? TextManager.Get(UILabel);
}
else
{

View File

@@ -37,7 +37,7 @@ namespace Barotrauma.Items.Components
private FabricationRecipe pendingFabricatedItem;
private Pair<Rectangle, string> tooltip;
private (Rectangle area, string text)? tooltip;
private GUITextBlock requiredTimeBlock;
@@ -270,7 +270,7 @@ namespace Barotrauma.Items.Components
});
var sufficientSkillsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
TextManager.Get("fabricatorsufficientskills", returnNull: true) ?? "Sufficient skills to fabricate", textColor: GUI.Style.Green, font: GUI.SubHeadingFont)
TextManager.Get("fabricatorsufficientskills"), textColor: GUI.Style.Green, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
CanBeFocused = false
@@ -278,7 +278,7 @@ namespace Barotrauma.Items.Components
sufficientSkillsText.RectTransform.SetAsFirstChild();
var insufficientSkillsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
TextManager.Get("fabricatorinsufficientskills", returnNull: true) ?? "Insufficient skills to fabricate", textColor: Color.Orange, font: GUI.SubHeadingFont)
TextManager.Get("fabricatorinsufficientskills"), textColor: Color.Orange, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
CanBeFocused = false
@@ -290,7 +290,7 @@ namespace Barotrauma.Items.Components
}
var requiresRecipeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
TextManager.Get("fabricatorrequiresrecipe", returnNull: true) ?? "Requires recipe to fabricate", textColor: Color.Red, font: GUI.SubHeadingFont)
TextManager.Get("fabricatorrequiresrecipe"), textColor: Color.Red, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
CanBeFocused = false
@@ -403,7 +403,7 @@ namespace Barotrauma.Items.Components
{
toolTipText += '\n' + requiredItem.ItemPrefabs.First().Description;
}
tooltip = new Pair<Rectangle, string>(slotRect, toolTipText);
tooltip = (slotRect, toolTipText);
}
slotIndex++;
@@ -443,7 +443,7 @@ namespace Barotrauma.Items.Components
if (tooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
GUIComponent.DrawToolTip(spriteBatch, tooltip.Value.text, tooltip.Value.area);
tooltip = null;
}
}
@@ -463,6 +463,22 @@ namespace Barotrauma.Items.Components
if (recipe?.DisplayName == null) { continue; }
child.Visible = recipe.DisplayName.ToLower().Contains(filter);
}
//go through the elements backwards, and disable the labels ("insufficient skills to fabricate", "recipe required...") if there's no items below them
bool recipeVisible = false;
foreach (GUIComponent child in itemList.Content.Children.Reverse())
{
if (!(child.UserData is FabricationRecipe recipe))
{
child.Visible = recipeVisible;
recipeVisible = false;
}
else
{
recipeVisible = child.Visible;
}
}
itemList.UpdateScrollBarSize();
itemList.BarScroll = 0.0f;
@@ -498,9 +514,20 @@ namespace Barotrauma.Items.Components
};
}*/
string name = GetRecipeNameAndAmount(selectedItem);
float quality = 0;
foreach (string tag in selectedItem.TargetItem.Tags)
{
quality += user?.Info?.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag) ?? 0;
}
if (quality > 0)
{
name = TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", name+'\n', fallBackTag: "itemname.quality3");
}
var nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
GetRecipeNameAndAmount(selectedItem), textAlignment: Alignment.CenterLeft, textColor: Color.Aqua, font: GUI.SubHeadingFont)
name, textAlignment: Alignment.CenterLeft, textColor: Color.Aqua, font: GUI.SubHeadingFont, parseRichText: true)
{
AutoScaleHorizontal = true
};

View File

@@ -218,8 +218,8 @@ namespace Barotrauma.Items.Components
DefaultNeutralColor = MiniMapBaseColor * 0.8f,
HoverColor = Color.White,
BlueprintBlue = new Color(23, 38, 33),
HullWaterColor = new Color(17, 173, 179),
HullWaterLineColor = Color.LightBlue,
HullWaterColor = new Color(17, 173, 179) * 0.5f,
HullWaterLineColor = Color.LightBlue * 0.5f,
NoPowerColor = MiniMapBaseColor * 0.1f,
ElectricalBaseColor = GUI.Style.Orange,
NoPowerElectricalColor = ElectricalBaseColor * 0.1f;
@@ -307,10 +307,13 @@ namespace Barotrauma.Items.Components
if (reports.Any())
{
CrewManager.CreateReports(GameMain.GameSession?.CrewManager, reportFrame, reports, true);
CrewManager.CreateReportButtons(GameMain.GameSession?.CrewManager, reportFrame, reports, true);
}
searchBarFrame = new GUILayoutGroup(new RectTransform(new Vector2(1), bottomFrame.RectTransform), isHorizontal: true, childAnchor: Anchor.Center);
searchBarFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.5f, 1.0f), bottomFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.Center)
{
Visible = false
};
searchBar = new GUITextBox(new RectTransform(new Vector2(1), searchBarFrame.RectTransform), string.Empty, createClearButton: true, createPenIcon: true)
{
OnEnterPressed = (box, text) =>
@@ -345,6 +348,7 @@ namespace Barotrauma.Items.Components
foreach (ItemPrefab prefab in ItemPrefab.Prefabs.OrderBy(prefab => prefab.Name))
{
if (prefab.HideInMenus) { continue; }
CreateItemFrame(prefab, listBox.Content.RectTransform);
}
@@ -355,7 +359,10 @@ namespace Barotrauma.Items.Components
searchBar.OnSelected += (sender, key) =>
{
itemsFoundOnSub = Item.ItemList.Where(it => it.Submarine == item.Submarine && !it.NonInteractable && !it.HiddenInGame && it.Components.OfType<Holdable>().Any()).Select(it => it.Prefab).ToImmutableHashSet();
itemsFoundOnSub = Item.ItemList.Where(it =>
it.Submarine == item.Submarine &&
!it.NonInteractable && !it.HiddenInGame &&
(it.GetComponent<Holdable>() != null || it.GetComponent<Wearable>() != null)).Select(it => it.Prefab).ToImmutableHashSet();
};
searchBar.OnKeyHit += ControlSearchTooltip;
@@ -487,21 +494,24 @@ namespace Barotrauma.Items.Components
CreateHUD();
}
if (PlayerInput.PrimaryMouseButtonDown() && currentMode != MiniMapMode.HullStatus)
if (scissorComponent != null)
{
if (GUI.MouseOn == scissorComponent || scissorComponent.IsParentOf(GUI.MouseOn))
if (PlayerInput.PrimaryMouseButtonDown() && currentMode != MiniMapMode.HullStatus)
{
dragMapStart = PlayerInput.MousePosition;
if (GUI.MouseOn == scissorComponent || scissorComponent.IsParentOf(GUI.MouseOn))
{
dragMapStart = PlayerInput.MousePosition;
}
}
}
if (currentMode != MiniMapMode.HullStatus && Math.Abs(PlayerInput.ScrollWheelSpeed) > 0 && (GUI.MouseOn == scissorComponent || scissorComponent.IsParentOf(GUI.MouseOn)))
{
float newZoom = Math.Clamp(Zoom + PlayerInput.ScrollWheelSpeed / 1000.0f * Zoom, minZoom, maxZoom);
float distanceScale = newZoom / Zoom;
mapOffset *= distanceScale;
recalculate |= !MathUtils.NearlyEqual(Zoom, newZoom);
Zoom = newZoom;
if (currentMode != MiniMapMode.HullStatus && Math.Abs(PlayerInput.ScrollWheelSpeed) > 0 && (GUI.MouseOn == scissorComponent || scissorComponent.IsParentOf(GUI.MouseOn)))
{
float newZoom = Math.Clamp(Zoom + PlayerInput.ScrollWheelSpeed / 1000.0f * Zoom, minZoom, maxZoom);
float distanceScale = newZoom / Zoom;
mapOffset *= distanceScale;
recalculate |= !MathUtils.NearlyEqual(Zoom, newZoom);
Zoom = newZoom;
}
}
if (dragMapStart is { } dragStart)
@@ -524,15 +534,13 @@ namespace Barotrauma.Items.Components
if (recalculate)
{
miniMapContainer.RectTransform.LocalScale = new Vector2(Zoom);
miniMapContainer.RectTransform.RecalculateChildren(true, true);
miniMapContainer.RectTransform.AbsoluteOffset = mapOffset.ToPoint();
if (miniMapContainer != null)
{
miniMapContainer.RectTransform.LocalScale = new Vector2(Zoom);
miniMapContainer.RectTransform.RecalculateChildren(true, true);
miniMapContainer.RectTransform.AbsoluteOffset = mapOffset.ToPoint();
}
recalculate = false;
// var (maxWidth, maxHeight) = miniMapContainer.Rect.Size.ToVector2() / 2f;
//
// mapOffset.X = Math.Clamp(mapOffset.X, -maxWidth, maxWidth);
// mapOffset.Y = Math.Clamp(mapOffset.Y, -maxHeight, maxHeight);
}
// is there a better way to do this?
@@ -606,17 +614,6 @@ namespace Barotrauma.Items.Components
private void DrawHUDFront(SpriteBatch spriteBatch, GUICustomComponent container)
{
// TODO remove
#warning remove
if (currentMode == MiniMapMode.HullCondition)
{
const string wipText = "work in progress";
Vector2 textSize = GUI.LargeFont.MeasureString(wipText);
Vector2 textPos = GuiFrame.Rect.Center.ToVector2();
GUI.DrawString(spriteBatch, textPos - textSize / 2, wipText.ToUpper(), GUI.Style.Orange, Color.Black * 0.8f, backgroundPadding: 8, font: GUI.LargeFont);
}
if (Voltage < MinVoltage)
{
Vector2 textSize = GUI.Font.MeasureString(noPowerTip);
@@ -627,18 +624,45 @@ namespace Barotrauma.Items.Components
return;
}
if (currentMode == MiniMapMode.HullStatus)
if (currentMode == MiniMapMode.HullStatus || currentMode == MiniMapMode.HullCondition)
{
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
spriteBatch.GraphicsDevice.ScissorRectangle = submarineContainer.Rect;
foreach (var (entity, component) in hullStatusComponents)
if (currentMode == MiniMapMode.HullCondition && item.Submarine != null)
{
if (!(entity is Hull hull)) { continue; }
if (!hullDatas.TryGetValue(hull, out HullData? hullData) || hullData is null) { continue; }
DrawHullCards(spriteBatch, hull, hullData, component.RectComponent);
var sprite = GUI.Style.UIGlowSolidCircular?.Sprite;
float alpha = (MathF.Sin(blipState / maxBlipState * MathHelper.TwoPi) + 1.5f) * 0.5f;
if (sprite != null)
{
Vector2 spriteSize = sprite.size;
Rectangle worldBorders = item.Submarine.GetDockedBorders();
worldBorders.Location += item.Submarine.WorldPosition.ToPoint();
foreach (Gap gap in Gap.GapList)
{
if (gap.IsRoomToRoom || gap.Submarine != item.Submarine || gap.ConnectedDoor != null) { continue; }
RectangleF entityRect = ScaleRectToUI(gap, miniMapFrame.Rect, worldBorders);
Vector2 scale = new Vector2(entityRect.Size.X / spriteSize.X, entityRect.Size.Y / spriteSize.Y) * 2.0f;
Color color = ToolBox.GradientLerp(gap.Open, GUI.Style.HealthBarColorMedium, GUI.Style.HealthBarColorLow) * alpha;
sprite.Draw(spriteBatch,
miniMapFrame.Rect.Location.ToVector2() + entityRect.Center,
color, origin: sprite.Origin, rotate: 0.0f, scale: scale);
}
}
}
if (currentMode == MiniMapMode.HullStatus)
{
foreach (var (entity, component) in hullStatusComponents)
{
if (!(entity is Hull hull)) { continue; }
if (!hullDatas.TryGetValue(hull, out HullData? hullData) || hullData is null) { continue; }
DrawHullCards(spriteBatch, hull, hullData, component.RectComponent);
}
}
spriteBatch.End();
@@ -721,14 +745,21 @@ namespace Barotrauma.Items.Components
UserData = prefab
};
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform), isHorizontal: true);
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform), isHorizontal: true)
{
Stretch = true
};
new GUIImage(new RectTransform(Vector2.One, layout.RectTransform, scaleBasis: ScaleBasis.BothHeight), sprite)
{
Color = prefab.InventoryIconColor
Color = prefab.InventoryIconColor,
UserData = prefab
};
new GUITextBlock(new RectTransform(Vector2.One, layout.RectTransform), prefab.Name, font: GUI.SubHeadingFont);
layout.UserData = prefab;
var nameText = new GUITextBlock(new RectTransform(Vector2.One, layout.RectTransform), prefab.Name);
nameText.RectTransform.SizeChanged += () =>
{
nameText.Text = ToolBox.LimitString(prefab.Name, nameText.Font, nameText.Rect.Width);
};
}
private void SearchItems(string text)
@@ -792,15 +823,18 @@ namespace Barotrauma.Items.Components
private void UpdateHUDBack()
{
if (item.Submarine == null) { return; }
hullInfoFrame.Visible = false;
electricalFrame.Visible = false;
miniMapFrame.Visible = false;
reportFrame.Visible = false;
searchBarFrame.Visible = false;
electricalFrame.Visible = false;
miniMapFrame.Visible = false;
switch (currentMode)
{
case MiniMapMode.HullStatus:
case MiniMapMode.HullCondition:
UpdateHullStatus();
miniMapFrame.Visible = true;
reportFrame.Visible = true;
@@ -937,16 +971,19 @@ namespace Barotrauma.Items.Components
SetTooltip(borderComponent.Rect.Center, header, line1, line2, line3, line1Color, line2Color, line3Color);
}
bool draggingReport = GameMain.GameSession?.CrewManager?.DraggedOrder != null;
// When setting the colors we want to know the linked hulls too or else the linked hull will not realize its being hovered over and reset the border color
foreach (Hull linkedHull in hullData.LinkedHulls)
{
if (!hullStatusComponents.ContainsKey(linkedHull)) { continue; }
isHoveringOver |= canHoverOverHull && hullStatusComponents[linkedHull].RectComponent == GUI.MouseOn;
isHoveringOver |=
canHoverOverHull &&
(hullStatusComponents[linkedHull].RectComponent == GUI.MouseOn || (draggingReport && hullStatusComponents[linkedHull].RectComponent.MouseRect.Contains(PlayerInput.MousePosition)));
if (isHoveringOver) { break; }
}
if (isHoveringOver)
if (isHoveringOver || (draggingReport && component.MouseRect.Contains(PlayerInput.MousePosition)))
{
borderColor = Color.Lerp(borderColor, Color.White, 0.5f);
componentColor = HoverColor;
@@ -1037,7 +1074,7 @@ namespace Barotrauma.Items.Components
}
else
{
bool hullsVisible = currentMode == MiniMapMode.HullStatus;
bool hullsVisible = currentMode == MiniMapMode.HullStatus || currentMode == MiniMapMode.HullCondition;
foreach (var (entity, component) in hullStatusComponents)
{
@@ -1150,7 +1187,7 @@ namespace Barotrauma.Items.Components
GameMain.GameScreen.BlueprintEffect.Parameters["width"].SetValue((float)texture.Width);
GameMain.GameScreen.BlueprintEffect.Parameters["height"].SetValue((float)texture.Height);
Color blueprintBlue = BlueprintBlue * currentMode switch { MiniMapMode.HullStatus => 0.1f, MiniMapMode.ElectricalView => 0.1f, _ => 0.5f };
Color blueprintBlue = BlueprintBlue * currentMode switch { MiniMapMode.HullStatus => 0.1f, MiniMapMode.HullCondition => 0.1f, MiniMapMode.ElectricalView => 0.1f, _ => 0.5f };
Vector2 origin = new Vector2(texture.Width / 2f, texture.Height / 2f);
float scale = currentMode == MiniMapMode.HullStatus ? 1.0f : Zoom;

View File

@@ -19,9 +19,11 @@ namespace Barotrauma.Items.Components
private GUIProgressBar progressBar;
private List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
private GUITextBlock progressBarOverlayText;
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
//the corresponding particle emitter is active when the condition is within this range
private List<Vector2> particleEmitterConditionRanges = new List<Vector2>();
private readonly List<Vector2> particleEmitterConditionRanges = new List<Vector2>();
private SoundChannel repairSoundChannel;
@@ -88,7 +90,7 @@ namespace Barotrauma.Items.Components
}
}
private void CreateGUI()
protected override void CreateGUI()
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.75f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
@@ -123,6 +125,11 @@ namespace Barotrauma.Items.Components
progressBar = new GUIProgressBar(new RectTransform(new Vector2(0.6f, 1.0f), progressBarHolder.RectTransform),
color: GUI.Style.Green, barSize: 0.0f, style: "DeviceProgressBar");
progressBarOverlayText = new GUITextBlock(new RectTransform(Vector2.One, progressBar.RectTransform), string.Empty, font: GUI.SubHeadingFont, textAlignment: Alignment.Center)
{
IgnoreLayoutGroups = true
};
repairButtonText = TextManager.Get("RepairButton");
repairingText = TextManager.Get("Repairing");
RepairButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), progressBarHolder.RectTransform, Anchor.TopCenter), repairButtonText)
@@ -138,6 +145,7 @@ namespace Barotrauma.Items.Components
progressBarHolder.RectTransform.MinSize = RepairButton.RectTransform.MinSize;
RepairButton.RectTransform.MinSize = new Point((int)(RepairButton.TextBlock.TextSize.X * 1.2f), RepairButton.RectTransform.MinSize.Y);
sabotageButtonText = TextManager.Get("SabotageButton");
sabotagingText = TextManager.Get("Sabotaging");
SabotageButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.15f), paddedFrame.RectTransform, Anchor.BottomCenter), sabotageButtonText, style: "GUIButtonSmall")
@@ -229,9 +237,23 @@ namespace Barotrauma.Items.Components
{
IsActive = true;
progressBar.BarSize = item.Condition / item.MaxCondition;
float defaultMaxCondition = (item.MaxCondition / item.MaxRepairConditionMultiplier);
progressBar.BarSize = item.Condition / defaultMaxCondition;
progressBar.Color = ToolBox.GradientLerp(progressBar.BarSize, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green);
if (item.Condition > defaultMaxCondition)
{
float extraCondition = item.MaxCondition * (item.MaxRepairConditionMultiplier - 1.0f);
progressBar.Color = ToolBox.GradientLerp((item.Condition - defaultMaxCondition) / extraCondition, GUI.Style.ColorReputationHigh, GUI.Style.ColorReputationVeryHigh);
progressBarOverlayText.Visible = true;
progressBarOverlayText.Text = $"{(int)Math.Round((item.Condition / defaultMaxCondition) * 100)}%";
}
else
{
progressBarOverlayText.Visible = false;
}
RepairButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Repair)) && !item.IsFullCondition;
RepairButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Repair) ?
repairButtonText :

View File

@@ -316,12 +316,17 @@ namespace Barotrauma
string colorStr = XMLExtensions.ColorToString(!item.AllowStealing ? GUI.Style.Red : Color.White);
if (item.Quality > 0)
{
name = TextManager.GetWithVariable("itemname.quality" + item.Quality, "[itemname]", name, fallBackTag: "itemname.quality3");
}
toolTip = $"‖color:{colorStr}‖{name}‖color:end‖";
if (itemsInSlot.All(it => it.NonInteractable || it.NonPlayerTeamInteractable))
{
toolTip += " " + TextManager.Get("connectionlocked");
}
if (!item.IsFullCondition && !item.Prefab.HideConditionBar)
if (!item.IsFullCondition && !item.Prefab.HideConditionInTooltip)
{
string conditionColorStr = XMLExtensions.ColorToString(ToolBox.GradientLerp(item.Condition / item.MaxCondition, GUI.Style.ColorInventoryEmpty, GUI.Style.ColorInventoryHalf, GUI.Style.ColorInventoryFull));
toolTip += $"‖color:{conditionColorStr}‖ ({(int)item.ConditionPercentage} %)‖color:end‖";

View File

@@ -1068,7 +1068,7 @@ namespace Barotrauma
foreach (Character otherCharacter in Character.CharacterList)
{
if (otherCharacter != character &&
otherCharacter.SelectedConstruction == character.SelectedConstruction)
otherCharacter.SelectedConstruction == this)
{
ItemInUseWarning.Visible = true;
if (mergedHUDRect.Width > GameMain.GraphicsWidth / 2) { mergedHUDRect.Inflate(-GameMain.GraphicsWidth / 4, 0); }
@@ -1194,7 +1194,7 @@ namespace Barotrauma
}
}
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != this)
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != this && GetComponent<RemoteController>() == null)
{
if (Character.Controlled.SelectedConstruction?.GetComponent<RemoteController>()?.TargetItem != this &&
!Character.Controlled.HeldItems.Any(it => it.GetComponent<RemoteController>()?.TargetItem == this))
@@ -1537,6 +1537,7 @@ namespace Barotrauma
byte bodyType = msg.ReadByte();
bool spawnedInOutpost = msg.ReadBoolean();
bool allowStealing = msg.ReadBoolean();
int quality = msg.ReadRangedInteger(0, Items.Components.Quality.MaxQuality);
byte teamID = msg.ReadByte();
bool tagsChanged = msg.ReadBoolean();
string tags = "";
@@ -1612,7 +1613,8 @@ namespace Barotrauma
item = new Item(itemPrefab, pos, sub, id: itemId)
{
SpawnedInOutpost = spawnedInOutpost,
AllowStealing = allowStealing
AllowStealing = allowStealing,
Quality = quality
};
}
catch (Exception e)

View File

@@ -1694,7 +1694,7 @@ namespace Barotrauma
private void CreateJobVariantTooltip(JobPrefab jobPrefab, int variant, GUIComponent parentSlot)
{
jobVariantTooltip = new GUIFrame(new RectTransform(new Point((int)(500 * GUI.Scale), (int)(200 * GUI.Scale)), GUI.Canvas, pivot: Pivot.BottomRight),
jobVariantTooltip = new GUIFrame(new RectTransform(new Point((int)(400 * GUI.Scale), (int)(180 * GUI.Scale)), GUI.Canvas, pivot: Pivot.BottomRight),
style: "GUIToolTip")
{
UserData = new Pair<JobPrefab, int>(jobPrefab, variant)
@@ -1707,17 +1707,7 @@ namespace Barotrauma
AbsoluteSpacing = (int)(15 * GUI.Scale)
};
string name =
TextManager.Get("jobname." + jobPrefab.Identifier + (variant + 1), returnNull: true, fallBackTag: "jobname." + jobPrefab.Identifier) ??
"";
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), name, font: GUI.SubHeadingFont);
string description =
TextManager.Get("jobdescription." + jobPrefab.Identifier + (variant + 1), returnNull: true, fallBackTag: "jobdescription." + jobPrefab.Identifier) ??
"";
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), description, wrap: true, font: GUI.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.GetWithVariable("startingequipmentname", "[number]", (variant + 1).ToString()), font: GUI.SubHeadingFont, textAlignment: Alignment.Center);
var itemIdentifiers = jobPrefab.ItemIdentifiers[variant]
.Distinct()
@@ -1726,7 +1716,7 @@ namespace Barotrauma
int itemsPerRow = 5;
int rows = (int)Math.Max(Math.Ceiling(itemIdentifiers.Count() / (float)itemsPerRow), 1);
new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.4f * rows), content.RectTransform, Anchor.BottomLeft),
new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.4f * rows), content.RectTransform, Anchor.BottomCenter),
onDraw: (sb, component) => { DrawJobVariantItems(sb, component, new Pair<JobPrefab, int>(jobPrefab, variant), itemsPerRow); });
jobVariantTooltip.RectTransform.MinSize = new Point(0, content.RectTransform.Children.Sum(c => c.Rect.Height + content.AbsoluteSpacing));

View File

@@ -1,6 +1,7 @@
#nullable enable
using System;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -23,6 +24,8 @@ namespace Barotrauma
private Character? dummyCharacter;
public static Effect BlueprintEffect;
private TabMenu tabMenu;
public TestScreen()
{
Cam = new Camera();
@@ -50,18 +53,15 @@ namespace Barotrauma
dummyCharacter?.Remove();
}
// ????????
submarine = new Submarine(SubmarineInfo.SavedSubmarines.FirstOrDefault(info => info.Name.Equals("Crescent", StringComparison.OrdinalIgnoreCase)));
miniMapItem = new Item(ItemPrefab.Find(null, "statusmonitor"), Vector2.Zero, submarine);
MiniMap miniMap = miniMapItem.GetComponent<MiniMap>();
miniMap.PowerConsumption = 0;
dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.DummyID, hasAi: false);
dummyCharacter.Info.Job = new Job(JobPrefab.Prefabs.Where(jp => TalentTree.JobTalentTrees.ContainsKey(jp.Identifier)).GetRandom());
dummyCharacter.Info.Name = "Galldren";
dummyCharacter.Inventory.CreateSlots();
Character.Controlled = dummyCharacter;
GameMain.World.ProcessChanges();
TabMenu.selectedTab = TabMenu.InfoFrameTab.Talents;
tabMenu = new TabMenu();
}
public override void AddToGUIUpdateList()
@@ -69,34 +69,21 @@ namespace Barotrauma
Frame.AddToGUIUpdateList();
CharacterHUD.AddToGUIUpdateList(dummyCharacter);
dummyCharacter?.SelectedConstruction?.AddToGUIUpdateList();
tabMenu.AddToGUIUpdateList();
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
tabMenu.Update();
if (dummyCharacter is { } dummy && miniMapItem is { } item)
if (dummyCharacter is { } dummy)
{
if (dummy.SelectedConstruction != item)
{
dummy.SelectedConstruction = item;
}
dummy.SelectedConstruction?.UpdateHUD(Cam, dummy, (float)deltaTime);
Vector2 pos = FarseerPhysics.ConvertUnits.ToSimUnits(item.Position);
foreach (Limb limb in dummy.AnimController.Limbs)
{
limb.body.SetTransform(pos, 0.0f);
}
if (dummy.AnimController?.Collider is { } collider)
{
collider.SetTransform(pos, 0);
}
dummy.ControlLocalPlayer((float)deltaTime, Cam, false);
dummy.Control((float)deltaTime, Cam);
dummy.Submarine = submarine;
}
GUI.Update((float)deltaTime);
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.1500.2.0</Version>
<Version>0.1500.3.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.1500.2.0</Version>
<Version>0.1500.3.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.1500.2.0</Version>
<Version>0.1500.3.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.1500.2.0</Version>
<Version>0.1500.3.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.1500.2.0</Version>
<Version>0.1500.3.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -282,6 +282,7 @@ namespace Barotrauma
msg.Write(body == null ? (byte)0 : (byte)body.BodyType);
msg.Write(SpawnedInOutpost);
msg.Write(AllowStealing);
msg.WriteRangedInteger(Quality, 0, Items.Components.Quality.MaxQuality);
byte teamID = 0;
foreach (WifiComponent wifiComponent in GetComponents<WifiComponent>())

View File

@@ -265,7 +265,7 @@ namespace Barotrauma.Networking
"192-255",
"384-591",
"1024-1279",
"19968-40959","13312-19903","131072-15043983","15043985-173791","173824-178207","178208-183983","63744-64255","194560-195103" //CJK
"19968-21327","21329-40959","13312-19903","131072-173791","173824-178207","178208-183983","63744-64255","194560-195103" //CJK
};
string[] allowedClientNameCharsStr = doc.Root.GetAttributeStringArray("AllowedClientNameChars", defaultAllowedClientNameChars);

View File

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

View File

@@ -161,8 +161,8 @@ namespace Barotrauma
for (int i = 0; i < container.Inventory.Capacity; i++)
{
if (container.Inventory.GetItemAt(i) != null) { continue; }
if (MapEntityPrefab.List.GetRandom(e => e is ItemPrefab i && container.CanBeContained(i) &&
Config.ForbiddenAmmunition.None(id => id.Equals(i.Identifier, StringComparison.OrdinalIgnoreCase)), Rand.RandSync.Server) is ItemPrefab ammoPrefab)
if (MapEntityPrefab.List.GetRandom(e => e is ItemPrefab ip && container.CanBeContained(ip, i) &&
Config.ForbiddenAmmunition.None(id => id.Equals(ip.Identifier, StringComparison.OrdinalIgnoreCase)), Rand.RandSync.Server) is ItemPrefab ammoPrefab)
{
Item ammo = new Item(ammoPrefab, container.Item.WorldPosition, Wreck);
if (!container.Inventory.TryPutItem(ammo, i, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: false))

View File

@@ -1430,6 +1430,7 @@ namespace Barotrauma
//throwing conscious/moving characters around takes more force -> double the flow force
if (character.CanMove) { flowForce *= 2.0f; }
flowForce *= 1 - Math.Clamp(character.GetStatValue(StatTypes.FlowResistance), 0f, 1f);
float flowForceMagnitude = flowForce.Length();
float limbMultipier = limbs.Count(l => l.inWater) / (float)limbs.Length;

View File

@@ -251,6 +251,8 @@ namespace Barotrauma
private readonly List<Attacker> lastAttackers = new List<Attacker>();
public IEnumerable<Attacker> LastAttackers => lastAttackers;
public Character LastAttacker => lastAttackers.LastOrDefault()?.Character;
public Character LastOrderedCharacter { get; private set; }
public Character SecondLastOrderedCharacter { get; private set; }
public Entity LastDamageSource;
@@ -2720,7 +2722,7 @@ namespace Barotrauma
//Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us!
bool allowRagdoll = GameMain.NetworkMember?.ServerSettings?.AllowRagdollButton ?? true;
bool tooFastToUnragdoll = AnimController.Collider.LinearVelocity.LengthSquared() > 5.0f * 5.0f;
bool tooFastToUnragdoll = AnimController.Collider.LinearVelocity.LengthSquared() > 2.5f * 2.5f;
bool wasRagdolled = false;
bool selfRagdolled = false;
@@ -3131,6 +3133,12 @@ namespace Barotrauma
{
var abilityOrderedCharacter = new AbilityCharacter(this);
orderGiver.CheckTalents(AbilityEffectType.OnGiveOrder, abilityOrderedCharacter);
if (orderGiver.LastOrderedCharacter != this)
{
orderGiver.SecondLastOrderedCharacter = orderGiver.LastOrderedCharacter;
orderGiver.LastOrderedCharacter = this;
}
}
if (AIController is HumanAIController humanAI)
@@ -3406,6 +3414,13 @@ namespace Barotrauma
AddDamage(worldPosition, attackAfflictions, attack.Stun, playSound, attackImpulse, out limbHit, attacker, attack.DamageMultiplier * attackData.DamageMultiplier) :
DamageLimb(worldPosition, targetLimb, attackAfflictions, attack.Stun, playSound, attackImpulse, attacker, attack.DamageMultiplier * attackData.DamageMultiplier, penetration: penetration + attackData.AddedPenetration);
if (attacker != null)
{
var abilityAttackResult = new AbilityAttackResult(attackResult);
attacker.CheckTalents(AbilityEffectType.OnAttackResult, abilityAttackResult);
CheckTalents(AbilityEffectType.OnAttackedResult, abilityAttackResult);
}
if (limbHit == null) { return new AttackResult(); }
Vector2 forceWorld = attack.TargetImpulseWorld + attack.TargetForceWorld;
if (attacker != null)
@@ -3623,11 +3638,6 @@ namespace Barotrauma
ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
hitLimb.ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
}
if (attacker != null)
{
var abilityAttackResult = new AbilityAttackResult(attackResult);
attacker.CheckTalents(AbilityEffectType.OnAttackResult, abilityAttackResult);
}
return attackResult;
}

View File

@@ -979,6 +979,8 @@ namespace Barotrauma
increase *= SkillSettings.Current.AssistantSkillIncreaseMultiplier;
}
increase *= 1f + Character.GetStatValue(StatTypes.SkillGainSpeed);
float prevLevel = Job.GetSkillLevel(skillIdentifier);
Job.IncreaseSkillLevel(skillIdentifier, increase, Character.HasAbilityFlag(AbilityFlags.GainSkillPastMaximum));
@@ -1527,6 +1529,17 @@ namespace Barotrauma
return 0f;
}
}
public float GetSavedStatValue(StatTypes statType, string statIdentifier)
{
if (savedStatValues.TryGetValue(statType, out var statValues))
{
return statValues.Where(s => s.StatIdentifier.Equals(statIdentifier, StringComparison.OrdinalIgnoreCase)).Sum(v => v.StatValue);
}
else
{
return 0f;
}
}
public void ChangeSavedStatValue(StatTypes statType, float value, string statIdentifier, bool removeOnDeath, bool removeAfterRound = false, float maxValue = float.MaxValue)
{

View File

@@ -971,7 +971,7 @@ namespace Barotrauma
/// <param name="treatmentSuitability">A dictionary where the key is the identifier of the item and the value the suitability</param>
/// <param name="normalize">If true, the suitability values are normalized between 0 and 1. If not, they're arbitrary values defined in the medical item XML, where negative values are unsuitable, and positive ones suitable.</param>
/// <param name="randomization">Amount of randomization to apply to the values (0 = the values are accurate, 1 = the values are completely random)</param>
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, Limb limb = null, float randomization = 0.0f)
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, Limb limb = null, bool ignoreHiddenAfflictions = false, float randomization = 0.0f)
{
//key = item identifier
//float = suitability
@@ -980,6 +980,7 @@ namespace Barotrauma
foreach (Affliction affliction in getAfflictions(limb))
{
if (affliction.Strength < affliction.Prefab.TreatmentThreshold) { continue; }
if (ignoreHiddenAfflictions && affliction.Strength < affliction.Prefab.ShowIconThreshold) { continue; }
foreach (KeyValuePair<string, float> treatment in affliction.Prefab.TreatmentSuitability)
{
if (!treatmentSuitability.ContainsKey(treatment.Key))

View File

@@ -7,12 +7,12 @@ namespace Barotrauma.Abilities
{
private enum WeaponType
{
Any = 0,
Melee = 1,
Any = 0,
Melee = 1,
Ranged = 2
};
private WeaponType weapontype;
private readonly WeaponType weapontype;
public AbilityConditionIsAiming(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
{
switch (conditionElement.GetAttributeString("weapontype", ""))
@@ -43,7 +43,7 @@ namespace Barotrauma.Abilities
break;
default:
aimingCorrectItem |= animController.IsAiming || animController.IsAimingMelee;
break;
break;
}
}
}

View File

@@ -6,12 +6,12 @@ namespace Barotrauma.Abilities
{
class AbilityConditionItem : AbilityConditionData
{
private readonly string identifier;
private readonly string[] identifiers;
private readonly string[] tags;
public AbilityConditionItem(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
{
identifier = conditionElement.GetAttributeString("identifier", string.Empty).ToLowerInvariant();
identifiers = conditionElement.GetAttributeStringArray("identifiers", Array.Empty<string>(), convertToLowerInvariant: true);
tags = conditionElement.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
}
@@ -29,15 +29,15 @@ namespace Barotrauma.Abilities
if (itemPrefab != null)
{
if (!string.IsNullOrEmpty(identifier))
if (identifiers.Any())
{
if (itemPrefab.Identifier != identifier)
if (!identifiers.Any(t => itemPrefab.Identifier == t))
{
return false;
}
}
return tags.Any(t => itemPrefab.Tags.Any(p => t == p));
return !tags.Any() || tags.Any(t => itemPrefab.Tags.Any(p => t == p));
}
else
{

View File

@@ -0,0 +1,20 @@
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class AbilityConditionHasVelocity : AbilityConditionDataless
{
private readonly float velocity;
public AbilityConditionHasVelocity(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
{
velocity = conditionElement.GetAttributeFloat("velocity", 0f);
}
protected override bool MatchesConditionSpecific()
{
return character.AnimController.Collider.LinearVelocity.LengthSquared() > velocity * velocity;
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Barotrauma.Abilities
protected override bool MatchesConditionSpecific()
{
if (character.Submarine == null || character.Submarine.TeamID != character.TeamID) { return false; }
if (!character.IsInFriendlySub) { return false; }
float currentFloodPercentage = character.Submarine.GetHulls(false).Average(h => h.WaterPercentage);
return currentFloodPercentage / 100 > floodPercentage;
}

View File

@@ -10,6 +10,7 @@ namespace Barotrauma.Abilities
protected readonly List<StatusEffect> statusEffects;
private readonly bool nearbyCharactersAppliesToSelf;
private readonly bool applyToSelected;
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
@@ -18,6 +19,7 @@ namespace Barotrauma.Abilities
{
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
applyToSelected = abilityElement.GetAttributeBool("applytoselected", false);
nearbyCharactersAppliesToSelf = abilityElement.GetAttributeBool("nearbycharactersappliestoself", true);
}
protected void ApplyEffectSpecific(Character targetCharacter)
@@ -26,7 +28,7 @@ namespace Barotrauma.Abilities
{
if (statusEffect.HasTargetType(StatusEffect.TargetType.UseTarget))
{
// currently used this to spawn items on the targeted character
// currently used to spawn items on the targeted character
statusEffect.SetUser(targetCharacter);
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targetCharacter);
}
@@ -34,6 +36,10 @@ namespace Barotrauma.Abilities
{
targets.Clear();
targets.AddRange(statusEffect.GetNearbyTargets(targetCharacter.WorldPosition, targets));
if (!nearbyCharactersAppliesToSelf)
{
targets.RemoveAll(c => c == Character);
}
statusEffect.SetUser(Character);
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targets);
}

View File

@@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityApplyStatusEffectsToLastOrderedCharacter : CharacterAbilityApplyStatusEffects
{
public CharacterAbilityApplyStatusEffectsToLastOrderedCharacter(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
}
protected override void ApplyEffect()
{
if (IsViableTarget(Character.LastOrderedCharacter))
{
ApplyEffectSpecific(Character.LastOrderedCharacter);
}
if (Character.HasAbilityFlag(AbilityFlags.AllowSecondOrderedTarget) && IsViableTarget(Character.SecondLastOrderedCharacter))
{
ApplyEffectSpecific(Character.SecondLastOrderedCharacter);
}
}
private bool IsViableTarget(Character targetCharacter)
{
if (targetCharacter == null || targetCharacter.Removed) { return false; }
if (targetCharacter == Character) { return false; }
return true;
}
}
}

View File

@@ -27,8 +27,7 @@ namespace Barotrauma.Abilities
targetAllies = abilityElement.GetAttributeBool("targetallies", false);
removeOnDeath = abilityElement.GetAttributeBool("removeondeath", true);
removeAfterRound = abilityElement.GetAttributeBool("removeafterround", false);
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", false);
//maximumValue = abilityElement.GetAttributeFloat("maximumvalue", float.MaxValue);
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", characterAbilityGroup.AbilityEffectType == AbilityEffectType.None);
}
public override void InitializeAbility(bool addingFirstTime)

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System.Xml.Linq;
namespace Barotrauma.Abilities
@@ -7,13 +8,22 @@ namespace Barotrauma.Abilities
{
public override bool AppliesEffectOnIntervalUpdate => true;
private string skillIdentifier;
private float skillIncrease;
private readonly string skillIdentifier;
private readonly float skillIncrease;
public CharacterAbilityIncreaseSkill(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
skillIdentifier = abilityElement.GetAttributeString("skillidentifier", "").ToLowerInvariant();
skillIncrease = abilityElement.GetAttributeFloat("skillincrease", 0f);
if (string.IsNullOrEmpty(skillIdentifier))
{
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill identifier not defined in CharacterAbilityIncreaseSkill.");
}
if (MathUtils.NearlyEqual(skillIncrease, 0))
{
DebugConsole.AddWarning($"Possible error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill increase set to 0.");
}
}
protected override void ApplyEffect()
@@ -35,7 +45,17 @@ namespace Barotrauma.Abilities
private void ApplyEffectSpecific(Character character)
{
character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
if (skillIdentifier.Equals("random"))
{
var skill = character.Info?.Job?.Skills?.GetRandom();
if (skill == null) { return; }
character.Info?.IncreaseSkillLevel(skill.Identifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
}
else
{
character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityModifyStatToFlooding : CharacterAbility
{
private readonly StatTypes statType;
private readonly float maxValue;
private float lastValue = 0f;
public CharacterAbilityModifyStatToFlooding(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
maxValue = abilityElement.GetAttributeFloat("maxvalue", 0f);
}
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
{
Character.ChangeStat(statType, -lastValue);
if (conditionsMatched && Character.IsInFriendlySub)
{
float currentFloodPercentage = Character.Submarine.GetHulls(false).Average(h => h.WaterPercentage);
lastValue = currentFloodPercentage / 100f * maxValue;
Character.ChangeStat(statType, lastValue);
}
else
{
lastValue = 0f;
}
}
}
}

View File

@@ -4,8 +4,8 @@ namespace Barotrauma.Abilities
{
class CharacterAbilityModifyValue : CharacterAbility
{
private float addedValue;
private float multiplyValue;
private readonly float addedValue;
private readonly float multiplyValue;
public CharacterAbilityModifyValue(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{

View File

@@ -6,12 +6,14 @@ namespace Barotrauma.Abilities
{
class CharacterAbilityByTheBook : CharacterAbility
{
private int moneyAmount;
private int max;
private readonly int moneyAmount;
private readonly int experienceAmount;
private readonly int max;
public CharacterAbilityByTheBook(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
moneyAmount = abilityElement.GetAttributeInt("moneyamount", 0);
experienceAmount = abilityElement.GetAttributeInt("experienceamount", 0);
max = abilityElement.GetAttributeInt("max", 0);
}
@@ -28,6 +30,10 @@ namespace Barotrauma.Abilities
if (!enemyCharacter.LockHands) { continue; }
if (timesGiven > max) { continue; }
Character.GiveMoney(moneyAmount);
foreach (Character character in Character.GetFriendlyCrew(Character))
{
character.Info?.GiveExperience(experienceAmount);
}
timesGiven++;
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityEnigmaMachine : CharacterAbility
{
private readonly float addedValue;
private readonly float multiplyValue;
private readonly string[] tags;
private readonly int maxMultiplyCount;
public CharacterAbilityEnigmaMachine(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
addedValue = abilityElement.GetAttributeFloat("addedvalue", 0f);
multiplyValue = abilityElement.GetAttributeFloat("multiplyvalue", 1f);
tags = abilityElement.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
maxMultiplyCount = abilityElement.GetAttributeInt("maxmultiplycount", int.MaxValue);
}
protected override void ApplyEffect(AbilityObject abilityObject)
{
if (abilityObject is IAbilityValue abilityValue)
{
int multiplyCount = 0;
foreach (Item item in Item.ItemList)
{
if (item.Prefab.Tags.Any(t => tags.Contains(t)))
{
multiplyCount++;
if (multiplyCount == maxMultiplyCount)
{
break;
}
}
}
abilityValue.Value += addedValue * multiplyCount;
}
}
}
}

View File

@@ -1,30 +0,0 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityIndustrialRevolution : CharacterAbility
{
float addedFabricationSpeed;
public CharacterAbilityIndustrialRevolution(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
addedFabricationSpeed = abilityElement.GetAttributeFloat("addedfabricationspeed", 0f);
}
public override void UpdateCharacterAbility(bool conditionsMatched, float timeSinceLastUpdate)
{
if (conditionsMatched)
{
// not necessarily the cleanest or performant way, but at least this shouldn't break anything.
// must be done every frame in order to work.
if (Character.SelectedConstruction?.GetComponent<Fabricator>() is Fabricator fabricator && fabricator.IsActive)
{
fabricator.FabricationSpeedMultiplier += addedFabricationSpeed;
}
}
}
}
}

View File

@@ -1,39 +0,0 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityTaskmaster : CharacterAbility
{
private readonly List<StatusEffect> statusEffects;
private readonly List<StatusEffect> statusEffectsRemove;
private Character lastCharacter;
public CharacterAbilityTaskmaster(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
statusEffectsRemove = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffectsremove"));
}
protected override void ApplyEffect(AbilityObject abilityObject)
{
if ((abilityObject as IAbilityCharacter)?.Character is Character targetCharacter)
{
if (targetCharacter == Character) { return; }
foreach (var statusEffect in statusEffectsRemove)
{
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, lastCharacter);
}
foreach (var statusEffect in statusEffects)
{
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, targetCharacter);
}
lastCharacter = targetCharacter;
}
}
}
}

View File

@@ -14,6 +14,8 @@ namespace Barotrauma.Abilities
// currently only used to turn off simulation if random conditions are in use
public bool IsActive { get; private set; } = true;
public readonly AbilityEffectType AbilityEffectType;
protected int maxTriggerCount { get; }
protected int timesTriggered = 0;
@@ -24,8 +26,9 @@ namespace Barotrauma.Abilities
// separate dictionaries for each type of characterability?
protected readonly List<CharacterAbility> characterAbilities = new List<CharacterAbility>();
public CharacterAbilityGroup(CharacterTalent characterTalent, XElement abilityElementGroup)
public CharacterAbilityGroup(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, XElement abilityElementGroup)
{
AbilityEffectType = abilityEffectType;
CharacterTalent = characterTalent;
Character = CharacterTalent.Character;
maxTriggerCount = abilityElementGroup.GetAttributeInt("maxtriggercount", int.MaxValue);
@@ -168,8 +171,7 @@ namespace Barotrauma.Abilities
public static StatTypes ParseStatType(string statTypeString, string debugIdentifier)
{
StatTypes statType;
if (!Enum.TryParse(statTypeString, true, out statType))
if (!Enum.TryParse(statTypeString, true, out StatTypes statType))
{
DebugConsole.ThrowError("Invalid stat type type \"" + statTypeString + "\" in CharacterTalent (" + debugIdentifier + ")");
}

View File

@@ -8,7 +8,8 @@ namespace Barotrauma.Abilities
{
class CharacterAbilityGroupEffect : CharacterAbilityGroup
{
public CharacterAbilityGroupEffect(CharacterTalent characterTalent, XElement abilityElementGroup) : base(characterTalent, abilityElementGroup) { }
public CharacterAbilityGroupEffect(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, XElement abilityElementGroup) :
base(abilityEffectType, characterTalent, abilityElementGroup) { }
public void CheckAbilityGroup(AbilityObject abilityObject)
{

View File

@@ -15,7 +15,8 @@ namespace Barotrauma.Abilities
private float effectDelayTimer;
public CharacterAbilityGroupInterval(CharacterTalent characterTalent, XElement abilityElementGroup) : base(characterTalent, abilityElementGroup)
public CharacterAbilityGroupInterval(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, XElement abilityElementGroup) :
base(abilityEffectType, characterTalent, abilityElementGroup)
{
// too many overlapping intervals could cause hitching? maybe randomize a little
interval = abilityElementGroup.GetAttributeFloat("interval", 0f);

View File

@@ -85,14 +85,13 @@ namespace Barotrauma
// XML logic
private void LoadAbilityGroupInterval(XElement abilityGroup)
{
string name = abilityGroup.Name.ToString().ToLowerInvariant();
characterAbilityGroupIntervals.Add(new CharacterAbilityGroupInterval(this, abilityGroup));
characterAbilityGroupIntervals.Add(new CharacterAbilityGroupInterval(AbilityEffectType.Undefined, this, abilityGroup));
}
private void LoadAbilityGroupEffect(XElement abilityGroup)
{
AbilityEffectType abilityEffectType = ParseAbilityEffectType(this, abilityGroup.GetAttributeString("abilityeffecttype", "none"));
AddAbilityGroupEffect(new CharacterAbilityGroupEffect(this, abilityGroup), abilityEffectType);
AddAbilityGroupEffect(new CharacterAbilityGroupEffect(abilityEffectType, this, abilityGroup), abilityEffectType);
}
public void AddAbilityGroupEffect(CharacterAbilityGroupEffect characterAbilityGroup, AbilityEffectType abilityEffectType = AbilityEffectType.None)

View File

@@ -1,5 +1,4 @@
using Microsoft.Xna.Framework;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
@@ -8,13 +7,19 @@ namespace Barotrauma
{
class TalentTree
{
public enum TalentTreeStageState
{
Invalid,
Locked,
Unlocked,
Available,
Highlighted
}
public static readonly Dictionary<string, TalentTree> JobTalentTrees = new Dictionary<string, TalentTree>();
public readonly List<TalentSubTree> TalentSubTrees = new List<TalentSubTree>();
private static HashSet<string> subtreeTalents = new HashSet<string>();
private const string PlaceholderTalent = "placeholder";
public XElement ConfigElement
{
get;
@@ -35,14 +40,13 @@ namespace Barotrauma
foreach (XElement subTreeElement in element.GetChildElements("subtree"))
{
TalentSubTrees.Add(new TalentSubTree(subTreeElement));
TalentSubTrees.Add(new TalentSubTree(subTreeElement));
}
// talents found and unlocked using the identifier wihin the talent tree, so no duplicates may occur
HashSet<string> duplicateSet = new HashSet<string>();
foreach (string talent in TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier))))
{
if (talent == PlaceholderTalent) { continue; }
TalentPrefab talentPrefab = TalentPrefab.TalentPrefabs.Find(c => c.Identifier.Equals(talent, StringComparison.OrdinalIgnoreCase));
if (talentPrefab == null)
{
@@ -97,7 +101,7 @@ namespace Barotrauma
}
break;
default:
DebugConsole.ThrowError($"Invalid XML root element: '{rootElement.Name.ToString()}' in {file.Path}");
DebugConsole.ThrowError($"Invalid XML root element: '{rootElement.Name}' in {file.Path}");
break;
}
}
@@ -117,10 +121,63 @@ namespace Barotrauma
return IsViableTalentForCharacter(character, talentIdentifier, character?.Info?.UnlockedTalents ?? Enumerable.Empty<string>());
}
// i hate this function - markus
public static TalentTreeStageState GetTalentOptionStageState(Character character, string subTreeIdentifier, int index, List<string> selectedTalents)
{
if (character?.Info?.Job.Prefab is null) { return TalentTreeStageState.Invalid; }
if (!JobTalentTrees.TryGetValue(character.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return TalentTreeStageState.Invalid; }
TalentSubTree subTree = talentTree.TalentSubTrees.FirstOrDefault(tst => tst.Identifier == subTreeIdentifier);
if (subTree == null) { return TalentTreeStageState.Invalid; }
TalentOption targetTalentOption = subTree.TalentOptionStages[index];
if (targetTalentOption.Talents.Any(t => character.HasTalent(t.Identifier)))
{
return TalentTreeStageState.Unlocked;
}
if (targetTalentOption.Talents.Any(t => selectedTalents.Contains(t.Identifier)))
{
return TalentTreeStageState.Highlighted;
}
bool hasTalentInLastTier = true;
bool isLastTalentPurchased = true;
int lastindex = index - 1;
if (lastindex >= 0)
{
TalentOption lastLatentOption = subTree.TalentOptionStages[lastindex];
hasTalentInLastTier = lastLatentOption.Talents.Any(HasTalent);
isLastTalentPurchased = lastLatentOption.Talents.Any(t => character.HasTalent(t.Identifier));
}
if (!hasTalentInLastTier)
{
return TalentTreeStageState.Locked;
}
bool hasPointsForNewTalent = character.Info.GetTotalTalentPoints() - selectedTalents.Count > 0;
if (hasPointsForNewTalent)
{
return isLastTalentPurchased ? TalentTreeStageState.Highlighted : TalentTreeStageState.Available;
}
return TalentTreeStageState.Locked;
bool HasTalent(TalentPrefab t)
{
return selectedTalents.Contains(t.Identifier);
}
}
public static bool IsViableTalentForCharacter(Character character, string talentIdentifier, IEnumerable<string> selectedTalents)
{
if (talentIdentifier == PlaceholderTalent) { return false; }
if (character?.Info?.Job.Prefab == null) { return false; }
if (character.Info.GetTotalTalentPoints() - selectedTalents.Count() <= 0) { return false; }
@@ -173,12 +230,16 @@ namespace Barotrauma
{
public string Identifier { get; }
public string DisplayName { get; }
public readonly List<TalentOption> TalentOptionStages = new List<TalentOption>();
public TalentSubTree(XElement subTreeElement)
{
Identifier = subTreeElement.GetAttributeString("identifier", "");
DisplayName = TextManager.Get("talenttree." + Identifier, returnNull: true) ?? Identifier;
foreach (XElement talentOptionsElement in subTreeElement.GetChildElements("talentoptions"))
{
TalentOptionStages.Add(new TalentOption(talentOptionsElement, Identifier));
@@ -196,6 +257,7 @@ namespace Barotrauma
foreach (XElement talentOptionElement in talentOptionsElement.GetChildElements("talentoption"))
{
string identifier = talentOptionElement.GetAttributeString("identifier", string.Empty);
if (!TalentPrefab.TalentPrefabs.ContainsKey(identifier))
{
DebugConsole.ThrowError($"Error in talent tree \"{debugIdentifier}\" - could not find a talent with the identifier \"{identifier}\".");

View File

@@ -12,16 +12,16 @@
public enum ActionType
{
Always, OnPicked, OnUse, OnSecondaryUse,
OnWearing, OnContaining, OnContained, OnNotContained,
OnActive, OnFailure, OnBroken,
OnFire, InWater, NotInWater,
OnImpact,
OnEating,
OnDamaged,
OnSevered,
OnProduceSpawned,
OnOpen, OnClose,
Always = 0, OnPicked = 1, OnUse = 2, OnSecondaryUse = 3,
OnWearing = 4, OnContaining = 5, OnContained = 6, OnNotContained = 7,
OnActive = 8, OnFailure = 9, OnBroken = 10,
OnFire = 11, InWater = 12, NotInWater = 13,
OnImpact = 14,
OnEating = 15,
OnDamaged = 16,
OnSevered = 17,
OnProduceSpawned = 18,
OnOpen = 19, OnClose = 20,
OnDeath = OnBroken,
OnSuccess,
OnAbility,
@@ -34,6 +34,7 @@
OnAttack,
OnAttackResult,
OnAttacked,
OnAttackedResult,
OnGainSkillPoint,
OnAllyGainSkillPoint,
OnRepairComplete,
@@ -57,7 +58,9 @@
OnGainMissionMoney,
OnItemDeconstructed,
OnItemDeconstructedMaterial,
OnItemDeconstructedRetainProbability,
OnStopTinkering,
OnItemPicked,
AfterSubmarineAttacked,
}
@@ -78,26 +81,37 @@
BuffDurationMultiplier,
DebuffDurationMultiplier,
MedicalItemEffectivenessMultiplier,
FlowResistance,
// Combat
AttackMultiplier,
TeamAttackMultiplier,
RangedAttackSpeed,
TurretAttackSpeed,
TurretPowerCostReduction,
TurretChargeSpeed,
MeleeAttackSpeed,
MeleeAttackMultiplier,
RangedAttackMultiplier,
RangedSpreadReduction,
// Utility
RepairSpeed,
DeconstructorSpeedMultiplier,
TinkeringDuration,
RepairToolStructureRepairMultiplier,
RepairToolStructureDamageMultiplier,
RepairToolDeattachTimeMultiplier,
MaxRepairConditionMultiplier,
IncreaseFabricationQuality,
GeneticMaterialRefineBonus,
GeneticMaterialTaintedProbabilityReductionOnCombine,
SkillGainSpeed,
// Misc
ReputationGainMultiplier,
MissionMoneyGainMultiplier,
ExperienceGainMultiplier,
MissionExperienceGainMultiplier,
// these should be deprecated and moved to their own implementation, no sense making them share space with stat values
Coathor,
Coauthor,
WarriorPoetMissionRuns,
WarriorPoetEnemiesKilled,
}
@@ -113,7 +127,8 @@
CanTinkerFabricatorsAndDeconstructors,
TinkeringPowersDevices,
GainSkillPastMaximum,
RetainExperienceForNewCharacter
RetainExperienceForNewCharacter,
AllowSecondOrderedTarget,
}
}

View File

@@ -306,7 +306,7 @@ namespace Barotrauma
case 0:
if (items.All(it => it.Removed || it.Condition <= 0.0f) &&
requireKill.All(c => c.Removed || c.IsDead) &&
requireKill.All(c => c.Removed || c.IsDead || (c.LockHands && c.Submarine == Submarine.MainSub)) &&
requireRescue.All(c => c.Submarine?.Info.Type == SubmarineType.Player))
{
State = 1;

View File

@@ -382,11 +382,11 @@ namespace Barotrauma
State = newState;
}
private bool CheckWinState() => !IsClient && (characters.All(m => !Survived(m)));
private bool CheckWinState() => !IsClient && characters.All(m => DeadOrCaptured(m));
private bool Survived(Character character)
private bool DeadOrCaptured(Character character)
{
return character != null && !character.Removed && !character.IsDead;
return character != null && !character.Removed && (character.IsDead || (character.LockHands && character.Submarine == Submarine.MainSub));
}
public override void End()

View File

@@ -247,8 +247,8 @@ namespace Barotrauma
public override void End()
{
var root = item.GetRootContainer() ?? item;
if (root.CurrentHull?.Submarine == null || (!root.CurrentHull.Submarine.AtEndExit && !root.CurrentHull.Submarine.AtStartExit) || item.Removed)
var root = item?.GetRootContainer() ?? item;
if (root?.CurrentHull?.Submarine == null || (!root.CurrentHull.Submarine.AtEndExit && !root.CurrentHull.Submarine.AtStartExit) || item.Removed)
{
return;
}

View File

@@ -92,5 +92,12 @@ namespace Barotrauma.Extensions
{
return MathUtils.NearlyEqual(v.X, other.X) && MathUtils.NearlyEqual(v.Y, other.Y);
}
public static Vector2 Pad(this Vector2 v, Vector4 padding)
{
v.X += padding.X + padding.Z;
v.Y += padding.Y + padding.W;
return v;
}
}
}

View File

@@ -60,7 +60,6 @@ namespace Barotrauma.Items.Components
}
}
public GeneticMaterial(Item item, XElement element)
: base(item, element)
{
@@ -85,7 +84,7 @@ namespace Barotrauma.Items.Components
public bool CanBeCombinedWith(GeneticMaterial otherGeneticMaterial)
{
return !tainted && otherGeneticMaterial != null && !otherGeneticMaterial.tainted;
return !tainted && otherGeneticMaterial != null && !otherGeneticMaterial.tainted && item.AllowDeconstruct && otherGeneticMaterial.item.AllowDeconstruct;
}
public override void Equip(Character character)
@@ -147,9 +146,12 @@ namespace Barotrauma.Items.Components
public bool Combine(GeneticMaterial otherGeneticMaterial, Character user)
{
if (!CanBeCombinedWith(otherGeneticMaterial)) { return false; }
float conditionIncrease = Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
conditionIncrease *= 1.0f + user.GetStatValue(StatTypes.GeneticMaterialRefineBonus);
if (item.Prefab == otherGeneticMaterial.item.Prefab)
{
item.Condition = Math.Max(item.Condition, otherGeneticMaterial.item.Condition) + Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
item.Condition = Math.Max(item.Condition, otherGeneticMaterial.item.Condition) + conditionIncrease;
float taintedProbability = GetTaintedProbabilityOnRefine(user);
if (taintedProbability >= Rand.Range(0.0f, 1.0f))
{
@@ -160,9 +162,14 @@ namespace Barotrauma.Items.Components
else
{
item.Condition = otherGeneticMaterial.Item.Condition =
(item.Condition + otherGeneticMaterial.Item.Condition) / 2.0f + Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
(item.Condition + otherGeneticMaterial.Item.Condition) / 2.0f + conditionIncrease;
item.OwnInventory?.TryPutItem(otherGeneticMaterial.Item, user: null);
MakeTainted();
item.AllowDeconstruct = false;
otherGeneticMaterial.Item.AllowDeconstruct = false;
if (GetTaintedProbabilityOnCombine(user) >= Rand.Range(0.0f, 1.0f))
{
MakeTainted();
}
return false;
}
}
@@ -172,7 +179,14 @@ namespace Barotrauma.Items.Components
if (user == null) { return 1.0f; }
float probability = MathHelper.Lerp(0.0f, 0.99f, item.Condition / 100.0f);
probability *= MathHelper.Lerp(1.0f, 0.25f, DegreeOfSuccess(user));
return probability;
return MathHelper.Clamp(probability, 0.0f, 1.0f);
}
private float GetTaintedProbabilityOnCombine(Character user)
{
if (user == null) { return 1.0f; }
float probability = 1.0f - user.GetStatValue(StatTypes.GeneticMaterialTaintedProbabilityReductionOnCombine);
return MathHelper.Clamp(probability, 0.0f, 1.0f);
}
private void MakeTainted()

View File

@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Abilities;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -73,12 +74,15 @@ namespace Barotrauma.Items.Components
if (PickingTime > 0.0f)
{
var abilityPickingTime = new AbilityValueItem(PickingTime, item.Prefab);
picker.CheckTalents(AbilityEffectType.OnItemPicked, abilityPickingTime);
if ((picker.PickingItem == null || picker.PickingItem == item) && PickingTime <= float.MaxValue)
{
#if SERVER
item.CreateServerEvent(this);
#endif
pickingCoroutine = CoroutineManager.StartCoroutine(WaitForPick(picker, PickingTime));
pickingCoroutine = CoroutineManager.StartCoroutine(WaitForPick(picker, abilityPickingTime.Value));
}
return false;
}

View File

@@ -523,7 +523,20 @@ namespace Barotrauma.Items.Components
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new ISerializableEntity[] { targetStructure });
FixStructureProjSpecific(user, deltaTime, targetStructure, sectionIndex);
targetStructure.AddDamage(sectionIndex, -StructureFixAmount * degreeOfSuccess, user);
float structureFixAmount = StructureFixAmount;
if (structureFixAmount >= 0f)
{
structureFixAmount *= 1 + user.GetStatValue(StatTypes.RepairToolStructureRepairMultiplier);
structureFixAmount *= 1 + item.GetQualityModifier(Quality.StatType.RepairToolStructureRepairMultiplier);
}
else
{
structureFixAmount *= 1 + user.GetStatValue(StatTypes.RepairToolStructureDamageMultiplier);
structureFixAmount *= 1 + item.GetQualityModifier(Quality.StatType.RepairToolStructureDamageMultiplier);
}
targetStructure.AddDamage(sectionIndex, -structureFixAmount * degreeOfSuccess, user);
//if the next section is small enough, apply the effect to it as well
//(to make it easier to fix a small "left-over" section)
@@ -535,7 +548,7 @@ namespace Barotrauma.Items.Components
(nextSectionLength > 0 && nextSectionLength < Structure.WallSectionSize * 0.3f))
{
//targetStructure.HighLightSection(sectionIndex + i);
targetStructure.AddDamage(sectionIndex + i, -StructureFixAmount * degreeOfSuccess);
targetStructure.AddDamage(sectionIndex + i, -structureFixAmount * degreeOfSuccess);
}
}
return true;
@@ -606,7 +619,8 @@ namespace Barotrauma.Items.Components
levelResource.requiredItems.Any() &&
levelResource.HasRequiredItems(user, addMessage: false))
{
levelResource.DeattachTimer += deltaTime;
float addedDetachTime = deltaTime * (1f + user.GetStatValue(StatTypes.RepairToolDeattachTimeMultiplier)) * item.GetQualityModifier(Quality.StatType.RepairToolDeattachTimeMultiplier);
levelResource.DeattachTimer += addedDetachTime;
#if CLIENT
Character.Controlled?.UpdateHUDProgressBar(
this,

View File

@@ -61,7 +61,7 @@ namespace Barotrauma.Items.Components
public int Capacity
{
get { return capacity; }
set { capacity = Math.Max(value, 1); }
set { capacity = Math.Max(value, 0); }
}
//how many items can be contained
@@ -86,15 +86,9 @@ namespace Barotrauma.Items.Components
}
}
#if DEBUG
[Editable]
#endif
[Serialize("0.0,0.0", false, description: "The position where the contained items get drawn at (offset from the upper left corner of the sprite in pixels).")]
public Vector2 ItemPos { get; set; }
#if DEBUG
[Editable]
#endif
[Serialize("0.0,0.0", false, description: "The interval at which the contained items are spaced apart from each other (in pixels).")]
public Vector2 ItemInterval { get; set; }
@@ -329,11 +323,24 @@ namespace Barotrauma.Items.Components
{
return slotRestrictions.Any(s => s.MatchesItem(item));
}
public bool CanBeContained(Item item, int index)
{
if (index < 0 || index >= capacity) { return false; }
return slotRestrictions[index].MatchesItem(item);
}
public bool CanBeContained(ItemPrefab itemPrefab)
{
return slotRestrictions.Any(s => s.MatchesItem(itemPrefab));
}
public bool CanBeContained(ItemPrefab itemPrefab, int index)
{
if (index < 0 || index >= capacity) { return false; }
return slotRestrictions[index].MatchesItem(itemPrefab);
}
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
public override void Update(float deltaTime, Camera cam)

View File

@@ -256,6 +256,17 @@ namespace Barotrauma.Items.Components
}
}
if (user != null && !user.Removed)
{
var deconstructItemRetainProbability = new AbilityValueItem(0f, targetItem.Prefab);
user.CheckTalents(AbilityEffectType.OnItemDeconstructedRetainProbability, deconstructItemRetainProbability);
if (deconstructItemRetainProbability.Value > Rand.Range(0f, 1f, Rand.RandSync.Unsynced))
{
allowRemove = false;
}
}
if (targetItem.AllowDeconstruct && allowRemove)
{
//drop all items that are inside the deconstructed item

View File

@@ -24,12 +24,6 @@ namespace Barotrauma.Items.Components
private Character user;
public float FabricationSpeedMultiplier
{
get;
set;
}
private ItemContainer inputContainer, outputContainer;
[Serialize(1.0f, true)]
@@ -249,7 +243,6 @@ namespace Barotrauma.Items.Components
var availableIngredients = GetAvailableIngredients();
if (fabricatedItem == null || !CanBeFabricated(fabricatedItem, availableIngredients, user))
{
FabricationSpeedMultiplier = 1f;
CancelFabricating();
return;
}
@@ -286,8 +279,7 @@ namespace Barotrauma.Items.Components
if (powerConsumption <= 0) { Voltage = 1.0f; }
timeUntilReady -= deltaTime * Math.Min(Voltage, 1.0f) * FabricationSpeedMultiplier;
FabricationSpeedMultiplier = 1f;
timeUntilReady -= deltaTime * Math.Min(Voltage, 1.0f);
UpdateRequiredTimeProjSpecific();
@@ -328,13 +320,21 @@ namespace Barotrauma.Items.Components
var fabricationValueItem = new AbilityValueItem(fabricatedItem.Amount, fabricatedItem.TargetItem);
if (user != null)
int quality = 0;
if (user?.Info != null)
{
foreach (Character character in Character.CharacterList.Where(c => c.TeamID == user.TeamID))
{
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationValueItem);
}
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationValueItem);
float floatQuality = 0.0f;
foreach (string tag in fabricatedItem.TargetItem.Tags)
{
floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag);
}
quality = (int)floatQuality;
}
var tempUser = user;
@@ -343,12 +343,20 @@ namespace Barotrauma.Items.Components
if (i < amountFittingContainer)
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
onSpawned: (Item spawnedItem) =>
{
onItemSpawned(spawnedItem, tempUser);
spawnedItem.Quality = quality;
});
}
else
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
onSpawned: (Item spawnedItem) =>
{
onItemSpawned(spawnedItem, tempUser);
spawnedItem.Quality = quality;
});
}
}

View File

@@ -116,6 +116,8 @@ namespace Barotrauma.Items.Components
item.CurrentHull.WaterVolume += currFlow;
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 0.5f; }
Voltage -= deltaTime;
}
public void InfectBallast(string identifier, bool allowMultiplePerShip = false)

View File

@@ -0,0 +1,72 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Quality : ItemComponent
{
public const int MaxQuality = 3;
public enum StatType
{
Condition,
ExplosionRadius,
ExplosionDamage,
RepairSpeed,
RepairToolStructureRepairMultiplier,
RepairToolStructureDamageMultiplier,
RepairToolDeattachTimeMultiplier,
// unused as of now
AttackMultiplier,
AttackSpeedMultiplier,
ForceDoorsOpenSpeedMultiplier,
RangedSpreadReduction,
ChargeSpeedMultiplier,
MovementSpeedMultiplier,
// generic stats to be used for various needs, declared just in case (localization)
EffectivenessMultiplier,
PowerOutputMultiplier,
ConsumptionReductionMultiplier,
}
private readonly Dictionary<StatType, float> statValues = new Dictionary<StatType, float>();
private int qualityLevel;
[Serialize(0, false)]
public int QualityLevel
{
get { return qualityLevel; }
set { qualityLevel = MathHelper.Clamp(value, 0, MaxQuality); }
}
public Quality(Item item, XElement element) : base(item, element)
{
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLower())
{
case "stattype":
case "statvalue":
case "qualitystat":
string statTypeString = subElement.GetAttributeString("stattype", "");
if (!Enum.TryParse(statTypeString, true, out StatType statType))
{
DebugConsole.ThrowError("Invalid stat type type \"" + statTypeString + "\" in item (" + item.prefab.Identifier + ")");
}
float statValue = subElement.GetAttributeFloat("value", 0f);
statValues.TryAdd(statType, statValue);
break;
}
}
}
public float GetValue(StatType statType)
{
if (!statValues.ContainsKey(statType)) { return 0.0f; }
return statValues[statType] * qualityLevel;
}
}
}

View File

@@ -35,6 +35,7 @@ namespace Barotrauma.Items.Components
public RemoteController(Item item, XElement element)
: base(item, element)
{
DrawHudWhenEquipped = false;
}
public override bool Select(Character character)

View File

@@ -11,7 +11,7 @@ namespace Barotrauma.Items.Components
{
partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable
{
private string header;
private readonly string header;
private float deteriorationTimer;
private float deteriorateAlwaysResetTimer;
@@ -182,6 +182,10 @@ namespace Barotrauma.Items.Components
if (Rand.Range(0.0f, 0.5f) < RepairDegreeOfSuccess(character, requiredSkills)) { return true; }
ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
if (bestRepairItem != null && bestRepairItem.GetComponent<Holdable>() is Holdable h)
{
h.ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
}
return false;
}
@@ -217,6 +221,11 @@ namespace Barotrauma.Items.Components
{
GameServer.Log($"{GameServer.CharacterLogName(character)} failed to {(action == FixActions.Sabotage ? "sabotage" : "repair")} {item.Name}", ServerLog.MessageType.ItemInteraction);
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, this, character.ID });
if (bestRepairItem != null && bestRepairItem.GetComponent<Holdable>() is Holdable h)
{
GameMain.Server?.CreateEntityEvent(bestRepairItem, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, h, character.ID });
}
return false;
}
@@ -243,7 +252,7 @@ namespace Barotrauma.Items.Components
}
return true;
Item GetBestRepairItem(Character character)
static Item GetBestRepairItem(Character character)
{
return character.HeldItems.OrderByDescending(i => i.Prefab.AddedRepairSpeedMultiplier).FirstOrDefault();
}
@@ -386,6 +395,9 @@ namespace Barotrauma.Items.Components
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
fixDuration /= 1 + CurrentFixer.GetStatValue(StatTypes.RepairSpeed) + currentRepairItem?.Prefab.AddedRepairSpeedMultiplier ?? 0f;
fixDuration /= 1 + item.GetQualityModifier(Quality.StatType.RepairSpeed);
item.MaxRepairConditionMultiplier = 1 + CurrentFixer.GetStatValue(StatTypes.MaxRepairConditionMultiplier);
if (currentFixerAction == FixActions.Repair)
{

View File

@@ -383,6 +383,10 @@ namespace Barotrauma.Items.Components
else
{
float chargeDeltaTime = tryingToCharge ? deltaTime : -deltaTime;
if (chargeDeltaTime > 0f && user != null)
{
chargeDeltaTime *= 1f + user.GetStatValue(StatTypes.TurretChargeSpeed);
}
currentChargeTime = Math.Clamp(currentChargeTime + chargeDeltaTime, 0f, MaxChargeTime);
}
tryingToCharge = false;

View File

@@ -285,7 +285,7 @@ namespace Barotrauma.Items.Components
int i = 0;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLower())
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
if (subElement.Attribute("texture") == null)

View File

@@ -97,13 +97,15 @@ namespace Barotrauma
private Dictionary<string, Connection> connections;
private List<Repairable> repairables;
private readonly List<Repairable> repairables;
private Queue<float> impactQueue = new Queue<float>();
private Quality qualityComponent;
private readonly Queue<float> impactQueue = new Queue<float>();
//a dictionary containing lists of the status effects in all the components of the item
private bool[] hasStatusEffectsOfType;
private Dictionary<ActionType, List<StatusEffect>> statusEffectLists;
private readonly bool[] hasStatusEffectsOfType;
private readonly Dictionary<ActionType, List<StatusEffect>> statusEffectLists;
public Dictionary<string, SerializableProperty> SerializableProperties { get; protected set; }
@@ -447,7 +449,7 @@ namespace Barotrauma
}
public bool IsFullCondition => MathUtils.NearlyEqual(Condition, MaxCondition);
public float MaxCondition => Prefab.Health * healthMultiplier;
public float MaxCondition => Prefab.Health * healthMultiplier * maxRepairConditionMultiplier * (1.0f + GetQualityModifier(Items.Components.Quality.StatType.Condition));
public float ConditionPercentage => MathUtils.Percentage(Condition, MaxCondition);
private float offsetOnSelectedMultiplier = 1.0f;
@@ -465,12 +467,18 @@ namespace Barotrauma
public float HealthMultiplier
{
get => healthMultiplier;
set
{
healthMultiplier = value;
}
set { healthMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity); }
}
private float maxRepairConditionMultiplier = 1.0f;
[Serialize(1.0f, true)]
public float MaxRepairConditionMultiplier
{
get => maxRepairConditionMultiplier;
set { maxRepairConditionMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity); }
}
//the default value should be Prefab.Health, but because we can't use it in the attribute,
//we'll just use NaN (which does nothing) and set the default value in the constructor/load
[Serialize(float.NaN, false), Editable]
@@ -618,6 +626,21 @@ namespace Barotrauma
get { return Prefab.UseInHealthInterface; }
}
public int Quality
{
get
{
return qualityComponent?.QualityLevel ?? 0;
}
set
{
if (qualityComponent != null)
{
qualityComponent.QualityLevel = value;
}
}
}
public bool InWater
{
get
@@ -933,6 +956,8 @@ namespace Barotrauma
ownInventory = itemContainer.Inventory;
}
qualityComponent = GetComponent<Quality>();
InitProjSpecific();
if (callOnItemLoaded)
@@ -1122,6 +1147,11 @@ namespace Barotrauma
if (!componentsByType.ContainsKey(typeof(T))) { return Enumerable.Empty<T>(); }
return components.Where(c => c is T).Cast<T>();
}
public float GetQualityModifier(Quality.StatType statType)
{
return GetComponent<Quality>()?.GetValue(statType) ?? 0.0f;
}
public void RemoveContained(Item contained)
{

View File

@@ -47,14 +47,14 @@ namespace Barotrauma
{
if (ItemOwnsSelf(item)) { return false; }
if (i < 0 || i >= slots.Length) { return false; }
if (!container.CanBeContained(item)) { return false; }
if (!container.CanBeContained(item, i)) { return false; }
return item != null && slots[i].CanBePut(item, ignoreCondition) && slots[i].ItemCount < container.GetMaxStackSize(i);
}
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition)
{
if (i < 0 || i >= slots.Length) { return false; }
if (!container.CanBeContained(itemPrefab)) { return false; }
if (!container.CanBeContained(itemPrefab, i)) { return false; }
return itemPrefab != null && slots[i].CanBePut(itemPrefab, condition) && slots[i].ItemCount < container.GetMaxStackSize(i);
}
@@ -62,7 +62,7 @@ namespace Barotrauma
{
if (itemPrefab == null) { return 0; }
if (i < 0 || i >= slots.Length) { return 0; }
if (!container.CanBeContained(itemPrefab)) { return 0; }
if (!container.CanBeContained(itemPrefab, i)) { return 0; }
return slots[i].HowManyCanBePut(itemPrefab, maxStackSize: Math.Min(itemPrefab.MaxStackSize, container.GetMaxStackSize(i)), condition);
}

View File

@@ -370,6 +370,9 @@ namespace Barotrauma
[Serialize(false, false, description: "Hides the condition bar displayed at the bottom of the inventory slot the item is in.")]
public bool HideConditionBar { get; set; }
[Serialize(false, false, description: "Hides the condition displayed in the item's tooltip.")]
public bool HideConditionInTooltip { get; set; }
//if true and the item has trigger areas defined, characters need to be within the trigger to interact with the item
//if false, trigger areas define areas that can be used to highlight the item
[Serialize(true, false)]
@@ -1164,6 +1167,8 @@ namespace Barotrauma
DefaultPrice ??= new PriceInfo(GetMinPrice() ?? 0, false);
}
HideConditionInTooltip = element.GetAttributeBool("hideconditionintooltip", HideConditionBar);
//backwards compatibility
if (categoryStr.Equals("Thalamus", StringComparison.OrdinalIgnoreCase))
{

View File

@@ -128,6 +128,11 @@ namespace Barotrauma
}
float displayRange = Attack.Range;
if (damageSource is Item sourceItem)
{
displayRange *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionRadius);
Attack.DamageMultiplier *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionDamage);
}
Vector2 cameraPos = GameMain.GameScreen.Cam.Position;
float cameraDist = Vector2.Distance(cameraPos, worldPosition) / 2.0f;
@@ -142,7 +147,7 @@ namespace Barotrauma
if (displayRange < 0.1f) { return; }
if (Attack.GetStructureDamage(1.0f) > 0.0f || Attack.GetLevelWallDamage(1.0f) > 0.0f)
if (!MathUtils.NearlyEqual(Attack.GetStructureDamage(1.0f), 0.0f) || !MathUtils.NearlyEqual(Attack.GetLevelWallDamage(1.0f), 0.0f))
{
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker);
}
@@ -211,9 +216,9 @@ namespace Barotrauma
float dist = Vector2.Distance(item.WorldPosition, worldPosition);
float itemRadius = item.body == null ? 0.0f : item.body.GetMaxExtent();
dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(itemRadius));
if (dist > Attack.Range) { continue; }
if (dist > displayRange) { continue; }
if (dist < Attack.Range * 0.5f && applyFireEffects && !item.FireProof && ignoreFireEffectsForTags.None(t => item.HasTag(t)))
if (dist < displayRange * 0.5f && applyFireEffects && !item.FireProof && ignoreFireEffectsForTags.None(t => item.HasTag(t)))
{
//don't apply OnFire effects if the item is inside a fireproof container
//(or if it's inside a container that's inside a fireproof container, etc)
@@ -240,7 +245,7 @@ namespace Barotrauma
if (item.Prefab.DamagedByExplosions && !item.Indestructible)
{
float distFactor = 1.0f - dist / Attack.Range;
float distFactor = 1.0f - dist / displayRange;
float damageAmount = Attack.GetItemDamage(1.0f) * item.Prefab.ExplosionDamageMultiplier;
Vector2 explosionPos = worldPosition;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
@@ -8,6 +9,7 @@ using System.Xml.Linq;
using Microsoft.Xna.Framework;
using File = Barotrauma.IO.File;
using FileStream = Barotrauma.IO.FileStream;
using Path = Barotrauma.IO.Path;
namespace Barotrauma
{
@@ -18,11 +20,12 @@ namespace Barotrauma
public static readonly XmlReaderSettings ReaderSettings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null
XmlResolver = null,
IgnoreWhitespace = true,
};
public static XmlReader CreateReader(System.IO.Stream stream)
=> XmlReader.Create(stream, ReaderSettings);
public static XmlReader CreateReader(System.IO.Stream stream, string baseUri = "")
=> XmlReader.Create(stream, ReaderSettings, baseUri);
public static XDocument TryLoadXml(System.IO.Stream stream)
{
@@ -52,8 +55,8 @@ namespace Barotrauma
{
ToolBox.IsProperFilenameCase(filePath);
using FileStream stream = File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
using XmlReader reader = CreateReader(stream);
doc = XDocument.Load(reader);
using XmlReader reader = CreateReader(stream, Path.GetFullPath(filePath));
doc = XDocument.Load(reader, LoadOptions.SetBaseUri);
}
catch (Exception e)
{
@@ -79,7 +82,7 @@ namespace Barotrauma
try
{
using FileStream stream = File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
using XmlReader reader = CreateReader(stream);
using XmlReader reader = CreateReader(stream, Path.GetFullPath(filePath));
doc = XDocument.Load(reader);
}
catch

View File

@@ -60,6 +60,8 @@ namespace Barotrauma
// Only used by conditionals targeting an item. By default, containers check the parent item. This allows you to check the grandparent instead.
public readonly bool TargetGrandParent;
public readonly bool TargetContainedItem;
// Remove this after refactoring
public static bool IsValid(XAttribute attribute)
{
@@ -112,6 +114,7 @@ namespace Barotrauma
TargetContainer = attribute.Parent.GetAttributeBool("targetcontainer", false);
TargetSelf = attribute.Parent.GetAttributeBool("targetself", false);
TargetGrandParent = attribute.Parent.GetAttributeBool("targetgrandparent", false);
TargetContainedItem = attribute.Parent.GetAttributeBool("targetcontaineditem", false);
if (!Enum.TryParse(AttributeName, true, out Type))
{
@@ -171,6 +174,22 @@ namespace Barotrauma
public bool Matches(ISerializableEntity target)
{
if (TargetContainedItem)
{
if (target is Item item)
{
return item.ContainedItems.Any(it => Matches(it));
}
else if (target is Items.Components.ItemComponent ic)
{
return ic.Item.ContainedItems.Any(it => Matches(it));
}
else if (target is Character character)
{
return character.Inventory != null && character.Inventory.AllItems.Any(it => Matches(it));
}
}
switch (Type)
{
case ConditionType.PropertyValue:

View File

@@ -315,7 +315,11 @@ namespace Barotrauma.IO
return System.IO.File.GetLastWriteTime(path);
}
public static FileStream Open(string path, System.IO.FileMode mode, System.IO.FileAccess access = System.IO.FileAccess.ReadWrite)
public static FileStream Open(
string path,
System.IO.FileMode mode,
System.IO.FileAccess access = System.IO.FileAccess.ReadWrite,
System.IO.FileShare? share = null)
{
switch (mode)
{
@@ -331,10 +335,12 @@ namespace Barotrauma.IO
}
break;
}
return new FileStream(path, System.IO.File.Open(path, mode,
access =
!Validation.CanWrite(path, false) ?
System.IO.FileAccess.Read :
access));
access;
var shareVal = share ?? (access == System.IO.FileAccess.Read ? System.IO.FileShare.Read : System.IO.FileShare.None);
return new FileStream(path, System.IO.File.Open(path, mode, access, shareVal));
}
public static FileStream OpenRead(string path)

View File

@@ -1,3 +1,32 @@
---------------------------------------------------------------------------------------------------------
v0.1500.3.0
---------------------------------------------------------------------------------------------------------
Additions and changes:
- More talents and talent-related items (all talent trees now functional and most of the talents implemented).
Changes:
- Ignore hidden afflictions when determining treatment suggestions to show in the health interface.
- Visualize leaks on the status monitor's hull condition tab (unstable only).
- Added "condition_out" output to outpost O2 generator (unstable only).
Fixes:
- Fixed crashing when reloading sprites or resetting to prefab in the sub editor.
- Fixed ability to combine unidentified genetic materials with other genetic materials (unstable only).
- Organ damage doesn't cause concussions (unstable only).
- Fixed talent menu being accessible if you leave it open and switch to a game mode where it shouldn't be accessible (unstable only).
- Fixed ability to contain items other than batteries in cargo scooter's battery slot (unstable only).
- Damaging the mudraptor beak given by mudraptor genes damages the head instead of torso, added damage protection to the beak (unstable only).
- Items that are set to be hidden in menus aren't shown in the status monitor's item finder (unstable only).
- Fixed status monitor's item finder not showing wearable items (unstable only).
- Fixed "in use by xxxx" warning being always visible when using a Reactor PDA (unstable only).
- Fixed Reactor PDA rendering over the command interface (unstable only).
- Fixed assault rifle crosshair being drawn when it's in the bag slot (unstable only).
- Fixed equip buttons not being drawn on equipped items that can only be put to other equip slots, but not on the non-limb slots (e.g. assault rifle).
Modding:
- Option to make property conditionals target contained items using the attribute targetcontaineditem="true".
---------------------------------------------------------------------------------------------------------
v0.1500.2.0
---------------------------------------------------------------------------------------------------------

View File

@@ -11,7 +11,7 @@
autorestart="false"
LevelDifficulty="20"
AllowedRandomMissionTypes="Random,Salvage,Monster,Cargo,Combat"
AllowedClientNameChars="32-33,38-46,48-57,65-90,91-91,93-93,95-122,192-255,384-591,1024-1279,19968-40959,13312-19903,131072-15043983,15043985-173791,173824-178207,178208-183983,63744-64255,194560-195103"
AllowedClientNameChars="32-33,38-46,48-57,65-90,91-91,93-93,95-122,192-255,384-591,1024-1279,19968-21327,21329-40959,13312-19903,131072-173791,173824-178207,178208-183983,63744-64255,194560-195103"
ServerMessage=""
tickrate="20"
randomizeseed="True"