Build 0.20.0.0
This commit is contained in:
@@ -442,8 +442,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb == null || limb.IsSevered || limb.ActiveSprite == null) { continue; }
|
||||
|
||||
if (limb == null || limb.IsSevered || limb.ActiveSprite == null || !limb.DoesFlip) { continue; }
|
||||
Vector2 spriteOrigin = limb.ActiveSprite.Origin;
|
||||
spriteOrigin.X = limb.ActiveSprite.SourceRect.Width - spriteOrigin.X;
|
||||
limb.ActiveSprite.Origin = spriteOrigin;
|
||||
@@ -468,7 +467,10 @@ namespace Barotrauma
|
||||
{
|
||||
var damageSound = character.GetSound(s => s.Type == CharacterSound.SoundType.Damage);
|
||||
float range = damageSound != null ? damageSound.Range * 2 : ConvertUnits.ToDisplayUnits(character.AnimController.Collider.GetSize().Length() * 10);
|
||||
SoundPlayer.PlayDamageSound(limbJoint.Params.BreakSound, 1.0f, limbJoint.LimbA.body.DrawPosition, range: range);
|
||||
if (!limbJoint.Params.BreakSound.IsNullOrEmpty() && !limbJoint.Params.BreakSound.Equals("none", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
SoundPlayer.PlayDamageSound(limbJoint.Params.BreakSound, 1.0f, limbJoint.LimbA.body.DrawPosition, range: range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,22 @@ namespace Barotrauma
|
||||
set => grainStrength = Math.Max(0, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used to set camera shake from status effects
|
||||
/// </summary>
|
||||
public float CameraShake
|
||||
{
|
||||
get { return Screen.Selected?.Cam?.Shake ?? 0.0f; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) { return; }
|
||||
if (Screen.Selected?.Cam != null)
|
||||
{
|
||||
Screen.Selected.Cam.Shake = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ParticleEmitter> bloodEmitters = new List<ParticleEmitter>();
|
||||
public IEnumerable<ParticleEmitter> BloodEmitters
|
||||
{
|
||||
|
||||
@@ -307,7 +307,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!brokenItem.IsInteractable(character)) { continue; }
|
||||
float alpha = GetDistanceBasedIconAlpha(brokenItem);
|
||||
if (alpha <= 0.0f) continue;
|
||||
if (alpha <= 0.0f) { continue; }
|
||||
GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUIStyle.BrokenIcon.Value.Sprite,
|
||||
Color.Lerp(GUIStyle.Red, GUIStyle.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
|
||||
}
|
||||
|
||||
@@ -501,7 +501,7 @@ namespace Barotrauma
|
||||
info?.ClearSavedStatValues(statType);
|
||||
for (int i = 0; i < savedStatValueCount; i++)
|
||||
{
|
||||
string statIdentifier = msg.ReadString();
|
||||
Identifier statIdentifier = msg.ReadIdentifier();
|
||||
float statValue = msg.ReadSingle();
|
||||
bool removeOnDeath = msg.ReadBoolean();
|
||||
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
|
||||
|
||||
@@ -1136,6 +1136,17 @@ namespace Barotrauma
|
||||
});
|
||||
AssignRelayToServer("debugdraw", false);
|
||||
|
||||
AssignOnExecute("debugdrawlocalization", (string[] args) =>
|
||||
{
|
||||
if (args.None() || !bool.TryParse(args[0], out bool state))
|
||||
{
|
||||
state = !TextManager.DebugDraw;
|
||||
}
|
||||
TextManager.DebugDraw = state;
|
||||
NewMessage("Localization debug draw mode " + (TextManager.DebugDraw ? "enabled" : "disabled"), Color.White);
|
||||
});
|
||||
AssignRelayToServer("debugdraw", false);
|
||||
|
||||
AssignOnExecute("togglevoicechatfilters", (string[] args) =>
|
||||
{
|
||||
if (args.None() || !bool.TryParse(args[0], out bool state))
|
||||
@@ -1695,6 +1706,8 @@ namespace Barotrauma
|
||||
config.Language = language;
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
}
|
||||
|
||||
HashSet<string> missingTexts = new HashSet<string>();
|
||||
|
||||
//key = text tag, value = list of languages the tag is missing from
|
||||
Dictionary<Identifier, HashSet<LanguageIdentifier>> missingTags = new Dictionary<Identifier, HashSet<LanguageIdentifier>>();
|
||||
@@ -1755,20 +1768,38 @@ namespace Barotrauma
|
||||
|
||||
foreach (Type itemComponentType in typeof(ItemComponent).Assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(ItemComponent))))
|
||||
{
|
||||
foreach (var property in itemComponentType.GetProperties())
|
||||
checkSerializableEntityType(itemComponentType);
|
||||
}
|
||||
checkSerializableEntityType(typeof(Item));
|
||||
checkSerializableEntityType(typeof(Hull));
|
||||
checkSerializableEntityType(typeof(Structure));
|
||||
|
||||
void checkSerializableEntityType(Type t)
|
||||
{
|
||||
foreach (var property in t.GetProperties())
|
||||
{
|
||||
if (!property.IsDefined(typeof(InGameEditable), false)) { continue; }
|
||||
if (!property.IsDefined(typeof(Editable), false)) { continue; }
|
||||
|
||||
string propertyTag = $"{property.DeclaringType.Name}.{property.Name}";
|
||||
|
||||
addIfMissingAll(language,
|
||||
if (addIfMissingAll(language,
|
||||
propertyTag.ToIdentifier(),
|
||||
property.Name.ToIdentifier(),
|
||||
$"sp.{propertyTag}.name".ToIdentifier());
|
||||
$"sp.{property.Name}.name".ToIdentifier(),
|
||||
$"sp.{propertyTag}.name".ToIdentifier()) && language == "English".ToLanguageIdentifier())
|
||||
{
|
||||
missingTexts.Add($"<sp.{propertyTag.ToLower()}.name>{property.Name.FormatCamelCaseWithSpaces()}</sp.{propertyTag.ToLower()}.name>");
|
||||
}
|
||||
|
||||
addIfMissingAll(language,
|
||||
var description = (property.GetCustomAttributes(true).First(a => a is Serialize) as Serialize).Description;
|
||||
|
||||
if (addIfMissingAll(language,
|
||||
$"sp.{propertyTag}.description".ToIdentifier(),
|
||||
$"{property.Name.ToIdentifier()}.description".ToIdentifier());
|
||||
$"sp.{property.Name}.description".ToIdentifier(),
|
||||
$"{property.Name.ToIdentifier()}.description".ToIdentifier()) && language == "English".ToLanguageIdentifier())
|
||||
{
|
||||
missingTexts.Add($"<sp.{propertyTag.ToLower()}.description>{description}</sp.{propertyTag.ToLower()}.description>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1889,6 +1920,23 @@ namespace Barotrauma
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
SwapLanguage(TextManager.DefaultLanguage);
|
||||
|
||||
if (missingTexts.Any())
|
||||
{
|
||||
ShowQuestionPrompt("Dump the property names and descriptions missing from English to a new xml file? Y/N",
|
||||
(option) =>
|
||||
{
|
||||
if (option.ToLowerInvariant() == "y")
|
||||
{
|
||||
string path = "newtexts.txt";
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(path, missingTexts);
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(path));
|
||||
SwapLanguage(TextManager.DefaultLanguage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void addIfMissing(Identifier tag, LanguageIdentifier language)
|
||||
{
|
||||
if (!tags[language].Contains(tag))
|
||||
@@ -1897,15 +1945,90 @@ namespace Barotrauma
|
||||
missingTags[tag].Add(language);
|
||||
}
|
||||
}
|
||||
void addIfMissingAll(LanguageIdentifier language, params Identifier[] potentialTags)
|
||||
bool addIfMissingAll(LanguageIdentifier language, params Identifier[] potentialTags)
|
||||
{
|
||||
if (!potentialTags.Any(t => tags[language].Contains(t)))
|
||||
{
|
||||
var tag = potentialTags.First();
|
||||
if (!missingTags.ContainsKey(tag)) { missingTags[tag] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[tag].Add(language);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
commands.Add(new Command("checkduplicateloca", "", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
ThrowError("Please specify a file path.");
|
||||
return;
|
||||
}
|
||||
XDocument doc1 = XMLExtensions.TryLoadXml(args[0]);
|
||||
if (doc1?.Root == null)
|
||||
{
|
||||
ThrowError($"Could not load the file \"{args[0]}\"");
|
||||
return;
|
||||
}
|
||||
List<(string tag, string text)> texts = new List<(string tag, string text)>();
|
||||
|
||||
bool duplicatesFound = false;
|
||||
foreach (XElement element in doc1.Root.Elements())
|
||||
{
|
||||
string tag = element.Name.ToString();
|
||||
string text = element.ElementInnerText();
|
||||
if (texts.Any(t => t.tag == tag))
|
||||
{
|
||||
ThrowError($"Duplicate tag \"{tag}\".");
|
||||
duplicatesFound = true;
|
||||
}
|
||||
}
|
||||
if (duplicatesFound)
|
||||
{
|
||||
ThrowError($"Aborting, please fix duplicate tags in the file and try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (XElement element in doc1.Root.Elements())
|
||||
{
|
||||
string tag = element.Name.ToString();
|
||||
string text = element.ElementInnerText();
|
||||
if (texts.Any(t => t.text == text))
|
||||
{
|
||||
if (tag.StartsWith("sp."))
|
||||
{
|
||||
string[] split = tag.Split('.');
|
||||
if (split.Length > 3)
|
||||
{
|
||||
texts.RemoveAll(t => t.text == text);
|
||||
string newTag = $"sp.{split[2]}.{split[3]}";
|
||||
texts.Add((newTag, text));
|
||||
NewMessage($"Duplicate text \"{tag}\", merging to \"{newTag}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage($"Duplicate text \"{tag}\", using existing one \"{texts.Find(t => t.text == text).tag}\".");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
texts.Add((tag, text));
|
||||
ThrowError($"Duplicate text \"{tag}\". Could not determine if the text can be merged with an existing one, please check it manually.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
texts.Add((tag, text));
|
||||
}
|
||||
}
|
||||
|
||||
string filePath = "uniquetexts.xml";
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(filePath, texts.Select(t => $"<{t.tag}>{t.text}</{t.tag}>"));
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
}));
|
||||
|
||||
commands.Add(new Command("comparelocafiles", "comparelocafiles [file1] [file2]", (string[] args) =>
|
||||
@@ -2585,99 +2708,6 @@ namespace Barotrauma
|
||||
}));
|
||||
#endif
|
||||
|
||||
commands.Add(new Command("cleanbuild", "", (string[] args) =>
|
||||
{
|
||||
/*GameSettings.CurrentConfig.MusicVolume = 0.5f;
|
||||
GameSettings.CurrentConfig.SoundVolume = 0.5f;
|
||||
GameSettings.CurrentConfig.DynamicRangeCompressionEnabled = true;
|
||||
GameSettings.CurrentConfig.VoipAttenuationEnabled = true;
|
||||
NewMessage("Music and sound volume set to 0.5", Color.Green);
|
||||
|
||||
GameSettings.CurrentConfig.GraphicsWidth = 0;
|
||||
GameSettings.CurrentConfig.GraphicsHeight = 0;
|
||||
GameSettings.CurrentConfig.WindowMode = WindowMode.BorderlessWindowed;
|
||||
NewMessage("Resolution set to 0 x 0 (screen resolution will be used)", Color.Green);
|
||||
NewMessage("Fullscreen enabled", Color.Green);
|
||||
|
||||
GameSettings.CurrentConfig.VerboseLogging = false;
|
||||
|
||||
if (GameSettings.CurrentConfig.MasterServerUrl != "http://www.undertowgames.com/baromaster")
|
||||
{
|
||||
ThrowError("MasterServerUrl \"" + GameSettings.CurrentConfig.MasterServerUrl + "\"!");
|
||||
}
|
||||
|
||||
GameSettings.SaveCurrentConfig();*/
|
||||
throw new NotImplementedException();
|
||||
#warning TODO: reimplement
|
||||
|
||||
var saveFiles = Barotrauma.IO.Directory.GetFiles(SaveUtil.SaveFolder);
|
||||
|
||||
foreach (string saveFile in saveFiles)
|
||||
{
|
||||
Barotrauma.IO.File.Delete(saveFile);
|
||||
NewMessage("Deleted " + saveFile, Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.Directory.Exists(Barotrauma.IO.Path.Combine(SaveUtil.SaveFolder, "temp")))
|
||||
{
|
||||
Barotrauma.IO.Directory.Delete(Barotrauma.IO.Path.Combine(SaveUtil.SaveFolder, "temp"), true);
|
||||
NewMessage("Deleted temp save folder", Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.Directory.Exists(ServerLog.SavePath))
|
||||
{
|
||||
var logFiles = Barotrauma.IO.Directory.GetFiles(ServerLog.SavePath);
|
||||
|
||||
foreach (string logFile in logFiles)
|
||||
{
|
||||
Barotrauma.IO.File.Delete(logFile);
|
||||
NewMessage("Deleted " + logFile, Color.Green);
|
||||
}
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.File.Exists("filelist.xml"))
|
||||
{
|
||||
Barotrauma.IO.File.Delete("filelist.xml");
|
||||
NewMessage("Deleted filelist", Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.File.Exists("Data/bannedplayers.txt"))
|
||||
{
|
||||
Barotrauma.IO.File.Delete("Data/bannedplayers.txt");
|
||||
NewMessage("Deleted bannedplayers.txt", Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.File.Exists("Submarines/TutorialSub.sub"))
|
||||
{
|
||||
Barotrauma.IO.File.Delete("Submarines/TutorialSub.sub");
|
||||
|
||||
NewMessage("Deleted TutorialSub from the submarine folder", Color.Green);
|
||||
}
|
||||
|
||||
/*if (Barotrauma.IO.File.Exists(GameServer.SettingsFile))
|
||||
{
|
||||
Barotrauma.IO.File.Delete(GameServer.SettingsFile);
|
||||
NewMessage("Deleted server settings", Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.File.Exists(GameServer.ClientPermissionsFile))
|
||||
{
|
||||
Barotrauma.IO.File.Delete(GameServer.ClientPermissionsFile);
|
||||
NewMessage("Deleted client permission file", Color.Green);
|
||||
}*/
|
||||
|
||||
if (Barotrauma.IO.File.Exists("crashreport.log"))
|
||||
{
|
||||
Barotrauma.IO.File.Delete("crashreport.log");
|
||||
NewMessage("Deleted crashreport.log", Color.Green);
|
||||
}
|
||||
|
||||
if (!Barotrauma.IO.File.Exists("Content/Map/TutorialSub.sub"))
|
||||
{
|
||||
ThrowError("TutorialSub.sub not found!");
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("reloadcorepackage", "", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 1)
|
||||
|
||||
@@ -769,7 +769,7 @@ namespace Barotrauma
|
||||
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
|
||||
{
|
||||
radio.Channel = channel;
|
||||
GameMain.Client?.CreateEntityEvent(radio.Item, new Item.ChangePropertyEventData(radio.SerializableProperties["channel".ToIdentifier()]));
|
||||
GameMain.Client?.CreateEntityEvent(radio.Item, new Item.ChangePropertyEventData(radio.SerializableProperties["channel".ToIdentifier()], radio));
|
||||
|
||||
if (setText)
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Barotrauma
|
||||
private PlayerBalanceElement? playerBalanceElement;
|
||||
|
||||
private List<CharacterInfo> PendingHires => campaign.Map?.CurrentLocation?.HireManager?.PendingHires;
|
||||
private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign(ClientPermissions.ManageHires);
|
||||
private bool HasPermission => CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageHires);
|
||||
|
||||
private Point resolutionWhenCreated;
|
||||
|
||||
|
||||
@@ -357,6 +357,17 @@ namespace Barotrauma
|
||||
string txt = directory;
|
||||
if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
|
||||
if (!txt.EndsWith("/")) { txt += "/"; }
|
||||
//get directory info
|
||||
DirectoryInfo dirInfo = new DirectoryInfo(directory);
|
||||
try
|
||||
{
|
||||
//this will throw an exception if the directory can't be opened
|
||||
Directory.GetDirectories(directory);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
|
||||
{
|
||||
UserData = ItemIsDirectory.Yes
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.CharacterEditor;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
@@ -50,6 +49,14 @@ namespace Barotrauma
|
||||
|
||||
static class GUI
|
||||
{
|
||||
// Controls where a line is drawn for given coords.
|
||||
public enum OutlinePosition
|
||||
{
|
||||
Default = 0, // Thickness is inside of top left and outside of bottom right coord
|
||||
Inside = 1, // Thickness is subtracted from the inside
|
||||
Centered = 2, // Thickness is centered on given coords
|
||||
Outside = 3, // Tickness is added to the outside
|
||||
}
|
||||
public static GUICanvas Canvas => GUICanvas.Instance;
|
||||
public static CursorState MouseCursor = CursorState.Default;
|
||||
|
||||
@@ -1605,6 +1612,54 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawRectangle(SpriteBatch sb, Vector2 position, Vector2 size, Vector2 origin, float rotation, Color clr, float depth = 0.0f, float thickness = 1, OutlinePosition outlinePos = OutlinePosition.Centered)
|
||||
{
|
||||
Vector2 topLeft = new Vector2(-origin.X, -origin.Y);
|
||||
Vector2 topRight = new Vector2(-origin.X + size.X, -origin.Y);
|
||||
Vector2 bottomLeft = new Vector2(-origin.X, -origin.Y + size.Y);
|
||||
Vector2 actualSize = size;
|
||||
|
||||
switch(outlinePos)
|
||||
{
|
||||
case OutlinePosition.Default:
|
||||
actualSize += new Vector2(thickness);
|
||||
break;
|
||||
case OutlinePosition.Centered:
|
||||
topLeft -= new Vector2(thickness * 0.5f);
|
||||
topRight -= new Vector2(thickness * 0.5f);
|
||||
bottomLeft -= new Vector2(thickness * 0.5f);
|
||||
actualSize += new Vector2(thickness);
|
||||
break;
|
||||
case OutlinePosition.Inside:
|
||||
topRight -= new Vector2(thickness, 0.0f);
|
||||
bottomLeft -= new Vector2(0.0f, thickness);
|
||||
break;
|
||||
case OutlinePosition.Outside:
|
||||
topLeft -= new Vector2(thickness);
|
||||
topRight -= new Vector2(0.0f, thickness);
|
||||
bottomLeft -= new Vector2(thickness, 0.0f);
|
||||
actualSize += new Vector2(thickness * 2.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
Matrix rotate = Matrix.CreateRotationZ(rotation);
|
||||
topLeft = Vector2.Transform(topLeft, rotate) + position;
|
||||
topRight = Vector2.Transform(topRight, rotate) + position;
|
||||
bottomLeft = Vector2.Transform(bottomLeft, rotate) + position;
|
||||
|
||||
Rectangle srcRect = new Rectangle(0, 0, 1, 1);
|
||||
sb.Draw(solidWhiteTexture, topLeft, srcRect, clr, rotation, Vector2.Zero, new Vector2(thickness, actualSize.Y), SpriteEffects.None, depth);
|
||||
sb.Draw(solidWhiteTexture, topLeft, srcRect, clr, rotation, Vector2.Zero, new Vector2(actualSize.X, thickness), SpriteEffects.None, depth);
|
||||
sb.Draw(solidWhiteTexture, topRight, srcRect, clr, rotation, Vector2.Zero, new Vector2(thickness, actualSize.Y), SpriteEffects.None, depth);
|
||||
sb.Draw(solidWhiteTexture, bottomLeft, srcRect, clr, rotation, Vector2.Zero, new Vector2(actualSize.X, thickness), SpriteEffects.None, depth);
|
||||
}
|
||||
|
||||
public static void DrawFilledRectangle(SpriteBatch sb, Vector2 position, Vector2 size, Vector2 pivot, float rotation, Color clr, float depth = 0.0f)
|
||||
{
|
||||
Rectangle srcRect = new Rectangle(0, 0, 1, 1);
|
||||
sb.Draw(solidWhiteTexture, position, srcRect, clr, rotation, (pivot/size), size, SpriteEffects.None, depth);
|
||||
}
|
||||
|
||||
public static void DrawFilledRectangle(SpriteBatch sb, RectangleF rect, Color clr, float depth = 0.0f)
|
||||
{
|
||||
DrawFilledRectangle(sb, rect.Location, rect.Size, clr, depth);
|
||||
|
||||
@@ -584,6 +584,13 @@ namespace Barotrauma
|
||||
{
|
||||
string textToShow = Censor ? censoredText : (Wrap ? wrappedText.Value : text.SanitizedValue);
|
||||
Color colorToShow = currentTextColor * (currentTextColor.A / 255.0f);
|
||||
if (TextManager.DebugDraw)
|
||||
{
|
||||
if (!text.NestedStr.Loaded || text.NestedStr.Language == LanguageIdentifier.None)
|
||||
{
|
||||
colorToShow = Color.Magenta;
|
||||
}
|
||||
}
|
||||
|
||||
if (Shadow)
|
||||
{
|
||||
|
||||
@@ -139,10 +139,10 @@ namespace Barotrauma
|
||||
return tab switch
|
||||
{
|
||||
StoreTab.Buy => true,
|
||||
StoreTab.Sell => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellInventoryItems),
|
||||
StoreTab.SellSub => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellSubItems),
|
||||
StoreTab.Sell => CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.SellInventoryItems),
|
||||
StoreTab.SellSub => CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.SellSubItems),
|
||||
_ => false,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdatePermissions()
|
||||
@@ -892,7 +892,7 @@ namespace Barotrauma
|
||||
|
||||
void CreateOrUpdateItemFrame(ItemPrefab itemPrefab, int quantity)
|
||||
{
|
||||
if (itemPrefab.CanBeBoughtFrom(ActiveStore, out PriceInfo priceInfo))
|
||||
if (itemPrefab.CanBeBoughtFrom(ActiveStore, out PriceInfo priceInfo) && itemPrefab.CanCharacterBuy())
|
||||
{
|
||||
bool isDailySpecial = ActiveStore.DailySpecials.Contains(itemPrefab);
|
||||
var itemFrame = isDailySpecial ?
|
||||
@@ -1995,11 +1995,23 @@ namespace Barotrauma
|
||||
int totalPrice = 0;
|
||||
foreach (var item in itemsToPurchase)
|
||||
{
|
||||
if (item?.ItemPrefab == null || !item.ItemPrefab.CanBeBoughtFrom(ActiveStore, out var priceInfo))
|
||||
if (item is null) { continue; }
|
||||
|
||||
if (item.ItemPrefab == null || !item.ItemPrefab.CanBeBoughtFrom(ActiveStore, out var priceInfo))
|
||||
{
|
||||
itemsToRemove.Add(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.ItemPrefab.DefaultPrice.RequiresUnlock)
|
||||
{
|
||||
if (!CargoManager.HasUnlockedStoreItem(item.ItemPrefab))
|
||||
{
|
||||
itemsToRemove.Add(item);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
totalPrice += item.Quantity * ActiveStore.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo);
|
||||
}
|
||||
itemsToRemove.ForEach(i => itemsToPurchase.Remove(i));
|
||||
|
||||
@@ -673,7 +673,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign?.PendingSubmarineSwitch == null)
|
||||
{
|
||||
return Submarine.MainSub.Info;
|
||||
return Submarine.MainSub?.Info;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Barotrauma
|
||||
private List<CharacterTeamType> teamIDs;
|
||||
private const string inLobbyString = "\u2022 \u2022 \u2022";
|
||||
|
||||
private GUIFrame pendingChangesFrame = null;
|
||||
public static GUIFrame PendingChangesFrame = null;
|
||||
|
||||
public static Color OwnCharacterBGColor = Color.Gold * 0.7f;
|
||||
private bool isTransferMenuOpen;
|
||||
@@ -44,6 +44,7 @@ namespace Barotrauma
|
||||
private float transferMenuOpenState;
|
||||
private bool transferMenuStateCompleted;
|
||||
private readonly HashSet<Identifier> registeredEvents = new HashSet<Identifier>();
|
||||
private readonly TalentMenu talentMenu = new TalentMenu();
|
||||
|
||||
private class LinkedGUI
|
||||
{
|
||||
@@ -206,14 +207,10 @@ namespace Barotrauma
|
||||
transferMenuButton.RectTransform.AbsoluteOffset = new Point(0, -pos - transferMenu.Rect.Height);
|
||||
}
|
||||
GameSession.UpdateTalentNotificationIndicator(talentPointNotification);
|
||||
if (Character.Controlled?.Info is { } characterInfo && talentResetButton != null && talentApplyButton != null)
|
||||
|
||||
if (SelectedTab is InfoFrameTab.Talents)
|
||||
{
|
||||
int talentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
|
||||
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
|
||||
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
talentApplyButton.Flash(GUIStyle.Orange);
|
||||
}
|
||||
talentMenu?.Update();
|
||||
}
|
||||
|
||||
if (SelectedTab != InfoFrameTab.Crew) { return; }
|
||||
@@ -251,6 +248,10 @@ namespace Barotrauma
|
||||
{
|
||||
infoFrame?.AddToGUIUpdateList();
|
||||
NetLobbyScreen.JobInfoFrame?.AddToGUIUpdateList();
|
||||
if (SelectedTab is InfoFrameTab.Talents)
|
||||
{
|
||||
talentMenu?.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnRoundEnded()
|
||||
@@ -325,11 +326,11 @@ namespace Barotrauma
|
||||
AbsoluteOffset = new Point(contentFrame.Rect.X, contentFrame.Rect.Bottom + GUI.IntScale(8))
|
||||
}, style: null);
|
||||
|
||||
pendingChangesFrame = new GUIFrame(new RectTransform(Vector2.One, bottomDisclaimerFrame.RectTransform, Anchor.Center), style: null);
|
||||
PendingChangesFrame = new GUIFrame(new RectTransform(Vector2.One, bottomDisclaimerFrame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
if (GameMain.NetLobbyScreen?.CampaignCharacterDiscarded ?? false)
|
||||
{
|
||||
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
|
||||
NetLobbyScreen.CreateChangesPendingFrame(PendingChangesFrame);
|
||||
}
|
||||
|
||||
SetBalanceText(balanceText, campaignMode.Bank.Balance);
|
||||
@@ -403,7 +404,7 @@ namespace Barotrauma
|
||||
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
|
||||
break;
|
||||
case InfoFrameTab.Talents:
|
||||
CreateCharacterInfo(infoFrameHolder);
|
||||
talentMenu.CreateGUI(infoFrameHolder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1774,370 +1775,10 @@ namespace Barotrauma
|
||||
sub.Info.CreateSpecsWindow(specsListBox, GUIStyle.Font, includeTitle: false, includeClass: false, includeDescription: true);
|
||||
}
|
||||
}
|
||||
private Color unselectedColor = new Color(240, 255, 255, 225);
|
||||
private Color unselectableColor = new Color(100, 100, 100, 225);
|
||||
private Color pressedColor = new Color(60, 60, 60, 225);
|
||||
|
||||
private readonly List<(GUIButton button, GUIComponent icon)> talentButtons = new List<(GUIButton button, GUIComponent icon)>();
|
||||
private readonly List<(Identifier talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)> talentCornerIcons = new List<(Identifier talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)>();
|
||||
private List<Identifier> selectedTalents = new List<Identifier>();
|
||||
|
||||
private GUITextBlock experienceText;
|
||||
private GUIProgressBar experienceBar;
|
||||
private GUITextBlock talentPointText;
|
||||
private GUIListBox skillListBox;
|
||||
|
||||
private GUIButton talentApplyButton,
|
||||
talentResetButton;
|
||||
|
||||
private GUIImage talentPointNotification;
|
||||
|
||||
private readonly ImmutableDictionary<TalentTree.TalentTreeStageState, GUIComponentStyle> talentStageStyles = new Dictionary<TalentTree.TalentTreeStageState, GUIComponentStyle>
|
||||
{
|
||||
{ TalentTree.TalentTreeStageState.Invalid, GUIStyle.GetComponentStyle("TalentTreeLocked") },
|
||||
{ TalentTree.TalentTreeStageState.Locked, GUIStyle.GetComponentStyle("TalentTreeLocked") },
|
||||
{ TalentTree.TalentTreeStageState.Unlocked, GUIStyle.GetComponentStyle("TalentTreePurchased") },
|
||||
{ TalentTree.TalentTreeStageState.Available, GUIStyle.GetComponentStyle("TalentTreeUnlocked") },
|
||||
{ TalentTree.TalentTreeStageState.Highlighted, GUIStyle.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 CreateCharacterInfo(GUIFrame infoFrame)
|
||||
{
|
||||
infoFrame.ClearChildren();
|
||||
talentButtons.Clear();
|
||||
talentCornerIcons.Clear();
|
||||
|
||||
GUIFrame background = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
int padding = GUI.IntScale(15);
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(background.Rect.Width - padding, background.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
GUIFrame content = new GUIFrame(new RectTransform(new Vector2(0.98f), frame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
GUIFrame characterSettingsFrame = null;
|
||||
GUILayoutGroup characterLayout = null;
|
||||
if (!(GameMain.NetworkMember is null))
|
||||
{
|
||||
characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, frame.RectTransform), style: null) { Visible = false };
|
||||
characterLayout = new GUILayoutGroup(new RectTransform(Vector2.One, characterSettingsFrame.RectTransform));
|
||||
GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null);
|
||||
GUIFrame playerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), containerFrame.RectTransform, Anchor.Center), style: null);
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
|
||||
}
|
||||
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
CharacterInfo info = controlledCharacter?.Info ?? GameMain.Client?.CharacterInfo;
|
||||
if (info == null) { return; }
|
||||
|
||||
Job job = info.Job;
|
||||
|
||||
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), content.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(10),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUILayoutGroup topLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), contentLayout.RectTransform, Anchor.Center), isHorizontal: true);
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), topLayout.RectTransform), onDraw: (batch, component) =>
|
||||
{
|
||||
float posY = component.Rect.Center.Y - component.Rect.Width / 2;
|
||||
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.3f, 1f), topLayout.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
CanBeFocused = true
|
||||
};
|
||||
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
if (!info.OmitJobInMenus)
|
||||
{
|
||||
nameBlock.TextColor = job.Prefab.UIColor;
|
||||
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
|
||||
}
|
||||
|
||||
LocalizedString traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), info.PersonalityTrait.DisplayName);
|
||||
Vector2 traitSize = GUIStyle.SmallFont.MeasureString(traitString);
|
||||
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUIStyle.SmallFont);
|
||||
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
|
||||
|
||||
IEnumerable<TalentPrefab> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e));
|
||||
if (talentsOutsideTree.Count() > 0)
|
||||
{
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), nameLayout.RectTransform), style: null);
|
||||
|
||||
GUILayoutGroup extraTalentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), nameLayout.RectTransform), childAnchor: Anchor.TopCenter);
|
||||
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), extraTalentLayout.RectTransform, anchor: Anchor.Center), TextManager.Get("talentmenu.extratalents"), font: GUIStyle.SubHeadingFont);
|
||||
talentPointText.RectTransform.MaxSize = new Point(int.MaxValue, (int)talentPointText.TextSize.Y);
|
||||
|
||||
var extraTalentList = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.8f), extraTalentLayout.RectTransform, anchor: Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AutoHideScrollBar = false,
|
||||
ResizeContentToMakeSpaceForScrollBar = false
|
||||
};
|
||||
extraTalentList.ScrollBar.RectTransform.SetPosition(Anchor.BottomCenter, Pivot.TopCenter);
|
||||
extraTalentList.RectTransform.MinSize = new Point(0, GUI.IntScale(65));
|
||||
extraTalentLayout.Recalculate();
|
||||
extraTalentList.ForceLayoutRecalculation();
|
||||
|
||||
foreach (var extraTalent in talentsOutsideTree)
|
||||
{
|
||||
var img = new GUIImage(new RectTransform(new Point(extraTalentList.Content.Rect.Height), extraTalentList.Content.RectTransform), sprite: extraTalent.Icon, scaleToFit: true)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{extraTalent.DisplayName}‖color:end‖" + "\n\n" + extraTalent.Description),
|
||||
Color = GUIStyle.Green
|
||||
};
|
||||
img.RectTransform.SizeChanged += () =>
|
||||
{
|
||||
img.RectTransform.MaxSize = new Point(img.Rect.Height);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
GUILayoutGroup skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1f), topLayout.RectTransform), childAnchor: Anchor.TopRight)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUITextBlock skillBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillLayout.RectTransform), TextManager.Get("skills"), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
|
||||
CreateSkillList(controlledCharacter, info, skillListBox);
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1f, 1f), contentLayout.RectTransform), style: "HorizontalLine");
|
||||
|
||||
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.6f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
|
||||
|
||||
if (controlledCharacter == null)
|
||||
{
|
||||
talentTreeListBox.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TalentTree.JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
|
||||
|
||||
selectedTalents = info.GetUnlockedTalentsInTree().ToList();
|
||||
|
||||
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
|
||||
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: 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");
|
||||
subTreeNames.Add(new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUIStyle.SubHeadingFont, 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: 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")
|
||||
{
|
||||
Color = talentStageBackgroundColors[TalentTree.TalentTreeStageState.Locked]
|
||||
};
|
||||
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.2f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { MaxSize = new Point(16) }, style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = talentStageBackgroundColors[TalentTree.TalentTreeStageState.Locked]
|
||||
};
|
||||
|
||||
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
|
||||
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
|
||||
|
||||
if (subTree.TalentOptionStages.Length <= i) { continue; }
|
||||
|
||||
TalentOption talentOption = subTree.TalentOptionStages[i];
|
||||
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 (Identifier talentId in talentOption.TalentIdentifiers.OrderBy(t => t))
|
||||
{
|
||||
if (!TalentPrefab.TalentPrefabs.TryGet(talentId, out TalentPrefab talent)) { continue; }
|
||||
GUIFrame talentFrame = new GUIFrame(new RectTransform(Vector2.One, talentOptionLayoutGroup.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
GUIFrame croppedTalentFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: null);
|
||||
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, croppedTalentFrame.RectTransform, anchor: Anchor.Center), style: null)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖" + "\n\n" + talent.Description),
|
||||
UserData = talent.Identifier,
|
||||
PressedColor = pressedColor,
|
||||
Enabled = controlledCharacter != null,
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
// deselect other buttons in tier by removing their selected talents from pool
|
||||
foreach (GUIButton guiButton in talentOptionLayoutGroup.GetAllChildren<GUIButton>())
|
||||
{
|
||||
if (guiButton.UserData is Identifier otherTalentIdentifier && guiButton != button)
|
||||
{
|
||||
if (!controlledCharacter.HasTalent(otherTalentIdentifier))
|
||||
{
|
||||
selectedTalents.Remove(otherTalentIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
Identifier talentIdentifier = (Identifier)userData;
|
||||
|
||||
if (TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents))
|
||||
{
|
||||
if (!selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
selectedTalents.Add(talentIdentifier);
|
||||
}
|
||||
}
|
||||
else if (!controlledCharacter.HasTalent(talentIdentifier))
|
||||
{
|
||||
selectedTalents.Remove(talentIdentifier);
|
||||
}
|
||||
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = talentButton.DisabledColor = Color.Transparent;
|
||||
|
||||
GUIComponent iconImage;
|
||||
if (talent.Icon is null)
|
||||
{
|
||||
iconImage = new GUITextBlock(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), text: "???", font: GUIStyle.LargeFont, textAlignment: Alignment.Center, style: null)
|
||||
{
|
||||
OutlineColor = GUIStyle.Red,
|
||||
TextColor = GUIStyle.Red,
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
iconImage = new GUIImage(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
|
||||
{
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor * 0.5f,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
iconImage.Enabled = talentButton.Enabled;
|
||||
talentButtons.Add((talentButton, iconImage));
|
||||
}
|
||||
talentCornerIcons.Add((subTree.Identifier, i, cornerIcon, talentBackground, talentBackgroundHighlight));
|
||||
}
|
||||
}
|
||||
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
||||
|
||||
GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.01f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), bottomLayout.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: info.GetProgressTowardsNextLevel(), color: GUIStyle.Green)
|
||||
{
|
||||
IsHorizontal = true,
|
||||
};
|
||||
|
||||
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
Shadow = true,
|
||||
ToolTip = TextManager.Get("experiencetooltip")
|
||||
};
|
||||
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
|
||||
|
||||
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ResetTalentSelection
|
||||
};
|
||||
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ApplyTalentSelection,
|
||||
};
|
||||
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
|
||||
}
|
||||
|
||||
if (!(GameMain.NetworkMember is null))
|
||||
{
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
|
||||
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
|
||||
{
|
||||
IgnoreLayoutGroups = false
|
||||
};
|
||||
newCharacterBox.TextBlock.AutoScaleHorizontal = true;
|
||||
|
||||
newCharacterBox.OnClicked = (button, o) =>
|
||||
{
|
||||
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
|
||||
{
|
||||
newCharacterBox.Text = TextManager.Get("settings");
|
||||
if (pendingChangesFrame != null)
|
||||
{
|
||||
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
|
||||
}
|
||||
OpenMenu();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenMenu();
|
||||
return true;
|
||||
|
||||
void OpenMenu()
|
||||
{
|
||||
characterSettingsFrame!.Visible = true;
|
||||
content.Visible = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (!(characterLayout is null))
|
||||
{
|
||||
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomCenter);
|
||||
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
|
||||
{
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
|
||||
characterSettingsFrame!.Visible = false;
|
||||
content.Visible = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTalentInfo();
|
||||
}
|
||||
|
||||
private void CreateSkillList(Character character, CharacterInfo info, GUIListBox parent)
|
||||
public static void CreateSkillList(Character character, CharacterInfo info, GUIListBox parent)
|
||||
{
|
||||
parent.Content.ClearChildren();
|
||||
List<GUITextBlock> skillNames = new List<GUITextBlock>();
|
||||
@@ -2172,116 +1813,10 @@ namespace Barotrauma
|
||||
GUITextBlock.AutoScaleAndNormalize(skillNames);
|
||||
}
|
||||
|
||||
private void UpdateTalentInfo()
|
||||
{
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
if (controlledCharacter?.Info == null) { return; }
|
||||
|
||||
if (SelectedTab != InfoFrameTab.Talents) { return; }
|
||||
|
||||
bool unlockedAllTalents = controlledCharacter.HasUnlockedAllTalents();
|
||||
|
||||
if (unlockedAllTalents)
|
||||
{
|
||||
experienceText.Text = string.Empty;
|
||||
experienceBar.BarSize = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
experienceText.Text = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}";
|
||||
experienceBar.BarSize = controlledCharacter.Info.GetProgressTowardsNextLevel();
|
||||
}
|
||||
|
||||
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
|
||||
|
||||
string pointsLeft = controlledCharacter.Info.GetAvailableTalentPoints().ToString();
|
||||
|
||||
int talentCount = selectedTalents.Count - controlledCharacter.Info.GetUnlockedTalentsInTree().Count();
|
||||
|
||||
if (unlockedAllTalents)
|
||||
{
|
||||
talentPointText.SetRichText($"‖color:{XMLExtensions.ToStringHex(Color.Gray)}‖{TextManager.Get("talentmenu.alltalentsunlocked")}‖color:end‖");
|
||||
}
|
||||
else if (talentCount > 0)
|
||||
{
|
||||
string pointsUsed = $"‖color:{XMLExtensions.ColorToString(GUIStyle.Red)}‖{-talentCount}‖color:end‖";
|
||||
LocalizedString localizedString = TextManager.GetWithVariables("talentmenu.points.spending", ("[amount]", pointsLeft), ("[used]", 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)
|
||||
{
|
||||
Identifier talentIdentifier = (Identifier)talentButton.button.UserData;
|
||||
bool unselectable = !TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents) || controlledCharacter.HasTalent(talentIdentifier);
|
||||
Color newTalentColor = unselectable ? unselectableColor : unselectedColor;
|
||||
Color hoverColor = Color.White;
|
||||
|
||||
if (controlledCharacter.HasTalent(talentIdentifier))
|
||||
{
|
||||
newTalentColor = GUIStyle.Green;
|
||||
}
|
||||
else if (selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
newTalentColor = GUIStyle.Orange;
|
||||
hoverColor = Color.Lerp(GUIStyle.Orange, Color.White, 0.7f);
|
||||
}
|
||||
|
||||
talentButton.icon.Color = newTalentColor;
|
||||
talentButton.icon.HoverColor = hoverColor;
|
||||
}
|
||||
|
||||
CreateSkillList(controlledCharacter, controlledCharacter.Info, skillListBox);
|
||||
}
|
||||
|
||||
private void ApplyTalents(Character controlledCharacter)
|
||||
{
|
||||
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
|
||||
foreach (Identifier talent in selectedTalents)
|
||||
{
|
||||
controlledCharacter.GiveTalent(talent);
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(controlledCharacter, new Character.UpdateTalentsEventData());
|
||||
}
|
||||
}
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
UpdateTalentInfo();
|
||||
}
|
||||
|
||||
private bool ApplyTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
ApplyTalents(controlledCharacter);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ResetTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
if (controlledCharacter?.Info == null) { return false; }
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnExperienceChanged(Character character)
|
||||
{
|
||||
if (character != Character.Controlled) { return; }
|
||||
UpdateTalentInfo();
|
||||
talentMenu.UpdateTalentInfo();
|
||||
}
|
||||
|
||||
public void OnClose()
|
||||
|
||||
@@ -0,0 +1,680 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using static Barotrauma.TalentTree;
|
||||
using static Barotrauma.TalentTree.TalentTreeStageState;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal readonly record struct TalentButton(GUIButton Button,
|
||||
GUIComponent IconComponent,
|
||||
TalentPrefab Prefab)
|
||||
{
|
||||
public Identifier Identifier => Prefab.Identifier;
|
||||
}
|
||||
|
||||
internal readonly record struct TalentCornerIcon(Identifier TalentTree,
|
||||
int Index,
|
||||
GUIImage IconComponent,
|
||||
GUIFrame BackgroundComponent,
|
||||
GUIFrame GlowComponent);
|
||||
|
||||
internal readonly struct TalentTreeStyle
|
||||
{
|
||||
public readonly GUIComponentStyle ComponentStyle;
|
||||
public readonly Color Color;
|
||||
|
||||
public TalentTreeStyle(string componentStyle, Color color)
|
||||
{
|
||||
ComponentStyle = GUIStyle.GetComponentStyle(componentStyle);
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TalentMenu
|
||||
{
|
||||
private static readonly Color unselectedColor = new Color(240, 255, 255, 225),
|
||||
unselectableColor = new Color(100, 100, 100, 225),
|
||||
pressedColor = new Color(60, 60, 60, 225),
|
||||
lockedColor = new Color(48, 48, 48, 255),
|
||||
unlockedColor = new Color(24, 37, 31, 255),
|
||||
availableColor = new Color(50, 47, 33, 255);
|
||||
|
||||
private static readonly ImmutableDictionary<TalentTreeStageState, TalentTreeStyle> talentStageStyles =
|
||||
new Dictionary<TalentTreeStageState, TalentTreeStyle>
|
||||
{
|
||||
[Invalid] = new TalentTreeStyle("TalentTreeLocked", lockedColor),
|
||||
[Locked] = new TalentTreeStyle("TalentTreeLocked", lockedColor),
|
||||
[Unlocked] = new TalentTreeStyle("TalentTreePurchased", unlockedColor),
|
||||
[Available] = new TalentTreeStyle("TalentTreeUnlocked", availableColor),
|
||||
[Highlighted] = new TalentTreeStyle("TalentTreeAvailable", availableColor)
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private readonly HashSet<TalentButton> talentButtons = new HashSet<TalentButton>();
|
||||
private readonly HashSet<GUIComponent> showCaseTalentFrames = new HashSet<GUIComponent>();
|
||||
private readonly HashSet<TalentCornerIcon> talentCornerIcons = new HashSet<TalentCornerIcon>();
|
||||
private HashSet<Identifier> selectedTalents = new HashSet<Identifier>();
|
||||
|
||||
private GUIListBox? skillListBox;
|
||||
private GUITextBlock? talentPointText;
|
||||
private GUIProgressBar? experienceBar;
|
||||
private GUITextBlock? experienceText;
|
||||
private GUILayoutGroup? skillLayout;
|
||||
|
||||
private GUIButton? talentApplyButton,
|
||||
talentResetButton;
|
||||
|
||||
public void CreateGUI(GUIFrame parent)
|
||||
{
|
||||
parent.ClearChildren();
|
||||
talentButtons.Clear();
|
||||
talentCornerIcons.Clear();
|
||||
showCaseTalentFrames.Clear();
|
||||
|
||||
GUIFrame background = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
int padding = GUI.IntScale(15);
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(background.Rect.Width - padding, background.Rect.Height - padding), parent.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
GUIFrame content = new GUIFrame(new RectTransform(new Vector2(0.98f), frame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(Vector2.One, content.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(10),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
Character? controlledCharacter = Character.Controlled;
|
||||
CharacterInfo? info = controlledCharacter?.Info ?? GameMain.Client?.CharacterInfo;
|
||||
if (info is null) { return; }
|
||||
|
||||
CreateStatPanel(contentLayout, info);
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1f, 1f), contentLayout.RectTransform), style: "HorizontalLine");
|
||||
|
||||
if (JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree? talentTree))
|
||||
{
|
||||
CreateTalentMenu(contentLayout, info, talentTree!);
|
||||
}
|
||||
|
||||
CreateFooter(contentLayout, info);
|
||||
UpdateTalentInfo();
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
CreateMultiplayerCharacterSettings(frame, content);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMultiplayerCharacterSettings(GUIComponent parent, GUIComponent content)
|
||||
{
|
||||
if (skillLayout is null) { return; }
|
||||
|
||||
GUIFrame characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null) { Visible = false };
|
||||
GUILayoutGroup characterLayout = new GUILayoutGroup(new RectTransform(Vector2.One, characterSettingsFrame.RectTransform));
|
||||
GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null);
|
||||
GUIFrame playerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), containerFrame.RectTransform, Anchor.Center), style: null);
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
|
||||
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
|
||||
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
|
||||
{
|
||||
IgnoreLayoutGroups = false,
|
||||
TextBlock =
|
||||
{
|
||||
AutoScaleHorizontal = true
|
||||
}
|
||||
};
|
||||
|
||||
newCharacterBox.OnClicked = (button, o) =>
|
||||
{
|
||||
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
|
||||
{
|
||||
newCharacterBox.Text = TextManager.Get("settings");
|
||||
if (TabMenu.PendingChangesFrame != null)
|
||||
{
|
||||
NetLobbyScreen.CreateChangesPendingFrame(TabMenu.PendingChangesFrame);
|
||||
}
|
||||
|
||||
OpenMenu();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenMenu();
|
||||
return true;
|
||||
|
||||
void OpenMenu()
|
||||
{
|
||||
characterSettingsFrame!.Visible = true;
|
||||
content.Visible = false;
|
||||
}
|
||||
};
|
||||
|
||||
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomCenter);
|
||||
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
|
||||
{
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
|
||||
characterSettingsFrame!.Visible = false;
|
||||
content.Visible = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void CreateStatPanel(GUIComponent parent, CharacterInfo info)
|
||||
{
|
||||
Job job = info.Job;
|
||||
|
||||
GUILayoutGroup topLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), parent.RectTransform, Anchor.Center), isHorizontal: true);
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), topLayout.RectTransform), onDraw: (batch, component) =>
|
||||
{
|
||||
float posY = component.Rect.Center.Y - component.Rect.Width / 2;
|
||||
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.3f, 1f), topLayout.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
CanBeFocused = true
|
||||
};
|
||||
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
if (!info.OmitJobInMenus)
|
||||
{
|
||||
nameBlock.TextColor = job.Prefab.UIColor;
|
||||
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
|
||||
}
|
||||
|
||||
if (info.PersonalityTrait != null)
|
||||
{
|
||||
LocalizedString traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), info.PersonalityTrait.DisplayName);
|
||||
Vector2 traitSize = GUIStyle.SmallFont.MeasureString(traitString);
|
||||
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUIStyle.SmallFont);
|
||||
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
|
||||
}
|
||||
|
||||
ImmutableHashSet<TalentPrefab?> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(static e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e)).ToImmutableHashSet();
|
||||
if (talentsOutsideTree.Any())
|
||||
{
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), nameLayout.RectTransform), style: null);
|
||||
|
||||
GUILayoutGroup extraTalentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), nameLayout.RectTransform), childAnchor: Anchor.TopCenter);
|
||||
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), extraTalentLayout.RectTransform, anchor: Anchor.Center), TextManager.Get("talentmenu.extratalents"), font: GUIStyle.SubHeadingFont);
|
||||
talentPointText.RectTransform.MaxSize = new Point(int.MaxValue, (int)talentPointText.TextSize.Y);
|
||||
|
||||
var extraTalentList = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.8f), extraTalentLayout.RectTransform, anchor: Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AutoHideScrollBar = false,
|
||||
ResizeContentToMakeSpaceForScrollBar = false
|
||||
};
|
||||
extraTalentList.ScrollBar.RectTransform.SetPosition(Anchor.BottomCenter, Pivot.TopCenter);
|
||||
extraTalentList.RectTransform.MinSize = new Point(0, GUI.IntScale(65));
|
||||
extraTalentLayout.Recalculate();
|
||||
extraTalentList.ForceLayoutRecalculation();
|
||||
|
||||
foreach (var extraTalent in talentsOutsideTree)
|
||||
{
|
||||
if (extraTalent is null) { continue; }
|
||||
GUIImage talentImg = new GUIImage(new RectTransform(Vector2.One, extraTalentList.Content.RectTransform, scaleBasis: ScaleBasis.BothHeight), sprite: extraTalent.Icon, scaleToFit: true)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{extraTalent.DisplayName}‖color:end‖" + "\n\n" + extraTalent.Description),
|
||||
Color = GUIStyle.Green
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1f), topLayout.RectTransform), childAnchor: Anchor.TopRight)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUITextBlock skillBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillLayout.RectTransform), TextManager.Get("skills"), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
|
||||
TabMenu.CreateSkillList(info.Character, info, skillListBox);
|
||||
}
|
||||
|
||||
private void CreateTalentMenu(GUIComponent parent, CharacterInfo info, TalentTree tree)
|
||||
{
|
||||
GUIListBox mainList = new GUIListBox(new RectTransform(new Vector2(1f, 0.9f), parent.RectTransform, anchor: Anchor.TopCenter));
|
||||
|
||||
selectedTalents = info.GetUnlockedTalentsInTree().ToHashSet();
|
||||
|
||||
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
|
||||
foreach (var subTree in tree.TalentSubTrees)
|
||||
{
|
||||
GUIListBox talentList;
|
||||
GUIComponent talentParent;
|
||||
Vector2 treeSize;
|
||||
switch (subTree.Type)
|
||||
{
|
||||
case TalentTreeType.Primary:
|
||||
talentList = mainList;
|
||||
treeSize = new Vector2(1f, 0.5f);
|
||||
break;
|
||||
case TalentTreeType.Specialization:
|
||||
talentList = GetSpecializationList();
|
||||
treeSize = new Vector2(0.333f, 1f);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Invalid TalentTreeType \"{subTree.Type}\"");
|
||||
}
|
||||
talentParent = talentList.Content;
|
||||
|
||||
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(treeSize, talentParent.RectTransform), isHorizontal: false, childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
if (subTree.Type != TalentTreeType.Primary)
|
||||
{
|
||||
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.05f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter)
|
||||
{ MinSize = new Point(0, GUI.IntScale(30)) }, style: null);
|
||||
subtreeTitleFrame.RectTransform.IsFixedSize = true;
|
||||
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");
|
||||
subTreeNames.Add(new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center));
|
||||
}
|
||||
|
||||
int optionAmount = subTree.TalentOptionStages.Length;
|
||||
for (int i = 0; i < optionAmount; i++)
|
||||
{
|
||||
TalentOption option = subTree.TalentOptionStages[i];
|
||||
CreateTalentOption(subTreeLayoutGroup, subTree, i, option, info);
|
||||
}
|
||||
subTreeLayoutGroup.RectTransform.Resize(new Point(subTreeLayoutGroup.Rect.Width,
|
||||
subTreeLayoutGroup.Children.Sum(c => c.Rect.Height + subTreeLayoutGroup.AbsoluteSpacing)));
|
||||
subTreeLayoutGroup.RectTransform.MinSize = new Point(subTreeLayoutGroup.Rect.Width, subTreeLayoutGroup.Rect.Height);
|
||||
subTreeLayoutGroup.Recalculate();
|
||||
if (subTree.Type == TalentTreeType.Specialization)
|
||||
{
|
||||
talentList.RectTransform.Resize(new Point(talentList.Rect.Width, Math.Max(subTreeLayoutGroup.Rect.Height, talentList.Rect.Height)));
|
||||
talentList.RectTransform.MinSize = new Point(0, talentList.Rect.Height);
|
||||
}
|
||||
}
|
||||
|
||||
var specializationList = GetSpecializationList();
|
||||
GetSpecializationList().RectTransform.Resize(new Point(specializationList.Content.Children.Sum(c => c.Rect.Width), specializationList.Rect.Height));
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
||||
|
||||
GUIListBox GetSpecializationList()
|
||||
{
|
||||
if (mainList.Content.Children.LastOrDefault() is GUIListBox specializationList)
|
||||
{
|
||||
return specializationList;
|
||||
}
|
||||
|
||||
GUIListBox newSpecializationList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), mainList.Content.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
|
||||
return newSpecializationList;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTalentOption(GUIComponent parent, TalentSubTree subTree, int index, TalentOption talentOption, CharacterInfo info)
|
||||
{
|
||||
int elementPadding = GUI.IntScale(8);
|
||||
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.01f), parent.RectTransform, anchor: Anchor.TopCenter)
|
||||
{ MinSize = new Point(0, GUI.IntScale(65)) }, 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")
|
||||
{
|
||||
Color = talentStageStyles[Locked].Color
|
||||
};
|
||||
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.2f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { MaxSize = new Point(16) }, style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = talentStageStyles[Locked].Color
|
||||
};
|
||||
|
||||
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
|
||||
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
|
||||
|
||||
GUILayoutGroup talentOptionCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 0.9f), talentOptionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
|
||||
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, talentOptionCenterGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
|
||||
HashSet<Identifier> talentOptionIdentifiers = talentOption.TalentIdentifiers.OrderBy(static t => t).ToHashSet();
|
||||
bool hasShowcase = talentOption.ShowcaseTalent.TryUnwrap(out Identifier showcaseTalentIdentifier);
|
||||
GUILayoutGroup showcaseLayout = talentOptionLayoutGroup;
|
||||
|
||||
if (hasShowcase)
|
||||
{
|
||||
talentOptionIdentifiers.Add(showcaseTalentIdentifier);
|
||||
Point parentSize = talentBackground.RectTransform.NonScaledSize;
|
||||
GUIFrame showCaseFrame = new GUIFrame(new RectTransform(new Point((int)(parentSize.X / 3f * (talentOptionIdentifiers.Count - 1)), parentSize.Y)), style: "GUITooltip")
|
||||
{
|
||||
UserData = showcaseTalentIdentifier,
|
||||
IgnoreLayoutGroups = true,
|
||||
Visible = false
|
||||
};
|
||||
GUILayoutGroup showcaseCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.7f), showCaseFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
|
||||
showcaseLayout = new GUILayoutGroup(new RectTransform(Vector2.One, showcaseCenterGroup.RectTransform), isHorizontal: true) { Stretch = true };
|
||||
showCaseTalentFrames.Add(showCaseFrame);
|
||||
}
|
||||
|
||||
foreach (Identifier talentId in talentOptionIdentifiers)
|
||||
{
|
||||
if (!TalentPrefab.TalentPrefabs.TryGet(talentId, out TalentPrefab? talent)) { continue; }
|
||||
|
||||
bool isShowCaseTalent = hasShowcase && talentId == showcaseTalentIdentifier;
|
||||
GUIComponent talentParent;
|
||||
|
||||
if (hasShowcase && talentId != showcaseTalentIdentifier)
|
||||
{
|
||||
talentParent = showcaseLayout;
|
||||
}
|
||||
else
|
||||
{
|
||||
talentParent = talentOptionLayoutGroup;
|
||||
}
|
||||
|
||||
GUIFrame talentFrame = new GUIFrame(new RectTransform(Vector2.One, talentParent.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
GUIFrame croppedTalentFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: null);
|
||||
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, croppedTalentFrame.RectTransform, anchor: Anchor.Center), style: null)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖" + "\n\n" + talent.Description),
|
||||
UserData = talent.Identifier,
|
||||
PressedColor = pressedColor,
|
||||
Enabled = info.Character != null,
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
if (isShowCaseTalent)
|
||||
{
|
||||
foreach (GUIComponent component in showCaseTalentFrames)
|
||||
{
|
||||
if (component.UserData is Identifier showcaseIdentifier && showcaseIdentifier == talentId)
|
||||
{
|
||||
component.RectTransform.ScreenSpaceOffset = new Point((int)(button.Rect.Location.X - component.Rect.Width / 2f + button.Rect.Width / 2f), button.Rect.Location.Y - component.Rect.Height);
|
||||
component.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
component.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Character? controlledCharacter = info.Character;
|
||||
if (controlledCharacter is null) { return false; }
|
||||
|
||||
if (talentOption.MaxChosenTalents is 1)
|
||||
{
|
||||
// deselect other buttons in tier by removing their selected talents from pool
|
||||
foreach (GUIButton guiButton in talentOptionLayoutGroup.GetAllChildren<GUIButton>())
|
||||
{
|
||||
if (guiButton.UserData is Identifier otherTalentIdentifier && guiButton != button)
|
||||
{
|
||||
if (!controlledCharacter.HasTalent(otherTalentIdentifier))
|
||||
{
|
||||
selectedTalents.Remove(otherTalentIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Identifier talentIdentifier = (Identifier)userData;
|
||||
|
||||
if (IsViableTalentForCharacter(info.Character, talentIdentifier, selectedTalents))
|
||||
{
|
||||
if (!selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
selectedTalents.Add(talentIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedTalents.Remove(talentIdentifier);
|
||||
}
|
||||
}
|
||||
else if (!controlledCharacter.HasTalent(talentIdentifier))
|
||||
{
|
||||
selectedTalents.Remove(talentIdentifier);
|
||||
}
|
||||
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = talentButton.DisabledColor = Color.Transparent;
|
||||
|
||||
GUIComponent iconImage;
|
||||
if (talent.Icon is null)
|
||||
{
|
||||
iconImage = new GUITextBlock(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), text: "???", font: GUIStyle.LargeFont, textAlignment: Alignment.Center, style: null)
|
||||
{
|
||||
OutlineColor = GUIStyle.Red,
|
||||
TextColor = GUIStyle.Red,
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
iconImage = new GUIImage(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
|
||||
{
|
||||
Color = talent.ColorOverride.TryUnwrap(out Color color) ? color : Color.White,
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor * 0.5f,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
|
||||
iconImage.Enabled = talentButton.Enabled;
|
||||
if (isShowCaseTalent) { continue; }
|
||||
|
||||
talentButtons.Add(new TalentButton(talentButton, iconImage, talent));
|
||||
}
|
||||
|
||||
talentCornerIcons.Add(new TalentCornerIcon(subTree.Identifier, index, cornerIcon, talentBackground, talentBackgroundHighlight));
|
||||
}
|
||||
|
||||
private void CreateFooter(GUIComponent parent, CharacterInfo info)
|
||||
{
|
||||
GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), parent.RectTransform, Anchor.TopCenter), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.01f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), bottomLayout.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: info.GetProgressTowardsNextLevel(), color: GUIStyle.Green)
|
||||
{
|
||||
IsHorizontal = true,
|
||||
};
|
||||
|
||||
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
Shadow = true,
|
||||
ToolTip = TextManager.Get("experiencetooltip")
|
||||
};
|
||||
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight)
|
||||
{ AutoScaleVertical = true };
|
||||
|
||||
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ResetTalentSelection
|
||||
};
|
||||
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ApplyTalentSelection,
|
||||
};
|
||||
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
|
||||
}
|
||||
|
||||
private bool ResetTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ApplyTalents(Character controlledCharacter)
|
||||
{
|
||||
foreach (Identifier talent in CheckTalentSelection(controlledCharacter, selectedTalents))
|
||||
{
|
||||
controlledCharacter.GiveTalent(talent);
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(controlledCharacter, new Character.UpdateTalentsEventData());
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTalentInfo();
|
||||
}
|
||||
|
||||
private bool ApplyTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
if (controlledCharacter is null) { return false; }
|
||||
|
||||
ApplyTalents(controlledCharacter);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateTalentInfo()
|
||||
{
|
||||
if (!(Character.Controlled is { Info: var info } character)) { return; }
|
||||
|
||||
bool unlockedAllTalents = character.HasUnlockedAllTalents();
|
||||
|
||||
if (experienceBar is null || experienceText is null) { return; }
|
||||
|
||||
if (unlockedAllTalents)
|
||||
{
|
||||
experienceText.Text = string.Empty;
|
||||
experienceBar.BarSize = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
experienceText.Text = $"{info.ExperiencePoints - info.GetExperienceRequiredForCurrentLevel()} / {info.GetExperienceRequiredToLevelUp() - info.GetExperienceRequiredForCurrentLevel()}";
|
||||
experienceBar.BarSize = info.GetProgressTowardsNextLevel();
|
||||
}
|
||||
|
||||
selectedTalents = CheckTalentSelection(character, selectedTalents).ToHashSet();
|
||||
|
||||
string pointsLeft = info.GetAvailableTalentPoints().ToString();
|
||||
|
||||
int talentCount = selectedTalents.Count - info.GetUnlockedTalentsInTree().Count();
|
||||
|
||||
if (unlockedAllTalents)
|
||||
{
|
||||
talentPointText?.SetRichText($"‖color:{Color.Gray.ToStringHex()}‖{TextManager.Get("talentmenu.alltalentsunlocked")}‖color:end‖");
|
||||
}
|
||||
else if (talentCount > 0)
|
||||
{
|
||||
string pointsUsed = $"‖color:{XMLExtensions.ToStringHex(GUIStyle.Red)}‖{-talentCount}‖color:end‖";
|
||||
LocalizedString localizedString = TextManager.GetWithVariables("talentmenu.points.spending", ("[amount]", pointsLeft), ("[used]", pointsUsed));
|
||||
talentPointText?.SetRichText(localizedString);
|
||||
}
|
||||
else
|
||||
{
|
||||
talentPointText?.SetRichText(TextManager.GetWithVariable("talentmenu.points", "[amount]", pointsLeft));
|
||||
}
|
||||
|
||||
foreach (TalentCornerIcon cornerIcon in talentCornerIcons)
|
||||
{
|
||||
TalentTreeStageState state = GetTalentOptionStageState(character, cornerIcon.TalentTree, cornerIcon.Index, selectedTalents);
|
||||
TalentTreeStyle style = talentStageStyles[state];
|
||||
GUIComponentStyle newStyle = style.ComponentStyle;
|
||||
cornerIcon.IconComponent.ApplyStyle(newStyle);
|
||||
cornerIcon.IconComponent.Color = newStyle.Color;
|
||||
cornerIcon.BackgroundComponent.Color = style.Color;
|
||||
cornerIcon.GlowComponent.Visible = state == Highlighted;
|
||||
}
|
||||
|
||||
foreach (TalentButton talentButton in talentButtons)
|
||||
{
|
||||
Identifier talentIdentifier = talentButton.Identifier;
|
||||
bool unselectable = !IsViableTalentForCharacter(character, talentIdentifier, selectedTalents) || character.HasTalent(talentIdentifier);
|
||||
Color newTalentColor = unselectable ? unselectableColor : unselectedColor;
|
||||
Color hoverColor = Color.White;
|
||||
bool selected = false;
|
||||
|
||||
if (character.HasTalent(talentIdentifier))
|
||||
{
|
||||
selected = true;
|
||||
newTalentColor = GUIStyle.Green;
|
||||
}
|
||||
else if (selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
selected = true;
|
||||
newTalentColor = GUIStyle.Orange;
|
||||
hoverColor = Color.Lerp(GUIStyle.Orange, Color.White, 0.7f);
|
||||
}
|
||||
|
||||
bool shouldOverride = !unselectable || selected;
|
||||
|
||||
if (shouldOverride && talentButton.Prefab.ColorOverride.TryUnwrap(out Color overrideColor))
|
||||
{
|
||||
newTalentColor = overrideColor;
|
||||
}
|
||||
|
||||
talentButton.IconComponent.Color = newTalentColor;
|
||||
talentButton.IconComponent.HoverColor = hoverColor;
|
||||
}
|
||||
|
||||
if (skillListBox is null) { return; }
|
||||
|
||||
TabMenu.CreateSkillList(character, info, skillListBox);
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
bool mouseInteracted = PlayerInput.PrimaryMouseButtonClicked() || PlayerInput.SecondaryMouseButtonClicked() || PlayerInput.ScrollWheelSpeed != 0;
|
||||
bool keyboardInteracted = PlayerInput.KeyHit(Keys.Escape) || GameSettings.CurrentConfig.KeyMap.Bindings[InputType.InfoTab].IsHit();
|
||||
|
||||
foreach (GUIComponent component in showCaseTalentFrames)
|
||||
{
|
||||
component.AddToGUIUpdateList(order: 1);
|
||||
if (!component.Visible) { continue; }
|
||||
|
||||
if (keyboardInteracted || (mouseInteracted && !component.Rect.Contains(PlayerInput.MousePosition)))
|
||||
{
|
||||
component.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (Character.Controlled?.Info is not { } characterInfo || talentResetButton is null || talentApplyButton is null) { return; }
|
||||
|
||||
int talentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
|
||||
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
|
||||
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
talentApplyButton.Flash(GUIStyle.Orange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -433,8 +433,8 @@ namespace Barotrauma
|
||||
|
||||
Location location = Campaign.Map.CurrentLocation;
|
||||
|
||||
int hullRepairCost = Campaign.GetHullRepairCost();
|
||||
int itemRepairCost = Campaign.GetItemRepairCost();
|
||||
int hullRepairCost = CampaignMode.GetHullRepairCost();
|
||||
int itemRepairCost = CampaignMode.GetItemRepairCost();
|
||||
int shuttleRetrieveCost = CampaignMode.ShuttleReplaceCost;
|
||||
if (location != null)
|
||||
{
|
||||
@@ -847,7 +847,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (UpgradePrefab prefab in prefabs)
|
||||
{
|
||||
if (prefab.MaxLevel is 0) { continue; }
|
||||
if (prefab.GetMaxLevelForCurrentSub() == 0) { continue; }
|
||||
CreateUpgradeEntry(prefab, category, parent.Content, submarine, entitiesOnSub);
|
||||
}
|
||||
}
|
||||
@@ -1080,7 +1080,7 @@ namespace Barotrauma
|
||||
|
||||
public static GUIFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
|
||||
{
|
||||
int price = prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
|
||||
}
|
||||
|
||||
@@ -1177,11 +1177,12 @@ namespace Barotrauma
|
||||
|
||||
private static void UpdateUpgradePercentageText(GUITextBlock text, UpgradePrefab upgradePrefab, int currentLevel)
|
||||
{
|
||||
float nextIncrease = upgradePrefab.IncreaseOnTooltip * (Math.Min(currentLevel + 1, upgradePrefab.MaxLevel));
|
||||
int maxLevel = upgradePrefab.GetMaxLevelForCurrentSub();
|
||||
float nextIncrease = upgradePrefab.IncreaseOnTooltip * Math.Min(currentLevel + 1, maxLevel);
|
||||
if (nextIncrease != 0f)
|
||||
{
|
||||
text.Text = $"{Math.Round(nextIncrease, 1)} %";
|
||||
if (currentLevel == upgradePrefab.MaxLevel)
|
||||
if (currentLevel == maxLevel)
|
||||
{
|
||||
text.TextColor = Color.Gray;
|
||||
}
|
||||
@@ -1221,7 +1222,7 @@ namespace Barotrauma
|
||||
{
|
||||
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
|
||||
("[upgradename]", prefab.Name),
|
||||
("[amount]", prefab.Price.GetBuyprice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation).ToString()));
|
||||
("[amount]", prefab.Price.GetBuyPrice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation).ToString()));
|
||||
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
|
||||
{
|
||||
if (GameMain.NetworkMember != null)
|
||||
@@ -1617,14 +1618,15 @@ namespace Barotrauma
|
||||
{
|
||||
int currentLevel = campaign.UpgradeManager.GetUpgradeLevel(prefab, category);
|
||||
|
||||
LocalizedString progressText = TextManager.GetWithVariables("upgrades.progressformat", ("[level]", currentLevel.ToString()), ("[maxlevel]", prefab.MaxLevel.ToString()));
|
||||
int maxLevel = prefab.GetMaxLevelForCurrentSub();
|
||||
LocalizedString progressText = TextManager.GetWithVariables("upgrades.progressformat", ("[level]", currentLevel.ToString()), ("[maxlevel]", maxLevel.ToString()));
|
||||
if (prefabFrame.FindChild("progressbar", true) is { } progressParent)
|
||||
{
|
||||
GUIProgressBar bar = progressParent.GetChild<GUIProgressBar>();
|
||||
if (bar != null)
|
||||
{
|
||||
bar.BarSize = currentLevel / (float) prefab.MaxLevel;
|
||||
bar.Color = currentLevel >= prefab.MaxLevel ? GUIStyle.Green : GUIStyle.Orange;
|
||||
bar.BarSize = currentLevel / (float)maxLevel;
|
||||
bar.Color = currentLevel >= maxLevel ? GUIStyle.Green : GUIStyle.Orange;
|
||||
}
|
||||
|
||||
GUITextBlock block = progressParent.GetChild<GUITextBlock>();
|
||||
@@ -1637,12 +1639,12 @@ namespace Barotrauma
|
||||
|
||||
GUITextBlock priceLabel = textBlocks[0];
|
||||
priceLabel.Visible = true;
|
||||
int price = prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||
|
||||
if (priceLabel != null && !WaitForServerUpdate)
|
||||
{
|
||||
priceLabel.Text = TextManager.FormatCurrency(price);
|
||||
if (currentLevel >= prefab.MaxLevel)
|
||||
if (currentLevel >= maxLevel)
|
||||
{
|
||||
priceLabel.Text = TextManager.Get("Upgrade.MaxedUpgrade");
|
||||
}
|
||||
@@ -1651,7 +1653,7 @@ namespace Barotrauma
|
||||
GUIButton button = buttonParent.GetChild<GUIButton>();
|
||||
if (button != null)
|
||||
{
|
||||
button.Enabled = currentLevel < prefab.MaxLevel;
|
||||
button.Enabled = currentLevel < maxLevel;
|
||||
if (WaitForServerUpdate || campaign.GetBalance() < price)
|
||||
{
|
||||
button.Enabled = false;
|
||||
@@ -1697,13 +1699,14 @@ namespace Barotrauma
|
||||
|
||||
foreach (GUIComponent component in indicators.Children)
|
||||
{
|
||||
if (!(component is GUIImage image)) { continue; }
|
||||
if (component is not GUIImage image) { continue; }
|
||||
|
||||
foreach (UpgradePrefab prefab in prefabs)
|
||||
{
|
||||
if (component.UserData != prefab) { continue; }
|
||||
|
||||
if (prefab.MaxLevel is 0)
|
||||
int maxLevel = prefab.GetMaxLevelForCurrentSub();
|
||||
if (maxLevel == 0)
|
||||
{
|
||||
component.Visible = false;
|
||||
continue;
|
||||
@@ -1715,7 +1718,6 @@ namespace Barotrauma
|
||||
GUIComponentStyle onStyle = styles["upgradeindicatoron".ToIdentifier()];
|
||||
GUIComponentStyle dimStyle = styles["upgradeindicatordim".ToIdentifier()];
|
||||
GUIComponentStyle offStyle = styles["upgradeindicatoroff".ToIdentifier()];
|
||||
int maxLevel = prefab.MaxLevel;
|
||||
|
||||
if (maxLevel == 0)
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
Data = data,
|
||||
OnClick = (GUITextBlock component, GUITextBlock.ClickableArea area) =>
|
||||
{
|
||||
GameMain.Instance.ShowOpenUrlInWebBrowserPrompt("https://gameanalytics.com/privacy/");
|
||||
GameMain.ShowOpenUrlInWebBrowserPrompt("https://gameanalytics.com/privacy/");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -791,6 +791,10 @@ namespace Barotrauma
|
||||
{
|
||||
GUI.TogglePauseMenu();
|
||||
}
|
||||
else if (GameSession?.Campaign is { ShowCampaignUI: true, ForceMapUI: false })
|
||||
{
|
||||
GameSession.Campaign.ShowCampaignUI = false;
|
||||
}
|
||||
//open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC
|
||||
else if ((Character.Controlled == null || !itemHudActive())
|
||||
&& CharacterHealth.OpenHealthWindow == null
|
||||
@@ -1200,7 +1204,7 @@ namespace Barotrauma
|
||||
base.OnExiting(sender, args);
|
||||
}
|
||||
|
||||
public void ShowOpenUrlInWebBrowserPrompt(string url, string promptExtensionTag = null)
|
||||
public static void ShowOpenUrlInWebBrowserPrompt(string url, string promptExtensionTag = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url)) { return; }
|
||||
if (GUIMessageBox.VisibleBox?.UserData as string == "verificationprompt") { return; }
|
||||
@@ -1218,7 +1222,14 @@ namespace Barotrauma
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
|
||||
{
|
||||
ToolBox.OpenFileWithShell(url);
|
||||
try
|
||||
{
|
||||
ToolBox.OpenFileWithShell(url);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to open the url {url}", e);
|
||||
}
|
||||
msgBox.Close();
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -86,10 +86,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsOwner(Client client) => client != null && client.IsOwner;
|
||||
|
||||
/// <summary>
|
||||
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
|
||||
/// </summary>
|
||||
public bool AllowedToManageCampaign(ClientPermissions permissions)
|
||||
public static bool AllowedToManageCampaign(ClientPermissions permissions)
|
||||
{
|
||||
//allow managing the round if the client has permissions, is the owner, the only client in the server,
|
||||
//or if no-one has management permissions
|
||||
@@ -97,9 +99,8 @@ namespace Barotrauma
|
||||
return
|
||||
GameMain.Client.HasPermission(permissions) ||
|
||||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
GameMain.Client.ConnectedClients.Count == 1 ||
|
||||
GameMain.Client.IsServerOwner ||
|
||||
GameMain.Client.ConnectedClients.None(c => c.InGame && (c.IsOwner || c.HasPermission(permissions)));
|
||||
AnyOneAllowedToManageCampaign(permissions);
|
||||
}
|
||||
|
||||
public static bool AllowedToManageWallets()
|
||||
|
||||
+5
-1
@@ -407,6 +407,11 @@ namespace Barotrauma
|
||||
GUI.SetSavingIndicatorState(success);
|
||||
crewDead = false;
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Event history must be registered before ending the round or it will be cleared
|
||||
GameMain.GameSession.EventManager.RegisterEventHistory();
|
||||
}
|
||||
GameMain.GameSession.EndRound("", traitorResults, transitionType);
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
RoundSummary roundSummary = null;
|
||||
@@ -466,7 +471,6 @@ namespace Barotrauma
|
||||
if (success)
|
||||
{
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
GameMain.GameSession.EventManager.RegisterEventHistory();
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -506,7 +506,7 @@ namespace Barotrauma
|
||||
|
||||
private LocalizedString GetHeaderText(bool gameOver, CampaignMode.TransitionType transitionType)
|
||||
{
|
||||
string locationName = Submarine.MainSub.AtEndExit ? endLocation?.Name : startLocation?.Name;
|
||||
string locationName = Submarine.MainSub is { AtEndExit: true } ? endLocation?.Name : startLocation?.Name;
|
||||
|
||||
string textTag;
|
||||
if (gameOver)
|
||||
|
||||
@@ -774,7 +774,6 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isEquippable = item.AllowedSlots.Any(s => s != InvSlotType.Any);
|
||||
var selectedContainer = character.SelectedItem?.GetComponent<ItemContainer>();
|
||||
|
||||
if (selectedContainer != null &&
|
||||
@@ -802,8 +801,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (character.HeldItems.Any(i =>
|
||||
i.OwnInventory != null &&
|
||||
/*disallow putting into equipped item if the item is equippable (equip as the quick action instead)*/
|
||||
((i.OwnInventory.CanBePut(item) && (allowInventorySwap || !isEquippable)) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
|
||||
(i.OwnInventory.CanBePut(item) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
|
||||
{
|
||||
return QuickUseAction.PutToEquippedItem;
|
||||
}
|
||||
|
||||
@@ -25,14 +25,31 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Node node in nodes)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("swirlysmoke", node.WorldPosition, Vector2.Zero);
|
||||
|
||||
if (node.ParentIndex > -1)
|
||||
{
|
||||
Vector2 diff = nodes[node.ParentIndex].WorldPosition - node.WorldPosition;
|
||||
float dist = diff.Length();
|
||||
Vector2 normalizedDiff = diff / dist;
|
||||
for (float x = 0.0f; x < dist; x += 50.0f)
|
||||
{
|
||||
var spark = GameMain.ParticleManager.CreateParticle("ElectricShock", node.WorldPosition + normalizedDiff * x, Vector2.Zero);
|
||||
if (spark != null)
|
||||
{
|
||||
spark.Size *= 0.3f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawElectricity(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (timer <= 0.0f) { return; }
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
if (nodes[i].Length <= 1.0f) continue;
|
||||
if (nodes[i].Length <= 1.0f) { continue; }
|
||||
var node = nodes[i];
|
||||
electricitySprite.Draw(spriteBatch,
|
||||
(i + frameOffset) % electricitySprite.FrameCount,
|
||||
@@ -46,10 +63,16 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
for (int i = 1; i < nodes.Count; i++)
|
||||
{
|
||||
if (nodes[i].Length <= 1.0f) continue;
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(nodes[i].WorldPosition.X, -nodes[i].WorldPosition.Y), Vector2.One * 5, Color.LightCyan, isFilled: true);
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(nodes[i].WorldPosition.X, -nodes[i].WorldPosition.Y),
|
||||
new Vector2(nodes[nodes[i].ParentIndex].WorldPosition.X, -nodes[nodes[i].ParentIndex].WorldPosition.Y),
|
||||
Color.LightCyan,
|
||||
width: 3);
|
||||
|
||||
if (nodes[i].Length <= 1.0f) { continue; }
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(nodes[i].WorldPosition.X, -nodes[i].WorldPosition.Y), Vector2.One * 10, Color.LightCyan, isFilled: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,6 +265,8 @@ namespace Barotrauma.Items.Components
|
||||
foreach (DeconstructItem deconstructItem in it.Prefab.DeconstructItems)
|
||||
{
|
||||
if (!deconstructItem.IsValidDeconstructor(item)) { continue; }
|
||||
float percentageHealth = it.Condition / it.MaxCondition;
|
||||
if (percentageHealth < deconstructItem.MinCondition || percentageHealth > deconstructItem.MaxCondition) { continue; }
|
||||
RegisterItem(deconstructItem.ItemIdentifier, deconstructItem.Amount);
|
||||
}
|
||||
|
||||
|
||||
@@ -1119,7 +1119,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (it.GetComponent<PowerContainer>() is { } battery)
|
||||
{
|
||||
int batteryCapacity = (int)(battery.Charge / battery.Capacity * 100f);
|
||||
int batteryCapacity = (int)(battery.Charge / battery.GetCapacity() * 100f);
|
||||
line2 = TextManager.GetWithVariable("statusmonitor.battery.tooltip", "[amount]", batteryCapacity.ToString());
|
||||
}
|
||||
else if (it.GetComponent<PowerTransfer>() is { } powerTransfer)
|
||||
|
||||
@@ -1028,7 +1028,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (var c in MineralClusters)
|
||||
{
|
||||
var unobtainedMinerals = c.resources.Where(i => i != null && i.GetRootInventoryOwner() == i);
|
||||
var unobtainedMinerals = c.resources.Where(i => i != null && i.GetComponent<Holdable>() is { Attached: true });
|
||||
if (unobtainedMinerals.None()) { continue; }
|
||||
if (!CheckResourceMarkerVisibility(c.center, transducerCenter)) { continue; }
|
||||
var i = unobtainedMinerals.FirstOrDefault();
|
||||
|
||||
@@ -390,7 +390,7 @@ namespace Barotrauma.Items.Components
|
||||
!ActiveDockingSource.Docked && DockingTarget?.Item?.Submarine == Level.Loaded.StartOutpost && (DockingTarget?.Item?.Submarine?.Info.IsOutpost ?? false))
|
||||
{
|
||||
// Docking to an outpost
|
||||
var subsToLeaveBehind = campaign.GetSubsToLeaveBehind(Item.Submarine);
|
||||
var subsToLeaveBehind = CampaignMode.GetSubsToLeaveBehind(Item.Submarine);
|
||||
if (subsToLeaveBehind.Any())
|
||||
{
|
||||
enterOutpostPrompt = new GUIMessageBox(
|
||||
|
||||
@@ -133,35 +133,43 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
{
|
||||
if (indicatorSize.X <= 1.0f || indicatorSize.Y <= 1.0f) { return; }
|
||||
Vector2 scaledIndicatorSize = indicatorSize * item.Scale;
|
||||
if (scaledIndicatorSize.X <= 2.0f || scaledIndicatorSize.Y <= 2.0f) { return; }
|
||||
|
||||
const float outlineThickness = 1.0f;
|
||||
Vector2 itemSize = new Vector2(item.Sprite.SourceRect.Width, item.Sprite.SourceRect.Height) * item.Scale;
|
||||
Vector2 indicatorPos = -itemSize / 2 + indicatorPosition * item.Scale;
|
||||
if (item.FlippedX && item.Prefab.CanSpriteFlipX) { indicatorPos.X = -indicatorPos.X - indicatorSize.X * item.Scale; }
|
||||
if (item.FlippedY && item.Prefab.CanSpriteFlipY) { indicatorPos.Y = -indicatorPos.Y - indicatorSize.Y * item.Scale; }
|
||||
Vector2 indicatorPos = -itemSize / 2.0f + indicatorPosition * item.Scale;
|
||||
Vector2 itemPosition = new Vector2(item.DrawPosition.X, -item.DrawPosition.Y);
|
||||
Vector2 flip = new Vector2(item.FlippedX && item.Prefab.CanSpriteFlipX ? -1.0f : 1.0f, item.FlippedY && item.Prefab.CanSpriteFlipY ? -1.0f : 1.0f);
|
||||
Matrix rotate = Matrix.CreateRotationZ(item.RotationRad);
|
||||
Vector2 center = Vector2.Transform((indicatorPos + (scaledIndicatorSize * 0.5f)) * flip, rotate) + itemPosition;
|
||||
|
||||
if (charge > 0 && capacity > 0)
|
||||
{
|
||||
float chargeRatio = MathHelper.Clamp(charge / capacity, 0.0f, 1.0f);
|
||||
Color indicatorColor = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
|
||||
if (!isHorizontal)
|
||||
Vector2 indicatorCenter = (indicatorPos + (scaledIndicatorSize * 0.5f)) * flip;
|
||||
Vector2 indicatorSize;
|
||||
|
||||
if (isHorizontal)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y + ((indicatorSize.Y * item.Scale) * (1.0f - chargeRatio))) + indicatorPos,
|
||||
new Vector2(indicatorSize.X * item.Scale, (indicatorSize.Y * item.Scale) * chargeRatio), indicatorColor, true,
|
||||
depth: item.SpriteDepth - 0.00001f);
|
||||
float indicatorLength = (scaledIndicatorSize.X - outlineThickness * 2.0f) * chargeRatio;
|
||||
indicatorCenter.X += -scaledIndicatorSize.X * 0.5f + (flipIndicator ? scaledIndicatorSize.X - outlineThickness - indicatorLength * 0.5f : outlineThickness + indicatorLength * 0.5f);
|
||||
indicatorSize = new Vector2(indicatorLength, scaledIndicatorSize.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y) + indicatorPos,
|
||||
new Vector2((indicatorSize.X * item.Scale) * chargeRatio, indicatorSize.Y * item.Scale), indicatorColor, true,
|
||||
depth: item.SpriteDepth - 0.00001f);
|
||||
float indicatorLength = (scaledIndicatorSize.Y - outlineThickness * 2.0f) * chargeRatio;
|
||||
indicatorCenter.Y += -scaledIndicatorSize.Y * 0.5f + (flipIndicator ? outlineThickness + indicatorLength * 0.5f : scaledIndicatorSize.Y - outlineThickness - indicatorLength * 0.5f);
|
||||
indicatorSize = new Vector2(scaledIndicatorSize.X, indicatorLength);
|
||||
}
|
||||
|
||||
indicatorCenter = Vector2.Transform(indicatorCenter, rotate) + itemPosition;
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, indicatorCenter, indicatorSize, indicatorSize * 0.5f, item.RotationRad, indicatorColor, item.SpriteDepth - 0.00001f);
|
||||
}
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y) + indicatorPos,
|
||||
indicatorSize * item.Scale, Color.Black, depth: item.SpriteDepth - 0.000015f);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, center, scaledIndicatorSize, scaledIndicatorSize * 0.5f, item.RotationRad, Color.Black, item.SpriteDepth - 0.000015f, outlineThickness, GUI.OutlinePosition.Inside);
|
||||
}
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData)
|
||||
|
||||
@@ -581,7 +581,7 @@ namespace Barotrauma.Items.Components
|
||||
var battery = recipient.Item?.GetComponent<PowerContainer>();
|
||||
if (battery == null || battery.Item.Condition <= 0.0f) { continue; }
|
||||
availableCharge += battery.Charge;
|
||||
availableCapacity += battery.Capacity;
|
||||
availableCapacity += battery.GetCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1415,6 +1415,15 @@ namespace Barotrauma
|
||||
case EventType.ChangeProperty:
|
||||
ReadPropertyChange(msg, false);
|
||||
break;
|
||||
case EventType.ItemStat:
|
||||
byte length = msg.ReadByte();
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
var statIdentifier = INetSerializableStruct.Read<ItemStatManager.TalentStatIdentifier>(msg);
|
||||
var statValue = msg.ReadSingle();
|
||||
StatManager.ApplyStat(statIdentifier, statValue);
|
||||
}
|
||||
break;
|
||||
case EventType.Upgrade:
|
||||
Identifier identifier = msg.ReadIdentifier();
|
||||
byte level = msg.ReadByte();
|
||||
|
||||
@@ -208,6 +208,13 @@ namespace Barotrauma
|
||||
DecorativeSpriteGroups = decorativeSpriteGroups.Select(kvp => (kvp.Key, kvp.Value.ToImmutableArray())).ToImmutableDictionary();
|
||||
}
|
||||
|
||||
public bool CanCharacterBuy()
|
||||
{
|
||||
if (!DefaultPrice.RequiresUnlock) { return true; }
|
||||
|
||||
return Character.Controlled is not null && Character.Controlled.HasStoreAccessForItem(this);
|
||||
}
|
||||
|
||||
public override void UpdatePlacing(Camera cam)
|
||||
{
|
||||
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
|
||||
|
||||
@@ -162,23 +162,27 @@ namespace Barotrauma
|
||||
|
||||
RemoveFogOfWar(StartLocation);
|
||||
|
||||
GenerateLocationConnectionVisuals();
|
||||
GenerateAllLocationConnectionVisuals();
|
||||
}
|
||||
|
||||
partial void GenerateLocationConnectionVisuals()
|
||||
partial void GenerateAllLocationConnectionVisuals()
|
||||
{
|
||||
foreach (LocationConnection connection in Connections)
|
||||
{
|
||||
Vector2 connectionStart = connection.Locations[0].MapPosition;
|
||||
Vector2 connectionEnd = connection.Locations[1].MapPosition;
|
||||
float connectionLength = Vector2.Distance(connectionStart, connectionEnd);
|
||||
int iterations = Math.Min((int)Math.Sqrt(connectionLength * generationParams.ConnectionIndicatorIterationMultiplier), 5);
|
||||
connection.CrackSegments.Clear();
|
||||
connection.CrackSegments.AddRange(MathUtils.GenerateJaggedLine(
|
||||
connectionStart, connectionEnd,
|
||||
iterations, connectionLength * generationParams.ConnectionIndicatorDisplacementMultiplier));
|
||||
GenerateLocationConnectionVisuals(connection);
|
||||
}
|
||||
}
|
||||
partial void GenerateLocationConnectionVisuals(LocationConnection connection)
|
||||
{
|
||||
Vector2 connectionStart = connection.Locations[0].MapPosition;
|
||||
Vector2 connectionEnd = connection.Locations[1].MapPosition;
|
||||
float connectionLength = Vector2.Distance(connectionStart, connectionEnd);
|
||||
int iterations = Math.Min((int)Math.Sqrt(connectionLength * generationParams.ConnectionIndicatorIterationMultiplier), 5);
|
||||
connection.CrackSegments.Clear();
|
||||
connection.CrackSegments.AddRange(MathUtils.GenerateJaggedLine(
|
||||
connectionStart, connectionEnd,
|
||||
iterations, connectionLength * generationParams.ConnectionIndicatorDisplacementMultiplier));
|
||||
}
|
||||
|
||||
private void LocationChanged(Location prevLocation, Location newLocation)
|
||||
{
|
||||
@@ -414,7 +418,7 @@ namespace Barotrauma
|
||||
new GUIMessageBox(string.Empty, TextManager.Get("LockedPathTooltip"));
|
||||
}
|
||||
//clients aren't allowed to select the location without a permission
|
||||
else if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap) ?? false)
|
||||
else if (CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap))
|
||||
{
|
||||
connectionHighlightState = 0.0f;
|
||||
SelectedConnection = connection;
|
||||
|
||||
+6
-6
@@ -277,16 +277,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ID = 0;
|
||||
|
||||
lastReceivedID = 0;
|
||||
|
||||
firstNewID = null;
|
||||
|
||||
events.Clear();
|
||||
eventLastSent.Clear();
|
||||
|
||||
MidRoundSyncingDone = false;
|
||||
|
||||
ClearSelf();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -297,6 +293,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
ID = 0;
|
||||
events.Clear();
|
||||
if (thisClient != null)
|
||||
{
|
||||
thisClient.LastSentEntityEventID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
static class PingUtils
|
||||
{
|
||||
private static readonly Dictionary<IPAddress, int> activePings = new Dictionary<IPAddress, int>();
|
||||
private static readonly Dictionary<IPEndPoint, int> activePings = new Dictionary<IPEndPoint, int>();
|
||||
|
||||
private static bool steamPingInfoReady;
|
||||
|
||||
@@ -36,9 +36,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
switch (serverInfo.Endpoint)
|
||||
{
|
||||
case LidgrenEndpoint { NetEndpoint: { Address: var address } }:
|
||||
case LidgrenEndpoint { NetEndpoint: var endPoint }:
|
||||
|
||||
GetIPAddressPing(serverInfo, address, onPingDiscovered);
|
||||
GetIPAddressPing(serverInfo, endPoint, onPingDiscovered);
|
||||
break;
|
||||
case SteamP2PEndpoint steamP2PEndpoint:
|
||||
TaskPool.Add($"EstimateSteamLobbyPing ({steamP2PEndpoint.StringRepresentation})",
|
||||
@@ -131,9 +131,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetIPAddressPing(ServerInfo serverInfo, IPAddress address, Action<ServerInfo> onPingDiscovered)
|
||||
private static void GetIPAddressPing(ServerInfo serverInfo, IPEndPoint endPoint, Action<ServerInfo> onPingDiscovered)
|
||||
{
|
||||
if (IPAddress.IsLoopback(address))
|
||||
if (IPAddress.IsLoopback(endPoint.Address))
|
||||
{
|
||||
serverInfo.Ping = Option<int>.Some(0);
|
||||
onPingDiscovered(serverInfo);
|
||||
@@ -142,24 +142,24 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
lock (activePings)
|
||||
{
|
||||
if (activePings.ContainsKey(address)) { return; }
|
||||
activePings.Add(address, activePings.Any() ? activePings.Values.Max() + 1 : 0);
|
||||
if (activePings.ContainsKey(endPoint)) { return; }
|
||||
activePings.Add(endPoint, activePings.Any() ? activePings.Values.Max() + 1 : 0);
|
||||
}
|
||||
serverInfo.Ping = Option<int>.None();
|
||||
TaskPool.Add($"PingServerAsync ({address})", PingServerAsync(address, 1000),
|
||||
TaskPool.Add($"PingServerAsync ({endPoint})", PingServerAsync(endPoint, 1000),
|
||||
rtt =>
|
||||
{
|
||||
if (!rtt.TryGetResult(out serverInfo.Ping)) { serverInfo.Ping = Option<int>.None(); }
|
||||
onPingDiscovered(serverInfo);
|
||||
lock (activePings)
|
||||
{
|
||||
activePings.Remove(address);
|
||||
activePings.Remove(endPoint);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<Option<int>> PingServerAsync(IPAddress ipAddress, int timeOut)
|
||||
private static async Task<Option<int>> PingServerAsync(IPEndPoint endPoint, int timeOut)
|
||||
{
|
||||
await Task.Yield();
|
||||
bool shouldGo = false;
|
||||
@@ -167,21 +167,21 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
lock (activePings)
|
||||
{
|
||||
shouldGo = activePings.Count(kvp => kvp.Value < activePings[ipAddress]) < 25;
|
||||
shouldGo = activePings.Count(kvp => kvp.Value < activePings[endPoint]) < 25;
|
||||
}
|
||||
await Task.Delay(25);
|
||||
}
|
||||
|
||||
if (ipAddress == null) { return Option<int>.None(); }
|
||||
if (endPoint?.Address == null) { return Option<int>.None(); }
|
||||
|
||||
//don't attempt to ping if the address is IPv6 and it's not supported
|
||||
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6) { return Option<int>.None(); }
|
||||
if (endPoint.Address.AddressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6) { return Option<int>.None(); }
|
||||
|
||||
Ping ping = new Ping();
|
||||
byte[] buffer = new byte[32];
|
||||
try
|
||||
{
|
||||
PingReply pingReply = await ping.SendPingAsync(ipAddress, timeOut, buffer, new PingOptions(128, true));
|
||||
PingReply pingReply = await ping.SendPingAsync(endPoint.Address, timeOut, buffer, new PingOptions(128, true));
|
||||
|
||||
return pingReply.Status switch
|
||||
{
|
||||
@@ -191,9 +191,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + ipAddress, GameAnalyticsManager.ErrorSeverity.Warning, "Failed to ping a server - " + (ex?.InnerException?.Message ?? ex.Message));
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + endPoint.Address, GameAnalyticsManager.ErrorSeverity.Warning, "Failed to ping a server - " + (ex?.InnerException?.Message ?? ex.Message));
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Failed to ping a server (" + ipAddress + ") - " + (ex?.InnerException?.Message ?? ex.Message), Color.Red);
|
||||
DebugConsole.NewMessage("Failed to ping a server (" + endPoint.Address + ") - " + (ex?.InnerException?.Message ?? ex.Message), Color.Red);
|
||||
#endif
|
||||
|
||||
return Option<int>.None();
|
||||
|
||||
@@ -702,10 +702,30 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
Enabled = !GameMain.NetworkMember.GameStarted
|
||||
};
|
||||
var cargoFrame = new GUIListBox(new RectTransform(new Vector2(0.6f, 0.7f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.BottomRight, Pivot.BottomLeft))
|
||||
|
||||
var cargoFrame = new GUIFrame(new RectTransform(new Vector2(0.6f, 0.7f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.BottomRight, Pivot.BottomLeft))
|
||||
{
|
||||
Visible = false
|
||||
};
|
||||
var cargoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), cargoFrame.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var filterText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), cargoContent.RectTransform), TextManager.Get("serverlog.filter"), font: GUIStyle.SubHeadingFont);
|
||||
var entityFilterBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), filterText.RectTransform, Anchor.CenterRight), font: GUIStyle.Font, createClearButton: true);
|
||||
filterText.RectTransform.MinSize = new Point(0, entityFilterBox.RectTransform.MinSize.Y);
|
||||
var cargoList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), cargoContent.RectTransform));
|
||||
entityFilterBox.OnTextChanged += (textBox, text) =>
|
||||
{
|
||||
foreach (var child in cargoList.Content.Children)
|
||||
{
|
||||
if (child.UserData is not ItemPrefab itemPrefab) { continue; }
|
||||
child.Visible = string.IsNullOrEmpty(text) || itemPrefab.Name.Contains(text, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
cargoButton.UserData = cargoFrame;
|
||||
cargoButton.OnClicked = (button, obj) =>
|
||||
{
|
||||
@@ -721,7 +741,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(buttonHolder.Children.Select(c => ((GUIButton)c).TextBlock));
|
||||
|
||||
foreach (ItemPrefab ip in ItemPrefab.Prefabs)
|
||||
foreach (ItemPrefab ip in ItemPrefab.Prefabs.OrderBy(ip => ip.Name))
|
||||
{
|
||||
if (ip.AllowAsExtraCargo.HasValue)
|
||||
{
|
||||
@@ -732,10 +752,10 @@ namespace Barotrauma.Networking
|
||||
if (!ip.CanBeBought) { continue; }
|
||||
}
|
||||
|
||||
var itemFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), cargoFrame.Content.RectTransform) { MinSize = new Point(0, 30) }, isHorizontal: true)
|
||||
var itemFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), cargoList.Content.RectTransform) { MinSize = new Point(0, 30) }, isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
UserData = cargoFrame,
|
||||
UserData = ip,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
@@ -778,7 +798,7 @@ namespace Barotrauma.Networking
|
||||
numberInput.IntValue = ExtraCargo.ContainsKey(ip) ? ExtraCargo[ip] : 0;
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
foreach (var child in cargoFrame.Content.GetAllChildren())
|
||||
foreach (var child in cargoList.Content.GetAllChildren())
|
||||
{
|
||||
if (child.GetChild<GUINumberInput>() is GUINumberInput otherNumberInput)
|
||||
{
|
||||
|
||||
@@ -82,6 +82,9 @@ namespace Barotrauma.Particles
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool CopyEntityAngle { get; set; }
|
||||
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should the entity heading direction be applied to the particle rotation? Only affects after flipping the texture and when CopyEntityAngle is true.")]
|
||||
public bool CopyEntityDir { get; set; }
|
||||
|
||||
[Editable, Serialize("1,1,1,1", IsPropertySaveable.Yes)]
|
||||
public Color ColorMultiplier { get; set; }
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace Barotrauma
|
||||
foreach (GUITickBox tickBox in missionTickBoxes)
|
||||
{
|
||||
bool disable = hasMaxMissions && !tickBox.Selected;
|
||||
tickBox.Enabled = Campaign.AllowedToManageCampaign(ClientPermissions.ManageMap) && !disable;
|
||||
tickBox.Enabled = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap) && !disable;
|
||||
tickBox.Box.DisabledColor = disable ? tickBox.Box.Color * 0.5f : tickBox.Box.Color * 0.8f;
|
||||
foreach (GUIComponent child in tickBox.Parent.Parent.Children)
|
||||
{
|
||||
@@ -315,7 +315,7 @@ namespace Barotrauma
|
||||
if (GUI.MouseOn == tickBox) { return false; }
|
||||
if (tickBox != null)
|
||||
{
|
||||
if (Campaign.AllowedToManageCampaign(ClientPermissions.ManageMap) && tickBox.Enabled)
|
||||
if (CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap) && tickBox.Enabled)
|
||||
{
|
||||
tickBox.Selected = !tickBox.Selected;
|
||||
}
|
||||
@@ -356,10 +356,10 @@ namespace Barotrauma
|
||||
};
|
||||
tickBox.RectTransform.MinSize = new Point(tickBox.Rect.Height, 0);
|
||||
tickBox.RectTransform.IsFixedSize = true;
|
||||
tickBox.Enabled = Campaign.AllowedToManageCampaign(ClientPermissions.ManageMap);
|
||||
tickBox.Enabled = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap);
|
||||
tickBox.OnSelected += (GUITickBox tb) =>
|
||||
{
|
||||
if (!Campaign.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap)) { return false; }
|
||||
if (!CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap)) { return false; }
|
||||
|
||||
if (tb.Selected)
|
||||
{
|
||||
@@ -379,7 +379,7 @@ namespace Barotrauma
|
||||
UpdateMaxMissions(connection.OtherLocation(currentDisplayLocation));
|
||||
|
||||
if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
|
||||
Campaign.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap))
|
||||
CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap))
|
||||
{
|
||||
GameMain.Client?.SendCampaignState();
|
||||
}
|
||||
@@ -472,7 +472,12 @@ namespace Barotrauma
|
||||
{
|
||||
TextGetter = () =>
|
||||
{
|
||||
return TextManager.AddPunctuation(':', TextManager.Get("Missions"), $"{Campaign.NumberOfMissionsAtLocation(destination)}/{Campaign.Settings.TotalMaxMissionCount}");
|
||||
int missionCount = 0;
|
||||
if (GameMain.GameSession != null && Campaign.Map?.CurrentLocation?.SelectedMissions != null)
|
||||
{
|
||||
missionCount = Campaign.Map.CurrentLocation.SelectedMissions.Count(m => m.Locations.Contains(location) && !GameMain.GameSession.Missions.Contains(m));
|
||||
}
|
||||
return TextManager.AddPunctuation(':', TextManager.Get("Missions"), $"{missionCount}/{Campaign.Settings.TotalMaxMissionCount}");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -500,7 +505,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
},
|
||||
Enabled = true,
|
||||
Visible = Campaign.AllowedToManageCampaign(ClientPermissions.ManageMap)
|
||||
Visible = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap)
|
||||
};
|
||||
|
||||
buttonArea.RectTransform.MinSize = new Point(0, StartButton.RectTransform.MinSize.Y);
|
||||
|
||||
@@ -351,7 +351,7 @@ namespace Barotrauma.CharacterEditor
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentPackageNameElement.Text))
|
||||
{
|
||||
contentPackageNameElement.Flash();
|
||||
contentPackageNameElement.Flash(useRectangleFlash: true);
|
||||
return false;
|
||||
}
|
||||
if (ContentPackageManager.AllPackages.Any(cp => cp.Name.ToLower() == contentPackageNameElement.Text.ToLower()))
|
||||
@@ -405,7 +405,7 @@ namespace Barotrauma.CharacterEditor
|
||||
{
|
||||
if (ContentPackage == null)
|
||||
{
|
||||
contentPackageDropDown.Flash();
|
||||
contentPackageDropDown.Flash(useRectangleFlash: true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ namespace Barotrauma.CharacterEditor
|
||||
if (!File.Exists(evaluatedTexturePath))
|
||||
{
|
||||
GUI.AddMessage(GetCharacterEditorTranslation("TextureDoesNotExist"), GUIStyle.Red);
|
||||
texturePathElement.Flash(GUIStyle.Red);
|
||||
texturePathElement.Flash(useRectangleFlash: true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -425,7 +425,7 @@ namespace Barotrauma.CharacterEditor
|
||||
if (!path.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
GUI.AddMessage(TextManager.Get("WrongFileType"), GUIStyle.Red);
|
||||
texturePathElement.Flash(GUIStyle.Red);
|
||||
texturePathElement.Flash(useRectangleFlash: true);
|
||||
return false;
|
||||
}
|
||||
if (IsCopy)
|
||||
@@ -486,7 +486,8 @@ namespace Barotrauma.CharacterEditor
|
||||
{
|
||||
PlaySoundOnSelect = true,
|
||||
};
|
||||
var removeLimbButton = new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), limbEditLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton")
|
||||
var limbButtonSize = Vector2.One * 0.8f;
|
||||
var removeLimbButton = new GUIButton(new RectTransform(limbButtonSize, limbEditLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton")
|
||||
{
|
||||
OnClicked = (b, d) =>
|
||||
{
|
||||
@@ -497,7 +498,7 @@ namespace Barotrauma.CharacterEditor
|
||||
return true;
|
||||
}
|
||||
};
|
||||
var addLimbButton = new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), limbEditLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton")
|
||||
var addLimbButton = new GUIButton(new RectTransform(limbButtonSize, limbEditLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton")
|
||||
{
|
||||
OnClicked = (b, d) =>
|
||||
{
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace Barotrauma
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
Hull.EditFire = false;
|
||||
Hull.EditWater = false;
|
||||
#endif
|
||||
HumanAIController.DisableCrewAI = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected virtual void DeselectEditorSpecific() { }
|
||||
|
||||
@@ -334,7 +334,7 @@ namespace Barotrauma
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
string url = TextManager.Get("EditorDisclaimerWikiUrl").Fallback("https://barotraumagame.com/wiki").Value;
|
||||
GameMain.Instance.ShowOpenUrlInWebBrowserPrompt(url, promptExtensionTag: "wikinotice");
|
||||
GameMain.ShowOpenUrlInWebBrowserPrompt(url, promptExtensionTag: "wikinotice");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -1011,7 +1011,7 @@ namespace Barotrauma
|
||||
GUI.DrawLine(spriteBatch, textPos, textPos - Vector2.UnitX * textSize.X, mouseOn ? Color.White : Color.White * 0.7f);
|
||||
if (mouseOn && PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
GameMain.Instance.ShowOpenUrlInWebBrowserPrompt("http://privacypolicy.daedalic.com");
|
||||
GameMain.ShowOpenUrlInWebBrowserPrompt("http://privacypolicy.daedalic.com");
|
||||
}
|
||||
}
|
||||
textPos.Y -= textSize.Y;
|
||||
|
||||
@@ -1817,7 +1817,11 @@ namespace Barotrauma
|
||||
subList = dropDown.ListBox.Content;
|
||||
}
|
||||
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), subList.RectTransform) { MinSize = new Point(0, 25) },
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), subList.RectTransform)
|
||||
{
|
||||
//enough space for 2 lines (price and class) + some padding
|
||||
MinSize = new Point(0, (int)(GUIStyle.SmallFont.LineHeight * 2.3f))
|
||||
},
|
||||
style: "ListBoxElement")
|
||||
{
|
||||
ToolTip = sub.Description,
|
||||
|
||||
+15
-2
@@ -7,6 +7,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -953,8 +955,19 @@ namespace Barotrauma
|
||||
okButton.Enabled = false;
|
||||
okButton.OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (!Endpoint.Parse(endpointBox.Text).TryUnwrap(out var endpoint)) { return false; }
|
||||
JoinServer(endpoint, "");
|
||||
if (Endpoint.Parse(endpointBox.Text).TryUnwrap(out var endpoint))
|
||||
{
|
||||
JoinServer(endpoint, "");
|
||||
}
|
||||
else if (LidgrenEndpoint.ParseFromWithHostNameCheck(endpointBox.Text, tryParseHostName: true).TryUnwrap(out var lidgrenEndpoint))
|
||||
{
|
||||
JoinServer(lidgrenEndpoint, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("error"), TextManager.GetWithVariable("invalidipaddress", "[serverip]:[port]", endpointBox.Text));
|
||||
endpointBox.Flash();
|
||||
}
|
||||
msgBox.Close();
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -379,6 +379,9 @@ namespace Barotrauma
|
||||
|
||||
void CreateSprite(ContentXElement element)
|
||||
{
|
||||
//empty element, probably an item variant?
|
||||
if (element.Attributes().None()) { return; }
|
||||
|
||||
string spriteFolder = "";
|
||||
ContentPath texturePath = null;
|
||||
|
||||
|
||||
@@ -1920,9 +1920,15 @@ namespace Barotrauma
|
||||
{
|
||||
filePath = $"{ContentPath.ModDirStr}/{filePath[packageDir.Length..]}";
|
||||
}
|
||||
if (!modProject.Files.Any(f => f.Type == subFileType &&
|
||||
f.Path == filePath))
|
||||
if (!modProject.Files.Any(f => f.Type == subFileType && f.Path == filePath))
|
||||
{
|
||||
//check if there's a file with the same name but different filename case
|
||||
var matchingFile = modProject.Files.FirstOrDefault(f => f.Type == subFileType && filePath.CleanUpPath().Equals(f.Path.CleanUpPath(), StringComparison.OrdinalIgnoreCase));
|
||||
if (matchingFile != null)
|
||||
{
|
||||
File.Delete(matchingFile.Path.Replace(ContentPath.ModDirStr, packageDir));
|
||||
modProject.RemoveFile(matchingFile);
|
||||
}
|
||||
var newFile = ModProject.File.FromPath(filePath, subFileType);
|
||||
modProject.AddFile(newFile);
|
||||
}
|
||||
@@ -2479,7 +2485,7 @@ namespace Barotrauma
|
||||
|
||||
new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), tierGroup.RectTransform), NumberType.Int)
|
||||
{
|
||||
IntValue = SubmarineInfo.GetDefaultTier(MainSub.Info.Price),
|
||||
IntValue = MainSub.Info.Tier,
|
||||
MinValueInt = 1,
|
||||
MaxValueInt = 3,
|
||||
OnValueChanged = (numberInput) =>
|
||||
@@ -2821,6 +2827,7 @@ namespace Barotrauma
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
var requiredPackages = MapEntity.mapEntityList.Select(e => e.Prefab.ContentPackage)
|
||||
.Where(cp => cp != null)
|
||||
.Distinct().OfType<ContentPackage>().Select(p => p.Name).ToHashSet();
|
||||
var tickboxes = requiredContentPackList.Content.Children.OfType<GUITickBox>().ToArray();
|
||||
tickboxes.ForEach(tb => tb.Selected = requiredPackages.Contains(tb.UserData as string ?? ""));
|
||||
@@ -2919,7 +2926,7 @@ namespace Barotrauma
|
||||
|
||||
subTypeDropdown.SelectItem(MainSub.Info.Type);
|
||||
|
||||
if (quickSave) { SaveSub(null); }
|
||||
if (quickSave) { SaveSub(packageToSaveInList.SelectedData as ContentPackage); }
|
||||
}
|
||||
|
||||
private void CreateSaveAssemblyScreen()
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Barotrauma
|
||||
|
||||
public static Character? dummyCharacter;
|
||||
public static Effect? BlueprintEffect;
|
||||
public TabMenu? TabMenu;
|
||||
|
||||
public TestScreen()
|
||||
{
|
||||
@@ -49,9 +50,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
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(Rand.RandSync.Unsynced));
|
||||
dummyCharacter.Info.Job = new Job(JobPrefab.Prefabs.FirstOrDefault(static jp => jp.Identifier == "assistant"));
|
||||
dummyCharacter.Info.Name = "Galldren";
|
||||
dummyCharacter.Inventory.CreateSlots();
|
||||
dummyCharacter.Info.GiveExperience(999999);
|
||||
|
||||
miniMapItem = new Item(ItemPrefab.Find(null, "deconstructor".ToIdentifier()), Vector2.Zero, null, 1337, false);
|
||||
|
||||
@@ -61,6 +63,7 @@ namespace Barotrauma
|
||||
}
|
||||
Character.Controlled = dummyCharacter;
|
||||
GameMain.World.ProcessChanges();
|
||||
TabMenu = new TabMenu();
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
@@ -68,35 +71,37 @@ namespace Barotrauma
|
||||
Frame.AddToGUIUpdateList();
|
||||
CharacterHUD.AddToGUIUpdateList(dummyCharacter);
|
||||
dummyCharacter?.SelectedItem?.AddToGUIUpdateList();
|
||||
TabMenu?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
TabMenu?.Update((float)deltaTime);
|
||||
|
||||
if (dummyCharacter is { } dummy && miniMapItem is { } item)
|
||||
{
|
||||
if (dummy.SelectedItem != item)
|
||||
{
|
||||
dummy.SelectedItem = item;
|
||||
}
|
||||
|
||||
dummy.SelectedItem?.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);
|
||||
}
|
||||
// if (dummyCharacter is { } dummy && miniMapItem is { } item)
|
||||
// {
|
||||
// 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);
|
||||
// }
|
||||
}
|
||||
|
||||
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
|
||||
|
||||
@@ -1332,16 +1332,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void TrySendNetworkUpdate(ISerializableEntity entity, SerializableProperty property)
|
||||
private static void TrySendNetworkUpdate(ISerializableEntity entity, SerializableProperty property)
|
||||
{
|
||||
if (entity is ItemComponent e)
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
entity = e.Item;
|
||||
}
|
||||
|
||||
if (GameMain.Client != null && entity is Item item)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(item, new Item.ChangePropertyEventData(property));
|
||||
if (entity is Item item)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(item, new Item.ChangePropertyEventData(property, item));
|
||||
}
|
||||
else if (entity is ItemComponent ic)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(ic.Item, new Item.ChangePropertyEventData(property, ic));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -785,13 +785,7 @@ namespace Barotrauma
|
||||
{
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
GameSettings.SetCurrentConfig(unsavedConfig);
|
||||
if (WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu &&
|
||||
mutableWorkshopMenu.CurrentTab == MutableWorkshopMenu.Tab.InstalledMods)
|
||||
{
|
||||
mutableWorkshopMenu.Apply();
|
||||
}
|
||||
GameSettings.SaveCurrentConfig();
|
||||
ApplyInstalledModChanges();
|
||||
mainFrame.Flash(color: GUIStyle.Green);
|
||||
return false;
|
||||
},
|
||||
@@ -804,6 +798,17 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
public void ApplyInstalledModChanges()
|
||||
{
|
||||
GameSettings.SetCurrentConfig(unsavedConfig);
|
||||
if (WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu &&
|
||||
mutableWorkshopMenu.CurrentTab == MutableWorkshopMenu.Tab.InstalledMods)
|
||||
{
|
||||
mutableWorkshopMenu.Apply();
|
||||
}
|
||||
GameSettings.SaveCurrentConfig();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (GameMain.Client is null || GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Disabled)
|
||||
|
||||
@@ -21,6 +21,13 @@ namespace Barotrauma
|
||||
private Entity soundEmitter;
|
||||
private double loopStartTime;
|
||||
private bool loopSound;
|
||||
/// <summary>
|
||||
/// Each new sound overrides the existing sounds that were launched with this status effect, meaning the old sound will be faded out and disposed and the new sound will be played instead of the old.
|
||||
/// Normally the call to play the sound is ignored if there's an existing sound playing when the effect triggers.
|
||||
/// Used for example for ensuring that rapid playing sounds restart playing even when the previous clip(s) have not yet stopped.
|
||||
/// Use with caution.
|
||||
/// </summary>
|
||||
private bool forcePlaySounds;
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element, string parentDebugName)
|
||||
{
|
||||
@@ -50,6 +57,7 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
forcePlaySounds = element.GetAttributeBool(nameof(forcePlaySounds), false);
|
||||
}
|
||||
|
||||
partial void ApplyProjSpecific(float deltaTime, Entity entity, IReadOnlyList<ISerializableEntity> targets, Hull hull, Vector2 worldPosition, bool playSound)
|
||||
@@ -71,7 +79,7 @@ namespace Barotrauma
|
||||
{
|
||||
angle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
particleRotation = -item.body.Rotation;
|
||||
if (item.body.Dir < 0.0f)
|
||||
if (emitter.Prefab.Properties.CopyEntityDir && item.body.Dir < 0.0f)
|
||||
{
|
||||
particleRotation += MathHelper.Pi;
|
||||
mirrorAngle = true;
|
||||
@@ -96,7 +104,9 @@ namespace Barotrauma
|
||||
{
|
||||
angle = targetLimb.body.Rotation + ((targetLimb.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
particleRotation = -targetLimb.body.Rotation;
|
||||
if (targetLimb.body.Dir < 0.0f)
|
||||
float offset = targetLimb.Params.GetSpriteOrientation() - MathHelper.PiOver2;
|
||||
particleRotation += offset;
|
||||
if (emitter.Prefab.Properties.CopyEntityDir && targetLimb.body.Dir < 0.0f)
|
||||
{
|
||||
particleRotation += MathHelper.Pi;
|
||||
mirrorAngle = true;
|
||||
@@ -112,10 +122,14 @@ namespace Barotrauma
|
||||
|
||||
private void PlaySound(Entity entity, Hull hull, Vector2 worldPosition)
|
||||
{
|
||||
if (sounds.Count == 0) return;
|
||||
if (sounds.Count == 0) { return; }
|
||||
|
||||
if (soundChannel == null || !soundChannel.IsPlaying)
|
||||
if (soundChannel == null || !soundChannel.IsPlaying || forcePlaySounds)
|
||||
{
|
||||
if (soundChannel != null && soundChannel.IsPlaying)
|
||||
{
|
||||
soundChannel.FadeOutAndDispose();
|
||||
}
|
||||
if (soundSelectionMode == SoundSelectionMode.All)
|
||||
{
|
||||
foreach (RoundSound sound in sounds)
|
||||
|
||||
+16
-7
@@ -93,7 +93,7 @@ namespace Barotrauma.Steam
|
||||
return (left, center, right);
|
||||
}
|
||||
|
||||
private void HandleDraggingAcrossModLists(GUIListBox from, GUIListBox to)
|
||||
private static void HandleDraggingAcrossModLists(GUIListBox from, GUIListBox to)
|
||||
{
|
||||
if (to.Rect.Contains(PlayerInput.MousePosition) && from.DraggedElement != null)
|
||||
{
|
||||
@@ -197,7 +197,11 @@ namespace Barotrauma.Steam
|
||||
out onInstalledInfoButtonHit, out var deselect);
|
||||
|
||||
GUILayoutGroup mainLayout =
|
||||
new GUILayoutGroup(new RectTransform(Vector2.One, outerContainer.Content.RectTransform), childAnchor: Anchor.TopCenter);
|
||||
new GUILayoutGroup(new RectTransform(Vector2.One, outerContainer.Content.RectTransform), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
Stretch = true,
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
mainLayout.RectTransform.SetAsFirstChild();
|
||||
|
||||
var (topLeft, _, topRight) = CreateSidebars(mainLayout, centerWidth: 0.05f, leftWidth: 0.475f, rightWidth: 0.475f, height: 0.13f);
|
||||
@@ -257,7 +261,12 @@ namespace Barotrauma.Steam
|
||||
right.ChildAnchor = Anchor.TopRight;
|
||||
|
||||
//enabled mods
|
||||
Label(left, TextManager.Get("enabledregular"), GUIStyle.SubHeadingFont);
|
||||
var label = Label(left, TextManager.Get("enabledregular"), GUIStyle.SubHeadingFont);
|
||||
new GUIImage(new RectTransform(new Point(label.Rect.Height), label.RectTransform, Anchor.CenterRight), style: "GUIButtonInfo")
|
||||
{
|
||||
ToolTip = TextManager.Get("ModLoadOrderExplanation")
|
||||
};
|
||||
|
||||
var enabledModsList = new GUIListBox(new RectTransform((1.0f, 0.93f), left.RectTransform))
|
||||
{
|
||||
CurrentDragMode = GUIListBox.DragMode.DragOutsideBox,
|
||||
@@ -478,7 +487,7 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
string str = modsListFilter.Text;
|
||||
enabledRegularModsList.Content.Children.Concat(disabledRegularModsList.Content.Children)
|
||||
.ForEach(c => c.Visible = !(c.UserData is ContentPackage p)
|
||||
.ForEach(c => c.Visible = c.UserData is not ContentPackage p
|
||||
|| ModNameMatches(p, str) && ModMatchesTickboxes(p, c));
|
||||
}
|
||||
|
||||
@@ -504,12 +513,12 @@ namespace Barotrauma.Steam
|
||||
//are enabled, and all files match either of them so show this mod
|
||||
}
|
||||
else if (modsListFilterTickboxes[Filter.ShowOnlySubs].Selected
|
||||
&& p.Files.Any(f => !(f is BaseSubFile)))
|
||||
&& p.Files.Any(f => f is not BaseSubFile))
|
||||
{
|
||||
matches = false;
|
||||
}
|
||||
else if (modsListFilterTickboxes[Filter.ShowOnlyItemAssemblies].Selected
|
||||
&& p.Files.Any(f => !(f is ItemAssemblyFile)))
|
||||
&& p.Files.Any(f => f is not ItemAssemblyFile))
|
||||
{
|
||||
matches = false;
|
||||
}
|
||||
@@ -520,7 +529,7 @@ namespace Barotrauma.Steam
|
||||
private void PrepareToShowModInfo(ContentPackage mod)
|
||||
{
|
||||
if (!mod.UgcId.TryUnwrap(out var ugcId)
|
||||
|| !(ugcId is SteamWorkshopId workshopId)) { return; }
|
||||
|| ugcId is not SteamWorkshopId workshopId) { return; }
|
||||
TaskPool.Add($"PrepareToShow{mod.UgcId}Info", SteamManager.Workshop.GetItem(workshopId.Value),
|
||||
t =>
|
||||
{
|
||||
|
||||
@@ -598,13 +598,14 @@ namespace Barotrauma.Steam
|
||||
|
||||
bool reinstallAction(GUIButton button, object o)
|
||||
{
|
||||
SettingsMenu.Instance?.ApplyInstalledModChanges();
|
||||
int prevIndex = ContentPackageManager.EnabledPackages.Regular.IndexOf(contentPackage);
|
||||
TaskPool.AddIfNotFound($"Reinstall{workshopItem.Id}",
|
||||
SteamManager.Workshop.Reinstall(workshopItem), t =>
|
||||
{
|
||||
ContentPackageManager.WorkshopPackages.Refresh();
|
||||
ContentPackageManager.EnabledPackages.RefreshUpdatedMods();
|
||||
if (SettingsMenu.Instance?.WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu)
|
||||
if (SettingsMenu.Instance?.WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu && !mutableWorkshopMenu.ViewingItemDetails)
|
||||
{
|
||||
mutableWorkshopMenu.PopulateInstalledModLists(forceRefreshEnabled: true);
|
||||
}
|
||||
|
||||
+16
-11
@@ -44,12 +44,26 @@ namespace Barotrauma.Steam
|
||||
public MutableWorkshopMenu(GUIFrame parent) : base(parent)
|
||||
{
|
||||
var mainLayout
|
||||
= new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: false);
|
||||
= new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: false)
|
||||
{
|
||||
Stretch = true,
|
||||
AbsoluteSpacing = GUI.IntScale(4)
|
||||
};
|
||||
|
||||
tabber = new GUILayoutGroup(new RectTransform((1.0f, 0.05f), mainLayout.RectTransform), isHorizontal: true)
|
||||
{ Stretch = true };
|
||||
tabContents = new Dictionary<Tab, (GUIButton Button, GUIFrame Content)>();
|
||||
|
||||
new GUIButton(new RectTransform((1.0f, 0.05f), mainLayout.RectTransform, Anchor.BottomLeft),
|
||||
style: "GUIButtonSmall", text: TextManager.Get("FindModsButton"))
|
||||
{
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
SteamManager.OverlayCustomUrl($"https://steamcommunity.com/app/{SteamManager.AppID}/workshop/");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
contentFrame = new GUIFrame(new RectTransform((1.0f, 0.95f), mainLayout.RectTransform), style: null);
|
||||
|
||||
new GUICustomComponent(new RectTransform(Vector2.Zero, mainLayout.RectTransform),
|
||||
@@ -130,17 +144,8 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
tabContents[Tab.PopularMods].Button.Enabled = false;
|
||||
}
|
||||
GUIFrame listFrame = new GUIFrame(new RectTransform((1.0f, 0.95f), content.RectTransform), style: null);
|
||||
GUIFrame listFrame = new GUIFrame(new RectTransform(Vector2.One, content.RectTransform), style: null);
|
||||
CreateWorkshopItemList(listFrame, out _, out popularModsList, onSelected: PopulateFrameWithItemInfo);
|
||||
new GUIButton(new RectTransform((1.0f, 0.05f), content.RectTransform, Anchor.BottomLeft),
|
||||
style: "GUIButtonSmall", text: TextManager.Get("FindModsButton"))
|
||||
{
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
SteamManager.OverlayCustomUrl($"https://steamcommunity.com/app/{SteamManager.AppID}/workshop/");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void CreatePublishTab(out GUIListBox selfModsList)
|
||||
|
||||
@@ -543,7 +543,8 @@ namespace Barotrauma.Steam
|
||||
|
||||
var localModProject = new ModProject(localPackage)
|
||||
{
|
||||
UgcId = Option<ContentPackageId>.Some(new SteamWorkshopId(resultId))
|
||||
UgcId = Option<ContentPackageId>.Some(new SteamWorkshopId(resultId)),
|
||||
ModVersion = modVersion
|
||||
};
|
||||
localModProject.DiscardHashAndInstallTime();
|
||||
localModProject.Save(localPackage.Path);
|
||||
|
||||
Reference in New Issue
Block a user