diff --git a/.github/DISCUSSION_TEMPLATE/bug-reports.yml b/.github/DISCUSSION_TEMPLATE/bug-reports.yml index 1ac7cf06e..fcd51cd55 100644 --- a/.github/DISCUSSION_TEMPLATE/bug-reports.yml +++ b/.github/DISCUSSION_TEMPLATE/bug-reports.yml @@ -73,7 +73,7 @@ body: label: Version description: Which version of the game did the bug happen in? You can see the current version number in the bottom left corner of your screen in the main menu. options: - - v1.8.8.1 (Calm Before the Storm Hotfix 2) + - v1.9.7.0 (Summer Update 2025) - Other validations: required: true diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs index 0d0b65cb1..7b92c12ac 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs @@ -435,6 +435,13 @@ namespace Barotrauma { cam.OffsetAmount = targetOffsetAmount = item.Prefab.OffsetOnSelected * item.OffsetOnSelectedMultiplier; } + else if (HeldItems.SelectMany(static item => item.GetComponents()) + .Where(static holdable => holdable.Aimable) + .MaxOrNull(static holdable => holdable.CameraAimOffset) is float maxOffset + && maxOffset > 0f && IsKeyDown(InputType.Aim)) + { + cam.OffsetAmount = targetOffsetAmount = maxOffset; + } else if (SelectedItem != null && ViewTarget == null && SelectedItem.Components.Any(ic => ic?.GuiFrame != null && ic.ShouldDrawHUD(this))) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs index 8c95ece49..77f71ceca 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs @@ -653,7 +653,10 @@ namespace Barotrauma (int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2), (int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f)), character.Info.IsDisguisedAsAnother); float yOffset = (GameMain.GameSession?.Campaign is MultiPlayerCampaign ? -10 : 4) * GUI.Scale; - character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, yOffset), targetWidth: HUDLayoutSettings.PortraitArea.Width, true, character.Info.IsDisguisedAsAnother); + + character.Info?.DrawIcon(spriteBatch, + new Vector2(HUDLayoutSettings.PortraitArea.Center.X - 12 * GUI.Scale, HUDLayoutSettings.PortraitArea.Center.Y), HUDLayoutSettings.PortraitArea.Size.ToVector2(), + flip: true); character.Info.DrawForeground(spriteBatch); } mouseOnPortrait = MouseOnCharacterPortrait() && !character.ShouldLockHud(); @@ -733,8 +736,9 @@ namespace Barotrauma string focusName = character.FocusedCharacter.Info == null ? character.FocusedCharacter.DisplayName : character.FocusedCharacter.Info.DisplayName; Vector2 textPos = startPos; - Vector2 textSize = GUIStyle.Font.MeasureString(focusName); - Vector2 largeTextSize = GUIStyle.SubHeadingFont.MeasureString(focusName); + //measure arbitrary one-line text instead of the potentially-multi-line name + Vector2 textSize = GUIStyle.Font.MeasureString("T"); + Vector2 largeTextSize = GUIStyle.SubHeadingFont.MeasureString("T"); textPos -= new Vector2(textSize.X / 2, textSize.Y); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs index 5afb4c2d0..028b373b2 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs @@ -360,64 +360,6 @@ namespace Barotrauma GUIStyle.Font.DrawString(spriteBatch, str, new Vector2(barRect.Right - iconXOffset - scaledTextSizeX - GUI.IntScale(4), barRect.Center.Y - scaledTextSizeY / 2), GUIStyle.TextColorNormal, 0f, Vector2.Zero, textScale, SpriteEffects.None, 0f); } - public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false, bool evaluateDisguise = false) - { - if (evaluateDisguise && IsDisguised) { return; } - - Vector2? sheetIndex; - Sprite portraitToDraw; - List attachmentsToDraw; - - Color hairColor; - Color facialHairColor; - Color skinColor; - - if (!IsDisguisedAsAnother || !evaluateDisguise) - { - sheetIndex = Head.SheetIndex; - portraitToDraw = Portrait; - attachmentsToDraw = AttachmentSprites; - - hairColor = Head.HairColor; - facialHairColor = Head.FacialHairColor; - skinColor = Head.SkinColor; - } - else - { - sheetIndex = disguisedSheetIndex; - portraitToDraw = disguisedPortrait; - attachmentsToDraw = disguisedAttachmentSprites; - - hairColor = disguisedHairColor; - facialHairColor = disguisedFacialHairColor; - skinColor = disguisedSkinColor; - } - - if (portraitToDraw != null) - { - var currEffect = spriteBatch.GetCurrentEffect(); - // Scale down the head sprite 10% - float scale = targetWidth * 0.9f / Portrait.size.X; - if (sheetIndex.HasValue) - { - SetHeadEffect(spriteBatch); - portraitToDraw.SourceRect = new Rectangle(CalculateOffset(portraitToDraw, sheetIndex.Value.ToPoint()), portraitToDraw.SourceRect.Size); - } - portraitToDraw.Draw(spriteBatch, screenPos + offset, skinColor, portraitToDraw.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None); - if (attachmentsToDraw != null) - { - float depthStep = 0.000001f; - foreach (var attachment in attachmentsToDraw) - { - SetAttachmentEffect(spriteBatch, attachment); - DrawAttachmentSprite(spriteBatch, attachment, portraitToDraw, sheetIndex, screenPos + offset, scale, depthStep, GetAttachmentColor(attachment, hairColor, facialHairColor), flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None); - depthStep += depthStep; - } - } - spriteBatch.SwapEffect(currEffect); - } - } - //TODO: I hate this so much :( private SpriteBatch.EffectWithParams headEffectParameters; private Dictionary attachmentEffectParameters @@ -466,23 +408,26 @@ namespace Barotrauma } } - public void DrawIcon(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 targetAreaSize) + public void DrawIcon(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 targetAreaSize, bool flip = false) { var headSprite = HeadSprite; if (headSprite != null) { + var spriteEffects = flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None; + var currEffect = spriteBatch.GetCurrentEffect(); float scale = Math.Min(targetAreaSize.X / headSprite.size.X, targetAreaSize.Y / headSprite.size.Y); headSprite.SourceRect = new Rectangle(CalculateOffset(headSprite, Head.SheetIndex.ToPoint()), headSprite.SourceRect.Size); SetHeadEffect(spriteBatch); - headSprite.Draw(spriteBatch, screenPos, scale: scale, color: Head.SkinColor); + headSprite.Draw(spriteBatch, screenPos, scale: scale, color: Head.SkinColor, spriteEffect: spriteEffects); if (AttachmentSprites != null) { float depthStep = 0.000001f; foreach (var attachment in AttachmentSprites) { SetAttachmentEffect(spriteBatch, attachment); - DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep, GetAttachmentColor(attachment, Head.HairColor, Head.FacialHairColor)); + DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep, GetAttachmentColor(attachment, Head.HairColor, Head.FacialHairColor), + spriteEffects: spriteEffects); depthStep += depthStep; } } @@ -534,9 +479,6 @@ namespace Barotrauma { origin.Y = attachment.Sprite.size.Y - origin.Y; } - //the portrait's origin is forced to 0,0 (presumably for easier drawing on the UI?), see LoadHeadElement - //we need to take that into account here and draw the attachment at where the origin of the "actual" head sprite would be - drawPos += HeadSprite.Origin * scale; } float depth = attachment.Sprite.Depth; if (attachment.InheritLimbDepth) @@ -545,7 +487,6 @@ namespace Barotrauma } attachment.Sprite.Draw(spriteBatch, drawPos, color ?? Color.White, origin, rotate: 0, scale: scale, depth: depth, spriteEffect: spriteEffects); } - public static CharacterInfo ClientRead(Identifier speciesName, IReadMessage inc, bool requireJobPrefabFound = true) { ushort infoID = inc.ReadUInt16(); @@ -568,7 +509,6 @@ namespace Barotrauma Color hairColor = inc.ReadColorR8G8B8(); Color facialHairColor = inc.ReadColorR8G8B8(); - Identifier npcId = inc.ReadIdentifier(); Identifier factionId = inc.ReadIdentifier(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs index 06e788eb3..3b937d68a 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs @@ -833,6 +833,9 @@ namespace Barotrauma causeOfDeathAffliction = afflictionPrefab; } } + + Character killer = FindEntityByID(msg.ReadUInt16()) as Character; + bool containsAfflictionData = msg.ReadBoolean(); if (!IsDead) { @@ -842,7 +845,7 @@ namespace Barotrauma } else { - Kill(causeOfDeathType, causeOfDeathAffliction?.Instantiate(1.0f), true); + Kill(causeOfDeathType, causeOfDeathAffliction?.Instantiate(1.0f, killer), true); } } if (containsAfflictionData) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs index 275d5ce4d..c7bfa8924 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs @@ -221,7 +221,7 @@ namespace Barotrauma new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform, Anchor.CenterLeft), onDraw: (spriteBatch, component) => { - character.Info?.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), Vector2.Zero, component.Rect.Width, false, character != Character.Controlled); + character.Info?.DrawIcon(spriteBatch, component.Rect.Center.ToVector2(), component.Rect.Size.ToVector2()); }); characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont) { @@ -1041,8 +1041,29 @@ namespace Barotrauma foreach (KeyValuePair kvp in afflictions) { var affliction = kvp.Key; - affliction.Prefab.AfflictionOverlay?.Draw(spriteBatch, Vector2.Zero, Color.White * affliction.GetAfflictionOverlayMultiplier(), Vector2.Zero, 0.0f, - new Vector2(GameMain.GraphicsWidth / DamageOverlay.size.X, GameMain.GraphicsHeight / DamageOverlay.size.Y)); + if (affliction.Prefab is AfflictionPrefab { AfflictionOverlay: not null } afflictionPrefab) + { + Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight); + if (afflictionPrefab.AfflictionOverlay is SpriteSheet spriteSheet) + { + spriteSheet.Draw(spriteBatch, + spriteIndex: spriteSheet.GetAnimatedSpriteIndex(afflictionPrefab.AfflictionOverlayAnimSpeed), + pos: Vector2.Zero, + color: Color.White * affliction.GetAfflictionOverlayMultiplier(), + origin: Vector2.Zero, + rotate: 0, + scale: screenSize / spriteSheet.FrameSize.ToVector2()); + } + else if (afflictionPrefab.AfflictionOverlay is Sprite sprite) + { + sprite.Draw(spriteBatch, + pos: Vector2.Zero, + color: Color.White * affliction.GetAfflictionOverlayMultiplier(), + origin: Vector2.Zero, + rotate: 0, + scale: screenSize / sprite.size); + } + } var activeEffect = affliction.GetActiveEffect(); if (activeEffect is { ThermalOverlayRange: > 0.0f }) @@ -1554,9 +1575,9 @@ namespace Barotrauma }; var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), parent.RectTransform), - affliction.Prefab.GetDescription( + RichString.Rich(affliction.Prefab.GetDescription( affliction.Strength, - Character == Character.Controlled ? AfflictionPrefab.Description.TargetType.Self : AfflictionPrefab.Description.TargetType.OtherCharacter), + Character == Character.Controlled ? AfflictionPrefab.Description.TargetType.Self : AfflictionPrefab.Description.TargetType.OtherCharacter)), textAlignment: Alignment.TopLeft, wrap: true) { CanBeFocused = false diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index 5f2fabb17..f8082821b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -912,6 +912,14 @@ namespace Barotrauma DebugConsole.ThrowError("The command 'wikiimage_sub' failed.", e); } })); + + AssignOnExecute("loslightingfreecam", (string[] args) => + { + ExecuteCommand("los"); + ExecuteCommand("lighting"); + ExecuteCommand("freecam"); + }); + AssignRelayToServer("loslightingfreecam", false); AssignRelayToServer("kick", false); AssignRelayToServer("kickid", false); @@ -1101,6 +1109,42 @@ namespace Barotrauma (lightComponent.LightColor.A / 255.0f) * value.W); } }, isCheat: false)); + + commands.Add(new Command("steamtimelinetest", "steamtimelinetest: Test the Steamworks timeline feature.", (string[] args) => + { + // Add an instantaneous event to the Steam timeline + var eventHandle = Steamworks.SteamTimeline.AddInstantaneousTimelineEvent( + "Barotrauma Test Event", + "This is a test event created from the debug console", + "steam_marker", // Important: icon must be specified, or it does nothing :D + 1, // Priority + 0.0f, // Current time (no offset) + Steamworks.TimelineEventClipPriority.Standard); + + NewMessage($"Steamworks timeline test: Added instantaneous event with handle: {eventHandle}", Color.Yellow); + })); + + commands.Add(new Command("setsteamtimelinegamemode", "setsteamtimelinegamemode [gamemode]: Sets the Steam timeline gamemode to the specified value.", args => + { + if (args.Length == 0) + { + NewMessage("Please specify a gamemode. Available modes: " + string.Join(", ", Enum.GetNames(typeof(SteamTimelineManager.TimelineGameMode))), Color.Red); + return; + } + + if (Enum.TryParse(args[0], ignoreCase: true, out var gameMode)) + { + SteamTimelineManager.SetTimelineGameMode(gameMode); + NewMessage($"Timeline gamemode set to: {gameMode}", Color.Green); + } + else + { + NewMessage($"Invalid gamemode '{args[0]}'. Available modes: " + string.Join(", ", Enum.GetNames(typeof(SteamTimelineManager.TimelineGameMode))), Color.Red); + } + }, isCheat: true, getValidArgs: () => + { + return new[] { Enum.GetNames(typeof(SteamTimelineManager.TimelineGameMode)) }; + })); commands.Add(new Command("color|colour", "Change color (as bytes from 0 to 255) of the selected item/structure instances. Applied only in the subeditor.", (string[] args) => { @@ -2535,6 +2579,85 @@ namespace Barotrauma #if DEBUG + commands.Add(new Command( + "listachievements", + "listachievements: Lists all achievement identifiers, names, descriptions, and their current Steam status (Locked/Unlocked).", + (string[] args) => + { + NewMessage("--- Achievement Status: Name - (Identifier) - [Status] - Description ---", Color.Cyan); + + var supportedIds = AchievementManager.SupportedAchievements; + + if (supportedIds == null || !supportedIds.Any()) + { + NewMessage("No achievement identifiers found in AchievementManager.SupportedAchievements.", Color.Yellow); + NewMessage("-------------------------------------------------------------------", Color.Cyan); + return; + } + + if (!SteamManager.IsInitialized || !Steamworks.SteamClient.IsValid) + { + NewMessage("Steam not initialized. Cannot fetch achievement status or texts.", Color.Red); + foreach (Identifier id in supportedIds.OrderBy(i => i.Value)) + { + NewMessage($"- Name N/A - ({id.Value}) - [Status Unknown] - Description N/A", Color.Red); + } + } + else + { + try + { + var steamAchievements = Steamworks.SteamUserStats.Achievements + .ToDictionary(a => a.Identifier, StringComparer.OrdinalIgnoreCase); + + foreach (Identifier id in supportedIds.OrderBy(i => i.Value)) + { + string statusText; + string nameText = "Name N/A"; + string descText = "Description N/A"; + Color statusColor; + + if (steamAchievements.TryGetValue(id.Value, out var steamAch)) + { + nameText = steamAch.Name ?? "Name N/A"; + descText = steamAch.Description ?? "Description N/A"; + + if (steamAch.State) + { + statusText = "[Unlocked]"; + statusColor = Color.LimeGreen; + } + else + { + statusText = "[Locked]"; + statusColor = Color.Orange; + } + } + else + { + statusText = "[Not Found on Steam]"; + statusColor = Color.Red; + } + + string output = $"- {nameText} - ({id.Value}) - {statusText} - {descText}"; + NewMessage(output, statusColor); + } + } + catch (Exception e) + { + ThrowError("Error retrieving achievement statuses/texts from Steam.", e); + foreach (Identifier id in supportedIds.OrderBy(i => i.Value)) + { + NewMessage($"- Name N/A - ({id.Value}) - [Status Error] - Description N/A", Color.Red); + } + } + } + + NewMessage("-------------------------------------------------------------------", Color.Cyan); + }, + isCheat: true + )); + commands.Add(new Command("unlockachievement", "unlockachievement [identifier]: Unlocks the specified achievement.", (string[] args) => { if (args.Length < 1) @@ -2545,6 +2668,60 @@ namespace Barotrauma NewMessage($"Unlocked \"{args[0]}\"."); AchievementManager.UnlockAchievement(args[0].ToIdentifier()); }, isCheat: true)); + + commands.Add(new Command( + "resetachievement", + "resetachievement [identifier]: Clears (locks) the specified Steam achievement for testing.", + args => + { + if (args.Length < 1) + { + ThrowError("Please specify the achievement identifier to reset."); + return; + } + + if (!SteamManager.IsInitialized || !Steamworks.SteamClient.IsValid) + { + ThrowError("Steam not initialized."); + return; + } + + string achievementId = args[0]; + bool found = false; + bool success = false; + + try + { + var achievement = Steamworks.SteamUserStats.Achievements + .FirstOrDefault(a => a.Identifier.Equals(achievementId, StringComparison.OrdinalIgnoreCase)); + + if (achievement.Identifier == null) + { + ThrowError($"Achievement with identifier \"{achievementId}\" not found."); + return; + } + + found = true; + success = achievement.Clear(); + + if (success) + { + // IMPORTANT: Store the stats to make the change persistent + SteamManager.StoreStats(); + NewMessage($"Reset achievement \"{achievementId}\".", Color.Yellow); + } + else + { + ThrowError($"Failed to clear achievement \"{achievementId}\" (Steamworks returned false)."); + } + } + catch (Exception e) + { + ThrowError($"An error occurred while trying to reset achievement \"{achievementId}\". Found: {found}, Success: {success}", e); + } + }, + isCheat: true + )); commands.Add(new Command("deathprompt", "Shows the death prompt for testing purposes.", (string[] args) => { @@ -2706,7 +2883,14 @@ namespace Barotrauma { int amount = 1; if (args.Length > 0) { int.TryParse(args[0], out amount); } - GameMain.LevelEditorScreen.TestLevelGenerationForErrors(amount); + try + { + GameMain.LevelEditorScreen.TestLevelGenerationForErrors(amount); + } + catch (Exception e) + { + ThrowError("Failed to generate levels", e); + } } else { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/SalvageMission.cs b/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/SalvageMission.cs index ac06e10fd..2009a3819 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/SalvageMission.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/SalvageMission.cs @@ -139,6 +139,6 @@ namespace Barotrauma } } - public override IEnumerable HudIconTargets => targets.Where(static t => !t.Retrieved && t.Item.GetRootInventoryOwner() is not Character { IsLocalPlayer: true }).Select(static t => t.Item); + public override IEnumerable HudIconTargets => targets.Where(static t => !t.Retrieved && t.Item?.GetRootInventoryOwner() is not Character { IsLocalPlayer: true }).Select(static t => t.Item); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs index c67ad7794..bf67f7b93 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs @@ -2236,6 +2236,29 @@ namespace Barotrauma return textBox; } + /// + /// Creates a pre-built filter box. + /// + /// + /// The filter function must be set using .. + /// + public static GUITextBox CreateFilterBox(RectTransform rectT) + { + GUITextBox textBox = new(rectT, createClearButton: true) + { + OnEnterPressed = (tb, _) => + { + tb.Deselect(); + return true; + } + }; + GUITextBlock label = new(new RectTransform(Vector2.One, textBox.TextBlock.RectTransform, Anchor.CenterLeft), TextManager.Get("serverlog.filter"), GUIStyle.TextColorNormal * 0.75f); + textBox.OnSelected += (_, _) => label.Visible = false; + textBox.OnDeselected += (tb, _) => label.Visible = tb.Text.IsNullOrEmpty(); + textBox.OnTextChanged += (tb, text) => label.Visible = !tb.Selected && text.IsNullOrEmpty(); + return textBox; + } + public static void NotifyPrompt(LocalizedString header, LocalizedString body) { GUIMessageBox msgBox = new GUIMessageBox(header, body, new[] { TextManager.Get("Ok") }, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175)); @@ -2295,9 +2318,61 @@ namespace Barotrauma return msgBox; } -#endregion +#nullable enable + #region Item UI + /// + /// Creates a 7-segment display. + /// + /// Returns if is or empty. + /// Defaults to TextManager.Get("kilowatt"). + /// Defaults to . + public static GUITextBlock CreateDigitalDisplay(RectTransform rect, out GUITextBlock? leftLabel, out GUITextBlock rightLabel, LocalizedString? leftLabelText = null, LocalizedString? rightLabelText = null, LocalizedString? tooltip = null, GUIFont? leftLabelFont = null) + { + GUILayoutGroup textArea = new(rect, isHorizontal: true, childAnchor: Anchor.CenterLeft) + { + Stretch = true, + CanBeFocused = true, + ToolTip = tooltip!, + AbsoluteSpacing = 5 + }; -#region Element positioning + leftLabel = null; + if (!leftLabelText.IsNullOrEmpty()) + { + leftLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1f), textArea.RectTransform), leftLabelText, textColor: GUIStyle.TextColorBright, font: leftLabelFont ?? GUIStyle.LargeFont, textAlignment: Alignment.CenterRight); + } + + GUIFrame displayBackground = new(new RectTransform(new Vector2(0.55f, 0.8f), textArea.RectTransform), style: "DigitalFrameDark"); + GUITextBlock displayText = new(new RectTransform(new Vector2(0.9f, 0.95f), displayBackground.RectTransform, Anchor.Center), "8888", font: GUIStyle.DigitalFont, textColor: GUIStyle.TextColorDark, textAlignment: Alignment.CenterRight); + displayText.TextScale = Math.Max((displayText.Rect.Height - 10) / GUIStyle.DigitalFont.LineHeight, 0.1f); + + rightLabel = new GUITextBlock(new RectTransform(Vector2.Zero, textArea.RectTransform), rightLabelText ?? TextManager.Get("kilowatt"), textColor: GUIStyle.TextColorNormal, font: GUIStyle.Font, textAlignment: Alignment.CenterRight) + { + Padding = Vector4.Zero + }; + rightLabel.RectTransform.MinSize = rightLabel.TextSize.ToPoint(); + + textArea.GetAllChildren().ForEach(child => child.CanBeFocused = false); + return displayText; + } + + /// Defaults to . + public static GUITickBox CreateIndicatorLight(RectTransform rect, string style = "", LocalizedString? label = null, LocalizedString? tooltip = null, GUIFont? labelFont = null) + { + GUITickBox indicator = new(rect, label, font: labelFont ?? GUIStyle.SubHeadingFont, style: style) + { + Enabled = false, + ToolTip = tooltip! + }; + indicator.TextBlock.OverrideTextColor(GUIStyle.TextColorNormal); + return indicator; + } + #endregion +#nullable restore + + #endregion + + #region Element positioning private static List CreateElements(int count, RectTransform parent, Func constructor, Vector2? relativeSize = null, Point? absoluteSize = null, Anchor anchor = Anchor.TopLeft, Pivot? pivot = null, Point? minSize = null, Point? maxSize = null, diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIButton.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIButton.cs index da33ae1cf..a5e5aa8c8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIButton.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIButton.cs @@ -211,7 +211,7 @@ namespace Barotrauma } var selfStyle = Style; - textBlock = new GUITextBlock(new RectTransform(Vector2.One, rectT, Anchor.Center), text, textAlignment: textAlignment, style: null) + textBlock = new GUITextBlock(new RectTransform(Vector2.One, rectT, Anchor.Center), RichString.Rich(text), textAlignment: textAlignment, style: null) { TextColor = selfStyle?.TextColor ?? Color.Black, HoverTextColor = selfStyle?.HoverTextColor ?? Color.Black, diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs index a96e57bf9..965b7ed90 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs @@ -446,19 +446,22 @@ namespace Barotrauma UpdateScrollBarSize(); } - public void Select(object userData, Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled) + public bool Select(object userData, Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled) { var children = Content.Children; int i = 0; + bool wasSelected = false; foreach (GUIComponent child in children) { if (Equals(child.UserData, userData)) { + wasSelected = true; Select(i, force, autoScroll); - if (!SelectMultiple) { return; } + if (!SelectMultiple) { return true; } } i++; } + return wasSelected; } private Point CalculateFrameSize(bool isHorizontal, int scrollBarSize) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/HRManagerUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/HRManagerUI.cs index cff44b879..928ae3519 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/HRManagerUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/HRManagerUI.cs @@ -1021,6 +1021,14 @@ namespace Barotrauma { MaxTextLength = Client.MaxNameLength }; + nameBox.OnTextChanged += (GUITextBox textBox, string text) => + { + if (text.Contains('\n') || text.Contains('\r')) + { + textBox.Text = text.Replace("\r\n", " ").Replace('\n', ' ').Replace('\r', ' '); + } + return true; + }; new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("confirm")) { OnClicked = (button, userData) => diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs index 85fe74086..99c4e785d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using System; using System.Collections.Generic; @@ -749,7 +749,7 @@ namespace Barotrauma { new GUICustomComponent(new RectTransform(Vector2.One, parent.RectTransform, scaleBasis: ScaleBasis.BothHeight), (spriteBatch, component) => { - info.DrawPortrait(spriteBatch, component.Rect.Location.ToVector2(), Vector2.Zero, component.Rect.Width); + info.DrawIcon(spriteBatch, component.Rect.Center.ToVector2(), component.Rect.Size.ToVector2()); }); GUILayoutGroup textGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.8f), parent.RectTransform)); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/TalentMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/TalentMenu.cs index 871dd8133..94aa5638e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/TalentMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/TalentMenu.cs @@ -273,7 +273,7 @@ namespace Barotrauma new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), topLayout.RectTransform), onDraw: (batch, component) => { - info.DrawPortrait(batch, component.Rect.Location.ToVector2(), Vector2.Zero, component.Rect.Width, false, false); + info.DrawIcon(batch, component.Rect.Center.ToVector2(), component.Rect.Size.ToVector2()); }); GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1f), topLayout.RectTransform)) @@ -327,7 +327,7 @@ namespace Barotrauma if (extraTalent.IsHiddenExtraTalent) { 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" + ToolBox.ExtendColorToPercentageSigns(extraTalent.Description.Value)), + ToolTip = GetTalentTooltip(extraTalent, characterInfo), Color = GUIStyle.Green }; } @@ -637,7 +637,7 @@ namespace Barotrauma 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 = CreateTooltip(talent, characterInfo), + ToolTip = GetTalentTooltip(talent, characterInfo), UserData = talent.Identifier, PressedColor = pressedColor, Enabled = info.Character != null, @@ -703,24 +703,6 @@ namespace Barotrauma }, }; - static RichString CreateTooltip(TalentPrefab talent, CharacterInfo? character) - { - LocalizedString progress = string.Empty; - - if (character is not null && talent.TrackedStat.TryUnwrap(out var stat)) - { - var statValue = character.GetSavedStatValue(StatTypes.None, stat.PermanentStatIdentifier); - var intValue = (int)MathF.Round(statValue); - progress = "\n\n"; - progress += statValue < stat.Max - ? TextManager.GetWithVariables("talentprogress", ("[amount]", intValue.ToString()), ("[max]", stat.Max.ToString())) - : TextManager.Get("talentprogresscompleted"); - } - - RichString tooltip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖\n\n{ToolBox.ExtendColorToPercentageSigns(talent.Description.Value)}{progress}"); - return tooltip; - } - talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = talentButton.DisabledColor = Color.Transparent; GUIComponent iconImage; @@ -814,6 +796,24 @@ namespace Barotrauma GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock); } + private static RichString GetTalentTooltip(TalentPrefab talent, CharacterInfo? character) + { + LocalizedString progress = string.Empty; + + if (character is not null && talent.TrackedStat.TryUnwrap(out var stat)) + { + var statValue = character.GetSavedStatValue(StatTypes.None, stat.PermanentStatIdentifier); + var intValue = (int)MathF.Round(statValue); + progress = "\n\n"; + progress += statValue < stat.Max + ? TextManager.GetWithVariables("talentprogress", ("[amount]", intValue.ToString()), ("[max]", stat.Max.ToString())) + : TextManager.Get("talentprogresscompleted"); + } + + RichString tooltip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖\n\n{ToolBox.ExtendColorToPercentageSigns(talent.Description.Value)}{progress}"); + return tooltip; + } + private bool ResetTalentSelection(GUIButton guiButton, object userData) { if (characterInfo is null) { return false; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs index dc2913de1..e8e9dd49f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using Barotrauma.Extensions; using Barotrauma.Items.Components; +using Barotrauma.Networking; using FarseerPhysics; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -949,7 +950,7 @@ namespace Barotrauma GUILayoutGroup buttonLayout = new GUILayoutGroup(rectT(1f, 1f, toggleButton.Frame), isHorizontal: true); LocalizedString slotText = ""; - if (linkedItems.Count() > 1) + if (linkedItems.Count > 1) { slotText = TextManager.GetWithVariable("weaponslot", "[number]", string.Join(", ", linkedItems.Select(it => (swappableEntities.IndexOf(it) + 1).ToString()))); } @@ -978,7 +979,7 @@ namespace Barotrauma List frames = new List(); if (currentOrPending != null) { - bool canUninstall = item.PendingItemSwap != null || !(currentOrPending.SwappableItem?.ReplacementOnUninstall.IsEmpty ?? true); + bool canUninstall = HasPermission && (item.PendingItemSwap != null || !(currentOrPending.SwappableItem?.ReplacementOnUninstall.IsEmpty ?? true)); bool isUninstallPending = item.Prefab.SwappableItem != null && item.PendingItemSwap?.Identifier == item.Prefab.SwappableItem.ReplacementOnUninstall; if (isUninstallPending) { canUninstall = false; } @@ -1030,7 +1031,8 @@ namespace Barotrauma buttonStyle: isPurchased ? "WeaponInstallButton" : "StoreAddToCrateButton").Frame); if (!(frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton buyButton)) { continue; } - if (PlayerBalance >= price) + + if (HasPermission && PlayerBalance >= price) { buyButton.Enabled = true; buyButton.OnClicked += (button, o) => @@ -1272,7 +1274,7 @@ namespace Barotrauma if (!prefabFrame.BuyButton.TryUnwrap(out BuyButtonFrame buyButtonFrame)) { return; } - if (!HasPermission || !prefab.IsApplicable(submarine.Info) || (itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab)))) + if (!prefab.IsApplicable(submarine.Info) || (itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab)))) { prefabFrame.Frame.Enabled = false; prefabFrame.Description.Enabled = false; @@ -1289,12 +1291,14 @@ namespace Barotrauma ("[amount]", prefab.Price.GetBuyPrice(prefab, Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation, characterList).ToString())); currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () => { - if (GameMain.NetworkMember != null) + if (Campaign.UpgradeManager.TryPurchaseUpgrade(prefab, category)) { - WaitForServerUpdate = true; + if (GameMain.NetworkMember != null) + { + WaitForServerUpdate = true; + } + GameMain.Client?.SendCampaignState(); } - Campaign.UpgradeManager.PurchaseUpgrade(prefab, category); - GameMain.Client?.SendCampaignState(); return true; }, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction); @@ -1722,7 +1726,7 @@ namespace Barotrauma if (buttonParent.FindChild(UpgradeStoreUserData.BuyButton, recursive: true) is GUIButton button) { - bool canBuy = !WaitForServerUpdate && !isMax && campaign.GetBalance() >= price && prefab.HasResourcesToUpgrade(Character.Controlled, currentLevel + 1); + bool canBuy = !WaitForServerUpdate && HasPermission && !isMax && campaign.GetBalance() >= price && prefab.HasResourcesToUpgrade(Character.Controlled, currentLevel + 1); button.Enabled = canBuy; } @@ -1935,7 +1939,7 @@ namespace Barotrauma return frames.ToArray(); } - private bool HasPermission => true; + private static bool HasPermission => CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageCampaign); // just a shortcut to create new RectTransforms since all the new RectTransform and new Vector2 confuses my IDE (and me) private static RectTransform rectT(float x, float y, GUIComponent parentComponent, Anchor anchor = Anchor.TopLeft, ScaleBasis scaleBasis = ScaleBasis.Normal) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs index c476a66ce..98d89bd8b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs @@ -686,6 +686,7 @@ namespace Barotrauma private void OnInvitedToSteamGame(string connectCommand) { + DebugConsole.NewMessage($"Invited to Steam game, connect command: {connectCommand}", Color.Lime); try { ConnectCommand = Barotrauma.Networking.ConnectCommand.Parse(ToolBox.SplitCommand(connectCommand)); @@ -825,6 +826,7 @@ namespace Barotrauma { if (ConnectCommand.TryUnwrap(out var connectCommand)) { + DebugConsole.NewMessage($"Processing connect command: {connectCommand}...", Color.Lime); if (Client != null) { Client.Quit(); @@ -836,6 +838,7 @@ namespace Barotrauma if (connectCommand.SteamLobbyIdOption.TryUnwrap(out var lobbyId)) { + DebugConsole.NewMessage($"Connecting to lobby ID {lobbyId}...", Color.Lime); SteamManager.JoinLobby(lobbyId.Value, joinServer: true); } else if ((connectCommand.NameAndP2PEndpointsOption.TryUnwrap(out var nameAndEndpoint) && nameAndEndpoint is { ServerName: var serverName, Endpoints: var endpoints })) @@ -844,6 +847,7 @@ namespace Barotrauma endpoints.Cast().ToImmutableArray(), string.IsNullOrWhiteSpace(serverName) ? endpoints.First().StringRepresentation : serverName, Option.None()); + DebugConsole.NewMessage($"Connecting to endpoint {endpoints.First().StringRepresentation}...", Color.Lime); } else if ((connectCommand.NameAndLidgrenEndpointOption.TryUnwrap(out var nameAndLidgrenEndpoint) && nameAndLidgrenEndpoint is { ServerName: var lidgrenServerName, Endpoint: var endpoint })) { @@ -853,6 +857,10 @@ namespace Barotrauma string.IsNullOrWhiteSpace(lidgrenServerName) ? endpoint.StringRepresentation : lidgrenServerName, Option.None()); } + else + { + DebugConsole.NewMessage($"Cannot connect: unrecognized connect command.", Color.Lime); + } ConnectCommand = Option.None(); } @@ -1197,8 +1205,9 @@ namespace Barotrauma CoroutineManager.StopCoroutines("EndCinematic"); - if (GameSession != null) + if (GameSession != null && GameSession.IsRunning) { + AchievementManager.OnRoundEnded(GameSession, roundInterrupted: true); GameAnalyticsManager.AddProgressionEvent(GameAnalyticsManager.ProgressionStatus.Fail, GameSession.GameMode?.Preset.Identifier.Value ?? "none", GameSession.RoundDuration); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs index a6735757e..08bf2e2be 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs @@ -592,8 +592,7 @@ namespace Barotrauma public bool CharacterClicked(GUIComponent component, object selection) { if (!AllowCharacterSwitch) { return false; } - if (selection is not Character character || character.IsDead || character.IsUnconscious) { return false; } - if (!character.IsOnPlayerTeam) { return false; } + if (selection is not Character character || !character.IsOnPlayerTeam) { return false; } if (GameMain.IsMultiplayer) { @@ -605,6 +604,8 @@ namespace Barotrauma return true; } + if (character.IsDead || character.IsUnconscious) { return false; } + SelectCharacter(character); if (GUI.KeyboardDispatcher.Subscriber == crewList) { GUI.KeyboardDispatcher.Subscriber = null; } return true; @@ -3703,7 +3704,8 @@ namespace Barotrauma canIssueOrders = ChatMessage.CanUseRadio(Character.Controlled) && Character.Controlled?.CurrentHull?.Submarine?.TeamID == Character.Controlled.TeamID && - !Character.Controlled.CurrentHull.Submarine.Info.IsWreck; + !Character.Controlled.CurrentHull.Submarine.Info.IsWreck && + GameMain.Client is not { IsBlockedBySpamFilter: true }; } if (canIssueOrders) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs index cdd4d140c..3c49e47eb 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs @@ -133,56 +133,15 @@ namespace Barotrauma case "map": map = Map.Load(this, subElement); break; - case "cargo": - CargoManager.LoadPurchasedItems(subElement); - break; - case "pendingupgrades": //backwards compatibility - case "upgrademanager": - UpgradeManager = new UpgradeManager(this, subElement, isSingleplayer: true); - break; - case "pets": - petsElement = subElement; - break; - case Wallet.LowerCaseSaveElementName: - Bank = new Wallet(Option.None(), subElement); - break; - case "stats": - LoadStats(subElement); - break; - case "eventmanager": - GameMain.GameSession.EventManager.Load(subElement); - break; } } + LoadSaveSharedSingleAndMultiplayer(element); + UpgradeManager ??= new UpgradeManager(this); InitUI(); - //backwards compatibility for saves made prior to the addition of personal wallets - int oldMoney = element.GetAttributeInt("money", 0); - if (oldMoney > 0) - { - Bank = new Wallet(Option.None()) - { - Balance = oldMoney - }; - } - - PurchasedLostShuttlesInLatestSave = element.GetAttributeBool("purchasedlostshuttles", false); - PurchasedHullRepairsInLatestSave = element.GetAttributeBool("purchasedhullrepairs", false); - PurchasedItemRepairsInLatestSave = element.GetAttributeBool("purchaseditemrepairs", false); - CheatsEnabled = element.GetAttributeBool("cheatsenabled", false); - if (CheatsEnabled) - { - DebugConsole.CheatsEnabled = true; - if (!AchievementManager.CheatsEnabled) - { - AchievementManager.CheatsEnabled = true; - new GUIMessageBox("Cheats enabled", "Cheat commands have been enabled on the campaign. You will not receive Steam Achievements until you restart the game."); - } - } - if (map == null) { throw new System.Exception("Failed to load the campaign save file (saved with an older, incompatible version of Barotrauma)."); @@ -687,6 +646,11 @@ namespace Barotrauma modeElement.Add(GameMain.GameSession?.EventManager.Save()); } + foreach (Identifier unlockedRecipe in GameMain.GameSession.UnlockedRecipes) + { + modeElement.Add(new XElement("unlockedrecipe", new XAttribute("identifier", unlockedRecipe))); + } + //save and remove all items that are in someone's inventory so they don't get included in the sub file as well foreach (Character c in Character.CharacterList) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs index df8b52048..26aa85a53 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs @@ -346,7 +346,6 @@ namespace Barotrauma /// public void RefreshAnyOpenPlayerInfo() { - DebugConsole.NewMessage($"Refreshing any open player info"); if (IsTabMenuOpen && TabMenu.SelectedTab == TabMenu.InfoFrameTab.Talents) { TabMenuInstance.SelectInfoFrameTab(TabMenu.InfoFrameTab.Talents); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs index 008011bf6..5b95c0428 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs @@ -907,7 +907,7 @@ namespace Barotrauma void SetReputationText(GUITextBlock textBlock) { LocalizedString reputationText = Reputation.GetFormattedReputationText(reputation.NormalizedValue, reputation.Value, addColorTags: true); - int reputationChange = (int)Math.Round(reputation.Value - initialReputation); + int reputationChange = (int)reputation.Value - (int)initialReputation; if (Math.Abs(reputationChange) > 0) { string changeText = $"{(reputationChange > 0 ? "+" : "") + reputationChange}"; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs index 8d3ad40d5..59e6c3f11 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs @@ -771,6 +771,8 @@ namespace Barotrauma private QuickUseAction GetQuickUseAction(Item item, bool allowEquip, bool allowInventorySwap, bool allowApplyTreatment) { + if (!item.IsInteractable(Character.Controlled)) { return QuickUseAction.None; } + if (allowApplyTreatment && CharacterHealth.OpenHealthWindow != null && //if the item can be equipped in the health interface slot, don't use it as a treatment but try to equip it !item.AllowedSlots.Contains(InvSlotType.HealthInterface)) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Growable.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Growable.cs index 5b8af4a6a..b9d3604f3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Growable.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Growable.cs @@ -158,7 +158,7 @@ namespace Barotrauma.Items.Components public void ClientEventRead(IReadMessage msg, float sendingTime) { - Health = msg.ReadRangedSingle(0, MaxHealth, 8); + Health = msg.ReadRangedSingle(0, MaxWater, 8); int startOffset = msg.ReadRangedInteger(-1, MaximumVines); if (startOffset > -1) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/Holdable.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/Holdable.cs index 0d532b2c2..bcd08e213 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/Holdable.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/Holdable.cs @@ -1,8 +1,9 @@ -using Barotrauma.Networking; +using Barotrauma.Extensions; +using Barotrauma.Networking; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; -using System.Diagnostics.Tracing; +using System.Collections.Generic; namespace Barotrauma.Items.Components { @@ -15,18 +16,33 @@ namespace Barotrauma.Items.Components public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null) { - if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) + if (!IsActive || picker == null || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled || !attachable) { Drawable = false; return; } - Vector2 gridPos = picker.Position; - Vector2 roundedGridPos = new Vector2( - MathUtils.RoundTowardsClosest(picker.Position.X, Submarine.GridSize.X), - MathUtils.RoundTowardsClosest(picker.Position.Y, Submarine.GridSize.Y)); + Color indicatorColor = Color.White; + if (!CanBeAttached(picker, out IEnumerable overlappingItems)) + { + foreach (var overlappingItem in overlappingItems) + { + overlappingItem.Draw(spriteBatch, editing: false, overrideColor: Color.Red * 0.7f, overrideDepth: 0.0f); + } + indicatorColor = Color.Red; + } + Vector2 attachPos = GetAttachPosition(picker); + Vector2 gridPos = picker.Position; + if (AttachesToFloor) + { + gridPos.Y = attachPos.Y - item.Rect.Height / 2; + } + Vector2 roundedGridPos = new Vector2( + MathUtils.RoundTowardsClosest(gridPos.X, Submarine.GridSize.X), + MathUtils.RoundTowardsClosest(gridPos.Y, Submarine.GridSize.Y)); + if (item.Submarine == null) { Structure attachTarget = Structure.GetAttachTarget(item.WorldPosition); @@ -35,20 +51,20 @@ namespace Barotrauma.Items.Components if (attachTarget.Submarine != null) { //set to submarine-relative position - gridPos += attachTarget.Submarine.Position; - roundedGridPos += attachTarget.Submarine.Position; - attachPos += attachTarget.Submarine.Position; + gridPos += attachTarget.Submarine.DrawPosition; + roundedGridPos += attachTarget.Submarine.DrawPosition; + attachPos += attachTarget.Submarine.DrawPosition; } } } else { - gridPos += item.Submarine.Position; - roundedGridPos += item.Submarine.Position; - attachPos += item.Submarine.Position; + gridPos += item.Submarine.DrawPosition; + roundedGridPos += item.Submarine.DrawPosition; + attachPos += item.Submarine.DrawPosition; } - Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.4f); + Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.4f, color: indicatorColor); Sprite sprite = item.Sprite; foreach (ContainedItemSprite containedSprite in item.Prefab.ContainedSprites) @@ -63,7 +79,7 @@ namespace Barotrauma.Items.Components sprite.Draw( spriteBatch, new Vector2(attachPos.X, -attachPos.Y), - item.SpriteColor * 0.5f, + item.SpriteColor.Multiply(indicatorColor) * 0.5f, item.RotationRad, item.Scale, SpriteEffects.None, 0.0f); @@ -75,7 +91,7 @@ namespace Barotrauma.Items.Components public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null) { - if (!attachable || body == null) { return; } + if (!attachable || originalBody == null) { return; } var eventData = ExtractEventData(extraData); @@ -115,9 +131,9 @@ namespace Barotrauma.Items.Components if (attached) { DropConnectedWires(null); - if (body != null) + if (originalBody != null) { - item.body = body; + item.body = originalBody; item.body.Enabled = true; } IsActive = false; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/RangedWeapon.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/RangedWeapon.cs index 7ceb067e3..42bdee14d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/RangedWeapon.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/RangedWeapon.cs @@ -76,7 +76,7 @@ namespace Barotrauma.Items.Components particleEmitterCharges.Add(new ParticleEmitter(subElement)); break; case "chargesound": - chargeSound = RoundSound.Load(subElement, false); + chargeSound = RoundSound.Load(subElement); break; } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemComponent.cs index aa5e3883a..8f01d40f2 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemComponent.cs @@ -236,7 +236,11 @@ namespace Barotrauma.Items.Components public ItemComponent GetReplacementOrThis() { - return ReplacedBy?.GetReplacementOrThis() ?? this; + if (ReplacedBy != null && ReplacedBy != this) + { + return ReplacedBy.GetReplacementOrThis(); + } + return this; } public bool NeedsSoundUpdate() @@ -511,7 +515,7 @@ namespace Barotrauma.Items.Components if (HUDOverlay is SpriteSheet spriteSheet) { spriteSheet.Draw(spriteBatch, - spriteIndex: (int)(Math.Floor(Timing.TotalTimeUnpaused * HUDOverlayAnimSpeed) % spriteSheet.FrameCount), + spriteIndex: spriteSheet.GetAnimatedSpriteIndex(HUDOverlayAnimSpeed), pos: screenSize / 2, color: Color.White, origin: HUDOverlay.Origin, rotate: 0, scale: screenSize / spriteSheet.FrameSize.ToVector2()); } else diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemContainer.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemContainer.cs index 353bb290b..da3a36e6a 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemContainer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemContainer.cs @@ -553,12 +553,12 @@ namespace Barotrauma.Items.Components bool flipX = rootBody is { Dir: -1 } || flippedX; if (flipX) { - spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipVertically : SpriteEffects.FlipHorizontally; + spriteEffects |= SpriteEffects.FlipHorizontally; } bool flipY = flippedY; if (flipY) { - spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically; + spriteEffects |= SpriteEffects.FlipVertically; } contained.Item.Sprite.Draw( diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs index b54aaef03..68451d63e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs @@ -434,12 +434,19 @@ namespace Barotrauma.Items.Components foreach (FabricationRecipe fi in fabricationRecipes.Values) { + RichString recipeTooltip = RichString.Rich(fi.TargetItem.Description); + if (fi.RequiresRecipe) + { + recipeTooltip += "\n\n" + $"‖color:{XMLExtensions.ToStringHex(GUIStyle.Red)}‖{TextManager.Get("fabricatorrequiresrecipe")}‖color:end‖"; + } + recipeTooltip = RichString.Rich(recipeTooltip); + var frame = new GUIFrame(new RectTransform(new Point(itemList.Content.Rect.Width, (int)(40 * GUI.yScale)), itemList.Content.RectTransform), style: null) { UserData = fi, HoverColor = Color.Gold * 0.2f, SelectedColor = Color.Gold * 0.5f, - ToolTip = RichString.Rich(fi.TargetItem.Description) + ToolTip = recipeTooltip }; var container = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform), @@ -451,8 +458,8 @@ namespace Barotrauma.Items.Components new GUIImage(new RectTransform(new Point(frame.Rect.Height,frame.Rect.Height), container.RectTransform), itemIcon, scaleToFit: true) { - Color = fi.TargetItem.InventoryIconColor, - ToolTip = RichString.Rich(fi.TargetItem.Description) + Color = itemIcon == fi.TargetItem.Sprite ? fi.TargetItem.SpriteColor : fi.TargetItem.InventoryIconColor, + ToolTip = recipeTooltip }; } @@ -461,7 +468,7 @@ namespace Barotrauma.Items.Components { Padding = Vector4.Zero, AutoScaleVertical = true, - ToolTip = RichString.Rich(fi.TargetItem.Description) + ToolTip = recipeTooltip }; new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), frame.RectTransform, Anchor.BottomRight), @@ -513,7 +520,7 @@ namespace Barotrauma.Items.Components var nonItems = itemList.Content.Children.Where(c => c.UserData is not FabricationRecipe).ToList(); nonItems.ForEach(i => i.Visible = false); - SortItems(character: null); + SortItems(character); FilterEntities(selectedItemCategory, itemFilterBox?.Text ?? string.Empty); HideEmptyItemListCategories(); } @@ -1196,6 +1203,15 @@ namespace Barotrauma.Items.Components new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), TextManager.FormatCurrency(SelectedItem.RequiredMoney), font: GUIStyle.SmallFont); } + + if (selectedRecipe.RequiresRecipe && !AnyOneHasRecipeForItem(Character.Controlled, selectedRecipe.TargetItem)) + { + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), + TextManager.Get("fabricatorrequiresrecipe"), textColor: GUIStyle.Red, font: GUIStyle.SubHeadingFont) + { + AutoScaleHorizontal = true, + }; + } } public void HighlightRecipe(string identifier, Color color) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Reactor.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Reactor.cs index ce3d63e73..ad7d75434 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Reactor.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Reactor.cs @@ -92,10 +92,10 @@ namespace Barotrauma.Items.Components switch (subElement.Name.ToString().ToLowerInvariant()) { case "temperatureboostsoundup": - temperatureBoostSoundUp = RoundSound.Load(subElement, false); + temperatureBoostSoundUp = RoundSound.Load(subElement); break; case "temperatureboostsounddown": - temperatureBoostSoundDown = RoundSound.Load(subElement, false); + temperatureBoostSoundDown = RoundSound.Load(subElement); break; } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs index dc0d6dcbf..9129002e4 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs @@ -1060,6 +1060,7 @@ namespace Barotrauma.Items.Components int missionIndex = 0; foreach (Mission mission in GameMain.GameSession.Missions) { + if (!mission.Prefab.ShowSonarLabels) { continue; } int i = 0; foreach ((LocalizedString label, Vector2 position) in mission.SonarLabels) { @@ -1714,15 +1715,15 @@ namespace Barotrauma.Items.Components foreach (Structure structure in Structure.WallList) { if (structure.Submarine != sub) { continue; } - CreateBlips(structure.IsHorizontal, structure.WorldPosition, structure.WorldRect); + CreateBlips(structure.IsHorizontal, structure.WorldPosition, structure.WorldRect, -structure.RotationWithFlipping); } foreach (var door in Door.DoorList) { if (door.Item.Submarine != sub || door.IsOpen) { continue; } - CreateBlips(door.IsHorizontal, door.Item.WorldPosition, door.Item.WorldRect, BlipType.Door); + CreateBlips(door.IsHorizontal, door.Item.WorldPosition, door.Item.WorldRect, rotation: 0.0f, BlipType.Door); } - void CreateBlips(bool isHorizontal, Vector2 worldPos, Rectangle worldRect, BlipType blipType = BlipType.Default) + void CreateBlips(bool isHorizontal, Vector2 worldPos, Rectangle worldRect, float rotation, BlipType blipType = BlipType.Default) { Vector2 point1, point2; if (isHorizontal) @@ -1735,6 +1736,14 @@ namespace Barotrauma.Items.Components point1 = new Vector2(worldPos.X, worldRect.Y); point2 = new Vector2(worldPos.X, worldRect.Y - worldRect.Height); } + + if (!MathUtils.NearlyEqual(rotation, 0.0f)) + { + float rotationRad = MathHelper.ToRadians(rotation); + point1 = MathUtils.RotatePointAroundTarget(point1, worldPos, rotationRad); + point2 = MathUtils.RotatePointAroundTarget(point2, worldPos, rotationRad); + } + CreateBlipsForLine( point1, point2, diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs index 931672145..dbfc0a1e8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs @@ -973,6 +973,7 @@ namespace Barotrauma.Items.Components } PosToMaintain += nudgeAmount; } + unsentChanges = true; return true; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerDistributor.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerDistributor.cs new file mode 100644 index 000000000..91d5f5d0f --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerDistributor.cs @@ -0,0 +1,180 @@ +#nullable enable +using Barotrauma.Extensions; +using Barotrauma.Networking; +using Microsoft.Xna.Framework; +using System.Collections.Generic; +using System.Linq; + +namespace Barotrauma.Items.Components +{ + internal partial class PowerDistributor : PowerTransfer, IServerSerializable, IClientSerializable + { + private partial class PowerGroup + { + private GUIFrame? frame; + private GUITextBox? nameBox; + private GUIScrollBar? ratioSlider; + private readonly List powerUnitLabels = new List(); + private GUIFrame? divider; + + public bool IsVisible { get; private set; } = true; + + public void CreateGUI() + { + frame = new GUIFrame(new RectTransform(new Vector2(1f, 0.25f), distributor.groupList!.Content.RectTransform, minSize: (0, 130)), style: null); + GUIFrame groupContent = new(new RectTransform(frame.Rect.Size - new Point(10), frame.RectTransform, Anchor.Center), style: null); + + GUILayoutGroup nameGroup = new(new RectTransform(new Vector2(0.65f, 0.33f), groupContent.RectTransform, Anchor.TopLeft), isHorizontal: true, childAnchor: Anchor.CenterLeft) + { + Stretch = true + }; + GUIButton penIcon = new(new RectTransform(new Vector2(0.75f), nameGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "TextBoxIcon") + { + HoverCursor = CursorState.IBeam, + OnClicked = (_, _) => + { + nameBox!.Select(); + return true; + } + }; + nameBox = new GUITextBox(new RectTransform(Vector2.One, nameGroup.RectTransform), Name, font: GUIStyle.SubHeadingFont, style: "GUITextBoxNoStyle") + { + MaxTextLength = MaxNameLength, + OverflowClip = true, + TextBlock = { ForceUpperCase = ForceUpperCase.No }, + OnEnterPressed = static (textBox, _) => + { + textBox.Deselect(); + return true; + } + }; + nameBox.OnDeselected += (tb, _) => + { + Name = tb.Text; + if (GameMain.Client == null) { return; } + distributor.item.CreateClientEvent(distributor, new EventData(this, EventType.NameChange)); + }; + + GUITextBlock loadDisplay = GUI.CreateDigitalDisplay(new RectTransform(new Vector2(0.35f, 0.33f), groupContent.RectTransform, Anchor.TopRight) { AbsoluteOffset = (5, 0) }, + out GUITextBlock? _, out GUITextBlock loadDisplayUnitLabel, TextManager.Get("PowerTransferLoadLabel"), tooltip: TextManager.Get("PowerTransferTipLoad"), leftLabelFont: GUIStyle.Font); + loadDisplay.TextGetter = () => MathUtils.RoundToInt(Load).ToString(); + + ratioSlider = new GUIScrollBar(new RectTransform(new Vector2(1f, 0.33f), groupContent.RectTransform, Anchor.Center), barSize: 0.15f, style: "DeviceSlider") + { + Step = SupplyRatioStep, + BarScroll = SupplyRatio, + OnMoved = (GUIScrollBar scrollBar, float barScroll) => + { + if (MathUtils.NearlyEqual(barScroll, SupplyRatio)) { return false; } + SupplyRatio = barScroll; + if (GameMain.Client != null) + { + distributor.item.CreateClientEvent(distributor, new EventData(this, EventType.RatioChange)); + distributor.correctionTimer = CorrectionDelay; + } + return true; + } + }; + ratioSlider.Bar.RectTransform.MaxSize = new Point(ratioSlider.Bar.Rect.Height); + + GUITextBlock ratioDisplay = GUI.CreateDigitalDisplay(new RectTransform(new Vector2(0.2f, 0.33f), groupContent.RectTransform, Anchor.BottomLeft), + out GUITextBlock? _, out GUITextBlock _, + rightLabelText: "%"); + ratioDisplay.TextGetter = () => DisplayRatio.ToString(); + + GUITextBlock outputDisplay = GUI.CreateDigitalDisplay(new RectTransform(new Vector2(0.35f, 0.33f), groupContent.RectTransform, Anchor.BottomRight) { AbsoluteOffset = (5, 0) }, + out GUITextBlock? _, out GUITextBlock outputDisplayUnitLabel, + TextManager.Get("powerdistributor.supplylabel"), tooltip: TextManager.Get("PowerTransferTipPower"), leftLabelFont: GUIStyle.Font); + outputDisplay.TextGetter = () => distributor.IsShortCircuited(PowerOut) ? "err" : MathUtils.RoundToInt(distributor.CalculatePowerOut(this)).ToString(); + + powerUnitLabels.Add(loadDisplayUnitLabel); + powerUnitLabels.Add(outputDisplayUnitLabel); + GUITextBlock.AutoScaleAndNormalize(powerUnitLabels); + + divider = new GUIFrame(new RectTransform(Vector2.UnitX, distributor.groupList!.Content.RectTransform), style: "HorizontalLine"); + } + + private void UpdateNameBox() + { + if (nameBox == null || nameBox.Text == DisplayName) { return; } + nameBox.Text = DisplayName?.Value ?? string.Empty; + } + + private void UpdateSlider() + { + if (ratioSlider == null || MathUtils.NearlyEqual(ratioSlider.BarScroll, supplyRatio)) { return; } + ratioSlider.BarScroll = supplyRatio; + } + + public void UpdateGUI() + { + IsVisible = PowerOut.Wires.Count >= 1; + frame!.Visible = IsVisible; + divider!.Visible = IsVisible && distributor.powerGroups.Last(group => group.frame!.Visible) != this; + if (distributor.prevLanguage != GameSettings.CurrentConfig.Language) { GUITextBlock.AutoScaleAndNormalize(powerUnitLabels); } + } + } + + private GUIListBox? groupList; + + private GUITextBlock? noConnectionsText; + + protected override void CreateGUI() + { + if (GuiFrame == null) { return; } + guiContent = new GUILayoutGroup(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset }) + { + Stretch = true + }; + + GUIFrame defaultUIContainer = new(new RectTransform(Vector2.UnitX, guiContent.RectTransform, minSize: (0, 125)), style: null) + { + CanBeFocused = false + }; + CreateDefaultPowerUI(defaultUIContainer); + + groupList = new(new RectTransform(Vector2.One, guiContent.RectTransform)) { Enabled = false }; + noConnectionsText = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), groupList.Content.RectTransform, Anchor.Center), TextManager.Get("powerdistributor.noconnections"), wrap: true) + { + Visible = false + }; + powerGroups.ForEach(group => group.CreateGUI()); + } + + public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam) + { + if (GuiFrame == null) { return; } + powerGroups.ForEach(group => group.UpdateGUI()); + noConnectionsText!.Visible = powerGroups.None(group => group.IsVisible); + base.UpdateHUDComponentSpecific(character, deltaTime, cam); + } + + #region Networking + public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData? extraData = null) => SharedEventWrite(msg, extraData); + + public void ClientEventRead(IReadMessage msg, float sendingTime) + { + int msgStartPos = msg.BitPosition; + SharedEventRead(msg, out EventType eventType, out PowerGroup powerGroup, out string newName, out float newRatio); + + if (correctionTimer > 0f) + { + int msgBits = msg.BitPosition - msgStartPos; + msg.BitPosition -= msgBits; + StartDelayedCorrection(msg.ExtractBits(msgBits), sendingTime); + return; + } + + switch (eventType) + { + case EventType.NameChange: + powerGroup.Name = newName; + break; + case EventType.RatioChange: + powerGroup.SupplyRatio = newRatio; + break; + } + } + #endregion + } +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerTransfer.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerTransfer.cs index cd4053924..128804360 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerTransfer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerTransfer.cs @@ -6,125 +6,84 @@ namespace Barotrauma.Items.Components { partial class PowerTransfer : Powered { + public override bool RecreateGUIOnResolutionChange => true; + protected GUIComponent guiContent; + private GUITickBox powerIndicator; private GUITickBox highVoltageIndicator; private GUITickBox lowVoltageIndicator; private GUITextBlock powerLabel, loadLabel; + protected GUITextBlock powerDisplay, loadDisplay; - private LanguageIdentifier prevLanguage; + protected LanguageIdentifier prevLanguage; partial void InitProjectSpecific(XElement element) { if (GuiFrame == null) { return; } + CreateGUI(); + prevLanguage = GameSettings.CurrentConfig.Language; + } - var paddedFrame = new GUIFrame(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset }, - style: null) + protected override void CreateGUI() + { + if (GuiFrame == null) { return; } + guiContent = new GUIFrame(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset }, style: null) { CanBeFocused = false }; + CreateDefaultPowerUI(guiContent); + } - var lightsArea = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1), paddedFrame.RectTransform, Anchor.CenterLeft)) + protected void CreateDefaultPowerUI(GUIComponent parent) + { + GUILayoutGroup lightsArea = new(new RectTransform(new Vector2(0.4f, 1f), parent.RectTransform, Anchor.CenterLeft)) { Stretch = true }; - powerIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.33f), lightsArea.RectTransform), - TextManager.Get("PowerTransferPowered"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightGreen") - { - CanBeFocused = false - }; - highVoltageIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.33f), lightsArea.RectTransform), - TextManager.Get("PowerTransferHighVoltage"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRed") - { - ToolTip = TextManager.Get("PowerTransferTipOvervoltage"), - Enabled = false - }; - lowVoltageIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.33f), lightsArea.RectTransform), - TextManager.Get("PowerTransferLowVoltage"), font: GUIStyle.SubHeadingFont, style: "IndicatorLightRed") - { - ToolTip = TextManager.Get("PowerTransferTipLowvoltage"), - Enabled = false - }; - powerIndicator.TextBlock.OverrideTextColor(GUIStyle.TextColorNormal); - highVoltageIndicator.TextBlock.OverrideTextColor(GUIStyle.TextColorNormal); - lowVoltageIndicator.TextBlock.OverrideTextColor(GUIStyle.TextColorNormal); + powerIndicator = GUI.CreateIndicatorLight(new RectTransform(new Vector2(1, 0.33f), lightsArea.RectTransform), + "IndicatorLightGreen", TextManager.Get("PowerTransferPowered")); + highVoltageIndicator = GUI.CreateIndicatorLight(new RectTransform(new Vector2(1, 0.33f), lightsArea.RectTransform), + "IndicatorLightRed", TextManager.Get("PowerTransferHighVoltage"), TextManager.Get("PowerTransferTipOvervoltage")); + lowVoltageIndicator = GUI.CreateIndicatorLight(new RectTransform(new Vector2(1, 0.33f), lightsArea.RectTransform), + "IndicatorLightRed", TextManager.Get("PowerTransferLowVoltage"), TextManager.Get("PowerTransferTipLowvoltage")); GUITextBlock.AutoScaleAndNormalize(powerIndicator.TextBlock, highVoltageIndicator.TextBlock, lowVoltageIndicator.TextBlock); - var textContainer = new GUIFrame(new RectTransform(new Vector2(0.58f, 1.0f), paddedFrame.RectTransform, Anchor.CenterRight), style: null); - var upperTextArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), textContainer.RectTransform, Anchor.TopLeft), isHorizontal: true, childAnchor: Anchor.CenterLeft) - { - Stretch = true - }; - var lowerTextArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), textContainer.RectTransform, Anchor.BottomLeft), isHorizontal: true, childAnchor: Anchor.CenterLeft) - { - Stretch = true - }; + GUIFrame textContainer = new(new RectTransform(new Vector2(0.58f, 1f), parent.RectTransform, Anchor.CenterRight), style: null); - powerLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), upperTextArea.RectTransform), - TextManager.Get("PowerTransferPowerLabel"), textColor: GUIStyle.TextColorBright, font: GUIStyle.LargeFont, textAlignment: Alignment.CenterRight) - { - ToolTip = TextManager.Get("PowerTransferTipPower") - }; - loadLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), lowerTextArea.RectTransform), - TextManager.Get("PowerTransferLoadLabel"), textColor: GUIStyle.TextColorBright, font: GUIStyle.LargeFont, textAlignment: Alignment.CenterRight) - { - ToolTip = TextManager.Get("PowerTransferTipLoad") - }; + powerDisplay = GUI.CreateDigitalDisplay(new RectTransform(new Vector2(1f, 0.5f), textContainer.RectTransform, Anchor.TopLeft), + out powerLabel, out GUITextBlock unitLabel1, TextManager.Get("PowerTransferPowerLabel"), TextManager.Get("kilowatt"), TextManager.Get("PowerTransferTipPower")); - var digitalBackground = new GUIFrame(new RectTransform(new Vector2(0.55f, 0.8f), upperTextArea.RectTransform), style: "DigitalFrameDark"); - var powerText = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.95f), digitalBackground.RectTransform, Anchor.Center), - "", font: GUIStyle.DigitalFont, textColor: GUIStyle.TextColorDark) + powerDisplay.TextGetter = () => { - TextAlignment = Alignment.CenterRight, - ToolTip = TextManager.Get("PowerTransferTipPower"), - TextGetter = () => { - float currPower = powerLoad < 0 ? -powerLoad: 0; - if (this is not RelayComponent && PowerConnections != null && PowerConnections.Count > 0 && PowerConnections[0].Grid != null) - { - currPower = PowerConnections[0].Grid.Power; - } - return ((int)Math.Round(currPower)).ToString(); - } - }; - var kw1 = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.5f), upperTextArea.RectTransform), - TextManager.Get("kilowatt"), textColor: GUIStyle.TextColorNormal, font: GUIStyle.Font) - { - Padding = Vector4.Zero, - TextAlignment = Alignment.BottomCenter - }; - - digitalBackground = new GUIFrame(new RectTransform(new Vector2(0.55f, 0.8f), lowerTextArea.RectTransform), style: "DigitalFrameDark"); - var loadText = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.95f), digitalBackground.RectTransform, Anchor.Center), - "", font: GUIStyle.DigitalFont, textColor: GUIStyle.TextColorDark) - { - TextAlignment = Alignment.CenterRight, - ToolTip = TextManager.Get("PowerTransferTipLoad"), - TextGetter = () => + float currPower = powerLoad < 0 ? -powerLoad : 0; + if (this is not RelayComponent && PowerConnections != null && PowerConnections.Count > 0 && PowerConnections[0].Grid != null) { - float load = PowerLoad; - if (this is RelayComponent relay) - { - load = relay.DisplayLoad; - } - else if (load < 0) - { - load = 0; - } - return ((int)Math.Round(load)).ToString(); + currPower = PowerConnections[0].Grid.Power; } + return MathUtils.RoundToInt(currPower).ToString(); }; - var kw2 = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.5f), lowerTextArea.RectTransform), - TextManager.Get("kilowatt"), textColor: GUIStyle.TextColorNormal, font: GUIStyle.Font) + + loadDisplay = GUI.CreateDigitalDisplay(new RectTransform(new Vector2(1f, 0.5f), textContainer.RectTransform, Anchor.BottomLeft), + out loadLabel, out GUITextBlock unitLabel2, TextManager.Get("PowerTransferLoadLabel"), TextManager.Get("kilowatt"), TextManager.Get("PowerTransferTipLoad")); + + loadDisplay.TextGetter = () => { - Padding = Vector4.Zero, - TextAlignment = Alignment.BottomCenter + float load = PowerLoad; + if (this is RelayComponent relay) + { + load = relay.DisplayLoad; + } + else if (load < 0) + { + load = 0; + } + return MathUtils.RoundToInt(load).ToString(); }; GUITextBlock.AutoScaleAndNormalize(powerLabel, loadLabel); - GUITextBlock.AutoScaleAndNormalize(true, true, powerText, loadText); - GUITextBlock.AutoScaleAndNormalize(kw1, kw2); - - prevLanguage = GameSettings.CurrentConfig.Language; + GUITextBlock.AutoScaleAndNormalize(true, true, powerDisplay, loadDisplay); + GUITextBlock.AutoScaleAndNormalize(unitLabel1, unitLabel2); } public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/Powered.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/Powered.cs index ae0b6e456..e52b73182 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/Powered.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/Powered.cs @@ -12,7 +12,7 @@ switch (subElement.Name.ToString().ToLowerInvariant()) { case "poweronsound": - powerOnSound = RoundSound.Load(subElement, false); + powerOnSound = RoundSound.Load(subElement); break; } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/RepairTool.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/RepairTool.cs index c7413f039..e0b154569 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/RepairTool.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/RepairTool.cs @@ -4,10 +4,6 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; namespace Barotrauma.Items.Components { @@ -28,7 +24,7 @@ namespace Barotrauma.Items.Components private readonly List particleEmitterHitCharacter = new List(); private readonly List<(RelatedItem relatedItem, ParticleEmitter emitter)> particleEmitterHitItem = new List<(RelatedItem relatedItem, ParticleEmitter emitter)>(); - private float prevProgressBarState; + private float prevProgressBarState = 1; private Item prevProgressBarTarget = null; partial void InitProjSpecific(ContentXElement element) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/ButtonTerminal.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/ButtonTerminal.cs index bea101f3b..393662631 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/ButtonTerminal.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/ButtonTerminal.cs @@ -1,4 +1,4 @@ -using Barotrauma.Extensions; +using Barotrauma.Extensions; using Barotrauma.Networking; using Microsoft.Xna.Framework; using System; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs index 885af0a17..7d29d3928 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs @@ -150,16 +150,16 @@ namespace Barotrauma.Items.Components crosshairPointerSprite = new Sprite(subElement, path: textureDir); break; case "startmovesound": - startMoveSound = RoundSound.Load(subElement, false); + startMoveSound = RoundSound.Load(subElement); break; case "endmovesound": - endMoveSound = RoundSound.Load(subElement, false); + endMoveSound = RoundSound.Load(subElement); break; case "movesound": - moveSound = RoundSound.Load(subElement, false); + moveSound = RoundSound.Load(subElement); break; case "chargesound": - chargeSound = RoundSound.Load(subElement, false); + chargeSound = RoundSound.Load(subElement); break; case "particleemitter": particleEmitters.Add(new ParticleEmitter(subElement)); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs index 0c73cf49e..fd2dc5ef0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs @@ -357,6 +357,18 @@ namespace Barotrauma #if DEBUG toolTip += $" ({item.Prefab.Identifier})"; #endif + if (!item.Prefab.UnlockedRecipeInToolTip.IsEmpty && GameMain.GameSession is { } GameSession) + { + if (GameSession.UnlockedRecipes.Contains(item.Prefab.UnlockedRecipeInToolTip)) + { + toolTip += TextManager.Get("unlockedrecipe.true"); + } + else + { + toolTip += $"\n‖color:{XMLExtensions.ToStringHex(GUIStyle.Yellow)}‖{TextManager.Get("unlockedrecipe.false")}‖color:end‖"; + } + } + if (PlayerInput.KeyDown(InputType.ContextualCommand)) { toolTip += $"\n‖color:gui.blue‖{TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders"))}‖color:end‖"; @@ -365,7 +377,8 @@ namespace Barotrauma { var colorStr = XMLExtensions.ToStringHex(Color.LightGray * 0.7f); toolTip += $"\n‖color:{colorStr}‖{TextManager.Get("itemmsg.morreoptionsavailable")}‖color:end‖"; - } + } + return RichString.Rich(toolTip); } @@ -425,9 +438,9 @@ namespace Barotrauma } } - public Inventory GetReplacementOrThiS() + public Inventory GetReplacementOrThis() { - return ReplacedBy?.GetReplacementOrThiS() ?? this; + return ReplacedBy?.GetReplacementOrThis() ?? this; } public virtual void CreateSlots() @@ -1255,7 +1268,7 @@ namespace Barotrauma container.AllowDragAndDrop && inventory.CanBePut(DraggingItems.FirstOrDefault()); - bool isTargetingValidCharacter = IsValidTargetForDragDropGive(Character.Controlled, Character.Controlled.FocusedCharacter); + bool isTargetingValidCharacter = IsValidTargetForDragDropGive(Character.Controlled, Character.Controlled.FocusedCharacter, DraggingItems); if (DraggingItemToWorld && (isTargetingValidContainer || isTargetingValidCharacter)) { @@ -1418,11 +1431,13 @@ namespace Barotrauma } } - private static bool IsValidTargetForDragDropGive(Character giver, Character receiver) + private static bool IsValidTargetForDragDropGive(Character giver, Character receiver, IEnumerable draggedItems) { - if (giver == null || receiver == null) { return false; } + if (giver == null || receiver == null || draggedItems.None()) { return false; } if (receiver == giver) { return false; } - return receiver.IsInventoryAccessibleTo(giver, IsDragAndDropGiveAllowed ? CharacterInventory.AccessLevel.Allowed : CharacterInventory.AccessLevel.Limited); + return + receiver.IsInventoryAccessibleTo(giver, IsDragAndDropGiveAllowed ? CharacterInventory.AccessLevel.Allowed : CharacterInventory.AccessLevel.Limited) && + receiver.Inventory.CanBePut(draggedItems.FirstOrDefault(), InvSlotType.Any); } private static bool CanSelectSlot(SlotReference selectedSlot) @@ -1651,7 +1666,7 @@ namespace Barotrauma (LocalizedString, Color) GetDragLabelTextAndColor(bool mouseOnHealthInterface) { - bool useDragDropGive = IsValidTargetForDragDropGive(Character.Controlled, Character.Controlled.FocusedCharacter); + bool useDragDropGive = IsValidTargetForDragDropGive(Character.Controlled, Character.Controlled.FocusedCharacter, DraggingItems); Color toolTipColor = Color.LightGreen; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs index e70235e79..7a96851e2 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs @@ -316,9 +316,6 @@ namespace Barotrauma if (worldPosition.X + extents.X > worldView.Right || worldPosition.X + extents.Width < worldView.X) { return false; } if (worldPosition.Y + extents.Height < worldView.Y - worldView.Height || worldPosition.Y + extents.Y > worldView.Y) { return false; } - if (extents.Width * Screen.Selected.Cam.Zoom < 1.0f) { return false; } - if (extents.Height * Screen.Selected.Cam.Zoom < 1.0f) { return false; } - return true; } @@ -327,7 +324,7 @@ namespace Barotrauma Draw(spriteBatch, editing, back, overrideColor: null); } - public void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Color? overrideColor = null) + public void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Color? overrideColor = null, float? overrideDepth = null) { if (!Visible || (!editing && IsHidden) || !SubEditorScreen.IsLayerVisible(this)) { return; } @@ -395,7 +392,7 @@ namespace Barotrauma } } - float depth = GetDrawDepth(); + float depth = overrideDepth ?? GetDrawDepth(); if (isWiringMode && isLogic && !PlayerInput.IsShiftDown()) { depth = 0.01f; } if (activeSprite != null) { @@ -427,7 +424,7 @@ namespace Barotrauma textureScale: Vector2.One * Scale, depth: d); } - DrawDecorativeSprites(spriteBatch, DrawPosition, flippedX && Prefab.CanSpriteFlipX, flippedY && Prefab.CanSpriteFlipY, rotation: 0, depth, overrideColor); + DrawDecorativeSprites(spriteBatch, DrawPosition, FlippedX && Prefab.CanSpriteFlipX, FlippedY && Prefab.CanSpriteFlipY, rotation: 0, depth, overrideColor); } } else @@ -448,7 +445,7 @@ namespace Barotrauma Prefab.DamagedInfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, Infector.HealthColor, Prefab.DamagedInfectedSprite.Origin, RotationRad, Scale, activeSprite.effects, depth - 0.002f); } - DrawDecorativeSprites(spriteBatch, DrawPosition, flippedX && Prefab.CanSpriteFlipX, flippedY && Prefab.CanSpriteFlipY, -RotationRad, depth, overrideColor); + DrawDecorativeSprites(spriteBatch, DrawPosition, FlippedX && Prefab.CanSpriteFlipX, FlippedY && Prefab.CanSpriteFlipY, -RotationRad, depth, overrideColor); } } else if (body.Enabled) @@ -524,8 +521,8 @@ namespace Barotrauma if (!spriteAnimState[decorativeSprite].IsActive) { continue; } float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor); Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -RotationRad) * Scale; - if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; } - if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; } + if (FlippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; } + if (FlippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; } decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color, decorativeSprite.Sprite.Origin, rotation, decorativeSprite.GetScale(ref spriteAnimState[decorativeSprite].ScaleState, spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects, depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth)); @@ -543,7 +540,7 @@ namespace Barotrauma //causing them to be removed from the list for (int i = drawableComponents.Count - 1; i >= 0; i--) { - drawableComponents[i].Draw(spriteBatch, editing, depth, overrideColor); + drawableComponents[i].Draw(spriteBatch, editing && !GameMain.SubEditorScreen.TransformWidgetSelected, depth, overrideColor); } if (GameMain.DebugDraw) @@ -813,6 +810,8 @@ namespace Barotrauma } if (Screen.Selected != GameMain.SubEditorScreen) { return; } + if (Character.Controlled == null) { activeHUDs.Clear(); } + if (GameMain.SubEditorScreen.TransformWidgetSelected) { return; } if (GetComponent() is { } discharger) { @@ -826,8 +825,6 @@ namespace Barotrauma } } - if (Character.Controlled == null) { activeHUDs.Clear(); } - foreach (ItemComponent ic in components) { ic.UpdateEditing(deltaTime); @@ -2341,6 +2338,7 @@ namespace Barotrauma } } + bool onInsertedEffectsAppliedOnPreviousRound = msg.ReadBoolean(); byte bodyType = msg.ReadByte(); bool spawnedInOutpost = msg.ReadBoolean(); bool allowStealing = msg.ReadBoolean(); @@ -2453,6 +2451,10 @@ namespace Barotrauma AllowStealing = allowStealing, Quality = quality }; + if (onInsertedEffectsAppliedOnPreviousRound) + { + item.OnInsertedEffectsApplied = item.OnInsertedEffectsAppliedOnPreviousRound = true; + } } catch (Exception e) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Levels/LevelObjects/LevelObject.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Levels/LevelObjects/LevelObject.cs index 300427deb..d56efd35b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Levels/LevelObjects/LevelObject.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Levels/LevelObjects/LevelObject.cs @@ -132,7 +132,7 @@ namespace Barotrauma SoundTriggers = new LevelTrigger[Prefab.Sounds.Count]; for (int i = 0; i < Prefab.Sounds.Count; i++) { - Sounds[i] = RoundSound.Load(Prefab.Sounds[i].SoundElement, false); + Sounds[i] = RoundSound.Load(Prefab.Sounds[i].SoundElement); SoundTriggers[i] = Prefab.Sounds[i].TriggerIndex > -1 ? Triggers[Prefab.Sounds[i].TriggerIndex] : null; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs index b65723a52..8202240aa 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs @@ -714,21 +714,21 @@ namespace Barotrauma.Lights const float MaxOffset = 256.0f; //the magic numbers here are just based on experimentation float MinHorizontalScale = MathHelper.Lerp(3.5f, 1.5f, ObstructVisionAmount); - float MaxHorizontalScale = MinHorizontalScale * 1.25f; + float MaxHorizontalScale = 10.0f; float VerticalScale = MathHelper.Lerp(4.0f, 1.25f, ObstructVisionAmount); - //Starting point and scale-based modifier that moves the point of origin closer to the edge of the texture if the player moves their mouse further away, or vice versa. - float relativeOriginStartPosition = 0.1f; //Increasing this value moves the origin further behind the character - float originStartPosition = visionCircle.Width * relativeOriginStartPosition * MinHorizontalScale; - float relativeOriginLookAtPosModifier = -0.055f; //Increase this value increases how much the vision changes by moving the mouse - float originLookAtPosModifier = visionCircle.Width * relativeOriginLookAtPosModifier; - Vector2 scale = new Vector2( MathHelper.Clamp(losOffset.Length() / MaxOffset, MinHorizontalScale, MaxHorizontalScale), VerticalScale); + //Increasing this value moves the origin further behind the character (current value chosen by experimentation) + float relativeOriginStartPosition = 0.2f; + //Divide by scale to move the origin closer to the edge of the texture, meaning the visible area moves forwards. + //Just stretching the texture without touching the origin would otherwise mean the blurry edge of visibility moves further behind the character (allowing you to see behind you better when looking far away) + float originStartPosition = visionCircle.Width * relativeOriginStartPosition / scale.X; + spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cam.Transform * Matrix.CreateScale(new Vector3(GameSettings.CurrentConfig.Graphics.LightMapScale, GameSettings.CurrentConfig.Graphics.LightMapScale, 1.0f))); spriteBatch.Draw(visionCircle, new Vector2(ViewTarget.WorldPosition.X, -ViewTarget.WorldPosition.Y), null, Color.White, rotation, - new Vector2(originStartPosition + (scale.X * originLookAtPosModifier), visionCircle.Height / 2), scale, SpriteEffects.None, 0.0f); + new Vector2(originStartPosition, visionCircle.Height / 2), scale, SpriteEffects.None, 0.0f); spriteBatch.End(); } else @@ -788,8 +788,8 @@ namespace Barotrauma.Lights if (!convexHull.Intersects(camView)) { continue; } Vector2 relativeViewPos = pos; - if (convexHull.ParentEntity?.Submarine != null) - { + if (convexHull.ParentEntity?.Submarine != null) + { relativeViewPos -= convexHull.ParentEntity.Submarine.DrawPosition; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs index c2d8b40dc..5fe6c6acf 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs @@ -83,6 +83,8 @@ namespace Barotrauma #if DEBUG private GUIComponent editor; + private bool editorEnabled; + private void CreateEditor() { editor = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), GUI.Canvas, Anchor.TopRight, minSize: new Point(400, 0))); @@ -534,8 +536,15 @@ namespace Barotrauma #if DEBUG if (GameMain.DebugDraw) { - if (editor == null) CreateEditor(); - editor.AddToGUIUpdateList(order: 1); + if (editor == null) { CreateEditor(); } + if (editorEnabled) + { + editor.AddToGUIUpdateList(order: 1); + } + if (PlayerInput.KeyHit(Keys.T)) + { + editorEnabled = !editorEnabled; + } } if (PlayerInput.KeyHit(Keys.Space)) @@ -822,7 +831,7 @@ namespace Barotrauma drawRect.X = (int)pos.X - drawRect.Width / 2; drawRect.Y = (int)pos.Y - drawRect.Width / 2; - if (drawRect.X > rect.Right - GUI.IntScale(100) && generationParams.MissionIcon != null && location.AvailableMissions.Any()) + if (drawRect.X > rect.Right - GUI.IntScale(100) && generationParams.MissionIcon != null && location.AvailableAndVisibleMissions.Any(m => m.Prefab.ShowInMenus)) { Vector2 offScreenMissionIconPos = new Vector2(rect.Right - GUI.IntScale(50), drawRect.Center.Y); generationParams.MissionIcon.Draw(spriteBatch, @@ -934,18 +943,19 @@ namespace Barotrauma } if (location != CurrentLocation && generationParams.MissionIcon != null) { - if ((CurrentLocation == currentDisplayLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location))) || - location.AvailableMissions.Any(m => m.Locations[0] == m.Locations[1])) + var currentLocationVisibleMissions = CurrentLocation.AvailableAndVisibleMissions; + if ((CurrentLocation == currentDisplayLocation && currentLocationVisibleMissions.Any(m => m.Locations.Contains(location))) || + location.AvailableAndVisibleMissions.Any(m => m.Locations[0] == m.Locations[1])) { Vector2 missionIconPos = pos + new Vector2(1.35f, 0.35f) * generationParams.LocationIconSize * 0.5f * zoom; generationParams.MissionIcon.Draw(spriteBatch, missionIconPos, generationParams.IndicatorColor, scale: missionIconScale * zoom); if (Vector2.Distance(PlayerInput.MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom && IsPreferredTooltip(missionIconPos)) { - var availableMissions = CurrentLocation.AvailableMissions + var allVisibleMissions = currentLocationVisibleMissions .Where(m => m.Locations.Contains(location)) - .Concat(location.AvailableMissions.Where(m => m.Locations[0] == m.Locations[1])) + .Concat(location.AvailableAndVisibleMissions.Where(m => m.Locations[0] == m.Locations[1])) .Distinct(); - tooltip = (new Rectangle(missionIconPos.ToPoint(), new Point(30)), TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name))); + tooltip = (new Rectangle(missionIconPos.ToPoint(), new Point(30)), TextManager.Get("mission") + '\n'+ string.Join('\n', allVisibleMissions.Select(m => "- " + m.Name))); } } } @@ -992,6 +1002,12 @@ namespace Barotrauma spriteBatch.End(); GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect; spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); +#if DEBUG + if (GameMain.DebugDraw) + { + GUI.DrawString(spriteBatch, new Vector2(mapContainer.Center.X, mapContainer.Rect.Y), "Press T to toggle editing map generation parameters.", Color.Magenta, font: GUIStyle.SmallFont); + } +#endif } public static void DrawNoise(SpriteBatch spriteBatch, Rectangle rect, float strength) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/MapEntity.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/MapEntity.cs index 38549dddf..f89edd996 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/MapEntity.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/MapEntity.cs @@ -1159,15 +1159,6 @@ namespace Barotrauma public virtual void DrawEditing(SpriteBatch spriteBatch, Camera cam) { } - private float RotationRad - => MathHelper.ToRadians( - this switch - { - Structure s => s.Rotation, - Item it => it.Rotation, - _ => 0.0f - }); - private Vector2 GetEditingHandlePos(int x, int y, Camera cam) { Vector2 handleDiff = new Vector2(x * (rect.Width * 0.5f), y * (rect.Height * 0.5f)); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/RoundSound.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/RoundSound.cs index ac2eeb854..61ca17006 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/RoundSound.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/RoundSound.cs @@ -29,6 +29,15 @@ namespace Barotrauma Volume = element.GetAttributeFloat("volume", 1.0f); IgnoreMuffling = element.GetAttributeBool("dontmuffle", false); MuteBackgroundMusic = element.GetAttributeBool("MuteBackgroundMusic", false); + + if (!Stream && Sound.DurationSeconds > 60.0f) + { + DebugConsole.AddWarning( + $"Potential issue in content package: a large audio clip \"{System.IO.Path.GetFileName(Filename)}\" is set to be loaded into memory instead of streaming it from the disk. "+ + "This can lead to excessive memory usage. Large clips should generally be streamed, while small and frequently played sounds should be loaded to memory to avoid the IO overhead of streaming. "+ + "Consider adding stream=\"true\" to the sound's XML element.", + contentPackage: element.ContentPackage); + } FrequencyMultiplierRange = new Vector2(1.0f); string freqMultAttr = element.GetAttributeString("frequencymultiplier", element.GetAttributeString("frequency", "1.0")); @@ -61,10 +70,11 @@ namespace Barotrauma private static readonly List roundSounds = new List(); private static readonly Dictionary roundSoundByPath = new Dictionary(); - public static RoundSound? Load(ContentXElement element, bool stream = false) + public static RoundSound? Load(ContentXElement element) { if (GameMain.SoundManager?.Disabled ?? true) { return null; } + bool stream = element.GetAttributeBool(nameof(Stream), false); var filename = element.GetAttributeContentPath("file") ?? element.GetAttributeContentPath("sound"); if (filename is null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs index f995583ef..a537a930c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs @@ -320,8 +320,8 @@ namespace Barotrauma { RectangleF worldRect = Quad2D.FromSubmarineRectangle(WorldRect).Rotated( FlippedX != FlippedY - ? rotationRad - : -rotationRad).BoundingAxisAlignedRectangle; + ? RotationRad + : -RotationRad).BoundingAxisAlignedRectangle; Vector2 worldPos = WorldPosition; Vector2 min = new Vector2(worldRect.X, worldRect.Y); @@ -451,7 +451,7 @@ namespace Barotrauma MathUtils.PositiveModulo(-textureOffset.X, Prefab.BackgroundSprite.SourceRect.Width * TextureScale.X * Scale), MathUtils.PositiveModulo(-textureOffset.Y, Prefab.BackgroundSprite.SourceRect.Height * TextureScale.Y * Scale)); - float rotationRad = GetRotationForSprite(this.rotationRad, Prefab.BackgroundSprite); + float rotationRad = GetRotationForSprite(RotationRad, Prefab.BackgroundSprite); Prefab.BackgroundSprite.DrawTiled( spriteBatch, @@ -484,7 +484,7 @@ namespace Barotrauma if (back == GetRealDepth() > 0.5f) { - Vector2 advanceX = MathUtils.RotatedUnitXRadians(this.rotationRad).FlipY(); + Vector2 advanceX = MathUtils.RotatedUnitXRadians(RotationRad).FlipY(); Vector2 advanceY = advanceX.YX().FlipX(); if (FlippedX != FlippedY) { @@ -492,7 +492,7 @@ namespace Barotrauma advanceY = advanceY.FlipX(); } - float sectionSpriteRotationRad = GetRotationForSprite(this.rotationRad, Prefab.Sprite); + float sectionSpriteRotationRad = GetRotationForSprite(RotationRad, Prefab.Sprite); for (int i = 0; i < Sections.Length; i++) { @@ -558,9 +558,11 @@ namespace Barotrauma foreach (var decorativeSprite in Prefab.DecorativeSprites) { if (!spriteAnimState[decorativeSprite].IsActive) { continue; } - float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor) + this.rotationRad; + float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor) + RotationRad; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier) * Scale; - Vector2 drawPos = DrawPosition + MathUtils.RotatePoint(offset, -this.rotationRad); + if (FlippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; } + if (FlippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; } + Vector2 drawPos = DrawPosition + MathUtils.RotatePoint(offset, -this.RotationRad); decorativeSprite.Sprite.Draw( spriteBatch: spriteBatch, pos: drawPos.FlipY(), diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs index be8ac5b7b..aa50745c8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs @@ -230,30 +230,28 @@ namespace Barotrauma } } - public static void DrawGrid(SpriteBatch spriteBatch, int gridCells, Vector2 gridCenter, Vector2 roundedGridCenter, float alpha = 1.0f) + public static void DrawGrid(SpriteBatch spriteBatch, int gridCells, Vector2 gridCenter, Vector2 roundedGridCenter, float alpha = 1.0f, Color? color = null) { Vector2 topLeft = roundedGridCenter - Vector2.One * GridSize * gridCells / 2; Vector2 bottomRight = roundedGridCenter + Vector2.One * GridSize * gridCells / 2; for (int i = 0; i < gridCells; i++) { - float distFromGridX = (MathUtils.RoundTowardsClosest(gridCenter.X, GridSize.X) - gridCenter.X) / GridSize.X; - float distFromGridY = (MathUtils.RoundTowardsClosest(gridCenter.Y, GridSize.Y) - gridCenter.Y) / GridSize.Y; + float middleIndex = (gridCells - 1) / 2.0f; + float normalizedPos = Math.Abs((i - middleIndex) / middleIndex); + float expandX = MathHelper.Lerp(30.0f, 0.0f, normalizedPos); + float expandY = expandX; - float normalizedDistX = Math.Abs(i + distFromGridX - gridCells / 2) / (gridCells / 2); - float normalizedDistY = Math.Abs(i - distFromGridY - gridCells / 2) / (gridCells / 2); - - float expandX = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY); - float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistX); + Color lineColor = color ?? Color.White; GUI.DrawLine(spriteBatch, new Vector2(topLeft.X - expandX, -bottomRight.Y + i * GridSize.Y), new Vector2(bottomRight.X + expandX, -bottomRight.Y + i * GridSize.Y), - Color.White * (1.0f - normalizedDistY) * alpha, depth: 0.6f, width: 3); + lineColor * (1.0f - normalizedPos) * alpha, depth: 0.6f, width: 3); GUI.DrawLine(spriteBatch, new Vector2(topLeft.X + i * GridSize.X, -topLeft.Y + expandY), new Vector2(topLeft.X + i * GridSize.X, -bottomRight.Y - expandY), - Color.White * (1.0f - normalizedDistX) * alpha, depth: 0.6f, width: 3); + lineColor * (1.0f - normalizedPos) * alpha, depth: 0.6f, width: 3); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/ChatMessage.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/ChatMessage.cs index bbf6fa312..03b91b4a7 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/ChatMessage.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/ChatMessage.cs @@ -150,6 +150,9 @@ namespace Barotrauma.Networking styleSetting = msg.ReadString(); txt = TextManager.GetServerMessage(txt).Value; break; + case ChatMessageType.BlockedBySpamFilter: + GameMain.Client.BlockedBySpamFilterTimer = BlockedBySpamFilterTime; + break; } if (NetIdUtils.IdMoreRecent(id, LastID)) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs index 0e6e5bb8d..80dcf1476 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs @@ -104,6 +104,10 @@ namespace Barotrauma.Networking private UInt16 lastQueueChatMsgID = 0; //last message added to the queue private readonly List chatMsgQueue = new List(); + public float BlockedBySpamFilterTimer; + + public bool IsBlockedBySpamFilter => BlockedBySpamFilterTimer > 0.0f; + public UInt16 LastSentEntityEventID; #if DEBUG @@ -479,6 +483,8 @@ namespace Barotrauma.Networking } #endif + BlockedBySpamFilterTimer -= deltaTime; + foreach (Client c in ConnectedClients) { if (c.Character != null && c.Character.Removed) { c.Character = null; } @@ -868,6 +874,10 @@ namespace Barotrauma.Networking case ServerPacketHeader.ACHIEVEMENT: ReadAchievement(inc); break; + case ServerPacketHeader.UNLOCKRECIPE: + Identifier identifier = inc.ReadIdentifier(); + GameMain.GameSession.UnlockRecipe(identifier, showNotifications: true); + break; case ServerPacketHeader.ACHIEVEMENT_STAT: ReadAchievementStat(inc); break; @@ -1069,13 +1079,15 @@ namespace Barotrauma.Networking CloseReconnectBox(); GUI.ClearCursorWait(); + + string disconnectMessage = $"Client received a disconnect message. Reason: {disconnectPacket.DisconnectReason}"; + SteamTimelineManager.OnClientDisconnect(disconnectMessage); if (disconnectPacket.ShouldCreateAnalyticsEvent) { GameAnalyticsManager.AddErrorEventOnce( "GameClient.HandleDisconnectMessage", - GameAnalyticsManager.ErrorSeverity.Debug, - $"Client received a disconnect message. Reason: {disconnectPacket.DisconnectReason}"); + GameAnalyticsManager.ErrorSeverity.Debug, disconnectMessage); } if (disconnectPacket.DisconnectReason == DisconnectReason.ServerFull) @@ -1235,7 +1247,16 @@ namespace Barotrauma.Networking private void OnConnectionInitializationComplete() { - UpdatePresence($"-connect \"{ToolBox.EscapeCharacters(ServerName)}\" {string.Join(",", serverEndpoints.Select(e => e.StringRepresentation))}"); + //don't allow connecting through the friend list if we're connected to localhost (others can't join to "localhost") + //we could potentially find the public IP of the server (assuming it's a public server) from the Steam API, but maybe not worth the trouble? + bool connectedToLocalHost = serverEndpoints.All(e => e is LidgrenEndpoint lidgrenEndpoint && lidgrenEndpoint.Address.IsLocalHost); + string escapedServerName = ServerName.IsNullOrWhiteSpace() ? "Server" : ToolBox.EscapeCharacters(ServerName); + string connectCommand = + connectedToLocalHost ? + string.Empty : + $"-connect \"{escapedServerName}\" {string.Join(",", serverEndpoints.Select(e => e.StringRepresentation))}"; + + UpdatePresence(connectCommand); canStart = true; connected = true; @@ -2150,11 +2171,14 @@ namespace Barotrauma.Networking if (lobbyUpdated) { + //we don't want the client to create any network events + //when they modify the server lobby to match the server state as a result of this message + ServerSettings.SuppressNetworkMessages = true; + var prevDispatcher = GUI.KeyboardDispatcher.Subscriber; UInt16 updateID = inc.ReadUInt16(); - UInt16 settingsLen = inc.ReadUInt16(); byte[] settingsData = inc.ReadBytes(settingsLen); @@ -2299,6 +2323,9 @@ namespace Barotrauma.Networking } lastSentChatMsgID = inc.ReadUInt16(); + + ServerSettings.SuppressNetworkMessages = false; + break; case ServerNetSegment.ClientList: ReadClientList(inc); @@ -3091,6 +3118,7 @@ namespace Barotrauma.Networking public void RequestSelectSub(SubmarineInfo sub, SelectedSubType type) { if (!HasPermission(ClientPermissions.SelectSub) || sub == null) { return; } + if (ServerSettings.SuppressNetworkMessages) { return; } IWriteMessage msg = new WriteOnlyMessage(); msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND); @@ -3392,6 +3420,10 @@ namespace Barotrauma.Networking { msgBox = GameMain.NetLobbyScreen.ChatInput; } + if (msgBox != null) + { + msgBox.Enabled = !IsBlockedBySpamFilter; + } UpdateLogButtonVisibility(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerSettings.cs index 082170271..a3bc69b81 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerSettings.cs @@ -12,6 +12,8 @@ namespace Barotrauma.Networking private static readonly LocalizedString packetAmountTooltip = TextManager.Get("ServerSettingsMaxPacketAmountTooltip"); private static readonly RichString packetAmountTooltipWarning = RichString.Rich($"{packetAmountTooltip}\n\n‖color:gui.red‖{TextManager.Get("PacketLimitWarning")}‖end‖"); + public static bool SuppressNetworkMessages; + partial class NetPropertyData { public GUIComponent GUIComponent; @@ -94,6 +96,14 @@ namespace Barotrauma.Networking get { if (GUIComponent == null) { return false; } + if (GUIComponent is GUIDropDown dropDown && + dropDown.SelectedIndex == -1) + { + //nothing selected in the dropdown + //it's not possible to select nothing via the UI, which means the client cannot have selected anything locally + //(so this must mean that either nothing has been selected yet or that there's nothing in the dropdown) + return false; + } return !PropEquals(TempValue, GUIComponentValue); } } @@ -238,6 +248,7 @@ namespace Barotrauma.Networking int traitorDangerLevel = 0) { if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) { return; } + if (SuppressNetworkMessages) { return; } IWriteMessage outMsg = new WriteOnlyMessage(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs index cf804cdbf..3dace2bb2 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs @@ -11,6 +11,14 @@ namespace Barotrauma { abstract class CampaignSetupUI { + protected enum SaveSortingType + { + LastPlayedDescending, LastPlayedAscending, + NameDescending, NameAscending + } + + private const SaveSortingType DefaultSaveSortingType = SaveSortingType.LastPlayedDescending; + protected readonly GUIComponent newGameContainer, loadGameContainer; protected GUIListBox saveList; @@ -111,24 +119,54 @@ namespace Barotrauma return saveFrame; } - protected void SortSaveList() + protected void SortSaveList(SaveSortingType sortingType = DefaultSaveSortingType) => saveList?.Content.RectTransform.SortChildren((rect1, rect2) => { - saveList.Content.RectTransform.SortChildren((c1, c2) => + if (rect1.GUIComponent.UserData is not CampaignMode.SaveInfo file1 || rect2.GUIComponent.UserData is not CampaignMode.SaveInfo file2) { return 0; } + if (!file1.SaveTime.TryUnwrap(out SerializableDateTime file1WriteTime) || !file2.SaveTime.TryUnwrap(out SerializableDateTime file2WriteTime)) { return 0; } + return sortingType switch { - if (c1.GUIComponent.UserData is not CampaignMode.SaveInfo file1 - || c2.GUIComponent.UserData is not CampaignMode.SaveInfo file2) - { - return 0; - } + SaveSortingType.LastPlayedDescending => file2WriteTime.CompareTo(file1WriteTime), + SaveSortingType.LastPlayedAscending => file1WriteTime.CompareTo(file2WriteTime), + SaveSortingType.NameDescending => string.Compare(Path.GetFileNameWithoutExtension(file1.FilePath), Path.GetFileNameWithoutExtension(file2.FilePath), StringComparison.OrdinalIgnoreCase), + SaveSortingType.NameAscending => string.Compare(Path.GetFileNameWithoutExtension(file2.FilePath), Path.GetFileNameWithoutExtension(file1.FilePath), StringComparison.OrdinalIgnoreCase), + _ => 0 + }; + }); - if (!file1.SaveTime.TryUnwrap(out var file1WriteTime) - || !file2.SaveTime.TryUnwrap(out var file2WriteTime)) + protected void CreateSaveFilteringHeader(GUIComponent parent) + { + GUILayoutGroup container = new(new RectTransform(Vector2.UnitX, parent.RectTransform), true) { Stretch = true }; + + GUI.CreateFilterBox(new RectTransform(new Vector2(0.6f, 1f), container.RectTransform)).OnTextChanged += (_, filterText) => + { + filterText = filterText.Trim(); + foreach (GUIComponent saveElement in saveList.Content.Children) { - return 0; + if (saveElement.UserData is not CampaignMode.SaveInfo saveInfo) { continue; } + saveElement.Visible = filterText.IsNullOrEmpty() + || Path.GetFileNameWithoutExtension(saveInfo.FilePath).Contains(filterText, StringComparison.OrdinalIgnoreCase); } - - return file2WriteTime.CompareTo(file1WriteTime); - }); + return true; + }; + + SaveSortingType[] sortingTypes = Enum.GetValues(); + GUIDropDown dropDown = new(new RectTransform(new Vector2(0.4f, 1f), container.RectTransform), elementCount: sortingTypes.Length) + { + OnSelected = (_, data) => + { + SortSaveList((SaveSortingType)data); + return true; + } + }; + + foreach (SaveSortingType sortingType in sortingTypes) + { + dropDown.AddItem(TextManager.Get($"SaveSortingType.{sortingType}"), sortingType); + } + + dropDown.SelectItem(DefaultSaveSortingType); + + container.RectTransform.MinSize = (0, container.Children.Max(child => child.Rect.Size.Y)); } public struct CampaignSettingElements diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/MultiPlayerCampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/MultiPlayerCampaignSetupUI.cs index 9e2528488..fb0318eae 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/MultiPlayerCampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/MultiPlayerCampaignSetupUI.cs @@ -217,6 +217,8 @@ namespace Barotrauma RelativeSpacing = 0.03f }; + CreateSaveFilteringHeader(leftColumn); + saveList = new GUIListBox(new RectTransform(Vector2.One, leftColumn.RectTransform)) { PlaySoundOnSelect = true, diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs index 9cd40f9a1..f91c3c158 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs @@ -320,14 +320,14 @@ namespace Barotrauma if (string.IsNullOrWhiteSpace(sender.Text)) { characterInfo.Name = characterInfo.GetRandomName(Rand.RandSync.Unsynced); - sender.Text = characterInfo.Name; sender.UserData = "random"; } else { - characterInfo.Name = sender.Text; + characterInfo.Rename(sender.Text); sender.UserData = "user"; } + sender.Text = characterInfo.Name; }; characterName.OnEnterPressed += (sender, text) => { @@ -594,6 +594,8 @@ namespace Barotrauma RelativeSpacing = 0.03f }; + CreateSaveFilteringHeader(leftColumn); + saveList = new GUIListBox(new RectTransform(Vector2.One, leftColumn.RectTransform)) { PlaySoundOnSelect = true, diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs index 280bc2c7f..6f1f6fb86 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs @@ -352,7 +352,7 @@ namespace Barotrauma Location currentDisplayLocation = Campaign.GetCurrentDisplayLocation(); if (connection != null && connection.Locations.Contains(currentDisplayLocation)) { - List availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).ToList(); + List availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).Where(m => m.Prefab.ShowInMenus || GameMain.DebugDraw).ToList(); if (!availableMissions.Any()) { availableMissions.Insert(0, null); } @@ -389,19 +389,26 @@ namespace Barotrauma AbsoluteSpacing = GUI.IntScale(5) }; - var missionName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission?.Name ?? TextManager.Get("NoMission"), font: GUIStyle.SubHeadingFont, wrap: true); - missionName.RectTransform.MinSize = new Point(0, GUI.IntScale(15)); + LocalizedString missionName = mission?.Name ?? TextManager.Get("NoMission"); + if (GameMain.DebugDraw && mission != null) + { + if (!mission.Prefab.ShowInMenus) { missionName = $"[HIDDEN] {missionName}"; } + missionName += $" ({mission.Prefab.Identifier})"; + } + + var missionNameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), missionName, font: GUIStyle.SubHeadingFont, wrap: true); + missionNameBlock.RectTransform.MinSize = new Point(0, GUI.IntScale(15)); if (mission == null) { - missionTextContent.RectTransform.MinSize = missionName.RectTransform.MinSize = new Point(0, GUI.IntScale(35)); + missionTextContent.RectTransform.MinSize = missionNameBlock.RectTransform.MinSize = new Point(0, GUI.IntScale(35)); missionTextContent.ChildAnchor = Anchor.CenterLeft; } else { GUITickBox tickBox = null; - if (!isMissionInNextLocation) + if (!isMissionInNextLocation && mission.Prefab.ShowInMenus) { - tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) }, label: string.Empty) + tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionNameBlock.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionNameBlock.Padding.X, 0) }, label: string.Empty) { UserData = mission, Selected = Campaign.Map.CurrentLocation?.SelectedMissions.Contains(mission) ?? false @@ -443,7 +450,7 @@ namespace Barotrauma GUILayoutGroup difficultyIndicatorGroup = null; if (mission.Difficulty.HasValue) { - difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.9f), missionName.RectTransform, anchor: Anchor.CenterRight) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) }, + difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.9f), missionNameBlock.RectTransform, anchor: Anchor.CenterRight) { AbsoluteOffset = new Point((int)missionNameBlock.Padding.Z, 0) }, isHorizontal: true, childAnchor: Anchor.CenterRight) { AbsoluteSpacing = 1, @@ -465,11 +472,11 @@ namespace Barotrauma float extraPadding = 0;// 0.8f * tickBox.Rect.Width; float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0; - missionName.Padding = new Vector4(missionName.Padding.X + (tickBox?.Rect.Width ?? 0) * 1.2f + extraPadding, - missionName.Padding.Y, - missionName.Padding.Z + extraZPadding + extraPadding, - missionName.Padding.W); - missionName.CalculateHeightFromText(); + missionNameBlock.Padding = new Vector4(missionNameBlock.Padding.X + (tickBox?.Rect.Width ?? 0) * 1.2f + extraPadding, + missionNameBlock.Padding.Y, + missionNameBlock.Padding.Z + extraZPadding + extraPadding, + missionNameBlock.Padding.W); + missionNameBlock.CalculateHeightFromText(); //spacing new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(10)) }, style: null); @@ -544,7 +551,7 @@ namespace Barotrauma OnClicked = (GUIButton btn, object obj) => { if (missionList.Content.FindChild(c => c is GUITickBox tickBox && tickBox.Selected, recursive: true) == null && - missionList.Content.Children.Any(c => c.UserData is Mission mission && mission.Locations.Contains(Campaign?.Map?.CurrentLocation))) + missionList.Content.Children.Any(c => c.UserData is Mission { Prefab.ShowInMenus: true } mission && mission.Locations.Contains(Campaign?.Map?.CurrentLocation))) { var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") }); noMissionVerification.Buttons[0].OnClicked = (btn, userdata) => diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs index 2cb36e846..ebd75d00b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs @@ -482,6 +482,11 @@ namespace Barotrauma public void TestLevelGenerationForErrors(int amountOfLevelsToGenerate) { + if (selectedParams == null) + { + throw new InvalidOperationException("No level generation parameters selected in the level editor."); + } + CoroutineManager.StartCoroutine(GenerateLevels()); IEnumerable GenerateLevels() @@ -520,7 +525,7 @@ namespace Barotrauma errorCatcher.Errors.ToList().ForEach(e => DebugConsole.ThrowError(e.Text)); yield return CoroutineStatus.Success; } - yield return CoroutineStatus.Running; + yield return new WaitForSeconds(0.1f); } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs index e871f9430..a2fb9b852 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs @@ -740,6 +740,30 @@ namespace Barotrauma List missionTypes = MissionPrefab.GetAllMultiplayerSelectableMissionTypes().ToList(); + GUILayoutGroup buttonGroup = new(new RectTransform(Vector2.UnitX, missionTypeList.Content.RectTransform), true) { Stretch = true }; + GUIButton selectAllMissionsButton = new(new RectTransform(new Vector2(0.5f, 1f), buttonGroup.RectTransform), TextManager.Get("selectall")) + { + OnClicked = (_, _) => + { + IEnumerable validMissions = GetValidMissions(); + validMissions.ForEach(missionType => GameMain.Client.ServerSettings?.ClientAdminWrite(ServerSettings.NetFlags.Misc, addedMissionType: missionType)); + return true; + } + }; + GUIButton deselectAllMissionsButton = new(new RectTransform(new Vector2(0.5f, 1f), buttonGroup.RectTransform), TextManager.Get("deselectall")) + { + OnClicked = (_, _) => + { + IEnumerable validMissions = GetValidMissions(); + + // The server must have at least one mission selected, so ensure the first in the list is enabled. + GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, addedMissionType: validMissions.First()); + validMissions.Skip(1).ForEach(missionType => GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, removedMissionType: missionType)); + return true; + } + }; + buttonGroup.RectTransform.MinSize = (0, buttonGroup.Children.Max(child => child.Rect.Height)); + missionTypeTickBoxes = new GUITickBox[missionTypes.Count]; int index = 0; foreach (var missionType in missionTypes.OrderBy(t => TextManager.Get("MissionType." + t.Value).Value)) @@ -763,6 +787,13 @@ namespace Barotrauma } else { + Identifier firstValidMission = GetValidMissions().First(); + if (missionTypeTickBoxes.None(tickBox => tickBox.Selected && tickBox.Parent.Visible)) + { + GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, addedMissionType: firstValidMission); + if ((Identifier)tickbox.UserData == firstValidMission) { return true; } + } + GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, removedMissionType: (Identifier)tickbox.UserData); } return true; @@ -771,9 +802,16 @@ namespace Barotrauma frame.RectTransform.MinSize = missionTypeTickBoxes[index].RectTransform.MinSize; index++; } + + clientDisabledElements.Add(selectAllMissionsButton); + clientDisabledElements.Add(deselectAllMissionsButton); clientDisabledElements.AddRange(missionTypeTickBoxes); return gameModeSpecificFrame; + + IEnumerable GetValidMissions() => missionTypeTickBoxes + .Where(tickBox => tickBox.Parent.Visible) + .Select(tickBox => (Identifier)tickBox.UserData); } private GUIFrame gameModeSettingsContent; @@ -2884,6 +2922,11 @@ namespace Barotrauma UserData = new JobVariant(jobPrefab, variant) }; jobVariantTooltip.RectTransform.AbsoluteOffset = new Point(parentSlot.Rect.Right, parentSlot.Rect.Y); + if (jobVariantTooltip.Rect.X < 0) + { + jobVariantTooltip.RectTransform.SetPosition(anchor: Anchor.TopLeft, pivot: Pivot.BottomLeft); + jobVariantTooltip.RectTransform.AbsoluteOffset = new Point(parentSlot.Rect.X, parentSlot.Rect.Y); + } var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), jobVariantTooltip.RectTransform, Anchor.Center)) { @@ -3049,6 +3092,8 @@ namespace Barotrauma private void RefreshOutpostDropdown() { + Identifier randomOutpostIdentifier = "Random".ToIdentifier(); + outpostDropdown.Parent.Visible = MissionTypeFrame.Visible; if (!outpostDropdown.Parent.Visible) { return; } @@ -3057,7 +3102,7 @@ namespace Barotrauma Identifier prevSelected = GameMain.NetworkMember?.ServerSettings.SelectedOutpostName ?? Identifier.Empty; outpostDropdown.ClearChildren(); - outpostDropdown.AddItem(TextManager.Get("Random"), "Random".ToIdentifier()); + outpostDropdown.AddItem(TextManager.Get("Random"), randomOutpostIdentifier); HashSet validOutpostTagsForMissions = new HashSet(); IEnumerable suitableMissionClasses = @@ -3081,12 +3126,22 @@ namespace Barotrauma foreach (var submarineInfo in SubmarineInfo.SavedSubmarines.DistinctBy(s => s.Name)) { if (submarineInfo.Type == SubmarineType.Outpost && - validOutpostTagsForMissions.Any(tag => submarineInfo.OutpostTags.Contains(tag))) + validOutpostTagsForMissions.Any(submarineInfo.OutpostTags.Contains)) { outpostDropdown.AddItem(submarineInfo.DisplayName, userData: submarineInfo.Name.ToIdentifier(), toolTip: submarineInfo.Description); } } - outpostDropdown.ListBox.Select(prevSelected); + if (!outpostDropdown.ListBox.Select(prevSelected)) + { + //could not select the previously selected outpost (not suitable for the selected missions) + // -> choose random instead + if (outpostDropdown.SelectedData is Identifier selectedIdentifier && + selectedIdentifier != randomOutpostIdentifier) + { + outpostDropdown.Flash(GUIStyle.Red); + } + outpostDropdown.ListBox.Select(randomOutpostIdentifier); + } GameMain.Client.ServerSettings.AssignGUIComponent(nameof(ServerSettings.SelectedOutpostName), outpostDropdown); } else @@ -4496,26 +4551,16 @@ namespace Barotrauma void PositionJobSelectionFrame() { - JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.Right - JobSelectionFrame.Rect.Width, characterInfoFrame.Rect.Bottom); - if (characterInfoFrame.Rect.Bottom + JobSelectionFrame.Rect.Height > GameMain.GraphicsHeight) + //move to the left side of the info frame + JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.X - JobSelectionFrame.Rect.Width, JobList.Rect.Y); + if (JobSelectionFrame.Rect.X < 0) { - //move to the left side of the info frame if the bottom goes below the screen - JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.X - JobSelectionFrame.Rect.Width, characterInfoFrame.Rect.Bottom - JobSelectionFrame.Rect.Height / 2); - if (JobSelectionFrame.Rect.X < 0) - { - //scale if goes outside the screen horizontally - JobSelectionFrame.RectTransform.Resize(new Point(characterInfoFrame.Rect.X, JobSelectionFrame.Rect.Height)); - JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.X - JobSelectionFrame.Rect.Width, JobSelectionFrame.RectTransform.AbsoluteOffset.Y); - } - } + //scale if goes outside the screen horizontally + JobSelectionFrame.RectTransform.Resize(new Point(characterInfoFrame.Rect.X, JobSelectionFrame.Rect.Height)); + JobSelectionFrame.RectTransform.AbsoluteOffset = new Point(characterInfoFrame.Rect.X - JobSelectionFrame.Rect.Width, JobSelectionFrame.RectTransform.AbsoluteOffset.Y); + } } - new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), JobSelectionFrame.RectTransform, anchor: Anchor.Center), style: "OuterGlow", color: Color.Black) - { - UserData = "outerglow", - CanBeFocused = false - }; - var jobSelectionList = new GUIListBox(new RectTransform(Vector2.One * listBoxRelativeSize, JobSelectionFrame.RectTransform, Anchor.Center), style: "GUIFrameListBox") { Padding = Vector4.One * GUI.IntScale(10) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SpriteEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SpriteEditorScreen.cs index 24fc2ae37..8c9232d06 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SpriteEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SpriteEditorScreen.cs @@ -368,6 +368,7 @@ namespace Barotrauma "DecorativeSprite", "BarrelSprite", "RailSprite", + "ChargeSprite", "SchematicSprite", "WeldedSprite" }; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs index 783eaee16..5bf25d7a5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs @@ -8,6 +8,7 @@ using Microsoft.Xna.Framework.Input; using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Globalization; using System.Linq; using System.Threading; using System.Xml.Linq; @@ -37,6 +38,151 @@ namespace Barotrauma Wiring } + #region Transform Editor + private const float TransformWidgetOffset = 300f; + + private GUITickBox rotateToolToggle, scaleToolToggle; + public bool TransformWidgetSelected => TransformWidget.IsSelected; + + private static Vector2 GetSelectionCenter() + { + IEnumerable nonWireEntities = MapEntity.FilteredSelectedList.Where(static entity => (entity as Item)?.GetComponent() is not { Drawable: true }); + if (nonWireEntities.None()) { return Vector2.Zero; } + + float minX = nonWireEntities.Min(static entity => entity.DrawPosition.X); + float minY = nonWireEntities.Min(static entity => entity.DrawPosition.Y); + float maxX = nonWireEntities.Max(static entity => entity.DrawPosition.X); + float maxY = nonWireEntities.Max(static entity => entity.DrawPosition.Y); + return new Vector2(minX + maxX, minY + maxY) / 2f; + } + + private Vector2 oldWidgetWorldPos; + + public record struct TransformData(float Scale, float RotationRad, Vector2 Pos, Rectangle Rect, Vector2? TexOffset, + Dictionary Nodes, float Width)> Wires, + Dictionary TextScales, + Dictionary LightRanges, + Dictionary TurretLimits); + private TransformToolCommand transformCommand; + + private Widget transformWidget; + private Widget TransformWidget + { + get + { + if (transformWidget != null) { return transformWidget; } + + int size = GUI.IntScale(16f); + transformWidget = new Widget("scale", size, WidgetShape.Rectangle) + { + Enabled = false, + Color = GUIStyle.Yellow, + InputAreaMargin = 20, + RequireMouseOn = false, + TooltipOffset = (size / 2f, -size / 2f), + IsFilled = true + }; + transformWidget.PreUpdate += _ => + { + transformWidget.Enabled = MapEntity.FilteredSelectedList.Any() && (rotateToolToggle.Selected || scaleToolToggle.Selected); + if (transformWidget.IsSelected && PlayerInput.PrimaryMouseButtonReleased()) + { + if (MapEntity.EditingHUD.GetChild() is GUIListBox listBox) + { + SerializableEntityEditor.LockEditing = true; + listBox.Content.Children.OfType().ForEach(editor => editor.RefreshValues()); + SerializableEntityEditor.LockEditing = false; + } + Widget.SelectedWidgets.Remove(transformWidget); + StoreCommand(transformCommand); + transformWidget.Color = Color.Yellow; + } + }; + transformWidget.Selected += () => + { + transformWidget.Color = GUIStyle.Blue; + + IEnumerable containedItems = MapEntity.SelectedList.OfType().SelectManyRecursive(item => item.ContainedItems); + IEnumerable allEntities = MapEntity.SelectedList.Concat(containedItems).Distinct(); + + Dictionary oldTransformData = allEntities.ToDictionary(static entity => entity, static entity => + { + Item item = entity as Item; + Structure structure = entity as Structure; + + float rotation = entity switch + { + Structure => MathHelper.ToRadians(structure.Rotation), + Item => item.RotationRad, + _ => 0f + }; + return new TransformData(entity.Scale, rotation, entity.DrawPosition, entity.Rect, structure?.TextureOffset, + GetPropertyDict, float)>(static wire => (wire.GetNodes(), wire.Width)), + GetPropertyDict(static label => label.TextScale), + GetPropertyDict(static light => light.Range), + GetPropertyDict(static turret => turret.RotationLimits)); + + Dictionary GetPropertyDict(Func propSelector) where TComponent : ItemComponent + => item?.GetComponents().ToDictionary(static comp => comp, propSelector); + }); + + transformCommand = new TransformToolCommand(oldTransformData, GetSelectionCenter()); + }; + transformWidget.MouseHeld += _ => + { + MapEntity.DisableSelect = true; + + Vector2 widgetWorldPos = Cam.ScreenToWorld(transformWidget.DrawPos * 2f); // Scale position to account for camera zooming as well. + if (MathUtils.NearlyEqual(widgetWorldPos, oldWidgetWorldPos)) { return; } + oldWidgetWorldPos = widgetWorldPos; + + transformCommand.RotationRad = null; + LocalizedString rotationString = null; + if (rotateToolToggle.Selected) + { + transformCommand.RotationRad = MathUtils.VectorToAngle(PlayerInput.MousePosition - Cam.WorldToScreen(transformCommand.Pivot)); + rotationString = TextManager.GetWithVariable("SubEditor.TransformWidget.Rotation", "[value]", MathHelper.ToDegrees(transformCommand.RotationRad.Value).ToString("0.000", CultureInfo.CurrentCulture)); + } + + transformCommand.ScaleMult = null; + LocalizedString scaleString = null; + if (scaleToolToggle.Selected) + { + transformCommand.ScaleMult = Math.Clamp(Vector2.Distance(PlayerInput.MousePosition, Cam.WorldToScreen(transformCommand.Pivot)) / (TransformWidgetOffset * GUI.Scale), transformCommand.MinScale, transformCommand.MaxScale); + scaleString = TextManager.GetWithVariable("SubEditor.TransformWidget.Scale", "[value]", transformCommand.ScaleMult.Value.ToString("0.000", CultureInfo.CurrentCulture)); + } + + transformWidget.Tooltip = !rotationString.IsNullOrEmpty() && !scaleString.IsNullOrEmpty() + ? LocalizedString.Join("\n", rotationString, scaleString) + : transformWidget.Tooltip = rotationString ?? scaleString; + + transformCommand.Execute(); + }; + transformWidget.PreDraw += (sb, _) => + { + Vector2 selectionCenterScreenPos = Cam.WorldToScreen(transformWidget.IsSelected ? transformCommand.Pivot : GetSelectionCenter()); + + if (!GameMain.Instance.Paused) + { + if (transformWidget.IsSelected && scaleToolToggle.Selected) + { + transformWidget.DrawPos = PlayerInput.MousePosition; + } + else + { + Vector2 dir = transformWidget.IsSelected ? Vector2.Normalize(PlayerInput.MousePosition - Cam.WorldToScreen(transformCommand.Pivot)) : Vector2.UnitX; + transformWidget.DrawPos = selectionCenterScreenPos + dir * TransformWidgetOffset * GUI.Scale; + } + } + + GUI.DrawLine(sb, selectionCenterScreenPos, transformWidget.DrawPos, Color.Black, width: 7f); + GUI.DrawLine(sb, selectionCenterScreenPos, transformWidget.DrawPos, Color.Red, width: 3f); + }; + return transformWidget; + } + } + #endregion + public enum WarningType { NoWaypoints, @@ -530,6 +676,16 @@ namespace Barotrauma }; spacing = new GUIFrame(new RectTransform(new Vector2(0.02f, 1.0f), paddedTopPanel.RectTransform), style: null); + new GUIFrame(new RectTransform(new Vector2(0.1f, 0.9f), spacing.RectTransform, Anchor.Center), style: "VerticalLine"); + + rotateToolToggle = new GUITickBox(new RectTransform(new Vector2(0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "SubEditorRotateToggle") + { + ToolTip = TextManager.Get("SubEditor.RotateToggleToolTip") + }; + scaleToolToggle = new GUITickBox(new RectTransform(new Vector2(0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "SubEditorScaleToggle") + { + ToolTip = TextManager.Get("SubEditor.ScaleToggleToolTip") + }; var selectedLayerText = new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), paddedTopPanel.RectTransform), string.Empty, textAlignment: Alignment.Center); @@ -1099,6 +1255,7 @@ namespace Barotrauma { Submarine.Unload(); GameMain.SubEditorScreen.Select(); + GameMain.GameSession = null; }; } @@ -1451,6 +1608,9 @@ namespace Barotrauma GUI.ForceMouseOn(null); SetMode(Mode.Default); + rotateToolToggle.Selected = false; + scaleToolToggle.Selected = false; + if (backedUpSubInfo != null) { MainSub = new Submarine(backedUpSubInfo); @@ -1660,6 +1820,9 @@ namespace Barotrauma }); ClearFilter(); + + Widget.SelectedWidgets.Remove(TransformWidget); + TransformWidget.Color = Color.Yellow; } private void CreateDummyCharacter() @@ -4636,7 +4799,10 @@ namespace Barotrauma { if (itemPrefab.Name.IsNullOrEmpty() || itemPrefab.HideInMenus || itemPrefab.HideInEditors) { continue; } if (!itemPrefab.Tags.Contains(Tags.WireItem)) { continue; } - if (CircuitBox.IsInGame() && itemPrefab.Tags.Contains(Tags.Thalamus)) { continue; } + if (CircuitBox.IsInGame() && (itemPrefab.Tags.Contains(Tags.Thalamus) || itemPrefab.Tags.Contains("alien"))) + { + continue; + } wirePrefabs.Add(itemPrefab); } @@ -6221,6 +6387,8 @@ namespace Barotrauma CharacterHUD.Update((float)deltaTime, dummyCharacter, cam); } + + TransformWidget.Update((float)deltaTime); } /// @@ -6350,6 +6518,11 @@ namespace Barotrauma } MapEntity.DrawEditor(spriteBatch, cam); + if (TransformWidget.Enabled) + { + TransformWidget.Draw(spriteBatch, (float)deltaTime); + } + GUI.Draw(Cam, spriteBatch); if (MeasurePositionStart != Vector2.Zero) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Social/FriendProviders/SteamFriendProvider.cs b/Barotrauma/BarotraumaClient/ClientSource/Social/FriendProviders/SteamFriendProvider.cs index fa94b7786..dc862bc90 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Social/FriendProviders/SteamFriendProvider.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Social/FriendProviders/SteamFriendProvider.cs @@ -20,7 +20,7 @@ namespace Barotrauma Steamworks.FriendState.Offline => FriendStatus.Offline, Steamworks.FriendState.Invisible => FriendStatus.Offline, _ when steamFriend.IsPlayingThisGame => FriendStatus.PlayingBarotrauma, - _ when steamFriend.GameInfo is { GameID: > 0 } => FriendStatus.PlayingAnotherGame, + _ when steamFriend.GameInfo is { GameID.Value: > 0 } => FriendStatus.PlayingAnotherGame, _ => FriendStatus.NotPlaying }, serverName: steamFriend.GetRichPresence("servername") ?? "", diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/OggSound.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/OggSound.cs index 03490b2c3..c8bfd077e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/OggSound.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/OggSound.cs @@ -17,11 +17,15 @@ namespace Barotrauma.Sounds private short[] sampleBuffer = Array.Empty(); private short[] muffleBuffer = Array.Empty(); + + private readonly double durationSeconds; + public override double? DurationSeconds => durationSeconds; + public OggSound(SoundManager owner, string filename, bool stream, ContentXElement xElement) : base(owner, filename, stream, true, xElement) { var reader = new VorbisReader(Filename); - + durationSeconds = reader.TotalTime.TotalSeconds; ALFormat = reader.Channels == 1 ? Al.FormatMono16 : Al.FormatStereo16; SampleRate = reader.SampleRate; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs index fa2f0080b..4da00abf5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs @@ -1,8 +1,6 @@ -using System; -using OpenAL; +using Barotrauma.IO; using Microsoft.Xna.Framework; -using Barotrauma.IO; -using System.Xml.Linq; +using System; namespace Barotrauma.Sounds { @@ -24,6 +22,11 @@ namespace Barotrauma.Sounds public readonly bool StreamsReliably; + /// + /// Length of the audio in seconds. Null if the length is unknown (e.g. a streaming audio source). + /// + public abstract double? DurationSeconds { get; } + public bool Loading { get; protected set; } private readonly SoundManager.SourcePoolIndex sourcePoolIndex = SoundManager.SourcePoolIndex.Default; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs index 1f2f37dea..0826aa2d3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs @@ -778,34 +778,36 @@ namespace Barotrauma.Sounds while (!killThread) { killThread = true; - for (int i = 0; i < playingChannels.Length; i++) + for (int sourcePoolIndex = 0; sourcePoolIndex < playingChannels.Length; sourcePoolIndex++) { - lock (playingChannels[i]) + lock (playingChannels[sourcePoolIndex]) { - for (int j = 0; j < playingChannels[i].Length; j++) + for (int channelIndex = 0; channelIndex < playingChannels[sourcePoolIndex].Length; channelIndex++) { - if (playingChannels[i][j] == null) { continue; } - if (playingChannels[i][j].IsStream) + var channel = playingChannels[sourcePoolIndex][channelIndex]; + + if (channel == null) { continue; } + if (channel.FadingOutAndDisposing) { - if (playingChannels[i][j].IsPlaying) + killThread = false; + channel.Gain -= 0.1f; + if (channel.Gain <= 0.0f) + { + channel.Dispose(); + playingChannels[sourcePoolIndex][channelIndex] = null; + } + } + else if (channel.IsStream) + { + if (channel.IsPlaying) { killThread = false; - playingChannels[i][j].UpdateStream(); + channel.UpdateStream(); } else { - playingChannels[i][j].Dispose(); - playingChannels[i][j] = null; - } - } - else if (playingChannels[i][j].FadingOutAndDisposing) - { - killThread = false; - playingChannels[i][j].Gain -= 0.1f; - if (playingChannels[i][j].Gain <= 0.0f) - { - playingChannels[i][j].Dispose(); - playingChannels[i][j] = null; + channel.Dispose(); + playingChannels[sourcePoolIndex][channelIndex] = null; } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs index d8c37f0ff..2dd754738 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs @@ -868,10 +868,28 @@ namespace Barotrauma if (Level.IsLoadedOutpost) { // Only return music type for location types which have music tracks defined - var locationType = Level.Loaded.StartLocation?.Type?.Identifier; - if (locationType.HasValue && locationType != Identifier.Empty && musicClips.Any(c => c.Type == locationType)) + var locationType = Level.Loaded?.StartLocation?.Type?.Identifier; + var backgroundMusicIdentifier = Level.Loaded?.StartLocation?.Type?.BackgroundMusicLocationType; + + if (MatchesTrack(backgroundMusicIdentifier, out Identifier id) || + MatchesTrack(locationType, out id)) { - return locationType.Value; + return id; + } + + bool MatchesTrack(Identifier? identifier, out Identifier idValue) + { + if (identifier.HasValue && identifier.Value != Identifier.Empty) + { + if (musicClips.Any(clip => clip.Type == identifier.Value)) + { + idValue = identifier.Value; + return true; + } + } + + idValue = Identifier.Empty; + return false; } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/VideoSound.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/VideoSound.cs index 0e4ca8b1b..11f09193f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/VideoSound.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/VideoSound.cs @@ -1,23 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using OpenAL; +using Barotrauma.Media; using Microsoft.Xna.Framework; -using System.Runtime.InteropServices; -using System.Threading; -using Barotrauma.Media; +using OpenAL; +using System; +using System.Collections.Generic; namespace Barotrauma.Sounds { class VideoSound : Sound { private readonly object mutex; - private Queue sampleQueue; + private readonly Queue sampleQueue; private SoundChannel soundChannel; - private Video video; + private readonly Video video; + + public override double? DurationSeconds => null; public VideoSound(SoundManager owner, string filename, int sampleRate, int channelCount, Video vid) : base(owner, filename, true, false) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/VoipSound.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/VoipSound.cs index f1e220189..83d1aa6b0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/VoipSound.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/VoipSound.cs @@ -8,6 +8,8 @@ namespace Barotrauma.Sounds { class VoipSound : Sound { + public override double? DurationSeconds => null; + public override SoundManager.SourcePoolIndex SourcePoolIndex { get diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sprite/SpriteSheet.cs b/Barotrauma/BarotraumaClient/ClientSource/Sprite/SpriteSheet.cs index 105bb81bf..a6c027014 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sprite/SpriteSheet.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sprite/SpriteSheet.cs @@ -1,4 +1,5 @@ -using Microsoft.Xna.Framework; +using System; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace Barotrauma @@ -18,5 +19,15 @@ namespace Barotrauma spriteBatch.Draw(texture, pos + offset, sourceRects[MathHelper.Clamp(spriteIndex, 0, sourceRects.Length - 1)], color, rotation + rotate, origin, scale, spriteEffect, depth == null ? this.depth : (float)depth); } + + /// + /// When this spritesheet is used for an animation, returns the current spriteIndex based on the given animation speed. + /// + /// Animation speed in frames per second + /// Should the animation run when paused? Defaults to false. + public int GetAnimatedSpriteIndex(float animationSpeed, bool animatePaused = false) + { + return (int)(Math.Floor((animatePaused ? Timing.TotalTime : Timing.TotalTimeUnpaused) * animationSpeed) % FrameCount); + } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/StatusEffects/StatusEffect.cs b/Barotrauma/BarotraumaClient/ClientSource/StatusEffects/StatusEffect.cs index 8f022f2e4..fa75a9b59 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/StatusEffects/StatusEffect.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/StatusEffects/StatusEffect.cs @@ -15,7 +15,8 @@ namespace Barotrauma private readonly static HashSet ActiveLoopingSounds = new HashSet(); private static double LastMuffleCheckTime; private readonly List sounds = new List(); - public IEnumerable Sounds { get { return sounds; } } + public IEnumerable Sounds => sounds; + private SoundSelectionMode soundSelectionMode; private SoundChannel soundChannel; private Entity soundEmitter; @@ -64,6 +65,16 @@ namespace Barotrauma partial void ApplyProjSpecific(float deltaTime, Entity entity, IReadOnlyList targets, Hull hull, Vector2 worldPosition, bool playSound) { + if (steamTimeLineEventToTrigger != default) + { + SteamTimelineManager.AddTimelineEvent( + steamTimeLineEventToTrigger.title, + steamTimeLineEventToTrigger.description, + steamTimeLineEventToTrigger.icon, + priority: 1, + submarine: entity?.Submarine); + } + if (playSound) { PlaySound(entity, hull, worldPosition); @@ -222,6 +233,7 @@ namespace Barotrauma { PlaySound(selectedSound); } + playSoundAfterLoadedCoroutine = null; yield return CoroutineStatus.Success; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamIcons.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamIcons.cs new file mode 100644 index 000000000..b5acce6b4 --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamIcons.cs @@ -0,0 +1,87 @@ +#nullable enable +using System; + +namespace Barotrauma +{ + /// + /// Contains all available Steam timeline event icons as constants and helper methods for number icons. + /// + public static class SteamIcons + { + // Standard icons + public const string Marker = "steam_marker"; + public const string Achievement = "steam_achievement"; + public const string Attack = "steam_attack"; + public const string Bolt = "steam_bolt"; + public const string Bookmark = "steam_bookmark"; + public const string Bug = "steam_bug"; + public const string Cart = "steam_cart"; + public const string Caution = "steam_caution"; + public const string Chat = "steam_chat"; + public const string Checkmark = "steam_checkmark"; + public const string Chest = "steam_chest"; + public const string Circle = "steam_circle"; + public const string Combat = "steam_combat"; + public const string Completed = "steam_completed"; + public const string Crown = "steam_crown"; + public const string Death = "steam_death"; + public const string Defend = "steam_defend"; + public const string Diamond = "steam_diamond"; + public const string Edit = "steam_edit"; + public const string Effect = "steam_effect"; + public const string Explosion = "steam_explosion"; + public const string Fix = "steam_fix"; + public const string Flag = "steam_flag"; + public const string Gem = "steam_gem"; + public const string Group = "steam_group"; + public const string Heart = "steam_heart"; + public const string Info = "steam_info"; + public const string Invalid = "steam_invalid"; + public const string Minus = "steam_minus"; + public const string Pair = "steam_pair"; + public const string Person = "steam_person"; + public const string Plus = "steam_plus"; + public const string Purchase = "steam_purchase"; + public const string Question = "steam_question"; + public const string Ribbon = "steam_ribbon"; + public const string Screenshot = "steam_screenshot"; + public const string Scroll = "steam_scroll"; + public const string Square = "steam_square"; + public const string Star = "steam_star"; + public const string Starburst = "steam_starburst"; + public const string Timer = "steam_timer"; + public const string Transfer = "steam_transfer"; + public const string Triangle = "steam_triangle"; + public const string Trophy = "steam_trophy"; + public const string View = "steam_view"; + public const string X = "steam_x"; + + // Common number icons + public const string Zero = "steam_0"; + public const string One = "steam_1"; + public const string Two = "steam_2"; + public const string Three = "steam_3"; + public const string Four = "steam_4"; + public const string Five = "steam_5"; + public const string Six = "steam_6"; + public const string Seven = "steam_7"; + public const string Eight = "steam_8"; + public const string Nine = "steam_9"; + public const string Ten = "steam_10"; + + /// + /// Gets the Steam icon name for a number between 0 and 99. + /// + /// The number to get the icon for (0-99) + /// The Steam icon name in the format "steam_X" + /// Thrown when the number is less than 0 or greater than 99 + public static string GetNumberIcon(int number) + { + if (number is < 0 or > 99) + { + throw new ArgumentOutOfRangeException(nameof(number), "Number must be between 0 and 99"); + } + return $"steam_{number}"; + } + } +} \ No newline at end of file diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamManager.cs index 875369abc..07ea8956f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamManager.cs @@ -92,6 +92,8 @@ namespace Barotrauma.Steam //Maybe I'm completely wrong! All I know is that we need to handle both! } + + SteamTimelineManager.Initialize(); } public static bool NetworkingDebugLog { get; private set; } = false; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamTimelineManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamTimelineManager.cs new file mode 100644 index 000000000..0558488f1 --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/SteamTimelineManager.cs @@ -0,0 +1,345 @@ +#nullable enable +using Barotrauma.Steam; +using System; +using System.Collections.Generic; +using System.Linq; +using Steamworks.Data; +using Steamworks; + +namespace Barotrauma +{ + internal static class SteamTimelineManager + { + private static Screen? prevScreen; + private static TimelineGameMode gameMode = TimelineGameMode.LoadingScreen; + /// + /// The current submarine that the controlled character is in (and has been for at least the delay amount). + /// + private static Submarine? currentSubmarine = null; + /// + /// For tracking the instantaneous switch of submarines, to reset the delay timer + /// + private static Submarine? previousTrackedSubmarine; + private static Character? trackedCharacter = null; + + /// + /// Delay in seconds before the submarine state change is considered valid, triggering events. + /// + private const float SubmarineStateChangeDelay = 2.0f; + private static float submarineStateChangeTimer = 0.0f; + + public enum TimelineGameMode + { + Playing, + Staging, + Menus, + LoadingScreen + } + + public static void Initialize() + { + SetTimelineGameMode(TimelineGameMode.LoadingScreen); + } + + public static void Update(float deltaTime) + { + PollScreenChange(); + PollCharacterChange(deltaTime); + PollSubmarineChange(deltaTime); + } + + private static void PollScreenChange() + { + if (!SteamManager.IsInitialized) { return; } + if (Screen.Selected == prevScreen) { return; } + + TimelineGameMode newMode = Screen.Selected switch + { + GameScreen _ => TimelineGameMode.Playing, + NetLobbyScreen _ => TimelineGameMode.Staging, + EditorScreen _ => TimelineGameMode.Playing, + MainMenuScreen _ => TimelineGameMode.Menus, + _ => TimelineGameMode.LoadingScreen // Default to Menus for other screens for now + }; + + if (GameMain.Instance != null && GameMain.Instance.LoadingScreenOpen) + { + newMode = TimelineGameMode.LoadingScreen; + } + + if (newMode == gameMode) { return; } + + SetTimelineGameMode(newMode); + gameMode = newMode; + + DebugConsole.NewMessage($"Timeline game mode set to {newMode}"); + + prevScreen = Screen.Selected; + } + + private static void PollCharacterChange(float deltaTime) + { + Character? controlledCharacter = Character.Controlled; + + // reset current sub state if character changes + if (controlledCharacter != trackedCharacter) + { + InstantlySetCurrentSubmarine(controlledCharacter?.Submarine ?? null); + trackedCharacter = controlledCharacter; + } + } + + private static void PollSubmarineChange(float deltaTime) + { + if (!SteamManager.IsInitialized) { return; } + if (trackedCharacter == null) { return; } + if (Screen.Selected is not GameScreen) { return; } + + Submarine? trackedCharacterSubmarine = trackedCharacter.Submarine; + + // timer makes sure only time-stable state changes are registered + if (submarineStateChangeTimer > 0f) + { + submarineStateChangeTimer -= deltaTime; + + if (submarineStateChangeTimer <= 0f) + { + // actually register our pending state change + CharacterSubChanged(trackedCharacter, trackedCharacterSubmarine); + } + } + + // detect instantaneous submarine change and start the delay timer + if (previousTrackedSubmarine != trackedCharacterSubmarine) + { + submarineStateChangeTimer = SubmarineStateChangeDelay; + } + previousTrackedSubmarine = trackedCharacterSubmarine; + } + + private static void InstantlySetCurrentSubmarine(Submarine? submarine) + { + currentSubmarine = submarine; + previousTrackedSubmarine = submarine; + submarineStateChangeTimer = 0f; + } + + private static void CharacterSubChanged(Character character, Submarine newSubmarine) + { + if (newSubmarine == currentSubmarine) { return; } + + // currentSub to none + if (currentSubmarine != null && newSubmarine == null) + { + OnCharacterLeftSubmarine(character, currentSubmarine); + } + // currentSub to newSub + else if (currentSubmarine != null && newSubmarine != null) + { + OnCharacterMovedBetweenSubmarines(character, currentSubmarine, newSubmarine); + } + //none to newSub + else if (currentSubmarine == null && newSubmarine != null) + { + OnCharacterEnteredSubmarine(character, newSubmarine); + } + + currentSubmarine = newSubmarine; + } + + public static void SetTimelineGameMode(TimelineGameMode mode) + { + if (!SteamManager.IsInitialized) { return; } + + Steamworks.TimelineGameMode steamMode = mode switch + { + TimelineGameMode.Playing => Steamworks.TimelineGameMode.Playing, + TimelineGameMode.Staging => Steamworks.TimelineGameMode.Staging, + TimelineGameMode.Menus => Steamworks.TimelineGameMode.Menus, + TimelineGameMode.LoadingScreen => Steamworks.TimelineGameMode.LoadingScreen, + _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, message: null) + }; + + try + { + SteamTimeline.SetTimelineGameMode(steamMode); + } + catch (Exception e) + { + DebugConsole.ThrowError($"Failed to set timeline game mode to {mode}", e); + } + } + + public static void OnPlayerDied(Character victim, CauseOfDeath causeOfDeath) + { + if (victim == null || causeOfDeath == null) { return; } + + string eventTitle = $"{victim.DisplayName} died"; + string causeOfDeathText = causeOfDeath.Affliction != null ? + causeOfDeath.Affliction.CauseOfDeathDescription.Value : + causeOfDeath.Type.ToString(); + string eventDescription = $"{victim.DisplayName} died: {causeOfDeathText}"; + + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Death, 1); + } + + public static void OnSignificantEnemyDied(Character victim, CauseOfDeath causeOfDeath) + { + string eventTitle = $"{victim.DisplayName} has died!"; + string causeOfDeathText = causeOfDeath.Affliction != null ? + causeOfDeath.Affliction.CauseOfDeathDescription.Value : + causeOfDeath.Type.ToString(); + string eventDescription = $"{victim.DisplayName} died: {causeOfDeathText}"; + if (causeOfDeath.Killer != null) + { + eventDescription = $"{victim.DisplayName} was killed by {causeOfDeath.Killer.DisplayName}"; + } + + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Attack, 2); + } + + public static void OnRoundStarted() + { + string eventTitle = "Round Started"; + string eventDescription = "The round has started"; + + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Marker, 0); + } + + public static void OnRoundEnded() + { + string eventTitle = "Round Ended"; + string eventDescription = "The round has ended"; + + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Completed, 0); + } + + public static void OnCharacterLeftSubmarine(Character character, Submarine submarine) + { + string eventTitle = $"{character.Name} Went Diving Outside"; + string eventDescription = $"{character.Name} left {submarine.Info.Name}"; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Transfer, 1); + } + + public static void OnCharacterMovedBetweenSubmarines(Character character, Submarine oldSubmarine, Submarine newSubmarine) + { + string eventTitle = $"{character.Name} Moved Between Locations"; + string eventDescription = $"{character.Name} moved from {oldSubmarine.Info.Name} to {newSubmarine.Info.Name}"; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Transfer, 1); + } + + public static void OnCharacterEnteredSubmarine(Character character, Submarine submarine) + { + string eventTitle = $"{character.Name} Entered Hull"; + string eventDescription = $"{character.Name} has entered {submarine.Info.Name}"; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Transfer, 1); + } + + public static void OnError(string errorMessage, Exception? e = null) + { + // these don't have localization support yet, use hardcoded strings + string eventTitle = "Error Occurred"; + string eventDescription = $"An error was logged: {errorMessage}"; + if (e != null) { eventDescription += $"\n{e.GetType().Name}"; } + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Bug, 3); // Higher priority for errors + } + + public static void OnClientDisconnect(string disconnectInfo) + { + // these don't have localization support yet, use hardcoded strings + string eventTitle = $"Client Disconnected"; + string eventDescription = $"{disconnectInfo}"; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Bug, 2); // Maybe slightly lower priority than code errors + } + + public static void OnMonsterMissionTargetsKilled(MonsterMission mission) + { + // these don't have localization support yet, use hardcoded strings + string eventTitle = $"Monsters Dispatched"; + string eventDescription = $"{mission.Name}: All targets were eliminated."; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Attack, 2); + } + + public static void OnScanSuccessful(ScanMission mission) + { + // these don't have localization support yet, use hardcoded strings + string eventTitle = "Scan Successful"; + string eventDescription = $"{mission.Name}: A scanner has successfully scanned a target."; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Marker, 1); + } + + public static void OnOutpostTargetEliminated(AbandonedOutpostMission mission) + { + // these don't have localization support yet, use hardcoded strings + string eventTitle = $"Target Character Eliminated"; + string eventDescription = $"{mission.Name}: A target was eliminated."; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Attack, 2); + } + + /// + /// How often can hull breach events be created? There's often multiple breaches very close to each other, not necessary to track all of them. + /// + const float HullBreachEventInterval = 10.0f; + private static double LastHullBreachTime; + + public static void OnHullBreached(Structure structure) + { + if (LastHullBreachTime > Timing.TotalTime - HullBreachEventInterval) { return; } + // only trigger this event for player subs, since beacon stations can fill the requirements at level start + if (structure.Submarine?.Info is not { IsPlayer: true }) { return; } + + string eventTitle = "Major Hull Breach"; + string eventDescription = $"The hull of {structure.Submarine?.Info.Name ?? "Unknown Submarine"} suffered a major breach."; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Caution, 2); + LastHullBreachTime = Timing.TotalTime; + } + + public static void OnMissionTargetRetrieved(Item item, Mission mission) + { + string eventTitle = $"Target Retrieved: {item.Name}"; + string eventDescription = $"{mission.Name}: A target item {item.Name} was retrieved."; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Checkmark, 1); + } + + public static void OnMissionTargetPickedUp(Item item, Mission mission) + { + string eventTitle = $"Target Picked Up: {item.Name}"; + string eventDescription = $"{mission.Name}: A target item {item.Name} was picked up."; + AddTimelineEvent(eventTitle, eventDescription, SteamIcons.Checkmark, 1); + } + + public static void AddTimelineEvent(string title, string description, string icon, uint priority = 1, Submarine? submarine = null) + { + if (!SteamManager.IsInitialized) { return; } + + // exit early if title, description or icon is empty + if (string.IsNullOrWhiteSpace(title) || string.IsNullOrWhiteSpace(description) || string.IsNullOrWhiteSpace(icon)) + { + DebugConsole.ThrowError("Failed to add timeline event: title, description or icon is empty"); + return; + } + + if (submarine != null) + { + string submarineName = submarine.Info?.DisplayName.Value ?? "Unknown Submarine"; + title = title.Replace("[sub]", submarineName); + description = description.Replace("[sub]", submarineName); + } + + try + { + var eventHandle = Steamworks.SteamTimeline.AddInstantaneousTimelineEvent( + title, + description, + icon, + priority, + 0.0f, + Steamworks.TimelineEventClipPriority.Standard); + } + catch (Exception e) + { + DebugConsole.ThrowError($"Failed to add timeline event", e); + } + } + } +} \ No newline at end of file diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/InstalledTab.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/InstalledTab.cs index 1b617aa07..20383db55 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/InstalledTab.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/InstalledTab.cs @@ -727,7 +727,9 @@ namespace Barotrauma.Steam } if (selectedMods.All(ContentPackageManager.WorkshopPackages.Contains)) { - if (parentList.AllSelected.All(c => c.GetChild()?.GetAllChildren().Last()?.Style?.Identifier == "WorkshopMenu.DownloadedIcon") && selectedMods.Length > 0 && SteamManager.IsInitialized) + static Identifier? GetButtonIconStyle(GUIComponent c) => c.GetChild()?.GetAllChildren().Last()?.Style?.Identifier; + if (parentList.AllSelected.All(c => GetButtonIconStyle(c) is { } style && (style == "WorkshopMenu.DownloadedIcon" || style == "WorkshopMenu.InfoButtonUpdate")) && + selectedMods.Length > 0 && SteamManager.IsInitialized) { contextMenuOptions.Add(new((selectedMods.Length > 1 ? "UnsubscribeFromAllSelected" : "WorkshopItemUnsubscribe").ToIdentifier(), true, () => { @@ -774,8 +776,7 @@ namespace Barotrauma.Steam return true; } }; - msgBox.Buttons[0].OnClicked += msgBox.Close; - msgBox.Buttons[1].OnClicked += (_, _) => + msgBox.Buttons[0].OnClicked += (_, _) => { if (textBox.Text == mod.Name) { @@ -794,6 +795,7 @@ namespace Barotrauma.Steam return false; } }; + msgBox.Buttons[1].OnClicked += msgBox.Close; } void CopyToLocal() { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/PublishTab.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/PublishTab.cs index b2f6731d2..e4b91f5bc 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/PublishTab.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/PublishTab.cs @@ -10,6 +10,7 @@ using Barotrauma.IO; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Steamworks; +using Steamworks.Ugc; using Directory = Barotrauma.IO.Directory; using ItemOrPackage = Barotrauma.Either; using Path = Barotrauma.IO.Path; @@ -335,9 +336,10 @@ namespace Barotrauma.Steam .WithTags(tagButtons.Where(kvp => kvp.Value.Selected).Select(kvp => kvp.Key.Value)) .WithChangeLog(changeNoteTextBox.Text) .WithMetaData($"gameversion={localPackage.GameVersion};modversion={versionTextBox.Text}") - .WithVisibility(visibility) .WithPreviewFile(thumbnailPath); + ugcEditor.Visibility = visibility; + CoroutineManager.StartCoroutine( MessageBoxCoroutine((currentStepText, messageBox) => PublishItem(currentStepText, messageBox, versionTextBox.Text, ugcEditor, localPackage))); diff --git a/Barotrauma/BarotraumaClient/ClientSource/SubEditorCommands.cs b/Barotrauma/BarotraumaClient/ClientSource/SubEditorCommands.cs index b47221c6d..b313d6008 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/SubEditorCommands.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/SubEditorCommands.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Xml.Linq; +using Barotrauma.Extensions; using Barotrauma.Items.Components; using Microsoft.Xna.Framework; @@ -298,7 +299,7 @@ namespace Barotrauma case null: continue; case ItemContainer { Inventory: not null } newContainer when ic is ItemContainer { Inventory: not null } itemContainer: - itemContainer.Inventory.GetReplacementOrThiS().ReplacedBy = newContainer.Inventory; + itemContainer.Inventory.GetReplacementOrThis().ReplacedBy = newContainer.Inventory; goto default; default: ic.GetReplacementOrThis().ReplacedBy = component; @@ -321,7 +322,7 @@ namespace Barotrauma { if (slotRef.Item == receiver) { - inventory.GetReplacementOrThiS().TryPutItem(it, slotRef.Slot, false, false, null, createNetworkEvent: false); + inventory.GetReplacementOrThis().TryPutItem(it, slotRef.Slot, false, false, null, createNetworkEvent: false); } } } @@ -416,7 +417,7 @@ namespace Barotrauma } else { - Inventory.GetReplacementOrThiS().TryPutItem(item, slot, false, false, null, createNetworkEvent: false); + Inventory.GetReplacementOrThis().TryPutItem(item, slot, false, false, null, createNetworkEvent: false); } } } @@ -609,7 +610,7 @@ namespace Barotrauma { if (targetItem.GetReplacementOrThis() is Item item) { - newInventory?.GetReplacementOrThiS().TryPutItem(item, newSlot, true, false, null, createNetworkEvent: false); + newInventory?.GetReplacementOrThis().TryPutItem(item, newSlot, true, false, null, createNetworkEvent: false); } } @@ -617,7 +618,7 @@ namespace Barotrauma { if (targetItem.GetReplacementOrThis() is Item item) { - oldInventory?.GetReplacementOrThiS().TryPutItem(item, oldSlot, true, false, null, createNetworkEvent: false); + oldInventory?.GetReplacementOrThis().TryPutItem(item, oldSlot, true, false, null, createNetworkEvent: false); } } @@ -628,4 +629,86 @@ namespace Barotrauma return TextManager.GetWithVariable("Undo.MovedItem", "[item]", targetItem.Name); } } + + /// + /// A command for applying changes from the . + /// + internal class TransformToolCommand : Command + { + private readonly Dictionary originalData; + public float? ScaleMult, RotationRad; + public readonly Vector2 Pivot; + private readonly Vector2 wirePivot; + public float MinScale, MaxScale; + + public TransformToolCommand(Dictionary data, Vector2 pivot) + { + originalData = data; + Pivot = pivot; + wirePivot = Pivot - Submarine.MainSub.HiddenSubPosition; + + MinScale = 0.01f / Math.Max(data.Values.Min(data => data.Scale), 0.01f); + MaxScale = 10f / Math.Min(data.Values.Max(data => data.Scale), 10f); + } + + public override void Execute() => UpdateTransforms(RotationRad ?? 0f, ScaleMult ?? 1f); + public override void UnExecute() => UpdateTransforms(0f, 1f); + + public override void Cleanup() => originalData.Clear(); + + private void UpdateTransforms(float rotationRad, float scaleMult) + { + foreach ((MapEntity receiver, SubEditorScreen.TransformData data) in originalData) + { + if (RotationRad.HasValue && receiver is Item { Prefab.AllowRotatingInEditor: true } or Structure { Prefab.AllowRotatingInEditor: true }) + { + int rotationDir = receiver is Structure && receiver.FlippedX ^ receiver.FlippedY ? -1 : 1; + float newRotation = MathHelper.ToDegrees(data.RotationRad + rotationRad * rotationDir); + switch (receiver) + { + case Item item: + item.Rotation = newRotation; + break; + case Structure structure: + structure.Rotation = newRotation; + break; + } + data.TurretLimits?.ForEach(pair => pair.Key.RotationLimits = pair.Value + new Vector2(MathHelper.ToDegrees(rotationRad))); + } + + if (ScaleMult.HasValue) + { + receiver.Scale = data.Scale * scaleMult; + if (receiver.ResizeVertical || receiver.ResizeHorizontal) + { + if (receiver.ResizeVertical) + { + receiver.RectHeight = (int)(data.Rect.Height * scaleMult); + } + if (receiver.ResizeHorizontal) + { + receiver.RectWidth = (int)(data.Rect.Width * scaleMult); + } + if (receiver is Structure structure && data.TexOffset.HasValue) + { + structure.TextureOffset = data.TexOffset.Value * scaleMult; + } + } + data.TextScales?.ForEach(pair => pair.Key.TextScale = pair.Value * scaleMult); + data.LightRanges?.ForEach(pair => pair.Key.Range = pair.Value * scaleMult); + data.Wires?.ForEach(pair => pair.Key.Width = pair.Value.Width * scaleMult); + } + + Vector2 newEntityPos = MathUtils.RotatePoint((data.Pos - Pivot) * scaleMult, -rotationRad) + Pivot; + receiver.Move(newEntityPos - receiver.DrawPosition); + + data.Wires?.ForEach(pair => pair.Key.SetNodes(pair.Value.Nodes.Select(TransformWireNode))); + Vector2 TransformWireNode(Vector2 node) => MathUtils.RotatePoint((node - wirePivot) * scaleMult, -rotationRad) + wirePivot; + } + } + + public override LocalizedString GetDescription() => originalData.Count > 1 + ? TextManager.GetWithVariable("Undo.ChangedTransformMultiple", "[amount]", originalData.Count.ToString()) + : TextManager.GetWithVariable("Undo.ChangedTransform", "[item]", originalData.First().Key.Name); + } } \ No newline at end of file diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 77d2bc5d3..504da8212 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -2,11 +2,11 @@ WinExe - net6.0 + net8.0 Barotrauma FakeFish, Undertow Games Barotrauma - 1.8.8.1 + 1.9.7.0 Copyright © FakeFish 2018-2024 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index 32fd5a28f..6e4ec84c4 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -2,11 +2,11 @@ WinExe - net6.0 + net8.0 Barotrauma FakeFish, Undertow Games Barotrauma - 1.8.8.1 + 1.9.7.0 Copyright © FakeFish 2018-2024 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index bedb1a22a..3faef4874 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -2,11 +2,11 @@ WinExe - net6.0 + net8.0 Barotrauma FakeFish, Undertow Games Barotrauma - 1.8.8.1 + 1.9.7.0 Copyright © FakeFish 2018-2024 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index e364ebb56..ecd62b76a 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -2,11 +2,11 @@ Exe - net6.0 + net8.0 Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 1.8.8.1 + 1.9.7.0 Copyright © FakeFish 2018-2023 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 86dc63f21..2b598f128 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -2,11 +2,11 @@ Exe - net6.0 + net8.0 Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 1.8.8.1 + 1.9.7.0 Copyright © FakeFish 2018-2023 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs index ea9c001ac..f4a7ae58a 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs @@ -677,6 +677,7 @@ namespace Barotrauma { msg.WriteUInt32(CauseOfDeath.Affliction.UintIdentifier); } + msg.WriteUInt16(CauseOfDeath.Killer?.ID ?? NullEntityID); msg.WriteBoolean(forceAfflictionData); if (forceAfflictionData) { diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index 812a348b6..d923ff729 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -2143,9 +2143,33 @@ namespace Barotrauma "freecam", (Client client, Vector2 cursorWorldPos, string[] args) => { - client.UsingFreeCam = true; - GameMain.Server.SetClientCharacter(client, null); - client.SpectateOnly = true; + if (client.UsingFreeCam) + { + // Exiting freecam - try to return to previous character + Character prevCharacter = null; + if (client.PreviousCharacter != null && client.PreviousCharacter.TryGetTarget(out prevCharacter) && + prevCharacter != null && !prevCharacter.IsDead && !prevCharacter.Removed) + { + GameMain.Server.SendConsoleMessage($"{client.Name}: Exiting freecam mode", client, Color.Yellow); + client.UsingFreeCam = false; + GameMain.Server.SetClientCharacter(client, prevCharacter); + client.SpectateOnly = false; + } + else + { + GameMain.Server.SendConsoleMessage($"{client.Name}: Could not regain control of the previous character (dead or removed).", client, Color.Red); + } + } + else + { + // Entering freecam - store current character ID + GameMain.Server.SendConsoleMessage($"{client.Name}: Entering freecam mode", client, Color.Yellow); + Character currentCharacter = client.Character; + client.PreviousCharacter = new WeakReference(currentCharacter); + client.UsingFreeCam = true; + client.SpectateOnly = true; + GameMain.Server.SetClientCharacter(client, null); + } } ); diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs index da7fa0676..0d8d1793f 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -92,6 +92,7 @@ namespace Barotrauma purchasedHullRepairs = value; PurchasedHullRepairsInLatestSave |= value; IncrementLastUpdateIdForFlag(NetFlags.Misc); + DebugConsole.NewMessage("Set PurchasedHullRepairs to " + PurchasedHullRepairs, Color.Cyan); } } public override bool PurchasedLostShuttles @@ -861,6 +862,13 @@ namespace Barotrauma purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall)); } + if (purchasedUpgradeCount > 0 || purchasedItemSwapCount > 0) + { + //if the client attempted to purchase something, increment flag regardless of whether the upgrades were actually purchased or not + //so we can sync the correct state in case the client incorrectly assumed they can buy something (e.g. lost permissions just as they were purchasing) + IncrementLastUpdateIdForFlag(NetFlags.UpgradeManager); + } + int hullRepairCost = GetHullRepairCost(); int itemRepairCost = GetItemRepairCost(); int shuttleRetrieveCost = CampaignMode.ShuttleReplaceCost; @@ -1100,7 +1108,7 @@ namespace Barotrauma var characterList = GameSession.GetSessionCrewCharacters(CharacterType.Both); foreach (var (prefab, category, _) in purchasedUpgrades) { - UpgradeManager.PurchaseUpgrade(prefab, category, client: sender); + UpgradeManager.TryPurchaseUpgrade(prefab, category, client: sender); // unstable logging int price = prefab.Price.GetBuyPrice(prefab, UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation, characterList); @@ -1111,7 +1119,7 @@ namespace Barotrauma { if (purchasedItemSwap.ItemToInstall == null) { - UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove); + UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove, client: sender); } else { @@ -1502,11 +1510,13 @@ namespace Barotrauma { element.Add(new XAttribute("campaignid", CampaignID)); XElement modeElement = new XElement("MultiPlayerCampaign", - new XAttribute("purchasedlostshuttles", PurchasedLostShuttles), - new XAttribute("purchasedhullrepairs", PurchasedHullRepairs), - new XAttribute("purchaseditemrepairs", PurchasedItemRepairs), + new XAttribute("purchasedlostshuttles", PurchasedLostShuttlesInLatestSave), + new XAttribute("purchasedhullrepairs", PurchasedHullRepairsInLatestSave), + new XAttribute("purchaseditemrepairs", PurchasedItemRepairsInLatestSave), new XAttribute("cheatsenabled", CheatsEnabled)); + DebugConsole.NewMessage("Saved PurchasedHullRepairs: "+ PurchasedHullRepairs+" (in last save "+PurchasedHullRepairsInLatestSave+")", Color.Magenta); + modeElement.Add(Settings.Save()); modeElement.Add(SaveStats()); if (GameMain.Server?.TraitorManager is TraitorManager traitorManager) @@ -1520,6 +1530,11 @@ namespace Barotrauma modeElement.Add(GameMain.GameSession?.EventManager.Save()); } + foreach (Identifier unlockedRecipe in GameMain.GameSession.UnlockedRecipes) + { + modeElement.Add(new XElement("unlockedrecipe", new XAttribute("identifier", unlockedRecipe))); + } + CampaignMetadata?.Save(modeElement); Map.Save(modeElement); CargoManager?.SavePurchasedItems(modeElement); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Growable.cs b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Growable.cs index 8749382f1..c8bfedcca 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Growable.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Growable.cs @@ -37,7 +37,7 @@ namespace Barotrauma.Items.Components public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null) { - msg.WriteRangedSingle(Health, 0f, (float)MaxHealth, 8); + msg.WriteRangedSingle(Health, 0f, (float)MaxWater, 8); if (TryExtractEventData(extraData, out EventData eventData)) { int offset = eventData.Offset; diff --git a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Holdable/Holdable.cs b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Holdable/Holdable.cs index 3f5692b46..7d96520a9 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Holdable/Holdable.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Holdable/Holdable.cs @@ -10,7 +10,7 @@ namespace Barotrauma.Items.Components { base.ServerEventWrite(msg, c, extraData); - bool writeAttachData = attachable && body != null; + bool writeAttachData = attachable && originalBody != null; msg.WriteBoolean(writeAttachData); if (!writeAttachData) { return; } @@ -22,8 +22,8 @@ namespace Barotrauma.Items.Components } msg.WriteBoolean(Attached); - msg.WriteSingle(body.SimPosition.X); - msg.WriteSingle(body.SimPosition.Y); + msg.WriteSingle(originalBody.SimPosition.X); + msg.WriteSingle(originalBody.SimPosition.Y); msg.WriteUInt16(item.Submarine?.ID ?? Entity.NullEntityID); msg.WriteUInt16(attacherId); } @@ -40,6 +40,9 @@ namespace Barotrauma.Items.Components Drop(false, null); item.SetTransform(simPosition, 0.0f, findNewHull: false); + //don't find the new hull in SetTransform, because that'd also potentially change the submarine (teleport the item outside if it's attached outside) + //instead just find the hull, so the item is considered to be in the right hull + item.CurrentHull = Hull.FindHull(item.WorldPosition, item.CurrentHull); AttachToWall(); OnUsed.Invoke(new ItemUseInfo(item, c.Character)); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Power/PowerDistributor.cs b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Power/PowerDistributor.cs new file mode 100644 index 000000000..7e5868876 --- /dev/null +++ b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Power/PowerDistributor.cs @@ -0,0 +1,33 @@ +#nullable enable +using Barotrauma.Networking; + +namespace Barotrauma.Items.Components +{ + internal partial class PowerDistributor : PowerTransfer, IServerSerializable, IClientSerializable + { + #region Networking + public void ServerEventRead(IReadMessage msg, Client c) + { + SharedEventRead(msg, out EventType eventType, out PowerGroup powerGroup, out string newName, out float newRatio); + + if (item.CanClientAccess(c)) + { + switch (eventType) + { + case EventType.NameChange: + powerGroup.Name = newName; + break; + case EventType.RatioChange: + powerGroup.SupplyRatio = newRatio; + GameServer.Log($"{GameServer.CharacterLogName(c.Character)} changed supply ratio of power group \"{powerGroup.Name}\" to \"{powerGroup.SupplyRatio}\"", ServerLog.MessageType.ItemInteraction); + break; + } + } + + item.CreateServerEvent(this, new EventData(powerGroup, eventType)); + } + + public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData? extraData = null) => SharedEventWrite(msg, extraData); + #endregion + } +} diff --git a/Barotrauma/BarotraumaServer/ServerSource/Items/Item.cs b/Barotrauma/BarotraumaServer/ServerSource/Items/Item.cs index 4713be50f..0a66bbd08 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Items/Item.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Items/Item.cs @@ -274,6 +274,7 @@ namespace Barotrauma msg.WriteByte(originalSlotIndex < 0 ? (byte)255 : (byte)originalSlotIndex); } + msg.WriteBoolean(OnInsertedEffectsAppliedOnPreviousRound); msg.WriteByte(body == null ? (byte)0 : (byte)body.BodyType); msg.WriteBoolean(SpawnedInCurrentOutpost); msg.WriteBoolean(AllowStealing); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Map/Hull.cs b/Barotrauma/BarotraumaServer/ServerSource/Map/Hull.cs index 463ff3427..86a40a127 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Map/Hull.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Map/Hull.cs @@ -30,8 +30,8 @@ namespace Barotrauma //don't create updates if all clients are very far from the hull float hullUpdateDistanceSqr = NetConfig.HullUpdateDistance * NetConfig.HullUpdateDistance; if (!GameMain.Server.ConnectedClients.Any(c => - c.Character != null && - Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr)) + (c.Character != null && Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr) || + (c.SpectatePos != null && Vector2.DistanceSquared(c.SpectatePos.Value, WorldPosition) < hullUpdateDistanceSqr)) ) { return; } @@ -76,6 +76,11 @@ namespace Barotrauma } } + public void ForceStatusUpdate() + { + statusUpdateTimer = NetConfig.SparseHullUpdateInterval; + } + public void CreateStatusEvent() { @@ -137,6 +142,12 @@ namespace Barotrauma WaterVolume = newWaterVolume; + if (newFireSources.Length != FireSources.Count) + { + //number of fire sources has changed, force a network update + ForceStatusUpdate(); + } + for (int i = 0; i < newFireSources.Length; i++) { Vector2 pos = newFireSources[i].Position; diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/ChatMessage.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/ChatMessage.cs index a407661d6..c76385bf7 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/ChatMessage.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/ChatMessage.cs @@ -176,9 +176,7 @@ namespace Barotrauma.Networking } else { - ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null); - c.ChatSpamTimer = 10.0f; - GameMain.Server.SendDirectChatMessage(denyMsg, c); + BlockBySpamFilter(); } flaggedAsSpam = true; return; @@ -188,14 +186,20 @@ namespace Barotrauma.Networking if (c.ChatSpamTimer > 0.0f && !isSpamExempt) { - ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null); - c.ChatSpamTimer = 10.0f; - GameMain.Server.SendDirectChatMessage(denyMsg, c); + BlockBySpamFilter(); flaggedAsSpam = true; return; } flaggedAsSpam = false; + + void BlockBySpamFilter() + { + ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.BlockedBySpamFilter, null); + c.ChatSpamTimer = BlockedBySpamFilterTime; + GameMain.Server.SendDirectChatMessage(denyMsg, c); + GameServer.Log(c.Name + " blocked by spam filter", ServerLog.MessageType.ServerMessage); + } } public int EstimateLengthBytesServer(Client c) diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/Client.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/Client.cs index 145571884..a89bd2c3b 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/Client.cs @@ -138,6 +138,8 @@ namespace Barotrauma.Networking get { return kickVoters.Count; } } + public WeakReference PreviousCharacter; + partial void InitProjSpecific() { kickVoters = new List(); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index f2f8f067b..b1a274b88 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -4279,6 +4279,9 @@ namespace Barotrauma.Networking public void GiveAchievement(Client client, Identifier achievementIdentifier) { if (client.GivenAchievements.Contains(achievementIdentifier)) { return; } + + DebugConsole.NewMessage($"Attempting to give the achievement {achievementIdentifier} to {client.Name}..."); + client.GivenAchievements.Add(achievementIdentifier); IWriteMessage msg = new WriteOnlyMessage(); @@ -4288,6 +4291,17 @@ namespace Barotrauma.Networking serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable); } + public void UnlockRecipe(Identifier identifier) + { + foreach (var client in connectedClients) + { + IWriteMessage msg = new WriteOnlyMessage(); + msg.WriteByte((byte)ServerPacketHeader.UNLOCKRECIPE); + msg.WriteIdentifier(identifier); + serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable); + } + } + public void IncrementStat(Client client, AchievementStat stat, int amount) { IWriteMessage msg = new WriteOnlyMessage(); diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index cf7484d2b..4f2c384a5 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -2,11 +2,11 @@ Exe - net6.0 + net8.0 Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 1.8.8.1 + 1.9.7.0 Copyright © FakeFish 2018-2023 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]PowerTestSub/PowerTestSub.sub b/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]PowerTestSub/PowerTestSub.sub new file mode 100644 index 000000000..4002bbb9f Binary files /dev/null and b/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]PowerTestSub/PowerTestSub.sub differ diff --git a/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]PowerTestSub/filelist.xml b/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]PowerTestSub/filelist.xml new file mode 100644 index 000000000..c60a2f12f --- /dev/null +++ b/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]PowerTestSub/filelist.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]RotationAndFlippingTests/RotationAndFlippingTests.sub b/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]RotationAndFlippingTests/RotationAndFlippingTests.sub index be65f40e5..0a09be029 100644 Binary files a/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]RotationAndFlippingTests/RotationAndFlippingTests.sub and b/Barotrauma/BarotraumaShared/LocalMods/[DebugOnlyTest]RotationAndFlippingTests/RotationAndFlippingTests.sub differ diff --git a/Barotrauma/BarotraumaShared/SharedSource/AchievementManager.cs b/Barotrauma/BarotraumaShared/SharedSource/AchievementManager.cs index 0e03c7356..a4feacedd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/AchievementManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/AchievementManager.cs @@ -16,7 +16,7 @@ namespace Barotrauma static class AchievementManager { - private static readonly ImmutableHashSet SupportedAchievements = ImmutableHashSet.Create( + public static readonly ImmutableHashSet SupportedAchievements = ImmutableHashSet.Create( "killmoloch".ToIdentifier(), "killhammerhead".ToIdentifier(), "killendworm".ToIdentifier(), @@ -146,6 +146,9 @@ namespace Barotrauma public static void OnStartRound(Biome biome = null) { +#if CLIENT + SteamTimelineManager.OnRoundStarted(); +#endif roundData = new RoundData(); foreach (Item item in Item.ItemList) { @@ -161,6 +164,7 @@ namespace Barotrauma if (GameMain.Client != null) { return; } #endif + if (biome != null && GameMain.GameSession?.GameMode is CampaignMode) { string shortBiomeIdentifier = biome.Identifier.Value.Replace(" ", ""); @@ -233,8 +237,8 @@ namespace Barotrauma //convert submarine velocity to km/h Vector2 submarineVel = Physics.DisplayToRealWorldRatio * ConvertUnits.ToDisplayUnits(sub.Velocity) * 3.6f; - //achievement for going > 100 km/h - if (Math.Abs(submarineVel.X) > 100.0f) + //achievement for going > 50 km/h + if (Math.Abs(submarineVel.X) > 50.0f) { //all conscious characters inside the sub get an achievement UnlockAchievement("subhighvelocity".ToIdentifier(), true, c => c != null && c.Submarine == sub && !c.IsDead && !c.IsUnconscious); @@ -279,7 +283,7 @@ namespace Barotrauma if (c == null || c.Removed) { return; } if (c.HasEquippedItem("clownmask".ToIdentifier()) && - c.HasEquippedItem("clowncostume".ToIdentifier())) + c.HasEquippedItem("clowngear".ToIdentifier())) { UnlockAchievement(c, "clowncostume".ToIdentifier()); } @@ -407,9 +411,33 @@ namespace Barotrauma UnlockAchievement(reviver, "healcrit".ToIdentifier()); } +#if CLIENT + private static void CheckSteamTimelineEvents(Character killedCharacter, CauseOfDeath causeOfDeath) + { + if (killedCharacter == Character.Controlled) + { + SteamTimelineManager.OnPlayerDied(killedCharacter, causeOfDeath); + return; + } + + bool pvpkill = killedCharacter.IsHuman && GameMain.GameSession?.GameMode is PvPMode; + + float combatStrength = killedCharacter.Params.AI?.CombatStrength ?? 0; + bool significantCombatStrength = combatStrength >= 300; + + if (pvpkill || significantCombatStrength) + { + // note: sometimes the causeOfDeath.Killer is null in multiplayer + SteamTimelineManager.OnSignificantEnemyDied(killedCharacter, causeOfDeath); + } + } +#endif + public static void OnCharacterKilled(Character character, CauseOfDeath causeOfDeath) { #if CLIENT + CheckSteamTimelineEvents(character, causeOfDeath); + // If this is a multiplayer game, the client should let the server handle achievements if (GameMain.Client != null || GameMain.GameSession == null) { return; } @@ -417,40 +445,41 @@ namespace Barotrauma causeOfDeath.Killer != null && causeOfDeath.Killer == Character.Controlled) { - IncrementStat(causeOfDeath.Killer, character.IsHuman ? AchievementStat.HumansKilled : AchievementStat.MonstersKilled , 1); + IncrementStat(causeOfDeath.Killer, character.IsHuman ? AchievementStat.HumansKilled : AchievementStat.MonstersKilled, 1); } + #elif SERVER if (character != causeOfDeath.Killer && causeOfDeath.Killer != null) { - IncrementStat(causeOfDeath.Killer, character.IsHuman ? AchievementStat.HumansKilled : AchievementStat.MonstersKilled , 1); + IncrementStat(causeOfDeath.Killer, character.IsHuman ? AchievementStat.HumansKilled : AchievementStat.MonstersKilled, 1); } #endif - UnlockAchievement(causeOfDeath.Killer, $"kill{character.SpeciesName}".ToIdentifier()); + UnlockKillAchievement(causeOfDeath.Killer, character, $"kill{character.SpeciesName}".ToIdentifier()); if (character.CurrentHull != null) { - UnlockAchievement(causeOfDeath.Killer, $"kill{character.SpeciesName}indoors".ToIdentifier()); + UnlockKillAchievement(causeOfDeath.Killer, character, $"kill{character.SpeciesName}indoors".ToIdentifier()); } if (character.SpeciesName.EndsWith("boss")) { - UnlockAchievement(causeOfDeath.Killer, $"kill{character.SpeciesName.Replace("boss", "")}".ToIdentifier()); + UnlockKillAchievement(causeOfDeath.Killer, character, $"kill{character.SpeciesName.Replace("boss", "")}".ToIdentifier()); if (character.CurrentHull != null) { - UnlockAchievement(causeOfDeath.Killer, $"kill{character.SpeciesName.Replace("boss", "")}indoors".ToIdentifier()); + UnlockKillAchievement(causeOfDeath.Killer, character, $"kill{character.SpeciesName.Replace("boss", "")}indoors".ToIdentifier()); } } if (character.SpeciesName.EndsWith("_m")) { - UnlockAchievement(causeOfDeath.Killer, $"kill{character.SpeciesName.Replace("_m", "")}".ToIdentifier()); + UnlockKillAchievement(causeOfDeath.Killer, character, $"kill{character.SpeciesName.Replace("_m", "")}".ToIdentifier()); if (character.CurrentHull != null) { - UnlockAchievement(causeOfDeath.Killer, $"kill{character.SpeciesName.Replace("_m", "")}indoors".ToIdentifier()); + UnlockKillAchievement(causeOfDeath.Killer, character, $"kill{character.SpeciesName.Replace("_m", "")}indoors".ToIdentifier()); } } #if SERVER if (character.SpeciesName == "Jove" && GameMain.GameSession.Campaign is MultiPlayerCampaign && - GameMain.Server?.ServerSettings is { IronmanModeActive: true }) + (GameMain.Server?.ServerSettings is { IronmanModeActive: true } or { RespawnMode: RespawnMode.Permadeath })) { UnlockAchievement( identifier: "europasfinest".ToIdentifier(), @@ -464,8 +493,12 @@ namespace Barotrauma causeOfDeath.Killer != character) { UnlockAchievement(causeOfDeath.Killer, "killclown".ToIdentifier()); + if (character.CharacterHealth?.GetAffliction("psychosis") != null) + { + UnlockAchievement(causeOfDeath.Killer, "whatsmirksbelow".ToIdentifier()); + } } - + if (character.CharacterHealth?.GetAffliction("psychoclown") != null && character.CurrentHull?.Submarine.Info is { Type: SubmarineType.BeaconStation }) { @@ -516,6 +549,20 @@ namespace Barotrauma #endif } + private static void UnlockKillAchievement(Character killer, Character target, Identifier identifier) + { + if (killer != null && + target.Params.UnlockKillAchievementForWholeCrew && + GameSession.GetSessionCrewCharacters(CharacterType.Player).Contains(killer)) + { + UnlockAchievement(identifier, unlockClients: true, characterConditions: c => c != null); + } + else + { + UnlockAchievement(killer, identifier); + } + } + public static void OnTraitorWin(Character character) { #if CLIENT @@ -525,9 +572,15 @@ namespace Barotrauma UnlockAchievement(character, "traitorwin".ToIdentifier()); } - public static void OnRoundEnded(GameSession gameSession) + public static void OnRoundEnded(GameSession gameSession, bool roundInterrupted = false) { +#if CLIENT + SteamTimelineManager.OnRoundEnded(); +#endif if (CheatsEnabled) { return; } + + // no processing for achievements if player quit to menu or such. + if (roundInterrupted) { return; } //made it to the destination if (gameSession?.Submarine != null && Level.Loaded != null && gameSession.Submarine.AtEndExit) @@ -713,7 +766,9 @@ namespace Barotrauma private static void UnlockAchievementsOnPlatforms(Identifier identifier) { if (unlockedAchievements.Contains(identifier)) { return; } - + + DebugConsole.NewMessage($"Attempting to unlock achievement {identifier}..."); + if (SteamManager.IsInitialized) { if (SteamManager.UnlockAchievement(identifier)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs index 5e7fd8e8c..49389c008 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs @@ -4396,11 +4396,11 @@ namespace Barotrauma else if (canAttackDoors && HasValidPath()) { var door = PathSteering.CurrentPath.CurrentNode?.ConnectedDoor ?? PathSteering.CurrentPath.NextNode?.ConnectedDoor; - if (door is { CanBeTraversed: false } && !door.HasAccess(Character)) + if (door is { CanBeTraversed: false } && !door.HasAccess(Character) && door.Item.AiTarget is { } doorAiTarget) { - if (SelectedAiTarget != door.Item.AiTarget || State != AIState.Attack) + if (SelectedAiTarget != doorAiTarget || State != AIState.Attack) { - SelectTarget(door.Item.AiTarget, CurrentTargetMemory.Priority); + SelectTarget(doorAiTarget, CurrentTargetMemory.Priority); State = AIState.Attack; return false; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs index a06695a10..8f0293ba7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs @@ -787,6 +787,13 @@ namespace Barotrauma foreach (Item item in Character.HeldItems) { if (item == null || !item.IsInteractable(Character)) { continue; } + if (!item.UnequipAutomatically) { continue; } + //NPC set to operate the item they're holding, don't put it away + if (ObjectiveManager.CurrentObjective is AIObjectiveOperateItem operateItem && + (operateItem.OperateTarget == item || operateItem.Component?.Item == item)) + { + continue; + } if (Character.TryPutItemInAnySlot(item)) { continue; } if (Character.TryPutItemInBag(item)) { continue; } if (item.HasTag(Tags.Weapon)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs index 743f4506c..a47026b94 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs @@ -69,6 +69,7 @@ namespace Barotrauma } var getItemObjective = new AIObjectiveGetItem(character, gearTag, objectiveManager, equip: true) { + IsFindDivingGearSubObjective = true, AllowStealing = HumanAIController.NeedsDivingGear(character.CurrentHull, out _), AllowToFindDivingGear = false, AllowDangerousPressure = true, diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs index 57e4f73e4..94a876ca7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs @@ -49,6 +49,13 @@ namespace Barotrauma public const float DefaultReach = 100; public const float MaxReach = 150; + /// + /// Is the goal of this objective to get diving gear (i.e. has it been created by )? + /// If so, the objective won't attempt to create another objective if the path requires diving gear + /// (wouldn't make sense to start looking for diving gear so the bot can get to a room they're trying to get diving gear from!) + /// + public bool IsFindDivingGearSubObjective; + public bool AllowToFindDivingGear { get; set; } = true; public bool MustBeSpecificItem { get; set; } @@ -378,6 +385,7 @@ namespace Barotrauma { return new AIObjectiveGoTo(moveToTarget, character, objectiveManager, repeat: false, getDivingGearIfNeeded: AllowToFindDivingGear, closeEnough: DefaultReach) { + IsFindDivingGearSubObjective = IsFindDivingGearSubObjective, // If the root container changes, the item is no longer where it was (taken by someone -> need to find another item) AbortCondition = obj => targetItem == null || (targetItem.GetRootInventoryOwner() is Entity owner && owner != moveToTarget && owner != character), SpeakIfFails = false, diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs index fc7126801..fd5ade494 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs @@ -14,6 +14,13 @@ namespace Barotrauma public override bool KeepDivingGearOn => GetTargetHull() == null; + /// + /// Is the goal of this objective to get diving gear (i.e. has it been created by )? + /// If so, the objective won't attempt to create another objective if the path requires diving gear + /// (wouldn't make sense to start looking for diving gear so the bot can get to a room they're trying to get diving gear from!) + /// + public bool IsFindDivingGearSubObjective; + private AIObjectiveFindDivingGear findDivingGear; private readonly bool repeat; //how long until the path to the target is declared unreachable @@ -379,85 +386,89 @@ namespace Barotrauma } } if (Abandon) { return; } - bool needsDivingSuit = (!isInside || hasOutdoorNodes) && !character.IsImmuneToPressure; - bool tryToGetDivingGear = needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit); - bool tryToGetDivingSuit = needsDivingSuit; - Character followTarget = Target as Character; - if (Mimic && !character.IsImmuneToPressure) + + if (!IsFindDivingGearSubObjective) { - if (HumanAIController.HasDivingSuit(followTarget)) + bool needsDivingSuit = (!isInside || hasOutdoorNodes) && !character.IsImmuneToPressure; + bool tryToGetDivingGear = needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit); + bool tryToGetDivingSuit = needsDivingSuit; + Character followTarget = Target as Character; + if (Mimic && !character.IsImmuneToPressure) { - tryToGetDivingGear = true; - tryToGetDivingSuit = true; + if (HumanAIController.HasDivingSuit(followTarget)) + { + tryToGetDivingGear = true; + tryToGetDivingSuit = true; + } + else if (HumanAIController.HasDivingMask(followTarget) && character.CharacterHealth.OxygenLowResistance < 1) + { + tryToGetDivingGear = true; + } } - else if (HumanAIController.HasDivingMask(followTarget) && character.CharacterHealth.OxygenLowResistance < 1) + bool needsEquipment = false; + float minOxygen = AIObjectiveFindDivingGear.GetMinOxygen(character); + if (tryToGetDivingSuit) { - tryToGetDivingGear = true; + needsEquipment = !HumanAIController.HasDivingSuit(character, minOxygen, requireSuitablePressureProtection: !objectiveManager.FailedToFindDivingGearForDepth); } - } - bool needsEquipment = false; - float minOxygen = AIObjectiveFindDivingGear.GetMinOxygen(character); - if (tryToGetDivingSuit) - { - needsEquipment = !HumanAIController.HasDivingSuit(character, minOxygen, requireSuitablePressureProtection: !objectiveManager.FailedToFindDivingGearForDepth); - } - else if (tryToGetDivingGear) - { - needsEquipment = !HumanAIController.HasDivingGear(character, minOxygen); - } - if (!getDivingGearIfNeeded) - { - if (needsEquipment) + else if (tryToGetDivingGear) { - // Don't try to reach the target without proper equipment. - Abandon = true; - return; + needsEquipment = !HumanAIController.HasDivingGear(character, minOxygen); } - } - else - { - if (character.LockHands) + if (!getDivingGearIfNeeded) { - cantFindDivingGear = true; + if (needsEquipment) + { + // Don't try to reach the target without proper equipment. + Abandon = true; + return; + } } - if (cantFindDivingGear && needsDivingSuit) + else { - // Don't try to reach the target without a suit because it's lethal. - Abandon = true; - return; - } - if (needsEquipment && !cantFindDivingGear) - { - SteeringManager.Reset(); - TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: tryToGetDivingSuit, objectiveManager), - onAbandon: () => - { - cantFindDivingGear = true; - if (needsDivingSuit) + if (character.LockHands) + { + cantFindDivingGear = true; + } + if (cantFindDivingGear && needsDivingSuit) + { + // Don't try to reach the target without a suit because it's lethal. + Abandon = true; + return; + } + if (needsEquipment && !cantFindDivingGear) + { + SteeringManager.Reset(); + TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: tryToGetDivingSuit, objectiveManager), + onAbandon: () => { - // Shouldn't try to reach the target without a suit, because it's lethal. - Abandon = true; - } - else - { - // Try again without requiring the diving suit (or mask) - RemoveSubObjective(ref findDivingGear); - TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: !tryToGetDivingSuit, objectiveManager), - onAbandon: () => - { - Abandon = character.CurrentHull != null && (objectiveManager.CurrentOrder != this || Target.Submarine == null); - RemoveSubObjective(ref findDivingGear); - }, - onCompleted: () => - { - RemoveSubObjective(ref findDivingGear); - }); - } - }, - onCompleted: () => RemoveSubObjective(ref findDivingGear)); - return; + cantFindDivingGear = true; + if (needsDivingSuit) + { + // Shouldn't try to reach the target without a suit, because it's lethal. + Abandon = true; + } + else + { + // Try again without requiring the diving suit (or mask) + RemoveSubObjective(ref findDivingGear); + TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: !tryToGetDivingSuit, objectiveManager), + onAbandon: () => + { + Abandon = character.CurrentHull != null && (objectiveManager.CurrentOrder != this || Target.Submarine == null); + RemoveSubObjective(ref findDivingGear); + }, + onCompleted: () => + { + RemoveSubObjective(ref findDivingGear); + }); + } + }, + onCompleted: () => RemoveSubObjective(ref findDivingGear)); + return; + } } - } + } if (IsDoneFollowing()) { OnCompleted(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs index 9cfaaf315..4405a791e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs @@ -347,7 +347,10 @@ namespace Barotrauma useItemTimer = 0.05f; StartUsingItem(); - if (!allowMovement) + //make the character move towards the item they're using... + if (!allowMovement && + //...except if they've selected an item that controls the character's direction (e.g. a periscope) + character.SelectedSecondaryItem?.GetComponent() is not { Direction: Direction.Left or Direction.Right }) { TargetMovement = Vector2.Zero; TargetDir = handWorldPos.X > character.WorldPosition.X ? Direction.Right : Direction.Left; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/CauseOfDeath.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/CauseOfDeath.cs index e1cd01576..adc94a484 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/CauseOfDeath.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/CauseOfDeath.cs @@ -4,7 +4,11 @@ namespace Barotrauma { enum CauseOfDeathType { - Unknown, Pressure, Suffocation, Drowning, Affliction, Disconnected + Unknown, Pressure, Suffocation, Drowning, Affliction, Disconnected, + /// + /// Special cause of death type returned by when the character is not dead. + /// + None } class CauseOfDeath diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs index 184ae6f8c..6c0602564 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs @@ -592,6 +592,9 @@ namespace Barotrauma public Identifier VariantOf => Prefab.VariantOf; + /// + /// Non-localized name of the character (for characters with info, their name, for monsters, their species). E.g. "Mudraptor_veteran", "John Smith". + /// public string Name { get @@ -600,6 +603,9 @@ namespace Barotrauma } } + /// + /// Localized display name of the character (e.g. "Mudraptor Veteran", "John Smith") - this should generally be used in any the player sees. + /// public string DisplayName { get @@ -1210,6 +1216,11 @@ namespace Barotrauma private set; } + /// + /// Can be used by mods to check the cause of death of the character using conditionals (e.g. if some effects should or should not be triggered by certain causes of death). + /// + public CauseOfDeathType CauseOfDeathType => CauseOfDeath?.Type ?? CauseOfDeathType.None; + //can other characters select (= grab) this character public bool CanBeSelected { @@ -2413,7 +2424,7 @@ namespace Barotrauma { if (!CanInteractWith(item)) { continue; } - if (SelectedItem?.OwnInventory != null && SelectedItem.OwnInventory.CanBePut(item)) + if (SelectedItem?.OwnInventory != null && !SelectedItem.OwnInventory.Locked && SelectedItem.OwnInventory.CanBePut(item)) { SelectedItem.OwnInventory.TryPutItem(item, this); } @@ -5729,6 +5740,7 @@ namespace Barotrauma public bool HasRecipeForItem(Identifier recipeIdentifier) { + if (GameMain.GameSession != null && GameMain.GameSession.UnlockedRecipes.Contains(recipeIdentifier)) { return true; } return characterTalents.Any(t => t.UnlockedRecipes.Contains(recipeIdentifier)); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs index 377e5894e..6039a7896 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs @@ -408,6 +408,11 @@ namespace Barotrauma public HashSet UnlockedTalents { get; private set; } = new HashSet(); + /// + /// Which of the character's extra talents (talents unlocked from outside their own talent tree) are reset when the talents are resetted using e.g. Mindwipe? + /// + public HashSet ResettableExtraTalents { get; private set; } = new HashSet(); + private int talentResetCount; /// @@ -792,14 +797,6 @@ namespace Barotrauma Salary = CalculateSalary(); } OriginalName = !string.IsNullOrEmpty(originalName) ? originalName : Name; - - TalentRefundPoints = CharacterConfigElement.GetAttributeInt("refundpoints", 0); - - int loadedLastRewardDistribution = CharacterConfigElement.GetAttributeInt("lastrewarddistribution", -1); - if (loadedLastRewardDistribution >= 0) - { - LastRewardDistribution = Option.Some(loadedLastRewardDistribution); - } } private void SetPersonalityTrait() @@ -1016,10 +1013,22 @@ namespace Barotrauma } UnlockedTalents.Add(talentIdentifier); + if (talentElement.GetAttributeBool("resettable", defaultValue: false)) + { + ResettableExtraTalents.Add(talentIdentifier); + } } } } + TalentRefundPoints = infoElement.GetAttributeInt("refundpoints", 0); + + int loadedLastRewardDistribution = infoElement.GetAttributeInt("lastrewarddistribution", -1); + if (loadedLastRewardDistribution >= 0) + { + LastRewardDistribution = Option.Some(loadedLastRewardDistribution); + } + LoadHeadAttachments(); } @@ -1535,7 +1544,13 @@ namespace Barotrauma if (TalentRefundPoints <= 0) { return; } //e.g. talents from endocrine booster or extra talents some special NPC has + //stored in a list so we can re-unlock them on the character var talentsFromOutsideTree = GetUnlockedTalentsOutsideTree().ToList(); + //remove resettable talents, so they DON'T get re-unlocked + foreach (var resettableExtraTalent in ResettableExtraTalents) + { + talentsFromOutsideTree.Remove(resettableExtraTalent); + } UnlockedTalents.Clear(); SavedStatValues.Clear(); @@ -1570,6 +1585,9 @@ namespace Barotrauma public void Rename(string newName) { if (string.IsNullOrEmpty(newName)) { return; } + + newName = Networking.Client.SanitizeName(newName); + // Replace the name tag of any existing id cards or duffel bags foreach (var item in Item.ItemList) { @@ -1673,7 +1691,10 @@ namespace Barotrauma foreach (Identifier talentIdentifier in UnlockedTalents) { - talentElement.Add(new XElement("Talent", new XAttribute("identifier", talentIdentifier))); + talentElement.Add( + new XElement("Talent", + new XAttribute("identifier", talentIdentifier), + new XAttribute("resettable", ResettableExtraTalents.Contains(talentIdentifier)))); } charElement.Add(savedStatElement); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs index 725e12478..132b79833 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs @@ -882,6 +882,16 @@ namespace Barotrauma /// Its opacity is controlled by the active effect's MinAfflictionOverlayAlphaMultiplier and MaxAfflictionOverlayAlphaMultiplier /// public readonly Sprite AfflictionOverlay; + + /// + /// The speed of the affliction overlay animation. + /// Only applicable with AfflictionOverlayAnimated, and the overlay has to be a spritesheet so there's something to animate. + /// + public float AfflictionOverlayAnimSpeed + { + get; + set; + } public ImmutableDictionary TreatmentSuitabilities { @@ -1005,6 +1015,10 @@ namespace Barotrauma case "afflictionoverlay": AfflictionOverlay = new Sprite(subElement); break; + case "afflictionoverlayanimated": + AfflictionOverlay = new SpriteSheet(subElement); + AfflictionOverlayAnimSpeed = subElement.GetAttributeFloat("animspeed", 1.0f); + break; case "statvalue": DebugConsole.ThrowError($"Error in affliction \"{Identifier}\" - stat values should be configured inside the affliction's effects.", contentPackage: element.ContentPackage); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/HumanPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/HumanPrefab.cs index eaf739333..67b0141e4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/HumanPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/HumanPrefab.cs @@ -302,7 +302,7 @@ namespace Barotrauma new List(item.GetComponent()?.AllowedSlots ?? item.GetComponent().AllowedSlots) : new List(item.AllowedSlots); allowedSlots.Remove(InvSlotType.Any); - + item.UnequipAutomatically = false; character.Inventory.TryPutItem(item, null, allowedSlots); } else diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Jobs/Job.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Jobs/Job.cs index d3d4bfc0c..8ea5d7839 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Jobs/Job.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Jobs/Job.cs @@ -191,6 +191,7 @@ namespace Barotrauma new List(item.GetComponent()?.AllowedSlots ?? item.GetComponent().AllowedSlots) : new List(item.AllowedSlots); allowedSlots.Remove(InvSlotType.Any); + item.UnequipAutomatically = false; character.Inventory.TryPutItem(item, null, allowedSlots); } else diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs index 1e9755e5a..963ea9a7c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs @@ -308,6 +308,9 @@ namespace Barotrauma public Vector2 DebugTargetPos; public Vector2 DebugRefPos; + /// + /// Is the limb the waist, a part of a leg or a tail? + /// public bool IsLowerBody { get @@ -330,22 +333,33 @@ namespace Barotrauma } } + /// + /// Is the limb a leg or a part of a leg (upper or lower leg or foot) + /// public bool IsLeg { get { - switch (type) + return type switch { - case LimbType.LeftFoot: - case LimbType.LeftLeg: - case LimbType.LeftThigh: - case LimbType.RightFoot: - case LimbType.RightLeg: - case LimbType.RightThigh: - return true; - default: - return false; - } + LimbType.LeftFoot or LimbType.LeftLeg or LimbType.LeftThigh or LimbType.RightFoot or LimbType.RightLeg or LimbType.RightThigh => true, + _ => false, + }; + } + } + + /// + /// Is the limb an arm or a part of an arm (upper or lower arm or hand) + /// + public bool IsArm + { + get + { + return type switch + { + LimbType.LeftArm or LimbType.LeftForearm or LimbType.LeftHand or LimbType.RightArm or LimbType.RightForearm or LimbType.RightHand => true, + _ => false, + }; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs index fbef6a4f5..14c8e83c8 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs @@ -156,6 +156,9 @@ namespace Barotrauma [Serialize(1.0f, IsPropertySaveable.Yes, description: "The multiplier of the minimum distance required between this character and the player/submarine before the music starts playing. The default distance is twice the length of the submarine, or a minimum of 50 meters."), Editable] public float MusicRangeMultiplier { get; private set; } + [Serialize(false, IsPropertySaveable.Yes, description: "Should the entire crew get an achievement (assuming there is one) if someone from the crew kills the character?")] + public bool UnlockKillAchievementForWholeCrew { get; set; } + public readonly CharacterFile File; public bool IsPet => AI?.IsPet ?? false; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityCondition.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityCondition.cs index 5c352384b..b91c04880 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityCondition.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityCondition.cs @@ -69,9 +69,9 @@ namespace Barotrauma.Abilities switch (targetType) { case TargetType.Enemy: - return !HumanAIController.IsFriendly(character, targetCharacter, onlySameTeam: false); + return !HumanAIController.IsFriendly(character, targetCharacter); case TargetType.Ally: - return HumanAIController.IsFriendly(character, targetCharacter, onlySameTeam: true); + return HumanAIController.IsFriendly(character, targetCharacter); case TargetType.NotSelf: return targetCharacter != character; case TargetType.Alive: @@ -84,5 +84,6 @@ namespace Barotrauma.Abilities return true; } } + } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityApplyStatusEffects.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityApplyStatusEffects.cs index 4ef07d6e2..210a852f9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityApplyStatusEffects.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityApplyStatusEffects.cs @@ -65,7 +65,7 @@ namespace Barotrauma.Abilities } if (!nearbyCharactersAppliesToAllies) { - targets.RemoveAll(c => c is Character otherCharacter && HumanAIController.IsFriendly(otherCharacter, Character, onlySameTeam: true)); + targets.RemoveAll(c => c is Character otherCharacter && HumanAIController.IsFriendly(otherCharacter, Character)); } if (!nearbyCharactersAppliesToEnemies) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityPsychoClown.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityPsychoClown.cs index 5f8b94591..570c3ddfb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityPsychoClown.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityPsychoClown.cs @@ -3,7 +3,7 @@ class CharacterAbilityPsychoClown : CharacterAbility { private readonly StatTypes statType; - private readonly float maxValue; + private readonly float minValue, maxValue; private readonly string afflictionIdentifier; private float lastValue = 0f; public override bool AllowClientSimulation => true; @@ -11,8 +11,9 @@ public CharacterAbilityPsychoClown(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement) { statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier); - maxValue = abilityElement.GetAttributeFloat("maxvalue", 0f); - afflictionIdentifier = abilityElement.GetAttributeString("afflictionidentifier", ""); + maxValue = abilityElement.GetAttributeFloat(nameof(maxValue), 0f); + minValue = abilityElement.GetAttributeFloat(nameof(minValue), 0f); + afflictionIdentifier = abilityElement.GetAttributeString(nameof(afflictionIdentifier), ""); } protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate) @@ -31,7 +32,7 @@ afflictionStrength = affliction.Strength / affliction.Prefab.MaxStrength; } - lastValue = afflictionStrength * maxValue; + lastValue = minValue + afflictionStrength * (maxValue - minValue); Character.ChangeStat(statType, lastValue); } else diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityUnlockApprenticeshipTalentTree.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityUnlockApprenticeshipTalentTree.cs index df658dfa7..c6d9d476a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityUnlockApprenticeshipTalentTree.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityUnlockApprenticeshipTalentTree.cs @@ -67,6 +67,7 @@ namespace Barotrauma.Abilities { if (Character.HasTalent(identifier)) { continue; } Character.GiveTalent(identifier); + Character.Info.ResettableExtraTalents.Add(identifier); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/CircuitBox/CircuitBoxInputOutputNode.cs b/Barotrauma/BarotraumaShared/SharedSource/CircuitBox/CircuitBoxInputOutputNode.cs index a212507dc..e589401bc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/CircuitBox/CircuitBoxInputOutputNode.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/CircuitBox/CircuitBoxInputOutputNode.cs @@ -73,10 +73,12 @@ namespace Barotrauma : TextManager.Get(value).Fallback(value); conn.SetLabel(newLabel, this); + conn.Connection.DisplayNameOverride = string.IsNullOrWhiteSpace(value) ? null : newLabel; } else { conn.SetLabel(conn.Connection.DisplayName, this); + conn.Connection.DisplayNameOverride = null; } } #endif diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/TextFile.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/TextFile.cs index 978429cbb..0a4cb4176 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/TextFile.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/TextFile.cs @@ -21,7 +21,7 @@ namespace Barotrauma { TextManager.TextPacks.TryAdd(language, ImmutableList.Empty); } - var newPack = new TextPack(this, mainElement, language); + var newPack = new TextPack(this, mainElement, language, load: language == GameSettings.CurrentConfig.Language); var newList = TextManager.TextPacks[language].Add(newPack); TextManager.TextPacks.TryRemove(language, out _); TextManager.TextPacks.TryAdd(language, newList); diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentXElement.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentXElement.cs index b0a296e7c..4d09534e5 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentXElement.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentXElement.cs @@ -76,10 +76,12 @@ namespace Barotrauma public string GetAttributeStringUnrestricted(string key, string def) => Element.GetAttributeStringUnrestricted(key, def); public string[]? GetAttributeStringArray(string key, string[]? def, bool convertToLowerInvariant = false) => Element.GetAttributeStringArray(key, def, convertToLowerInvariant); public ContentPath? GetAttributeContentPath(string key) => Element.GetAttributeContentPath(key, ContentPackage); + public int? GetAttributeNullableInt(string key) => Element.GetAttributeNullableInt(key); public int GetAttributeInt(string key, int def) => Element.GetAttributeInt(key, def); public ushort GetAttributeUInt16(string key, ushort def) => Element.GetAttributeUInt16(key, def); public int[]? GetAttributeIntArray(string key, int[]? def) => Element.GetAttributeIntArray(key, def); public ushort[]? GetAttributeUshortArray(string key, ushort[]? def) => Element.GetAttributeUshortArray(key, def); + public float? GetAttributeNullableFloat(string key) => Element.GetAttributeNullableFloat(key); public float GetAttributeFloat(string key, float def) => Element.GetAttributeFloat(key, def); public float[]? GetAttributeFloatArray(string key, float[]? def) => Element.GetAttributeFloatArray(key, def); public float GetAttributeFloat(float def, params string[] keys) => Element.GetAttributeFloat(def, keys); diff --git a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs index 1c7af9433..d5c65aa95 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs @@ -163,6 +163,8 @@ namespace Barotrauma private static readonly int messagesPerFile = 800; public const string SavePath = "ConsoleLogs"; + private static WeakReference previousControlledCharacter; // For SP freecam + public static void AssignOnExecute(string names, Action onExecute) { var matchingCommand = commands.Find(c => c.Names.Intersect(names.Split('|').ToIdentifiers()).Any()); @@ -936,8 +938,29 @@ namespace Barotrauma if (GameMain.Client == null) { - Character.Controlled = null; - GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; + if (Character.Controlled == null) + { + // Exiting freecam - try to return to previous character + Character prevCharacter = null; + if (previousControlledCharacter != null && previousControlledCharacter.TryGetTarget(out prevCharacter) && + prevCharacter != null && !prevCharacter.IsDead && !prevCharacter.Removed) + { + Character.Controlled = prevCharacter; + NewMessage("Exiting freecam mode", Color.Yellow); + } + else + { + NewMessage("Could not regain control of the previous character (dead or removed).", Color.Red); + } + } + else + { + // Entering freecam - store current character ID + previousControlledCharacter = new WeakReference(Character.Controlled); + Character.Controlled = null; + GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; + NewMessage("Entering freecam mode", Color.Yellow); + } } else { @@ -1349,14 +1372,13 @@ namespace Barotrauma } else if (args[0].Equals("endoutpost", StringComparison.OrdinalIgnoreCase)) { - Submarine.MainSub.SetPosition(Level.Loaded.EndExitPosition - Vector2.UnitY * Submarine.MainSub.Borders.Height); - - var submarineDockingPort = DockingPort.List.FirstOrDefault(d => d.Item.Submarine == Submarine.MainSub); if (Level.Loaded?.EndOutpost == null) { NewMessage("Can't teleport the sub to the end outpost (no outpost at the end of the level).", Color.Red); return; } + Submarine.MainSub.SetPosition(Level.Loaded.EndExitPosition - Vector2.UnitY * Submarine.MainSub.Borders.Height); + var submarineDockingPort = DockingPort.List.FirstOrDefault(d => d.Item.Submarine == Submarine.MainSub); var outpostDockingPort = DockingPort.List.FirstOrDefault(d => d.Item.Submarine == Level.Loaded.EndOutpost); if (submarineDockingPort != null && outpostDockingPort != null) { @@ -1617,7 +1639,18 @@ namespace Barotrauma yield return CoroutineStatus.Success; } } - + + if (Level.Loaded.StartOutpost != null && + Level.Loaded.StartOutpost.Info.OutpostTags.Contains("PvPOutpost".ToIdentifier())) + { + ThrowError("Chose a PvP outpost for the start of the level. This is probably not intentional, unless there's a PvP outpost that's also intended to be used in normal levels?"); + } + if (Level.Loaded.EndOutpost != null && + Level.Loaded.EndOutpost.Info.OutpostTags.Contains("PvPOutpost".ToIdentifier())) + { + ThrowError("Chose a PvP outpost for the end of the level. This is probably not intentional, unless there's a PvP outpost that's also intended to be used in normal levels?"); + } + var levelCells = Level.Loaded.GetCells( Submarine.MainSub.WorldPosition, Math.Max(Submarine.MainSub.Borders.Width / Level.GridCellSize, 2)); @@ -1797,7 +1830,7 @@ namespace Barotrauma int targetLevel = prefab.GetMaxLevelForCurrentSub() - upgradeManager.GetRealUpgradeLevel(prefab, category); for (int i = 0; i < targetLevel; i++) { - upgradeManager.PurchaseUpgrade(prefab, category, force: true); + upgradeManager.TryPurchaseUpgrade(prefab, category, force: true); } NewMessage($"Upgraded {category.Identifier}.{prefab.Identifier} by {targetLevel} levels.", Color.DarkGreen); } @@ -2298,6 +2331,7 @@ namespace Barotrauma commands.Add(new Command("devmode", "Toggle the dev mode on/off (client-only).", null, isCheat: true)); commands.Add(new Command("showmonsters", "Permanently unlocks all the monsters in the character editor. Use \"hidemonsters\" to undo.", null, isCheat: true)); commands.Add(new Command("hidemonsters", "Permanently hides in the character editor all the monsters that haven't been encountered in the game. Use \"showmonsters\" to undo.", null, isCheat: true)); + commands.Add(new Command("loslightingfreecam", "Toggles line of sight effect, lighting, and enables freecam mode. (client-only)", null, isCheat: true)); InitProjectSpecific(); @@ -2912,7 +2946,7 @@ namespace Barotrauma isHuman = job != null || characterLowerCase == CharacterPrefab.HumanSpeciesName; } - ParseOptionalArgs(out Vector2 spawnPosition, out WayPoint spawnPoint, out CharacterTeamType teamType, out bool addToCrew); + ParseOptionalArgs(out Vector2 spawnPosition, out WayPoint spawnPoint, out CharacterTeamType? teamType, out bool addToCrew); if (usePreConfiguredNPC) { @@ -2951,25 +2985,30 @@ namespace Barotrauma } else if (CharacterPrefab.FindBySpeciesName(args[0].ToIdentifier()) is { } prefab) { - Entity.Spawner.AddCharacterToSpawnQueue(args[0].ToIdentifier(), spawnPosition, prefab.HasCharacterInfo ? new CharacterInfo(prefab.Identifier) : null, onSpawn: newCharacter => - { - SetTeamAndCrew(newCharacter); - }); + Entity.Spawner.AddCharacterToSpawnQueue(args[0].ToIdentifier(), spawnPosition, prefab.HasCharacterInfo ? new CharacterInfo(prefab.Identifier) : null, onSpawn: SetTeamAndCrew); } void SetTeamAndCrew(Character newCharacter) { - newCharacter.TeamID = teamType; + if (teamType.HasValue) + { + newCharacter.TeamID = teamType.Value; + } + else if (isHuman) + { + newCharacter.TeamID = Character.Controlled?.TeamID ?? CharacterTeamType.Team1; + } if (addToCrew) { GameMain.GameSession?.CrewManager.AddCharacter(newCharacter); } } - void ParseOptionalArgs(out Vector2 spawnPosition, out WayPoint spawnPoint, out CharacterTeamType teamType, out bool addToCrew) + void ParseOptionalArgs(out Vector2 spawnPosition, out WayPoint spawnPoint, out CharacterTeamType? teamType, out bool addToCrew) { spawnPosition = Vector2.Zero; spawnPoint = null; + teamType = null; int argIndex = characterArgumentCount; if (args.Length > argIndex) @@ -3025,15 +3064,14 @@ namespace Barotrauma { teamType = (CharacterTeamType)teamID; } - else if (!Enum.TryParse(args[argIndex], ignoreCase: true, out teamType)) + else if (Enum.TryParse(args[argIndex], ignoreCase: true, out CharacterTeamType parsedTeamType)) { - teamType = Character.Controlled != null ? Character.Controlled.TeamID : CharacterTeamType.Team1; - ThrowError($"\"{args[argIndex]}\" is not a valid team id. Defaulting to {teamType}."); + teamType = parsedTeamType; + } + else + { + ThrowError($"\"{args[argIndex]}\" is not a valid team id."); } - } - else - { - teamType = Character.Controlled != null ? Character.Controlled.TeamID : CharacterTeamType.Team1; } argIndex++; @@ -3394,6 +3432,9 @@ namespace Barotrauma public static void ThrowError(string error, Exception e = null, ContentPackage contentPackage = null, bool createMessageBox = false, bool appendStackTrace = false) { error = AddContentPackageInfoToMessage(error, contentPackage); +#if CLIENT + SteamTimelineManager.OnError(error, e); +#endif if (e != null) { error += " {" + e.Message + "}\n"; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Enums.cs b/Barotrauma/BarotraumaShared/SharedSource/Enums.cs index bc7e4df7f..79a25913a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Enums.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Enums.cs @@ -123,7 +123,7 @@ namespace Barotrauma /// OnAbility = 23, /// - /// Executes once when a specific Containable is placed inside an ItemContainer. Only valid for Containables defined in an ItemContainer component. + /// Executes once when a specific Containable is placed inside an ItemContainer. Only valid for Containables defined in an ItemContainer component. Does not execute if the item is placed into the container when loading an existing submarine or a character (i.e. when the item was "already in" the container). /// OnInserted = 24, /// diff --git a/Barotrauma/BarotraumaShared/SharedSource/Eos/Session.cs b/Barotrauma/BarotraumaShared/SharedSource/Eos/Session.cs index d87a7c2e1..95c00e229 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Eos/Session.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Eos/Session.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using System.Collections.Generic; using System.Linq; using Barotrauma.Networking; @@ -53,7 +53,7 @@ static class EosSessionManager // If the session already exists then this failure is not a problem return; } - DebugConsole.ThrowError($"Failed to create session: {result}"); + DebugConsole.ThrowError($"Failed to create Epic Online Services session: {result}"); return; } CurrentOwnedSession = Option.Some(newOwnedSession); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/CombatAction.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/CombatAction.cs index a9cc76a69..f1074eb50 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/CombatAction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/CombatAction.cs @@ -1,4 +1,4 @@ -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using System.Collections.Generic; using System.Linq; @@ -30,6 +30,9 @@ namespace Barotrauma [Serialize(120.0f, IsPropertySaveable.Yes, description: "How long it takes for the NPC to \"cool down\" (stop attacking).")] public float CoolDown { get; set; } + [Serialize(true, IsPropertySaveable.Yes, description: "The event actions reset when a GoTo action makes the event jump to a different point. Should the NPC revert back to a normal state when the event resets?")] + public bool AbandonOnReset { get; set; } + private bool isFinished = false; @@ -81,7 +84,7 @@ namespace Barotrauma public override void Reset() { - if (affectedNpcs != null) + if (affectedNpcs != null && AbandonOnReset) { foreach (var npc in affectedNpcs) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/GodModeAction.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/GodModeAction.cs index f029d9b1f..f7d2dd681 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/GodModeAction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/GodModeAction.cs @@ -1,4 +1,4 @@ -namespace Barotrauma +namespace Barotrauma { /// /// Makes a specific character invulnerable to damage and unable to die. @@ -36,13 +36,21 @@ namespace Barotrauma { if (target != null && target is Character character) { - if (UpdateAfflictions) + if (Enabled) { - character.CharacterHealth.Unkillable = Enabled; + if (UpdateAfflictions) + { + character.CharacterHealth.Unkillable = true; + } + else + { + character.GodMode = true; + } } else { - character.GodMode = Enabled; + character.CharacterHealth.Unkillable = false; + character.GodMode = false; } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCWaitAction.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCWaitAction.cs index 487e06ca9..93e37a32e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCWaitAction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCWaitAction.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; @@ -14,7 +14,10 @@ namespace Barotrauma [Serialize(true, IsPropertySaveable.Yes, description: "Should the NPC start or stop waiting?")] public bool Wait { get; set; } - + + [Serialize(true, IsPropertySaveable.Yes, description: "The event actions reset when a GoTo action makes the event jump to a different point. Should the NPC stop waiting when the event resets?")] + public bool AbandonOnReset { get; set; } + [Serialize(AIObjectiveManager.MaxObjectivePriority, IsPropertySaveable.Yes, description: "AI priority for the action. Uses 100 by default, which is the absolute maximum for any objectives, " + "meaning nothing can be prioritized over it, including the emergency objectives, such as find safety and combat." + "Setting the priority to 70 would function like a regular order, but with the highest priority." + @@ -76,7 +79,7 @@ namespace Barotrauma public override void Reset() { - if (affectedNpcs != null) + if (affectedNpcs != null && AbandonOnReset) { foreach (var npc in affectedNpcs) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/SpawnAction.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/SpawnAction.cs index aead92a1d..4974504fc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/SpawnAction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/SpawnAction.cs @@ -367,7 +367,7 @@ namespace Barotrauma SpawnType? spawnPointType = null; if (!ignoreSpawnPointType) { spawnPointType = SpawnPointType; } - return GetSpawnPos(SpawnLocation, spawnPointType, targetModuleTags, SpawnPointTag.ToEnumerable(), requireTaggedSpawnPoint: RequireSpawnPointTag, allowInPlayerView: AllowInPlayerView); + return GetSpawnPos(SpawnLocation, spawnPointType, targetModuleTags, SpawnPointTag.IsEmpty ? null : SpawnPointTag.ToEnumerable(), requireTaggedSpawnPoint: RequireSpawnPointTag, allowInPlayerView: AllowInPlayerView); } private static bool IsValidSubmarineType(SpawnLocationType spawnLocation, Submarine submarine) @@ -422,12 +422,32 @@ namespace Barotrauma } if (spawnpointTags != null && spawnpointTags.Any()) { - var spawnPoints = potentialSpawnPoints.Where(wp => spawnpointTags.Any(tag => wp.Tags.Contains(tag) && wp.ConnectedDoor == null && wp.IsTraversable)); - if (requireTaggedSpawnPoint || spawnPoints.Any()) + var spawnPointsWithTag = potentialSpawnPoints.Where(wp => spawnpointTags.Any(tag => wp.Tags.Contains(tag) && wp.ConnectedDoor == null && wp.IsTraversable)); + if (requireTaggedSpawnPoint || spawnPointsWithTag.Any()) { - potentialSpawnPoints = spawnPoints.ToList(); + potentialSpawnPoints = spawnPointsWithTag.ToList(); + } + else + { + //no spawnpoints with the tag we want -> choose something with no tags + TryGetSpawnPointsWithNoTag(); } } + else + { + //if no tags are specified, prefer a spawnpoint with no tags, i.e. prefer a "generic" spawnpoint instead of some special one like a jail spawnpoint + TryGetSpawnPointsWithNoTag(); + } + + void TryGetSpawnPointsWithNoTag() + { + var spawnPointsWithNoTag = potentialSpawnPoints.Where(wp => wp.Tags.None()); + if (spawnPointsWithNoTag.Any()) + { + potentialSpawnPoints = spawnPointsWithNoTag.ToList(); + } + } + if (potentialSpawnPoints.None()) { if (requireTaggedSpawnPoint && spawnpointTags != null && spawnpointTags.Any()) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs index 842110b0f..01009f1db 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs @@ -777,7 +777,15 @@ namespace Barotrauma var locationType = location.GetLocationTypeToDisplay(); bool includeGenericEvents = level.Type == LevelData.LevelType.LocationConnection || !locationType.IgnoreGenericEvents; if (includeGenericEvents && eventSet.LocationTypeIdentifiers == null) { return true; } - return eventSet.LocationTypeIdentifiers != null && eventSet.LocationTypeIdentifiers.Any(identifier => identifier == locationType.Identifier); + if (eventSet.LocationTypeIdentifiers == null) { return false; } + + // EventLocationType is used to have the event set consider the location id as something else, for example "city" to get events that go to city locations + bool hasMatchingEventLocationId = !locationType.EventLocationType.IsEmpty && + eventSet.LocationTypeIdentifiers.Contains(locationType.EventLocationType); + + bool hasMatchingLocationId = eventSet.LocationTypeIdentifiers.Contains(locationType.Identifier); + + return hasMatchingEventLocationId || hasMatchingLocationId; } private Location GetEventLocation() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs index 67c18ce0d..09703cf05 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs @@ -156,9 +156,34 @@ namespace Barotrauma } } } + + private int previousKillTargetsRemaining = -1; + + private void TrackKillTargetCount() + { + if (requireKill.Count == 0) { return; } + + if (previousKillTargetsRemaining == -1) + { + previousKillTargetsRemaining = requireKill.Count(); + } + + int killTargetsRemaining = requireKill.Count(c => !c.Removed && !c.IsDead && !(c.LockHands && c.Submarine == Submarine.MainSub)); + // at least one of the targets have been eliminated + if (killTargetsRemaining < previousKillTargetsRemaining) + { +#if CLIENT + SteamTimelineManager.OnOutpostTargetEliminated(this); +#endif + } + + previousKillTargetsRemaining = killTargetsRemaining; + } protected override void UpdateMissionSpecific(float deltaTime) { + TrackKillTargetCount(); + if (State != HostagesKilledState) { if (requireRescue.Any(r => r.Removed || r.IsDead)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/EscortMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/EscortMission.cs index 15d69334c..473006b60 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/EscortMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/EscortMission.cs @@ -9,14 +9,30 @@ namespace Barotrauma { partial class EscortMission : Mission { - private readonly ContentXElement itemConfig; + private readonly ContentXElement terroristItemConfig; private readonly Dictionary> characterStatusEffects = new Dictionary>(); + /// + /// Number of escorted characters by default. + /// private readonly int baseEscortedCharacters; + /// + /// A scaling factor for the number of escorted characters, relative to the recommended crew size of the sub. The total amount of escorted characters is calculated as + /// baseEscortedCharacters + scalingEscortedCharacters * (RecommendedCrewSizeMin + RecommendedCrewSizeMax) / 2 + /// private readonly float scalingEscortedCharacters; + /// + /// The probability for the escorted characters to be "terrorists" (turning them hostile when the sub has progressed enough in the level). + /// A value of 0.5 would mean about half of the characters are terrorist, 1 would mean they all are. There's 20% of randomness applied to the value to make it less predictable. + /// private readonly float terroristChance; + /// + /// Dialog tag the terrorists use in their dialog when they become hostile. + /// + private readonly string terroristAnnounceDialogTag; + private int calculatedReward; private Submarine missionSub; @@ -26,7 +42,6 @@ namespace Barotrauma private bool terroristsShouldAct = false; private float terroristDistanceSquared; private const string TerroristTeamChangeIdentifier = "terrorist"; - private readonly string terroristAnnounceDialogTag = string.Empty; public EscortMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub) @@ -35,8 +50,10 @@ namespace Barotrauma baseEscortedCharacters = prefab.ConfigElement.GetAttributeInt("baseescortedcharacters", 1); scalingEscortedCharacters = prefab.ConfigElement.GetAttributeFloat("scalingescortedcharacters", 0); terroristChance = prefab.ConfigElement.GetAttributeFloat("terroristchance", 0); - itemConfig = prefab.ConfigElement.GetChildElement("TerroristItems"); - terroristAnnounceDialogTag = prefab.ConfigElement.GetAttributeString("terroristannouncedialogtag", string.Empty); + terroristItemConfig = prefab.ConfigElement.GetChildElement("TerroristItems"); + terroristAnnounceDialogTag = + prefab.ConfigElement.GetAttributeString("dialogterroristannounce", + prefab.ConfigElement.GetAttributeString("terroristAnnounceDialogTag", string.Empty)); CalculateReward(); } @@ -94,35 +111,29 @@ namespace Barotrauma randSync = Rand.RandSync.Unsynced; } - List humanPrefabsToSpawn = new List(); + List<(HumanPrefab humanPrefab, List statusEffects)> humanPrefabsToSpawn = new List<(HumanPrefab humanPrefab, List statusEffects)>(); foreach (ContentXElement characterElement in characterConfig.Elements()) { int count = CalculateScalingEscortedCharacterCount(inMission: true); var humanPrefab = GetHumanPrefabFromElement(characterElement); for (int i = 0; i < count; i++) { - humanPrefabsToSpawn.Add(humanPrefab); - } - foreach (var element in characterElement.Elements()) - { - if (element.NameAsIdentifier() == "statuseffect") + List characterStatusEffects = new List(); + foreach (var element in characterElement.Elements()) { - var newEffect = StatusEffect.Load(element, parentDebugName: Prefab.Name.Value); - if (newEffect == null) { continue; } - if (!characterStatusEffects.ContainsKey(humanPrefab)) + if (element.NameAsIdentifier() == "statuseffect") { - characterStatusEffects[humanPrefab] = new List { newEffect }; + var newEffect = StatusEffect.Load(element, parentDebugName: Prefab.Name.Value); + if (newEffect == null) { continue; } + characterStatusEffects.Add(newEffect); } - else - { - characterStatusEffects[humanPrefab].Add(newEffect); - } } + humanPrefabsToSpawn.Add((humanPrefab, characterStatusEffects)); } } //if any of the escortees have a job defined, try to use a spawnpoint designated for that job - foreach (var humanPrefab in humanPrefabsToSpawn) + foreach ((var humanPrefab, var statusEffectList) in humanPrefabsToSpawn) { if (humanPrefab == null || humanPrefab.Job.IsEmpty || humanPrefab.Job == "any") { continue; } var jobPrefab = humanPrefab.GetJobPrefab(randSync); @@ -136,23 +147,19 @@ namespace Barotrauma } } } - foreach (var humanPrefab in humanPrefabsToSpawn) + foreach ((var humanPrefab, var statusEffectList) in humanPrefabsToSpawn) { Character spawnedCharacter = CreateHuman(humanPrefab, characters, characterItems, Submarine.MainSub, CharacterTeamType.FriendlyNPC, explicitStayInHullPos, humanPrefabRandSync: randSync); if (spawnedCharacter.AIController is HumanAIController humanAI) { humanAI.InitMentalStateManager(); } - if (characterStatusEffects.TryGetValue(humanPrefab, out var statusEffectList)) + foreach (var statusEffect in statusEffectList) { - foreach (var statusEffect in statusEffectList) - { - statusEffect.Apply(statusEffect.type, 1.0f, spawnedCharacter, spawnedCharacter); - } - } + statusEffect.Apply(statusEffect.type, 1.0f, spawnedCharacter, spawnedCharacter); + } } - if (terroristChance > 0f) { int terroristCount = (int)Math.Ceiling(terroristChance * Rand.Range(0.8f, 1.2f) * characters.Count); @@ -256,13 +263,23 @@ namespace Barotrauma character.TryAddNewTeamChange(TerroristTeamChangeIdentifier, new ActiveTeamChange(CharacterTeamType.None, ActiveTeamChange.TeamChangePriorities.Willful, aggressiveBehavior: true)); if (!string.IsNullOrEmpty(terroristAnnounceDialogTag)) { - character.Speak(TextManager.Get("dialogterroristannounce").Value, null, Rand.Range(0.5f, 3f)); + character.Speak(TextManager.Get(terroristAnnounceDialogTag).Value, null, Rand.Range(0.5f, 3f)); } - ContentXElement randomElement = itemConfig.Elements().GetRandomUnsynced(e => e.GetAttributeFloat(0f, "mindifficulty") <= Level.Loaded.Difficulty); - if (randomElement != null) + foreach (var itemElement in terroristItemConfig.Elements()) { - HumanPrefab.InitializeItem(character, randomElement, character.Submarine, humanPrefab: null, createNetworkEvents: true); + float levelDifficulty = Level.Loaded?.Difficulty ?? 0.0f; + var selectedItemElement = itemElement; + if (itemElement.NameAsIdentifier() == "chooserandom".ToIdentifier()) + { + selectedItemElement = itemElement.Elements().GetRandomUnsynced(e => e.GetAttributeFloat(0f, "mindifficulty") <= levelDifficulty); + } + if (selectedItemElement != null) + { + if (levelDifficulty < selectedItemElement.GetAttributeFloat(0f, "mindifficulty")) { continue; } + HumanPrefab.InitializeItem(character, selectedItemElement, character.Submarine, humanPrefab: null, createNetworkEvents: true); + } } + } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs index e4edf96d7..757561538 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs @@ -26,6 +26,7 @@ namespace Barotrauma { if (state != value) { + int previousState = state; state = value; TryTriggerEvents(state); #if SERVER @@ -38,6 +39,7 @@ namespace Barotrauma #endif ShowMessage(State); OnMissionStateChanged?.Invoke(this); + MissionStateChanged(previousState); } } } @@ -198,6 +200,11 @@ namespace Barotrauma Messages = messages.ToImmutableArray(); characterConfig = prefab.ConfigElement.GetChildElement("Characters"); + if (prefab.ConfigElement.GetChildElements("Characters").Count() > 1) + { + DebugConsole.AddWarning($"Error in mission {Prefab.Identifier}: multiple elements found. Only the first one will be used.", + contentPackage: prefab.ContentPackage); + } } public LocalizedString ReplaceVariablesInMissionMessage(LocalizedString message, Submarine sub, bool replaceReward = true) @@ -214,6 +221,8 @@ namespace Barotrauma } return message; } + + protected virtual void MissionStateChanged(int previousState) {} public virtual void SetLevel(LevelData level) { } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs index b692d0725..25829bd93 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs @@ -109,6 +109,8 @@ namespace Barotrauma public readonly bool AllowRetry; + public readonly bool ShowSonarLabels; + public readonly bool ShowInMenus, ShowStartMessage; public readonly bool IsSideObjective; @@ -219,11 +221,12 @@ namespace Barotrauma } } - Reward = element.GetAttributeInt("reward", 1); - ExperienceMultiplier = element.GetAttributeFloat("experiencemultiplier", 1.0f); - AllowRetry = element.GetAttributeBool("allowretry", false); - ShowInMenus = element.GetAttributeBool("showinmenus", true); - ShowStartMessage = element.GetAttributeBool("showstartmessage", true); + Reward = element.GetAttributeInt(nameof(Reward), 1); + ExperienceMultiplier = element.GetAttributeFloat(nameof(ExperienceMultiplier), 1.0f); + AllowRetry = element.GetAttributeBool(nameof(AllowRetry), false); + ShowSonarLabels = element.GetAttributeBool(nameof(ShowSonarLabels), true); + ShowInMenus = element.GetAttributeBool(nameof(ShowInMenus), true); + ShowStartMessage = element.GetAttributeBool(nameof(ShowStartMessage), true); IsSideObjective = element.GetAttributeBool("sideobjective", false); RequireWreck = element.GetAttributeBool(nameof(RequireWreck), false); @@ -237,11 +240,12 @@ namespace Barotrauma BlockLocationTypeChanges = element.GetAttributeBool(nameof(BlockLocationTypeChanges), false); RequiredLocationFaction = element.GetAttributeIdentifier(nameof(RequiredLocationFaction), Identifier.Empty); - Commonness = element.GetAttributeInt("commonness", 1); - AllowOtherMissionsInLevel = element.GetAttributeBool("allowothermissionsinlevel", true); + Commonness = element.GetAttributeInt(nameof(Commonness), 1); + AllowOtherMissionsInLevel = element.GetAttributeBool(nameof(AllowOtherMissionsInLevel), true); + if (element.GetAttribute("difficulty") != null) { - int difficulty = element.GetAttributeInt("difficulty", MinDifficulty); + int difficulty = element.GetAttributeInt(nameof(Difficulty), MinDifficulty); Difficulty = Math.Clamp(difficulty, MinDifficulty, MaxDifficulty); } MinLevelDifficulty = element.GetAttributeInt(nameof(MinLevelDifficulty), MinLevelDifficulty); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MonsterMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MonsterMission.cs index 4b3e1a2b8..5037081ce 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MonsterMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MonsterMission.cs @@ -173,6 +173,17 @@ namespace Barotrauma } } + protected override void MissionStateChanged(int previousState) + { + // state of 1+ here means the mission is completed + if (previousState == 0 && State >= 1) + { +#if CLIENT + SteamTimelineManager.OnMonsterMissionTargetsKilled(this); +#endif + } + } + protected override void UpdateMissionSpecific(float deltaTime) { switch (State) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs index 3f958c88d..7f2dbba0a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs @@ -92,13 +92,38 @@ namespace Barotrauma set { if (value == state) { return; } + bool wasRetrieved = Retrieved; state = value; #if SERVER GameMain.Server?.UpdateMissionState(mission); #endif + if (!wasRetrieved && Retrieved) + { + OnTargetRetrieved(); + } + else if (state == RetrievalState.PickedUp) + { + OnTargetPickedUp(); + } } } + private void OnTargetRetrieved() + { + if (Item == null) { return; } +#if CLIENT + SteamTimelineManager.OnMissionTargetRetrieved(Item, mission); +#endif + } + + private void OnTargetPickedUp() + { + if (Item == null) { return; } +#if CLIENT + SteamTimelineManager.OnMissionTargetPickedUp(Item, mission); +#endif + } + public bool Interacted; private readonly SalvageMission mission; @@ -469,13 +494,13 @@ namespace Barotrauma target.Item.ExternalHighlight = true; #endif target.Item.UpdateTransform(); - if (target.Item.CurrentHull == null) + if (target.Item.CurrentHull == null && target.Item.body != null) { //prevent the body from moving if it spawned outside the hulls (we don't want it e.g. falling to the bottom of a cave or into the abyss) target.Item.body.FarseerBody.BodyType = BodyType.Kinematic; } } - else if (target.RequiredRetrievalState == Target.RetrievalState.Interact) + if (target.RequiredRetrievalState == Target.RetrievalState.Interact) { target.Item.OnInteract += () => { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/ScanMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/ScanMission.cs index 288e37b92..e388b2908 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/ScanMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/ScanMission.cs @@ -168,6 +168,17 @@ namespace Barotrauma } } + protected override void MissionStateChanged(int previousState) + { + // detect successful scanned targets increasing after scan is completed + if (previousState < State) + { +#if CLIENT + SteamTimelineManager.OnScanSuccessful(this); +#endif + } + } + private void GetScanners() { foreach (var startingItem in startingItems) diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs index a24303b83..5037e693d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs @@ -206,7 +206,9 @@ namespace Barotrauma LocalizedString reputationName = GetReputationName(normalizedValue); LocalizedString formattedReputation = TextManager.GetWithVariables("reputationformat", ("[reputationname]", reputationName), - ("[reputationvalue]", ((int)Math.Round(value)).ToString())); + //simply cast to float (dropping the decimals) + //we don't want any rounding here, otherwise it might look like you have enough rep to reach some threshold when you're actually some fraction off + ("[reputationvalue]", ((int)value).ToString())); if (addColorTags) { formattedReputation = $"‖color:{XMLExtensions.ToStringHex(GetReputationColor(normalizedValue))}‖{formattedReputation}‖end‖"; diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs index ff5a4ade7..a64925b36 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs @@ -160,10 +160,34 @@ namespace Barotrauma protected set; } - public bool PurchasedLostShuttlesInLatestSave, PurchasedHullRepairsInLatestSave, PurchasedItemRepairsInLatestSave; + /// + /// Has recovery of lost shuttles been purchased in the latest save? Determines whether the shuttles should be recovered when loading into the round. + /// + public bool PurchasedLostShuttlesInLatestSave; + /// + /// Have hull repairs been purchased in the latest save? Determines whether the walls will be repaired when loading into the round. + /// + public bool PurchasedHullRepairsInLatestSave; + + /// + /// Has repairing damaged items been purchased in the latest save? Determines whether the items will be repaired when loading into the round. + /// + public bool PurchasedItemRepairsInLatestSave; + + /// + /// Have hull repairs been purchased on the current round? + /// public virtual bool PurchasedHullRepairs { get; set; } + + /// + /// Has recovery of lost shuttles been purchased on the current round? + /// public virtual bool PurchasedLostShuttles { get; set; } + + /// + /// Has repairing damaged items been purchased on the current round? + /// public virtual bool PurchasedItemRepairs { get; set; } public bool DivingSuitWarningShown; @@ -441,6 +465,16 @@ namespace Barotrauma currentLocation.DeselectMission(mission); } } + + foreach (var mission in currentLocation.AvailableMissions) + { + //if the mission isn't shown in menus, it cannot be selected by the player -> must be something that is supposed to be automatically selected + if (!mission.Prefab.ShowInMenus) + { + currentLocation.SelectMission(mission); + } + } + if (levelData.HasBeaconStation && !levelData.IsBeaconActive && Missions.None(m => m.Prefab.Type == Tags.MissionTypeBeacon)) { var beaconMissionPrefabs = MissionPrefab.Prefabs.Where(m => m.IsSideObjective && m.Type == Tags.MissionTypeBeacon); @@ -1632,6 +1666,70 @@ namespace Barotrauma parentElement?.Add(petsElement); } + /// + /// Loads the parts of a campaign save that are the same between single player and multiplayer saves. + /// + public void LoadSaveSharedSingleAndMultiplayer(XElement element) + { + PurchasedLostShuttlesInLatestSave = element.GetAttributeBool("purchasedlostshuttles", false); + PurchasedHullRepairsInLatestSave = element.GetAttributeBool("purchasedhullrepairs", false); + PurchasedItemRepairsInLatestSave = element.GetAttributeBool("purchaseditemrepairs", false); + CheatsEnabled = element.GetAttributeBool("cheatsenabled", false); + if (CheatsEnabled) + { + DebugConsole.CheatsEnabled = true; + if (!AchievementManager.CheatsEnabled) + { + AchievementManager.CheatsEnabled = true; +#if CLIENT + new GUIMessageBox("Cheats enabled", "Cheat commands have been enabled on the server. You will not receive achievements until you restart the game."); +#else + DebugConsole.NewMessage("Cheat commands have been enabled.", Color.Red); +#endif + } + } + + //backwards compatibility for saves made prior to the addition of personal wallets + int oldMoney = element.GetAttributeInt("money", 0); + if (oldMoney > 0) + { + Bank = new Wallet(Option.None()) + { + Balance = oldMoney + }; + } + + + foreach (var subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLowerInvariant()) + { + case "cargo": + CargoManager.LoadPurchasedItems(subElement); + break; + case "pendingupgrades": //backwards compatibility + case "upgrademanager": + UpgradeManager = new UpgradeManager(this, subElement, isSingleplayer: IsSinglePlayer); + break; + case "pets": + petsElement = subElement; + break; + case Wallet.LowerCaseSaveElementName: + Bank = new Wallet(Option.None(), subElement); + break; + case "stats": + LoadStats(subElement); + break; + case "eventmanager": + GameMain.GameSession.EventManager.Load(subElement); + break; + case "unlockedrecipe": + GameMain.GameSession.UnlockRecipe(subElement.GetAttributeIdentifier("identifier", Identifier.Empty), showNotifications: false); + break; + } + } + } + public void LoadPets() { if (petsElement != null) diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs index 80e30c2cf..bff64b674 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -161,23 +161,7 @@ namespace Barotrauma /// private void Load(XElement element) { - PurchasedLostShuttlesInLatestSave = element.GetAttributeBool("purchasedlostshuttles", false); - PurchasedHullRepairsInLatestSave = element.GetAttributeBool("purchasedhullrepairs", false); - PurchasedItemRepairsInLatestSave = element.GetAttributeBool("purchaseditemrepairs", false); - CheatsEnabled = element.GetAttributeBool("cheatsenabled", false); - if (CheatsEnabled) - { - DebugConsole.CheatsEnabled = true; - if (!AchievementManager.CheatsEnabled) - { - AchievementManager.CheatsEnabled = true; -#if CLIENT - new GUIMessageBox("Cheats enabled", "Cheat commands have been enabled on the server. You will not receive achievements until you restart the game."); -#else - DebugConsole.NewMessage("Cheat commands have been enabled.", Color.Red); -#endif - } - } + LoadSaveSharedSingleAndMultiplayer(element); foreach (var subElement in element.Elements()) { @@ -215,30 +199,11 @@ namespace Barotrauma } } break; - case "upgrademanager": - case "pendingupgrades": - UpgradeManager = new UpgradeManager(this, subElement, isSingleplayer: false); - break; case "bots" when GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer: CrewManager.HasBots = subElement.GetAttributeBool("hasbots", false); CrewManager.AddCharacterElements(subElement); ActiveOrdersElement = subElement.GetChildElement("activeorders"); break; - case "cargo": - CargoManager?.LoadPurchasedItems(subElement); - break; - case "pets": - petsElement = subElement; - break; - case "stats": - LoadStats(subElement); - break; - case "eventmanager": - GameMain.GameSession.EventManager.Load(subElement); - break; - case Wallet.LowerCaseSaveElementName: - Bank = new Wallet(Option.None(), subElement); - break; #if SERVER case "traitormanager": GameMain.Server?.TraitorManager?.Load(subElement); @@ -253,15 +218,6 @@ namespace Barotrauma } } - int oldMoney = element.GetAttributeInt("money", 0); - if (oldMoney > 0) - { - Bank = new Wallet(Option.None()) - { - Balance = oldMoney - }; - } - UpgradeManager ??= new UpgradeManager(this); #if SERVER diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs index 703d9323f..f9fb073bd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs @@ -170,6 +170,9 @@ namespace Barotrauma public Submarine? Submarine { get; set; } + private readonly HashSet unlockedRecipes = new HashSet(); + public IEnumerable UnlockedRecipes => unlockedRecipes; + public CampaignDataPath DataPath { get; set; } public bool TraitorsEnabled => @@ -275,6 +278,10 @@ namespace Barotrauma } } break; + //NOTE: if you're adding something that's supposed to load something that's persistent in a campaign, + //this is probably not the correct place! This is where the GameSession itself is initialized, + //and if you let's say quit to the server lobby and reload, this method won't be called again. + //You should probably add it to CampaignMode.LoadSaveSharedSingleAndMultiplayer } } } @@ -385,7 +392,7 @@ namespace Barotrauma var dummyLocations = new Location[2]; for (int i = 0; i < 2; i++) { - dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), null, rand, requireOutpost: true, forceLocationType); + dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), zone: null, biomeId: null, rand, requireOutpost: true, forceLocationType); } return dummyLocations; } @@ -401,6 +408,7 @@ namespace Barotrauma public void LoadPreviousSave() { + AchievementManager.OnRoundEnded(this, roundInterrupted: true); Submarine.Unload(); SaveUtil.LoadGame(DataPath); } @@ -1501,6 +1509,25 @@ namespace Barotrauma #endif } + public void UnlockRecipe(Identifier identifier, bool showNotifications) + { + if (unlockedRecipes.Add(identifier)) + { +#if CLIENT + if (showNotifications) + { + foreach (var character in GetSessionCrewCharacters(CharacterType.Both)) + { + LocalizedString recipeName = TextManager.Get($"entityname.{identifier}").Fallback(identifier.Value); + character.AddMessage(TextManager.GetWithVariable("recipeunlockednotification", "[name]", recipeName).Value, GUIStyle.Yellow, playSound: true); + } + } +#else + GameMain.Server.UnlockRecipe(identifier); +#endif + } + } + public static bool IsCompatibleWithEnabledContentPackages(IList contentPackageNames, out LocalizedString errorMsg) { errorMsg = ""; @@ -1612,9 +1639,11 @@ namespace Barotrauma ownedSubsElement.Add(new XElement("sub", new XAttribute("name", ownedSub.Name))); } } + if (Map != null) { rootElement.Add(new XAttribute("mapseed", Map.Seed)); } rootElement.Add(new XAttribute("selectedcontentpackagenames", string.Join("|", ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).Select(cp => cp.Name.Replace("|", @"\|"))))); + XElement permadeathsElement = new XElement("permadeaths"); foreach (var kvp in permadeathsPerAccount) diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/UpgradeManager.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/UpgradeManager.cs index 223ff5c0e..8d3067f55 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/UpgradeManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/UpgradeManager.cs @@ -169,12 +169,14 @@ namespace Barotrauma /// Purchased upgrades are temporarily stored in and they are applied /// after the next round starts similarly how items are spawned in the stowage room after the round starts. /// - public void PurchaseUpgrade(UpgradePrefab prefab, UpgradeCategory category, bool force = false, Client? client = null) + public bool TryPurchaseUpgrade(UpgradePrefab prefab, UpgradeCategory category, bool force = false, Client? client = null) { + if (!HasPermissionToManageUpgrades(client)) { return false; } + if (!CanUpgradeSub()) { DebugConsole.ThrowError("Cannot upgrade when switching to another submarine."); - return; + return false; } int price = prefab.Price.GetBuyPrice(prefab, GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation); @@ -185,7 +187,7 @@ namespace Barotrauma if (currentLevel + 1 > maxLevel) { DebugConsole.ThrowError($"Tried to purchase \"{prefab.Name}\" over the max level! ({newLevel} > {maxLevel}). The transaction has been cancelled."); - return; + return false; } bool TryTakeResources(Character character) @@ -203,17 +205,17 @@ namespace Barotrauma switch (GameMain.NetworkMember) { case null when Character.Controlled is { } controlled: // singleplayer - if (!TryTakeResources(controlled)) { return; } + if (!TryTakeResources(controlled)) { return false; } break; case { IsClient: true }: - if (!prefab.HasResourcesToUpgrade(Character.Controlled, newLevel)) { return; } + if (!prefab.HasResourcesToUpgrade(Character.Controlled, newLevel)) { return false; } break; case { IsServer: true } when client?.Character is { } character: - if (!TryTakeResources(character)) { return; } + if (!TryTakeResources(character)) { return false; } break; default: DebugConsole.ThrowError($"Tried to purchase \"{prefab.Name}\" without a player."); - return; + return false; } } @@ -270,11 +272,15 @@ namespace Barotrauma PurchasedUpgrades.Add(new PurchasedUpgrade(prefab, category)); #endif OnUpgradesChanged?.Invoke(this); + + return true; } else { DebugConsole.ThrowError("Tried to purchase an upgrade with insufficient funds, the transaction has not been completed.\n" + $"Upgrade: {prefab.Name}, Cost: {price}, Have: {Campaign.GetWallet(client).Balance}"); + + return false; } } @@ -297,6 +303,8 @@ namespace Barotrauma /// public void PurchaseItemSwap(Item itemToRemove, ItemPrefab itemToInstall, bool isNetworkMessage = false, Client? client = null) { + if (!HasPermissionToManageUpgrades(client)) { return; } + if (!CanUpgradeSub()) { DebugConsole.ThrowError("Cannot swap items when switching to another submarine."); @@ -398,8 +406,10 @@ namespace Barotrauma /// /// Cancels the currently pending item swap, or uninstalls the item if there's no swap pending /// - public void CancelItemSwap(Item itemToRemove, bool force = false) + public void CancelItemSwap(Item itemToRemove, bool force = false, Client? client = null) { + if (!HasPermissionToManageUpgrades(client)) { return; } + if (!CanUpgradeSub()) { DebugConsole.ThrowError("Cannot swap items when switching to another submarine."); @@ -757,6 +767,18 @@ namespace Barotrauma Campaign.PendingSubmarineSwitch.Name == Submarine.MainSub.Info.Name; } + public bool HasPermissionToManageUpgrades(Client? client = null) + { + if (!GameMain.IsMultiplayer) { return true; } + +#if SERVER + if (client is null) { return false; } + return CampaignMode.AllowedToManageCampaign(client, ClientPermissions.ManageCampaign); +#elif CLIENT + return CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageCampaign); +#endif + } + public void Save(XElement? parent) { if (parent == null) { return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs index 6675da68d..15eb5f958 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs @@ -220,6 +220,22 @@ namespace Barotrauma return false; } + + /// + /// Can the item be put in the inventory in a slot of the specified type (i.e. is there a suitable free slot or a stack the item can be put in). + /// + public bool CanBePut(Item item, InvSlotType slotType) + { + for (int i = 0; i < capacity; i++) + { + if (slotType.HasFlag(SlotTypes[i])) + { + if (CanBePutInSlot(item, i)) { return true; } + } + } + return false; + } + public override bool CanBePutInSlot(Item item, int i, bool ignoreCondition = false) { return diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs index 402634f42..00e85a1c2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs @@ -179,7 +179,8 @@ namespace Barotrauma.Items.Components var prevDockingTarget = DockingTarget; Undock(applyEffects: false); Dock(prevDockingTarget); - Lock(isNetworkMessage: true, applyEffects: false); + //don't move subs at this point, it will mess up the placement logic when flipping multi-part subs + Lock(isNetworkMessage: true, applyEffects: false, moveSubs: false); } } @@ -281,7 +282,7 @@ namespace Barotrauma.Items.Components OnDocked = null; } - public void Lock(bool isNetworkMessage, bool applyEffects = true) + public void Lock(bool isNetworkMessage, bool applyEffects = true, bool moveSubs = true) { #if CLIENT if (GameMain.Client != null && !isNetworkMessage) { return; } @@ -312,16 +313,19 @@ namespace Barotrauma.Items.Components ApplyStatusEffects(ActionType.OnUse, 1.0f); } - Vector2 jointDiff = joint.WorldAnchorB - joint.WorldAnchorA; - if (item.Submarine.PhysicsBody.Mass < DockingTarget.item.Submarine.PhysicsBody.Mass || - DockingTarget.item.Submarine.Info.IsOutpost) + if (moveSubs) { - item.Submarine.SubBody.SetPosition(item.Submarine.SubBody.Position + ConvertUnits.ToDisplayUnits(jointDiff)); - } - else if (DockingTarget.item.Submarine.PhysicsBody.Mass < item.Submarine.PhysicsBody.Mass || - item.Submarine.Info.IsOutpost) - { - DockingTarget.item.Submarine.SubBody.SetPosition(DockingTarget.item.Submarine.SubBody.Position - ConvertUnits.ToDisplayUnits(jointDiff)); + Vector2 jointDiff = joint.WorldAnchorB - joint.WorldAnchorA; + if (item.Submarine.PhysicsBody.Mass < DockingTarget.item.Submarine.PhysicsBody.Mass || + DockingTarget.item.Submarine.Info.IsOutpost) + { + item.Submarine.SubBody.SetPosition(item.Submarine.SubBody.Position + ConvertUnits.ToDisplayUnits(jointDiff)); + } + else if (DockingTarget.item.Submarine.PhysicsBody.Mass < item.Submarine.PhysicsBody.Mass || + item.Submarine.Info.IsOutpost) + { + DockingTarget.item.Submarine.SubBody.SetPosition(DockingTarget.item.Submarine.SubBody.Position - ConvertUnits.ToDisplayUnits(jointDiff)); + } } ConnectWireBetweenPorts(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Growable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Growable.cs index 2d593d6a4..aafbdecd3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Growable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Growable.cs @@ -349,31 +349,31 @@ namespace Barotrauma.Items.Components // used for debugging where a vine failed to grow public readonly HashSet FailedRectangles = new HashSet(); - [Serialize(1f, IsPropertySaveable.Yes, "How fast the plant grows.")] + [Serialize(1f, IsPropertySaveable.Yes, "How fast the plant grows. Value of 1 means a vine attempts to grow every 10 seconds while 2 and 0.5 mean every 5 and 20 seconds respectively.")] public float GrowthSpeed { get; set; } - [Serialize(100f, IsPropertySaveable.Yes, "How long the plant can go without watering.")] - public float MaxHealth { get; set; } + [Serialize(100f, IsPropertySaveable.Yes, "How much water the plant can hold. Affects how long the plant can survive without water.")] + public float MaxWater { get; set; } - [Serialize(1f, IsPropertySaveable.Yes, "How much damage the plant takes while in water.")] - public float FloodTolerance { get; set; } + [Serialize(1f, IsPropertySaveable.Yes, "How much extra water the plant uses per second while it is submerged in a flooded hull.")] + public float ExtraWaterUsedPerSecondWhileFlooded { get; set; } - [Serialize(1f, IsPropertySaveable.Yes, "How much damage the plant takes while growing.")] - public float Hardiness { get; set; } + [Serialize(1f, IsPropertySaveable.Yes, "How much water the plant consumes passively per second.")] + public float WaterUsedPerSecond { get; set; } - [Serialize(0.01f, IsPropertySaveable.Yes, "How often a seed is produced.")] - public float SeedRate { get; set; } + [Serialize(0.01f, IsPropertySaveable.Yes, "Percentage chance of a seed item being produced on growth ticks (every 10 seconds without a multiplier). 0.01 means 1% chance. Not used in vanilla plants.")] + public float SeedSpawnChance { get; set; } - [Serialize(0.01f, IsPropertySaveable.Yes, "How often a product item is produced.")] - public float ProductRate { get; set; } + [Serialize(0.01f, IsPropertySaveable.Yes, "How often a product item is produced on growth ticks (every 10 seconds without a multiplier). 0.01 means 1% chance.")] + public float ProductSpawnChance { get; set; } - [Serialize(0.5f, IsPropertySaveable.Yes, "Probability of an attribute being randomly modified in a newly produced seed.")] + [Serialize(0.5f, IsPropertySaveable.Yes, "Completely unused property that was added on the first design pass but due to the first pass being too complex was never used and now it is used by mods so it cannot be removed.")] public float MutationProbability { get; set; } [Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes, "Color of the flowers.")] public Color FlowerTint { get; set; } - [Serialize(3, IsPropertySaveable.Yes, "Number of flowers drawn when fully grown")] + [Serialize(3, IsPropertySaveable.Yes, "Number of flowers drawn.")] public int FlowerQuantity { get; set; } [Serialize(0.25f, IsPropertySaveable.Yes, "Size of the flower sprites.")] @@ -403,9 +403,12 @@ namespace Barotrauma.Items.Components [Serialize("1,1,1,1", IsPropertySaveable.Yes, "Probability for the plant to grow in a direction.")] public Vector4 GrowthWeights { get; set; } - [Serialize(0.0f, IsPropertySaveable.Yes, "How much damage is taken from fires.")] + [Serialize(0.0f, IsPropertySaveable.Yes, "How much water is lost due to fires every 10 seconds.")] public float FireVulnerability { get; set; } + [Serialize("0.0, 0.0", IsPropertySaveable.Yes, "Modifier to the percentage of product and seed items produced before the plant is fully grown based on how many vines have been grown. 0 would mean no products or seeds are produced while 0.5 would mean half of the normal amount.")] + public Vector2 LinearProductAndSeedMultiplierBeforeFullyGrown { get; set; } + private const float increasedDeathSpeed = 10f; private bool accelerateDeath; private float health; @@ -417,7 +420,7 @@ namespace Barotrauma.Items.Components public float Health { get => health; - set => health = Math.Clamp(value, 0, MaxHealth); + set => health = Math.Clamp(value, 0, MaxWater); } public bool Decayed { get; set; } @@ -441,7 +444,14 @@ namespace Barotrauma.Items.Components { SerializableProperty.DeserializeProperties(this, element); - Health = MaxHealth; + // backwards compatibility + MaxWater = element.GetAttributeFloat("maxhealth", MaxWater); + WaterUsedPerSecond = element.GetAttributeFloat("hardiness", WaterUsedPerSecond); + ExtraWaterUsedPerSecondWhileFlooded = element.GetAttributeFloat("floodtolerance", ExtraWaterUsedPerSecondWhileFlooded); + ProductSpawnChance = element.GetAttributeFloat("productrate", ProductSpawnChance); + SeedSpawnChance = element.GetAttributeFloat("seedrate", SeedSpawnChance); + + Health = MaxWater; if (element.HasElements) { @@ -492,10 +502,7 @@ namespace Barotrauma.Items.Components { if (Decayed) { return; } - if (FullyGrown) - { - TryGenerateProduct(planter, slot); - } + TryGenerateProduct(planter, slot); if (Health > 0) { @@ -504,11 +511,11 @@ namespace Barotrauma.Items.Components // fertilizer makes the plant tick faster, compensate by halving water requirement float multipler = planter.Fertilizer > 0 ? 0.5f : 1f; - Health -= (accelerateDeath ? Hardiness * increasedDeathSpeed : Hardiness) * multipler; + Health -= (accelerateDeath ? WaterUsedPerSecond * increasedDeathSpeed : WaterUsedPerSecond) * multipler; if (planter.Item.InWater) { - Health -= FloodTolerance * multipler; + Health -= ExtraWaterUsedPerSecondWhileFlooded * multipler; } #if SERVER if (FullyGrown) @@ -535,7 +542,7 @@ namespace Barotrauma.Items.Components private void UpdateBranchHealth() { - Color healthColor = Color.White * (1.0f - Health / MaxHealth); + Color healthColor = Color.White * (1.0f - Health / MaxWater); foreach (VineTile vine in Vines) { vine.HealthColor = healthColor; @@ -546,11 +553,24 @@ namespace Barotrauma.Items.Components { productDelay++; if (productDelay <= maxProductDelay) { return; } - productDelay = 0; - bool spawnProduct = Rand.Range(0f, 1f, Rand.RandSync.Unsynced) < ProductRate, - spawnSeed = Rand.Range(0f, 1f, Rand.RandSync.Unsynced) < SeedRate; + float spawnChanceMultiplier = 1f; + + if (!FullyGrown) + { + if (LinearProductAndSeedMultiplierBeforeFullyGrown.NearlyEquals(Vector2.Zero)) { return; } + + float growthProgress = Vines.Count / (float)MaximumVines; + + spawnChanceMultiplier = MathHelper.Lerp(LinearProductAndSeedMultiplierBeforeFullyGrown.X, LinearProductAndSeedMultiplierBeforeFullyGrown.Y, growthProgress); + + if (MathUtils.NearlyEqual(spawnChanceMultiplier, 0f)) { return; } + } + + + bool spawnProduct = Rand.Range(0f, 1f) < (ProductSpawnChance * spawnChanceMultiplier), + spawnSeed = Rand.Range(0f, 1f) < (SeedSpawnChance * spawnChanceMultiplier); Vector2 spawnPos; @@ -678,7 +698,7 @@ namespace Barotrauma.Items.Components } } - fireCheckCooldown = 5f; + fireCheckCooldown = 10f; } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Holdable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Holdable.cs index 10f02f6fc..0b01d7546 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Holdable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Holdable.cs @@ -1,4 +1,5 @@ using Barotrauma.Abilities; +using Barotrauma.Extensions; using Barotrauma.Networking; using FarseerPhysics; using FarseerPhysics.Dynamics; @@ -44,7 +45,12 @@ namespace Barotrauma.Items.Components private bool attachable, attached, attachedByDefault; private Voronoi2.VoronoiCell attachTargetCell; - private PhysicsBody body; + + /// + /// The item's original physics body (if one exists). When the item is attached to a wall, it's gets set to null, + /// and we use this field to keep track of the original body. + /// + private PhysicsBody originalBody; public readonly ImmutableDictionary HoldableStatValues; @@ -62,7 +68,7 @@ namespace Barotrauma.Items.Components public PhysicsBody Body { - get { return item.body ?? body; } + get { return item.body ?? originalBody; } } [Serialize(false, IsPropertySaveable.Yes, description: "Is the item currently attached to a wall (only valid if Attachable is set to true).")] @@ -84,6 +90,9 @@ namespace Barotrauma.Items.Components set; } + [Serialize(0f, IsPropertySaveable.Yes, description: "Camera offset to apply when aiming this item. Only valid if Aimable is set to true.")] + public float CameraAimOffset { get; set; } + [Serialize(false, IsPropertySaveable.No, description: "Should the character adjust its pose when aiming with the item. Most noticeable underwater, where the character will rotate its entire body to face the direction the item is aimed at.")] public bool ControlPose { @@ -119,6 +128,32 @@ namespace Barotrauma.Items.Components set; } + [Serialize(false, IsPropertySaveable.No, description: "When enabled, the item can only be attached to a position where it touches the floor.")] + public bool AttachesToFloor + { + get; + set; + } + + [Serialize(true, IsPropertySaveable.No, description: "Can the item be attached inside doors?")] + public bool AllowAttachInsideDoors + { + get; + set; + } + + private HashSet disallowAttachingOverTags = new HashSet(); + + [Editable, Serialize("", IsPropertySaveable.Yes)] + public string DisallowAttachingOverTags + { + get => disallowAttachingOverTags.ConvertToString(); + set + { + disallowAttachingOverTags = value.ToIdentifiers().ToHashSet(); + } + } + [Serialize(false, IsPropertySaveable.No, description: "Should the item be attached to a wall by default when it's placed in the submarine editor.")] public bool AttachedByDefault { @@ -275,7 +310,7 @@ namespace Barotrauma.Items.Components public Holdable(Item item, ContentXElement element) : base(item, element) { - body = item.body; + originalBody = item.body; Pusher = null; if (element.GetAttributeBool("blocksplayers", false)) @@ -411,9 +446,9 @@ namespace Barotrauma.Items.Components if (attachable) { - if (body != null) + if (originalBody != null) { - item.body = body; + item.body = originalBody; } DeattachFromWall(); } @@ -514,9 +549,9 @@ namespace Barotrauma.Items.Components if (character != null) { item.Submarine = character.Submarine; } if (item.body == null) { - if (body != null) + if (originalBody != null) { - item.body = body; + item.body = originalBody; } else { @@ -565,13 +600,45 @@ namespace Barotrauma.Items.Components public bool CanBeAttached(Character user) { + return CanBeAttached(user, out _); + } + + private static List tempOverlappingItems = new List(); + + private bool CanBeAttached(Character user, out IEnumerable overlappingItems) + { + tempOverlappingItems.Clear(); + overlappingItems = tempOverlappingItems; if (!attachable || !Reattachable) { return false; } //can be attached anywhere in sub editor if (Screen.Selected == GameMain.SubEditorScreen) { return true; } + if (AttachesToFloor && item.CurrentHull == null) { return false; } + Vector2 attachPos = user == null ? item.WorldPosition : GetAttachPosition(user, useWorldCoordinates: true); + if (disallowAttachingOverTags.Any() || !AllowAttachInsideDoors) + { + var connectedHulls = item.CurrentHull?.GetConnectedHulls(includingThis: true, searchDepth: 5, ignoreClosedGaps: true); + Vector2 size = item.Rect.Size.ToVector2() / 2; + foreach (Item otherItem in Item.ItemList) + { + if (otherItem == item || otherItem.body is { BodyType: BodyType.Dynamic, Enabled: true }) { continue; } + if (connectedHulls != null && !connectedHulls.Contains(otherItem.CurrentHull)) { continue; } + if (disallowAttachingOverTags.None(tag => otherItem.HasTag(tag)) && + (otherItem.GetComponent() == null || AllowAttachInsideDoors)) + { + continue; + } + Rectangle worldRect = otherItem.WorldRect; + if (attachPos.X + size.X < worldRect.X || attachPos.X - size.X > worldRect.Right) { continue; } + if (attachPos.Y - size.Y > worldRect.Y || attachPos.Y + size.Y < worldRect.Y - worldRect.Height) { continue; } + tempOverlappingItems.Add(otherItem); + } + if (tempOverlappingItems.Any()) { return false; } + } + //can be attached anywhere inside hulls if (item.CurrentHull != null && Submarine.RectContains(item.CurrentHull.WorldRect, attachPos)) { return true; } @@ -670,14 +737,19 @@ namespace Barotrauma.Items.Components { if (!attachable) { return; } - if (body == null) + if (originalBody == null) { throw new InvalidOperationException($"Tried to attach an item with no physics body to a wall ({item.Prefab.Identifier})."); } - body.Enabled = false; - body.SetTransformIgnoreContacts(body.SimPosition, rotation: 0.0f); - item.body = null; + originalBody.Enabled = false; + originalBody.SetTransformIgnoreContacts(originalBody.SimPosition, rotation: 0.0f); + if (item.body != null) + { + item.body.Dir = 1; + item.body = null; + } + item.GetComponents().ForEach(static light => light.SetLightSourceTransform()); //outside hulls/subs -> we need to check if the item is being attached on a structure outside the sub if (item.CurrentHull == null && item.Submarine == null) @@ -689,7 +761,7 @@ namespace Barotrauma.Items.Components { //set to submarine-relative position item.SetTransform(ConvertUnits.ToSimUnits(item.WorldPosition - attachTarget.Submarine.Position), 0.0f, false); - body.SetTransformIgnoreContacts(item.SimPosition, 0.0f); + originalBody.SetTransformIgnoreContacts(item.SimPosition, 0.0f); } item.Submarine = attachTarget.Submarine; } @@ -833,6 +905,9 @@ namespace Barotrauma.Items.Components { item.Drop(character); item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f, findNewHull: false); + //don't find the new hull in SetTransform, because that'd also potentially change the submarine (teleport the item outside if it's attached outside) + //instead just find the hull, so the item is considered to be in the right hull + item.CurrentHull = Hull.FindHull(item.WorldPosition, item.CurrentHull); //the light source won't get properly updated if lighting is disabled (even though the light sprite is still drawn when lighting is disabled) //so let's ensure the light source is up-to-date RefreshLightSources(item); @@ -869,34 +944,60 @@ namespace Barotrauma.Items.Components Vector2 mouseDiff = user.CursorWorldPosition - user.WorldPosition; mouseDiff = mouseDiff.ClampLength(MaxAttachDistance); + Vector2 submarinePos = useWorldCoordinates && user.Submarine != null ? user.Submarine.Position : Vector2.Zero; Vector2 userPos = useWorldCoordinates ? user.WorldPosition : user.Position; Vector2 attachPos = userPos + mouseDiff; + Vector2 halfSize = new Vector2(item.Rect.Width, item.Rect.Height) / 2; + //offset the position by half the size of the grid to get the item to adhere to the grid in the same way as in the sub editor //in the sub editor, we align the top-left corner of the item with the grid //but here the origin of the item is placed at the attach position, so we need to offset it Vector2 offset = new Vector2( - -(item.Rect.Width / 2) % Submarine.GridSize.X, - (item.Rect.Height / 2) % Submarine.GridSize.Y); + -halfSize.X % Submarine.GridSize.X, + halfSize.Y % Submarine.GridSize.Y); if (user.Submarine != null) { //we must add some "padding" to the raycast to ensure it reaches all the way to a wall //otherwise the cursor might be outside a wall, but the grid cell it's in might be partially inside - Vector2 padding = Submarine.GridSize * new Vector2(Math.Sign(mouseDiff.X), Math.Sign(mouseDiff.Y)); + Vector2 padding = halfSize * new Vector2(Math.Sign(mouseDiff.X), Math.Sign(mouseDiff.Y)); if (Submarine.PickBody( ConvertUnits.ToSimUnits(user.Position), - ConvertUnits.ToSimUnits(user.Position + mouseDiff + padding), collisionCategory: Physics.CollisionWall) != null) + ConvertUnits.ToSimUnits(user.Position + mouseDiff + padding), collisionCategory: Physics.CollisionWall, + /*don't ignore sensors so the raycast can hit open doors or broken walls*/ + ignoreSensors: AllowAttachInsideDoors, customPredicate: (Fixture fixture) => + { + if (fixture.UserData is Door) { return false; } + return true; + }) != null) { - attachPos = userPos + mouseDiff * Submarine.LastPickedFraction + offset; - + Vector2 pickedPos = userPos + mouseDiff * Submarine.LastPickedFraction + offset - submarinePos; //round down if we're placing on the right side and vice versa: ensures we don't round the position inside a wall - return + attachPos = new Vector2( - (mouseDiff.X > 0 ? MathF.Floor(attachPos.X / Submarine.GridSize.X) : MathF.Ceiling(attachPos.X / Submarine.GridSize.X)) * Submarine.GridSize.X, - (mouseDiff.Y > 0 ? MathF.Floor(attachPos.Y / Submarine.GridSize.Y) : MathF.Ceiling(attachPos.Y / Submarine.GridSize.Y)) * Submarine.GridSize.Y) - - offset; + RoundToGrid(pickedPos.X, Submarine.GridSize.X, roundingDir: -Math.Sign(mouseDiff.X)), + RoundToGrid(pickedPos.Y, Submarine.GridSize.Y, roundingDir: -Math.Sign(mouseDiff.Y))) + - offset + submarinePos; + } + + if (AttachesToFloor) + { + //if attaching to floor, do a raycast down and move the attach pos where it hits + float size = item.Rect.Height / 2.0f; + Vector2 rayStart = attachPos - submarinePos; + Vector2 rayEnd = rayStart - Vector2.UnitY * MaxAttachDistance * 2; + if (Submarine.PickBody( + ConvertUnits.ToSimUnits(rayStart), + ConvertUnits.ToSimUnits(rayEnd), collisionCategory: Physics.CollisionWall | Physics.CollisionPlatform) != null) + { + attachPos = ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.UnitY * size + submarinePos; + } + else + { + return Vector2.Zero; + } } } else if (Level.Loaded != null) @@ -919,9 +1020,30 @@ namespace Barotrauma.Items.Components } } - return new Vector2( - MathUtils.RoundTowardsClosest(attachPos.X + offset.X, Submarine.GridSize.X), - MathUtils.RoundTowardsClosest(attachPos.Y + offset.Y, Submarine.GridSize.Y)) - offset; + //subtract the submarine position so we're doing the rounding in the sub's + //internal/local coordinate space regardless if we're using world coordinates + //(otherwise the rounding would behave differently depending on the value of useWorldCoordinates) + Vector2 offsetAttachPos = attachPos + offset - submarinePos; + return + new Vector2( + RoundToGrid(offsetAttachPos.X, Submarine.GridSize.X), + //don't round the vertical position if we're attaching to floor - we want the item to align with the floor, not the grid + AttachesToFloor ? offsetAttachPos.Y : RoundToGrid(offsetAttachPos.Y, Submarine.GridSize.Y)) + - offset + submarinePos; + + ///If < 0, the method rounds down. If > 0, rounds up. If 0, rounds to the closest integer. + static float RoundToGrid(float position, float gridSize, int roundingDir = 0) + { + if (roundingDir < 0) + { + return MathF.Floor(position / gridSize) * gridSize; + } + else if (roundingDir > 0) + { + return MathF.Ceiling(position / gridSize) * gridSize; + } + return MathUtils.RoundTowardsClosest(position, gridSize); + } } private Voronoi2.VoronoiCell GetAttachTargetCell(float maxDist) @@ -977,7 +1099,7 @@ namespace Barotrauma.Items.Components return; } - if (picker == Character.Controlled && picker.IsKeyDown(InputType.Aim) && CanBeAttached(picker)) + if (picker == Character.Controlled && picker.IsKeyDown(InputType.Aim) && attachable && Reattachable) { Drawable = true; } @@ -1115,11 +1237,11 @@ namespace Barotrauma.Items.Components } else { - if (body != null) + if (originalBody != null) { - body.SetTransformIgnoreContacts(item.SimPosition, item.Rotation); - item.body = body; - body.Enabled = item.ParentInventory == null; + originalBody.SetTransformIgnoreContacts(item.SimPosition, item.Rotation); + item.body = originalBody; + originalBody.Enabled = item.ParentInventory == null; } DeattachFromWall(); } @@ -1134,7 +1256,7 @@ namespace Barotrauma.Items.Components Pusher.Remove(); Pusher = null; } - body = null; + originalBody = null; } public override XElement Save(XElement parentElement) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs index c601c551c..7610321f2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs @@ -436,55 +436,65 @@ namespace Barotrauma.Items.Components Item targetItem = target.UserData is Holdable h ? h.Item : target.UserData as Item ?? targetFixture.UserData as Item; Entity targetEntity = targetCharacter ?? targetStructure ?? targetItem ?? target.UserData as Entity; GameMain.LuaCs.Hook.Call("meleeWeapon.handleImpact", this, target); + if (Attack != null) { Attack.SetUser(user); - Attack.DamageMultiplier = damageMultiplier; - if (targetLimb != null) + bool applyAttack = true; + if (Attack.Conditionals.Any(c => !c.TargetSelf && !c.Matches(targetEntity as ISerializableEntity)) || + Attack.Conditionals.Any(c => c.TargetSelf && !c.Matches(user))) { - if (targetLimb.character.Removed) { return; } - targetLimb.character.LastDamageSource = item; - Attack.DoDamageToLimb(user, targetLimb, item.WorldPosition, 1.0f); + applyAttack = false; } - else if (targetCharacter != null) + if (applyAttack) { - if (targetCharacter.Removed) { return; } - targetCharacter.LastDamageSource = item; - Attack.DoDamage(user, targetCharacter, item.WorldPosition, 1.0f); - } - else if (targetStructure != null) - { - if (targetStructure.Removed) { return; } - Attack.DoDamage(user, targetStructure, item.WorldPosition, 1.0f); - } - else if (targetItem != null && targetItem.Prefab.DamagedByMeleeWeapons && targetItem.Condition > 0) - { - if (targetItem.Removed) { return; } - var attackResult = Attack.DoDamage(user, targetItem, item.WorldPosition, 1.0f); -#if CLIENT - if (attackResult.Damage > 0.0f && targetItem.Prefab.ShowHealthBar && Character.Controlled != null && - (user == Character.Controlled || Character.Controlled.CanSeeTarget(item))) + Attack.DamageMultiplier = damageMultiplier; + if (targetLimb != null) { - Character.Controlled.UpdateHUDProgressBar(targetItem, - targetItem.WorldPosition, - targetItem.Condition / targetItem.MaxCondition, - emptyColor: GUIStyle.HealthBarColorLow, - fullColor: GUIStyle.HealthBarColorHigh, - textTag: targetItem.Prefab.ShowNameInHealthBar ? targetItem.Name : string.Empty); + if (targetLimb.character.Removed) { return; } + targetLimb.character.LastDamageSource = item; + Attack.DoDamageToLimb(user, targetLimb, item.WorldPosition, 1.0f); } + else if (targetCharacter != null) + { + if (targetCharacter.Removed) { return; } + targetCharacter.LastDamageSource = item; + Attack.DoDamage(user, targetCharacter, item.WorldPosition, 1.0f); + } + else if (targetStructure != null) + { + if (targetStructure.Removed) { return; } + Attack.DoDamage(user, targetStructure, item.WorldPosition, 1.0f); + } + else if (targetItem != null && targetItem.Prefab.DamagedByMeleeWeapons && targetItem.Condition > 0) + { + if (targetItem.Removed) { return; } + var attackResult = Attack.DoDamage(user, targetItem, item.WorldPosition, 1.0f); +#if CLIENT + if (attackResult.Damage > 0.0f && targetItem.Prefab.ShowHealthBar && Character.Controlled != null && + (user == Character.Controlled || Character.Controlled.CanSeeTarget(item))) + { + Character.Controlled.UpdateHUDProgressBar(targetItem, + targetItem.WorldPosition, + targetItem.Condition / targetItem.MaxCondition, + emptyColor: GUIStyle.HealthBarColorLow, + fullColor: GUIStyle.HealthBarColorHigh, + textTag: targetItem.Prefab.ShowNameInHealthBar ? targetItem.Name : string.Empty); + } #endif - } - else if (target.UserData is Holdable { CanPush: true } holdable) - { - if (holdable.Item.Removed) { return; } - RestoreCollision(); - hitting = false; - User = null; - } - else - { - return; - } + } + else if (target.UserData is Holdable { CanPush: true } holdable) + { + if (holdable.Item.Removed) { return; } + RestoreCollision(); + hitting = false; + User = null; + } + else + { + return; + } + } } if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs index 00819bee9..f5c73ab42 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs @@ -522,7 +522,7 @@ namespace Barotrauma.Items.Components #if CLIENT float barOffset = 10f * GUI.Scale; Vector2 offset = planter.PlantSlots.ContainsKey(i) ? planter.PlantSlots[i].Offset : Vector2.Zero; - user?.UpdateHUDProgressBar(planter, planter.Item.DrawPosition + new Vector2(barOffset, 0) + offset, seed.Health / seed.MaxHealth, GUIStyle.Blue, GUIStyle.Blue, "progressbar.watering"); + user?.UpdateHUDProgressBar(planter, planter.Item.DrawPosition + new Vector2(barOffset, 0) + offset, seed.Health / seed.MaxWater, GUIStyle.Blue, GUIStyle.Blue, "progressbar.watering"); #endif } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Throwable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Throwable.cs index 3c22efde5..6ca8e627f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Throwable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Throwable.cs @@ -47,7 +47,7 @@ namespace Barotrauma.Items.Components public override bool Use(float deltaTime, Character character = null) { //actual throwing logic is handled in Update - return characterUsable || character == null; + return (characterUsable && !UsageDisabledByRangedWeapon(character)) || character == null; } public override bool SecondaryUse(float deltaTime, Character character = null) @@ -111,24 +111,28 @@ namespace Barotrauma.Items.Components return; } - if (throwState != ThrowState.Throwing) + bool aim = false; + if (!UsageDisabledByRangedWeapon(picker)) { - if (picker.IsKeyDown(InputType.Aim)) + if (throwState != ThrowState.Throwing) { - if (picker.IsKeyDown(InputType.Shoot)) { throwState = ThrowState.Initiated; } + if (picker.IsKeyDown(InputType.Aim)) + { + if (picker.IsKeyDown(InputType.Shoot)) { throwState = ThrowState.Initiated; } + } + else if (throwState != ThrowState.Initiated) + { + throwAngle = ThrowAngleStart; + } } - else if (throwState != ThrowState.Initiated) - { - throwAngle = ThrowAngleStart; - } - } - bool aim = picker.IsKeyDown(InputType.Aim) && picker.CanAim; + aim = picker.IsKeyDown(InputType.Aim) && picker.CanAim; + } if (picker.IsDead || !picker.AllowInput) { throwState = ThrowState.None; aim = false; - } + } ApplyStatusEffects(ActionType.OnActive, deltaTime, picker); //return if the status effect got rid of the picker somehow diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemComponent.cs index 7c5561f83..275daa85b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemComponent.cs @@ -1000,6 +1000,13 @@ namespace Barotrauma.Items.Components /// public virtual void OnItemLoaded() { } + /// + /// Implement in a base class if the instances of the component contain some sort of data that isn't serialized using the normal serializable properties + /// (i.e. some data that changes per-item and isn't loaded from the prefab, but that isn't a property marked with [Serialize] either), + /// but that must be copied when cloning the item. + /// + public virtual void Clone(ItemComponent original) { } + public virtual void OnScaleChanged() { } /// @@ -1095,7 +1102,6 @@ namespace Barotrauma.Items.Components componentElement.Add(newElement); } - SerializableProperty.SerializeProperties(this, componentElement); parentElement.Add(componentElement); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs index a070e9a96..ae47871e3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs @@ -438,9 +438,14 @@ namespace Barotrauma.Items.Components ActiveContainedItem activeContainedItem = new(containedItem, effect, containableItem.ExcludeBroken, containableItem.ExcludeFullCondition, containableItem.BlameEquipperForDeath); activeContainedItems.Add(activeContainedItem); - if (!ShouldApplyEffects(activeContainedItem)) { continue; } + if (!ShouldApplyEffects(activeContainedItem) || item.Submarine is { Loading: true} || initializingLoadedItems || + containedItem.OnInsertedEffectsApplied) + { + continue; + } activeContainedItem.StatusEffect.Apply(ActionType.OnInserted, deltaTime: 1, item, targets); } + containedItem.OnInsertedEffectsApplied = true; } } } @@ -511,6 +516,8 @@ namespace Barotrauma.Items.Components activeContainedItem.StatusEffect.Apply(ActionType.OnRemoved, deltaTime: 1, item, targets); } + containedItem.OnInsertedEffectsApplied = false; + activeContainedItems.RemoveAll(i => i.Item == containedItem); containedItems.RemoveAll(i => i.Item == containedItem); item.SetContainedItemPositions(); @@ -680,7 +687,7 @@ namespace Barotrauma.Items.Components foreach (ActiveContainedItem activeContainedItem in activeContainedItems) { - if (!ShouldApplyEffects(activeContainedItem)) continue; + if (!ShouldApplyEffects(activeContainedItem)) { continue; } StatusEffect effect = activeContainedItem.StatusEffect; effect.Apply(ActionType.OnActive, deltaTime, item, targets); @@ -993,6 +1000,8 @@ namespace Barotrauma.Items.Components } else { + //flip if flipped on one axis but not both (flipping on both axes is basically "double negative" and makes the rotation normal again) + if (flippedX ^ flippedY) { rotation = -rotation; } rotation += -item.RotationRad; } contained.Item.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, rotation); @@ -1050,8 +1059,17 @@ namespace Barotrauma.Items.Components transformedItemIntervalHorizontal = new Vector2(transformedItemInterval.X, 0.0f); transformedItemIntervalVertical = new Vector2(0.0f, transformedItemInterval.Y); - flippedX = item.RootContainer?.FlippedX ?? (item.FlippedX && item.Prefab.CanSpriteFlipX); - flippedY = item.RootContainer?.FlippedY ?? (item.FlippedY && item.Prefab.CanSpriteFlipY); + if (item.RootContainer != null) + { + flippedX = item.RootContainer.FlippedX && item.RootContainer.Prefab.CanSpriteFlipX; + flippedY = item.RootContainer.FlippedY && item.RootContainer.Prefab.CanSpriteFlipY; + } + else + { + flippedX = item.FlippedX && item.Prefab.CanSpriteFlipX; + flippedY = item.FlippedY && item.Prefab.CanSpriteFlipY; + } + var rootBody = item.RootContainer?.body ?? item.body; bool bodyFlipped = rootBody is { Dir: -1 }; @@ -1123,10 +1141,13 @@ namespace Barotrauma.Items.Components } } + private bool initializingLoadedItems; + public override void OnMapLoaded() { if (itemIds != null) { + initializingLoadedItems = true; for (ushort i = 0; i < itemIds.Length; i++) { if (i >= Inventory.Capacity) @@ -1138,10 +1159,11 @@ namespace Barotrauma.Items.Components } foreach (ushort id in itemIds[i]) { - if (!(Entity.FindEntityByID(id) is Item item)) { continue; } + if (Entity.FindEntityByID(id) is not Item item) { continue; } Inventory.TryPutItem(item, i, false, false, null, createNetworkEvent: false, ignoreCondition: true); } } + initializingLoadedItems = false; itemIds = null; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Controller.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Controller.cs index a41b9e10c..5bd44334a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Controller.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Controller.cs @@ -349,7 +349,7 @@ namespace Barotrauma.Items.Components // Don't move lower body limbs if there's another selected secondary item that should control them if (limb.IsLowerBody && user.HasSelectedAnotherSecondaryItem(Item)) { continue; } // Don't move hands if there's a selected primary item that should control them - if (!limb.IsLowerBody && Item == user.SelectedSecondaryItem && user.SelectedItem != null) { continue; } + if (limb.IsArm && Item == user.SelectedSecondaryItem && user.SelectedItem != null) { continue; } if (lb.AllowUsingLimb) { switch (lb.LimbType) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Reactor.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Reactor.cs index fd50424b0..b5a84491f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Reactor.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Reactor.cs @@ -564,7 +564,7 @@ namespace Barotrauma.Items.Components { fireTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition); #if SERVER - if (fireTimer > Math.Min(5.0f, FireDelay / 2) && blameOnBroken?.Character?.SelectedItem == item) + if (fireTimer > Math.Min(5.0f, FireDelay / 2) && blameOnBroken?.Character != null) { GameMain.Server.KarmaManager.OnReactorOverHeating(item, blameOnBroken.Character, deltaTime); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs index 01a4fa818..d7130f534 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs @@ -76,7 +76,7 @@ namespace Barotrauma.Items.Components private double lastReceivedSteeringSignalTime; - [Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes, AlwaysUseInstanceValues = true)] + [Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes, description: "Is autopilot currently on or not?", AlwaysUseInstanceValues = true)] public bool AutoPilot { get { return autoPilot; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Planter.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Planter.cs index f00e9bf4a..bff43ce3c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Planter.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Planter.cs @@ -110,17 +110,15 @@ namespace Barotrauma.Items.Components { base.OnItemLoaded(); IsActive = true; -#if CLIENT var lights = item.GetComponents(); if (lights.Any()) { lightComponents = lights.ToList(); - foreach (var light in lightComponents) + foreach (var light in item.GetComponents()) { - light.Light.Enabled = false; + light.IsOn = false; } } -#endif container = item.GetComponent(); GrowableSeeds = new Growable[container.Capacity]; } @@ -233,7 +231,6 @@ namespace Barotrauma.Items.Components { base.Update(deltaTime, cam); -#if CLIENT if (lightComponents != null && lightComponents.Count > 0) { bool hasSeed = false; @@ -243,10 +240,9 @@ namespace Barotrauma.Items.Components } foreach (var light in lightComponents) { - light.Light.Enabled = hasSeed; + light.IsOn = hasSeed; } } -#endif if (container?.Inventory == null) { return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/PowerDistributor.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/PowerDistributor.cs new file mode 100644 index 000000000..fd4340201 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/PowerDistributor.cs @@ -0,0 +1,230 @@ +#nullable enable +using Barotrauma.Networking; +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Xml.Linq; + +namespace Barotrauma.Items.Components +{ + internal partial class PowerDistributor : PowerTransfer + { + private const int MaxNameLength = 32; + + private const int SupplyRatioSteps = 20; + private const float SupplyRatioStep = 1f / SupplyRatioSteps; + + private partial class PowerGroup + { + private readonly PowerDistributor distributor; + public readonly Connection PowerOut; + public readonly Connection? RatioInput, RatioOutput; + + private string name; + public string Name + { + get => name; + set + { + name = value; + DisplayName = TextManager.Get(name).Fallback(name); +#if CLIENT + UpdateNameBox(); +#endif + } + } + + public LocalizedString? DisplayName { get; private set; } + + private float supplyRatio = 1f; + public float SupplyRatio + { + get => supplyRatio; + set + { + if (!MathUtils.IsValid(value)) { return; } + supplyRatio = MathUtils.RoundTowardsClosest(MathHelper.Clamp(value, 0f, 1f), SupplyRatioStep); +#if CLIENT + UpdateSlider(); +#endif + } + } + + public float DisplayRatio + { + get => MathUtils.RoundToInt(supplyRatio * 100); + set => SupplyRatio = value / 100f; + } + + public float Load; + public float ModifiedLoad => Load * SupplyRatio; + + public PowerGroup(PowerDistributor distributor, Connection power, XElement? element = null, Connection? ratioInput = null, Connection? ratioOutput = null) + { + this.distributor = distributor; + PowerOut = power; + + RatioInput = ratioInput; + RatioOutput = ratioOutput; + + distributor.powerGroups.Add(this); + + name = TextManager.GetWithVariable("groupx", "[num]", distributor.powerGroups.Count.ToString()).Value; + SupplyRatio = 1f; + + if (element != null) + { + name = element.GetAttributeString("name", name); + SupplyRatio = element.GetAttributeFloat("ratio", SupplyRatio); + } + +#if CLIENT + CreateGUI(); +#endif + } + + #region Signals + public void ReceiveRatioSignal(Signal signal) + { + if (!float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float receivedSignal) || !MathUtils.IsValid(receivedSignal)) { return; } + DisplayRatio = receivedSignal; + } + + public void SendRatioSignal() => distributor.item.SendSignal(new Signal(DisplayRatio.ToString()), RatioOutput); + #endregion + } + + private readonly List powerGroups = new List(); + + protected override PowerPriority Priority => PowerPriority.Relay; + + public PowerDistributor(Item item, ContentXElement element) : base(item, element) { } + + public override void OnItemLoaded() + { + base.OnItemLoaded(); + + IEnumerable ratioInputs = Item.Connections.Where(static conn => !conn.IsOutput && conn.Name.StartsWith("set_supply_ratio")); + IEnumerable ratioOutputs = Item.Connections.Where(static conn => conn.IsOutput && conn.Name.StartsWith("supply_ratio_out")); + + for (int i = 0; i < powerOuts.Count; i++) + { + new PowerGroup(this, powerOuts[i], cachedGroupData.ElementAtOrDefault(i), ratioInputs.ElementAtOrDefault(i), ratioOutputs.ElementAtOrDefault(i)); + } + + cachedGroupData.Clear(); + } + + public override void Clone(ItemComponent original) + { + if (original is not PowerDistributor originalPowerDistributor) { return; } + for (int i = 0; i < powerOuts.Count; i++) + { + powerGroups[i].SupplyRatio = originalPowerDistributor.powerGroups[i].SupplyRatio; + powerGroups[i].Name = originalPowerDistributor.powerGroups[i].Name; + } + } + + #region Signals + protected override void SendSignals() + { + item.SendSignal(MathUtils.RoundToInt(powerIn.Grid?.Power ?? 0f).ToString(), "power_value_out"); + item.SendSignal(MathUtils.RoundToInt(GetCurrentPowerConsumption(powerIn)).ToString(), "load_value_out"); + powerGroups.ForEach(static group => group.SendRatioSignal()); + } + + public override void ReceiveSignal(Signal signal, Connection connection) + { + if (item.Condition <= 0f || connection.IsPower) { return; } + if (connection.IsOutput) { return; } + + powerGroups.FirstOrDefault(group => group.RatioInput == connection)?.ReceiveRatioSignal(signal); + } + #endregion + + #region Power Calculation + private bool IsShortCircuited(Connection conn) => powerIn.Grid == conn.Grid; + + public override float GetCurrentPowerConsumption(Connection? connection = null) + { + if (connection != powerIn) { return -1f; } + if (isBroken) { return 0f; } + return powerGroups.Sum(group => IsShortCircuited(group.PowerOut) ? 0f : group.ModifiedLoad) + ExtraLoad; + } + + private float CalculatePowerOut(PowerGroup group) + { + if (isBroken || powerIn.Grid == null || IsShortCircuited(group.PowerOut)) { return 0f; } + return Math.Max(group.ModifiedLoad * Voltage, 0f); + } + + public override float GetConnectionPowerOut(Connection connection, float power, PowerRange minMaxPower, float load) + { + if (connection == powerIn) { return 0f; } + PowerGroup group = powerGroups.First(group => group.PowerOut == connection); + group.Load = load; + return CalculatePowerOut(group); + } + #endregion + + #region Serialization + private readonly List cachedGroupData = new List(); + + public override XElement Save(XElement parentElement) + { + XElement componentElement = base.Save(parentElement); + foreach (PowerGroup powerGroup in powerGroups) + { + componentElement.Add(new XElement("PowerGroup", + new XAttribute("name", powerGroup.Name), + new XAttribute("ratio", powerGroup.SupplyRatio))); + } + return componentElement; + } + + public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap) + { + base.Load(componentElement, usePrefabValues, idRemap, isItemSwap); + if (usePrefabValues) { return; } + + foreach (XElement element in componentElement.Elements()) + { + cachedGroupData.Add(element); + } + } + #endregion + + #region Networking + private enum EventType { NameChange, RatioChange } + + private void SharedEventWrite(IWriteMessage msg, NetEntityEvent.IData? extraData = null) + { + EventData data = ExtractEventData(extraData); + msg.WriteRangedInteger((int)data.EventType, 0, 1); + msg.WriteRangedInteger(powerGroups.IndexOf(data.PowerGroup), 0, powerGroups.Count - 1); + switch (data.EventType) + { + case EventType.NameChange: + msg.WriteString(data.PowerGroup.Name); + break; + case EventType.RatioChange: + msg.WriteRangedInteger(MathUtils.RoundToInt(data.PowerGroup.SupplyRatio / SupplyRatioStep), 0, SupplyRatioSteps); + break; + } + } + + private void SharedEventRead(IReadMessage msg, out EventType eventType, out PowerGroup powerGroup, out string newName, out float newRatio) + { + eventType = (EventType)msg.ReadRangedInteger(0, 1); + powerGroup = powerGroups[msg.ReadRangedInteger(0, powerGroups.Count - 1)]; + + newName = eventType == EventType.NameChange ? string.Concat(msg.ReadString().Take(MaxNameLength)) : powerGroup.Name; + newRatio = eventType == EventType.RatioChange ? msg.ReadRangedInteger(0, SupplyRatioSteps) * SupplyRatioStep : powerGroup.SupplyRatio; + } + + private readonly record struct EventData(PowerGroup PowerGroup, EventType EventType) : IEventData; + #endregion + } +} \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/PowerTransfer.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/PowerTransfer.cs index 8473a50de..c110e8b9e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/PowerTransfer.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/PowerTransfer.cs @@ -177,18 +177,7 @@ namespace Barotrauma.Items.Components { RefreshConnections(); - if (Timing.TotalTime > extraLoadSetTime + 1.0) - { - //Decay the extra load to 0 from either positive or negative - if (extraLoad > 0) - { - extraLoad = Math.Max(extraLoad - 1000.0f * deltaTime, 0); - } - else - { - extraLoad = Math.Min(extraLoad + 1000.0f * deltaTime, 0); - } - } + UpdateExtraLoad(deltaTime); if (!CanTransfer) { return; } @@ -200,6 +189,28 @@ namespace Barotrauma.Items.Components ApplyStatusEffects(ActionType.OnActive, deltaTime); + SendSignals(); + + UpdateOvervoltage(deltaTime); + } + + protected virtual void UpdateExtraLoad(float deltaTime) + { + if (Timing.TotalTime <= extraLoadSetTime + 1.0) { return; } + + //Decay the extra load to 0 from either positive or negative + if (extraLoad > 0) + { + extraLoad = Math.Max(extraLoad - 1000.0f * deltaTime, 0); + } + else + { + extraLoad = Math.Min(extraLoad + 1000.0f * deltaTime, 0); + } + } + + protected virtual void SendSignals() + { float powerReadingOut = 0; float loadReadingOut = ExtraLoad; if (powerLoad < 0) @@ -226,7 +237,10 @@ namespace Barotrauma.Items.Components } item.SendSignal(powerSignal, "power_value_out"); item.SendSignal(loadSignal, "load_value_out"); + } + protected virtual void UpdateOvervoltage(float deltaTime) + { //if the item can't be fixed, don't allow it to break if (!item.Repairables.Any() || !CanBeOverloaded) { return; } @@ -234,46 +248,45 @@ namespace Barotrauma.Items.Components Overload = Voltage > maxOverVoltage && GameMain.GameSession is not { RoundDuration: < 5 }; - if (Overload && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)) + if (!Overload || GameMain.NetworkMember is { IsClient: true }) { return; } + + if (overloadCooldownTimer > 0.0f) { - if (overloadCooldownTimer > 0.0f) - { - overloadCooldownTimer -= deltaTime; - return; - } + overloadCooldownTimer -= deltaTime; + return; + } - //damage the item if voltage is too high (except if running as a client) - float prevCondition = item.Condition; - //some randomness to prevent all junction boxes from breaking at the same time - if (Rand.Range(0.0f, 1.0f) < 0.01f) - { - //damaged boxes are more sensitive to overvoltage (also preventing all boxes from breaking at the same time) - float conditionFactor = MathHelper.Lerp(5.0f, 1.0f, item.Condition / item.MaxCondition); - item.Condition -= deltaTime * Rand.Range(10.0f, 500.0f) * conditionFactor; - } - if (item.Condition <= 0.0f && prevCondition > 0.0f) - { - overloadCooldownTimer = OverloadCooldown; + //damage the item if voltage is too high (except if running as a client) + float prevCondition = item.Condition; + //some randomness to prevent all junction boxes from breaking at the same time + if (Rand.Range(0.0f, 1.0f) < 0.01f) + { + //damaged boxes are more sensitive to overvoltage (also preventing all boxes from breaking at the same time) + float conditionFactor = MathHelper.Lerp(5.0f, 1.0f, item.Condition / item.MaxCondition); + item.Condition -= deltaTime * Rand.Range(10.0f, 500.0f) * conditionFactor; + } + + if (item.Condition > 0.0f || prevCondition <= 0.0f) { return; } + + overloadCooldownTimer = OverloadCooldown; #if CLIENT - SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull); - Vector2 baseVel = Rand.Vector(300.0f); - for (int i = 0; i < 10; i++) - { - var particle = GameMain.ParticleManager.CreateParticle("spark", item.WorldPosition, - baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull); - if (particle != null) particle.Size *= Rand.Range(0.5f, 1.0f); - } + SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull); + Vector2 baseVel = Rand.Vector(300.0f); + for (int i = 0; i < 10; i++) + { + var particle = GameMain.ParticleManager.CreateParticle("spark", item.WorldPosition, + baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull); + if (particle != null) particle.Size *= Rand.Range(0.5f, 1.0f); + } #endif - float currentIntensity = GameMain.GameSession?.EventManager != null ? - GameMain.GameSession.EventManager.CurrentIntensity : 0.5f; + float currentIntensity = GameMain.GameSession?.EventManager != null ? + GameMain.GameSession.EventManager.CurrentIntensity : 0.5f; - //higher probability for fires if the current intensity is low - if (FireProbability > 0.0f && - Rand.Range(0.0f, 1.0f) < MathHelper.Lerp(FireProbability, FireProbability * 0.1f, currentIntensity)) - { - new FireSource(item.WorldPosition); - } - } + //higher probability for fires if the current intensity is low + if (FireProbability > 0.0f && + Rand.Range(0.0f, 1.0f) < MathHelper.Lerp(FireProbability, FireProbability * 0.1f, currentIntensity)) + { + new FireSource(item.WorldPosition); } } @@ -424,7 +437,7 @@ namespace Barotrauma.Items.Components } } - if (this is not RelayComponent) + if (this is not RelayComponent and not PowerDistributor) { if (PowerConnections.Any(p => !p.IsOutput) && PowerConnections.Any(p => p.IsOutput)) { @@ -452,7 +465,7 @@ namespace Barotrauma.Items.Components { //other junction boxes don't need to receive the signal in the pass-through signal connections //because we relay it straight to the connected items without going through the whole chain of junction boxes - if (ic is PowerTransfer && ic is not RelayComponent) { continue; } + if (ic is PowerTransfer and not RelayComponent and not PowerDistributor) { continue; } ic.ReceiveSignal(signal, recipient); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/Powered.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/Powered.cs index 13db3297f..962b6a132 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/Powered.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Power/Powered.cs @@ -2,6 +2,7 @@ using Microsoft.Xna.Framework; using System.Collections.Generic; using System.Linq; +using Barotrauma.Extensions; #if CLIENT using Barotrauma.Sounds; #endif @@ -93,7 +94,22 @@ namespace Barotrauma.Items.Components /// protected float powerConsumption; - protected Connection powerIn, powerOut; + protected Connection powerIn; + protected List powerOuts = new List(); + /// + /// Throws an error if there is more than one power out connection.
+ /// Use if a component should handle multiple outputs. + ///
+ protected Connection powerOut + { + get + { + if (powerOuts.Count > 1) { DebugConsole.ThrowErrorOnce($"{item.ID}.multiplePowerOut", $"Item {item.Name} ({item.Prefab.Identifier}) has multiple power outputs, but only supports one!"); } + return powerOuts.FirstOrDefault(); + } + } + + protected bool powerInIsPowerOut => powerOuts.Contains(powerIn); /// /// Maximum voltage factor when the device is being overvolted. I.e. how many times more effectively the device can function when it's being overvolted @@ -153,9 +169,10 @@ namespace Barotrauma.Items.Components { if (powerIn?.Grid != null) { return powerIn.Grid.Voltage; } } - else if (powerOut != null) + else if (powerOuts.Any()) { - if (powerOut?.Grid != null) { return powerOut.Grid.Voltage; } + IEnumerable gridConnections = powerOuts.Where(static conn => conn.Grid != null); + if (gridConnections.Any()) { return gridConnections.Average(static conn => conn.Grid.Voltage); } } if (this is PowerTransfer && item.Condition <= 0.0f) @@ -245,18 +262,19 @@ namespace Barotrauma.Items.Components { powerIn = c; } - else if (c.Name == "power_out") - { - powerOut = c; - // Connection takes the lowest priority - if (Priority > powerOut.Priority) - { - powerOut.Priority = Priority; - } - } else if (c.Name == "power") { - powerIn = powerOut = c; + powerIn = c; + powerOuts.Add(c); + } + else if (c.IsOutput) + { + powerOuts.Add(c); + // Connection takes the lowest priority + if (Priority > c.Priority) + { + c.Priority = Priority; + } } } else @@ -271,11 +289,11 @@ namespace Barotrauma.Items.Components DebugConsole.NewMessage($"Item \"{item.Name}\" has a power output connection called power_in. If the item is supposed to receive power through the connection, change it to an input connection.", Color.Orange); #endif } - powerOut = c; + powerOuts.Add(c); // Connection takes the lowest priority - if (Priority > powerOut.Priority) + if (Priority > c.Priority) { - powerOut.Priority = Priority; + c.Priority = Priority; } } else @@ -335,9 +353,9 @@ namespace Barotrauma.Items.Components { powered.powerIn.Grid = null; } - if (powered.powerOut != null) + foreach (Connection powerOut in powered.powerOuts) { - powered.powerOut.Grid = null; + powerOut.Grid = null; } } @@ -345,8 +363,10 @@ namespace Barotrauma.Items.Components foreach (Powered powered in poweredList) { + if (powered.Item.Condition <= 0f) { continue; } + //Probe through all connections that don't have a gridID - if (powered.powerIn != null && powered.powerIn.Grid == null && powered.powerIn != powered.powerOut && powered.Item.Condition > 0.0f) + if (powered.powerIn != null && powered.powerIn.Grid == null && !powered.powerInIsPowerOut) { // Only create grids for networks with more than 1 device if (powered.powerIn.Recipients.Count > 0) @@ -356,13 +376,16 @@ namespace Barotrauma.Items.Components } } - if (powered.powerOut != null && powered.powerOut.Grid == null && powered.Item.Condition > 0.0f) + foreach (Connection powerOut in powered.powerOuts) { - //Only create grids for networks with more than 1 device - if (powered.powerOut.Recipients.Count > 0) + if (powerOut != null && powerOut.Grid == null) { - GridInfo grid = PropagateGrid(powered.powerOut); - Grids[grid.ID] = grid; + //Only create grids for networks with more than 1 device + if (powerOut.Recipients.Count > 0) + { + GridInfo grid = PropagateGrid(powerOut); + Grids[grid.ID] = grid; + } } } } @@ -479,7 +502,7 @@ namespace Barotrauma.Items.Components powered.Voltage -= deltaTime; //Handle the device if it's got a power connection - if (powered.powerIn != null && powered.powerOut != powered.powerIn) + if (powered.powerIn != null && !powered.powerInIsPowerOut) { //Get the new load for the connection float currLoad = powered.GetCurrentPowerConsumption(powered.powerIn); @@ -507,10 +530,10 @@ namespace Barotrauma.Items.Components } //Handle the device power depending on if its powerout - if (powered.powerOut != null) + foreach (Connection powerOut in powered.powerOuts) { //Get the connection's load - float currLoad = powered.GetCurrentPowerConsumption(powered.powerOut); + float currLoad = powered.GetCurrentPowerConsumption(powerOut); //Update the device's output load to the correct variable if (powered is PowerTransfer pt) @@ -529,20 +552,20 @@ namespace Barotrauma.Items.Components if (currLoad >= 0) { //Add to the grid load if possible - if (powered.powerOut.Grid != null) + if (powerOut.Grid != null) { - powered.powerOut.Grid.Load += currLoad; + powerOut.Grid.Load += currLoad; } } - else if (powered.powerOut.Grid != null) + else if (powerOut.Grid != null) { //Add connection as a source to be processed - powered.powerOut.Grid.AddSrc(powered.powerOut); + powerOut.Grid.AddSrc(powerOut); } else { //Perform power calculations for the singular connection - float loadOut = -powered.GetConnectionPowerOut(powered.powerOut, 0, powered.MinMaxPowerOut(powered.powerOut, 0), 0); + float loadOut = -powered.GetConnectionPowerOut(powerOut, 0, powered.MinMaxPowerOut(powerOut, 0), 0); if (powered is PowerTransfer pt2) { pt2.PowerLoad = loadOut; @@ -557,7 +580,7 @@ namespace Barotrauma.Items.Components } //Indicate grid is resolved as it was the only device - powered.GridResolved(powered.powerOut); + powered.GridResolved(powerOut); } } } @@ -625,7 +648,7 @@ namespace Barotrauma.Items.Components public virtual float GetCurrentPowerConsumption(Connection connection = null) { // If a handheld device there is no consumption - if (powerIn == null && powerOut == null) + if (powerIn == null && powerOuts.None()) { return 0; } @@ -664,7 +687,7 @@ namespace Barotrauma.Items.Components /// Power pushed to the grid public virtual float GetConnectionPowerOut(Connection conn, float power, PowerRange minMaxPower, float load) { - return conn == powerOut ? MathHelper.Max(-CurrPowerConsumption, 0) : 0; + return powerOuts.Contains(conn) ? MathHelper.Max(-CurrPowerConsumption, 0) : 0; } /// diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/CircuitBox.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/CircuitBox.cs index ab13440a8..031fee432 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/CircuitBox.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/CircuitBox.cs @@ -233,6 +233,7 @@ namespace Barotrauma.Items.Components var cloneNode = InputOutputNodes[ioIndex]; cloneNode.Position = origNode.Position; + cloneNode.ReplaceAllConnectionLabelOverrides(origNode.ConnectionLabelOverrides); } if (!clonedContainedItems.Any()) { return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Connection.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Connection.cs index 6708881d7..7d0e38cc8 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Connection.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Connection.cs @@ -18,7 +18,14 @@ namespace Barotrauma.Items.Components public readonly int MaxWires = 5; public readonly string Name; - public readonly LocalizedString DisplayName; + private readonly LocalizedString _displayName; + public LocalizedString DisplayName + { + get => DisplayNameOverride ?? _displayName; + private init => _displayName = value; + } + + public LocalizedString DisplayNameOverride; private readonly HashSet wires; public IReadOnlyCollection Wires => wires; @@ -160,8 +167,7 @@ namespace Barotrauma.Items.Components DisplayName = Name; } - IsPower = Name == "power_in" || Name == "power" || Name == "power_out"; - + IsPower = element.GetAttributeBool("ispower", Name is "power_in" or "power" or "power_out"); LoadedWires = new List<(ushort wireId, int? connectionIndex)>(); foreach (var subElement in element.Elements()) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/MotionSensor.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/MotionSensor.cs index 46f1d1948..bbc728e28 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/MotionSensor.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/MotionSensor.cs @@ -325,23 +325,18 @@ namespace Barotrauma.Items.Components private bool TriggersOn(Character character, bool triggerFromHumans, bool triggerFromPets, bool triggerFromMonsters) { if (IgnoreDead && character.IsDead) { return false; } - if (character.IsHuman) - { - if (!triggerFromHumans) { return false; } - } - else if (character.IsPet) + if (character.IsPet) { if (!triggerFromPets) { return false; } } + else if (character.IsHuman || CharacterParams.CompareGroup(character.Group, CharacterPrefab.HumanGroup)) + { + if (!triggerFromHumans) { return false; } + } else { // Not a human or a pet -> monster? if (!triggerFromMonsters) { return false; } - if (CharacterParams.CompareGroup(character.Group, CharacterPrefab.HumanGroup)) - { - //characters in the "human" group aren't considered monsters (even if they were something like a friendly mudraptor) - return false; - } } // Check matching character, if defined. if (targetCharacters.Any()) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs index bb4c8f643..2dd1fbd7f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs @@ -553,9 +553,9 @@ namespace Barotrauma.Items.Components return new List(nodes); } - public void SetNodes(List nodes) + public void SetNodes(IEnumerable nodes) { - this.nodes = new List(nodes); + this.nodes = nodes.ToList(); UpdateSections(); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/TriggerComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/TriggerComponent.cs index e9597ccf6..28728b077 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/TriggerComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/TriggerComponent.cs @@ -21,7 +21,7 @@ namespace Barotrauma.Items.Components public bool ForceFluctuation { get; set; } [Serialize(1.0f, IsPropertySaveable.Yes, description: "How much the fluctuation affects the force. 1 is the maximum fluctuation, 0 is no fluctuation.", alwaysUseInstanceValues: true)] - private float ForceFluctuationStrength + public float ForceFluctuationStrength { get { @@ -33,7 +33,7 @@ namespace Barotrauma.Items.Components } } [Serialize(1.0f, IsPropertySaveable.Yes, description: "How fast (cycles per second) the force fluctuates.", alwaysUseInstanceValues: true)] - private float ForceFluctuationFrequency + public float ForceFluctuationFrequency { get { @@ -45,7 +45,7 @@ namespace Barotrauma.Items.Components } } [Serialize(0.01f, IsPropertySaveable.Yes, description: "How often (in seconds) the force fluctuation is calculated.", alwaysUseInstanceValues: true)] - private float ForceFluctuationInterval + public float ForceFluctuationInterval { get { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Wearable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Wearable.cs index ded04cd5c..f352de4cb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Wearable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Wearable.cs @@ -542,6 +542,10 @@ namespace Barotrauma.Items.Components } IsActive = false; +#if CLIENT + //stop any sounds that may have been looping while wearing the item + StopSounds(ActionType.OnWearing); +#endif } public override void UpdateBroken(float deltaTime, Camera cam) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs index f121cecb0..868a625ab 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs @@ -430,8 +430,6 @@ namespace Barotrauma } } - public float RotationRad { get; private set; } - [ConditionallyEditable(ConditionallyEditable.ConditionType.AllowRotating, DecimalCount = 3, ForceShowPlusMinusButtons = true, ValueStep = 0.1f), Serialize(0.0f, IsPropertySaveable.Yes)] public float Rotation { @@ -706,6 +704,22 @@ namespace Barotrauma get; set; } + /// + /// Have the effects of the item already triggered when it was placed inside it's current container? + /// Used to prevent the effects from executing again when e.g. an existing character (who's inventory items' effects already triggered on some earlier round) spawns mid-round. + /// + [Serialize(false, IsPropertySaveable.Yes)] + public bool OnInsertedEffectsApplied + { + get; set; + } + + /// + /// Were the effects already been applied when the item first spawned (loaded from a save)? + /// Needed for communicating to the clients whether they should trigger when the item spawns. + /// + public bool OnInsertedEffectsAppliedOnPreviousRound; + public Color Color { get { return spriteColor; } @@ -766,7 +780,7 @@ namespace Barotrauma } [Serialize(false, IsPropertySaveable.Yes)] - private bool HasBeenInstantiatedOnce { get; set; } + public bool HasBeenInstantiatedOnce { get; set; } //the default value should be Prefab.Health, but because we can't use it in the attribute, //we'll just use NaN (which does nothing) and set the default value in the constructor/load @@ -808,9 +822,37 @@ namespace Barotrauma set; } + private bool? isDangerous; + /// + /// Bots avoid rooms with dangerous items in them. Normally this value is defined in the prefab, + /// but this property can be used to override the prefab value. + /// + public bool IsDangerous + { + get { return isDangerous ?? Prefab.IsDangerous; } + set + { + isDangerous = value; + if (!value) + { + _dangerousItems.Remove(this); + } + else + { + _dangerousItems.Add(this); + } + } + } + [Editable, Serialize(false, isSaveable: IsPropertySaveable.Yes, "When enabled will prevent the item from taking damage from all sources")] public bool InvulnerableToDamage { get; set; } + /// + /// Should bots automatically unequip the item? Normally always true, but disabled on items that have been configured to be equipped by default in an item set or the character's job items. + /// Note that the value is not saved: if the NPC becomes persistent (e.g. Artie Dolittle hired to the crew) they will no longer keep holding the item. + /// + public bool UnequipAutomatically = true; + /// /// Was the item stolen during the current round. Note that it's possible for the items to be found in the player's inventory even though they weren't actually stolen. /// For example, a guard can place handcuffs there. So use for checking if the item is illegitimately held. @@ -1085,6 +1127,9 @@ namespace Barotrauma public bool IsLadder { get; } + /// + /// Secondary items can be selected at the same time with a primary item (e.g. a ladder or a chair can be selected at the same time with some device). + /// public bool IsSecondaryItem { get; } private ItemStatManager statManager; @@ -1372,13 +1417,7 @@ namespace Barotrauma conditionMultiplierCampaign *= campaign.Settings.FuelMultiplier; } } - if (!HasBeenInstantiatedOnce) - { - // This only needs to be done on the very first instantiation. - // MaxCondition will be multiplied in RecalculateConditionValues(), ensuring - // that Condition will stay in line with the multiplier from then on. - condition *= conditionMultiplierCampaign; - } + condition *= conditionMultiplierCampaign; RecalculateConditionValues(); @@ -1481,13 +1520,14 @@ namespace Barotrauma { ItemComponent component = components[i], cloneComp = clone.components[i]; - - if (component is not CircuitBox origBox || cloneComp is not CircuitBox cloneBox) + if (component.GetType() == cloneComp.GetType()) { - continue; + cloneComp.Clone(component); + } + if (component is CircuitBox origBox && cloneComp is CircuitBox cloneBox) + { + cloneBox.CloneFrom(origBox, clonedContainedItems); } - - cloneBox.CloneFrom(origBox, clonedContainedItems); } clone.FullyInitialized = true; @@ -2296,6 +2336,7 @@ namespace Barotrauma private void SendPendingNetworkUpdatesInternal() { + DebugConsole.NewMessage($"Sending status event for item {Name}", Color.Gray); CreateStatusEvent(loadingRound: false); lastSentCondition = condition; sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval; @@ -2303,6 +2344,17 @@ namespace Barotrauma public void CreateStatusEvent(bool loadingRound) { + //A little hacky: clients aren't allowed to apply OnFire effects themselves, which means effects that rely on the "onfire" status tag + //won't work properly. But let's notify clients of the item being on fire when it breaks, so they can e.g. make tanks explode. + + //An alternative could be to allow clients to run OnFire effects, but I suspect it could lead to desyncs if/when there's minor + //discrepancies in the progress of the fires (which is most likely why running them was disabled on clients). + if (GameMain.NetworkMember is { IsServer: true } && + condition <= 0.0f && + StatusEffect.DurationList.Any(d => d.Targets.Contains(this) && d.Parent.HasTag(Barotrauma.Tags.OnFireStatusEffectTag))) + { + GameMain.NetworkMember.CreateEntityEvent(this, new ApplyStatusEffectEventData(ActionType.OnFire)); + } GameMain.NetworkMember.CreateEntityEvent(this, new ItemStatusEventData(loadingRound)); } @@ -2329,10 +2381,11 @@ namespace Barotrauma } private bool isActive = true; + public bool IsInRemoveQueue; public override void Update(float deltaTime, Camera cam) { - if (!isActive || IsLayerHidden) { return; } + if (!isActive || IsLayerHidden || IsInRemoveQueue) { return; } if (impactQueue != null) { @@ -2688,14 +2741,14 @@ namespace Barotrauma partial void OnCollisionProjSpecific(float impact); - public override void FlipX(bool relativeToSub) + public override void FlipX(bool relativeToSub, bool force = false) { //call the base method even if the item can't flip, to handle repositioning when flipping the whole sub base.FlipX(relativeToSub); - if (!Prefab.CanFlipX) + if (!Prefab.CanFlipX && !force) { - flippedX = false; + FlippedX = false; return; } @@ -2717,14 +2770,14 @@ namespace Barotrauma SetContainedItemPositions(); } - public override void FlipY(bool relativeToSub) + public override void FlipY(bool relativeToSub, bool force = false) { //call the base method even if the item can't flip, to handle repositioning when flipping the whole sub base.FlipY(relativeToSub); - if (!Prefab.CanFlipY) + if (!Prefab.CanFlipY && !force) { - flippedY = false; + FlippedY = false; return; } @@ -4075,6 +4128,10 @@ namespace Barotrauma } } + //store this at this point so we can tell the clients whether the effects had already been applied when the item was first loaded, + //(in which case a client should not execute them when they spawn the item) + item.OnInsertedEffectsAppliedOnPreviousRound = item.OnInsertedEffectsApplied; + item.ParseLinks(element, idRemap); bool thisIsOverride = element.GetAttributeBool("isoverride", false); @@ -4140,8 +4197,8 @@ namespace Barotrauma if (element.GetAttributeBool("markedfordeconstruction", false)) { _deconstructItems.Add(item); } float prevRotation = item.Rotation; - if (element.GetAttributeBool("flippedx", false)) { item.FlipX(false); } - if (element.GetAttributeBool("flippedy", false)) { item.FlipY(false); } + if (element.GetAttributeBool("flippedx", false)) { item.FlipX(relativeToSub: false, force: true); } + if (element.GetAttributeBool("flippedy", false)) { item.FlipY(relativeToSub: false, force: true); } item.Rotation = prevRotation; if (appliedSwap != null) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs index c98548641..3ae662440 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs @@ -5,6 +5,7 @@ using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Security.Cryptography; using System.Xml.Linq; @@ -132,10 +133,30 @@ namespace Barotrauma { public readonly Identifier ItemPrefabIdentifier; - public ItemPrefab ItemPrefab => - ItemPrefab.Prefabs.TryGet(ItemPrefabIdentifier, out var prefab) ? prefab - : MapEntityPrefab.FindByName(ItemPrefabIdentifier.Value) as ItemPrefab; - + [MaybeNull, AllowNull] + public ItemPrefab cachedItemPrefab; + + [MaybeNull, AllowNull] + private Md5Hash prevContentPackagesHash; + + [MaybeNull] + public ItemPrefab ItemPrefab + { + get + { + if (prevContentPackagesHash == null || + !prevContentPackagesHash.Equals(ContentPackageManager.EnabledPackages.MergedHash)) + { + cachedItemPrefab = ItemPrefab.Prefabs.TryGet(ItemPrefabIdentifier, out var prefab) + ? prefab + : MapEntityPrefab.FindByName(ItemPrefabIdentifier.Value) as ItemPrefab; + prevContentPackagesHash = ContentPackageManager.EnabledPackages.MergedHash; + } + + return cachedItemPrefab; + } + } + public override UInt32 UintIdentifier { get; } public override IEnumerable ItemPrefabs => ItemPrefab == null ? Enumerable.Empty() : ItemPrefab.ToEnumerable(); @@ -145,7 +166,7 @@ namespace Barotrauma public override bool MatchesItem(Item item) { - return item?.Prefab.Identifier == ItemPrefabIdentifier; + return item?.Prefab.Identifier == (ItemPrefab?.Identifier ?? ItemPrefabIdentifier); } public RequiredItemByIdentifier(Identifier itemPrefab, int amount, float minCondition, float maxCondition, bool useCondition, LocalizedString overrideDescription, LocalizedString overrideHeader) : @@ -711,6 +732,9 @@ namespace Barotrauma [Serialize(false, IsPropertySaveable.No, description: "Hides the condition displayed in the item's tooltip.")] public bool HideConditionInTooltip { get; set; } + [Serialize("", IsPropertySaveable.No, description: "If set, displays if the given fabrication recipe has been unlocked or not in the tooltip. The actual unlocking of the recipe should be handled in a status effect.")] + public Identifier UnlockedRecipeInToolTip { get; set; } + //if true and the item has trigger areas defined, characters need to be within the trigger to interact with the item //if false, trigger areas define areas that can be used to highlight the item [Serialize(true, IsPropertySaveable.No)] @@ -864,7 +888,7 @@ namespace Barotrauma [Serialize(10.0f, IsPropertySaveable.No)] public float MaxScale { get; private set; } - [Serialize(false, IsPropertySaveable.No)] + [Serialize(false, IsPropertySaveable.No, description: "Bots avoid rooms with dangerous items in them.")] public bool IsDangerous { get; private set; } private int maxStackSize; @@ -1285,6 +1309,11 @@ namespace Barotrauma this.LevelCommonness = levelCommonness.ToImmutableDictionary(); this.LevelQuantity = levelQuantity.ToImmutableDictionary(); + //flipping holdable items vertically is not properly supported (uses the orientation of the physics body, which depends on which direction the character holding the item is facing) + //so let's by default make the item non-flippable, but if there's some use case where the item needs to flip vertically, it can be enabled by explicitly defining it in the XML. + bool canFlipYByDefault = ConfigElement.GetChildElement(nameof(Holdable)) == null; + CanFlipY = ConfigElement.GetAttributeBool(nameof(CanFlipY), def: canFlipYByDefault); + // Backwards compatibility if (storePrices.Any()) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/CoreEntityPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/CoreEntityPrefab.cs index 193e7fdf0..e6b4bc5ab 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/CoreEntityPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/CoreEntityPrefab.cs @@ -34,6 +34,7 @@ namespace Barotrauma this.Linkable = linkable; this.AllowedLinks = (allowedLinks ?? Enumerable.Empty()).ToImmutableHashSet(); this.Aliases = (aliases ?? Enumerable.Empty()).Concat(identifier.Value.ToEnumerable()).ToImmutableHashSet(); + this.Scale = 1; } public static CoreEntityPrefab HullPrefab { get; private set; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs index b1bee1910..a06585116 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs @@ -570,11 +570,11 @@ namespace Barotrauma.MapCreatures.Behavior if (HasBrokenThrough) { - // I wasn't 100% sure what the performance impact on this so I decide to limit it to only check every 5 seconds + // I wasn't 100% sure what the performance impact on this so I decide to limit it to only check every 10 seconds if (fireCheckCooldown <= 0) { UpdateFireSources(); - fireCheckCooldown = 5f; + fireCheckCooldown = 10f; } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/DummyFireSource.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/DummyFireSource.cs index 80428221a..ce25137dc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/DummyFireSource.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/DummyFireSource.cs @@ -8,6 +8,8 @@ namespace Barotrauma private Vector2 maxSize; public bool CausedByPsychosis; + + protected override float SpreadToOtherHullsProbability => 0.0f; public DummyFireSource(Vector2 maxSize, Vector2 worldPosition, Hull spawningHull = null, bool isNetworkMessage = false) : base(worldPosition, spawningHull, sourceCharacter: null, isNetworkMessage: isNetworkMessage) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/FireSource.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/FireSource.cs index c1f8f335d..9f32878dd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/FireSource.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/FireSource.cs @@ -22,10 +22,11 @@ namespace Barotrauma /// How often the FireSource checks whether it can spread to nearby hulls. /// const float SpreadToOtherHullsInterval = 5.0f; + /// /// The probability of the fire spreading to a nearby hull when the check is made. /// - const float SpreadToOtherHullsProbability = 0.15f; + protected virtual float SpreadToOtherHullsProbability => 0.15f; protected Hull hull; @@ -252,11 +253,14 @@ namespace Barotrauma LimitSize(); - spreadToOtherHullsTimer -= deltaTime; - if (spreadToOtherHullsTimer <= 0.0f) + if (SpreadToOtherHullsProbability > 0.0f) { - TrySpreadToNearbyHulls(); - spreadToOtherHullsTimer = SpreadToOtherHullsInterval; + spreadToOtherHullsTimer -= deltaTime; + if (spreadToOtherHullsTimer <= 0.0f) + { + TrySpreadToNearbyHulls(); + spreadToOtherHullsTimer = SpreadToOtherHullsInterval; + } } if (size.X > 256.0f && this is not DummyFireSource) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs index b248fb7d9..94ff48094 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs @@ -1,4 +1,5 @@ -using Barotrauma.Items.Components; +using Barotrauma.Extensions; +using Barotrauma.Items.Components; using FarseerPhysics; using FarseerPhysics.Dynamics; using Microsoft.Xna.Framework; @@ -834,13 +835,14 @@ namespace Barotrauma foreach (var connectedGap in hull.ConnectedGaps) { if (connectedGap == this) { continue; } + if (connectedGap.IsRoomToRoom != IsRoomToRoom) { continue; } //let the "more open" gap reduce this gap's flow rate //or if they're both equally open, let the one that was created first handle it //(note that we can't use Entity.ID here because gaps on walls don't have IDs) if (connectedGap.open > open || (connectedGap.open == open && connectedGap.CreationIndex < CreationIndex)) { - Rectangle intersection = Rectangle.Intersect(rect, connectedGap.rect); + Rectangle intersection = Rectangle.Intersect(rect.ToWorldRect(), connectedGap.rect.ToWorldRect()); if (intersection.Width > 0 && intersection.Height > 0) { //reduce flow rate based on how much of this gap is covered by the connected one, and how open the connected one is @@ -848,6 +850,7 @@ namespace Barotrauma intersection.Height / (float)rect.Height : intersection.Width / (float)rect.Width; overlappingGapFlowRateReduction += relativeOverlap * connectedGap.open; + overlappingGaps.Add(connectedGap); } } if (overlappingGapFlowRateReduction >= 1.0f) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs index 4e87e96d9..dce43c267 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs @@ -442,6 +442,8 @@ namespace Barotrauma /// public bool IsBlue => ColorExtensions.IsBlueDominant(AveragePaintedColor, minimumAlpha: 100); + public const int MaxFireSources = 16; + public List FireSources { get; private set; } public List FakeFireSources { get; private set; } @@ -731,6 +733,10 @@ namespace Barotrauma } else { + if (FireSources.Count >= MaxFireSources) + { + return; + } FireSources.Add(fireSource); } } @@ -781,8 +787,9 @@ namespace Barotrauma { msg.WriteRangedSingle(MathHelper.Clamp(waterVolume / Volume, 0.0f, 1.5f), 0.0f, 1.5f, 8); - msg.WriteRangedInteger(Math.Min(FireSources.Count, 16), 0, 16); - for (int i = 0; i < Math.Min(FireSources.Count, 16); i++) + System.Diagnostics.Debug.Assert(FireSources.Count <= MaxFireSources, $"Too many fire sources ({FireSources.Count}) in hull {ID} (max {MaxFireSources})."); + msg.WriteRangedInteger(Math.Min(FireSources.Count, MaxFireSources), 0, MaxFireSources); + for (int i = 0; i < Math.Min(FireSources.Count, MaxFireSources); i++) { var fireSource = FireSources[i]; Vector2 normalizedPos = new Vector2( @@ -828,7 +835,7 @@ namespace Barotrauma { newWaterVolume = msg.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; - int fireSourceCount = msg.ReadRangedInteger(0, 16); + int fireSourceCount = msg.ReadRangedInteger(0, MaxFireSources); newFireSources = new NetworkFireSource[fireSourceCount]; for (int i = 0; i < fireSourceCount; i++) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Biome.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Biome.cs index 8dbc714ea..402f64bae 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Biome.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Biome.cs @@ -2,6 +2,7 @@ using Barotrauma.Extensions; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; namespace Barotrauma { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs index 30132cf6c..45f5c4244 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs @@ -4481,6 +4481,10 @@ namespace Barotrauma } } } + + bool onlyEntrance = LevelData.Type != LevelData.LevelType.Outpost; + + LocationType locationType = location?.Type; if (missionForcedOutpostParamsId != null && OutpostGenerationParams.OutpostParams.TryGet(missionForcedOutpostParamsId, out var missionForcedOutpostParams)) { @@ -4490,6 +4494,13 @@ namespace Barotrauma { outpostGenerationParams = LevelData.ForceOutpostGenerationParams; } + else if (locationType != null && + locationType.GetForcedOutpostGenerationParams() is { } forcedOutpostGenerationParams && + //do not use the forced parameters if we want to generate only the entrance, and the parameters define a full, pre-built outpost + (!onlyEntrance || forcedOutpostGenerationParams.OutpostFilePath.IsNullOrEmpty())) + { + outpostGenerationParams = forcedOutpostGenerationParams; + } else { outpostGenerationParams = @@ -4497,7 +4508,6 @@ namespace Barotrauma LevelData.GetSuitableOutpostGenerationParams(location, LevelData).GetRandom(Rand.RandSync.ServerAndClient); } - LocationType locationType = location?.Type; if (locationType == null) { locationType = LocationType.Prefabs.GetRandom(Rand.RandSync.ServerAndClient); @@ -4512,7 +4522,7 @@ namespace Barotrauma if (location != null) { DebugConsole.NewMessage($"Generating an outpost for the {(isStart ? "start" : "end")} of the level... (Location: {location.DisplayName}, level type: {LevelData.Type})"); - outpost = OutpostGenerator.Generate(outpostGenerationParams, location, onlyEntrance: LevelData.Type != LevelData.LevelType.Outpost, LevelData.AllowInvalidOutpost); + outpost = OutpostGenerator.Generate(outpostGenerationParams, location, onlyEntrance: onlyEntrance, LevelData.AllowInvalidOutpost); } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelData.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelData.cs index db21e3a4e..03e37859c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelData.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelData.cs @@ -370,19 +370,36 @@ namespace Barotrauma public static IEnumerable GetSuitableOutpostGenerationParams(Location location, LevelData levelData) { - var suitableParams = OutpostGenerationParams.OutpostParams - .Where(p => p.LevelType == null || levelData.Type == p.LevelType) + var paramsForGameMode = OutpostGenerationParams.OutpostParams.Where(p => + p.AllowedGameModeIdentifiers.None() || GameMain.GameSession?.GameMode is not GameMode gameMode || p.AllowedGameModeIdentifiers.Contains(gameMode.Preset.Identifier)); + + var paramsWithMatchingLevelType = paramsForGameMode + .Where(p => p.LevelType == null || levelData.Type == p.LevelType); + + //1. try finding params specifically for this location type + var suitableParams = paramsWithMatchingLevelType .Where(p => location == null || p.AllowedLocationTypes.Contains(location.Type.Identifier)); if (!suitableParams.Any()) { - suitableParams = OutpostGenerationParams.OutpostParams - .Where(p => p.LevelType == null || levelData.Type == p.LevelType) - .Where(p => location == null || !p.AllowedLocationTypes.Any()); + //2. not found, if the location type is configured to use the modules of some other location type, + // see if we could use that location type's generation params + if (!location.Type.UseOutpostModulesOfLocationType.IsEmpty) + { + suitableParams = paramsWithMatchingLevelType + .Where(p => p.AllowedLocationTypes.Contains(location.Type.UseOutpostModulesOfLocationType)); + } if (!suitableParams.Any()) { - DebugConsole.ThrowError($"No suitable outpost generation parameters found for the location type \"{location.Type.Identifier}\". Selecting random parameters."); - suitableParams = OutpostGenerationParams.OutpostParams; + //3. still not found, choose some parameters that are suitable for any location type + suitableParams = paramsWithMatchingLevelType + .Where(p => location == null || !p.AllowedLocationTypes.Any()); + if (!suitableParams.Any()) + { + DebugConsole.ThrowError($"No suitable outpost generation parameters found for the location type \"{location.Type.Identifier}\". Selecting random parameters."); + suitableParams = paramsForGameMode; + } } + } return suitableParams; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Ruins/RuinGenerator.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Ruins/RuinGenerator.cs index 4d4d7ab3b..015787cab 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Ruins/RuinGenerator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Ruins/RuinGenerator.cs @@ -37,7 +37,7 @@ namespace Barotrauma.RuinGeneration public void Generate(Level level, LocationType locationType, Point position, bool mirror = false) { - Submarine = OutpostGenerator.Generate(generationParams, locationType, onlyEntrance: false); + Submarine = OutpostGenerator.Generate(generationParams, locationType, onlyEntrance: false, allowInvalidOutpost: level.LevelData.AllowInvalidOutpost); Submarine.Info.Name = $"Ruin ({level.Seed})"; Submarine.Info.Type = SubmarineType.Ruin; Submarine.TeamID = CharacterTeamType.None; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs index 29ddeb2e4..95d602dce 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs @@ -478,6 +478,11 @@ namespace Barotrauma } } + /// + /// Missions that are available and visible in menus () + /// + public IEnumerable AvailableAndVisibleMissions => AvailableMissions.Where(m => m.Prefab.ShowInMenus); + private readonly List selectedMissions = new List(); public IEnumerable SelectedMissions { @@ -580,9 +585,9 @@ namespace Barotrauma return $"Location ({DisplayName ?? "null"})"; } - public Location(Vector2 mapPosition, int? zone, Random rand, bool requireOutpost = false, LocationType forceLocationType = null, IEnumerable existingLocations = null) + public Location(Vector2 mapPosition, int? zone, Identifier? biomeId, Random rand, bool requireOutpost = false, LocationType forceLocationType = null, IEnumerable existingLocations = null) { - Type = OriginalType = forceLocationType ?? LocationType.Random(rand, zone, requireOutpost); + Type = OriginalType = forceLocationType ?? LocationType.Random(rand, zone, biomeId, requireOutpost); CreateRandomName(Type, rand, existingLocations); MapPosition = mapPosition; PortraitId = ToolBox.StringToInt(nameIdentifier.Value); @@ -782,12 +787,12 @@ namespace Barotrauma } } - public static Location CreateRandom(Vector2 position, int? zone, Random rand, bool requireOutpost, LocationType forceLocationType = null, IEnumerable existingLocations = null) + public static Location CreateRandom(Vector2 position, int? zone, Identifier? biomeId, Random rand, bool requireOutpost, LocationType forceLocationType = null, IEnumerable existingLocations = null) { - return new Location(position, zone, rand, requireOutpost, forceLocationType, existingLocations); + return new Location(position, zone, biomeId, rand, requireOutpost, forceLocationType, existingLocations); } - public void ChangeType(CampaignMode campaign, LocationType newType, bool createStores = true) + public void ChangeType(CampaignMode campaign, LocationType newType, bool createStores = true, bool unlockInitialMissions = true) { if (newType == Type) { return; } @@ -826,10 +831,10 @@ namespace Barotrauma if (Type.Faction == Identifier.Empty) { Faction = null; } if (Type.SecondaryFaction == Identifier.Empty) { SecondaryFaction = null; } } - - if (!IsCriticallyRadiated()) + + if (unlockInitialMissions && !IsCriticallyRadiated()) { - UnlockInitialMissions(Rand.RandSync.Unsynced); + UnlockInitialMissions(Rand.RandSync.Unsynced); } if (createStores) @@ -1257,7 +1262,7 @@ namespace Barotrauma { if (type?.NameFormats == null || !type.NameFormats.Any() || nameFormatIndex < 0) { - return TextManager.Get(nameId); + return TextManager.Get(nameId).Fallback(nameId.Value); } return type.NameFormats[nameFormatIndex % type.NameFormats.Count].Replace("[name]", TextManager.Get(nameId).Value); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs index 3c9960488..710397b4a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs @@ -13,7 +13,7 @@ namespace Barotrauma class LocationType : PrefabWithUintIdentifier { public static readonly PrefabCollection Prefabs = new PrefabCollection(); - + private readonly ImmutableArray rawNames; private readonly ImmutableArray portraits; @@ -21,13 +21,14 @@ namespace Barotrauma private readonly ImmutableArray<(Identifier Identifier, float Commonness, bool AlwaysAvailableIfMissingFromCrew)> hireableJobs; private readonly float totalHireableWeight; - public readonly Dictionary CommonnessPerZone = new Dictionary(); - public readonly Dictionary MinCountPerZone = new Dictionary(); - public readonly LocalizedString Name; + public readonly LocalizedString Description; - public readonly Identifier ForceLocationName; + /// + /// Forces all locations of this LocationType to use the given name. Can either be a text tag or the actual name. + /// + public readonly Identifier ForceLocationName; public readonly float BeaconStationChance; @@ -43,11 +44,151 @@ namespace Barotrauma public readonly ImmutableArray MissionIdentifiers; public readonly ImmutableArray MissionTags; - public readonly List HideEntitySubcategories = new List(); + public abstract class AreaSettingData + { + public int? MinCount { get; } + public int? MaxCount { get; } + public float Commonness { get; } - public bool IsEnterable { get; private set; } + /// + /// Desired position in the biome or difficulty zone. A value between 0 and 1, where 0 is the left side of the zone/biome, and 1 the right side. + /// + public float? DesiredPosition { get; } + + public bool HasCounts => MinCount.HasValue && MaxCount.HasValue && (MaxCount > 0 || MinCount > 0); + public virtual bool HasValidData => true; - public bool AllowAsBiomeGate { get; private set; } + internal AreaSettingData(int? minCount, int? maxCount, float commonness, float? desiredPosition) + { + MinCount = minCount; + MaxCount = maxCount; + Commonness = commonness; + DesiredPosition = desiredPosition; + } + + public virtual bool MatchesRemainingCount(MapLocationTypeGenerator.LocationTypeCount locationTypeCount) + { + return false; + } + + public virtual bool MatchesLocation(Map map, Location location) + { + return false; + } + + public virtual bool MatchesZone(int zoneIndex) + { + return false; + } + + public virtual bool MatchesBiome(Identifier biomeIdentifier) + { + return false; + } + + public virtual bool Matches(int? zone = null, Identifier? biomeId = null) + { + return false; + } + } + + public class BiomeSettingData : AreaSettingData + { + public Identifier BiomeIdentifier { get; } + + public override bool HasValidData => Biome.Prefabs.ContainsKey(BiomeIdentifier); + + public BiomeSettingData(Identifier biomeIdentifier, int? minCount, int? maxCount, float commonness, float? desiredPosition, LocationType locationType) + : base(minCount, maxCount, commonness, desiredPosition) + { + if (minCount > maxCount) + { + DebugConsole.AddWarning($"Error in location type {locationType.Identifier}: minimum count larger than maximum count in biome {biomeIdentifier}.", + contentPackage: locationType.ContentPackage); + } + BiomeIdentifier = biomeIdentifier; + } + + public override bool MatchesRemainingCount(MapLocationTypeGenerator.LocationTypeCount locationTypeCount) + { + return locationTypeCount.BiomeId == BiomeIdentifier; + } + + public override bool MatchesLocation(Map map, Location location) + { + return BiomeIdentifier == location.Biome?.Identifier; + } + + public override bool MatchesBiome(Identifier biomeIdentifier) + { + return BiomeIdentifier == biomeIdentifier; + } + + public override bool Matches(int? zone = null, Identifier? biomeId = null) + { + return biomeId.HasValue && biomeId == BiomeIdentifier; + } + } + + public class DifficultyZoneSettingData : AreaSettingData + { + public int DifficultyZone { get; } + + public DifficultyZoneSettingData(int difficultyZone, int? minCount, int? maxCount, float commonness, float? desiredPosition, LocationType locationType) + : base(minCount, maxCount, commonness, desiredPosition) + { + if (minCount > maxCount) + { + DebugConsole.AddWarning($"Error in location type {locationType.Identifier}: minimum count larger than maximum count in difficulty zone {difficultyZone}.", + contentPackage: locationType.ContentPackage); + } + DifficultyZone = difficultyZone; + } + + public override bool MatchesRemainingCount(MapLocationTypeGenerator.LocationTypeCount locationTypeCount) + { + return locationTypeCount.DifficultyZone == DifficultyZone; + } + + public override bool MatchesLocation(Map map, Location location) + { + return DifficultyZone == map.GetZoneIndex(location.MapPosition.X); + } + + public override bool MatchesZone(int zoneIndex) + { + return DifficultyZone == zoneIndex; + } + + public override bool Matches(int? zone = null, Identifier? biomeId = null) + { + return zone.HasValue && zone == DifficultyZone; + } + } + + public readonly List AreaSettings = new List(); + + public readonly List HideEntitySubcategories; + + public enum BiomeGateSetting + { + /// + /// Can be used as a gate between biomes, but not required + /// + Allow, + /// + /// Cannot be used as a gate between biomes + /// + Deny, + /// + /// Must be used as a gate between biomes during map generation + /// + Force + } + + public BiomeGateSetting BiomeGate { get; private set; } + + public bool ForceAsStartOutpost { get; private set; } /// /// Can this location type be used in the random, non-campaign levels that don't take place in any specific zone @@ -108,10 +249,26 @@ namespace Barotrauma private readonly Identifier forceOutpostGenerationParamsIdentifier; + /// + /// Can be used to make the location type use the same background music tracks as another location type. + /// + public readonly Identifier BackgroundMusicLocationType; + /// /// If set to true, only event sets that explicitly define this location type in can be selected at this location. Defaults to false. /// public bool IgnoreGenericEvents { get; } + + /// + /// Used as criteria for validating if a given event set is suitable for this locationType. + /// For example, if set to "city", events that appear in "city" type locations can also appear here. + /// + public Identifier EventLocationType { get; private set; } + + /// + /// If set, outpost modules configured to be suitable for the specified location type can also be used in this type of location. + /// + public Identifier UseOutpostModulesOfLocationType { get; set; } public Color SpriteColor { @@ -134,7 +291,7 @@ namespace Barotrauma public int DailySpecialsCount { get; } = 1; public int RequestedGoodsCount { get; } = 1; - public readonly bool ShowSonarMarker = true; + public readonly bool ShowSonarMarker; public override string ToString() { @@ -145,13 +302,41 @@ namespace Barotrauma { Name = TextManager.Get("LocationName." + Identifier, "unknown"); Description = TextManager.Get("LocationDescription." + Identifier, ""); - + + // for location types based on others, e.g., Named Unique outpost, we may want to override the name of the type to still say Outpost on the map: + var forceNameId = element.GetAttributeIdentifier("ForceLocationTypeName", string.Empty); + if (!forceNameId.IsEmpty) + { + var forcedName = TextManager.Get("LocationName." + forceNameId); + if (!forcedName.IsNullOrEmpty()) + { + Name = forcedName; + } + } + + var forceDescriptionId = element.GetAttributeIdentifier("ForceLocationTypeDescription", string.Empty); + if (!forceDescriptionId.IsEmpty) + { + var forcedDescription = TextManager.Get("LocationDescription." + forceDescriptionId); + if (!forcedDescription.IsNullOrEmpty()) + { + Description = forcedDescription; + } + } + BeaconStationChance = element.GetAttributeFloat("beaconstationchance", 0.0f); UsePortraitInRandomLoadingScreens = element.GetAttributeBool(nameof(UsePortraitInRandomLoadingScreens), true); HasOutpost = element.GetAttributeBool("hasoutpost", true); - IsEnterable = element.GetAttributeBool("isenterable", HasOutpost); - AllowAsBiomeGate = element.GetAttributeBool(nameof(AllowAsBiomeGate), true); + bool allowAsBiomeGateLegacy = element.GetAttributeBool("allowasbiomegate", true); + BiomeGate = element.GetAttributeEnum("BiomeGate", def: allowAsBiomeGateLegacy ? BiomeGateSetting.Allow : BiomeGateSetting.Deny); + if (BiomeGate != BiomeGateSetting.Deny && !HasOutpost) + { + DebugConsole.AddWarning($"Potential error in location type {Identifier}: the location is set to be allowed as a biome gate, but will never be chosen as one because it has no outpost.", + contentPackage: ContentPackage); + } + + ForceAsStartOutpost = element.GetAttributeBool(nameof(ForceAsStartOutpost), false); AllowInRandomLevels = element.GetAttributeBool(nameof(AllowInRandomLevels), true); Faction = element.GetAttributeIdentifier(nameof(Faction), Identifier.Empty); @@ -168,17 +353,24 @@ namespace Barotrauma DescriptionInRadiation = element.GetAttributeIdentifier(nameof(DescriptionInRadiation), "locationdescription.abandonedirradiated"); forceOutpostGenerationParamsIdentifier = element.GetAttributeIdentifier("forceoutpostgenerationparams", Identifier.Empty); + BackgroundMusicLocationType = element.GetAttributeIdentifier(nameof(BackgroundMusicLocationType), Identifier.Empty); IgnoreGenericEvents = element.GetAttributeBool(nameof(IgnoreGenericEvents), false); + + EventLocationType = element.GetAttributeIdentifier(nameof(EventLocationType), Identifier.Empty); + UseOutpostModulesOfLocationType = element.GetAttributeIdentifier(nameof(UseOutpostModulesOfLocationType), Identifier.Empty); IsAnyOutpost = element.GetAttributeBool(nameof(IsAnyOutpost), def: HasOutpost); string teamStr = element.GetAttributeString("outpostteam", "FriendlyNPC"); Enum.TryParse(teamStr, out OutpostTeam); - if (element.GetAttribute("name") != null) + if (element.GetAttribute(nameof(ForceLocationName)) != null || + element.GetAttribute("name") != null) { - ForceLocationName = element.GetAttributeIdentifier("name", string.Empty); + ForceLocationName = element.GetAttributeIdentifier(nameof(ForceLocationName), + //backwards compatibility + def: element.GetAttributeIdentifier("name", string.Empty)); } else { @@ -214,15 +406,18 @@ namespace Barotrauma string[] commonnessPerZoneStrs = element.GetAttributeStringArray("commonnessperzone", Array.Empty()); foreach (string commonnessPerZoneStr in commonnessPerZoneStrs) { - string[] splitCommonnessPerZone = commonnessPerZoneStr.Split(':'); + string[] splitCommonnessPerZone = commonnessPerZoneStr.Split(':'); if (splitCommonnessPerZone.Length != 2 || !int.TryParse(splitCommonnessPerZone[0].Trim(), out int zoneIndex) || !float.TryParse(splitCommonnessPerZone[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out float zoneCommonness)) { - DebugConsole.ThrowError("Failed to read commonness values for location type \"" + Identifier + "\" - commonness should be given in the format \"zone1index: zone1commonness, zone2index: zone2commonness\""); + DebugConsole.ThrowError("Failed to read commonness values for location type \"" + Identifier + "\" - commonness should be given in the format \"zone1index: zone1commonness, zone2index: zone2commonness\"", contentPackage: element.ContentPackage); break; } - CommonnessPerZone[zoneIndex] = zoneCommonness; + + if (zoneCommonness <= 0.0f) { continue; } + + AugmentDifficultyZoneSettings(zoneIndex, zoneCommonness, minCount: null); } string[] minCountPerZoneStrs = element.GetAttributeStringArray("mincountperzone", Array.Empty()); @@ -233,13 +428,40 @@ namespace Barotrauma !int.TryParse(splitMinCountPerZone[0].Trim(), out int zoneIndex) || !int.TryParse(splitMinCountPerZone[1].Trim(), out int minCount)) { - DebugConsole.ThrowError("Failed to read minimum count values for location type \"" + Identifier + "\" - minimum counts should be given in the format \"zone1index: zone1mincount, zone2index: zone2mincount\""); + DebugConsole.ThrowError("Failed to read minimum zone count values for location type \"" + Identifier + + "\" - minimum zone counts should be given in the format \"zone1index: zone1mincount, zone2index: zone2mincount\"", contentPackage: element.ContentPackage); break; } - MinCountPerZone[zoneIndex] = minCount; + + if (minCount <= 0) { continue; } + + AugmentDifficultyZoneSettings(zoneIndex, zoneCommonness: null, minCount); } - var portraits = new List(); - var hireableJobs = new List<(Identifier, float, bool)>(); + + void AugmentDifficultyZoneSettings(int zoneIndex, float? zoneCommonness, int? minCount) + { + + var existingSettings = AreaSettings.Find(areaSettingData => areaSettingData is DifficultyZoneSettingData difficultyZoneSettingData && + difficultyZoneSettingData.DifficultyZone == zoneIndex); + + if (existingSettings != null) + { + int index = AreaSettings.IndexOf(existingSettings); + AreaSettings[index] = new DifficultyZoneSettingData(zoneIndex, + minCount ?? existingSettings.MinCount, + //note that assigning minCount to maxCount is intentional here: + //previously it was only possible to define minCount (essentially the same as just defining "count" now) + maxCount: minCount ?? existingSettings.MaxCount, + commonness: zoneCommonness ?? existingSettings.Commonness, desiredPosition: null, locationType: this); + } + else + { + AreaSettings.Add(new DifficultyZoneSettingData(zoneIndex, minCount ?? 0, maxCount: minCount ?? 0, commonness: zoneCommonness ?? 0, desiredPosition: null, locationType: this)); + } + } + + var portraitsList = new List(); + var hireableJobsList = new List<(Identifier, float, bool)>(); foreach (var subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) @@ -249,7 +471,7 @@ namespace Barotrauma float jobCommonness = subElement.GetAttributeFloat("commonness", 1.0f); bool availableIfMissing = subElement.GetAttributeBool("AlwaysAvailableIfMissingFromCrew", false); totalHireableWeight += jobCommonness; - hireableJobs.Add((jobIdentifier, jobCommonness, availableIfMissing)); + hireableJobsList.Add((jobIdentifier, jobCommonness, availableIfMissing)); break; case "symbol": Sprite = new Sprite(subElement, lazyLoad: true); @@ -265,7 +487,7 @@ namespace Barotrauma var portrait = new Sprite(subElement, lazyLoad: true); if (portrait != null) { - portraits.Add(portrait); + portraitsList.Add(portrait); } break; case "store": @@ -281,10 +503,71 @@ namespace Barotrauma DailySpecialsCount = subElement.GetAttributeInt("dailyspecialscount", DailySpecialsCount); RequestedGoodsCount = subElement.GetAttributeInt("requestedgoodscount", RequestedGoodsCount); break; + case "areasettings": + ParseAreaSettings(subElement); + break; + } + } + this.portraits = portraitsList.ToImmutableArray(); + this.hireableJobs = hireableJobsList.ToImmutableArray(); + + void ParseAreaSettings(ContentXElement areaSettingsElement) + { + Identifier biomeIdentifier = areaSettingsElement.GetAttributeIdentifier("biome", Identifier.Empty); + int zone = areaSettingsElement.GetAttributeInt("zone", 0); + + if (biomeIdentifier == Identifier.Empty && zone == 0) + { + DebugConsole.ThrowError("Failed to read area settings for locationType \"" + Identifier + "\" - biome identifier and zone are both missing.", contentPackage: element.ContentPackage); + return; + } + + if (biomeIdentifier != Identifier.Empty && zone != 0) + { + DebugConsole.ThrowError("Failed to read area settings for locationType \"" + Identifier + "\" - both biome identifier and zone are defined. Must be one or the other.", contentPackage: element.ContentPackage); + return; + } + + bool HasComma(string intAttributeName) + { + var attr = areaSettingsElement.GetAttribute(intAttributeName); + if (attr == null) { return false;} + return attr.Value.Contains(','); + } + + if (HasComma("mincount") || HasComma("maxcount") || HasComma("count")) + { + DebugConsole.LogError($"AreaSettings for locationType {Identifier} has comma inside int count attribute. This causes the resulting parse to combine the numbers, resulting in incorrect amount of locations.", + contentPackage: ContentPackage); + } + + int? minCount = areaSettingsElement.GetAttributeNullableInt("mincount"); + int? maxCount = areaSettingsElement.GetAttributeNullableInt("maxcount"); + int? count = areaSettingsElement.GetAttributeNullableInt("count"); + float? desiredPosition = areaSettingsElement.GetAttributeNullableFloat("desiredposition"); + float commonness = areaSettingsElement.GetAttributeFloat("commonness", 0); + + // if set, count overrides min and max count to eliminate randomness + if (count.HasValue) + { + minCount = count; + maxCount = count; + } + else if (minCount.HasValue && maxCount.HasValue && minCount <= 0 && maxCount <= 0) + { + DebugConsole.AddWarning("Failed to read count value for location type \"" + Identifier + "\" - both min and max count are 0.", contentPackage: element.ContentPackage); + return; + } + + if (biomeIdentifier != Identifier.Empty) + { + AreaSettings.Add(new BiomeSettingData(biomeIdentifier, minCount, maxCount, commonness, desiredPosition, locationType: this)); + } + else + { + AreaSettings.Add(new DifficultyZoneSettingData(zone, minCount, maxCount, commonness, desiredPosition, locationType: this)); } } - this.portraits = portraits.ToImmutableArray(); - this.hireableJobs = hireableJobs.ToImmutableArray(); } public IEnumerable GetHireablesMissingFromCrew() @@ -383,7 +666,7 @@ namespace Barotrauma return rawNames[rand.Next() % rawNames.Length]; } - public static LocationType Random(Random rand, int? zone = null, bool requireOutpost = false, Func predicate = null) + public static LocationType Random(Random rand, int? zone = null, Identifier? biomeId = null, bool requireOutpost = false, Func predicate = null) { Debug.Assert(Prefabs.Any(), "LocationType.list.Count == 0, you probably need to initialize LocationTypes"); @@ -392,19 +675,22 @@ namespace Barotrauma (predicate == null || predicate(lt)) && IsValid(lt)) .OrderBy(p => p.UintIdentifier).ToArray(); - bool IsValid(LocationType lt) + bool IsValid(LocationType locationType) { - if (requireOutpost && !lt.HasOutpost) { return false; } - if (zone.HasValue) - { - if (!lt.CommonnessPerZone.ContainsKey(zone.Value)) { return false; } - } + if (requireOutpost && !locationType.HasOutpost) { return false; } + + bool validZone = !zone.HasValue || locationType.AreaSettings.Any(areaSetting => areaSetting.MatchesZone(zone.Value)); + bool validBiome = !biomeId.HasValue || locationType.AreaSettings.Any(areaSetting => areaSetting.MatchesBiome(biomeId.Value)); + + if (!validZone && !validBiome) { return false; } + //if zone is not defined, this is a "random" (non-campaign) level //-> don't choose location types that aren't allowed in those - else if (!lt.AllowInRandomLevels) + if (!zone.HasValue && !biomeId.HasValue && !locationType.AllowInRandomLevels) { return false; } + return true; } @@ -413,11 +699,12 @@ namespace Barotrauma DebugConsole.ThrowError("Could not generate a random location type - no location types for the zone " + zone + " found!"); } - if (zone.HasValue) + if (zone.HasValue || biomeId.HasValue) { return ToolBox.SelectWeightedRandom( allowedLocationTypes, - allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToArray(), + allowedLocationTypes.Select(allowedType => + allowedType.AreaSettings.Find(areaSetting => areaSetting.MatchesZone(zone.Value) || areaSetting.MatchesBiome(biomeId.Value))?.Commonness ?? 0).ToArray(), rand); } else @@ -425,6 +712,17 @@ namespace Barotrauma return allowedLocationTypes[rand.Next() % allowedLocationTypes.Length]; } } + + public bool IsValidForZoneOrBiome(int? zone, Identifier? biomeIdentifier) + { + //if zone is not defined, this is a "random" (non-campaign) level + //-> don't choose location types that aren't allowed in those + if (!zone.HasValue && !AllowInRandomLevels) { return false; } + + if (!zone.HasValue && !biomeIdentifier.HasValue) { return true; } + + return AreaSettings.Any(setting => setting.Matches(zone, biomeIdentifier)); + } public OutpostGenerationParams GetForcedOutpostGenerationParams() { @@ -434,6 +732,11 @@ namespace Barotrauma } return null; } + + public bool HasCounts() + { + return AreaSettings.Any(setting => setting.HasCounts); + } public override void Dispose() { } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs index 7565080b8..dbfb5ece0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs @@ -77,6 +77,9 @@ namespace Barotrauma public Radiation Radiation; private bool trackedLocationDiscoveryAndVisitOrder = true; + + private IOrderedEnumerable _orderedBiomes; + public IOrderedEnumerable OrderedBiomes => _orderedBiomes ??= Biome.Prefabs.GetOrdered(); public Map(CampaignSettings settings) { @@ -240,10 +243,7 @@ namespace Barotrauma Vector2 mapPos = new Vector2( MathHelper.Lerp(firstEndLocation.MapPosition.X, Width, MathHelper.Lerp(0.2f, 0.8f, i / (float)missingOutpostCount)), Height * MathHelper.Lerp(0.2f, 1.0f, (float)rand.NextDouble())); - var newEndLocation = new Location(mapPos, generationParams.DifficultyZones, rand, forceLocationType: firstEndLocation.Type, existingLocations: Locations) - { - Biome = endLocations.First().Biome - }; + var newEndLocation = new Location(mapPos, generationParams.DifficultyZones, firstEndLocation.Biome.Identifier, rand, forceLocationType: firstEndLocation.Type, existingLocations: Locations); newEndLocation.LevelData = new LevelData(newEndLocation, this, difficulty: 100.0f); Locations.Add(newEndLocation); endLocations.Add(newEndLocation); @@ -365,6 +365,14 @@ namespace Barotrauma { CurrentLocation.ChangeType(campaign, tutorialOutpost); } + else + { + var forceStartOutpostType = LocationType.Prefabs.Where(lt => lt.ForceAsStartOutpost).GetRandom(Rand.RandSync.ServerAndClient); + if (forceStartOutpostType != null) + { + CurrentLocation.ChangeType(campaign, forceStartOutpostType); + } + } Discover(CurrentLocation); Visit(CurrentLocation); CurrentLocation.CreateStores(); @@ -396,13 +404,16 @@ namespace Barotrauma y + generationParams.VoronoiSiteVariance.Y * Rand.Range(-0.5f, 0.5f, Rand.RandSync.ServerAndClient))); } } + + // put some of this stuff in a helper class, this function is getting unwieldy + MapLocationTypeGenerator mapLocationTypeGenerator = new MapLocationTypeGenerator(campaign, this); Voronoi voronoi = new Voronoi(0.5f); List edges = voronoi.MakeVoronoiGraph(voronoiSites, Width, Height); Vector2 margin = new Vector2( - Math.Min(10, Width * 0.1f), - Math.Min(10, Height * 0.2f)); + Math.Min(10, Width * 0.1f), + Math.Min(10, Height * 0.2f)); float startX = margin.X, endX = Width - margin.X; float startY = margin.Y, endY = Height - margin.Y; @@ -413,8 +424,6 @@ namespace Barotrauma } voronoiSites.Clear(); - Dictionary> locationsPerZone = new Dictionary>(); - bool possibleStartOutpostCreated = false; foreach (GraphEdge edge in edges) { if (edge.Point1 == edge.Point2) { continue; } @@ -442,33 +451,18 @@ namespace Barotrauma Vector2 position = points[positionIndex]; if (newLocations[1 - i] != null && newLocations[1 - i].MapPosition == position) { position = points[1 - positionIndex]; } + int zone = GetZoneIndex(position.X); - if (!locationsPerZone.ContainsKey(zone)) - { - locationsPerZone[zone] = new List(); - } - LocationType forceLocationType = null; - if (forceLocationType == null) - { - foreach (LocationType locationType in LocationType.Prefabs.OrderBy(lt => lt.Identifier)) - { - if (locationType.MinCountPerZone.TryGetValue(zone, out int minCount) && locationsPerZone[zone].Count(l => l.Type == locationType) < minCount) - { - forceLocationType = locationType; - break; - } - } - } - - newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.ServerAndClient), - requireOutpost: false, forceLocationType: forceLocationType, existingLocations: Locations); - locationsPerZone[zone].Add(newLocations[i]); + newLocations[i] = Location.CreateRandom(position, zone, GetBiome(position.X)?.Identifier, Rand.GetRNG(Rand.RandSync.ServerAndClient), + requireOutpost: false, forceLocationType: null, existingLocations: Locations); + + mapLocationTypeGenerator.AddToLocationsPerZone(zone, newLocations[i]); Locations.Add(newLocations[i]); } var newConnection = new LocationConnection(newLocations[0], newLocations[1]); - Connections.Add(newConnection); + Connections.Add(newConnection); } //remove connections that are too short @@ -481,7 +475,6 @@ namespace Barotrauma { continue; } - //locations.Remove(connection.Locations[0]); Connections.Remove(connection); @@ -572,7 +565,6 @@ namespace Barotrauma { (zone1, zone2) = (zone2, zone1); } - if (generationParams.GateCount[zone1] == 0) { continue; } if (!connectionsBetweenZones[zone1].Any()) @@ -589,7 +581,7 @@ namespace Barotrauma } } else if (connectionsBetweenZones[zone1].Count() < generationParams.GateCount[zone1] && - connectionsBetweenZones[zone1].None(c => c.Locations.Contains(connection.Locations[0]) || c.Locations.Contains(connection.Locations[1]))) + connectionsBetweenZones[zone1].None(c => c.Locations.Contains(connection.Locations[0]) || c.Locations.Contains(connection.Locations[1]))) { connectionsBetweenZones[zone1].Add(connection); } @@ -599,6 +591,8 @@ namespace Barotrauma } } + var orderedPrefabs = LocationType.Prefabs.GetOrdered(); + List forciblyReassignedGateLocations = new List(); var gateFactions = campaign.Factions.Where(f => f.Prefab.ControlledOutpostPercentage > 0).OrderBy(f => f.Prefab.Identifier).ToList(); for (int i = Connections.Count - 1; i >= 0; i--) { @@ -617,20 +611,37 @@ namespace Barotrauma { var leftMostLocation = Connections[i].Locations[0].MapPosition.X < Connections[i].Locations[1].MapPosition.X ? - Connections[i].Locations[0] : - Connections[i].Locations[1]; + Connections[i].Locations[0] : + Connections[i].Locations[1]; if (!AllowAsBiomeGate(leftMostLocation.Type)) { + var potentialGateLocationTypes = orderedPrefabs.Where(AllowAsBiomeGate); + LocationType gateLocationType = + //choose some location type that's allowed in this zone/biome, and has a non zero commonness (instead of some fixed count) + potentialGateLocationTypes.Where(lt => lt.AreaSettings.Any(areaSettings => areaSettings.MatchesLocation(this, leftMostLocation) && areaSettings.Commonness > 0)).GetRandom(Rand.RandSync.ServerAndClient) ?? + //if not found, use something with a fixed count + potentialGateLocationTypes.Where(lt => lt.AreaSettings.Any(areaSettings => areaSettings.MatchesLocation(this, leftMostLocation) && areaSettings.MinCount > 0)).GetRandom(Rand.RandSync.ServerAndClient) ?? + //if that's not found either, try finding a type that doesn't spawn in any biome, but is allowed as a biome gate + //(a mod might have some special "biome gate" location types that are meant just for the gate locations) + potentialGateLocationTypes.Where(lt => lt.AreaSettings.None(areaSettings => areaSettings.Commonness > 0.0f || areaSettings.HasCounts)).GetRandom(Rand.RandSync.ServerAndClient); + + if (gateLocationType == null) + { + DebugConsole.ThrowError($"Failed to find a suitable location type for a gate location between zones {zone1} and {zone2}."); + continue; + } leftMostLocation.ChangeType( campaign, - LocationType.Prefabs.OrderBy(lt => lt.Identifier).First(lt => AllowAsBiomeGate(lt)), - createStores: false); + gateLocationType, + createStores: false, + unlockInitialMissions: false); + forciblyReassignedGateLocations.Add(leftMostLocation); } static bool AllowAsBiomeGate(LocationType lt) { //checking for "abandoned" is not strictly necessary here because it's now configured to not be allowed as a biome gate //but might be better to keep it for backwards compatibility (previously we relied only on that check) - return lt.HasOutpost && lt.Identifier != "abandoned" && lt.AllowAsBiomeGate; + return lt.HasOutpost && lt.Identifier != "abandoned" && lt.BiomeGate != LocationType.BiomeGateSetting.Deny; } leftMostLocation.IsGateBetweenBiomes = true; @@ -663,8 +674,8 @@ namespace Barotrauma if (!connection.Locked) { continue; } var rightMostLocation = connection.Locations[0].MapPosition.X > connection.Locations[1].MapPosition.X ? - connection.Locations[0] : - connection.Locations[1]; + connection.Locations[0] : + connection.Locations[1]; //if all of the other connected locations are to the left (= if there's no path forwards from the outpost), //create a new connection to the closest location to the right @@ -693,8 +704,18 @@ namespace Barotrauma //remove orphans Locations.RemoveAll(l => !Connections.Any(c => c.Locations.Contains(l))); + + AssignBiomes(Rand.GetRNG(Rand.RandSync.ServerAndClient)); - AssignBiomes(new MTRandom(ToolBox.StringToInt(Seed))); + var gateLocations = Locations.Where(l => l.IsGateBetweenBiomes); + foreach (var gateLocation in forciblyReassignedGateLocations) + { + //remove the gate locations who's types we've reassigned from the remaining types left to assign, + //(i.e. if we want just 1 of some location type, and we were forced to choose it as a gate, don't use that type again) + //must be done after assigning biomes + mapLocationTypeGenerator.RemoveOneFromTotals(gateLocation.Type, gateLocation); + } + mapLocationTypeGenerator.AssignForcedBiomeGateTypes(gateLocations); foreach (LocationConnection connection in Connections) { @@ -713,8 +734,13 @@ namespace Barotrauma if (LocationType.Prefabs.TryGet("outpost", out LocationType startLocationType)) { startLocation.ChangeType(campaign, startLocationType, createStores: false); + mapLocationTypeGenerator.AddToFilled(startLocation); } + mapLocationTypeGenerator.AssignLocationTypesBasedOnDesiredPosition(gateLocations); + + //create proper level data and stores for all locations + //(needs to be done before AssignLocationCounts, since LevelData may be required for the location type changes) foreach (Location location in Locations) { location.LevelData = new LevelData(location, this, CalculateDifficulty(location.MapPosition.X, location.Biome)); @@ -733,6 +759,15 @@ namespace Barotrauma } } } + //needs to be done after the LevelData has been assigned above + foreach (var gateLocation in forciblyReassignedGateLocations) + { + gateLocation.UnlockInitialMissions(Rand.RandSync.ServerAndClient); + } + + List locationsToAssign = Locations.ToList(); + locationsToAssign.Remove(GetPreviousToEndLocation()); + mapLocationTypeGenerator.AssignLocationTypesBasedOnCount(gateLocations, locations: locationsToAssign); foreach (LocationConnection connection in Connections) { @@ -740,7 +775,6 @@ namespace Barotrauma } CreateEndLocation(campaign); - float CalculateDifficulty(float mapPosition, Biome biome) { float settingsFactor = campaign.Settings.LevelDifficultyMultiplier; @@ -779,23 +813,27 @@ namespace Barotrauma float zoneWidth = Width / generationParams.DifficultyZones; int zoneIndex = (int)Math.Floor(xPos / zoneWidth) + 1; zoneIndex = Math.Clamp(zoneIndex, 1, generationParams.DifficultyZones - 1); - return Biome.Prefabs.FirstOrDefault(b => b.AllowedZones.Contains(zoneIndex)); + return OrderedBiomes.FirstOrDefault(b => b.AllowedZones.Contains(zoneIndex)); } + /// + /// Assign biomes for the connections between locations, ad for the locations that don't yet have a biome assigned. + /// private void AssignBiomes(Random rand) { - var biomes = Biome.Prefabs; float zoneWidth = Width / generationParams.DifficultyZones; List allowedBiomes = new List(10); for (int i = 0; i < generationParams.DifficultyZones; i++) { + int zoneIndex = i + 1; allowedBiomes.Clear(); - allowedBiomes.AddRange(biomes.Where(b => b.AllowedZones.Contains(generationParams.DifficultyZones - i))); - float zoneX = zoneWidth * (generationParams.DifficultyZones - i); + allowedBiomes.AddRange(OrderedBiomes.Where(b => b.AllowedZones.Contains(zoneIndex))); + float zoneX = zoneWidth * (zoneIndex); foreach (Location location in Locations) { + if (location.Biome != null) { continue; } if (location.MapPosition.X < zoneX) { location.Biome = allowedBiomes[rand.Next() % allowedBiomes.Count]; @@ -812,6 +850,9 @@ namespace Barotrauma System.Diagnostics.Debug.Assert(Connections.All(c => c.Biome != null)); } + /// + /// Returns the location prior to the final location. The type of this location is hard-coded just as that of the final location. + /// private Location GetPreviousToEndLocation() { Location previousToEndLocation = null; @@ -970,9 +1011,8 @@ namespace Barotrauma } } - #endregion Generation - + public void MoveToNextLocation() { if (SelectedLocation == null && Level.Loaded?.EndLocation != null) @@ -1167,7 +1207,6 @@ namespace Barotrauma { List nextLocations = CurrentLocation.Connections.Where(c => !c.Locked).Select(c => c.OtherLocation(CurrentLocation)).ToList(); List undiscoveredLocations = nextLocations.FindAll(l => !l.Discovered); - if (undiscoveredLocations.Count > 0 && preferUndiscovered) { SelectLocation(undiscoveredLocations[Rand.Int(undiscoveredLocations.Count, Rand.RandSync.Unsynced)]); @@ -1285,8 +1324,8 @@ namespace Barotrauma { location.PendingLocationTypeChange = (location.PendingLocationTypeChange.Value.typeChange, - location.PendingLocationTypeChange.Value.delay - 1, - location.PendingLocationTypeChange.Value.parentMission); + location.PendingLocationTypeChange.Value.delay - 1, + location.PendingLocationTypeChange.Value.parentMission); if (location.PendingLocationTypeChange.Value.delay <= 0) { return ChangeLocationType(campaign, location, location.PendingLocationTypeChange.Value.typeChange); @@ -1317,8 +1356,8 @@ namespace Barotrauma { location.PendingLocationTypeChange = (selectedTypeChange, - Rand.Range(selectedTypeChange.RequiredDurationRange.X, selectedTypeChange.RequiredDurationRange.Y), - null); + Rand.Range(selectedTypeChange.RequiredDurationRange.X, selectedTypeChange.RequiredDurationRange.Y), + parentMission: null); } else { @@ -1554,7 +1593,7 @@ namespace Barotrauma Identifier locationType = subElement.GetAttributeIdentifier("type", Identifier.Empty); LocalizedString prevLocationName = location.DisplayName; LocationType prevLocationType = location.Type; - LocationType newLocationType = LocationType.Prefabs.Find(lt => lt.Identifier == locationType) ?? LocationType.Prefabs.First(); + LocationType newLocationType = LocationType.Prefabs.Find(lt => lt.Identifier == locationType) ?? LocationType.Prefabs.GetOrdered().First(); location.ChangeType(campaign, newLocationType); if (showNotifications && prevLocationType != location.Type) @@ -1636,7 +1675,7 @@ namespace Barotrauma if (index < 0) { return null; } return Locations[index]; } - + } void Discover(Location location) @@ -1774,4 +1813,4 @@ namespace Barotrauma partial void RemoveProjSpecific(); } -} +} \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/MapLocationTypeGenerator.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/MapLocationTypeGenerator.cs new file mode 100644 index 000000000..7003b9848 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/MapLocationTypeGenerator.cs @@ -0,0 +1,397 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Barotrauma.Extensions; +using Microsoft.Xna.Framework; + +namespace Barotrauma +{ + internal class MapLocationTypeGenerator + { + internal class LocationTypeCount + { + public int AmountToAssign; + public int? DifficultyZone; + public Identifier? BiomeId; + + public LocationTypeCount(int amountToAssign, int difficultyZone) + { + AmountToAssign = amountToAssign; + DifficultyZone = difficultyZone; + } + + public LocationTypeCount(int amountToAssign, Identifier biomeId) + { + AmountToAssign = amountToAssign; + BiomeId = biomeId; + } + + public string ToDebugString() + { + if (DifficultyZone.HasValue) + { + return $"x{AmountToAssign} in (zone {DifficultyZone.Value})"; + } + else if (BiomeId.HasValue) + { + return $"x{AmountToAssign} in (biome {BiomeId.Value})"; + } + return $"x{AmountToAssign}"; + } + } + + /// + /// Actual amounts of location types that need to be assigned to locations, after resolving random variation from min/max counts. + /// + private Dictionary> locationTypeAmountsToAssign; + private readonly Map map; + + private readonly CampaignMode campaign; + /// + /// List of locations that have been filled from specific requirements and should not be overwritten by some subsequent generation pass (e.g. the start outpost). + /// + private readonly List filledLocations; + private readonly Dictionary> locationsPerZone = new Dictionary>(); + + private readonly IOrderedEnumerable orderedLocationTypes = LocationType.Prefabs.GetOrdered(); + + private bool IsEveryLocationTypeAssigned + { + get + { + return locationTypeAmountsToAssign.SelectMany(kvp => kvp.Value) + .None(locationTypeCount => locationTypeCount.AmountToAssign > 0); + } + } + + public MapLocationTypeGenerator(CampaignMode campaign, Map map) + { + filledLocations = new List(); + this.map = map; + this.campaign = campaign; + locationTypeAmountsToAssign = new Dictionary>(); + GenerateTotalAmountsToAssign(); + } + + private void GenerateTotalAmountsToAssign() + { + foreach (var locationTypePrefab in orderedLocationTypes) + { + foreach (var areaSetting in locationTypePrefab.AreaSettings) + { + if (!areaSetting.HasCounts) { continue; } + + if (!areaSetting.HasValidData) + { + // the only case for invalid data right now is the biome id + DebugConsole.AddWarning($"Biome ID is invalid for AreaSetting in locationType '{locationTypePrefab.Identifier}'. Skipping invalid setting.", locationTypePrefab.ContentPackage); + continue; + } + + int amountToAdd = Rand.GetRNG(Rand.RandSync.ServerAndClient).Next(areaSetting.MinCount ?? 0, areaSetting.MaxCount ?? 0 + 1); + if (amountToAdd <= 0) { continue; } + + // data is either for a biome or a zone, but not both + var existingCount = GetExistingCount(locationTypePrefab, areaSetting); + + if (existingCount != null) + { + existingCount.AmountToAssign += amountToAdd; + } + else + { + if (!locationTypeAmountsToAssign.ContainsKey(locationTypePrefab)) + { + locationTypeAmountsToAssign[locationTypePrefab] = new List(); + } + locationTypeAmountsToAssign[locationTypePrefab].Add(CreateNewCount(areaSetting, amountToAdd)); + } + } + } + + LocationTypeCount CreateNewCount(LocationType.AreaSettingData areaSettingData, int amountToAdd) + { + if (areaSettingData is LocationType.BiomeSettingData biomeSettingData) + { + return new LocationTypeCount(amountToAdd, biomeSettingData.BiomeIdentifier); + } + else if (areaSettingData is LocationType.DifficultyZoneSettingData difficultyZoneSettingData) + { + return new LocationTypeCount(amountToAdd, difficultyZoneSettingData.DifficultyZone); + } + else + { + throw new ArgumentException("Unrecognized areaSettingData"); + } + } + + LocationTypeCount? GetExistingCount(LocationType locationTypePrefab, LocationType.AreaSettingData areaSettingData) + { + if (!locationTypeAmountsToAssign.TryGetValue(locationTypePrefab, out List? value)) { return null; } + return value.FirstOrDefault(areaSettingData.MatchesRemainingCount); + } + } + + public void AddToLocationsPerZone(int zone, Location location) + { + if (!locationsPerZone.ContainsKey(zone)) + { + locationsPerZone[zone] = new List(); + } + locationsPerZone[zone].Add(location); + } + + public void AddToFilled(Location location) + { + if (filledLocations.Contains(location)) { return; } + filledLocations.Add(location); + } + + public bool IsFilled(Location location) + { + return filledLocations.Contains(location); + } + + public static void ChangeLocationTypeAndName(CampaignMode campaign, Location location, LocationType suitableLocationType) + { + location.ChangeType(campaign, suitableLocationType, createStores: false, unlockInitialMissions: false); + if (!suitableLocationType.ForceLocationName.IsEmpty) + { + location.ForceName(suitableLocationType.ForceLocationName); + } + } + + public void AssignForcedBiomeGateTypes(IEnumerable gateLocations) + { + foreach (Location gateLocation in gateLocations) + { + foreach (LocationType locationType in orderedLocationTypes) + { + if (locationType.BiomeGate != LocationType.BiomeGateSetting.Force) { continue; } + + int zone = map.GetZoneIndex(gateLocation.MapPosition.X); + + // if there are no counts left for this location type, skip it + if (locationType.HasCounts() && !TypeHasRemainingCountForLocation(locationType, gateLocation)) { continue; } + + // wrong faction, can't place here + if (!locationType.Faction.IsEmpty && locationType.Faction != gateLocation.Faction?.Prefab.Identifier) + { + continue; + } + + // if the location already happens to be of the type we want to assign, skip and remove from totals + if (gateLocation.Type == locationType) + { + AddToFilled(gateLocation); + RemoveOneFromTotals(locationType, gateLocation); + break; + } + + if (!IsFilled(gateLocation) && + locationType.IsValidForZoneOrBiome(zone, gateLocation.Biome.Identifier)) + { + AddToFilled(gateLocation); + ChangeLocationTypeAndName(campaign, gateLocation, locationType); + RemoveOneFromTotals(locationType, gateLocation); + break; + } + } + } + } + + private bool TypeHasRemainingCountForLocation(LocationType countLocationType, Location location) + { + if (!locationTypeAmountsToAssign.TryGetValue(countLocationType, out List? locationTypeCounts)) + { + return false; + } + + bool hasZoneCount = locationTypeCounts.Any(ltc => ltc.DifficultyZone == map.GetZoneIndex(location.MapPosition.X) && ltc.AmountToAssign > 0); + bool hasBiomeCount = locationTypeCounts.Any(ltc => ltc.BiomeId == location.Biome.Identifier && ltc.AmountToAssign > 0); + + return hasZoneCount || hasBiomeCount; + } + + private int GetRemainingCount(LocationType locationType, LocationType.AreaSettingData areaSetting) + { + locationTypeAmountsToAssign.TryGetValue(locationType, out List? locationTypeCounts); + if (locationTypeCounts == null || locationTypeCounts.None()) { return 0; } + + var match = locationTypeCounts.FirstOrDefault(ltc => areaSetting.MatchesRemainingCount(ltc)); + return match?.AmountToAssign ?? 0; + } + + public void RemoveOneFromTotals(LocationType locationType, Location location) + { + if (!locationTypeAmountsToAssign.TryGetValue(locationType, out List? locationTypeCounts)) + { + return; + } + + var zoneMatch = locationTypeCounts.FirstOrDefault(ltc => ltc.AmountToAssign > 0 && ltc.DifficultyZone == map.GetZoneIndex(location.MapPosition.X)); + if (zoneMatch != null) + { + zoneMatch.AmountToAssign--; + } + var biomeMatch = locationTypeCounts.FirstOrDefault(ltc => ltc.AmountToAssign > 0 && ltc.BiomeId == location.Biome.Identifier); + if (biomeMatch != null) + { + biomeMatch.AmountToAssign--; + } + } + + /// + /// Assign the location types that should be placed in some specific part of a biome/zone . + /// + public void AssignLocationTypesBasedOnDesiredPosition(IEnumerable gateLocations) + { + foreach (LocationType locationType in LocationType.Prefabs) + { + foreach (var areaSetting in locationType.AreaSettings) + { + if (!areaSetting.DesiredPosition.HasValue) { continue; } + + int remainingCount = GetRemainingCount(locationType, areaSetting); + if (remainingCount == 0) { continue; } + + var locations = map.Locations.Where(location => areaSetting.MatchesLocation(map, location)).Where(location => !gateLocations.Contains(location)); + if (locations.None()) { continue; } + + FillLocations(locations, areaSetting.DesiredPosition.Value, remainingCount, locationType); + } + } + + void FillLocations(IEnumerable locations, float desiredPosition, int locationCount, LocationType locationType) + { + float areaStart = locations.Min(l => l.MapPosition.X); + float areaEnd = locations.Max(l => l.MapPosition.X); + + float desiredMapPosition = MathHelper.Lerp(areaStart, areaEnd, desiredPosition); + + List sortedLocations = locations.Where(location => !IsFilled(location)).ToList(); + sortedLocations.Sort((firstLocation, secondLocation) => Math.Abs(firstLocation.MapPosition.X - desiredMapPosition).CompareTo(secondLocation.MapPosition.X - desiredMapPosition)); + + for (int i = 0; i < locationCount; i++) + { + if (sortedLocations.None()) { break; } + var closestLocation = sortedLocations.First(); + ChangeLocationTypeAndName(campaign, closestLocation, locationType); + AddToFilled(closestLocation); + RemoveOneFromTotals(closestLocation.Type, closestLocation); + sortedLocations.Remove(closestLocation); + } + } + } + + public void AssignLocationTypesBasedOnCount(IEnumerable gateLocations, IEnumerable locations) + { + // generate lists of all the instances of location types that we are supposed to try and fit into the available locations + List shuffledLocations = locations.ToList(); + shuffledLocations.Shuffle(Rand.GetRNG(Rand.RandSync.ServerAndClient)); + + foreach (Location location in shuffledLocations) + { + if (IsFilled(location)) { continue; } + + bool isBiomeGate = gateLocations.Contains(location); + if (isBiomeGate && location.Type.BiomeGate == LocationType.BiomeGateSetting.Force) + { + //forced as a biome gate, let's not touch this location + continue; + } + if (IsEveryLocationTypeAssigned) { break; } + + var suitableLocationType = TryPickSuitableLocationTypeFromTotals(location, isBiomeGate); + + // if we found something suitable, change the location type and name, and add to filled locations + // (otherwise we will honor the initial random location type) + if (suitableLocationType != null) + { + ChangeLocationTypeAndName(campaign, location, suitableLocationType); + AddToFilled(location); + } + } + + // warn if we couldn't fill in all the desired counts + if (!IsEveryLocationTypeAssigned) + { + ContentPackage? nonVanillaContentPackage = null; + StringBuilder sb = new StringBuilder("Following location types could not be assigned to locations:\n"); + foreach ((LocationType locationType, List locationTypeCounts) in locationTypeAmountsToAssign) + { + foreach (var locationTypeCount in locationTypeCounts) + { + if (locationTypeCount.AmountToAssign > 0) + { + if (locationType.ContentPackage != ContentPackageManager.VanillaCorePackage) + { + nonVanillaContentPackage = locationType.ContentPackage; + } + sb.AppendLine($"- {locationType.Identifier} - {locationType.Name} ({locationTypeCount.ToDebugString()})"); + } + } + } + DebugConsole.AddWarning(sb.ToString(), + //blame the mod where one of the problematic location types is defined in + contentPackage: nonVanillaContentPackage); + } + } + + private LocationType? TryPickSuitableLocationTypeFromTotals(Location location, bool isBiomeGate) + { + int locationZone = map.GetZoneIndex(location.MapPosition.X); + Identifier locationBiomeId = location.Biome.Identifier; + LocationType? suitableLocationType = null; + + //find location type counts that haven't been fully assigned yet + List<(LocationType LocationType, LocationTypeCount Count)> potentialLocationTypeCounts = []; + foreach ((LocationType locationType, List countList) in locationTypeAmountsToAssign) + { + //if we're picking a potential new type for a biome gate, it must be a location type that's allowed as a biome gate + if (isBiomeGate && locationType.BiomeGate == LocationType.BiomeGateSetting.Deny) { continue; } + foreach (var locationTypeCount in countList) + { + if (locationTypeCount.AmountToAssign > 0) + { + potentialLocationTypeCounts.Add((locationType, locationTypeCount)); + } + } + } + + var zoneMatches = potentialLocationTypeCounts.Where(locationTypeCount => locationTypeCount.Count.DifficultyZone == locationZone); + var biomeMatches = potentialLocationTypeCounts.Where(locationTypeCount => locationTypeCount.Count.BiomeId == locationBiomeId); + if (zoneMatches.None() && biomeMatches.None()) + { + return null; + } + + // if both lists have something, we will try to find a location type that is in both lists + if (zoneMatches.Any() && biomeMatches.Any()) + { + var dualMatch = zoneMatches.FirstOrDefault(zoneCount => biomeMatches.Any(biomeCount => biomeCount.LocationType == zoneCount.LocationType)); + if (dualMatch.LocationType != null) + { + suitableLocationType = dualMatch.LocationType; + } + } + + // no dual match, find individual match + if (suitableLocationType == null) + { + suitableLocationType = zoneMatches.Any() ? + zoneMatches.First().LocationType : biomeMatches.First().LocationType; + } + + if (suitableLocationType != null) + { + RemoveOneFromTotals(suitableLocationType, location); + } + + return suitableLocationType; + } + } +} \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs index d99cd9078..2f0b1388e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs @@ -51,9 +51,8 @@ namespace Barotrauma public readonly List linkedTo = new List(); - protected bool flippedX, flippedY; - public bool FlippedX { get { return flippedX; } } - public bool FlippedY { get { return flippedY; } } + public bool FlippedX { get; protected set; } + public bool FlippedY { get; protected set; } public bool ShouldBeSaved = true; @@ -95,6 +94,16 @@ namespace Barotrauma } } + public virtual float RotationRad { get; protected set; } + + /// + /// Rotation taking into account flipping: if the entity is flipped on either axis, the rotation is negated + /// (but not if it's flipped on both axes, two flips is essentially double negation). + /// + public float RotationRadWithFlipping => FlippedX ^ FlippedY ? -RotationRad : RotationRad; + + public float RotationWithFlipping => MathHelper.ToDegrees(RotationRadWithFlipping); + public virtual Rectangle Rect { get { return rect; } @@ -728,9 +737,10 @@ namespace Barotrauma /// Flip the entity horizontally /// /// Should the entity be flipped across the y-axis of the sub it's inside - public virtual void FlipX(bool relativeToSub) + /// Forces the item to be flipped even if it's configured not to be flippable. + public virtual void FlipX(bool relativeToSub, bool force = false) { - flippedX = !flippedX; + FlippedX = !FlippedX; if (!relativeToSub || Submarine == null) { return; } Vector2 relative = WorldPosition - Submarine.WorldPosition; @@ -742,9 +752,10 @@ namespace Barotrauma /// Flip the entity vertically /// /// Should the entity be flipped across the x-axis of the sub it's inside - public virtual void FlipY(bool relativeToSub) + /// Forces the item to be flipped even if it's configured not to be flippable. + public virtual void FlipY(bool relativeToSub, bool force = false) { - flippedY = !flippedY; + FlippedY = !FlippedY; if (!relativeToSub || Submarine == null) { return; } Vector2 relative = WorldPosition - Submarine.WorldPosition; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerationParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerationParams.cs index bdbf0c3cb..0a932c4e4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerationParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerationParams.cs @@ -23,6 +23,12 @@ namespace Barotrauma get { return allowedLocationTypes; } } + private readonly HashSet allowedGameModeIdentifiers = new HashSet(); + + public IEnumerable AllowedGameModeIdentifiers + { + get { return allowedGameModeIdentifiers; } + } [Serialize(-1, IsPropertySaveable.Yes, description: "Should this type of outpost be forced to the locations at the end of the campaign map? 0 = first end level, 1 = second end level, and so on."), Editable(MinValueInt = -1, MaxValueInt = 10)] public int ForceToEndLocationIndex @@ -279,6 +285,7 @@ namespace Barotrauma { Name = element.GetAttributeString("name", Identifier.Value); allowedLocationTypes = element.GetAttributeIdentifierArray("allowedlocationtypes", Array.Empty()).ToHashSet(); + allowedGameModeIdentifiers = element.GetAttributeIdentifierArray("allowedgamemodes", Array.Empty()).ToHashSet(); SerializableProperties = SerializableProperty.DeserializeProperties(this, element); if (element.GetAttribute("leveltype") != null) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs index 99c20a501..dcb60b4b1 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs @@ -1,5 +1,6 @@ using Barotrauma.Extensions; using Barotrauma.Items.Components; +using Barotrauma.RuinGeneration; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; @@ -110,6 +111,17 @@ namespace Barotrauma } } + var prebuiltOutpostInfo = ChooseOutpost(generationParams); + prebuiltOutpostInfo.Type = SubmarineType.Outpost; + sub = new Submarine(prebuiltOutpostInfo); + sub.Info.OutpostGenerationParams = generationParams; + location?.RemoveTakenItems(); + EnableFactionSpecificEntities(sub, location); + return sub; + } + + private static SubmarineInfo ChooseOutpost(OutpostGenerationParams generationParams) + { var outpostFiles = ContentPackageManager.EnabledPackages.All .SelectMany(p => p.GetFiles()) .Where(f => !TutorialPrefab.Prefabs.Any(tp => tp.OutpostPath == f.Path)) @@ -120,6 +132,50 @@ namespace Barotrauma { outpostInfos.Add(new SubmarineInfo(outpostFile.Path.Value)); } + + + //if there's missions selected that allow outpost selection from some specific set of outposts, + //choose one of those outposts + List outpostInfosSuitableForMission = new List(); + if (GameMain.GameSession?.GameMode is { } gameMode) + { + foreach (var mission in gameMode.Missions) + { + if (!mission.Prefab.AllowOutpostSelectionFromTag.IsEmpty) + { + foreach (var outpostInfo in outpostInfos) + { + if (outpostInfo.OutpostTags.Contains(mission.Prefab.AllowOutpostSelectionFromTag) && + !outpostInfosSuitableForMission.Contains(outpostInfo)) + { + outpostInfosSuitableForMission.Add(outpostInfo); + } + } + } + } + } + + //if an outpost has been select in the server settings, choose that... + if (GameMain.NetworkMember?.ServerSettings is { } serverSettings && + serverSettings.SelectedOutpostName != "Random") + { + //...but only if the outpost is suitable for the mission (or if the mission has no specific requirements for the outpost) + if (outpostInfosSuitableForMission.None() || + outpostInfosSuitableForMission.Any(outpostInfo => outpostInfo.OutpostTags.Contains(serverSettings.SelectedOutpostName))) + { + var matchingOutpost = outpostInfos.FirstOrDefault(o => o.Name == serverSettings.SelectedOutpostName); + if (matchingOutpost != null) + { + return matchingOutpost; + } + } + } + + if (outpostInfosSuitableForMission.Any()) + { + return outpostInfosSuitableForMission.GetRandom(Rand.RandSync.ServerAndClient); + } + if (generationParams.OutpostTag.IsEmpty) { outpostInfos = outpostInfos.FindAll(o => o.OutpostTags.None()); @@ -139,24 +195,7 @@ namespace Barotrauma { throw new Exception("Failed to generate an outpost. Could not generate an outpost from the available outpost modules and there are no pre-built outposts available."); } - var prebuiltOutpostInfo = outpostInfos.GetRandom(Rand.RandSync.ServerAndClient); - - if (GameMain.NetworkMember?.ServerSettings is { } serverSettings && - serverSettings.SelectedOutpostName != "Random") - { - var matchingOutpost = outpostInfos.FirstOrDefault(o => o.Name == serverSettings.SelectedOutpostName); - if (matchingOutpost != null) - { - prebuiltOutpostInfo = matchingOutpost; - } - } - - prebuiltOutpostInfo.Type = SubmarineType.Outpost; - sub = new Submarine(prebuiltOutpostInfo); - sub.Info.OutpostGenerationParams = generationParams; - location?.RemoveTakenItems(); - EnableFactionSpecificEntities(sub, location); - return sub; + return outpostInfos.GetRandom(Rand.RandSync.ServerAndClient); } private static Submarine GenerateFromModules(OutpostGenerationParams generationParams, OutpostModuleFile[] outpostModuleFiles, Submarine sub, LocationType locationType, Location location, bool onlyEntrance = false, bool allowInvalidOutpost = false) @@ -260,13 +299,15 @@ namespace Barotrauma if (hasForceOutpostWithInitialFlag) { - DebugConsole.NewMessage($"Using Force outpost module as initial in Outpost generation: {GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.Name}", Color.Yellow); + DebugConsole.NewMessage($"Forcing module \"{GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.Name}\" as the initial module...", Color.Yellow); usedForceOutpostModule = GameMain.GameSession.ForceOutpostModule; - GameMain.GameSession.ForceOutpostModule = null; } if (initialModule == null) { + //reset the forced outpost module so that it won't be used + //if we attempt to generate a new outpost later after this failed attempt + GameMain.GameSession.ForceOutpostModule = null; throw new Exception("Failed to generate an outpost (no airlock modules found)."); } foreach (Identifier initialFlag in initialModule.OutpostModuleInfo.ModuleFlags) @@ -297,25 +338,34 @@ namespace Barotrauma remainingOutpostGenerationTries--; continue; } - DebugConsole.ThrowError($"Could not place force outpost module: {GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.Name}"); - GameMain.GameSession.ForceOutpostModule = null; + DebugConsole.ThrowError($"Could not force the outpost module \"{GameMain.GameSession.ForceOutpostModule.OutpostModuleInfo.Name}\" to the outpost. Loading the module as-is..."); return null; } + if (GameMain.GameSession != null) + { + GameMain.GameSession.ForceOutpostModule = null; + } + if (pendingModuleFlags.Any(flag => flag != "none")) { if (!allowInvalidOutpost) { remainingOutpostGenerationTries--; - if (remainingOutpostGenerationTries <= 0) + if (remainingOutpostGenerationTries > 0) { - DebugConsole.ThrowError("Could not generate an outpost with all of the required modules. Some modules may not have enough connections at the edges to generate a valid layout. Pending modules: " + string.Join(", ", pendingModuleFlags)); + //tries left -> don't finish generating the outpost, try generating another layout + continue; + } + else + { + //out of tries, log an error, but let the method continue into loading the outpost (even if it doesn't have all the required modules) + DebugConsole.AddSafeError("Could not generate an outpost with all of the required modules. Some modules may not have enough connections at the edges to generate a valid layout. Pending modules: " + string.Join(", ", pendingModuleFlags)); } - continue; } else { - DebugConsole.ThrowError("Could not generate an outpost with all of the required modules. Some modules may not have enough connections at the edges to generate a valid layout. Pending modules: " + string.Join(", ", pendingModuleFlags) + ". Won't retry because invalid outposts are allowed."); + DebugConsole.AddSafeError("Could not generate an outpost with all of the required modules. Some modules may not have enough connections at the edges to generate a valid layout. Pending modules: " + string.Join(", ", pendingModuleFlags) + ". Won't retry because invalid outposts are allowed."); } } @@ -354,7 +404,7 @@ namespace Barotrauma remainingOutpostGenerationTries--; } - DebugConsole.AddSafeError("Failed to generate an outpost without overlapping modules. Trying to use a pre-built outpost instead..."); + DebugConsole.AddSafeError("Failed to generate an outpost with a valid layout and all the required modules. Trying to use a pre-built outpost instead..."); return null; List loadEntities(Submarine sub) @@ -463,10 +513,13 @@ namespace Barotrauma int maxMoveAmount = Math.Max(2000, selectedModules.Max(m => Math.Max(m.Bounds.Width, m.Bounds.Height))); bool overlapsFound = true; + PlacedModule overlappingModule1, overlappingModule2, moduleBelowAirlock; int iteration = 0; + const int MaxIterations = 20; while (overlapsFound) { overlapsFound = false; + overlappingModule1 = overlappingModule2 = moduleBelowAirlock = null; foreach (PlacedModule placedModule in selectedModules) { if (placedModule.PreviousModule == null) { continue; } @@ -478,6 +531,8 @@ namespace Barotrauma int remainingOverlapPreventionTries = 10; while (FindOverlap(subsequentModules, otherModules, out var module1, out var module2) && remainingOverlapPreventionTries > 0) { + overlappingModule1 = module1; + overlappingModule2 = module2; overlapsFound = true; if (FindOverlapSolution(subsequentModules, module1, module2, selectedModules, generationParams.MinHallwayLength, maxMoveAmount, out Dictionary solution)) { @@ -492,16 +547,40 @@ namespace Barotrauma } remainingOverlapPreventionTries--; } - if (remainingOutpostGenerationTries > MaxOutpostGenerationRetries / 2 && + + //check that the module doesn't extend below the airlock and potentially overlap with the sub + if (generationParams is not RuinGenerationParams && + //if we've already exhausted half of the retries, accept potential overlaps + remainingOutpostGenerationTries > MaxOutpostGenerationRetries / 2 && + //if the module is horizontally very far, it's ok to expand below the airlock + (placedModule.Bounds.X + placedModule.Offset.X < 5000 && placedModule.Bounds.Right + placedModule.Offset.X > -5000) && ModuleBelowInitialModule(placedModule, selectedModules.First())) { + moduleBelowAirlock = placedModule; overlapsFound = true; } } iteration++; - if (iteration > 10) + if (iteration > MaxIterations) { +#if DEBUG + string warningMsg = "Failed to create an outpost layout with no overlaps."; + if (overlappingModule1 != null && overlappingModule2 != null) + { + warningMsg += $" Overlapping modules: {overlappingModule1.Info.Name}, {overlappingModule2.Info.Name}."; + } + if (moduleBelowAirlock != null) + { + warningMsg += $" Module below airlock: {moduleBelowAirlock.Info.Name}."; + } + if (remainingOutpostGenerationTries > 0) + { + warningMsg += " Retrying..."; + } + + DebugConsole.AddWarning(warningMsg); +#endif generationFailed = true; break; } @@ -931,7 +1010,7 @@ namespace Barotrauma Rectangle initialModuleBounds = initialModule.Bounds; initialModuleBounds.Location += (initialModule.Offset + initialModule.MoveOffset).ToPoint(); - return bounds.Bottom < initialModuleBounds.Bottom; + return bounds.Y < initialModuleBounds.Y; } /// @@ -1053,8 +1132,8 @@ namespace Barotrauma } modulesWithCorrectFlags = modulesWithCorrectFlags.Where(m => m.OutpostModuleInfo.GapPositions.HasFlag(gapPosition) && m.OutpostModuleInfo.CanAttachToPrevious.HasFlag(gapPosition)); - var suitableModules = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: true, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: true); - var suitableModulesForAnyOutpost = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: true, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: false); + var suitableModules = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: true, requireCorrectLocationType: true, requireLocationTypeSpecific: true); + var suitableModulesForAnyOutpost = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: true, requireCorrectLocationType: true, requireLocationTypeSpecific: false); if (!suitableModules.Any()) { //no suitable module found, see if we can find a "generic" module that's not meant for any specific type of outpost @@ -1062,12 +1141,12 @@ namespace Barotrauma //still not found, see if we can find something that's otherwise suitable but not meant to attach to the previous module if (!suitableModules.Any()) { - suitableModules = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: false, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: true); + suitableModules = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: false, requireCorrectLocationType: true, requireLocationTypeSpecific: true); } //still not found! Try if we can find a generic module that's not meant to attach to the previous module if (!suitableModules.Any()) { - suitableModules = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: false, requireCorrectLocationType: true, disallowNonLocationTypeSpecific: false); + suitableModules = GetSuitableModules(modulesWithCorrectFlags, requireAllowAttachToPrevious: false, requireCorrectLocationType: true, requireLocationTypeSpecific: false); } } @@ -1109,20 +1188,12 @@ namespace Barotrauma return suitableModule; } - IEnumerable GetSuitableModules(IEnumerable modules, bool requireAllowAttachToPrevious, bool requireCorrectLocationType, bool disallowNonLocationTypeSpecific) + IEnumerable GetSuitableModules(IEnumerable modules, bool requireAllowAttachToPrevious, bool requireCorrectLocationType, bool requireLocationTypeSpecific) { IEnumerable suitable = modules; if (requireCorrectLocationType) { - if (disallowNonLocationTypeSpecific) - { - //don't use OutpostModuleInfo.IsLocationTypeAllowed here - we're trying to choose a module specifically for this location type, not modules suitable for any location type - suitable = modules.Where(m => m.OutpostModuleInfo.AllowedLocationTypes.Contains(locationType.Identifier)); - } - else - { - suitable = modules.Where(m => m.OutpostModuleInfo.IsAllowedInLocationType(locationType)); - } + suitable = modules.Where(m => m.OutpostModuleInfo.IsAllowedInLocationType(locationType, requireLocationTypeSpecific: requireLocationTypeSpecific)); } if (requireAllowAttachToPrevious && prevModule != null) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostModuleInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostModuleInfo.cs index 46330d892..8c0c97477 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostModuleInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostModuleInfo.cs @@ -138,10 +138,15 @@ namespace Barotrauma return allowedLocationTypes.None() || allowedLocationTypes.Contains("Any".ToIdentifier()); } - public bool IsAllowedInLocationType(LocationType locationType) + + /// Does the module need to be explicitly configured as suitable for the location type, or is it ok if it's allowed for any location type? + public bool IsAllowedInLocationType(LocationType locationType, bool requireLocationTypeSpecific = false) { - if (locationType == null || IsAllowedInAnyLocationType()) { return true; } - return allowedLocationTypes.Contains(locationType.Identifier); + if (locationType == null) { return true; } + if (!requireLocationTypeSpecific && IsAllowedInAnyLocationType()) { return true; } + return + allowedLocationTypes.Contains(locationType.Identifier) || + (!locationType.UseOutpostModulesOfLocationType.IsEmpty && allowedLocationTypes.Contains(locationType.UseOutpostModulesOfLocationType)); } public void DetermineGapPositions(Submarine sub) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Structure.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Structure.cs index 0fbaa6e0b..3d1a11195 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Structure.cs @@ -251,14 +251,13 @@ namespace Barotrauma } } - protected float rotationRad = 0f; [ConditionallyEditable(ConditionallyEditable.ConditionType.AllowRotating, DecimalCount = 3, ForceShowPlusMinusButtons = true, ValueStep = 0.1f), Serialize(0.0f, IsPropertySaveable.Yes)] public float Rotation { - get => MathHelper.ToDegrees(rotationRad); + get => MathHelper.ToDegrees(RotationRad); set { - rotationRad = MathHelper.WrapAngle(MathHelper.ToRadians(value)); + RotationRad = MathHelper.WrapAngle(MathHelper.ToRadians(value)); if (StairDirection != Direction.None) { CreateStairBodies(); @@ -373,7 +372,7 @@ namespace Barotrauma { get { - float rotation = MathHelper.ToRadians(Prefab.BodyRotation) + this.rotationRad; + float rotation = MathHelper.ToRadians(Prefab.BodyRotation) + this.RotationRad; if (IsHorizontal) { if (FlippedX) { rotation = -MathHelper.Pi - rotation; } @@ -396,9 +395,9 @@ namespace Barotrauma get { Vector2 bodyOffset = Prefab.BodyOffset; - if (rotationRad != 0f) + if (RotationRad != 0f) { - bodyOffset = MathUtils.RotatePoint(bodyOffset, -rotationRad); + bodyOffset = MathUtils.RotatePoint(bodyOffset, -RotationRad); } if (FlippedX) { bodyOffset.X = -bodyOffset.X; } if (FlippedY) { bodyOffset.Y = -bodyOffset.Y; } @@ -627,7 +626,7 @@ namespace Barotrauma Body newBody = GameMain.World.CreateRectangle(bodyWidth, bodyHeight, 1.5f); - var rotationWithFlip = FlippedX ^ FlippedY ? -rotationRad : rotationRad; + float rotationWithFlip = RotationRadWithFlipping; newBody.BodyType = BodyType.Static; Vector2 stairRectHeightDiff = new Vector2(0f, stairHeight / 2.0f - rect.Height / 2.0f); @@ -764,8 +763,8 @@ namespace Barotrauma public override Quad2D GetTransformedQuad() => Quad2D.FromSubmarineRectangle(rect).Rotated( FlippedX != FlippedY - ? rotationRad - : -rotationRad); + ? RotationRad + : -RotationRad); /// /// Checks if there's a structure items can be attached to at the given position and returns it. @@ -1280,7 +1279,7 @@ namespace Barotrauma gapRect.Width += 20; gapRect.Height += 20; - bool rotatedEnoughToChangeOrientation = (MathUtils.WrapAngleTwoPi(rotationRad - MathHelper.PiOver4) % MathHelper.Pi < MathHelper.PiOver2); + bool rotatedEnoughToChangeOrientation = (MathUtils.WrapAngleTwoPi(RotationRad - MathHelper.PiOver4) % MathHelper.Pi < MathHelper.PiOver2); if (rotatedEnoughToChangeOrientation) { var center = gapRect.Location + gapRect.Size.FlipY() / new Point(2); @@ -1345,6 +1344,9 @@ namespace Barotrauma if (gapOpen - prevGapOpenState > 0.25f && createExplosionEffect && !gap.IsRoomToRoom) { CreateWallDamageExplosion(gap, attacker, createWallDamageProjectiles); +#if CLIENT + SteamTimelineManager.OnHullBreached(this); +#endif } } @@ -1378,7 +1380,7 @@ namespace Barotrauma { const float explosionRange = 500.0f; float explosionStrength = gap.Open; - + var linkedHull = gap.linkedTo.FirstOrDefault() as Hull; if (linkedHull != null) { @@ -1595,7 +1597,7 @@ namespace Barotrauma partial void CreateConvexHull(Vector2 position, Vector2 size, float rotation); - public override void FlipX(bool relativeToSub) + public override void FlipX(bool relativeToSub, bool force = false) { base.FlipX(relativeToSub); @@ -1623,7 +1625,7 @@ namespace Barotrauma } } - public override void FlipY(bool relativeToSub) + public override void FlipY(bool relativeToSub, bool force = false) { base.FlipY(relativeToSub); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs index 1d262b380..e3816f58c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs @@ -1141,6 +1141,17 @@ namespace Barotrauma } } + foreach (var dockingPort in DockingPort.List) + { + //a little hacky: undock and redock to ensure the hulls and gaps between docking ports are correct + //after all the parts of the submarine have been flipped and moved to correct places. + if (dockingPort.DockingTarget is { } dockingTarget) + { + dockingPort.Undock(); + dockingPort.Dock(dockingTarget); + } + } + Item.UpdateHulls(); Gap.UpdateHulls(); #if CLIENT @@ -2090,6 +2101,10 @@ namespace Barotrauma foreach (Submarine sub in _loaded) { sub.Remove(); + if (sub.Info.LazyLoad) + { + sub.Info.UnloadSubmarineElement(); + } } loaded.Clear(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs index 2ae85e4eb..239b38056 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs @@ -1,4 +1,4 @@ -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -194,10 +194,27 @@ namespace Barotrauma set; } + /// + /// When enabled, the XML element is not loaded until it is accessed. + /// + public readonly bool LazyLoad; + + private XElement submarineElement; + public XElement SubmarineElement { - get; - private set; + get + { + if (LazyLoad && submarineElement == null) + { + Reload(); + } + return submarineElement; + } + private set + { + submarineElement = value; + } } public override string ToString() @@ -263,7 +280,11 @@ namespace Barotrauma RequiredContentPackages = new HashSet(); } - public SubmarineInfo(string filePath, string hash = "", XElement element = null, bool tryLoad = true) + /// + /// Creates a new SubmarineInfo from a file. + /// + /// When enabled, the XML element is not loaded until it is accessed. + public SubmarineInfo(string filePath, string hash = "", XElement element = null, bool tryLoad = true, bool lazyLoad = false) { FilePath = filePath; if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath)) @@ -296,11 +317,17 @@ namespace Barotrauma else { SubmarineElement = element; - } + } Name = SubmarineElement.GetAttributeString("name", null) ?? Name; Init(); + + if (lazyLoad) + { + LazyLoad = true; + SubmarineElement = null; + } } public SubmarineInfo(Submarine sub) : this(sub.Info) @@ -512,6 +539,11 @@ namespace Barotrauma if (savedSubmarines.Contains(this)) { savedSubmarines.Remove(this); } } + public void UnloadSubmarineElement() + { + SubmarineElement = null; + } + public bool IsVanillaSubmarine() { if (FilePath == null) { return false; } @@ -689,7 +721,7 @@ namespace Barotrauma RemoveSavedSub(filePath); if (File.Exists(filePath)) { - var subInfo = new SubmarineInfo(filePath); + var subInfo = new SubmarineInfo(filePath, lazyLoad: true); if (!subInfo.IsFileCorrupted) { savedSubmarines.Add(subInfo); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/ChatMessage.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/ChatMessage.cs index 912acd019..0b78817b7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/ChatMessage.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/ChatMessage.cs @@ -21,6 +21,7 @@ namespace Barotrauma.Networking ServerMessageBox = 10, ServerMessageBoxInGame = 11, Team = 12, + BlockedBySpamFilter = 13, } public enum PlayerConnectionChangeType { None = 0, Joined = 1, Kicked = 2, Disconnected = 3, Banned = 4 } @@ -39,24 +40,26 @@ namespace Barotrauma.Networking /// public const float SpeakRangeVOIP = 1000.0f; + public const float BlockedBySpamFilterTime = 10.0f; + private static readonly string dateTimeFormatLongTimePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern; public static Color[] MessageColor = { new Color(190, 198, 205), //default new Color(204, 74, 78), //error - new Color(136, 177, 255), //dead + new Color(136, 177, 255), //dead new Color(157, 225, 160), //server new Color(238, 208, 0), //radio new Color(64, 240, 89), //private new Color(255, 255, 255), //console new Color(255, 255, 255), //messagebox - new Color(255, 128, 0), //order - new Color(), // ServerLog - new Color(), // ServerMessageBox - new Color(), // ServerMessageBoxInGame - //new Color(128, 0, 255), // team - new Color(86, 91, 205), // team + new Color(255, 128, 0), //order + new Color(), // ServerLog + new Color(), // ServerMessageBox + new Color(), // ServerMessageBoxInGame + new Color(86, 91, 205), // team + new Color(255, 0, 0), // blocked by spam filter }; public string Text; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs index 85b8d2872..c59fd4ec9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs @@ -92,13 +92,6 @@ namespace Barotrauma.Networking CharacterID = value.ID; } } - else - { - if (value != null) - { - DebugConsole.NewMessage(value.Name, Color.Yellow); - } - } character = value; if (character != null) { @@ -272,12 +265,15 @@ namespace Barotrauma.Networking SetPermissions(permissions, permittedCommands); } - public static string SanitizeName(string name) + /// + /// Strips out newlines and some common non-renderable symbols (ASCII codes below 32) out of the name, and optionally limits the maximum size. + /// + public static string SanitizeName(string name, int maxLength = MaxNameLength) { - name = name.Trim(); - if (name.Length > MaxNameLength) + name = name.Trim().Replace("\r\n", " ").Replace('\n', ' ').Replace('\r', ' '); + if (name.Length > maxLength) { - name = name.Substring(0, MaxNameLength); + name = name.Substring(0, maxLength); } string rName = ""; for (int i = 0; i < name.Length; i++) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs index 0b85e7ac2..87c9cda4a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs @@ -377,6 +377,7 @@ namespace Barotrauma if (IsInRemoveQueue(item) || item.Removed) { return; } spawnOrRemoveQueue.Enqueue(item); + item.IsInRemoveQueue = true; foreach (var containedItem in item.ContainedItems) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs index f0b1e6345..252040def 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs @@ -1,8 +1,6 @@ -using Barotrauma.Items.Components; -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using System; using System.Collections.Generic; -using System.Linq; namespace Barotrauma.Networking { @@ -104,7 +102,10 @@ namespace Barotrauma.Networking CIRCUITBOX, MONEY, READY_CHECK, //start, end and update a ready check + UNLOCKRECIPE, //unlocking a fabrication recipe + SEND_BACKUP_INDICES, // the server sends a list of available backups for a save file + LUA_NET_MESSAGE } enum ServerNetSegment diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/Auth/SteamAuthTicketForEosHostAuthenticator.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/Auth/SteamAuthTicketForEosHostAuthenticator.cs index 1202e1052..ff2788158 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/Auth/SteamAuthTicketForEosHostAuthenticator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/Auth/SteamAuthTicketForEosHostAuthenticator.cs @@ -1,25 +1,23 @@ -using System; -using System.Collections.Generic; +using RestSharp; +using System; using System.Text.Json; using System.Threading.Tasks; -using RestSharp; namespace Barotrauma.Networking; sealed class SteamAuthTicketForEosHostAuthenticator : Authenticator { - #warning FIXME change URL back to the non-experimental one once this passes QA - private const string consentServerUrl = "https://barotraumagame.com/baromaster/experimental/"; - private const string consentServerFile = "getOwnerSteamId.php"; + private const string ServerUrl = "https://barotraumagame.com/baromaster/"; + private const string ServerFile = "getOwnerSteamId.php"; private const int RemoteRequestVersion = 1; public override async Task VerifyTicket(AuthenticationTicket ticket) { string ticketData = ToolBoxCore.ByteArrayToHexString(ticket.Data); - var client = new RestClient(consentServerUrl); + var client = new RestClient(ServerUrl); - var request = new RestRequest(consentServerFile, Method.GET); + var request = new RestRequest(ServerFile, Method.GET); request.AddParameter("authticket", ticketData); request.AddParameter("request_version", RemoteRequestVersion); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Prefabs/IImplementsVariants.cs b/Barotrauma/BarotraumaShared/SharedSource/Prefabs/IImplementsVariants.cs index 568235ecd..1c70a649d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Prefabs/IImplementsVariants.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Prefabs/IImplementsVariants.cs @@ -57,6 +57,18 @@ namespace Barotrauma { ReplaceAttribute(element, attribute); } + + //collect the names of the elements that we want to remove all instances of + //(e.g. if a variant wants to remove all fabrication recipes) + List elementNamesToRemove = new List(); + foreach (var subElement in replacement.Elements()) + { + if (subElement.Name.ToString().Equals("clearall", StringComparison.OrdinalIgnoreCase)) + { + elementNamesToRemove.AddRange(subElement.Elements().Select(e => e.Name.ToIdentifier())); + } + } + foreach (XElement replacementSubElement in replacement.Elements()) { int index = replacement.Elements().ToList().FindAll(e => e.Name.ToString().Equals(replacementSubElement.Name.ToString(), StringComparison.OrdinalIgnoreCase)).IndexOf(replacementSubElement); @@ -67,7 +79,17 @@ namespace Barotrauma bool cleared = false; foreach (var subElement in element.Elements()) { - if (replacementSubElement.Name.ToString().Equals("clear", StringComparison.OrdinalIgnoreCase)) + if (elementNamesToRemove.Contains(subElement.NameAsIdentifier())) + { + if (!elementsToRemove.Contains(subElement)) { elementsToRemove.Add(subElement); } + matchingElementFound = true; + continue; + } + if (replacementSubElement.Name.ToString().Equals("clearall", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + else if (replacementSubElement.Name.ToString().Equals("clear", StringComparison.OrdinalIgnoreCase)) { matchingElementFound = true; newElementsFromBase.Clear(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabCollection.cs b/Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabCollection.cs index cef4c5681..47b575be5 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabCollection.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabCollection.cs @@ -45,6 +45,20 @@ namespace Barotrauma { OnSort = onSort; } + + /// + /// For iterating through the Prefabs in a deterministic order (e.g. for map generation). Sorting is not cached, so use sparingly. + /// + public IOrderedEnumerable GetOrdered() + { + // UintIdentifier comparison is preferred to Identifier comparison that uses strings + if ((typeof(T).IsAssignableFrom(typeof(PrefabWithUintIdentifier)))) + { + return this.OrderBy(p => (p as PrefabWithUintIdentifier)!.UintIdentifier); + } + + return this.OrderBy(p => p.Identifier); + } /// /// Method to be called when calling Add(T prefab, bool override). diff --git a/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs b/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs index 930719585..754bb6d26 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs @@ -140,7 +140,10 @@ namespace Barotrauma foreach (PhysicsBody body in PhysicsBody.List) { - if (body.Enabled && body.BodyType != FarseerPhysics.BodyType.Static) + //update character (colliders) regardless if they're enabled or not, so that the draw position is updated + //necessary to sync the character's position even if the character is ragdolled and the collider is disabled + if ((body.Enabled || body.UserData is Character) && + body.BodyType != BodyType.Static) { body.Update(); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Serialization/SerializableProperty/SerializableProperty.cs b/Barotrauma/BarotraumaShared/SharedSource/Serialization/SerializableProperty/SerializableProperty.cs index f3979fd50..ab31f4582 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Serialization/SerializableProperty/SerializableProperty.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Serialization/SerializableProperty/SerializableProperty.cs @@ -1,6 +1,7 @@ using Barotrauma.Extensions; using Barotrauma.Items.Components; using Microsoft.Xna.Framework; +using RestSharp.Extensions; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -12,6 +13,10 @@ using System.Xml.Linq; namespace Barotrauma { + /// + /// Is the value of the property saved when saving (serializing) the entity? + /// Can be set to false if e.g. the value doesn't ever change from the prefab value, or if changes to it shouldn't persist between rounds. + /// public enum IsPropertySaveable { Yes, @@ -883,7 +888,16 @@ namespace Barotrauma public static Dictionary DeserializeProperties(object obj, XElement element = null) { Dictionary dictionary = GetProperties(obj); - +#if DEBUG + var nonPublicProperties = obj.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance); + foreach (var property in nonPublicProperties) + { + if (property.GetAttribute() != null) + { + DebugConsole.ThrowError($"The property {property.Name} in class {obj.GetType()} is set as serializable, but isn't public. Serializable properties must have at least a public getter."); + } + } +#endif foreach (var property in dictionary.Values) { //set the value of the property to the default value if there is one diff --git a/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs b/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs index 0d1fa0fcd..b04914057 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs @@ -239,6 +239,16 @@ namespace Barotrauma return element.GetAttributeIdentifierArray(key, null, trim)?.ToImmutableHashSet() ?? defaultValue; } + + public static float? GetAttributeNullableFloat(this XElement element, string attributeName) + { + if (element.GetAttribute(attributeName) is XAttribute attribute) + { + // if there is an error in parsing, we will return the default value, which may cause edge case errors down the line + return GetAttributeFloat(attribute, 0.0f); + } + return null; + } public static float GetAttributeFloat(this XElement element, float defaultValue, params string[] matchingAttributeName) { @@ -253,7 +263,7 @@ namespace Barotrauma return defaultValue; } - + public static float GetAttributeFloat(this XElement element, string name, float defaultValue) => GetAttributeFloat(element?.GetAttribute(name), defaultValue); public static float GetAttributeFloat(this XAttribute attribute, float defaultValue) @@ -352,6 +362,16 @@ namespace Barotrauma } return false; } + + public static int? GetAttributeNullableInt(this XElement element, string attributeName) + { + if (element.GetAttribute(attributeName) is XAttribute attribute) + { + // if there is an error in parsing, we will return the default value, which may cause edge case errors down the line + return GetAttributeInt(attribute, 0); + } + return null; + } public static int GetAttributeInt(this XElement element, string name, int defaultValue) => GetAttributeInt(element?.GetAttribute(name), defaultValue); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs b/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs index 540696e3c..1f5e2d51b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs @@ -632,7 +632,7 @@ namespace Barotrauma GameMain.SoundManager?.ApplySettings(); #endif - if (languageChanged) { TextManager.ClearCache(); } + if (languageChanged) { TextManager.LanguageChanged(); } } public static void SaveCurrentConfig() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Sprite/SpriteSheet.cs b/Barotrauma/BarotraumaShared/SharedSource/Sprite/SpriteSheet.cs index b9164a09a..1383fe3bb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Sprite/SpriteSheet.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Sprite/SpriteSheet.cs @@ -1,6 +1,5 @@ using Microsoft.Xna.Framework; using System; -using System.Xml.Linq; namespace Barotrauma { diff --git a/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/DelayedEffect.cs b/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/DelayedEffect.cs index 11c2e0ef2..5a72a36cb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/DelayedEffect.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/DelayedEffect.cs @@ -1,10 +1,8 @@ -using System; +using Barotrauma.Items.Components; +using Microsoft.Xna.Framework; +using System; using System.Collections.Generic; using System.Linq; -using System.Xml.Linq; -using Barotrauma.Extensions; -using Barotrauma.Items.Components; -using Microsoft.Xna.Framework; namespace Barotrauma { @@ -12,7 +10,11 @@ namespace Barotrauma { public readonly DelayedEffect Parent; public readonly Entity Entity; - public readonly Vector2? WorldPosition; + public Vector2? WorldPosition; + /// + /// Should the delayed effect attempt to determine the position of the effect based on the targets, or just use the position that was passed to the constructor. + /// + public bool GetPositionBasedOnTargets; public readonly Vector2? StartPosition; public readonly List Targets; public float Delay; @@ -62,7 +64,10 @@ namespace Barotrauma { foreach (var existingEffect in DelayList) { - if (existingEffect.Parent == this && existingEffect.Targets.FirstOrDefault() == target) { return; } + if (existingEffect.Parent == this && existingEffect.Targets.FirstOrDefault() == target) + { + return; + } } } if (!IsValidTarget(target)) { return; } @@ -74,7 +79,11 @@ namespace Barotrauma switch (delayType) { case DelayTypes.Timer: - DelayList.Add(new DelayedListElement(this, entity, currentTargets, delay, worldPosition ?? GetPosition(entity, currentTargets, worldPosition), startPosition: null)); + var newDelayListElement = new DelayedListElement(this, entity, currentTargets, delay, worldPosition ?? GetPosition(entity, currentTargets, worldPosition), startPosition: null) + { + GetPositionBasedOnTargets = worldPosition == null + }; + DelayList.Add(newDelayListElement); break; case DelayTypes.ReachCursor: Projectile projectile = (entity as Item)?.GetComponent(); @@ -171,7 +180,16 @@ namespace Barotrauma { case DelayTypes.Timer: element.Delay -= deltaTime; - if (element.Delay > 0.0f) { continue; } + if (element.Delay > 0.0f) + { + //if the delayed effect is supposed to get the position from the targets, + //keep refreshing the position until the effect runs (so e.g. a delayed effect runs at the last known position of a monster before it despawned) + if (element.GetPositionBasedOnTargets && element.Entity is { Removed: false }) + { + element.WorldPosition = element.Parent.GetPosition(element.Entity, element.Parent.currentTargets); + } + continue; + } break; case DelayTypes.ReachCursor: if (Vector2.Distance(element.Entity.WorldPosition, element.StartPosition.Value) < element.Delay) { continue; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs b/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs index f38593ed1..d21662483 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs @@ -5,6 +5,7 @@ using Barotrauma.Networking; using FarseerPhysics; using FarseerPhysics.Dynamics; using Microsoft.Xna.Framework; +using Steamworks; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -639,6 +640,10 @@ namespace Barotrauma /// Should the sound(s) configured in the effect be played if the required items aren't found? /// private readonly bool playSoundOnRequiredItemFailure = false; + + public readonly record struct SteamTimeLineEvent(string title, string description, string icon); + private readonly SteamTimeLineEvent steamTimeLineEventToTrigger; + #endif private readonly int useItemCount; @@ -716,6 +721,11 @@ namespace Barotrauma /// private readonly List<(Identifier eventIdentifier, Identifier tag)> eventTargetTags; + /// + /// Can be used to make the effect unlock a fabrication recipe globally for the entire crew. + /// + public readonly Identifier UnlockRecipe; + private Character user; public readonly float FireSize; @@ -928,6 +938,8 @@ namespace Barotrauma playSoundOnRequiredItemFailure = element.GetAttributeBool("playsoundonrequireditemfailure", false); #endif + UnlockRecipe = element.GetAttributeIdentifier(nameof(UnlockRecipe), Identifier.Empty); + List propertyAttributes = new List(); propertyConditionals = new List(); foreach (XAttribute attribute in element.Attributes()) @@ -1245,6 +1257,26 @@ namespace Barotrauma forceSayIdentifier = subElement.GetAttributeIdentifier("message", Identifier.Empty); forceSayInRadio = subElement.GetAttributeBool("sayinradio", false); break; +#if CLIENT + case "steamtimelineevent": + steamTimeLineEventToTrigger = new SteamTimeLineEvent( + subElement.GetAttributeString("title", string.Empty), + subElement.GetAttributeString("description", string.Empty), + subElement.GetAttributeString("icon", string.Empty)); + if (steamTimeLineEventToTrigger.title.IsNullOrWhiteSpace()) + { + DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - steam timeline event has no title.", contentPackage: element.ContentPackage); + } + if (steamTimeLineEventToTrigger.description.IsNullOrWhiteSpace()) + { + DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - steam timeline event has no description.", contentPackage: element.ContentPackage); + } + if (steamTimeLineEventToTrigger.icon.IsNullOrWhiteSpace()) + { + DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - steam timeline event has no icon.", contentPackage: element.ContentPackage); + } + break; +#endif } } InitProjSpecific(element, parentDebugName); @@ -2155,6 +2187,11 @@ namespace Barotrauma fire.Size = new Vector2(FireSize, fire.Size.Y); } + if (isNotClient && !UnlockRecipe.IsEmpty && GameMain.GameSession is { } gameSession) + { + gameSession.UnlockRecipe(UnlockRecipe, showNotifications: true); + } + if (isNotClient && triggeredEvents != null && GameMain.GameSession?.EventManager is { } eventManager) { foreach (EventPrefab eventPrefab in triggeredEvents) @@ -2522,6 +2559,12 @@ namespace Barotrauma { rotation = parentItemBody.TransformRotation(chosenItemSpawnInfo.RotationRad); } + else if (parentItem != null) + { + rotation = PhysicsBody.TransformRotation( + -parentItem.RotationRad + chosenItemSpawnInfo.RotationRad, + dir: parentItem.FlippedX ? -1.0f : 1.0f); + } break; case ItemSpawnInfo.SpawnRotationType.Target: if (!entity.Removed) @@ -2589,11 +2632,17 @@ namespace Barotrauma projectile.Shoot(user, spawnPos, spawnPos, rotation, ignoredBodies: ignoredBodies, createNetworkEvent: true, damageMultiplier: damageMultiplier); projectile.Item.Submarine = projectile.LaunchSub = sourceEntity?.Submarine; } - else if (newItem.body != null) + else { - newItem.body.SetTransform(newItem.SimPosition, rotation); - Vector2 impulseDir = new Vector2(MathF.Cos(rotation), MathF.Sin(rotation)); - newItem.body.ApplyLinearImpulse(impulseDir * chosenItemSpawnInfo.Impulse); + if (newItem.body != null) + { + //flipped on one axis = need to flip the rotation of the item (not if flipped on both, that's essentially double negation) + bool flip = parentItem is { FlippedX: true } != parentItem is { FlippedY: true }; + newItem.body.Dir = flip ? -1 : 1; + newItem.body.SetTransform(newItem.SimPosition, flip ? rotation - MathHelper.Pi : rotation); + Vector2 impulseDir = new Vector2(MathF.Cos(rotation), MathF.Sin(rotation)); + newItem.body.ApplyLinearImpulse(impulseDir * chosenItemSpawnInfo.Impulse); + } } } OnItemSpawned(newItem, chosenItemSpawnInfo); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Steam/AuthTicket.cs b/Barotrauma/BarotraumaShared/SharedSource/Steam/AuthTicket.cs index a41889ad4..bd5b5811e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Steam/AuthTicket.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Steam/AuthTicket.cs @@ -44,8 +44,8 @@ namespace Barotrauma.Steam #region Auth ticket for EOS host private const string EosHostAuthIdentity = "BarotraumaRemotePlayerAuth"; - private static Option currentEosHostAuthTicket = Option.None; - public static async Task> GetAuthTicketForEosHostAuth() + private static Option currentEosHostAuthTicket = Option.None; + public static async Task> GetAuthTicketForEosHostAuth() { if (!IsInitialized) { @@ -58,7 +58,7 @@ namespace Barotrauma.Steam } currentEosHostAuthTicket = Option.None; - var newTicket = await Steamworks.SteamUser.GetAuthTicketForWebApi(identity: EosHostAuthIdentity); + var newTicket = await Steamworks.SteamUser.GetAuthTicketForWebApiAsync(identity: EosHostAuthIdentity); currentEosHostAuthTicket = newTicket != null ? Option.Some(newTicket) @@ -71,8 +71,8 @@ namespace Barotrauma.Steam #region Auth ticket for GameAnalytics consent server private const string GameAnalyticsConsentIdentity = "BarotraumaGameAnalyticsConsent"; - private static Option currentGameAnalyticsConsentTicket = Option.None; - public static async Task> GetAuthTicketForGameAnalyticsConsent() + private static Option currentGameAnalyticsConsentTicket = Option.None; + public static async Task> GetAuthTicketForGameAnalyticsConsent() { if (!IsInitialized) { @@ -85,7 +85,7 @@ namespace Barotrauma.Steam } currentGameAnalyticsConsentTicket = Option.None; - var newTicket = await Steamworks.SteamUser.GetAuthTicketForWebApi(identity: GameAnalyticsConsentIdentity); + var newTicket = await Steamworks.SteamUser.GetAuthTicketForWebApiAsync(identity: GameAnalyticsConsentIdentity); currentGameAnalyticsConsentTicket = newTicket != null ? Option.Some(newTicket) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Steam/SteamManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Steam/SteamManager.cs index 18f992415..0b709c74c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Steam/SteamManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Steam/SteamManager.cs @@ -247,6 +247,9 @@ namespace Barotrauma.Steam //this should be run even if SteamManager is uninitialized //servers need to be able to notify clients of unlocked talents even if the server isn't connected to Steam AchievementManager.Update(deltaTime); +#if CLIENT + SteamTimelineManager.Update(deltaTime); +#endif if (!IsInitialized) { return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Tags.cs b/Barotrauma/BarotraumaShared/SharedSource/Tags.cs index e398bed82..6af96e15e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Tags.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Tags.cs @@ -164,5 +164,9 @@ public static class Tags public static readonly Identifier Decoy = "decoy".ToIdentifier(); public static readonly Identifier Provocative = "provocative".ToIdentifier(); + /// + /// Tag used on status effects that mark the entity as being on fire. + /// + public static readonly Identifier OnFireStatusEffectTag = "onfire".ToIdentifier(); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Text/TextManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Text/TextManager.cs index 99334e23f..d0035a9ab 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Text/TextManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Text/TextManager.cs @@ -295,6 +295,25 @@ namespace Barotrauma return TextPacks[languageIdentifier].First().TranslatedName; } + public static void LanguageChanged() + { + foreach ((LanguageIdentifier language, ImmutableList textPacks) in TextPacks) + { + foreach (TextPack textPack in textPacks) + { + if (GameSettings.CurrentConfig.Language == language) + { + textPack.VerifyLoaded(); + } + else + { + textPack.Unload(); + } + } + } + ClearCache(); + } + public static void ClearCache() { lock (cachedStrings) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Text/TextPack.cs b/Barotrauma/BarotraumaShared/SharedSource/Text/TextPack.cs index 142343aaf..19ee41a2f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Text/TextPack.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Text/TextPack.cs @@ -48,17 +48,30 @@ namespace Barotrauma public readonly LanguageIdentifier Language; - public readonly record struct Text( string String, bool IsOverride, TextPack TextPack); - public readonly ImmutableDictionary> Texts; + + private ImmutableDictionary> texts; + + public ImmutableDictionary> Texts + { + get + { + if (texts == null) + { + DebugConsole.NewMessage($"Accessed texts in an unloaded text package ({Language}). Loading the text pack..."); + VerifyLoaded(); + } + return texts; + } + } public readonly string TranslatedName; public readonly bool NoWhitespace; - public TextPack(TextFile file, ContentXElement mainElement, LanguageIdentifier language) + public TextPack(TextFile file, ContentXElement mainElement, LanguageIdentifier language, bool load = false) { ContentFile = file; @@ -73,6 +86,20 @@ namespace Barotrauma TranslatedName = mainElement.GetAttributeString("translatedname", languageName.Value); NoWhitespace = mainElement.GetAttributeBool("nowhitespace", false); + if (load) + { + VerifyLoaded(); + } + } + + public void VerifyLoaded() + { + //already loaded + if (this.texts != null) { return; } + + XDocument doc = XMLExtensions.TryLoadXml(ContentFile.Path); + var mainElement = doc.Root.FromPackage(ContentFile.ContentPackage); + Dictionary> texts = new Dictionary>(); LoadElements(mainElement, isOverride: mainElement.IsOverride()); @@ -103,9 +130,14 @@ namespace Barotrauma } } - Texts = texts.Select(kvp => (kvp.Key, kvp.Value.ToImmutableArray())).ToImmutableDictionary(); + this.texts = texts.Select(kvp => (kvp.Key, kvp.Value.ToImmutableArray())).ToImmutableDictionary(); } - + + public void Unload() + { + texts = null; + } + #if DEBUG public void CheckForDuplicates(int index) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Utils/Rand.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/Rand.cs index 84d609f92..3559d4313 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Utils/Rand.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Utils/Rand.cs @@ -11,9 +11,22 @@ namespace Barotrauma { public enum RandSync { - Unsynced, //not synced, used for unimportant details like minor particle properties - ServerAndClient, //synced with the server (used for gameplay elements that the players can interact with) + /// + /// Not synced, used for unimportant details like minor particle properties. + /// + Unsynced, + /// + /// An RNG instance shared by the client and the server is used to generate the values. + /// Please note that just using RandSync.ServerAndClient DOES NOT guarantee that the server and the client will generate the same values when doing some arbitrary calls to the methods in this class. + /// The values are only guaranteed to be the same if the same seed () is set on both sides, and then the same sequence of calls is made. + /// + ServerAndClient, #if CLIENT + /// + /// An RNG instance shared by different clients. Used for things that the server doesn't track, but clients want to match anyway (e.g. certain level visuals). + /// Please note that just using RandSync.ClientOnly DOES NOT guarantee that the clients will generate the same values when doing some arbitrary calls to the methods in this class. + /// The values are only guaranteed to be the same if the same seed () is set on both sides, and then the same sequence of calls is made. + /// ClientOnly //set to match between clients (used for misc elements that the server doesn't track, but clients want to match anyway) #endif } diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 0c63ae6ec..93a55aa61 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,4 +1,126 @@ ------------------------------------------------------------------------------------------------------------------------------------------------- +v1.9.7.0 +------------------------------------------------------------------------------------------------------------------------------------------------- + +Multiplayer: +- Fixed anyone being allowed to modify turrets and the sub's upgrades. Now it requires campaign management permissions. + +Modding: +- Fixed only the first OnInserted effect defined for an item being executed when the item is placed in a container. +- Fixed conditional's TargetSelf property not working on melee weapons. + +------------------------------------------------------------------------------------------------------------------------------------------------- +v1.9.6.0 +------------------------------------------------------------------------------------------------------------------------------------------------- + +Changes and additions: +- Added Power Distributor item. Includes 8 power outputs and sliders that can be used to control how much of the input power is distributed to each output. Can be used to, for example, build a circuit that redirects power from less vital systems to weapons or pumps in an emergency. +- Added Rifle Scope. +- Better Steam Timeline support: when recording gameplay using Steam, the game adds markers about notable events in the game. +- Added scaling and rotation tools to the submarine editor. Enabled using buttons in the top-right corner of the editor. +- Characters don't throw flares when also holding a ranged weapon (allows shooting with a flare in hand). +- Improvements to pets (kudos to uberpendragon): improved ragdolls and animations, gave them head slots so they can wear hats, added damage modifiers to shells. +- Mindwipe resets the extra talents gained via the assistant's Apprentice talent tree, preventing the use of Mindwipes as a way to make an assistant unlock the specialization trees of all classes. +- Alien Blood is now less powerful, by curing less "Bloodloss" affliction and causing more psychosis. It is widely available and made saline and blood packs pretty redundant. It now also requires more medical skill. +- Added option to filter by name and to change the sorting of the save files listed in the "load game" menu. +- Made circuit boxes' external connections (the once that hook up to other items) use the same labels that have been set inside the circuit box. +- Made the freecam console command a toggle: entering it again gives you back control of the character. +- Added "loslightingfreecam" console command (convenient for testing, executes those 3 commands). +- Upgraded to .NET 8. This should not cause any noticeable changes, aside from perhaps very minor performance improvements. Allows code mods to use C# 12 features. + +Multiplayer: +- Fixed sending rapid reports of leaks, fires, enemies etc easily causing the spam filter to kick players with no warning. +- Added buttons for selecting or deselecting all mission types to the server lobby's mission type selection. +- Fixed wall damage desyncing if you purchase wall repairs in an outpost, undock and then save and reload (the walls would appear repaired client-side, even though they're actually broken server-side). +- Fixed the "nudge buttons" that appear next to the nav terminal's docking button not working in multiplayer. +- Fixed oxygen and fuel tank explosions not appearing client-side in multiplayer, making it seem like you're taking burn damage out of nowhere when you happened to be near one an "infinite explosion loop" (described below in the fixes section). +- Fixed players only getting a karma penalty for overheating the reactor if they stay at the reactor. +- Fixed another networking issue that sometimes caused the biome and outpost selection dropdowns to break in the server lobby, causing the clients to constantly spam the server with requests to change the biome/outpost. + +Optimization: +- Optimized memory usage of text files. Now only the text files for the language you have currently selected are kept in memory. +- Optimized the memory usage of submarine files. Previously the XML of all the enabled submarines was kept in memory, causing excessive memory usage when you have a ton of custom submarines installed. + +Fixes: +- Fixed oxygen tank shelves sometimes causing "infinite explosion loops", when tanks in the shelves exploded other tanks while the shelves were filling the tanks. +- Fixed camera not following your character when ragdolled/unconscious. +- Fixed "unsubscribe" option not showing up on the mod list on mods that have pending updates. +- Fixed NPCs spawned by events sometimes spawning inside jail cells in outposts. +- Fixed clowns never turning psychotic in the clown escort missions (there should be a small chance of that happening as the mission progresses). +- Fixed talent progress not being mentioned in the tooltip on "extra talents" (talents unlocked from outside the tree). +- Fixed salt bulbs exploding immediately in water (as opposed to having a small delay like other water-reactant items). +- Fixed hair and other attachment sprites still being misaligned in some UIs (character creation menu and outpost medic UI) if their origin doesn't match the head's origin. +- Fixes to several outpost and ruin generation issues: + - Pre-built "fallback outposts" sometimes spawning instead of a proper outpost, and sometimes even instead of a ruin. + - PvP outposts still sometimes generating outside the PvP mode (particularly when using mods with new location/outpost types). +- Fixed some consumables (e.g. energy drinks, beer, protein bars) getting consumed immediately if you hit E while drinking/eating. +- Fixed lights rendering upside-down on planters that have been flipped horizontally. +- Fixed inability to sit while using a surveillance center. +- Fixed option to give an item to another character showing up when you drag a large item (an item that won't go in the inventory slots) over someone, even though these items can't be given by dragging and dropping. +- Fixed ability to give characters multi-line names, which would break some UI elements. +- Fixed spawncharacter console command spawning monsters in the player team if the team argument is not given. +- Fixed R-29's bottom wall not leaking. Might've affected other submarines as well: there was a bug in the logic that prevents overlapping walls leaking faster than individual walls, which caused the game to sometimes interpret large gaps next to the wall as "overlapping walls". +- Fixed fabricator no longer communicating in any way when an item can't be fabricated because it requires a recipe. +- Fixed inability to focus on unconscious characters by clicking on the crew list. +- Fixed parts of the Jove sculptures found in ruins being misaligned in mirrored levels (= when travelling through the level backwards). +- Fixed the Multi-tool not functioning as a screwdriver for event checks. +- Fixed "fake fires" you see when under psychosis sometimes turning into real fires in single player. +- Fixed PvP mode weapon crates showing up in the sub editor. +- Fixed undoing the removal of an item from a container sometimes causing a crash in the sub editor. +- Fixed hulls extending below the floor in Kastrull's drone and the lowest deck, preventing them from draining fully. +- Fixed endworm's jaws sometimes doing AOE damage to characters after the endworm has died. +- Fixed rotated structures not appearing rotated on the sonar. +- Fixed talent refund points getting lost on save and reload. +- Fixed multi-part subs (subs consisting of multiple docked subs with other subs ducked to them) getting flipped incorrectly in PvP mode. +- Fixed "teleportsub endoutpost" crashing the game when a level is not loaded (e.g. in the sub editor's test mode). +- Fixed selected outpost module sometimes not loading in the sub editor's test mode, and a random outpost generating instead. +- Fixed pre-built "fallback outposts" failing to load in the sub editor (generating a full outpost instead). +- Fixed ability to put items in locked containers (e.g. running deconstructors) by hitting the drop key. +- Fixed wearing "mother's providence" and "mother's countenance" (special clown suits worn by the Jestmasters) not unlocking the "Praise the Honkmother" achievement. +- Fixed "secondary explosion" 2 seconds after a gravity shell has exploded and disappeared. +- Yet another fix to contained items rendering incorrectly on flipped items: affected contained items that are set to draw at an angle of 90 degrees relative to the container (e.g. fuel rod in a rapid fissile accelerator), causing them to draw backwards. +- Hid PvP weapon crates in the sub editor (because these require the PvP event and do nothing by themselves when placed in the editor). +- Fixed inability to make motion sensors react to non-humans that are in the "human" group (e.g. some custom android type of character) - they were not considered either humans, pets or monsters. Now characters in the human group are treated as humans, except if they're pets (in that case the sensor only triggers if set to trigger on pets). +- Fixed "firing blanks" traitor event requiring you to replace standard coilgun ammo boxes with fake ones, instead of accepting any type of coilgun ammo. +- Fixed PvP variants of underwater scooters sometimes spawning in cabinets in the campaign. + +Modding: +- Better support for configuring custom location types and configuring where they appear. Examples in the locationTypes.xml file. + - You can spawn a fixed number of a location type on the map (e.g. to have unique named locations). + - More control where specific location types spawn (e.g. near the beginning of a biome). + - Location types can be configured to "inherit" things such as event or outpost modules from another location type. +- Escort mission fixes and improvements: + - Fixed ""terroristAnnounceDialogTag" being completely ignored, making the terrorists always say the default separatist lines regardless of what's configured in XML. + - Added support for spawning multiple items for the terrorist, or optionally choosing random ones using a "ChooseRandom" element. Previously only one random item was chosen. + - Added some documentation for the mission type. +- Added support for making an item variant clear all elements of a certain type from the XML. Done by adding the element to the item variant, and then defining the elements to clear as it's child elements (e.g. would clear all fabrication recipes). +- Support for streaming any audio clip instead of loading them into memory (previously streaming was only supported for background music). Enabled by adding stream="true" to the sound element. Recommended for longer audio clips (such as music tracks played by items). Please note though that streaming audio comes with some CPU and IO overhead, so it's not recommended to blindly enable it on everything. +- Fixed specific kinds of looping sounds sometimes not playing in multiplayer (specifically, long sounds that are triggered right at the start of the round by a status effect, but not in the player's hearing range, e.g. rave music in Dynamic Europa's pirate outposts). +- Fixed NPCs made to hold an item using NPCOperateItemAction repeatedly equipping and unequipping it. +- Fixed delayed effects executing at the position where the entity was when the effect was triggered, not where the entity is when the delay passes. +- Fixed salvage missions causing a crash and the "interact" target type not working if the target item has no physics body. +- Fixed performance issue caused by element when using an invalid item identifier. +- Fixed colored text not working in the options in conversation prompts. +- Added CameraAimOffset property to Holdable component. Allows modifying how far the camera can be offset when aiming, making it possible to implement e.g. scopes. +- Added AbandonOnReset property to CombatAction and NPCWaitAction. +- Fixed some issues with PvP outpost selection that made it difficult to implement custom maps: + - If a mission was set to use a PvP outpost with some custom tag, the game might still not use it, because it chose the PvP level generation parameters, which would enforce an outpost with the vanilla "PvPOutpost" tag. Now the game tries to choose an outpost with the specified tag regardless of the level generation parameters. + - When "random" was selected on the outpost dropdown, the game would choose any random outpost without taking the selected mission into account. +- Fixed sounds configured to loop while an item is worn not stopping when the item is unequipped. Did not affect any vanilla items. +- Option to force a specific type of location as the campaign's start location by adding ForceAsStartOutpost="true" to the location type config. Ignored when the tutorial is enabled. +- Option to disallow attaching items over specific kinds of other items (e.g. DisallowAttachingOverTags="container,wallattachment"). +- Fixed inability to make motion sensors react to non-humans that are in the "human" group (e.g. some custom android type of character) - they were not considered either humans, pets or monsters. Now characters in the human group are treated as humans, except if they're pets (in that case the sensor only triggers if set to trigger on pets). +- Fixed OnInserted status effects triggering when loading a submarine/container which already contains items. Now the effects only trigger when the item is placed in the container. +- Fixed ability to equip/unequip noninteractable items using the "quick use" button above the slot. Only affects modded content (non-interactable items that are forced into a player inventory). +- Fixed Items' HasBeenInstantiatedOnce property not being saved, making it basically unusable. +- Fabrication recipes can be unlocked using StatusEffects (e.g. unlockrecipe="autoinjectorheadset"). +- Fixed SpawnRotationType.This not doing anything if the item executing the effect doesn't have a physics body, or if it's attached to a wall (which makes the item "temporarily" not have a body). +- Hidden missions (ShowInMenus="false") don't show up on the campaign map nor the mission selection panel. In the vanilla game, all hidden mission were triggered when the round starts so they wouldn't be visible there anyway, but that may not be the case with modded missions. +- Added CauseOfDeathType property to Character. Not used by the vanilla game, but can be used by mods to check the cause of death of the character using conditionals (e.g. if some OnDeath effects should or should not be triggered by certain causes of death). +- Support for animated affliction overlays (works the same as the animated HUD overlays on items such as the surveillance cameras). +- More options for configuring how Holdable items can be attached to the sub: AttachesToFloor (when enabled, the item can only be attached to a position where it touches the floor), AllowAttachInsideDoors (enabled by default), DisallowAttachingOverTags (tags of items the item cannot attach over). + +------------------------------------------------------------------------------------------------------------------------------------------------- v1.8.8.1 ------------------------------------------------------------------------------------------------------------------------------------------------- @@ -17,6 +139,7 @@ v1.8.8.1 Modding: - Fixed transferring afflictions to a newly spawned character using status effects causing a crash if the original character had already been removed. Didn't affect any vanilla content. +>>>>>>> master ------------------------------------------------------------------------------------------------------------------------------------------------- v1.8.7.0 diff --git a/Barotrauma/BarotraumaTest/LinuxTest.csproj b/Barotrauma/BarotraumaTest/LinuxTest.csproj index ec8379055..51555d397 100644 --- a/Barotrauma/BarotraumaTest/LinuxTest.csproj +++ b/Barotrauma/BarotraumaTest/LinuxTest.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable false diff --git a/Barotrauma/BarotraumaTest/MacTest.csproj b/Barotrauma/BarotraumaTest/MacTest.csproj index cda656429..b190a0e0d 100644 --- a/Barotrauma/BarotraumaTest/MacTest.csproj +++ b/Barotrauma/BarotraumaTest/MacTest.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable false diff --git a/Barotrauma/BarotraumaTest/WindowsTest.csproj b/Barotrauma/BarotraumaTest/WindowsTest.csproj index a64bc5835..e1babd296 100644 --- a/Barotrauma/BarotraumaTest/WindowsTest.csproj +++ b/Barotrauma/BarotraumaTest/WindowsTest.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable false diff --git a/Deploy/DeployAll/DeployAll.csproj b/Deploy/DeployAll/DeployAll.csproj index 72669ffd0..0e1e96340 100644 --- a/Deploy/DeployAll/DeployAll.csproj +++ b/Deploy/DeployAll/DeployAll.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 disable enable diff --git a/Deploy/DeployAll/DotnetCmd.cs b/Deploy/DeployAll/DotnetCmd.cs index 2ce19f8e6..c83839c2b 100644 --- a/Deploy/DeployAll/DotnetCmd.cs +++ b/Deploy/DeployAll/DotnetCmd.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -16,7 +16,7 @@ public static class DotnetCmd { private const string DotnetAppName = "dotnet"; - private const string DesiredRuntimeVersion = "6.0.8"; + private const string DesiredRuntimeVersion = "8.0.0"; public static void Publish(string projPath, string configuration, string runtime, string resultPath) { diff --git a/Libraries/BarotraumaLibs/BarotraumaCore/BarotraumaCore.csproj b/Libraries/BarotraumaLibs/BarotraumaCore/BarotraumaCore.csproj index 693263fbc..43147b2b5 100644 --- a/Libraries/BarotraumaLibs/BarotraumaCore/BarotraumaCore.csproj +++ b/Libraries/BarotraumaLibs/BarotraumaCore/BarotraumaCore.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 Barotrauma disable enable diff --git a/Libraries/BarotraumaLibs/BarotraumaCore/Extensions/EnumerableExtensionsCore.cs b/Libraries/BarotraumaLibs/BarotraumaCore/Extensions/EnumerableExtensionsCore.cs index e78ac9442..25da635d3 100644 --- a/Libraries/BarotraumaLibs/BarotraumaCore/Extensions/EnumerableExtensionsCore.cs +++ b/Libraries/BarotraumaLibs/BarotraumaCore/Extensions/EnumerableExtensionsCore.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace Barotrauma.Extensions; @@ -9,18 +10,9 @@ public static class EnumerableExtensionsCore public static ImmutableDictionary ToImmutableDictionary(this IEnumerable<(TKey, TValue)> enumerable) where TKey : notnull { - return enumerable.ToDictionary().ToImmutableDictionary(); - } - - public static Dictionary ToDictionary(this IEnumerable<(TKey, TValue)> enumerable) - where TKey : notnull - { - var dictionary = new Dictionary(); - foreach (var (k,v) in enumerable) - { - dictionary.Add(k, v); - } - return dictionary; + return enumerable + .ToDictionary(static pair => pair.Item1, static pair => pair.Item2) + .ToImmutableDictionary(); } [return: NotNullIfNotNull("immutableDictionary")] diff --git a/Libraries/BarotraumaLibs/BarotraumaCore/Extensions/RectangleExtensions.cs b/Libraries/BarotraumaLibs/BarotraumaCore/Extensions/RectangleExtensions.cs index 3c221e32e..c18de4d59 100644 --- a/Libraries/BarotraumaLibs/BarotraumaCore/Extensions/RectangleExtensions.cs +++ b/Libraries/BarotraumaLibs/BarotraumaCore/Extensions/RectangleExtensions.cs @@ -66,6 +66,15 @@ namespace Barotrauma.Extensions value.Top > bottom && rect.Top > otherBottom; } + /// + /// Converts the y-coordinate of the rectangle so that up is greater and down is lower + /// (i.e. so the Location of the rectangle is at the top-left corner in world coordinates). + /// + public static Rectangle ToWorldRect(this Rectangle rect) + { + return new Rectangle(rect.X, rect.Y - rect.Height, rect.Size.X, rect.Size.Y); + } + /// /// Like the XNA method, but treats the y-coordinate so that up is greater and down is lower. /// diff --git a/Libraries/BarotraumaLibs/BarotraumaCore/Utils/IEnumerableExtensionsCore.cs b/Libraries/BarotraumaLibs/BarotraumaCore/Utils/IEnumerableExtensionsCore.cs index 76fce3681..1b9eddbcc 100644 --- a/Libraries/BarotraumaLibs/BarotraumaCore/Utils/IEnumerableExtensionsCore.cs +++ b/Libraries/BarotraumaLibs/BarotraumaCore/Utils/IEnumerableExtensionsCore.cs @@ -69,6 +69,10 @@ public static class IEnumerableExtensionsCore } } + // Upgrading to .NET 8 seems to have caused a false positive on this line, disabled the warning for now. + // See https://github.com/dotnet/roslyn-analyzers/pull/7488 +#pragma warning disable CA2021 + public static IEnumerable Successes( this IEnumerable> source) where TSuccess : notnull @@ -84,4 +88,7 @@ public static class IEnumerableExtensionsCore => source .OfType>() .Select(f => f.Error); + +#pragma warning disable 2021 + } \ No newline at end of file diff --git a/Libraries/BarotraumaLibs/EosInterface/EosInterface.csproj b/Libraries/BarotraumaLibs/EosInterface/EosInterface.csproj index 1acbecf34..e366bb52f 100644 --- a/Libraries/BarotraumaLibs/EosInterface/EosInterface.csproj +++ b/Libraries/BarotraumaLibs/EosInterface/EosInterface.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 disable enable Barotrauma diff --git a/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.Linux.csproj b/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.Linux.csproj index 57341009e..7d6285b43 100644 --- a/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.Linux.csproj +++ b/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.Linux.csproj @@ -2,7 +2,7 @@ EosInterface.Implementation.Linux - net6.0 + net8.0 EosInterfacePrivate false disable diff --git a/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.MacOS.csproj b/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.MacOS.csproj index df8cbade0..92b296731 100644 --- a/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.MacOS.csproj +++ b/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.MacOS.csproj @@ -2,7 +2,7 @@ EosInterface.Implementation.MacOS - net6.0 + net8.0 EosInterfacePrivate false disable diff --git a/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.Win64.csproj b/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.Win64.csproj index 7af67291b..a83dbc148 100644 --- a/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.Win64.csproj +++ b/Libraries/BarotraumaLibs/EosInterfacePrivate/EosInterface.Implementation.Win64.csproj @@ -2,7 +2,7 @@ EosInterface.Implementation.Win64 - net6.0 + net8.0 EosInterfacePrivate false disable diff --git a/Libraries/BarotraumaLibs/EosInterfacePrivate/InterfaceImpl/IdAndAuth/LoginPrivate.cs b/Libraries/BarotraumaLibs/EosInterfacePrivate/InterfaceImpl/IdAndAuth/LoginPrivate.cs index 689ff998a..3b7a78fd6 100644 --- a/Libraries/BarotraumaLibs/EosInterfacePrivate/InterfaceImpl/IdAndAuth/LoginPrivate.cs +++ b/Libraries/BarotraumaLibs/EosInterfacePrivate/InterfaceImpl/IdAndAuth/LoginPrivate.cs @@ -12,7 +12,7 @@ namespace EosInterfacePrivate; static class LoginPrivate { private const string EosLoginSteamIdentity = "BarotraumaEosLogin"; - private static Option steamworksAuthTicket; + private static Option steamworksAuthTicket; private static Option eosConnectExpirationNotifyId, eosConnectStatusChangedNotifyId; private static Option egsAuthExpirationNotifyId; @@ -65,7 +65,7 @@ static class LoginPrivate { if (!Steamworks.SteamClient.IsValid || !Steamworks.SteamClient.IsLoggedOn) { return Result.Failure(EosInterface.Login.LoginError.SteamNotLoggedIn); } if (steamworksAuthTicket.TryUnwrap(out var oldTicket)) { oldTicket.Cancel(); } - var newTicketNullable = await Steamworks.SteamUser.GetAuthTicketForWebApi(EosLoginSteamIdentity); + var newTicketNullable = await Steamworks.SteamUser.GetAuthTicketForWebApiAsync(EosLoginSteamIdentity); if (newTicketNullable is not { Data: not null } ticket) { return Result.Failure(EosInterface.Login.LoginError.FailedToGetSteamSessionTicket); @@ -195,7 +195,7 @@ static class LoginPrivate public static async Task, EosInterface.Login.LoginError>> LoginEpicWithLinkedSteamAccount(EosInterface.Login.LoginEpicFlags flags) { if (steamworksAuthTicket.TryUnwrap(out var oldTicket)) { oldTicket.Cancel(); } - var newTicketNullable = await Steamworks.SteamUser.GetAuthTicketForWebApi(EosLoginSteamIdentity); + var newTicketNullable = await Steamworks.SteamUser.GetAuthTicketForWebApiAsync(EosLoginSteamIdentity); if (newTicketNullable is not { Data: not null } ticket) { return Result.Failure(EosInterface.Login.LoginError.FailedToGetSteamSessionTicket); diff --git a/Libraries/BarotraumaLibs/EosInterfacePrivate/InterfaceImpl/Util/ResultExtension.cs b/Libraries/BarotraumaLibs/EosInterfacePrivate/InterfaceImpl/Util/ResultExtension.cs index a37e80169..3a2ee338d 100644 --- a/Libraries/BarotraumaLibs/EosInterfacePrivate/InterfaceImpl/Util/ResultExtension.cs +++ b/Libraries/BarotraumaLibs/EosInterfacePrivate/InterfaceImpl/Util/ResultExtension.cs @@ -8,7 +8,7 @@ public static class ResultExtension { public static T FailAndLogUnhandledError(this Epic.OnlineServices.Result result, T unknown, [CallerMemberName] string caller = null) { - DebugConsoleCore.NewMessage($"Result \"{result}\" was not handled by \"{caller}\".", Color.Red); + DebugConsoleCore.NewMessage($"Epic Online Services Result \"{result}\" was not handled by \"{caller}\".", Color.Red); return unknown; } } \ No newline at end of file diff --git a/Libraries/Facepunch.Steamworks/Classes/AuthTicketForWebApi.cs b/Libraries/Facepunch.Steamworks/Classes/AuthTicketForWebApi.cs deleted file mode 100644 index d1a363494..000000000 --- a/Libraries/Facepunch.Steamworks/Classes/AuthTicketForWebApi.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace Steamworks; - -public class AuthTicketForWebApi : IDisposable -{ - public byte[]? Data { get; private set; } - public uint Handle { get; private set; } - - public bool Canceled { get; private set; } - - public AuthTicketForWebApi( byte[] data, uint handle ) - { - Data = data; - Handle = handle; - } - - /// - /// Cancels a ticket. - /// You should cancel your ticket when you close the game or leave a server. - /// - public void Cancel() - { - if (Handle != 0) - { - SteamUser.Internal?.CancelAuthTicket(Handle); - } - - Handle = 0; - Data = null; - Canceled = true; - } - - public void Dispose() - { - Cancel(); - } -} diff --git a/Libraries/Facepunch.Steamworks/Classes/SteamApi.cs b/Libraries/Facepunch.Steamworks/Classes/SteamApi.cs index a8b882b28..14ec7f9fe 100644 --- a/Libraries/Facepunch.Steamworks/Classes/SteamApi.cs +++ b/Libraries/Facepunch.Steamworks/Classes/SteamApi.cs @@ -11,9 +11,8 @@ namespace Steamworks { internal static class Native { - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_Init", CallingConvention = CallingConvention.Cdecl )] - [return: MarshalAs( UnmanagedType.I1 )] - public static extern bool SteamAPI_Init(); + [DllImport( Platform.LibraryName, EntryPoint = "SteamInternal_SteamAPI_Init", CallingConvention = CallingConvention.Cdecl )] + public static extern SteamAPIInitResult SteamInternal_SteamAPI_Init( IntPtr pszInternalCheckInterfaceVersions, IntPtr pOutErrMsg ); [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_Shutdown", CallingConvention = CallingConvention.Cdecl )] public static extern void SteamAPI_Shutdown(); @@ -26,9 +25,14 @@ namespace Steamworks public static extern bool SteamAPI_RestartAppIfNecessary( uint unOwnAppID ); } - static internal bool Init() + + static internal SteamAPIInitResult Init( string pszInternalCheckInterfaceVersions, out string pOutErrMsg ) { - return Native.SteamAPI_Init(); + using var interfaceVersionsStr = new Utf8StringToNative( pszInternalCheckInterfaceVersions ); + using var buffer = Helpers.Memory.Take(); + var result = Native.SteamInternal_SteamAPI_Init( interfaceVersionsStr.Pointer, buffer.Ptr ); + pOutErrMsg = Helpers.MemoryToString( buffer.Ptr ); + return result; } static internal void Shutdown() diff --git a/Libraries/Facepunch.Steamworks/Classes/SteamInternal.cs b/Libraries/Facepunch.Steamworks/Classes/SteamInternal.cs index bc9ea6d5a..0f76c88ef 100644 --- a/Libraries/Facepunch.Steamworks/Classes/SteamInternal.cs +++ b/Libraries/Facepunch.Steamworks/Classes/SteamInternal.cs @@ -11,14 +11,18 @@ namespace Steamworks { internal static class Native { - [DllImport( Platform.LibraryName, EntryPoint = "SteamInternal_GameServer_Init", CallingConvention = CallingConvention.Cdecl )] - [return: MarshalAs( UnmanagedType.I1 )] - public static extern bool SteamInternal_GameServer_Init( uint unIP, ushort usPort, ushort usGamePort, ushort usQueryPort, int eServerMode, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersionString ); + [DllImport( Platform.LibraryName, EntryPoint = "SteamInternal_GameServer_Init_V2", CallingConvention = CallingConvention.Cdecl )] + public static extern SteamAPIInitResult SteamInternal_GameServer_Init_V2( uint unIP, ushort usGamePort, ushort usQueryPort, int eServerMode, IntPtr pchVersionString, IntPtr pszInternalCheckInterfaceVersions, IntPtr pOutErrMsg ); } - static internal bool GameServer_Init( uint unIP, ushort usPort, ushort usGamePort, ushort usQueryPort, int eServerMode, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersionString ) + static internal SteamAPIInitResult GameServer_Init( uint unIP, ushort usGamePort, ushort usQueryPort, int eServerMode, string pchVersionString, string pszInternalCheckInterfaceVersions, out string pOutErrMsg ) { - return Native.SteamInternal_GameServer_Init( unIP, usPort, usGamePort, usQueryPort, eServerMode, pchVersionString ); + using var versionStr = new Utf8StringToNative( pchVersionString ); + using var interfaceVersionsStr = new Utf8StringToNative( pszInternalCheckInterfaceVersions ); + using var buffer = Helpers.Memory.Take(); + var result = Native.SteamInternal_GameServer_Init_V2( unIP, usGamePort, usQueryPort, eServerMode, versionStr.Pointer, interfaceVersionsStr.Pointer, buffer.Ptr ); + pOutErrMsg = Helpers.MemoryToString( buffer.Ptr ); + return result; } } } diff --git a/Libraries/Facepunch.Steamworks/Facepunch.Steamworks.targets b/Libraries/Facepunch.Steamworks/Facepunch.Steamworks.targets index 2e6b10014..1213aa6e9 100644 --- a/Libraries/Facepunch.Steamworks/Facepunch.Steamworks.targets +++ b/Libraries/Facepunch.Steamworks/Facepunch.Steamworks.targets @@ -6,8 +6,8 @@ - 2.3.4 - 2.3.4 + 2.4.1 + 2.4.1 diff --git a/Libraries/Facepunch.Steamworks/Generated/CustomEnums.cs b/Libraries/Facepunch.Steamworks/Generated/CustomEnums.cs index c7b55f544..34fbefbd7 100644 --- a/Libraries/Facepunch.Steamworks/Generated/CustomEnums.cs +++ b/Libraries/Facepunch.Steamworks/Generated/CustomEnums.cs @@ -159,8 +159,6 @@ namespace Steamworks DeleteItemResult = 3417, UserSubscribedItemsListChanged = 3418, WorkshopEULAStatus = 3420, - SteamAppInstalled = 3901, - SteamAppUninstalled = 3902, PlaybackStatusHasChanged = 4001, VolumeHasChanged = 4002, MusicPlayerWantsVolume = 4011, @@ -200,6 +198,8 @@ namespace Steamworks HTML_UpdateToolTip = 4525, HTML_HideToolTip = 4526, HTML_BrowserRestarted = 4527, + BroadcastUploadStart = 4604, + BroadcastUploadStop = 4605, GetVideoURLResult = 4611, GetOPFSettingsResult = 4624, SteamInventoryResultReady = 4700, @@ -225,6 +225,8 @@ namespace Steamworks SteamRemotePlaySessionConnected = 5701, SteamRemotePlaySessionDisconnected = 5702, SteamRemotePlayTogetherGuestInvite = 5703, + SteamTimelineGamePhaseRecordingExists = 6001, + SteamTimelineEventRecordingExists = 6002, } internal static partial class CallbackTypeFactory { @@ -379,8 +381,6 @@ namespace Steamworks { CallbackType.DeleteItemResult, typeof( DeleteItemResult_t )}, { CallbackType.UserSubscribedItemsListChanged, typeof( UserSubscribedItemsListChanged_t )}, { CallbackType.WorkshopEULAStatus, typeof( WorkshopEULAStatus_t )}, - { CallbackType.SteamAppInstalled, typeof( SteamAppInstalled_t )}, - { CallbackType.SteamAppUninstalled, typeof( SteamAppUninstalled_t )}, { CallbackType.PlaybackStatusHasChanged, typeof( PlaybackStatusHasChanged_t )}, { CallbackType.VolumeHasChanged, typeof( VolumeHasChanged_t )}, { CallbackType.MusicPlayerWantsVolume, typeof( MusicPlayerWantsVolume_t )}, @@ -420,6 +420,8 @@ namespace Steamworks { CallbackType.HTML_UpdateToolTip, typeof( HTML_UpdateToolTip_t )}, { CallbackType.HTML_HideToolTip, typeof( HTML_HideToolTip_t )}, { CallbackType.HTML_BrowserRestarted, typeof( HTML_BrowserRestarted_t )}, + { CallbackType.BroadcastUploadStart, typeof( BroadcastUploadStart_t )}, + { CallbackType.BroadcastUploadStop, typeof( BroadcastUploadStop_t )}, { CallbackType.GetVideoURLResult, typeof( GetVideoURLResult_t )}, { CallbackType.GetOPFSettingsResult, typeof( GetOPFSettingsResult_t )}, { CallbackType.SteamInventoryResultReady, typeof( SteamInventoryResultReady_t )}, @@ -445,6 +447,8 @@ namespace Steamworks { CallbackType.SteamRemotePlaySessionConnected, typeof( SteamRemotePlaySessionConnected_t )}, { CallbackType.SteamRemotePlaySessionDisconnected, typeof( SteamRemotePlaySessionDisconnected_t )}, { CallbackType.SteamRemotePlayTogetherGuestInvite, typeof( SteamRemotePlayTogetherGuestInvite_t )}, + { CallbackType.SteamTimelineGamePhaseRecordingExists, typeof( SteamTimelineGamePhaseRecordingExists_t )}, + { CallbackType.SteamTimelineEventRecordingExists, typeof( SteamTimelineEventRecordingExists_t )}, }; } } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamAppList.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamAppList.cs index b67457561..2b1a7b552 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamAppList.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamAppList.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamAppList : SteamInterface + internal unsafe partial class ISteamAppList : SteamInterface { internal ISteamAppList( bool IsGameServer ) diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs index a64317fc6..2f5c2b42b 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamApps : SteamInterface + internal unsafe partial class ISteamApps : SteamInterface { + public const string Version = "STEAMAPPS_INTERFACE_VERSION008"; internal ISteamApps( bool IsGameServer ) { @@ -156,9 +157,9 @@ namespace Steamworks #endregion internal bool BGetDLCDataByIndex( int iDLC, ref AppId pAppID, [MarshalAs( UnmanagedType.U1 )] ref bool pbAvailable, out string pchName ) { - using var mempchName = Helpers.TakeMemory(); - var returnValue = _BGetDLCDataByIndex( Self, iDLC, ref pAppID, ref pbAvailable, mempchName, (1024 * 32) ); - pchName = Helpers.MemoryToString( mempchName ); + using var mem__pchName = Helpers.TakeMemory(); + var returnValue = _BGetDLCDataByIndex( Self, iDLC, ref pAppID, ref pbAvailable, mem__pchName, (1024 * 32) ); + pchName = Helpers.MemoryToString( mem__pchName ); return returnValue; } @@ -200,9 +201,9 @@ namespace Steamworks #endregion internal bool GetCurrentBetaName( out string pchName ) { - using var mempchName = Helpers.TakeMemory(); - var returnValue = _GetCurrentBetaName( Self, mempchName, (1024 * 32) ); - pchName = Helpers.MemoryToString( mempchName ); + using var mem__pchName = Helpers.TakeMemory(); + var returnValue = _GetCurrentBetaName( Self, mem__pchName, (1024 * 32) ); + pchName = Helpers.MemoryToString( mem__pchName ); return returnValue; } @@ -236,9 +237,9 @@ namespace Steamworks #endregion internal uint GetAppInstallDir( AppId appID, out string pchFolder ) { - using var mempchFolder = Helpers.TakeMemory(); - var returnValue = _GetAppInstallDir( Self, appID, mempchFolder, (1024 * 32) ); - pchFolder = Helpers.MemoryToString( mempchFolder ); + using var mem__pchFolder = Helpers.TakeMemory(); + var returnValue = _GetAppInstallDir( Self, appID, mem__pchFolder, (1024 * 32) ); + pchFolder = Helpers.MemoryToString( mem__pchFolder ); return returnValue; } @@ -267,12 +268,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamApps_GetLaunchQueryParam", CallingConvention = Platform.CC)] - private static extern Utf8StringPointer _GetLaunchQueryParam( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ); + private static extern Utf8StringPointer _GetLaunchQueryParam( IntPtr self, IntPtr pchKey ); #endregion - internal string GetLaunchQueryParam( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ) + internal string GetLaunchQueryParam( string pchKey ) { - var returnValue = _GetLaunchQueryParam( Self, pchKey ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + var returnValue = _GetLaunchQueryParam( Self, str__pchKey.Pointer ); return returnValue; } @@ -311,12 +313,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamApps_GetFileDetails", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _GetFileDetails( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszFileName ); + private static extern SteamAPICall_t _GetFileDetails( IntPtr self, IntPtr pszFileName ); #endregion - internal CallResult GetFileDetails( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszFileName ) + internal CallResult GetFileDetails( string pszFileName ) { - var returnValue = _GetFileDetails( Self, pszFileName ); + using var str__pszFileName = new Utf8StringToNative( pszFileName ); + var returnValue = _GetFileDetails( Self, str__pszFileName.Pointer ); return new CallResult( returnValue, IsServer ); } @@ -327,9 +330,9 @@ namespace Steamworks #endregion internal int GetLaunchCommandLine( out string pszCommandLine ) { - using var mempszCommandLine = Helpers.TakeMemory(); - var returnValue = _GetLaunchCommandLine( Self, mempszCommandLine, (1024 * 32) ); - pszCommandLine = Helpers.MemoryToString( mempszCommandLine ); + using var mem__pszCommandLine = Helpers.TakeMemory(); + var returnValue = _GetLaunchCommandLine( Self, mem__pszCommandLine, (1024 * 32) ); + pszCommandLine = Helpers.MemoryToString( mem__pszCommandLine ); return returnValue; } @@ -369,5 +372,45 @@ namespace Steamworks return returnValue; } + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamApps_GetNumBetas", CallingConvention = Platform.CC)] + private static extern int _GetNumBetas( IntPtr self, ref int pnAvailable, ref int pnPrivate ); + + #endregion + internal int GetNumBetas( ref int pnAvailable, ref int pnPrivate ) + { + var returnValue = _GetNumBetas( Self, ref pnAvailable, ref pnPrivate ); + return returnValue; + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamApps_GetBetaInfo", CallingConvention = Platform.CC)] + [return: MarshalAs( UnmanagedType.I1 )] + private static extern bool _GetBetaInfo( IntPtr self, int iBetaIndex, ref uint punFlags, ref uint punBuildID, IntPtr pchBetaName, int cchBetaName, IntPtr pchDescription, int cchDescription ); + + #endregion + internal bool GetBetaInfo( int iBetaIndex, ref uint punFlags, ref uint punBuildID, out string pchBetaName, out string pchDescription ) + { + using var mem__pchBetaName = Helpers.TakeMemory(); + using var mem__pchDescription = Helpers.TakeMemory(); + var returnValue = _GetBetaInfo( Self, iBetaIndex, ref punFlags, ref punBuildID, mem__pchBetaName, (1024 * 32), mem__pchDescription, (1024 * 32) ); + pchBetaName = Helpers.MemoryToString( mem__pchBetaName ); + pchDescription = Helpers.MemoryToString( mem__pchDescription ); + return returnValue; + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamApps_SetActiveBeta", CallingConvention = Platform.CC)] + [return: MarshalAs( UnmanagedType.I1 )] + private static extern bool _SetActiveBeta( IntPtr self, IntPtr pchBetaName ); + + #endregion + internal bool SetActiveBeta( string pchBetaName ) + { + using var str__pchBetaName = new Utf8StringToNative( pchBetaName ); + var returnValue = _SetActiveBeta( Self, str__pchBetaName.Pointer ); + return returnValue; + } + } } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamClient.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamClient.cs index 418f5dab6..ff610e118 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamClient.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamClient.cs @@ -7,9 +7,8 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamClient : SteamInterface + internal unsafe partial class ISteamClient : SteamInterface { - internal ISteamClient( bool IsGameServer ) { SetupInterface( IsGameServer ); @@ -72,23 +71,25 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamUser", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamUser( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamUser( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamUser( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamUser( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamGameServer", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamGameServer( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamGameServer( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamGameServer( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamGameServer( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } @@ -104,133 +105,145 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamFriends", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamFriends( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamFriends( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamFriends( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamFriends( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamUtils", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamUtils( IntPtr self, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamUtils( IntPtr self, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamUtils( HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamUtils( HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamUtils( Self, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamUtils( Self, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamMatchmaking", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamMatchmaking( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamMatchmaking( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamMatchmaking( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamMatchmaking( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamMatchmakingServers", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamMatchmakingServers( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamMatchmakingServers( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamMatchmakingServers( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamMatchmakingServers( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamGenericInterface", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamGenericInterface( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamGenericInterface( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamGenericInterface( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamGenericInterface( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamUserStats", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamUserStats( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamUserStats( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamUserStats( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamUserStats( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamGameServerStats", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamGameServerStats( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamGameServerStats( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamGameServerStats( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamGameServerStats( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamGameServerStats( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamGameServerStats( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamApps", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamApps( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamApps( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamApps( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamApps( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamNetworking", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamNetworking( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamNetworking( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamNetworking( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamNetworking( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamRemoteStorage", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamRemoteStorage( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamRemoteStorage( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamRemoteStorage( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamRemoteStorage( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamScreenshots", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamScreenshots( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamScreenshots( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamScreenshots( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamScreenshots( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamScreenshots( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamScreenshots( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamGameSearch", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamGameSearch( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamGameSearch( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamGameSearch( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamGameSearch( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamGameSearch( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamGameSearch( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } @@ -269,144 +282,145 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamHTTP", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamHTTP( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamHTTP( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamHTTP( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamHTTP( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamController", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamController( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamController( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamController( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamController( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamController( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamController( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamUGC", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamUGC( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamUGC( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamUGC( Self, hSteamUser, hSteamPipe, pchVersion ); - return returnValue; - } - - #region FunctionMeta - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamAppList", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamAppList( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); - - #endregion - internal IntPtr GetISteamAppList( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) - { - var returnValue = _GetISteamAppList( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamUGC( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamMusic", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamMusic( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamMusic( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamMusic( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamMusic( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamMusicRemote", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamMusicRemote( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamMusicRemote( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamMusicRemote( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamMusicRemote( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamMusicRemote( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamMusicRemote( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamHTMLSurface", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamHTMLSurface( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamHTMLSurface( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamHTMLSurface( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamHTMLSurface( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamHTMLSurface( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamHTMLSurface( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamInventory", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamInventory( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamInventory( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamInventory( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamInventory( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamInventory( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamInventory( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamVideo", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamVideo( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamVideo( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamVideo( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamVideo( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamParentalSettings", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamParentalSettings( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamParentalSettings( IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamParentalSettings( HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamParentalSettings( HSteamUser hSteamuser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamParentalSettings( Self, hSteamuser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamParentalSettings( Self, hSteamuser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamInput", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamInput( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamInput( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamInput( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamInput( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamInput( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamInput( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamParties", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamParties( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamParties( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamParties( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamParties( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamParties( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamParties( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamClient_GetISteamRemotePlay", CallingConvention = Platform.CC)] - private static extern IntPtr _GetISteamRemotePlay( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ); + private static extern IntPtr _GetISteamRemotePlay( IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, IntPtr pchVersion ); #endregion - internal IntPtr GetISteamRemotePlay( HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersion ) + internal IntPtr GetISteamRemotePlay( HSteamUser hSteamUser, HSteamPipe hSteamPipe, string pchVersion ) { - var returnValue = _GetISteamRemotePlay( Self, hSteamUser, hSteamPipe, pchVersion ); + using var str__pchVersion = new Utf8StringToNative( pchVersion ); + var returnValue = _GetISteamRemotePlay( Self, hSteamUser, hSteamPipe, str__pchVersion.Pointer ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamController.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamController.cs index d93dccffe..95b1f592e 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamController.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamController.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamController : SteamInterface + internal unsafe partial class ISteamController : SteamInterface { + public const string Version = "SteamController008"; internal ISteamController( bool IsGameServer ) { @@ -67,12 +68,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamController_GetActionSetHandle", CallingConvention = Platform.CC)] - private static extern ControllerActionSetHandle_t _GetActionSetHandle( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionSetName ); + private static extern ControllerActionSetHandle_t _GetActionSetHandle( IntPtr self, IntPtr pszActionSetName ); #endregion - internal ControllerActionSetHandle_t GetActionSetHandle( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionSetName ) + internal ControllerActionSetHandle_t GetActionSetHandle( string pszActionSetName ) { - var returnValue = _GetActionSetHandle( Self, pszActionSetName ); + using var str__pszActionSetName = new Utf8StringToNative( pszActionSetName ); + var returnValue = _GetActionSetHandle( Self, str__pszActionSetName.Pointer ); return returnValue; } @@ -140,12 +142,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamController_GetDigitalActionHandle", CallingConvention = Platform.CC)] - private static extern ControllerDigitalActionHandle_t _GetDigitalActionHandle( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionName ); + private static extern ControllerDigitalActionHandle_t _GetDigitalActionHandle( IntPtr self, IntPtr pszActionName ); #endregion - internal ControllerDigitalActionHandle_t GetDigitalActionHandle( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionName ) + internal ControllerDigitalActionHandle_t GetDigitalActionHandle( string pszActionName ) { - var returnValue = _GetDigitalActionHandle( Self, pszActionName ); + using var str__pszActionName = new Utf8StringToNative( pszActionName ); + var returnValue = _GetDigitalActionHandle( Self, str__pszActionName.Pointer ); return returnValue; } @@ -173,12 +176,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamController_GetAnalogActionHandle", CallingConvention = Platform.CC)] - private static extern ControllerAnalogActionHandle_t _GetAnalogActionHandle( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionName ); + private static extern ControllerAnalogActionHandle_t _GetAnalogActionHandle( IntPtr self, IntPtr pszActionName ); #endregion - internal ControllerAnalogActionHandle_t GetAnalogActionHandle( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionName ) + internal ControllerAnalogActionHandle_t GetAnalogActionHandle( string pszActionName ) { - var returnValue = _GetAnalogActionHandle( Self, pszActionName ); + using var str__pszActionName = new Utf8StringToNative( pszActionName ); + var returnValue = _GetAnalogActionHandle( Self, str__pszActionName.Pointer ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs index b8e5cd443..b4be58e29 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamFriends : SteamInterface + internal unsafe partial class ISteamFriends : SteamInterface { + public const string Version = "SteamFriends017"; internal ISteamFriends( bool IsGameServer ) { @@ -33,12 +34,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_SetPersonaName", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _SetPersonaName( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPersonaName ); + private static extern SteamAPICall_t _SetPersonaName( IntPtr self, IntPtr pchPersonaName ); #endregion - internal CallResult SetPersonaName( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPersonaName ) + internal CallResult SetPersonaName( string pchPersonaName ) { - var returnValue = _SetPersonaName( Self, pchPersonaName ); + using var str__pchPersonaName = new Utf8StringToNative( pchPersonaName ); + var returnValue = _SetPersonaName( Self, str__pchPersonaName.Pointer ); return new CallResult( returnValue, IsServer ); } @@ -332,32 +334,35 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_ActivateGameOverlay", CallingConvention = Platform.CC)] - private static extern void _ActivateGameOverlay( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchDialog ); + private static extern void _ActivateGameOverlay( IntPtr self, IntPtr pchDialog ); #endregion - internal void ActivateGameOverlay( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchDialog ) + internal void ActivateGameOverlay( string pchDialog ) { - _ActivateGameOverlay( Self, pchDialog ); + using var str__pchDialog = new Utf8StringToNative( pchDialog ); + _ActivateGameOverlay( Self, str__pchDialog.Pointer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_ActivateGameOverlayToUser", CallingConvention = Platform.CC)] - private static extern void _ActivateGameOverlayToUser( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchDialog, SteamId steamID ); + private static extern void _ActivateGameOverlayToUser( IntPtr self, IntPtr pchDialog, SteamId steamID ); #endregion - internal void ActivateGameOverlayToUser( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchDialog, SteamId steamID ) + internal void ActivateGameOverlayToUser( string pchDialog, SteamId steamID ) { - _ActivateGameOverlayToUser( Self, pchDialog, steamID ); + using var str__pchDialog = new Utf8StringToNative( pchDialog ); + _ActivateGameOverlayToUser( Self, str__pchDialog.Pointer, steamID ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_ActivateGameOverlayToWebPage", CallingConvention = Platform.CC)] - private static extern void _ActivateGameOverlayToWebPage( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchURL, ActivateGameOverlayToWebPageMode eMode ); + private static extern void _ActivateGameOverlayToWebPage( IntPtr self, IntPtr pchURL, ActivateGameOverlayToWebPageMode eMode ); #endregion - internal void ActivateGameOverlayToWebPage( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchURL, ActivateGameOverlayToWebPageMode eMode ) + internal void ActivateGameOverlayToWebPage( string pchURL, ActivateGameOverlayToWebPageMode eMode ) { - _ActivateGameOverlayToWebPage( Self, pchURL, eMode ); + using var str__pchURL = new Utf8StringToNative( pchURL ); + _ActivateGameOverlayToWebPage( Self, str__pchURL.Pointer, eMode ); } #region FunctionMeta @@ -493,12 +498,14 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_SetRichPresence", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetRichPresence( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ); + private static extern bool _SetRichPresence( IntPtr self, IntPtr pchKey, IntPtr pchValue ); #endregion - internal bool SetRichPresence( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ) + internal bool SetRichPresence( string pchKey, string pchValue ) { - var returnValue = _SetRichPresence( Self, pchKey, pchValue ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + using var str__pchValue = new Utf8StringToNative( pchValue ); + var returnValue = _SetRichPresence( Self, str__pchKey.Pointer, str__pchValue.Pointer ); return returnValue; } @@ -514,12 +521,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_GetFriendRichPresence", CallingConvention = Platform.CC)] - private static extern Utf8StringPointer _GetFriendRichPresence( IntPtr self, SteamId steamIDFriend, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ); + private static extern Utf8StringPointer _GetFriendRichPresence( IntPtr self, SteamId steamIDFriend, IntPtr pchKey ); #endregion - internal string GetFriendRichPresence( SteamId steamIDFriend, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ) + internal string GetFriendRichPresence( SteamId steamIDFriend, string pchKey ) { - var returnValue = _GetFriendRichPresence( Self, steamIDFriend, pchKey ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + var returnValue = _GetFriendRichPresence( Self, steamIDFriend, str__pchKey.Pointer ); return returnValue; } @@ -558,12 +566,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_InviteUserToGame", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _InviteUserToGame( IntPtr self, SteamId steamIDFriend, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchConnectString ); + private static extern bool _InviteUserToGame( IntPtr self, SteamId steamIDFriend, IntPtr pchConnectString ); #endregion - internal bool InviteUserToGame( SteamId steamIDFriend, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchConnectString ) + internal bool InviteUserToGame( SteamId steamIDFriend, string pchConnectString ) { - var returnValue = _InviteUserToGame( Self, steamIDFriend, pchConnectString ); + using var str__pchConnectString = new Utf8StringToNative( pchConnectString ); + var returnValue = _InviteUserToGame( Self, steamIDFriend, str__pchConnectString.Pointer ); return returnValue; } @@ -659,12 +668,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_SendClanChatMessage", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SendClanChatMessage( IntPtr self, SteamId steamIDClanChat, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchText ); + private static extern bool _SendClanChatMessage( IntPtr self, SteamId steamIDClanChat, IntPtr pchText ); #endregion - internal bool SendClanChatMessage( SteamId steamIDClanChat, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchText ) + internal bool SendClanChatMessage( SteamId steamIDClanChat, string pchText ) { - var returnValue = _SendClanChatMessage( Self, steamIDClanChat, pchText ); + using var str__pchText = new Utf8StringToNative( pchText ); + var returnValue = _SendClanChatMessage( Self, steamIDClanChat, str__pchText.Pointer ); return returnValue; } @@ -742,12 +752,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_ReplyToFriendMessage", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _ReplyToFriendMessage( IntPtr self, SteamId steamIDFriend, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchMsgToSend ); + private static extern bool _ReplyToFriendMessage( IntPtr self, SteamId steamIDFriend, IntPtr pchMsgToSend ); #endregion - internal bool ReplyToFriendMessage( SteamId steamIDFriend, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchMsgToSend ) + internal bool ReplyToFriendMessage( SteamId steamIDFriend, string pchMsgToSend ) { - var returnValue = _ReplyToFriendMessage( Self, steamIDFriend, pchMsgToSend ); + using var str__pchMsgToSend = new Utf8StringToNative( pchMsgToSend ); + var returnValue = _ReplyToFriendMessage( Self, steamIDFriend, str__pchMsgToSend.Pointer ); return returnValue; } @@ -843,23 +854,25 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_RegisterProtocolInOverlayBrowser", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _RegisterProtocolInOverlayBrowser( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchProtocol ); + private static extern bool _RegisterProtocolInOverlayBrowser( IntPtr self, IntPtr pchProtocol ); #endregion - internal bool RegisterProtocolInOverlayBrowser( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchProtocol ) + internal bool RegisterProtocolInOverlayBrowser( string pchProtocol ) { - var returnValue = _RegisterProtocolInOverlayBrowser( Self, pchProtocol ); + using var str__pchProtocol = new Utf8StringToNative( pchProtocol ); + var returnValue = _RegisterProtocolInOverlayBrowser( Self, str__pchProtocol.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamFriends_ActivateGameOverlayInviteDialogConnectString", CallingConvention = Platform.CC)] - private static extern void _ActivateGameOverlayInviteDialogConnectString( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchConnectString ); + private static extern void _ActivateGameOverlayInviteDialogConnectString( IntPtr self, IntPtr pchConnectString ); #endregion - internal void ActivateGameOverlayInviteDialogConnectString( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchConnectString ) + internal void ActivateGameOverlayInviteDialogConnectString( string pchConnectString ) { - _ActivateGameOverlayInviteDialogConnectString( Self, pchConnectString ); + using var str__pchConnectString = new Utf8StringToNative( pchConnectString ); + _ActivateGameOverlayInviteDialogConnectString( Self, str__pchConnectString.Pointer ); } #region FunctionMeta diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameSearch.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameSearch.cs index 3c70b84f7..23566ee05 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameSearch.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameSearch.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamGameSearch : SteamInterface + internal unsafe partial class ISteamGameSearch : SteamInterface { + public const string Version = "SteamMatchGameSearch001"; internal ISteamGameSearch( bool IsGameServer ) { @@ -22,12 +23,14 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameSearch_AddGameSearchParams", CallingConvention = Platform.CC)] - private static extern GameSearchErrorCode_t _AddGameSearchParams( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKeyToFind, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValuesToFind ); + private static extern GameSearchErrorCode_t _AddGameSearchParams( IntPtr self, IntPtr pchKeyToFind, IntPtr pchValuesToFind ); #endregion - internal GameSearchErrorCode_t AddGameSearchParams( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKeyToFind, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValuesToFind ) + internal GameSearchErrorCode_t AddGameSearchParams( string pchKeyToFind, string pchValuesToFind ) { - var returnValue = _AddGameSearchParams( Self, pchKeyToFind, pchValuesToFind ); + using var str__pchKeyToFind = new Utf8StringToNative( pchKeyToFind ); + using var str__pchValuesToFind = new Utf8StringToNative( pchValuesToFind ); + var returnValue = _AddGameSearchParams( Self, str__pchKeyToFind.Pointer, str__pchValuesToFind.Pointer ); return returnValue; } @@ -82,9 +85,9 @@ namespace Steamworks #endregion internal GameSearchErrorCode_t RetrieveConnectionDetails( SteamId steamIDHost, out string pchConnectionDetails ) { - using var mempchConnectionDetails = Helpers.TakeMemory(); - var returnValue = _RetrieveConnectionDetails( Self, steamIDHost, mempchConnectionDetails, (1024 * 32) ); - pchConnectionDetails = Helpers.MemoryToString( mempchConnectionDetails ); + using var mem__pchConnectionDetails = Helpers.TakeMemory(); + var returnValue = _RetrieveConnectionDetails( Self, steamIDHost, mem__pchConnectionDetails, (1024 * 32) ); + pchConnectionDetails = Helpers.MemoryToString( mem__pchConnectionDetails ); return returnValue; } @@ -101,23 +104,26 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameSearch_SetGameHostParams", CallingConvention = Platform.CC)] - private static extern GameSearchErrorCode_t _SetGameHostParams( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ); + private static extern GameSearchErrorCode_t _SetGameHostParams( IntPtr self, IntPtr pchKey, IntPtr pchValue ); #endregion - internal GameSearchErrorCode_t SetGameHostParams( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ) + internal GameSearchErrorCode_t SetGameHostParams( string pchKey, string pchValue ) { - var returnValue = _SetGameHostParams( Self, pchKey, pchValue ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + using var str__pchValue = new Utf8StringToNative( pchValue ); + var returnValue = _SetGameHostParams( Self, str__pchKey.Pointer, str__pchValue.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameSearch_SetConnectionDetails", CallingConvention = Platform.CC)] - private static extern GameSearchErrorCode_t _SetConnectionDetails( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchConnectionDetails, int cubConnectionDetails ); + private static extern GameSearchErrorCode_t _SetConnectionDetails( IntPtr self, IntPtr pchConnectionDetails, int cubConnectionDetails ); #endregion - internal GameSearchErrorCode_t SetConnectionDetails( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchConnectionDetails, int cubConnectionDetails ) + internal GameSearchErrorCode_t SetConnectionDetails( string pchConnectionDetails, int cubConnectionDetails ) { - var returnValue = _SetConnectionDetails( Self, pchConnectionDetails, cubConnectionDetails ); + using var str__pchConnectionDetails = new Utf8StringToNative( pchConnectionDetails ); + var returnValue = _SetConnectionDetails( Self, str__pchConnectionDetails.Pointer, cubConnectionDetails ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs index b80a6953c..f9cccce75 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamGameServer : SteamInterface + internal unsafe partial class ISteamGameServer : SteamInterface { + public const string Version = "SteamGameServer015"; internal ISteamGameServer( bool IsGameServer ) { @@ -22,32 +23,35 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetProduct", CallingConvention = Platform.CC)] - private static extern void _SetProduct( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszProduct ); + private static extern void _SetProduct( IntPtr self, IntPtr pszProduct ); #endregion - internal void SetProduct( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszProduct ) + internal void SetProduct( string pszProduct ) { - _SetProduct( Self, pszProduct ); + using var str__pszProduct = new Utf8StringToNative( pszProduct ); + _SetProduct( Self, str__pszProduct.Pointer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetGameDescription", CallingConvention = Platform.CC)] - private static extern void _SetGameDescription( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszGameDescription ); + private static extern void _SetGameDescription( IntPtr self, IntPtr pszGameDescription ); #endregion - internal void SetGameDescription( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszGameDescription ) + internal void SetGameDescription( string pszGameDescription ) { - _SetGameDescription( Self, pszGameDescription ); + using var str__pszGameDescription = new Utf8StringToNative( pszGameDescription ); + _SetGameDescription( Self, str__pszGameDescription.Pointer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetModDir", CallingConvention = Platform.CC)] - private static extern void _SetModDir( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszModDir ); + private static extern void _SetModDir( IntPtr self, IntPtr pszModDir ); #endregion - internal void SetModDir( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszModDir ) + internal void SetModDir( string pszModDir ) { - _SetModDir( Self, pszModDir ); + using var str__pszModDir = new Utf8StringToNative( pszModDir ); + _SetModDir( Self, str__pszModDir.Pointer ); } #region FunctionMeta @@ -62,12 +66,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_LogOn", CallingConvention = Platform.CC)] - private static extern void _LogOn( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszToken ); + private static extern void _LogOn( IntPtr self, IntPtr pszToken ); #endregion - internal void LogOn( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszToken ) + internal void LogOn( string pszToken ) { - _LogOn( Self, pszToken ); + using var str__pszToken = new Utf8StringToNative( pszToken ); + _LogOn( Self, str__pszToken.Pointer ); } #region FunctionMeta @@ -159,22 +164,24 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetServerName", CallingConvention = Platform.CC)] - private static extern void _SetServerName( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszServerName ); + private static extern void _SetServerName( IntPtr self, IntPtr pszServerName ); #endregion - internal void SetServerName( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszServerName ) + internal void SetServerName( string pszServerName ) { - _SetServerName( Self, pszServerName ); + using var str__pszServerName = new Utf8StringToNative( pszServerName ); + _SetServerName( Self, str__pszServerName.Pointer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetMapName", CallingConvention = Platform.CC)] - private static extern void _SetMapName( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszMapName ); + private static extern void _SetMapName( IntPtr self, IntPtr pszMapName ); #endregion - internal void SetMapName( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszMapName ) + internal void SetMapName( string pszMapName ) { - _SetMapName( Self, pszMapName ); + using var str__pszMapName = new Utf8StringToNative( pszMapName ); + _SetMapName( Self, str__pszMapName.Pointer ); } #region FunctionMeta @@ -199,12 +206,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetSpectatorServerName", CallingConvention = Platform.CC)] - private static extern void _SetSpectatorServerName( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszSpectatorServerName ); + private static extern void _SetSpectatorServerName( IntPtr self, IntPtr pszSpectatorServerName ); #endregion - internal void SetSpectatorServerName( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszSpectatorServerName ) + internal void SetSpectatorServerName( string pszSpectatorServerName ) { - _SetSpectatorServerName( Self, pszSpectatorServerName ); + using var str__pszSpectatorServerName = new Utf8StringToNative( pszSpectatorServerName ); + _SetSpectatorServerName( Self, str__pszSpectatorServerName.Pointer ); } #region FunctionMeta @@ -219,42 +227,47 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetKeyValue", CallingConvention = Platform.CC)] - private static extern void _SetKeyValue( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pValue ); + private static extern void _SetKeyValue( IntPtr self, IntPtr pKey, IntPtr pValue ); #endregion - internal void SetKeyValue( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pValue ) + internal void SetKeyValue( string pKey, string pValue ) { - _SetKeyValue( Self, pKey, pValue ); + using var str__pKey = new Utf8StringToNative( pKey ); + using var str__pValue = new Utf8StringToNative( pValue ); + _SetKeyValue( Self, str__pKey.Pointer, str__pValue.Pointer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetGameTags", CallingConvention = Platform.CC)] - private static extern void _SetGameTags( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchGameTags ); + private static extern void _SetGameTags( IntPtr self, IntPtr pchGameTags ); #endregion - internal void SetGameTags( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchGameTags ) + internal void SetGameTags( string pchGameTags ) { - _SetGameTags( Self, pchGameTags ); + using var str__pchGameTags = new Utf8StringToNative( pchGameTags ); + _SetGameTags( Self, str__pchGameTags.Pointer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetGameData", CallingConvention = Platform.CC)] - private static extern void _SetGameData( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchGameData ); + private static extern void _SetGameData( IntPtr self, IntPtr pchGameData ); #endregion - internal void SetGameData( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchGameData ) + internal void SetGameData( string pchGameData ) { - _SetGameData( Self, pchGameData ); + using var str__pchGameData = new Utf8StringToNative( pchGameData ); + _SetGameData( Self, str__pchGameData.Pointer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_SetRegion", CallingConvention = Platform.CC)] - private static extern void _SetRegion( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszRegion ); + private static extern void _SetRegion( IntPtr self, IntPtr pszRegion ); #endregion - internal void SetRegion( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszRegion ) + internal void SetRegion( string pszRegion ) { - _SetRegion( Self, pszRegion ); + using var str__pszRegion = new Utf8StringToNative( pszRegion ); + _SetRegion( Self, str__pszRegion.Pointer ); } #region FunctionMeta @@ -445,12 +458,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServer_BUpdateUserData", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _BUpdateUserData( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPlayerName, uint uScore ); + private static extern bool _BUpdateUserData( IntPtr self, SteamId steamIDUser, IntPtr pchPlayerName, uint uScore ); #endregion - internal bool BUpdateUserData( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPlayerName, uint uScore ) + internal bool BUpdateUserData( SteamId steamIDUser, string pchPlayerName, uint uScore ) { - var returnValue = _BUpdateUserData( Self, steamIDUser, pchPlayerName, uScore ); + using var str__pchPlayerName = new Utf8StringToNative( pchPlayerName ); + var returnValue = _BUpdateUserData( Self, steamIDUser, str__pchPlayerName.Pointer, uScore ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServerStats.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServerStats.cs index de293cbe5..04c7b41b5 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServerStats.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServerStats.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamGameServerStats : SteamInterface + internal unsafe partial class ISteamGameServerStats : SteamInterface { + public const string Version = "SteamGameServerStats001"; internal ISteamGameServerStats( bool IsGameServer ) { @@ -34,96 +35,104 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServerStats_GetUserStatInt32", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetUserStat( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref int pData ); + private static extern bool _GetUserStat( IntPtr self, SteamId steamIDUser, IntPtr pchName, ref int pData ); #endregion - internal bool GetUserStat( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref int pData ) + internal bool GetUserStat( SteamId steamIDUser, string pchName, ref int pData ) { - var returnValue = _GetUserStat( Self, steamIDUser, pchName, ref pData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetUserStat( Self, steamIDUser, str__pchName.Pointer, ref pData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServerStats_GetUserStatFloat", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetUserStat( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pData ); + private static extern bool _GetUserStat( IntPtr self, SteamId steamIDUser, IntPtr pchName, ref float pData ); #endregion - internal bool GetUserStat( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pData ) + internal bool GetUserStat( SteamId steamIDUser, string pchName, ref float pData ) { - var returnValue = _GetUserStat( Self, steamIDUser, pchName, ref pData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetUserStat( Self, steamIDUser, str__pchName.Pointer, ref pData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServerStats_GetUserAchievement", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetUserAchievement( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ); + private static extern bool _GetUserAchievement( IntPtr self, SteamId steamIDUser, IntPtr pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ); #endregion - internal bool GetUserAchievement( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ) + internal bool GetUserAchievement( SteamId steamIDUser, string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ) { - var returnValue = _GetUserAchievement( Self, steamIDUser, pchName, ref pbAchieved ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetUserAchievement( Self, steamIDUser, str__pchName.Pointer, ref pbAchieved ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServerStats_SetUserStatInt32", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetUserStat( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, int nData ); + private static extern bool _SetUserStat( IntPtr self, SteamId steamIDUser, IntPtr pchName, int nData ); #endregion - internal bool SetUserStat( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, int nData ) + internal bool SetUserStat( SteamId steamIDUser, string pchName, int nData ) { - var returnValue = _SetUserStat( Self, steamIDUser, pchName, nData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _SetUserStat( Self, steamIDUser, str__pchName.Pointer, nData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServerStats_SetUserStatFloat", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetUserStat( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, float fData ); + private static extern bool _SetUserStat( IntPtr self, SteamId steamIDUser, IntPtr pchName, float fData ); #endregion - internal bool SetUserStat( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, float fData ) + internal bool SetUserStat( SteamId steamIDUser, string pchName, float fData ) { - var returnValue = _SetUserStat( Self, steamIDUser, pchName, fData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _SetUserStat( Self, steamIDUser, str__pchName.Pointer, fData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServerStats_UpdateUserAvgRateStat", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _UpdateUserAvgRateStat( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, float flCountThisSession, double dSessionLength ); + private static extern bool _UpdateUserAvgRateStat( IntPtr self, SteamId steamIDUser, IntPtr pchName, float flCountThisSession, double dSessionLength ); #endregion - internal bool UpdateUserAvgRateStat( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, float flCountThisSession, double dSessionLength ) + internal bool UpdateUserAvgRateStat( SteamId steamIDUser, string pchName, float flCountThisSession, double dSessionLength ) { - var returnValue = _UpdateUserAvgRateStat( Self, steamIDUser, pchName, flCountThisSession, dSessionLength ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _UpdateUserAvgRateStat( Self, steamIDUser, str__pchName.Pointer, flCountThisSession, dSessionLength ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServerStats_SetUserAchievement", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetUserAchievement( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ); + private static extern bool _SetUserAchievement( IntPtr self, SteamId steamIDUser, IntPtr pchName ); #endregion - internal bool SetUserAchievement( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ) + internal bool SetUserAchievement( SteamId steamIDUser, string pchName ) { - var returnValue = _SetUserAchievement( Self, steamIDUser, pchName ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _SetUserAchievement( Self, steamIDUser, str__pchName.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamGameServerStats_ClearUserAchievement", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _ClearUserAchievement( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ); + private static extern bool _ClearUserAchievement( IntPtr self, SteamId steamIDUser, IntPtr pchName ); #endregion - internal bool ClearUserAchievement( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ) + internal bool ClearUserAchievement( SteamId steamIDUser, string pchName ) { - var returnValue = _ClearUserAchievement( Self, steamIDUser, pchName ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _ClearUserAchievement( Self, steamIDUser, str__pchName.Pointer ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamHTMLSurface.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamHTMLSurface.cs index 38c6b462e..04863e7ad 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamHTMLSurface.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamHTMLSurface.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamHTMLSurface : SteamInterface + internal unsafe partial class ISteamHTMLSurface : SteamInterface { + public const string Version = "STEAMHTMLSURFACE_INTERFACE_VERSION_005"; internal ISteamHTMLSurface( bool IsGameServer ) { @@ -46,12 +47,14 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTMLSurface_CreateBrowser", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _CreateBrowser( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchUserAgent, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchUserCSS ); + private static extern SteamAPICall_t _CreateBrowser( IntPtr self, IntPtr pchUserAgent, IntPtr pchUserCSS ); #endregion - internal CallResult CreateBrowser( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchUserAgent, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchUserCSS ) + internal CallResult CreateBrowser( string pchUserAgent, string pchUserCSS ) { - var returnValue = _CreateBrowser( Self, pchUserAgent, pchUserCSS ); + using var str__pchUserAgent = new Utf8StringToNative( pchUserAgent ); + using var str__pchUserCSS = new Utf8StringToNative( pchUserCSS ); + var returnValue = _CreateBrowser( Self, str__pchUserAgent.Pointer, str__pchUserCSS.Pointer ); return new CallResult( returnValue, IsServer ); } @@ -67,12 +70,14 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTMLSurface_LoadURL", CallingConvention = Platform.CC)] - private static extern void _LoadURL( IntPtr self, HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchURL, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPostData ); + private static extern void _LoadURL( IntPtr self, HHTMLBrowser unBrowserHandle, IntPtr pchURL, IntPtr pchPostData ); #endregion - internal void LoadURL( HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchURL, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPostData ) + internal void LoadURL( HHTMLBrowser unBrowserHandle, string pchURL, string pchPostData ) { - _LoadURL( Self, unBrowserHandle, pchURL, pchPostData ); + using var str__pchURL = new Utf8StringToNative( pchURL ); + using var str__pchPostData = new Utf8StringToNative( pchPostData ); + _LoadURL( Self, unBrowserHandle, str__pchURL.Pointer, str__pchPostData.Pointer ); } #region FunctionMeta @@ -127,22 +132,25 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTMLSurface_AddHeader", CallingConvention = Platform.CC)] - private static extern void _AddHeader( IntPtr self, HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ); + private static extern void _AddHeader( IntPtr self, HHTMLBrowser unBrowserHandle, IntPtr pchKey, IntPtr pchValue ); #endregion - internal void AddHeader( HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ) + internal void AddHeader( HHTMLBrowser unBrowserHandle, string pchKey, string pchValue ) { - _AddHeader( Self, unBrowserHandle, pchKey, pchValue ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + using var str__pchValue = new Utf8StringToNative( pchValue ); + _AddHeader( Self, unBrowserHandle, str__pchKey.Pointer, str__pchValue.Pointer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTMLSurface_ExecuteJavascript", CallingConvention = Platform.CC)] - private static extern void _ExecuteJavascript( IntPtr self, HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchScript ); + private static extern void _ExecuteJavascript( IntPtr self, HHTMLBrowser unBrowserHandle, IntPtr pchScript ); #endregion - internal void ExecuteJavascript( HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchScript ) + internal void ExecuteJavascript( HHTMLBrowser unBrowserHandle, string pchScript ) { - _ExecuteJavascript( Self, unBrowserHandle, pchScript ); + using var str__pchScript = new Utf8StringToNative( pchScript ); + _ExecuteJavascript( Self, unBrowserHandle, str__pchScript.Pointer ); } #region FunctionMeta @@ -287,12 +295,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTMLSurface_Find", CallingConvention = Platform.CC)] - private static extern void _Find( IntPtr self, HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchSearchStr, [MarshalAs( UnmanagedType.U1 )] bool bCurrentlyInFind, [MarshalAs( UnmanagedType.U1 )] bool bReverse ); + private static extern void _Find( IntPtr self, HHTMLBrowser unBrowserHandle, IntPtr pchSearchStr, [MarshalAs( UnmanagedType.U1 )] bool bCurrentlyInFind, [MarshalAs( UnmanagedType.U1 )] bool bReverse ); #endregion - internal void Find( HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchSearchStr, [MarshalAs( UnmanagedType.U1 )] bool bCurrentlyInFind, [MarshalAs( UnmanagedType.U1 )] bool bReverse ) + internal void Find( HHTMLBrowser unBrowserHandle, string pchSearchStr, [MarshalAs( UnmanagedType.U1 )] bool bCurrentlyInFind, [MarshalAs( UnmanagedType.U1 )] bool bReverse ) { - _Find( Self, unBrowserHandle, pchSearchStr, bCurrentlyInFind, bReverse ); + using var str__pchSearchStr = new Utf8StringToNative( pchSearchStr ); + _Find( Self, unBrowserHandle, str__pchSearchStr.Pointer, bCurrentlyInFind, bReverse ); } #region FunctionMeta @@ -317,12 +326,16 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTMLSurface_SetCookie", CallingConvention = Platform.CC)] - private static extern void _SetCookie( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHostname, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPath, RTime32 nExpires, [MarshalAs( UnmanagedType.U1 )] bool bSecure, [MarshalAs( UnmanagedType.U1 )] bool bHTTPOnly ); + private static extern void _SetCookie( IntPtr self, IntPtr pchHostname, IntPtr pchKey, IntPtr pchValue, IntPtr pchPath, RTime32 nExpires, [MarshalAs( UnmanagedType.U1 )] bool bSecure, [MarshalAs( UnmanagedType.U1 )] bool bHTTPOnly ); #endregion - internal void SetCookie( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHostname, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPath, RTime32 nExpires, [MarshalAs( UnmanagedType.U1 )] bool bSecure, [MarshalAs( UnmanagedType.U1 )] bool bHTTPOnly ) + internal void SetCookie( string pchHostname, string pchKey, string pchValue, string pchPath, RTime32 nExpires, [MarshalAs( UnmanagedType.U1 )] bool bSecure, [MarshalAs( UnmanagedType.U1 )] bool bHTTPOnly ) { - _SetCookie( Self, pchHostname, pchKey, pchValue, pchPath, nExpires, bSecure, bHTTPOnly ); + using var str__pchHostname = new Utf8StringToNative( pchHostname ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + using var str__pchValue = new Utf8StringToNative( pchValue ); + using var str__pchPath = new Utf8StringToNative( pchPath ); + _SetCookie( Self, str__pchHostname.Pointer, str__pchKey.Pointer, str__pchValue.Pointer, str__pchPath.Pointer, nExpires, bSecure, bHTTPOnly ); } #region FunctionMeta @@ -387,12 +400,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTMLSurface_FileLoadDialogResponse", CallingConvention = Platform.CC)] - private static extern void _FileLoadDialogResponse( IntPtr self, HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchSelectedFiles ); + private static extern void _FileLoadDialogResponse( IntPtr self, HHTMLBrowser unBrowserHandle, IntPtr pchSelectedFiles ); #endregion - internal void FileLoadDialogResponse( HHTMLBrowser unBrowserHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchSelectedFiles ) + internal void FileLoadDialogResponse( HHTMLBrowser unBrowserHandle, string pchSelectedFiles ) { - _FileLoadDialogResponse( Self, unBrowserHandle, pchSelectedFiles ); + using var str__pchSelectedFiles = new Utf8StringToNative( pchSelectedFiles ); + _FileLoadDialogResponse( Self, unBrowserHandle, str__pchSelectedFiles.Pointer ); } } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamHTTP.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamHTTP.cs index 599054b5c..5f3cccb61 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamHTTP.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamHTTP.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamHTTP : SteamInterface + internal unsafe partial class ISteamHTTP : SteamInterface { + public const string Version = "STEAMHTTP_INTERFACE_VERSION003"; internal ISteamHTTP( bool IsGameServer ) { @@ -25,12 +26,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTTP_CreateHTTPRequest", CallingConvention = Platform.CC)] - private static extern HTTPRequestHandle _CreateHTTPRequest( IntPtr self, HTTPMethod eHTTPRequestMethod, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchAbsoluteURL ); + private static extern HTTPRequestHandle _CreateHTTPRequest( IntPtr self, HTTPMethod eHTTPRequestMethod, IntPtr pchAbsoluteURL ); #endregion - internal HTTPRequestHandle CreateHTTPRequest( HTTPMethod eHTTPRequestMethod, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchAbsoluteURL ) + internal HTTPRequestHandle CreateHTTPRequest( HTTPMethod eHTTPRequestMethod, string pchAbsoluteURL ) { - var returnValue = _CreateHTTPRequest( Self, eHTTPRequestMethod, pchAbsoluteURL ); + using var str__pchAbsoluteURL = new Utf8StringToNative( pchAbsoluteURL ); + var returnValue = _CreateHTTPRequest( Self, eHTTPRequestMethod, str__pchAbsoluteURL.Pointer ); return returnValue; } @@ -61,24 +63,28 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTTP_SetHTTPRequestHeaderValue", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetHTTPRequestHeaderValue( IntPtr self, HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHeaderName, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHeaderValue ); + private static extern bool _SetHTTPRequestHeaderValue( IntPtr self, HTTPRequestHandle hRequest, IntPtr pchHeaderName, IntPtr pchHeaderValue ); #endregion - internal bool SetHTTPRequestHeaderValue( HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHeaderName, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHeaderValue ) + internal bool SetHTTPRequestHeaderValue( HTTPRequestHandle hRequest, string pchHeaderName, string pchHeaderValue ) { - var returnValue = _SetHTTPRequestHeaderValue( Self, hRequest, pchHeaderName, pchHeaderValue ); + using var str__pchHeaderName = new Utf8StringToNative( pchHeaderName ); + using var str__pchHeaderValue = new Utf8StringToNative( pchHeaderValue ); + var returnValue = _SetHTTPRequestHeaderValue( Self, hRequest, str__pchHeaderName.Pointer, str__pchHeaderValue.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTTP_SetHTTPRequestGetOrPostParameter", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetHTTPRequestGetOrPostParameter( IntPtr self, HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchParamName, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchParamValue ); + private static extern bool _SetHTTPRequestGetOrPostParameter( IntPtr self, HTTPRequestHandle hRequest, IntPtr pchParamName, IntPtr pchParamValue ); #endregion - internal bool SetHTTPRequestGetOrPostParameter( HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchParamName, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchParamValue ) + internal bool SetHTTPRequestGetOrPostParameter( HTTPRequestHandle hRequest, string pchParamName, string pchParamValue ) { - var returnValue = _SetHTTPRequestGetOrPostParameter( Self, hRequest, pchParamName, pchParamValue ); + using var str__pchParamName = new Utf8StringToNative( pchParamName ); + using var str__pchParamValue = new Utf8StringToNative( pchParamValue ); + var returnValue = _SetHTTPRequestGetOrPostParameter( Self, hRequest, str__pchParamName.Pointer, str__pchParamValue.Pointer ); return returnValue; } @@ -133,24 +139,26 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTTP_GetHTTPResponseHeaderSize", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetHTTPResponseHeaderSize( IntPtr self, HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHeaderName, ref uint unResponseHeaderSize ); + private static extern bool _GetHTTPResponseHeaderSize( IntPtr self, HTTPRequestHandle hRequest, IntPtr pchHeaderName, ref uint unResponseHeaderSize ); #endregion - internal bool GetHTTPResponseHeaderSize( HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHeaderName, ref uint unResponseHeaderSize ) + internal bool GetHTTPResponseHeaderSize( HTTPRequestHandle hRequest, string pchHeaderName, ref uint unResponseHeaderSize ) { - var returnValue = _GetHTTPResponseHeaderSize( Self, hRequest, pchHeaderName, ref unResponseHeaderSize ); + using var str__pchHeaderName = new Utf8StringToNative( pchHeaderName ); + var returnValue = _GetHTTPResponseHeaderSize( Self, hRequest, str__pchHeaderName.Pointer, ref unResponseHeaderSize ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTTP_GetHTTPResponseHeaderValue", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetHTTPResponseHeaderValue( IntPtr self, HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHeaderName, ref byte pHeaderValueBuffer, uint unBufferSize ); + private static extern bool _GetHTTPResponseHeaderValue( IntPtr self, HTTPRequestHandle hRequest, IntPtr pchHeaderName, ref byte pHeaderValueBuffer, uint unBufferSize ); #endregion - internal bool GetHTTPResponseHeaderValue( HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHeaderName, ref byte pHeaderValueBuffer, uint unBufferSize ) + internal bool GetHTTPResponseHeaderValue( HTTPRequestHandle hRequest, string pchHeaderName, ref byte pHeaderValueBuffer, uint unBufferSize ) { - var returnValue = _GetHTTPResponseHeaderValue( Self, hRequest, pchHeaderName, ref pHeaderValueBuffer, unBufferSize ); + using var str__pchHeaderName = new Utf8StringToNative( pchHeaderName ); + var returnValue = _GetHTTPResponseHeaderValue( Self, hRequest, str__pchHeaderName.Pointer, ref pHeaderValueBuffer, unBufferSize ); return returnValue; } @@ -217,12 +225,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTTP_SetHTTPRequestRawPostBody", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetHTTPRequestRawPostBody( IntPtr self, HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchContentType, [In,Out] byte[] pubBody, uint unBodyLen ); + private static extern bool _SetHTTPRequestRawPostBody( IntPtr self, HTTPRequestHandle hRequest, IntPtr pchContentType, [In,Out] byte[] pubBody, uint unBodyLen ); #endregion - internal bool SetHTTPRequestRawPostBody( HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchContentType, [In,Out] byte[] pubBody, uint unBodyLen ) + internal bool SetHTTPRequestRawPostBody( HTTPRequestHandle hRequest, string pchContentType, [In,Out] byte[] pubBody, uint unBodyLen ) { - var returnValue = _SetHTTPRequestRawPostBody( Self, hRequest, pchContentType, pubBody, unBodyLen ); + using var str__pchContentType = new Utf8StringToNative( pchContentType ); + var returnValue = _SetHTTPRequestRawPostBody( Self, hRequest, str__pchContentType.Pointer, pubBody, unBodyLen ); return returnValue; } @@ -252,12 +261,15 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTTP_SetCookie", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetCookie( IntPtr self, HTTPCookieContainerHandle hCookieContainer, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHost, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchUrl, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchCookie ); + private static extern bool _SetCookie( IntPtr self, HTTPCookieContainerHandle hCookieContainer, IntPtr pchHost, IntPtr pchUrl, IntPtr pchCookie ); #endregion - internal bool SetCookie( HTTPCookieContainerHandle hCookieContainer, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchHost, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchUrl, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchCookie ) + internal bool SetCookie( HTTPCookieContainerHandle hCookieContainer, string pchHost, string pchUrl, string pchCookie ) { - var returnValue = _SetCookie( Self, hCookieContainer, pchHost, pchUrl, pchCookie ); + using var str__pchHost = new Utf8StringToNative( pchHost ); + using var str__pchUrl = new Utf8StringToNative( pchUrl ); + using var str__pchCookie = new Utf8StringToNative( pchCookie ); + var returnValue = _SetCookie( Self, hCookieContainer, str__pchHost.Pointer, str__pchUrl.Pointer, str__pchCookie.Pointer ); return returnValue; } @@ -276,12 +288,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamHTTP_SetHTTPRequestUserAgentInfo", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetHTTPRequestUserAgentInfo( IntPtr self, HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchUserAgentInfo ); + private static extern bool _SetHTTPRequestUserAgentInfo( IntPtr self, HTTPRequestHandle hRequest, IntPtr pchUserAgentInfo ); #endregion - internal bool SetHTTPRequestUserAgentInfo( HTTPRequestHandle hRequest, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchUserAgentInfo ) + internal bool SetHTTPRequestUserAgentInfo( HTTPRequestHandle hRequest, string pchUserAgentInfo ) { - var returnValue = _SetHTTPRequestUserAgentInfo( Self, hRequest, pchUserAgentInfo ); + using var str__pchUserAgentInfo = new Utf8StringToNative( pchUserAgentInfo ); + var returnValue = _SetHTTPRequestUserAgentInfo( Self, hRequest, str__pchUserAgentInfo.Pointer ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs index 3d1ab5227..b591f675a 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamInput : SteamInterface + internal unsafe partial class ISteamInput : SteamInterface { + public const string Version = "SteamInput006"; internal ISteamInput( bool IsGameServer ) { @@ -47,12 +48,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInput_SetInputActionManifestFilePath", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetInputActionManifestFilePath( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchInputActionManifestAbsolutePath ); + private static extern bool _SetInputActionManifestFilePath( IntPtr self, IntPtr pchInputActionManifestAbsolutePath ); #endregion - internal bool SetInputActionManifestFilePath( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchInputActionManifestAbsolutePath ) + internal bool SetInputActionManifestFilePath( string pchInputActionManifestAbsolutePath ) { - var returnValue = _SetInputActionManifestFilePath( Self, pchInputActionManifestAbsolutePath ); + using var str__pchInputActionManifestAbsolutePath = new Utf8StringToNative( pchInputActionManifestAbsolutePath ); + var returnValue = _SetInputActionManifestFilePath( Self, str__pchInputActionManifestAbsolutePath.Pointer ); return returnValue; } @@ -113,12 +115,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInput_GetActionSetHandle", CallingConvention = Platform.CC)] - private static extern InputActionSetHandle_t _GetActionSetHandle( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionSetName ); + private static extern InputActionSetHandle_t _GetActionSetHandle( IntPtr self, IntPtr pszActionSetName ); #endregion - internal InputActionSetHandle_t GetActionSetHandle( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionSetName ) + internal InputActionSetHandle_t GetActionSetHandle( string pszActionSetName ) { - var returnValue = _GetActionSetHandle( Self, pszActionSetName ); + using var str__pszActionSetName = new Utf8StringToNative( pszActionSetName ); + var returnValue = _GetActionSetHandle( Self, str__pszActionSetName.Pointer ); return returnValue; } @@ -186,12 +189,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInput_GetDigitalActionHandle", CallingConvention = Platform.CC)] - private static extern InputDigitalActionHandle_t _GetDigitalActionHandle( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionName ); + private static extern InputDigitalActionHandle_t _GetDigitalActionHandle( IntPtr self, IntPtr pszActionName ); #endregion - internal InputDigitalActionHandle_t GetDigitalActionHandle( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionName ) + internal InputDigitalActionHandle_t GetDigitalActionHandle( string pszActionName ) { - var returnValue = _GetDigitalActionHandle( Self, pszActionName ); + using var str__pszActionName = new Utf8StringToNative( pszActionName ); + var returnValue = _GetDigitalActionHandle( Self, str__pszActionName.Pointer ); return returnValue; } @@ -230,12 +234,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInput_GetAnalogActionHandle", CallingConvention = Platform.CC)] - private static extern InputAnalogActionHandle_t _GetAnalogActionHandle( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionName ); + private static extern InputAnalogActionHandle_t _GetAnalogActionHandle( IntPtr self, IntPtr pszActionName ); #endregion - internal InputAnalogActionHandle_t GetAnalogActionHandle( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszActionName ) + internal InputAnalogActionHandle_t GetAnalogActionHandle( string pszActionName ) { - var returnValue = _GetAnalogActionHandle( Self, pszActionName ); + using var str__pszActionName = new Utf8StringToNative( pszActionName ); + var returnValue = _GetAnalogActionHandle( Self, str__pszActionName.Pointer ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamInventory.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamInventory.cs index 5b4d20160..27a795f13 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamInventory.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamInventory.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamInventory : SteamInterface + internal unsafe partial class ISteamInventory : SteamInterface { + public const string Version = "STEAMINVENTORY_INTERFACE_V003"; internal ISteamInventory( bool IsGameServer ) { @@ -49,14 +50,16 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInventory_GetResultItemProperty", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetResultItemProperty( IntPtr self, SteamInventoryResult_t resultHandle, uint unItemIndex, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string? pchPropertyName, IntPtr pchValueBuffer, ref uint punValueBufferSizeOut ); + private static extern bool _GetResultItemProperty( IntPtr self, SteamInventoryResult_t resultHandle, uint unItemIndex, IntPtr pchPropertyName, IntPtr pchValueBuffer, ref uint punValueBufferSizeOut ); #endregion - internal bool GetResultItemProperty( SteamInventoryResult_t resultHandle, uint unItemIndex, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string? pchPropertyName, out string pchValueBuffer, ref uint punValueBufferSizeOut ) + internal bool GetResultItemProperty( SteamInventoryResult_t resultHandle, uint unItemIndex, string? pchPropertyName, out string pchValueBuffer ) { - using var mempchValueBuffer = Helpers.TakeMemory(); - var returnValue = _GetResultItemProperty( Self, resultHandle, unItemIndex, pchPropertyName, mempchValueBuffer, ref punValueBufferSizeOut ); - pchValueBuffer = Helpers.MemoryToString( mempchValueBuffer ); + using var str__pchPropertyName = new Utf8StringToNative( pchPropertyName ); + using var mem__pchValueBuffer = Helpers.TakeMemory(); + uint szpunValueBufferSizeOut = (1024 * 32); + var returnValue = _GetResultItemProperty( Self, resultHandle, unItemIndex, str__pchPropertyName.Pointer, mem__pchValueBuffer, ref szpunValueBufferSizeOut ); + pchValueBuffer = Helpers.MemoryToString( mem__pchValueBuffer ); return returnValue; } @@ -286,14 +289,16 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInventory_GetItemDefinitionProperty", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetItemDefinitionProperty( IntPtr self, InventoryDefId iDefinition, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string? pchPropertyName, IntPtr pchValueBuffer, ref uint punValueBufferSizeOut ); + private static extern bool _GetItemDefinitionProperty( IntPtr self, InventoryDefId iDefinition, IntPtr pchPropertyName, IntPtr pchValueBuffer, ref uint punValueBufferSizeOut ); #endregion - internal bool GetItemDefinitionProperty( InventoryDefId iDefinition, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string? pchPropertyName, out string pchValueBuffer, ref uint punValueBufferSizeOut ) + internal bool GetItemDefinitionProperty( InventoryDefId iDefinition, string? pchPropertyName, out string pchValueBuffer ) { - using var mempchValueBuffer = Helpers.TakeMemory(); - var returnValue = _GetItemDefinitionProperty( Self, iDefinition, pchPropertyName, mempchValueBuffer, ref punValueBufferSizeOut ); - pchValueBuffer = Helpers.MemoryToString( mempchValueBuffer ); + using var str__pchPropertyName = new Utf8StringToNative( pchPropertyName ); + using var mem__pchValueBuffer = Helpers.TakeMemory(); + uint szpunValueBufferSizeOut = (1024 * 32); + var returnValue = _GetItemDefinitionProperty( Self, iDefinition, str__pchPropertyName.Pointer, mem__pchValueBuffer, ref szpunValueBufferSizeOut ); + pchValueBuffer = Helpers.MemoryToString( mem__pchValueBuffer ); return returnValue; } @@ -391,60 +396,66 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInventory_RemoveProperty", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _RemoveProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName ); + private static extern bool _RemoveProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, IntPtr pchPropertyName ); #endregion - internal bool RemoveProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName ) + internal bool RemoveProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, string pchPropertyName ) { - var returnValue = _RemoveProperty( Self, handle, nItemID, pchPropertyName ); + using var str__pchPropertyName = new Utf8StringToNative( pchPropertyName ); + var returnValue = _RemoveProperty( Self, handle, nItemID, str__pchPropertyName.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInventory_SetPropertyString", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyValue ); + private static extern bool _SetProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, IntPtr pchPropertyName, IntPtr pchPropertyValue ); #endregion - internal bool SetProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyValue ) + internal bool SetProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, string pchPropertyName, string pchPropertyValue ) { - var returnValue = _SetProperty( Self, handle, nItemID, pchPropertyName, pchPropertyValue ); + using var str__pchPropertyName = new Utf8StringToNative( pchPropertyName ); + using var str__pchPropertyValue = new Utf8StringToNative( pchPropertyValue ); + var returnValue = _SetProperty( Self, handle, nItemID, str__pchPropertyName.Pointer, str__pchPropertyValue.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInventory_SetPropertyBool", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName, [MarshalAs( UnmanagedType.U1 )] bool bValue ); + private static extern bool _SetProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, IntPtr pchPropertyName, [MarshalAs( UnmanagedType.U1 )] bool bValue ); #endregion - internal bool SetProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName, [MarshalAs( UnmanagedType.U1 )] bool bValue ) + internal bool SetProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, string pchPropertyName, [MarshalAs( UnmanagedType.U1 )] bool bValue ) { - var returnValue = _SetProperty( Self, handle, nItemID, pchPropertyName, bValue ); + using var str__pchPropertyName = new Utf8StringToNative( pchPropertyName ); + var returnValue = _SetProperty( Self, handle, nItemID, str__pchPropertyName.Pointer, bValue ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInventory_SetPropertyInt64", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName, long nValue ); + private static extern bool _SetProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, IntPtr pchPropertyName, long nValue ); #endregion - internal bool SetProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName, long nValue ) + internal bool SetProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, string pchPropertyName, long nValue ) { - var returnValue = _SetProperty( Self, handle, nItemID, pchPropertyName, nValue ); + using var str__pchPropertyName = new Utf8StringToNative( pchPropertyName ); + var returnValue = _SetProperty( Self, handle, nItemID, str__pchPropertyName.Pointer, nValue ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInventory_SetPropertyFloat", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName, float flValue ); + private static extern bool _SetProperty( IntPtr self, SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, IntPtr pchPropertyName, float flValue ); #endregion - internal bool SetProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchPropertyName, float flValue ) + internal bool SetProperty( SteamInventoryUpdateHandle_t handle, InventoryItemId nItemID, string pchPropertyName, float flValue ) { - var returnValue = _SetProperty( Self, handle, nItemID, pchPropertyName, flValue ); + using var str__pchPropertyName = new Utf8StringToNative( pchPropertyName ); + var returnValue = _SetProperty( Self, handle, nItemID, str__pchPropertyName.Pointer, flValue ); return returnValue; } @@ -463,12 +474,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamInventory_InspectItem", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _InspectItem( IntPtr self, ref SteamInventoryResult_t pResultHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchItemToken ); + private static extern bool _InspectItem( IntPtr self, ref SteamInventoryResult_t pResultHandle, IntPtr pchItemToken ); #endregion - internal bool InspectItem( ref SteamInventoryResult_t pResultHandle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchItemToken ) + internal bool InspectItem( ref SteamInventoryResult_t pResultHandle, string pchItemToken ) { - var returnValue = _InspectItem( Self, ref pResultHandle, pchItemToken ); + using var str__pchItemToken = new Utf8StringToNative( pchItemToken ); + var returnValue = _InspectItem( Self, ref pResultHandle, str__pchItemToken.Pointer ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmaking.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmaking.cs index 801d41086..8d952d90d 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmaking.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmaking.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamMatchmaking : SteamInterface + internal unsafe partial class ISteamMatchmaking : SteamInterface { + public const string Version = "SteamMatchMaking009"; internal ISteamMatchmaking( bool IsGameServer ) { @@ -79,32 +80,36 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmaking_AddRequestLobbyListStringFilter", CallingConvention = Platform.CC)] - private static extern void _AddRequestLobbyListStringFilter( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKeyToMatch, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValueToMatch, LobbyComparison eComparisonType ); + private static extern void _AddRequestLobbyListStringFilter( IntPtr self, IntPtr pchKeyToMatch, IntPtr pchValueToMatch, LobbyComparison eComparisonType ); #endregion - internal void AddRequestLobbyListStringFilter( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKeyToMatch, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValueToMatch, LobbyComparison eComparisonType ) + internal void AddRequestLobbyListStringFilter( string pchKeyToMatch, string pchValueToMatch, LobbyComparison eComparisonType ) { - _AddRequestLobbyListStringFilter( Self, pchKeyToMatch, pchValueToMatch, eComparisonType ); + using var str__pchKeyToMatch = new Utf8StringToNative( pchKeyToMatch ); + using var str__pchValueToMatch = new Utf8StringToNative( pchValueToMatch ); + _AddRequestLobbyListStringFilter( Self, str__pchKeyToMatch.Pointer, str__pchValueToMatch.Pointer, eComparisonType ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmaking_AddRequestLobbyListNumericalFilter", CallingConvention = Platform.CC)] - private static extern void _AddRequestLobbyListNumericalFilter( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKeyToMatch, int nValueToMatch, LobbyComparison eComparisonType ); + private static extern void _AddRequestLobbyListNumericalFilter( IntPtr self, IntPtr pchKeyToMatch, int nValueToMatch, LobbyComparison eComparisonType ); #endregion - internal void AddRequestLobbyListNumericalFilter( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKeyToMatch, int nValueToMatch, LobbyComparison eComparisonType ) + internal void AddRequestLobbyListNumericalFilter( string pchKeyToMatch, int nValueToMatch, LobbyComparison eComparisonType ) { - _AddRequestLobbyListNumericalFilter( Self, pchKeyToMatch, nValueToMatch, eComparisonType ); + using var str__pchKeyToMatch = new Utf8StringToNative( pchKeyToMatch ); + _AddRequestLobbyListNumericalFilter( Self, str__pchKeyToMatch.Pointer, nValueToMatch, eComparisonType ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmaking_AddRequestLobbyListNearValueFilter", CallingConvention = Platform.CC)] - private static extern void _AddRequestLobbyListNearValueFilter( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKeyToMatch, int nValueToBeCloseTo ); + private static extern void _AddRequestLobbyListNearValueFilter( IntPtr self, IntPtr pchKeyToMatch, int nValueToBeCloseTo ); #endregion - internal void AddRequestLobbyListNearValueFilter( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKeyToMatch, int nValueToBeCloseTo ) + internal void AddRequestLobbyListNearValueFilter( string pchKeyToMatch, int nValueToBeCloseTo ) { - _AddRequestLobbyListNearValueFilter( Self, pchKeyToMatch, nValueToBeCloseTo ); + using var str__pchKeyToMatch = new Utf8StringToNative( pchKeyToMatch ); + _AddRequestLobbyListNearValueFilter( Self, str__pchKeyToMatch.Pointer, nValueToBeCloseTo ); } #region FunctionMeta @@ -226,24 +231,27 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmaking_GetLobbyData", CallingConvention = Platform.CC)] - private static extern Utf8StringPointer _GetLobbyData( IntPtr self, SteamId steamIDLobby, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ); + private static extern Utf8StringPointer _GetLobbyData( IntPtr self, SteamId steamIDLobby, IntPtr pchKey ); #endregion - internal string GetLobbyData( SteamId steamIDLobby, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ) + internal string GetLobbyData( SteamId steamIDLobby, string pchKey ) { - var returnValue = _GetLobbyData( Self, steamIDLobby, pchKey ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + var returnValue = _GetLobbyData( Self, steamIDLobby, str__pchKey.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmaking_SetLobbyData", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetLobbyData( IntPtr self, SteamId steamIDLobby, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ); + private static extern bool _SetLobbyData( IntPtr self, SteamId steamIDLobby, IntPtr pchKey, IntPtr pchValue ); #endregion - internal bool SetLobbyData( SteamId steamIDLobby, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ) + internal bool SetLobbyData( SteamId steamIDLobby, string pchKey, string pchValue ) { - var returnValue = _SetLobbyData( Self, steamIDLobby, pchKey, pchValue ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + using var str__pchValue = new Utf8StringToNative( pchValue ); + var returnValue = _SetLobbyData( Self, steamIDLobby, str__pchKey.Pointer, str__pchValue.Pointer ); return returnValue; } @@ -266,45 +274,49 @@ namespace Steamworks #endregion internal bool GetLobbyDataByIndex( SteamId steamIDLobby, int iLobbyData, out string pchKey, out string pchValue ) { - using var mempchKey = Helpers.TakeMemory(); - using var mempchValue = Helpers.TakeMemory(); - var returnValue = _GetLobbyDataByIndex( Self, steamIDLobby, iLobbyData, mempchKey, (1024 * 32), mempchValue, (1024 * 32) ); - pchKey = Helpers.MemoryToString( mempchKey ); - pchValue = Helpers.MemoryToString( mempchValue ); + using var mem__pchKey = Helpers.TakeMemory(); + using var mem__pchValue = Helpers.TakeMemory(); + var returnValue = _GetLobbyDataByIndex( Self, steamIDLobby, iLobbyData, mem__pchKey, (1024 * 32), mem__pchValue, (1024 * 32) ); + pchKey = Helpers.MemoryToString( mem__pchKey ); + pchValue = Helpers.MemoryToString( mem__pchValue ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmaking_DeleteLobbyData", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _DeleteLobbyData( IntPtr self, SteamId steamIDLobby, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ); + private static extern bool _DeleteLobbyData( IntPtr self, SteamId steamIDLobby, IntPtr pchKey ); #endregion - internal bool DeleteLobbyData( SteamId steamIDLobby, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ) + internal bool DeleteLobbyData( SteamId steamIDLobby, string pchKey ) { - var returnValue = _DeleteLobbyData( Self, steamIDLobby, pchKey ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + var returnValue = _DeleteLobbyData( Self, steamIDLobby, str__pchKey.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmaking_GetLobbyMemberData", CallingConvention = Platform.CC)] - private static extern Utf8StringPointer _GetLobbyMemberData( IntPtr self, SteamId steamIDLobby, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ); + private static extern Utf8StringPointer _GetLobbyMemberData( IntPtr self, SteamId steamIDLobby, SteamId steamIDUser, IntPtr pchKey ); #endregion - internal string GetLobbyMemberData( SteamId steamIDLobby, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ) + internal string GetLobbyMemberData( SteamId steamIDLobby, SteamId steamIDUser, string pchKey ) { - var returnValue = _GetLobbyMemberData( Self, steamIDLobby, steamIDUser, pchKey ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + var returnValue = _GetLobbyMemberData( Self, steamIDLobby, steamIDUser, str__pchKey.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmaking_SetLobbyMemberData", CallingConvention = Platform.CC)] - private static extern void _SetLobbyMemberData( IntPtr self, SteamId steamIDLobby, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ); + private static extern void _SetLobbyMemberData( IntPtr self, SteamId steamIDLobby, IntPtr pchKey, IntPtr pchValue ); #endregion - internal void SetLobbyMemberData( SteamId steamIDLobby, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ) + internal void SetLobbyMemberData( SteamId steamIDLobby, string pchKey, string pchValue ) { - _SetLobbyMemberData( Self, steamIDLobby, pchKey, pchValue ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + using var str__pchValue = new Utf8StringToNative( pchValue ); + _SetLobbyMemberData( Self, steamIDLobby, str__pchKey.Pointer, str__pchValue.Pointer ); } #region FunctionMeta diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPingResponse.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPingResponse.cs index 0306b76f6..059189f68 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPingResponse.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPingResponse.cs @@ -7,9 +7,8 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamMatchmakingPingResponse : SteamInterface + internal unsafe partial class ISteamMatchmakingPingResponse : SteamInterface { - internal ISteamMatchmakingPingResponse( bool IsGameServer ) { SetupInterface( IsGameServer ); diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPlayersResponse.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPlayersResponse.cs index a5ed92691..bd57a7d0a 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPlayersResponse.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPlayersResponse.cs @@ -7,9 +7,8 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamMatchmakingPlayersResponse : SteamInterface + internal unsafe partial class ISteamMatchmakingPlayersResponse : SteamInterface { - internal ISteamMatchmakingPlayersResponse( bool IsGameServer ) { SetupInterface( IsGameServer ); @@ -17,12 +16,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmakingPlayersResponse_AddPlayerToList", CallingConvention = Platform.CC)] - private static extern void _AddPlayerToList( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, int nScore, float flTimePlayed ); + private static extern void _AddPlayerToList( IntPtr self, IntPtr pchName, int nScore, float flTimePlayed ); #endregion - internal void AddPlayerToList( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, int nScore, float flTimePlayed ) + internal void AddPlayerToList( string pchName, int nScore, float flTimePlayed ) { - _AddPlayerToList( Self, pchName, nScore, flTimePlayed ); + using var str__pchName = new Utf8StringToNative( pchName ); + _AddPlayerToList( Self, str__pchName.Pointer, nScore, flTimePlayed ); } #region FunctionMeta diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingRulesResponse.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingRulesResponse.cs index 78507b9ea..7bee06ba8 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingRulesResponse.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingRulesResponse.cs @@ -7,9 +7,8 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamMatchmakingRulesResponse : SteamInterface + internal unsafe partial class ISteamMatchmakingRulesResponse : SteamInterface { - internal ISteamMatchmakingRulesResponse( bool IsGameServer ) { SetupInterface( IsGameServer ); @@ -17,12 +16,14 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmakingRulesResponse_RulesResponded", CallingConvention = Platform.CC)] - private static extern void _RulesResponded( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchRule, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ); + private static extern void _RulesResponded( IntPtr self, IntPtr pchRule, IntPtr pchValue ); #endregion - internal void RulesResponded( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchRule, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ) + internal void RulesResponded( string pchRule, string pchValue ) { - _RulesResponded( Self, pchRule, pchValue ); + using var str__pchRule = new Utf8StringToNative( pchRule ); + using var str__pchValue = new Utf8StringToNative( pchValue ); + _RulesResponded( Self, str__pchRule.Pointer, str__pchValue.Pointer ); } #region FunctionMeta diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServerListResponse.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServerListResponse.cs index 8fd0a9e98..68a4a55ff 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServerListResponse.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServerListResponse.cs @@ -7,9 +7,8 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamMatchmakingServerListResponse : SteamInterface + internal unsafe partial class ISteamMatchmakingServerListResponse : SteamInterface { - internal ISteamMatchmakingServerListResponse( bool IsGameServer ) { SetupInterface( IsGameServer ); diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs index 3eb098c00..93f582b03 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamMatchmakingServers : SteamInterface + internal unsafe partial class ISteamMatchmakingServers : SteamInterface { + public const string Version = "SteamMatchMakingServers002"; internal ISteamMatchmakingServers( bool IsGameServer ) { @@ -25,37 +26,9 @@ namespace Steamworks private static extern HServerListRequest _RequestInternetServerList( IntPtr self, AppId iApp, IntPtr ppchFilters, uint nFilters, IntPtr pRequestServersResponse ); #endregion - internal HServerListRequest RequestInternetServerList( AppId iApp, MatchMakingKeyValuePair[] ppchFilters, uint nFilters, IntPtr pRequestServersResponse ) + internal HServerListRequest RequestInternetServerList( AppId iApp, IntPtr ppchFilters, uint nFilters, IntPtr pRequestServersResponse ) { - int numPtrs = ppchFilters.Length; - if (numPtrs <= 0) { numPtrs = 1; } - - IntPtr[] filterPtrs = new IntPtr[numPtrs]; - GCHandle?[] filterHandles = new GCHandle?[numPtrs]; - for (int i=0;i CreateBeacon( uint unOpenSlots, /* ref */ SteamPartyBeaconLocation_t pBeaconLocation, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchConnectString, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchMetadata ) + internal CallResult CreateBeacon( uint unOpenSlots, /* ref */ SteamPartyBeaconLocation_t pBeaconLocation, string pchConnectString, string pchMetadata ) { - var returnValue = _CreateBeacon( Self, unOpenSlots, ref pBeaconLocation, pchConnectString, pchMetadata ); + using var str__pchConnectString = new Utf8StringToNative( pchConnectString ); + using var str__pchMetadata = new Utf8StringToNative( pchMetadata ); + var returnValue = _CreateBeacon( Self, unOpenSlots, ref pBeaconLocation, str__pchConnectString.Pointer, str__pchMetadata.Pointer ); return new CallResult( returnValue, IsServer ); } @@ -153,9 +156,9 @@ namespace Steamworks #endregion internal bool GetBeaconLocationData( SteamPartyBeaconLocation_t BeaconLocation, SteamPartyBeaconLocationData eData, out string pchDataStringOut ) { - using var mempchDataStringOut = Helpers.TakeMemory(); - var returnValue = _GetBeaconLocationData( Self, BeaconLocation, eData, mempchDataStringOut, (1024 * 32) ); - pchDataStringOut = Helpers.MemoryToString( mempchDataStringOut ); + using var mem__pchDataStringOut = Helpers.TakeMemory(); + var returnValue = _GetBeaconLocationData( Self, BeaconLocation, eData, mem__pchDataStringOut, (1024 * 32) ); + pchDataStringOut = Helpers.MemoryToString( mem__pchDataStringOut ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamRemotePlay.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamRemotePlay.cs index 6ad42043c..c744b158a 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamRemotePlay.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamRemotePlay.cs @@ -7,17 +7,18 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamRemotePlay : SteamInterface + internal unsafe partial class ISteamRemotePlay : SteamInterface { + public const string Version = "STEAMREMOTEPLAY_INTERFACE_VERSION002"; internal ISteamRemotePlay( bool IsGameServer ) { SetupInterface( IsGameServer ); } - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamRemotePlay_v001", CallingConvention = Platform.CC)] - internal static extern IntPtr SteamAPI_SteamRemotePlay_v001(); - public override IntPtr GetUserInterfacePointer() => SteamAPI_SteamRemotePlay_v001(); + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamRemotePlay_v002", CallingConvention = Platform.CC)] + internal static extern IntPtr SteamAPI_SteamRemotePlay_v002(); + public override IntPtr GetUserInterfacePointer() => SteamAPI_SteamRemotePlay_v002(); #region FunctionMeta @@ -87,6 +88,18 @@ namespace Steamworks return returnValue; } + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemotePlay_BStartRemotePlayTogether", CallingConvention = Platform.CC)] + [return: MarshalAs( UnmanagedType.I1 )] + private static extern bool _BStartRemotePlayTogether( IntPtr self, [MarshalAs( UnmanagedType.U1 )] bool bShowOverlay ); + + #endregion + internal bool BStartRemotePlayTogether( [MarshalAs( UnmanagedType.U1 )] bool bShowOverlay ) + { + var returnValue = _BStartRemotePlayTogether( Self, bShowOverlay ); + return returnValue; + } + #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemotePlay_BSendRemotePlayTogetherInvite", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamRemoteStorage.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamRemoteStorage.cs index c1a872115..c22f50253 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamRemoteStorage.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamRemoteStorage.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamRemoteStorage : SteamInterface + internal unsafe partial class ISteamRemoteStorage : SteamInterface { + public const string Version = "STEAMREMOTESTORAGE_INTERFACE_VERSION016"; internal ISteamRemoteStorage( bool IsGameServer ) { @@ -23,45 +24,49 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FileWrite", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _FileWrite( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, IntPtr pvData, int cubData ); + private static extern bool _FileWrite( IntPtr self, IntPtr pchFile, IntPtr pvData, int cubData ); #endregion - internal bool FileWrite( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, IntPtr pvData, int cubData ) + internal bool FileWrite( string pchFile, IntPtr pvData, int cubData ) { - var returnValue = _FileWrite( Self, pchFile, pvData, cubData ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FileWrite( Self, str__pchFile.Pointer, pvData, cubData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FileRead", CallingConvention = Platform.CC)] - private static extern int _FileRead( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, IntPtr pvData, int cubDataToRead ); + private static extern int _FileRead( IntPtr self, IntPtr pchFile, IntPtr pvData, int cubDataToRead ); #endregion - internal int FileRead( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, IntPtr pvData, int cubDataToRead ) + internal int FileRead( string pchFile, IntPtr pvData, int cubDataToRead ) { - var returnValue = _FileRead( Self, pchFile, pvData, cubDataToRead ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FileRead( Self, str__pchFile.Pointer, pvData, cubDataToRead ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FileWriteAsync", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _FileWriteAsync( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, IntPtr pvData, uint cubData ); + private static extern SteamAPICall_t _FileWriteAsync( IntPtr self, IntPtr pchFile, IntPtr pvData, uint cubData ); #endregion - internal CallResult FileWriteAsync( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, IntPtr pvData, uint cubData ) + internal CallResult FileWriteAsync( string pchFile, IntPtr pvData, uint cubData ) { - var returnValue = _FileWriteAsync( Self, pchFile, pvData, cubData ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FileWriteAsync( Self, str__pchFile.Pointer, pvData, cubData ); return new CallResult( returnValue, IsServer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FileReadAsync", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _FileReadAsync( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, uint nOffset, uint cubToRead ); + private static extern SteamAPICall_t _FileReadAsync( IntPtr self, IntPtr pchFile, uint nOffset, uint cubToRead ); #endregion - internal CallResult FileReadAsync( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, uint nOffset, uint cubToRead ) + internal CallResult FileReadAsync( string pchFile, uint nOffset, uint cubToRead ) { - var returnValue = _FileReadAsync( Self, pchFile, nOffset, cubToRead ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FileReadAsync( Self, str__pchFile.Pointer, nOffset, cubToRead ); return new CallResult( returnValue, IsServer ); } @@ -80,58 +85,63 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FileForget", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _FileForget( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ); + private static extern bool _FileForget( IntPtr self, IntPtr pchFile ); #endregion - internal bool FileForget( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ) + internal bool FileForget( string pchFile ) { - var returnValue = _FileForget( Self, pchFile ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FileForget( Self, str__pchFile.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FileDelete", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _FileDelete( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ); + private static extern bool _FileDelete( IntPtr self, IntPtr pchFile ); #endregion - internal bool FileDelete( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ) + internal bool FileDelete( string pchFile ) { - var returnValue = _FileDelete( Self, pchFile ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FileDelete( Self, str__pchFile.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FileShare", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _FileShare( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ); + private static extern SteamAPICall_t _FileShare( IntPtr self, IntPtr pchFile ); #endregion - internal CallResult FileShare( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ) + internal CallResult FileShare( string pchFile ) { - var returnValue = _FileShare( Self, pchFile ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FileShare( Self, str__pchFile.Pointer ); return new CallResult( returnValue, IsServer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_SetSyncPlatforms", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetSyncPlatforms( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, RemoteStoragePlatform eRemoteStoragePlatform ); + private static extern bool _SetSyncPlatforms( IntPtr self, IntPtr pchFile, RemoteStoragePlatform eRemoteStoragePlatform ); #endregion - internal bool SetSyncPlatforms( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile, RemoteStoragePlatform eRemoteStoragePlatform ) + internal bool SetSyncPlatforms( string pchFile, RemoteStoragePlatform eRemoteStoragePlatform ) { - var returnValue = _SetSyncPlatforms( Self, pchFile, eRemoteStoragePlatform ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _SetSyncPlatforms( Self, str__pchFile.Pointer, eRemoteStoragePlatform ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FileWriteStreamOpen", CallingConvention = Platform.CC)] - private static extern UGCFileWriteStreamHandle_t _FileWriteStreamOpen( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ); + private static extern UGCFileWriteStreamHandle_t _FileWriteStreamOpen( IntPtr self, IntPtr pchFile ); #endregion - internal UGCFileWriteStreamHandle_t FileWriteStreamOpen( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ) + internal UGCFileWriteStreamHandle_t FileWriteStreamOpen( string pchFile ) { - var returnValue = _FileWriteStreamOpen( Self, pchFile ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FileWriteStreamOpen( Self, str__pchFile.Pointer ); return returnValue; } @@ -174,57 +184,62 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FileExists", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _FileExists( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ); + private static extern bool _FileExists( IntPtr self, IntPtr pchFile ); #endregion - internal bool FileExists( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ) + internal bool FileExists( string pchFile ) { - var returnValue = _FileExists( Self, pchFile ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FileExists( Self, str__pchFile.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_FilePersisted", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _FilePersisted( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ); + private static extern bool _FilePersisted( IntPtr self, IntPtr pchFile ); #endregion - internal bool FilePersisted( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ) + internal bool FilePersisted( string pchFile ) { - var returnValue = _FilePersisted( Self, pchFile ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _FilePersisted( Self, str__pchFile.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_GetFileSize", CallingConvention = Platform.CC)] - private static extern int _GetFileSize( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ); + private static extern int _GetFileSize( IntPtr self, IntPtr pchFile ); #endregion - internal int GetFileSize( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ) + internal int GetFileSize( string pchFile ) { - var returnValue = _GetFileSize( Self, pchFile ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _GetFileSize( Self, str__pchFile.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_GetFileTimestamp", CallingConvention = Platform.CC)] - private static extern long _GetFileTimestamp( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ); + private static extern long _GetFileTimestamp( IntPtr self, IntPtr pchFile ); #endregion - internal long GetFileTimestamp( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ) + internal long GetFileTimestamp( string pchFile ) { - var returnValue = _GetFileTimestamp( Self, pchFile ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _GetFileTimestamp( Self, str__pchFile.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_GetSyncPlatforms", CallingConvention = Platform.CC)] - private static extern RemoteStoragePlatform _GetSyncPlatforms( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ); + private static extern RemoteStoragePlatform _GetSyncPlatforms( IntPtr self, IntPtr pchFile ); #endregion - internal RemoteStoragePlatform GetSyncPlatforms( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFile ) + internal RemoteStoragePlatform GetSyncPlatforms( string pchFile ) { - var returnValue = _GetSyncPlatforms( Self, pchFile ); + using var str__pchFile = new Utf8StringToNative( pchFile ); + var returnValue = _GetSyncPlatforms( Self, str__pchFile.Pointer ); return returnValue; } @@ -366,12 +381,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamRemoteStorage_UGCDownloadToLocation", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _UGCDownloadToLocation( IntPtr self, UGCHandle_t hContent, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLocation, uint unPriority ); + private static extern SteamAPICall_t _UGCDownloadToLocation( IntPtr self, UGCHandle_t hContent, IntPtr pchLocation, uint unPriority ); #endregion - internal CallResult UGCDownloadToLocation( UGCHandle_t hContent, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLocation, uint unPriority ) + internal CallResult UGCDownloadToLocation( UGCHandle_t hContent, string pchLocation, uint unPriority ) { - var returnValue = _UGCDownloadToLocation( Self, hContent, pchLocation, unPriority ); + using var str__pchLocation = new Utf8StringToNative( pchLocation ); + var returnValue = _UGCDownloadToLocation( Self, hContent, str__pchLocation.Pointer, unPriority ); return new CallResult( returnValue, IsServer ); } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamScreenshots.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamScreenshots.cs index ff9380a3f..a341eb2be 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamScreenshots.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamScreenshots.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamScreenshots : SteamInterface + internal unsafe partial class ISteamScreenshots : SteamInterface { + public const string Version = "STEAMSCREENSHOTS_INTERFACE_VERSION003"; internal ISteamScreenshots( bool IsGameServer ) { @@ -33,12 +34,14 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamScreenshots_AddScreenshotToLibrary", CallingConvention = Platform.CC)] - private static extern ScreenshotHandle _AddScreenshotToLibrary( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFilename, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchThumbnailFilename, int nWidth, int nHeight ); + private static extern ScreenshotHandle _AddScreenshotToLibrary( IntPtr self, IntPtr pchFilename, IntPtr pchThumbnailFilename, int nWidth, int nHeight ); #endregion - internal ScreenshotHandle AddScreenshotToLibrary( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFilename, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchThumbnailFilename, int nWidth, int nHeight ) + internal ScreenshotHandle AddScreenshotToLibrary( string pchFilename, string pchThumbnailFilename, int nWidth, int nHeight ) { - var returnValue = _AddScreenshotToLibrary( Self, pchFilename, pchThumbnailFilename, nWidth, nHeight ); + using var str__pchFilename = new Utf8StringToNative( pchFilename ); + using var str__pchThumbnailFilename = new Utf8StringToNative( pchThumbnailFilename ); + var returnValue = _AddScreenshotToLibrary( Self, str__pchFilename.Pointer, str__pchThumbnailFilename.Pointer, nWidth, nHeight ); return returnValue; } @@ -65,12 +68,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamScreenshots_SetLocation", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetLocation( IntPtr self, ScreenshotHandle hScreenshot, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLocation ); + private static extern bool _SetLocation( IntPtr self, ScreenshotHandle hScreenshot, IntPtr pchLocation ); #endregion - internal bool SetLocation( ScreenshotHandle hScreenshot, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLocation ) + internal bool SetLocation( ScreenshotHandle hScreenshot, string pchLocation ) { - var returnValue = _SetLocation( Self, hScreenshot, pchLocation ); + using var str__pchLocation = new Utf8StringToNative( pchLocation ); + var returnValue = _SetLocation( Self, hScreenshot, str__pchLocation.Pointer ); return returnValue; } @@ -112,12 +116,14 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamScreenshots_AddVRScreenshotToLibrary", CallingConvention = Platform.CC)] - private static extern ScreenshotHandle _AddVRScreenshotToLibrary( IntPtr self, VRScreenshotType eType, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFilename, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVRFilename ); + private static extern ScreenshotHandle _AddVRScreenshotToLibrary( IntPtr self, VRScreenshotType eType, IntPtr pchFilename, IntPtr pchVRFilename ); #endregion - internal ScreenshotHandle AddVRScreenshotToLibrary( VRScreenshotType eType, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchFilename, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVRFilename ) + internal ScreenshotHandle AddVRScreenshotToLibrary( VRScreenshotType eType, string pchFilename, string pchVRFilename ) { - var returnValue = _AddVRScreenshotToLibrary( Self, eType, pchFilename, pchVRFilename ); + using var str__pchFilename = new Utf8StringToNative( pchFilename ); + using var str__pchVRFilename = new Utf8StringToNative( pchVRFilename ); + var returnValue = _AddVRScreenshotToLibrary( Self, eType, str__pchFilename.Pointer, str__pchVRFilename.Pointer ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamTimeline.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamTimeline.cs new file mode 100644 index 000000000..8be80a7cc --- /dev/null +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamTimeline.cs @@ -0,0 +1,231 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Steamworks.Data; + + +namespace Steamworks +{ + internal unsafe partial class ISteamTimeline : SteamInterface + { + public const string Version = "STEAMTIMELINE_INTERFACE_V004"; + + internal ISteamTimeline( bool IsGameServer ) + { + SetupInterface( IsGameServer ); + } + + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamTimeline_v004", CallingConvention = Platform.CC)] + internal static extern IntPtr SteamAPI_SteamTimeline_v004(); + public override IntPtr GetUserInterfacePointer() => SteamAPI_SteamTimeline_v004(); + + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_SetTimelineTooltip", CallingConvention = Platform.CC)] + private static extern void _SetTimelineTooltip( IntPtr self, IntPtr pchDescription, float flTimeDelta ); + + #endregion + internal void SetTimelineTooltip( string pchDescription, float flTimeDelta ) + { + using var str__pchDescription = new Utf8StringToNative( pchDescription ); + _SetTimelineTooltip( Self, str__pchDescription.Pointer, flTimeDelta ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_ClearTimelineTooltip", CallingConvention = Platform.CC)] + private static extern void _ClearTimelineTooltip( IntPtr self, float flTimeDelta ); + + #endregion + internal void ClearTimelineTooltip( float flTimeDelta ) + { + _ClearTimelineTooltip( Self, flTimeDelta ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_SetTimelineGameMode", CallingConvention = Platform.CC)] + private static extern void _SetTimelineGameMode( IntPtr self, TimelineGameMode eMode ); + + #endregion + internal void SetTimelineGameMode( TimelineGameMode eMode ) + { + _SetTimelineGameMode( Self, eMode ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_AddInstantaneousTimelineEvent", CallingConvention = Platform.CC)] + private static extern TimelineEventHandle _AddInstantaneousTimelineEvent( IntPtr self, IntPtr pchTitle, IntPtr pchDescription, IntPtr pchIcon, uint unIconPriority, float flStartOffsetSeconds, TimelineEventClipPriority ePossibleClip ); + + #endregion + internal TimelineEventHandle AddInstantaneousTimelineEvent( string pchTitle, string pchDescription, string pchIcon, uint unIconPriority, float flStartOffsetSeconds, TimelineEventClipPriority ePossibleClip ) + { + using var str__pchTitle = new Utf8StringToNative( pchTitle ); + using var str__pchDescription = new Utf8StringToNative( pchDescription ); + using var str__pchIcon = new Utf8StringToNative( pchIcon ); + var returnValue = _AddInstantaneousTimelineEvent( Self, str__pchTitle.Pointer, str__pchDescription.Pointer, str__pchIcon.Pointer, unIconPriority, flStartOffsetSeconds, ePossibleClip ); + return returnValue; + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_AddRangeTimelineEvent", CallingConvention = Platform.CC)] + private static extern TimelineEventHandle _AddRangeTimelineEvent( IntPtr self, IntPtr pchTitle, IntPtr pchDescription, IntPtr pchIcon, uint unIconPriority, float flStartOffsetSeconds, float flDuration, TimelineEventClipPriority ePossibleClip ); + + #endregion + internal TimelineEventHandle AddRangeTimelineEvent( string pchTitle, string pchDescription, string pchIcon, uint unIconPriority, float flStartOffsetSeconds, float flDuration, TimelineEventClipPriority ePossibleClip ) + { + using var str__pchTitle = new Utf8StringToNative( pchTitle ); + using var str__pchDescription = new Utf8StringToNative( pchDescription ); + using var str__pchIcon = new Utf8StringToNative( pchIcon ); + var returnValue = _AddRangeTimelineEvent( Self, str__pchTitle.Pointer, str__pchDescription.Pointer, str__pchIcon.Pointer, unIconPriority, flStartOffsetSeconds, flDuration, ePossibleClip ); + return returnValue; + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_StartRangeTimelineEvent", CallingConvention = Platform.CC)] + private static extern TimelineEventHandle _StartRangeTimelineEvent( IntPtr self, IntPtr pchTitle, IntPtr pchDescription, IntPtr pchIcon, uint unPriority, float flStartOffsetSeconds, TimelineEventClipPriority ePossibleClip ); + + #endregion + internal TimelineEventHandle StartRangeTimelineEvent( string pchTitle, string pchDescription, string pchIcon, uint unPriority, float flStartOffsetSeconds, TimelineEventClipPriority ePossibleClip ) + { + using var str__pchTitle = new Utf8StringToNative( pchTitle ); + using var str__pchDescription = new Utf8StringToNative( pchDescription ); + using var str__pchIcon = new Utf8StringToNative( pchIcon ); + var returnValue = _StartRangeTimelineEvent( Self, str__pchTitle.Pointer, str__pchDescription.Pointer, str__pchIcon.Pointer, unPriority, flStartOffsetSeconds, ePossibleClip ); + return returnValue; + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_UpdateRangeTimelineEvent", CallingConvention = Platform.CC)] + private static extern void _UpdateRangeTimelineEvent( IntPtr self, TimelineEventHandle ulEvent, IntPtr pchTitle, IntPtr pchDescription, IntPtr pchIcon, uint unPriority, TimelineEventClipPriority ePossibleClip ); + + #endregion + internal void UpdateRangeTimelineEvent( TimelineEventHandle ulEvent, string pchTitle, string pchDescription, string pchIcon, uint unPriority, TimelineEventClipPriority ePossibleClip ) + { + using var str__pchTitle = new Utf8StringToNative( pchTitle ); + using var str__pchDescription = new Utf8StringToNative( pchDescription ); + using var str__pchIcon = new Utf8StringToNative( pchIcon ); + _UpdateRangeTimelineEvent( Self, ulEvent, str__pchTitle.Pointer, str__pchDescription.Pointer, str__pchIcon.Pointer, unPriority, ePossibleClip ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_EndRangeTimelineEvent", CallingConvention = Platform.CC)] + private static extern void _EndRangeTimelineEvent( IntPtr self, TimelineEventHandle ulEvent, float flEndOffsetSeconds ); + + #endregion + internal void EndRangeTimelineEvent( TimelineEventHandle ulEvent, float flEndOffsetSeconds ) + { + _EndRangeTimelineEvent( Self, ulEvent, flEndOffsetSeconds ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_RemoveTimelineEvent", CallingConvention = Platform.CC)] + private static extern void _RemoveTimelineEvent( IntPtr self, TimelineEventHandle ulEvent ); + + #endregion + internal void RemoveTimelineEvent( TimelineEventHandle ulEvent ) + { + _RemoveTimelineEvent( Self, ulEvent ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_DoesEventRecordingExist", CallingConvention = Platform.CC)] + private static extern SteamAPICall_t _DoesEventRecordingExist( IntPtr self, TimelineEventHandle ulEvent ); + + #endregion + internal CallResult DoesEventRecordingExist( TimelineEventHandle ulEvent ) + { + var returnValue = _DoesEventRecordingExist( Self, ulEvent ); + return new CallResult( returnValue, IsServer ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_StartGamePhase", CallingConvention = Platform.CC)] + private static extern void _StartGamePhase( IntPtr self ); + + #endregion + internal void StartGamePhase() + { + _StartGamePhase( Self ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_EndGamePhase", CallingConvention = Platform.CC)] + private static extern void _EndGamePhase( IntPtr self ); + + #endregion + internal void EndGamePhase() + { + _EndGamePhase( Self ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_SetGamePhaseID", CallingConvention = Platform.CC)] + private static extern void _SetGamePhaseID( IntPtr self, IntPtr pchPhaseID ); + + #endregion + internal void SetGamePhaseID( string pchPhaseID ) + { + using var str__pchPhaseID = new Utf8StringToNative( pchPhaseID ); + _SetGamePhaseID( Self, str__pchPhaseID.Pointer ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_DoesGamePhaseRecordingExist", CallingConvention = Platform.CC)] + private static extern SteamAPICall_t _DoesGamePhaseRecordingExist( IntPtr self, IntPtr pchPhaseID ); + + #endregion + internal CallResult DoesGamePhaseRecordingExist( string pchPhaseID ) + { + using var str__pchPhaseID = new Utf8StringToNative( pchPhaseID ); + var returnValue = _DoesGamePhaseRecordingExist( Self, str__pchPhaseID.Pointer ); + return new CallResult( returnValue, IsServer ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_AddGamePhaseTag", CallingConvention = Platform.CC)] + private static extern void _AddGamePhaseTag( IntPtr self, IntPtr pchTagName, IntPtr pchTagIcon, IntPtr pchTagGroup, uint unPriority ); + + #endregion + internal void AddGamePhaseTag( string pchTagName, string pchTagIcon, string pchTagGroup, uint unPriority ) + { + using var str__pchTagName = new Utf8StringToNative( pchTagName ); + using var str__pchTagIcon = new Utf8StringToNative( pchTagIcon ); + using var str__pchTagGroup = new Utf8StringToNative( pchTagGroup ); + _AddGamePhaseTag( Self, str__pchTagName.Pointer, str__pchTagIcon.Pointer, str__pchTagGroup.Pointer, unPriority ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_SetGamePhaseAttribute", CallingConvention = Platform.CC)] + private static extern void _SetGamePhaseAttribute( IntPtr self, IntPtr pchAttributeGroup, IntPtr pchAttributeValue, uint unPriority ); + + #endregion + internal void SetGamePhaseAttribute( string pchAttributeGroup, string pchAttributeValue, uint unPriority ) + { + using var str__pchAttributeGroup = new Utf8StringToNative( pchAttributeGroup ); + using var str__pchAttributeValue = new Utf8StringToNative( pchAttributeValue ); + _SetGamePhaseAttribute( Self, str__pchAttributeGroup.Pointer, str__pchAttributeValue.Pointer, unPriority ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_OpenOverlayToGamePhase", CallingConvention = Platform.CC)] + private static extern void _OpenOverlayToGamePhase( IntPtr self, IntPtr pchPhaseID ); + + #endregion + internal void OpenOverlayToGamePhase( string pchPhaseID ) + { + using var str__pchPhaseID = new Utf8StringToNative( pchPhaseID ); + _OpenOverlayToGamePhase( Self, str__pchPhaseID.Pointer ); + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamTimeline_OpenOverlayToTimelineEvent", CallingConvention = Platform.CC)] + private static extern void _OpenOverlayToTimelineEvent( IntPtr self, TimelineEventHandle ulEvent ); + + #endregion + internal void OpenOverlayToTimelineEvent( TimelineEventHandle ulEvent ) + { + _OpenOverlayToTimelineEvent( Self, ulEvent ); + } + + } +} diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs index 758020c64..f7f69a40b 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs @@ -7,20 +7,21 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamUGC : SteamInterface + internal unsafe partial class ISteamUGC : SteamInterface { + public const string Version = "STEAMUGC_INTERFACE_VERSION020"; internal ISteamUGC( bool IsGameServer ) { SetupInterface( IsGameServer ); } - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamUGC_v017", CallingConvention = Platform.CC)] - internal static extern IntPtr SteamAPI_SteamUGC_v017(); - public override IntPtr GetUserInterfacePointer() => SteamAPI_SteamUGC_v017(); - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamGameServerUGC_v017", CallingConvention = Platform.CC)] - internal static extern IntPtr SteamAPI_SteamGameServerUGC_v017(); - public override IntPtr GetServerInterfacePointer() => SteamAPI_SteamGameServerUGC_v017(); + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamUGC_v020", CallingConvention = Platform.CC)] + internal static extern IntPtr SteamAPI_SteamUGC_v020(); + public override IntPtr GetUserInterfacePointer() => SteamAPI_SteamUGC_v020(); + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamGameServerUGC_v020", CallingConvention = Platform.CC)] + internal static extern IntPtr SteamAPI_SteamGameServerUGC_v020(); + public override IntPtr GetServerInterfacePointer() => SteamAPI_SteamGameServerUGC_v020(); #region FunctionMeta @@ -47,12 +48,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_CreateQueryAllUGCRequestCursor", CallingConvention = Platform.CC)] - private static extern UGCQueryHandle_t _CreateQueryAllUGCRequest( IntPtr self, UGCQuery eQueryType, UgcType eMatchingeMatchingUGCTypeFileType, AppId nCreatorAppID, AppId nConsumerAppID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchCursor ); + private static extern UGCQueryHandle_t _CreateQueryAllUGCRequest( IntPtr self, UGCQuery eQueryType, UgcType eMatchingeMatchingUGCTypeFileType, AppId nCreatorAppID, AppId nConsumerAppID, IntPtr pchCursor ); #endregion - internal UGCQueryHandle_t CreateQueryAllUGCRequest( UGCQuery eQueryType, UgcType eMatchingeMatchingUGCTypeFileType, AppId nCreatorAppID, AppId nConsumerAppID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchCursor ) + internal UGCQueryHandle_t CreateQueryAllUGCRequest( UGCQuery eQueryType, UgcType eMatchingeMatchingUGCTypeFileType, AppId nCreatorAppID, AppId nConsumerAppID, string pchCursor ) { - var returnValue = _CreateQueryAllUGCRequest( Self, eQueryType, eMatchingeMatchingUGCTypeFileType, nCreatorAppID, nConsumerAppID, pchCursor ); + using var str__pchCursor = new Utf8StringToNative( pchCursor ); + var returnValue = _CreateQueryAllUGCRequest( Self, eQueryType, eMatchingeMatchingUGCTypeFileType, nCreatorAppID, nConsumerAppID, str__pchCursor.Pointer ); return returnValue; } @@ -109,9 +111,9 @@ namespace Steamworks #endregion internal bool GetQueryUGCTag( UGCQueryHandle_t handle, uint index, uint indexTag, out string pchValue ) { - using var mempchValue = Helpers.TakeMemory(); - var returnValue = _GetQueryUGCTag( Self, handle, index, indexTag, mempchValue, (1024 * 32) ); - pchValue = Helpers.MemoryToString( mempchValue ); + using var mem__pchValue = Helpers.TakeMemory(); + var returnValue = _GetQueryUGCTag( Self, handle, index, indexTag, mem__pchValue, (1024 * 32) ); + pchValue = Helpers.MemoryToString( mem__pchValue ); return returnValue; } @@ -123,9 +125,9 @@ namespace Steamworks #endregion internal bool GetQueryUGCTagDisplayName( UGCQueryHandle_t handle, uint index, uint indexTag, out string pchValue ) { - using var mempchValue = Helpers.TakeMemory(); - var returnValue = _GetQueryUGCTagDisplayName( Self, handle, index, indexTag, mempchValue, (1024 * 32) ); - pchValue = Helpers.MemoryToString( mempchValue ); + using var mem__pchValue = Helpers.TakeMemory(); + var returnValue = _GetQueryUGCTagDisplayName( Self, handle, index, indexTag, mem__pchValue, (1024 * 32) ); + pchValue = Helpers.MemoryToString( mem__pchValue ); return returnValue; } @@ -137,9 +139,9 @@ namespace Steamworks #endregion internal bool GetQueryUGCPreviewURL( UGCQueryHandle_t handle, uint index, out string pchURL ) { - using var mempchURL = Helpers.TakeMemory(); - var returnValue = _GetQueryUGCPreviewURL( Self, handle, index, mempchURL, (1024 * 32) ); - pchURL = Helpers.MemoryToString( mempchURL ); + using var mem__pchURL = Helpers.TakeMemory(); + var returnValue = _GetQueryUGCPreviewURL( Self, handle, index, mem__pchURL, (1024 * 32) ); + pchURL = Helpers.MemoryToString( mem__pchURL ); return returnValue; } @@ -151,9 +153,9 @@ namespace Steamworks #endregion internal bool GetQueryUGCMetadata( UGCQueryHandle_t handle, uint index, out string pchMetadata ) { - using var mempchMetadata = Helpers.TakeMemory(); - var returnValue = _GetQueryUGCMetadata( Self, handle, index, mempchMetadata, (1024 * 32) ); - pchMetadata = Helpers.MemoryToString( mempchMetadata ); + using var mem__pchMetadata = Helpers.TakeMemory(); + var returnValue = _GetQueryUGCMetadata( Self, handle, index, mem__pchMetadata, (1024 * 32) ); + pchMetadata = Helpers.MemoryToString( mem__pchMetadata ); return returnValue; } @@ -200,11 +202,11 @@ namespace Steamworks #endregion internal bool GetQueryUGCAdditionalPreview( UGCQueryHandle_t handle, uint index, uint previewIndex, out string pchURLOrVideoID, out string pchOriginalFileName, ref ItemPreviewType pPreviewType ) { - using var mempchURLOrVideoID = Helpers.TakeMemory(); - using var mempchOriginalFileName = Helpers.TakeMemory(); - var returnValue = _GetQueryUGCAdditionalPreview( Self, handle, index, previewIndex, mempchURLOrVideoID, (1024 * 32), mempchOriginalFileName, (1024 * 32), ref pPreviewType ); - pchURLOrVideoID = Helpers.MemoryToString( mempchURLOrVideoID ); - pchOriginalFileName = Helpers.MemoryToString( mempchOriginalFileName ); + using var mem__pchURLOrVideoID = Helpers.TakeMemory(); + using var mem__pchOriginalFileName = Helpers.TakeMemory(); + var returnValue = _GetQueryUGCAdditionalPreview( Self, handle, index, previewIndex, mem__pchURLOrVideoID, (1024 * 32), mem__pchOriginalFileName, (1024 * 32), ref pPreviewType ); + pchURLOrVideoID = Helpers.MemoryToString( mem__pchURLOrVideoID ); + pchOriginalFileName = Helpers.MemoryToString( mem__pchOriginalFileName ); return returnValue; } @@ -227,25 +229,53 @@ namespace Steamworks #endregion internal bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint index, uint keyValueTagIndex, out string pchKey, out string pchValue ) { - using var mempchKey = Helpers.TakeMemory(); - using var mempchValue = Helpers.TakeMemory(); - var returnValue = _GetQueryUGCKeyValueTag( Self, handle, index, keyValueTagIndex, mempchKey, (1024 * 32), mempchValue, (1024 * 32) ); - pchKey = Helpers.MemoryToString( mempchKey ); - pchValue = Helpers.MemoryToString( mempchValue ); + using var mem__pchKey = Helpers.TakeMemory(); + using var mem__pchValue = Helpers.TakeMemory(); + var returnValue = _GetQueryUGCKeyValueTag( Self, handle, index, keyValueTagIndex, mem__pchKey, (1024 * 32), mem__pchValue, (1024 * 32) ); + pchKey = Helpers.MemoryToString( mem__pchKey ); + pchValue = Helpers.MemoryToString( mem__pchValue ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_GetQueryFirstUGCKeyValueTag", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetQueryUGCKeyValueTag( IntPtr self, UGCQueryHandle_t handle, uint index, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, IntPtr pchValue, uint cchValueSize ); + private static extern bool _GetQueryUGCKeyValueTag( IntPtr self, UGCQueryHandle_t handle, uint index, IntPtr pchKey, IntPtr pchValue, uint cchValueSize ); #endregion - internal bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint index, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, out string pchValue ) + internal bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint index, string pchKey, out string pchValue ) { - using var mempchValue = Helpers.TakeMemory(); - var returnValue = _GetQueryUGCKeyValueTag( Self, handle, index, pchKey, mempchValue, (1024 * 32) ); - pchValue = Helpers.MemoryToString( mempchValue ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + using var mem__pchValue = Helpers.TakeMemory(); + var returnValue = _GetQueryUGCKeyValueTag( Self, handle, index, str__pchKey.Pointer, mem__pchValue, (1024 * 32) ); + pchValue = Helpers.MemoryToString( mem__pchValue ); + return returnValue; + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_GetNumSupportedGameVersions", CallingConvention = Platform.CC)] + private static extern uint _GetNumSupportedGameVersions( IntPtr self, UGCQueryHandle_t handle, uint index ); + + #endregion + internal uint GetNumSupportedGameVersions( UGCQueryHandle_t handle, uint index ) + { + var returnValue = _GetNumSupportedGameVersions( Self, handle, index ); + return returnValue; + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_GetSupportedGameVersionData", CallingConvention = Platform.CC)] + [return: MarshalAs( UnmanagedType.I1 )] + private static extern bool _GetSupportedGameVersionData( IntPtr self, UGCQueryHandle_t handle, uint index, uint versionIndex, IntPtr pchGameBranchMin, IntPtr pchGameBranchMax, uint cchGameBranchSize ); + + #endregion + internal bool GetSupportedGameVersionData( UGCQueryHandle_t handle, uint index, uint versionIndex, out string pchGameBranchMin, out string pchGameBranchMax ) + { + using var mem__pchGameBranchMin = Helpers.TakeMemory(); + using var mem__pchGameBranchMax = Helpers.TakeMemory(); + var returnValue = _GetSupportedGameVersionData( Self, handle, index, versionIndex, mem__pchGameBranchMin, mem__pchGameBranchMax, (1024 * 32) ); + pchGameBranchMin = Helpers.MemoryToString( mem__pchGameBranchMin ); + pchGameBranchMax = Helpers.MemoryToString( mem__pchGameBranchMax ); return returnValue; } @@ -275,12 +305,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_AddRequiredTag", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _AddRequiredTag( IntPtr self, UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pTagName ); + private static extern bool _AddRequiredTag( IntPtr self, UGCQueryHandle_t handle, IntPtr pTagName ); #endregion - internal bool AddRequiredTag( UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pTagName ) + internal bool AddRequiredTag( UGCQueryHandle_t handle, string pTagName ) { - var returnValue = _AddRequiredTag( Self, handle, pTagName ); + using var str__pTagName = new Utf8StringToNative( pTagName ); + var returnValue = _AddRequiredTag( Self, handle, str__pTagName.Pointer ); return returnValue; } @@ -299,12 +330,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_AddExcludedTag", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _AddExcludedTag( IntPtr self, UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pTagName ); + private static extern bool _AddExcludedTag( IntPtr self, UGCQueryHandle_t handle, IntPtr pTagName ); #endregion - internal bool AddExcludedTag( UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pTagName ) + internal bool AddExcludedTag( UGCQueryHandle_t handle, string pTagName ) { - var returnValue = _AddExcludedTag( Self, handle, pTagName ); + using var str__pTagName = new Utf8StringToNative( pTagName ); + var returnValue = _AddExcludedTag( Self, handle, str__pTagName.Pointer ); return returnValue; } @@ -407,12 +439,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetLanguage", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetLanguage( IntPtr self, UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLanguage ); + private static extern bool _SetLanguage( IntPtr self, UGCQueryHandle_t handle, IntPtr pchLanguage ); #endregion - internal bool SetLanguage( UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLanguage ) + internal bool SetLanguage( UGCQueryHandle_t handle, string pchLanguage ) { - var returnValue = _SetLanguage( Self, handle, pchLanguage ); + using var str__pchLanguage = new Utf8StringToNative( pchLanguage ); + var returnValue = _SetLanguage( Self, handle, str__pchLanguage.Pointer ); return returnValue; } @@ -429,14 +462,27 @@ namespace Steamworks } #region FunctionMeta - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetCloudFileNameFilter", CallingConvention = Platform.CC)] + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetAdminQuery", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetCloudFileNameFilter( IntPtr self, UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pMatchCloudFileName ); + private static extern bool _SetAdminQuery( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.U1 )] bool bAdminQuery ); #endregion - internal bool SetCloudFileNameFilter( UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pMatchCloudFileName ) + internal bool SetAdminQuery( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.U1 )] bool bAdminQuery ) { - var returnValue = _SetCloudFileNameFilter( Self, handle, pMatchCloudFileName ); + var returnValue = _SetAdminQuery( Self, handle, bAdminQuery ); + return returnValue; + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetCloudFileNameFilter", CallingConvention = Platform.CC)] + [return: MarshalAs( UnmanagedType.I1 )] + private static extern bool _SetCloudFileNameFilter( IntPtr self, UGCQueryHandle_t handle, IntPtr pMatchCloudFileName ); + + #endregion + internal bool SetCloudFileNameFilter( UGCQueryHandle_t handle, string pMatchCloudFileName ) + { + using var str__pMatchCloudFileName = new Utf8StringToNative( pMatchCloudFileName ); + var returnValue = _SetCloudFileNameFilter( Self, handle, str__pMatchCloudFileName.Pointer ); return returnValue; } @@ -455,12 +501,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetSearchText", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetSearchText( IntPtr self, UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pSearchText ); + private static extern bool _SetSearchText( IntPtr self, UGCQueryHandle_t handle, IntPtr pSearchText ); #endregion - internal bool SetSearchText( UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pSearchText ) + internal bool SetSearchText( UGCQueryHandle_t handle, string pSearchText ) { - var returnValue = _SetSearchText( Self, handle, pSearchText ); + using var str__pSearchText = new Utf8StringToNative( pSearchText ); + var returnValue = _SetSearchText( Self, handle, str__pSearchText.Pointer ); return returnValue; } @@ -503,12 +550,14 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_AddRequiredKeyValueTag", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _AddRequiredKeyValueTag( IntPtr self, UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pValue ); + private static extern bool _AddRequiredKeyValueTag( IntPtr self, UGCQueryHandle_t handle, IntPtr pKey, IntPtr pValue ); #endregion - internal bool AddRequiredKeyValueTag( UGCQueryHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pValue ) + internal bool AddRequiredKeyValueTag( UGCQueryHandle_t handle, string pKey, string pValue ) { - var returnValue = _AddRequiredKeyValueTag( Self, handle, pKey, pValue ); + using var str__pKey = new Utf8StringToNative( pKey ); + using var str__pValue = new Utf8StringToNative( pValue ); + var returnValue = _AddRequiredKeyValueTag( Self, handle, str__pKey.Pointer, str__pValue.Pointer ); return returnValue; } @@ -537,48 +586,52 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetItemTitle", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetItemTitle( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchTitle ); + private static extern bool _SetItemTitle( IntPtr self, UGCUpdateHandle_t handle, IntPtr pchTitle ); #endregion - internal bool SetItemTitle( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchTitle ) + internal bool SetItemTitle( UGCUpdateHandle_t handle, string pchTitle ) { - var returnValue = _SetItemTitle( Self, handle, pchTitle ); + using var str__pchTitle = new Utf8StringToNative( pchTitle ); + var returnValue = _SetItemTitle( Self, handle, str__pchTitle.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetItemDescription", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetItemDescription( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchDescription ); + private static extern bool _SetItemDescription( IntPtr self, UGCUpdateHandle_t handle, IntPtr pchDescription ); #endregion - internal bool SetItemDescription( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchDescription ) + internal bool SetItemDescription( UGCUpdateHandle_t handle, string pchDescription ) { - var returnValue = _SetItemDescription( Self, handle, pchDescription ); + using var str__pchDescription = new Utf8StringToNative( pchDescription ); + var returnValue = _SetItemDescription( Self, handle, str__pchDescription.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetItemUpdateLanguage", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetItemUpdateLanguage( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLanguage ); + private static extern bool _SetItemUpdateLanguage( IntPtr self, UGCUpdateHandle_t handle, IntPtr pchLanguage ); #endregion - internal bool SetItemUpdateLanguage( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLanguage ) + internal bool SetItemUpdateLanguage( UGCUpdateHandle_t handle, string pchLanguage ) { - var returnValue = _SetItemUpdateLanguage( Self, handle, pchLanguage ); + using var str__pchLanguage = new Utf8StringToNative( pchLanguage ); + var returnValue = _SetItemUpdateLanguage( Self, handle, str__pchLanguage.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetItemMetadata", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetItemMetadata( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchMetaData ); + private static extern bool _SetItemMetadata( IntPtr self, UGCUpdateHandle_t handle, IntPtr pchMetaData ); #endregion - internal bool SetItemMetadata( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchMetaData ) + internal bool SetItemMetadata( UGCUpdateHandle_t handle, string pchMetaData ) { - var returnValue = _SetItemMetadata( Self, handle, pchMetaData ); + using var str__pchMetaData = new Utf8StringToNative( pchMetaData ); + var returnValue = _SetItemMetadata( Self, handle, str__pchMetaData.Pointer ); return returnValue; } @@ -597,36 +650,38 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetItemTags", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetItemTags( IntPtr self, UGCUpdateHandle_t updateHandle, ref SteamParamStringArray_t pTags ); + private static extern bool _SetItemTags( IntPtr self, UGCUpdateHandle_t updateHandle, ref SteamParamStringArray_t pTags, [MarshalAs( UnmanagedType.U1 )] bool bAllowAdminTags ); #endregion - internal bool SetItemTags( UGCUpdateHandle_t updateHandle, ref SteamParamStringArray_t pTags ) + internal bool SetItemTags( UGCUpdateHandle_t updateHandle, ref SteamParamStringArray_t pTags, [MarshalAs( UnmanagedType.U1 )] bool bAllowAdminTags ) { - var returnValue = _SetItemTags( Self, updateHandle, ref pTags ); + var returnValue = _SetItemTags( Self, updateHandle, ref pTags, bAllowAdminTags ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetItemContent", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetItemContent( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszContentFolder ); + private static extern bool _SetItemContent( IntPtr self, UGCUpdateHandle_t handle, IntPtr pszContentFolder ); #endregion - internal bool SetItemContent( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszContentFolder ) + internal bool SetItemContent( UGCUpdateHandle_t handle, string pszContentFolder ) { - var returnValue = _SetItemContent( Self, handle, pszContentFolder ); + using var str__pszContentFolder = new Utf8StringToNative( pszContentFolder ); + var returnValue = _SetItemContent( Self, handle, str__pszContentFolder.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetItemPreview", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetItemPreview( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszPreviewFile ); + private static extern bool _SetItemPreview( IntPtr self, UGCUpdateHandle_t handle, IntPtr pszPreviewFile ); #endregion - internal bool SetItemPreview( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszPreviewFile ) + internal bool SetItemPreview( UGCUpdateHandle_t handle, string pszPreviewFile ) { - var returnValue = _SetItemPreview( Self, handle, pszPreviewFile ); + using var str__pszPreviewFile = new Utf8StringToNative( pszPreviewFile ); + var returnValue = _SetItemPreview( Self, handle, str__pszPreviewFile.Pointer ); return returnValue; } @@ -657,72 +712,79 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_RemoveItemKeyValueTags", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _RemoveItemKeyValueTags( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ); + private static extern bool _RemoveItemKeyValueTags( IntPtr self, UGCUpdateHandle_t handle, IntPtr pchKey ); #endregion - internal bool RemoveItemKeyValueTags( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ) + internal bool RemoveItemKeyValueTags( UGCUpdateHandle_t handle, string pchKey ) { - var returnValue = _RemoveItemKeyValueTags( Self, handle, pchKey ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + var returnValue = _RemoveItemKeyValueTags( Self, handle, str__pchKey.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_AddItemKeyValueTag", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _AddItemKeyValueTag( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ); + private static extern bool _AddItemKeyValueTag( IntPtr self, UGCUpdateHandle_t handle, IntPtr pchKey, IntPtr pchValue ); #endregion - internal bool AddItemKeyValueTag( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchValue ) + internal bool AddItemKeyValueTag( UGCUpdateHandle_t handle, string pchKey, string pchValue ) { - var returnValue = _AddItemKeyValueTag( Self, handle, pchKey, pchValue ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + using var str__pchValue = new Utf8StringToNative( pchValue ); + var returnValue = _AddItemKeyValueTag( Self, handle, str__pchKey.Pointer, str__pchValue.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_AddItemPreviewFile", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _AddItemPreviewFile( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszPreviewFile, ItemPreviewType type ); + private static extern bool _AddItemPreviewFile( IntPtr self, UGCUpdateHandle_t handle, IntPtr pszPreviewFile, ItemPreviewType type ); #endregion - internal bool AddItemPreviewFile( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszPreviewFile, ItemPreviewType type ) + internal bool AddItemPreviewFile( UGCUpdateHandle_t handle, string pszPreviewFile, ItemPreviewType type ) { - var returnValue = _AddItemPreviewFile( Self, handle, pszPreviewFile, type ); + using var str__pszPreviewFile = new Utf8StringToNative( pszPreviewFile ); + var returnValue = _AddItemPreviewFile( Self, handle, str__pszPreviewFile.Pointer, type ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_AddItemPreviewVideo", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _AddItemPreviewVideo( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszVideoID ); + private static extern bool _AddItemPreviewVideo( IntPtr self, UGCUpdateHandle_t handle, IntPtr pszVideoID ); #endregion - internal bool AddItemPreviewVideo( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszVideoID ) + internal bool AddItemPreviewVideo( UGCUpdateHandle_t handle, string pszVideoID ) { - var returnValue = _AddItemPreviewVideo( Self, handle, pszVideoID ); + using var str__pszVideoID = new Utf8StringToNative( pszVideoID ); + var returnValue = _AddItemPreviewVideo( Self, handle, str__pszVideoID.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_UpdateItemPreviewFile", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _UpdateItemPreviewFile( IntPtr self, UGCUpdateHandle_t handle, uint index, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszPreviewFile ); + private static extern bool _UpdateItemPreviewFile( IntPtr self, UGCUpdateHandle_t handle, uint index, IntPtr pszPreviewFile ); #endregion - internal bool UpdateItemPreviewFile( UGCUpdateHandle_t handle, uint index, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszPreviewFile ) + internal bool UpdateItemPreviewFile( UGCUpdateHandle_t handle, uint index, string pszPreviewFile ) { - var returnValue = _UpdateItemPreviewFile( Self, handle, index, pszPreviewFile ); + using var str__pszPreviewFile = new Utf8StringToNative( pszPreviewFile ); + var returnValue = _UpdateItemPreviewFile( Self, handle, index, str__pszPreviewFile.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_UpdateItemPreviewVideo", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _UpdateItemPreviewVideo( IntPtr self, UGCUpdateHandle_t handle, uint index, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszVideoID ); + private static extern bool _UpdateItemPreviewVideo( IntPtr self, UGCUpdateHandle_t handle, uint index, IntPtr pszVideoID ); #endregion - internal bool UpdateItemPreviewVideo( UGCUpdateHandle_t handle, uint index, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszVideoID ) + internal bool UpdateItemPreviewVideo( UGCUpdateHandle_t handle, uint index, string pszVideoID ) { - var returnValue = _UpdateItemPreviewVideo( Self, handle, index, pszVideoID ); + using var str__pszVideoID = new Utf8StringToNative( pszVideoID ); + var returnValue = _UpdateItemPreviewVideo( Self, handle, index, str__pszVideoID.Pointer ); return returnValue; } @@ -763,13 +825,28 @@ namespace Steamworks } #region FunctionMeta - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SubmitItemUpdate", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _SubmitItemUpdate( IntPtr self, UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchChangeNote ); + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SetRequiredGameVersions", CallingConvention = Platform.CC)] + [return: MarshalAs( UnmanagedType.I1 )] + private static extern bool _SetRequiredGameVersions( IntPtr self, UGCUpdateHandle_t handle, IntPtr pszGameBranchMin, IntPtr pszGameBranchMax ); #endregion - internal CallResult SubmitItemUpdate( UGCUpdateHandle_t handle, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchChangeNote ) + internal bool SetRequiredGameVersions( UGCUpdateHandle_t handle, string pszGameBranchMin, string pszGameBranchMax ) { - var returnValue = _SubmitItemUpdate( Self, handle, pchChangeNote ); + using var str__pszGameBranchMin = new Utf8StringToNative( pszGameBranchMin ); + using var str__pszGameBranchMax = new Utf8StringToNative( pszGameBranchMax ); + var returnValue = _SetRequiredGameVersions( Self, handle, str__pszGameBranchMin.Pointer, str__pszGameBranchMax.Pointer ); + return returnValue; + } + + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_SubmitItemUpdate", CallingConvention = Platform.CC)] + private static extern SteamAPICall_t _SubmitItemUpdate( IntPtr self, UGCUpdateHandle_t handle, IntPtr pchChangeNote ); + + #endregion + internal CallResult SubmitItemUpdate( UGCUpdateHandle_t handle, string pchChangeNote ) + { + using var str__pchChangeNote = new Utf8StringToNative( pchChangeNote ); + var returnValue = _SubmitItemUpdate( Self, handle, str__pchChangeNote.Pointer ); return new CallResult( returnValue, IsServer ); } @@ -891,9 +968,9 @@ namespace Steamworks #endregion internal bool GetItemInstallInfo( PublishedFileId nPublishedFileID, ref ulong punSizeOnDisk, out string pchFolder, ref uint punTimeStamp ) { - using var mempchFolder = Helpers.TakeMemory(); - var returnValue = _GetItemInstallInfo( Self, nPublishedFileID, ref punSizeOnDisk, mempchFolder, (1024 * 32), ref punTimeStamp ); - pchFolder = Helpers.MemoryToString( mempchFolder ); + using var mem__pchFolder = Helpers.TakeMemory(); + var returnValue = _GetItemInstallInfo( Self, nPublishedFileID, ref punSizeOnDisk, mem__pchFolder, (1024 * 32), ref punTimeStamp ); + pchFolder = Helpers.MemoryToString( mem__pchFolder ); return returnValue; } @@ -924,12 +1001,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_BInitWorkshopForGameServer", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _BInitWorkshopForGameServer( IntPtr self, DepotId_t unWorkshopDepotID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszFolder ); + private static extern bool _BInitWorkshopForGameServer( IntPtr self, DepotId_t unWorkshopDepotID, IntPtr pszFolder ); #endregion - internal bool BInitWorkshopForGameServer( DepotId_t unWorkshopDepotID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszFolder ) + internal bool BInitWorkshopForGameServer( DepotId_t unWorkshopDepotID, string pszFolder ) { - var returnValue = _BInitWorkshopForGameServer( Self, unWorkshopDepotID, pszFolder ); + using var str__pszFolder = new Utf8StringToNative( pszFolder ); + var returnValue = _BInitWorkshopForGameServer( Self, unWorkshopDepotID, str__pszFolder.Pointer ); return returnValue; } @@ -1065,5 +1143,16 @@ namespace Steamworks return new CallResult( returnValue, IsServer ); } + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUGC_GetUserContentDescriptorPreferences", CallingConvention = Platform.CC)] + private static extern uint _GetUserContentDescriptorPreferences( IntPtr self, [In,Out] UGCContentDescriptorID[] pvecDescriptors, uint cMaxEntries ); + + #endregion + internal uint GetUserContentDescriptorPreferences( [In,Out] UGCContentDescriptorID[] pvecDescriptors, uint cMaxEntries ) + { + var returnValue = _GetUserContentDescriptorPreferences( Self, pvecDescriptors, cMaxEntries ); + return returnValue; + } + } } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs index 18147edf6..5325c6114 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamUser : SteamInterface + internal unsafe partial class ISteamUser : SteamInterface { + public const string Version = "SteamUser023"; internal ISteamUser( bool IsGameServer ) { @@ -77,12 +78,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUser_TrackAppUsageEvent", CallingConvention = Platform.CC)] - private static extern void _TrackAppUsageEvent( IntPtr self, GameId gameID, int eAppUsageEvent, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchExtraInfo ); + private static extern void _TrackAppUsageEvent( IntPtr self, GameId gameID, int eAppUsageEvent, IntPtr pchExtraInfo ); #endregion - internal void TrackAppUsageEvent( GameId gameID, int eAppUsageEvent, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchExtraInfo ) + internal void TrackAppUsageEvent( GameId gameID, int eAppUsageEvent, string pchExtraInfo ) { - _TrackAppUsageEvent( Self, gameID, eAppUsageEvent, pchExtraInfo ); + using var str__pchExtraInfo = new Utf8StringToNative( pchExtraInfo ); + _TrackAppUsageEvent( Self, gameID, eAppUsageEvent, str__pchExtraInfo.Pointer ); } #region FunctionMeta @@ -93,9 +95,9 @@ namespace Steamworks #endregion internal bool GetUserDataFolder( out string pchBuffer ) { - using var mempchBuffer = Helpers.TakeMemory(); - var returnValue = _GetUserDataFolder( Self, mempchBuffer, (1024 * 32) ); - pchBuffer = Helpers.MemoryToString( mempchBuffer ); + using var mem__pchBuffer = Helpers.TakeMemory(); + var returnValue = _GetUserDataFolder( Self, mem__pchBuffer, (1024 * 32) ); + pchBuffer = Helpers.MemoryToString( mem__pchBuffer ); return returnValue; } @@ -176,12 +178,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUser_GetAuthTicketForWebApi", CallingConvention = Platform.CC)] - private static extern HAuthTicket _GetAuthTicketForWebApi( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchIdentity ); + private static extern HAuthTicket _GetAuthTicketForWebApi( IntPtr self, IntPtr pchIdentity ); #endregion - internal HAuthTicket GetAuthTicketForWebApi( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchIdentity ) + internal HAuthTicket GetAuthTicketForWebApi( string pchIdentity ) { - var returnValue = _GetAuthTicketForWebApi( Self, pchIdentity ); + using var str__pchIdentity = new Utf8StringToNative( pchIdentity ); + var returnValue = _GetAuthTicketForWebApi( Self, str__pchIdentity.Pointer ); return returnValue; } @@ -296,12 +299,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUser_RequestStoreAuthURL", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _RequestStoreAuthURL( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchRedirectURL ); + private static extern SteamAPICall_t _RequestStoreAuthURL( IntPtr self, IntPtr pchRedirectURL ); #endregion - internal CallResult RequestStoreAuthURL( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchRedirectURL ) + internal CallResult RequestStoreAuthURL( string pchRedirectURL ) { - var returnValue = _RequestStoreAuthURL( Self, pchRedirectURL ); + using var str__pchRedirectURL = new Utf8StringToNative( pchRedirectURL ); + var returnValue = _RequestStoreAuthURL( Self, str__pchRedirectURL.Pointer ); return new CallResult( returnValue, IsServer ); } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUserStats.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUserStats.cs index ab98ca638..9dc4bfd7f 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUserStats.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUserStats.cs @@ -7,136 +7,134 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamUserStats : SteamInterface + internal unsafe partial class ISteamUserStats : SteamInterface { + public const string Version = "STEAMUSERSTATS_INTERFACE_VERSION013"; internal ISteamUserStats( bool IsGameServer ) { SetupInterface( IsGameServer ); } - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamUserStats_v012", CallingConvention = Platform.CC)] - internal static extern IntPtr SteamAPI_SteamUserStats_v012(); - public override IntPtr GetUserInterfacePointer() => SteamAPI_SteamUserStats_v012(); + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamUserStats_v013", CallingConvention = Platform.CC)] + internal static extern IntPtr SteamAPI_SteamUserStats_v013(); + public override IntPtr GetUserInterfacePointer() => SteamAPI_SteamUserStats_v013(); - #region FunctionMeta - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_RequestCurrentStats", CallingConvention = Platform.CC)] - [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _RequestCurrentStats( IntPtr self ); - - #endregion - internal bool RequestCurrentStats() - { - var returnValue = _RequestCurrentStats( Self ); - return returnValue; - } - #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetStatInt32", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetStat( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref int pData ); + private static extern bool _GetStat( IntPtr self, IntPtr pchName, ref int pData ); #endregion - internal bool GetStat( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref int pData ) + internal bool GetStat( string pchName, ref int pData ) { - var returnValue = _GetStat( Self, pchName, ref pData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetStat( Self, str__pchName.Pointer, ref pData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetStatFloat", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetStat( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pData ); + private static extern bool _GetStat( IntPtr self, IntPtr pchName, ref float pData ); #endregion - internal bool GetStat( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pData ) + internal bool GetStat( string pchName, ref float pData ) { - var returnValue = _GetStat( Self, pchName, ref pData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetStat( Self, str__pchName.Pointer, ref pData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_SetStatInt32", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetStat( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, int nData ); + private static extern bool _SetStat( IntPtr self, IntPtr pchName, int nData ); #endregion - internal bool SetStat( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, int nData ) + internal bool SetStat( string pchName, int nData ) { - var returnValue = _SetStat( Self, pchName, nData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _SetStat( Self, str__pchName.Pointer, nData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_SetStatFloat", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetStat( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, float fData ); + private static extern bool _SetStat( IntPtr self, IntPtr pchName, float fData ); #endregion - internal bool SetStat( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, float fData ) + internal bool SetStat( string pchName, float fData ) { - var returnValue = _SetStat( Self, pchName, fData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _SetStat( Self, str__pchName.Pointer, fData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_UpdateAvgRateStat", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _UpdateAvgRateStat( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, float flCountThisSession, double dSessionLength ); + private static extern bool _UpdateAvgRateStat( IntPtr self, IntPtr pchName, float flCountThisSession, double dSessionLength ); #endregion - internal bool UpdateAvgRateStat( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, float flCountThisSession, double dSessionLength ) + internal bool UpdateAvgRateStat( string pchName, float flCountThisSession, double dSessionLength ) { - var returnValue = _UpdateAvgRateStat( Self, pchName, flCountThisSession, dSessionLength ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _UpdateAvgRateStat( Self, str__pchName.Pointer, flCountThisSession, dSessionLength ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetAchievement", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetAchievement( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ); + private static extern bool _GetAchievement( IntPtr self, IntPtr pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ); #endregion - internal bool GetAchievement( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ) + internal bool GetAchievement( string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ) { - var returnValue = _GetAchievement( Self, pchName, ref pbAchieved ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetAchievement( Self, str__pchName.Pointer, ref pbAchieved ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_SetAchievement", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _SetAchievement( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ); + private static extern bool _SetAchievement( IntPtr self, IntPtr pchName ); #endregion - internal bool SetAchievement( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ) + internal bool SetAchievement( string pchName ) { - var returnValue = _SetAchievement( Self, pchName ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _SetAchievement( Self, str__pchName.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_ClearAchievement", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _ClearAchievement( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ); + private static extern bool _ClearAchievement( IntPtr self, IntPtr pchName ); #endregion - internal bool ClearAchievement( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ) + internal bool ClearAchievement( string pchName ) { - var returnValue = _ClearAchievement( Self, pchName ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _ClearAchievement( Self, str__pchName.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetAchievementAndUnlockTime", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetAchievementAndUnlockTime( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved, ref uint punUnlockTime ); + private static extern bool _GetAchievementAndUnlockTime( IntPtr self, IntPtr pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved, ref uint punUnlockTime ); #endregion - internal bool GetAchievementAndUnlockTime( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved, ref uint punUnlockTime ) + internal bool GetAchievementAndUnlockTime( string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved, ref uint punUnlockTime ) { - var returnValue = _GetAchievementAndUnlockTime( Self, pchName, ref pbAchieved, ref punUnlockTime ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetAchievementAndUnlockTime( Self, str__pchName.Pointer, ref pbAchieved, ref punUnlockTime ); return returnValue; } @@ -154,35 +152,39 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetAchievementIcon", CallingConvention = Platform.CC)] - private static extern int _GetAchievementIcon( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ); + private static extern int _GetAchievementIcon( IntPtr self, IntPtr pchName ); #endregion - internal int GetAchievementIcon( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName ) + internal int GetAchievementIcon( string pchName ) { - var returnValue = _GetAchievementIcon( Self, pchName ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetAchievementIcon( Self, str__pchName.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute", CallingConvention = Platform.CC)] - private static extern Utf8StringPointer _GetAchievementDisplayAttribute( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ); + private static extern Utf8StringPointer _GetAchievementDisplayAttribute( IntPtr self, IntPtr pchName, IntPtr pchKey ); #endregion - internal string GetAchievementDisplayAttribute( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchKey ) + internal string GetAchievementDisplayAttribute( string pchName, string pchKey ) { - var returnValue = _GetAchievementDisplayAttribute( Self, pchName, pchKey ); + using var str__pchName = new Utf8StringToNative( pchName ); + using var str__pchKey = new Utf8StringToNative( pchKey ); + var returnValue = _GetAchievementDisplayAttribute( Self, str__pchName.Pointer, str__pchKey.Pointer ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_IndicateAchievementProgress", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _IndicateAchievementProgress( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, uint nCurProgress, uint nMaxProgress ); + private static extern bool _IndicateAchievementProgress( IntPtr self, IntPtr pchName, uint nCurProgress, uint nMaxProgress ); #endregion - internal bool IndicateAchievementProgress( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, uint nCurProgress, uint nMaxProgress ) + internal bool IndicateAchievementProgress( string pchName, uint nCurProgress, uint nMaxProgress ) { - var returnValue = _IndicateAchievementProgress( Self, pchName, nCurProgress, nMaxProgress ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _IndicateAchievementProgress( Self, str__pchName.Pointer, nCurProgress, nMaxProgress ); return returnValue; } @@ -222,48 +224,52 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetUserStatInt32", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetUserStat( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref int pData ); + private static extern bool _GetUserStat( IntPtr self, SteamId steamIDUser, IntPtr pchName, ref int pData ); #endregion - internal bool GetUserStat( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref int pData ) + internal bool GetUserStat( SteamId steamIDUser, string pchName, ref int pData ) { - var returnValue = _GetUserStat( Self, steamIDUser, pchName, ref pData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetUserStat( Self, steamIDUser, str__pchName.Pointer, ref pData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetUserStatFloat", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetUserStat( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pData ); + private static extern bool _GetUserStat( IntPtr self, SteamId steamIDUser, IntPtr pchName, ref float pData ); #endregion - internal bool GetUserStat( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pData ) + internal bool GetUserStat( SteamId steamIDUser, string pchName, ref float pData ) { - var returnValue = _GetUserStat( Self, steamIDUser, pchName, ref pData ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetUserStat( Self, steamIDUser, str__pchName.Pointer, ref pData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetUserAchievement", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetUserAchievement( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ); + private static extern bool _GetUserAchievement( IntPtr self, SteamId steamIDUser, IntPtr pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ); #endregion - internal bool GetUserAchievement( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ) + internal bool GetUserAchievement( SteamId steamIDUser, string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ) { - var returnValue = _GetUserAchievement( Self, steamIDUser, pchName, ref pbAchieved ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetUserAchievement( Self, steamIDUser, str__pchName.Pointer, ref pbAchieved ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetUserAchievementAndUnlockTime", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetUserAchievementAndUnlockTime( IntPtr self, SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved, ref uint punUnlockTime ); + private static extern bool _GetUserAchievementAndUnlockTime( IntPtr self, SteamId steamIDUser, IntPtr pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved, ref uint punUnlockTime ); #endregion - internal bool GetUserAchievementAndUnlockTime( SteamId steamIDUser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved, ref uint punUnlockTime ) + internal bool GetUserAchievementAndUnlockTime( SteamId steamIDUser, string pchName, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved, ref uint punUnlockTime ) { - var returnValue = _GetUserAchievementAndUnlockTime( Self, steamIDUser, pchName, ref pbAchieved, ref punUnlockTime ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetUserAchievementAndUnlockTime( Self, steamIDUser, str__pchName.Pointer, ref pbAchieved, ref punUnlockTime ); return returnValue; } @@ -281,23 +287,25 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_FindOrCreateLeaderboard", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _FindOrCreateLeaderboard( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLeaderboardName, LeaderboardSort eLeaderboardSortMethod, LeaderboardDisplay eLeaderboardDisplayType ); + private static extern SteamAPICall_t _FindOrCreateLeaderboard( IntPtr self, IntPtr pchLeaderboardName, LeaderboardSort eLeaderboardSortMethod, LeaderboardDisplay eLeaderboardDisplayType ); #endregion - internal CallResult FindOrCreateLeaderboard( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLeaderboardName, LeaderboardSort eLeaderboardSortMethod, LeaderboardDisplay eLeaderboardDisplayType ) + internal CallResult FindOrCreateLeaderboard( string pchLeaderboardName, LeaderboardSort eLeaderboardSortMethod, LeaderboardDisplay eLeaderboardDisplayType ) { - var returnValue = _FindOrCreateLeaderboard( Self, pchLeaderboardName, eLeaderboardSortMethod, eLeaderboardDisplayType ); + using var str__pchLeaderboardName = new Utf8StringToNative( pchLeaderboardName ); + var returnValue = _FindOrCreateLeaderboard( Self, str__pchLeaderboardName.Pointer, eLeaderboardSortMethod, eLeaderboardDisplayType ); return new CallResult( returnValue, IsServer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_FindLeaderboard", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _FindLeaderboard( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLeaderboardName ); + private static extern SteamAPICall_t _FindLeaderboard( IntPtr self, IntPtr pchLeaderboardName ); #endregion - internal CallResult FindLeaderboard( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchLeaderboardName ) + internal CallResult FindLeaderboard( string pchLeaderboardName ) { - var returnValue = _FindLeaderboard( Self, pchLeaderboardName ); + using var str__pchLeaderboardName = new Utf8StringToNative( pchLeaderboardName ); + var returnValue = _FindLeaderboard( Self, str__pchLeaderboardName.Pointer ); return new CallResult( returnValue, IsServer ); } @@ -430,9 +438,9 @@ namespace Steamworks #endregion internal int GetMostAchievedAchievementInfo( out string pchName, ref float pflPercent, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ) { - using var mempchName = Helpers.TakeMemory(); - var returnValue = _GetMostAchievedAchievementInfo( Self, mempchName, (1024 * 32), ref pflPercent, ref pbAchieved ); - pchName = Helpers.MemoryToString( mempchName ); + using var mem__pchName = Helpers.TakeMemory(); + var returnValue = _GetMostAchievedAchievementInfo( Self, mem__pchName, (1024 * 32), ref pflPercent, ref pbAchieved ); + pchName = Helpers.MemoryToString( mem__pchName ); return returnValue; } @@ -443,21 +451,22 @@ namespace Steamworks #endregion internal int GetNextMostAchievedAchievementInfo( int iIteratorPrevious, out string pchName, ref float pflPercent, [MarshalAs( UnmanagedType.U1 )] ref bool pbAchieved ) { - using var mempchName = Helpers.TakeMemory(); - var returnValue = _GetNextMostAchievedAchievementInfo( Self, iIteratorPrevious, mempchName, (1024 * 32), ref pflPercent, ref pbAchieved ); - pchName = Helpers.MemoryToString( mempchName ); + using var mem__pchName = Helpers.TakeMemory(); + var returnValue = _GetNextMostAchievedAchievementInfo( Self, iIteratorPrevious, mem__pchName, (1024 * 32), ref pflPercent, ref pbAchieved ); + pchName = Helpers.MemoryToString( mem__pchName ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetAchievementAchievedPercent", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetAchievementAchievedPercent( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pflPercent ); + private static extern bool _GetAchievementAchievedPercent( IntPtr self, IntPtr pchName, ref float pflPercent ); #endregion - internal bool GetAchievementAchievedPercent( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pflPercent ) + internal bool GetAchievementAchievedPercent( string pchName, ref float pflPercent ) { - var returnValue = _GetAchievementAchievedPercent( Self, pchName, ref pflPercent ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetAchievementAchievedPercent( Self, str__pchName.Pointer, ref pflPercent ); return returnValue; } @@ -475,70 +484,76 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetGlobalStatInt64", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetGlobalStat( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchStatName, ref long pData ); + private static extern bool _GetGlobalStat( IntPtr self, IntPtr pchStatName, ref long pData ); #endregion - internal bool GetGlobalStat( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchStatName, ref long pData ) + internal bool GetGlobalStat( string pchStatName, ref long pData ) { - var returnValue = _GetGlobalStat( Self, pchStatName, ref pData ); + using var str__pchStatName = new Utf8StringToNative( pchStatName ); + var returnValue = _GetGlobalStat( Self, str__pchStatName.Pointer, ref pData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetGlobalStatDouble", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetGlobalStat( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchStatName, ref double pData ); + private static extern bool _GetGlobalStat( IntPtr self, IntPtr pchStatName, ref double pData ); #endregion - internal bool GetGlobalStat( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchStatName, ref double pData ) + internal bool GetGlobalStat( string pchStatName, ref double pData ) { - var returnValue = _GetGlobalStat( Self, pchStatName, ref pData ); + using var str__pchStatName = new Utf8StringToNative( pchStatName ); + var returnValue = _GetGlobalStat( Self, str__pchStatName.Pointer, ref pData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetGlobalStatHistoryInt64", CallingConvention = Platform.CC)] - private static extern int _GetGlobalStatHistory( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchStatName, [In,Out] long[] pData, uint cubData ); + private static extern int _GetGlobalStatHistory( IntPtr self, IntPtr pchStatName, [In,Out] long[] pData, uint cubData ); #endregion - internal int GetGlobalStatHistory( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchStatName, [In,Out] long[] pData, uint cubData ) + internal int GetGlobalStatHistory( string pchStatName, [In,Out] long[] pData, uint cubData ) { - var returnValue = _GetGlobalStatHistory( Self, pchStatName, pData, cubData ); + using var str__pchStatName = new Utf8StringToNative( pchStatName ); + var returnValue = _GetGlobalStatHistory( Self, str__pchStatName.Pointer, pData, cubData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetGlobalStatHistoryDouble", CallingConvention = Platform.CC)] - private static extern int _GetGlobalStatHistory( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchStatName, [In,Out] double[] pData, uint cubData ); + private static extern int _GetGlobalStatHistory( IntPtr self, IntPtr pchStatName, [In,Out] double[] pData, uint cubData ); #endregion - internal int GetGlobalStatHistory( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchStatName, [In,Out] double[] pData, uint cubData ) + internal int GetGlobalStatHistory( string pchStatName, [In,Out] double[] pData, uint cubData ) { - var returnValue = _GetGlobalStatHistory( Self, pchStatName, pData, cubData ); + using var str__pchStatName = new Utf8StringToNative( pchStatName ); + var returnValue = _GetGlobalStatHistory( Self, str__pchStatName.Pointer, pData, cubData ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetAchievementProgressLimitsInt32", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetAchievementProgressLimits( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref int pnMinProgress, ref int pnMaxProgress ); + private static extern bool _GetAchievementProgressLimits( IntPtr self, IntPtr pchName, ref int pnMinProgress, ref int pnMaxProgress ); #endregion - internal bool GetAchievementProgressLimits( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref int pnMinProgress, ref int pnMaxProgress ) + internal bool GetAchievementProgressLimits( string pchName, ref int pnMinProgress, ref int pnMaxProgress ) { - var returnValue = _GetAchievementProgressLimits( Self, pchName, ref pnMinProgress, ref pnMaxProgress ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetAchievementProgressLimits( Self, str__pchName.Pointer, ref pnMinProgress, ref pnMaxProgress ); return returnValue; } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUserStats_GetAchievementProgressLimitsFloat", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _GetAchievementProgressLimits( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pfMinProgress, ref float pfMaxProgress ); + private static extern bool _GetAchievementProgressLimits( IntPtr self, IntPtr pchName, ref float pfMinProgress, ref float pfMaxProgress ); #endregion - internal bool GetAchievementProgressLimits( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchName, ref float pfMinProgress, ref float pfMaxProgress ) + internal bool GetAchievementProgressLimits( string pchName, ref float pfMinProgress, ref float pfMaxProgress ) { - var returnValue = _GetAchievementProgressLimits( Self, pchName, ref pfMinProgress, ref pfMaxProgress ); + using var str__pchName = new Utf8StringToNative( pchName ); + var returnValue = _GetAchievementProgressLimits( Self, str__pchName.Pointer, ref pfMinProgress, ref pfMaxProgress ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs index f0a4cf544..472b7f32d 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs @@ -7,8 +7,9 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamUtils : SteamInterface + internal unsafe partial class ISteamUtils : SteamInterface { + public const string Version = "SteamUtils010"; internal ISteamUtils( bool IsGameServer ) { @@ -216,24 +217,27 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUtils_CheckFileSignature", CallingConvention = Platform.CC)] - private static extern SteamAPICall_t _CheckFileSignature( IntPtr self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string szFileName ); + private static extern SteamAPICall_t _CheckFileSignature( IntPtr self, IntPtr szFileName ); #endregion - internal CallResult CheckFileSignature( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string szFileName ) + internal CallResult CheckFileSignature( string szFileName ) { - var returnValue = _CheckFileSignature( Self, szFileName ); + using var str__szFileName = new Utf8StringToNative( szFileName ); + var returnValue = _CheckFileSignature( Self, str__szFileName.Pointer ); return new CallResult( returnValue, IsServer ); } #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUtils_ShowGamepadTextInput", CallingConvention = Platform.CC)] [return: MarshalAs( UnmanagedType.I1 )] - private static extern bool _ShowGamepadTextInput( IntPtr self, GamepadTextInputMode eInputMode, GamepadTextInputLineMode eLineInputMode, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchDescription, uint unCharMax, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchExistingText ); + private static extern bool _ShowGamepadTextInput( IntPtr self, GamepadTextInputMode eInputMode, GamepadTextInputLineMode eLineInputMode, IntPtr pchDescription, uint unCharMax, IntPtr pchExistingText ); #endregion - internal bool ShowGamepadTextInput( GamepadTextInputMode eInputMode, GamepadTextInputLineMode eLineInputMode, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchDescription, uint unCharMax, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchExistingText ) + internal bool ShowGamepadTextInput( GamepadTextInputMode eInputMode, GamepadTextInputLineMode eLineInputMode, string pchDescription, uint unCharMax, string pchExistingText ) { - var returnValue = _ShowGamepadTextInput( Self, eInputMode, eLineInputMode, pchDescription, unCharMax, pchExistingText ); + using var str__pchDescription = new Utf8StringToNative( pchDescription ); + using var str__pchExistingText = new Utf8StringToNative( pchExistingText ); + var returnValue = _ShowGamepadTextInput( Self, eInputMode, eLineInputMode, str__pchDescription.Pointer, unCharMax, str__pchExistingText.Pointer ); return returnValue; } @@ -256,9 +260,9 @@ namespace Steamworks #endregion internal bool GetEnteredGamepadTextInput( out string pchText ) { - using var mempchText = Helpers.TakeMemory(); - var returnValue = _GetEnteredGamepadTextInput( Self, mempchText, (1024 * 32) ); - pchText = Helpers.MemoryToString( mempchText ); + using var mem__pchText = Helpers.TakeMemory(); + var returnValue = _GetEnteredGamepadTextInput( Self, mem__pchText, (1024 * 32) ); + pchText = Helpers.MemoryToString( mem__pchText ); return returnValue; } @@ -365,14 +369,15 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUtils_FilterText", CallingConvention = Platform.CC)] - private static extern int _FilterText( IntPtr self, TextFilteringContext eContext, SteamId sourceSteamID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchInputMessage, IntPtr pchOutFilteredText, uint nByteSizeOutFilteredText ); + private static extern int _FilterText( IntPtr self, TextFilteringContext eContext, SteamId sourceSteamID, IntPtr pchInputMessage, IntPtr pchOutFilteredText, uint nByteSizeOutFilteredText ); #endregion - internal int FilterText( TextFilteringContext eContext, SteamId sourceSteamID, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchInputMessage, out string pchOutFilteredText ) + internal int FilterText( TextFilteringContext eContext, SteamId sourceSteamID, string pchInputMessage, out string pchOutFilteredText ) { - using var mempchOutFilteredText = Helpers.TakeMemory(); - var returnValue = _FilterText( Self, eContext, sourceSteamID, pchInputMessage, mempchOutFilteredText, (1024 * 32) ); - pchOutFilteredText = Helpers.MemoryToString( mempchOutFilteredText ); + using var str__pchInputMessage = new Utf8StringToNative( pchInputMessage ); + using var mem__pchOutFilteredText = Helpers.TakeMemory(); + var returnValue = _FilterText( Self, eContext, sourceSteamID, str__pchInputMessage.Pointer, mem__pchOutFilteredText, (1024 * 32) ); + pchOutFilteredText = Helpers.MemoryToString( mem__pchOutFilteredText ); return returnValue; } @@ -433,5 +438,17 @@ namespace Steamworks return returnValue; } + #region FunctionMeta + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamUtils_DismissGamepadTextInput", CallingConvention = Platform.CC)] + [return: MarshalAs( UnmanagedType.I1 )] + private static extern bool _DismissGamepadTextInput( IntPtr self ); + + #endregion + internal bool DismissGamepadTextInput() + { + var returnValue = _DismissGamepadTextInput( Self ); + return returnValue; + } + } } diff --git a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs index bd4d029a8..b40b85930 100644 --- a/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs +++ b/Libraries/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs @@ -7,17 +7,18 @@ using Steamworks.Data; namespace Steamworks { - internal unsafe class ISteamVideo : SteamInterface + internal unsafe partial class ISteamVideo : SteamInterface { + public const string Version = "STEAMVIDEO_INTERFACE_V007"; internal ISteamVideo( bool IsGameServer ) { SetupInterface( IsGameServer ); } - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamVideo_v002", CallingConvention = Platform.CC)] - internal static extern IntPtr SteamAPI_SteamVideo_v002(); - public override IntPtr GetUserInterfacePointer() => SteamAPI_SteamVideo_v002(); + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamVideo_v007", CallingConvention = Platform.CC)] + internal static extern IntPtr SteamAPI_SteamVideo_v007(); + public override IntPtr GetUserInterfacePointer() => SteamAPI_SteamVideo_v007(); #region FunctionMeta @@ -58,11 +59,12 @@ namespace Steamworks private static extern bool _GetOPFStringForApp( IntPtr self, AppId unVideoAppID, IntPtr pchBuffer, ref int pnBufferSize ); #endregion - internal bool GetOPFStringForApp( AppId unVideoAppID, out string pchBuffer, ref int pnBufferSize ) + internal bool GetOPFStringForApp( AppId unVideoAppID, out string pchBuffer ) { - using var mempchBuffer = Helpers.TakeMemory(); - var returnValue = _GetOPFStringForApp( Self, unVideoAppID, mempchBuffer, ref pnBufferSize ); - pchBuffer = Helpers.MemoryToString( mempchBuffer ); + using var mem__pchBuffer = Helpers.TakeMemory(); + int szpnBufferSize = (1024 * 32); + var returnValue = _GetOPFStringForApp( Self, unVideoAppID, mem__pchBuffer, ref szpnBufferSize ); + pchBuffer = Helpers.MemoryToString( mem__pchBuffer ); return returnValue; } diff --git a/Libraries/Facepunch.Steamworks/Generated/SteamCallbacks.cs b/Libraries/Facepunch.Steamworks/Generated/SteamCallbacks.cs index 883114778..e674544b8 100644 --- a/Libraries/Facepunch.Steamworks/Generated/SteamCallbacks.cs +++ b/Libraries/Facepunch.Steamworks/Generated/SteamCallbacks.cs @@ -144,7 +144,7 @@ namespace Steamworks.Data [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] internal struct GameWebCallback_t : ICallbackData { - internal string URLUTF8() => System.Text.Encoding.UTF8.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); + internal string URLUTF8() => Steamworks.Utility.Utf8NoBom.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // byte[] m_szURL internal byte[] URL; // m_szURL char [256] @@ -158,7 +158,7 @@ namespace Steamworks.Data [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] internal struct StoreAuthURLResponse_t : ICallbackData { - internal string URLUTF8() => System.Text.Encoding.UTF8.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); + internal string URLUTF8() => Steamworks.Utility.Utf8NoBom.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] // byte[] m_szURL internal byte[] URL; // m_szURL char [512] @@ -242,6 +242,7 @@ namespace Steamworks.Data [MarshalAs(UnmanagedType.I1)] internal bool UserInitiated; // m_bUserInitiated bool internal AppId AppID; // m_nAppID AppId_t + internal uint DwOverlayPID; // m_dwOverlayPID uint32 #region SteamCallback public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(GameOverlayActivated_t) ); @@ -253,10 +254,10 @@ namespace Steamworks.Data [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] internal struct GameServerChangeRequested_t : ICallbackData { - internal string ServerUTF8() => System.Text.Encoding.UTF8.GetString( Server, 0, System.Array.IndexOf( Server, 0 ) ); + internal string ServerUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Server, 0, System.Array.IndexOf( Server, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] // byte[] m_rgchServer internal byte[] Server; // m_rgchServer char [64] - internal string PasswordUTF8() => System.Text.Encoding.UTF8.GetString( Password, 0, System.Array.IndexOf( Password, 0 ) ); + internal string PasswordUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Password, 0, System.Array.IndexOf( Password, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] // byte[] m_rgchPassword internal byte[] Password; // m_rgchPassword char [64] @@ -326,7 +327,7 @@ namespace Steamworks.Data internal struct GameRichPresenceJoinRequested_t : ICallbackData { internal ulong SteamIDFriend; // m_steamIDFriend CSteamID - internal string ConnectUTF8() => System.Text.Encoding.UTF8.GetString( Connect, 0, System.Array.IndexOf( Connect, 0 ) ); + internal string ConnectUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Connect, 0, System.Array.IndexOf( Connect, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // byte[] m_rgchConnect internal byte[] Connect; // m_rgchConnect char [256] @@ -495,7 +496,7 @@ namespace Steamworks.Data [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] internal struct OverlayBrowserProtocolNavigation_t : ICallbackData { - internal string RgchURIUTF8() => System.Text.Encoding.UTF8.GetString( RgchURI, 0, System.Array.IndexOf( RgchURI, 0 ) ); + internal string RgchURIUTF8() => Steamworks.Utility.Utf8NoBom.GetString( RgchURI, 0, System.Array.IndexOf( RgchURI, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)] // byte[] rgchURI internal byte[] RgchURI; // rgchURI char [1024] @@ -533,6 +534,8 @@ namespace Steamworks.Data internal bool HasProfileBackground; // m_bHasProfileBackground bool [MarshalAs(UnmanagedType.I1)] internal bool HasMiniProfileBackground; // m_bHasMiniProfileBackground bool + [MarshalAs(UnmanagedType.I1)] + internal bool FromCache; // m_bFromCache bool #region SteamCallback public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(EquippedProfileItems_t) ); @@ -946,7 +949,7 @@ namespace Steamworks.Data internal Result Result; // m_eResult EResult internal ulong BeaconID; // m_ulBeaconID PartyBeaconID_t internal ulong SteamIDBeaconOwner; // m_SteamIDBeaconOwner CSteamID - internal string ConnectStringUTF8() => System.Text.Encoding.UTF8.GetString( ConnectString, 0, System.Array.IndexOf( ConnectString, 0 ) ); + internal string ConnectStringUTF8() => Steamworks.Utility.Utf8NoBom.GetString( ConnectString, 0, System.Array.IndexOf( ConnectString, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // byte[] m_rgchConnectString internal byte[] ConnectString; // m_rgchConnectString char [256] @@ -1022,7 +1025,7 @@ namespace Steamworks.Data { internal Result Result; // m_eResult EResult internal ulong File; // m_hFile UGCHandle_t - internal string FilenameUTF8() => System.Text.Encoding.UTF8.GetString( Filename, 0, System.Array.IndexOf( Filename, 0 ) ); + internal string FilenameUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Filename, 0, System.Array.IndexOf( Filename, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)] // byte[] m_rgchFilename internal byte[] Filename; // m_rgchFilename char [260] @@ -1143,7 +1146,7 @@ namespace Steamworks.Data internal ulong File; // m_hFile UGCHandle_t internal AppId AppID; // m_nAppID AppId_t internal int SizeInBytes; // m_nSizeInBytes int32 - internal string PchFileNameUTF8() => System.Text.Encoding.UTF8.GetString( PchFileName, 0, System.Array.IndexOf( PchFileName, 0 ) ); + internal string PchFileNameUTF8() => Steamworks.Utility.Utf8NoBom.GetString( PchFileName, 0, System.Array.IndexOf( PchFileName, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)] // byte[] m_pchFileName internal byte[] PchFileName; // m_pchFileName char [260] internal ulong SteamIDOwner; // m_ulSteamIDOwner uint64 @@ -1162,10 +1165,10 @@ namespace Steamworks.Data internal PublishedFileId PublishedFileId; // m_nPublishedFileId PublishedFileId_t internal AppId CreatorAppID; // m_nCreatorAppID AppId_t internal AppId ConsumerAppID; // m_nConsumerAppID AppId_t - internal string TitleUTF8() => System.Text.Encoding.UTF8.GetString( Title, 0, System.Array.IndexOf( Title, 0 ) ); + internal string TitleUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Title, 0, System.Array.IndexOf( Title, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 129)] // byte[] m_rgchTitle internal byte[] Title; // m_rgchTitle char [129] - internal string DescriptionUTF8() => System.Text.Encoding.UTF8.GetString( Description, 0, System.Array.IndexOf( Description, 0 ) ); + internal string DescriptionUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Description, 0, System.Array.IndexOf( Description, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8000)] // byte[] m_rgchDescription internal byte[] Description; // m_rgchDescription char [8000] internal ulong File; // m_hFile UGCHandle_t @@ -1176,17 +1179,17 @@ namespace Steamworks.Data internal RemoteStoragePublishedFileVisibility Visibility; // m_eVisibility ERemoteStoragePublishedFileVisibility [MarshalAs(UnmanagedType.I1)] internal bool Banned; // m_bBanned bool - internal string TagsUTF8() => System.Text.Encoding.UTF8.GetString( Tags, 0, System.Array.IndexOf( Tags, 0 ) ); + internal string TagsUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Tags, 0, System.Array.IndexOf( Tags, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1025)] // byte[] m_rgchTags internal byte[] Tags; // m_rgchTags char [1025] [MarshalAs(UnmanagedType.I1)] internal bool TagsTruncated; // m_bTagsTruncated bool - internal string PchFileNameUTF8() => System.Text.Encoding.UTF8.GetString( PchFileName, 0, System.Array.IndexOf( PchFileName, 0 ) ); + internal string PchFileNameUTF8() => Steamworks.Utility.Utf8NoBom.GetString( PchFileName, 0, System.Array.IndexOf( PchFileName, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)] // byte[] m_pchFileName internal byte[] PchFileName; // m_pchFileName char [260] internal int FileSize; // m_nFileSize int32 internal int PreviewFileSize; // m_nPreviewFileSize int32 - internal string URLUTF8() => System.Text.Encoding.UTF8.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); + internal string URLUTF8() => Steamworks.Utility.Utf8NoBom.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // byte[] m_rgchURL internal byte[] URL; // m_rgchURL char [256] internal WorkshopFileType FileType; // m_eFileType EWorkshopFileType @@ -1451,7 +1454,7 @@ namespace Steamworks.Data internal ulong GameID; // m_nGameID uint64 [MarshalAs(UnmanagedType.I1)] internal bool GroupAchievement; // m_bGroupAchievement bool - internal string AchievementNameUTF8() => System.Text.Encoding.UTF8.GetString( AchievementName, 0, System.Array.IndexOf( AchievementName, 0 ) ); + internal string AchievementNameUTF8() => Steamworks.Utility.Utf8NoBom.GetString( AchievementName, 0, System.Array.IndexOf( AchievementName, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] // byte[] m_rgchAchievementName internal byte[] AchievementName; // m_rgchAchievementName char [128] internal uint CurProgress; // m_nCurProgress uint32 @@ -1537,7 +1540,7 @@ namespace Steamworks.Data internal struct UserAchievementIconFetched_t : ICallbackData { internal GameId GameID; // m_nGameID CGameID - internal string AchievementNameUTF8() => System.Text.Encoding.UTF8.GetString( AchievementName, 0, System.Array.IndexOf( AchievementName, 0 ) ); + internal string AchievementNameUTF8() => Steamworks.Utility.Utf8NoBom.GetString( AchievementName, 0, System.Array.IndexOf( AchievementName, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] // byte[] m_rgchAchievementName internal byte[] AchievementName; // m_rgchAchievementName char [128] [MarshalAs(UnmanagedType.I1)] @@ -1619,7 +1622,7 @@ namespace Steamworks.Data internal Result Result; // m_eResult EResult internal uint AppID; // m_nAppID uint32 internal uint CchKeyLength; // m_cchKeyLength uint32 - internal string KeyUTF8() => System.Text.Encoding.UTF8.GetString( Key, 0, System.Array.IndexOf( Key, 0 ) ); + internal string KeyUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Key, 0, System.Array.IndexOf( Key, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 240)] // byte[] m_rgchKey internal byte[] Key; // m_rgchKey char [240] @@ -2010,7 +2013,7 @@ namespace Steamworks.Data internal uint TotalMatchingResults; // m_unTotalMatchingResults uint32 [MarshalAs(UnmanagedType.I1)] internal bool CachedData; // m_bCachedData bool - internal string NextCursorUTF8() => System.Text.Encoding.UTF8.GetString( NextCursor, 0, System.Array.IndexOf( NextCursor, 0 ) ); + internal string NextCursorUTF8() => Steamworks.Utility.Utf8NoBom.GetString( NextCursor, 0, System.Array.IndexOf( NextCursor, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // byte[] m_rgchNextCursor internal byte[] NextCursor; // m_rgchNextCursor char [256] @@ -2070,6 +2073,8 @@ namespace Steamworks.Data { internal AppId AppID; // m_unAppID AppId_t internal PublishedFileId PublishedFileId; // m_nPublishedFileId PublishedFileId_t + internal ulong LegacyContent; // m_hLegacyContent UGCHandle_t + internal ulong ManifestID; // m_unManifestID uint64 #region SteamCallback public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(ItemInstalled_t) ); @@ -2282,32 +2287,6 @@ namespace Steamworks.Data #endregion } - [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] - internal struct SteamAppInstalled_t : ICallbackData - { - internal AppId AppID; // m_nAppID AppId_t - internal int InstallFolderIndex; // m_iInstallFolderIndex int - - #region SteamCallback - public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(SteamAppInstalled_t) ); - public int DataSize => _datasize; - public CallbackType CallbackType => CallbackType.SteamAppInstalled; - #endregion - } - - [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] - internal struct SteamAppUninstalled_t : ICallbackData - { - internal AppId AppID; // m_nAppID AppId_t - internal int InstallFolderIndex; // m_iInstallFolderIndex int - - #region SteamCallback - public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(SteamAppUninstalled_t) ); - public int DataSize => _datasize; - public CallbackType CallbackType => CallbackType.SteamAppUninstalled; - #endregion - } - [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] internal struct HTML_BrowserReady_t : ICallbackData { @@ -2721,7 +2700,7 @@ namespace Steamworks.Data internal struct SteamInventoryRequestPricesResult_t : ICallbackData { internal Result Result; // m_result EResult - internal string CurrencyUTF8() => System.Text.Encoding.UTF8.GetString( Currency, 0, System.Array.IndexOf( Currency, 0 ) ); + internal string CurrencyUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Currency, 0, System.Array.IndexOf( Currency, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] // byte[] m_rgchCurrency internal byte[] Currency; // m_rgchCurrency char [4] @@ -2732,12 +2711,44 @@ namespace Steamworks.Data #endregion } + [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] + internal struct SteamTimelineGamePhaseRecordingExists_t : ICallbackData + { + internal string PhaseIDUTF8() => Steamworks.Utility.Utf8NoBom.GetString( PhaseID, 0, System.Array.IndexOf( PhaseID, 0 ) ); + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] // byte[] m_rgchPhaseID + internal byte[] PhaseID; // m_rgchPhaseID char [64] + internal ulong RecordingMS; // m_ulRecordingMS uint64 + internal ulong LongestClipMS; // m_ulLongestClipMS uint64 + internal uint ClipCount; // m_unClipCount uint32 + internal uint ScreenshotCount; // m_unScreenshotCount uint32 + + #region SteamCallback + public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(SteamTimelineGamePhaseRecordingExists_t) ); + public int DataSize => _datasize; + public CallbackType CallbackType => CallbackType.SteamTimelineGamePhaseRecordingExists; + #endregion + } + + [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] + internal struct SteamTimelineEventRecordingExists_t : ICallbackData + { + internal ulong EventID; // m_ulEventID uint64 + [MarshalAs(UnmanagedType.I1)] + internal bool RecordingExists; // m_bRecordingExists bool + + #region SteamCallback + public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(SteamTimelineEventRecordingExists_t) ); + public int DataSize => _datasize; + public CallbackType CallbackType => CallbackType.SteamTimelineEventRecordingExists; + #endregion + } + [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] internal struct GetVideoURLResult_t : ICallbackData { internal Result Result; // m_eResult EResult internal AppId VideoAppID; // m_unVideoAppID AppId_t - internal string URLUTF8() => System.Text.Encoding.UTF8.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); + internal string URLUTF8() => Steamworks.Utility.Utf8NoBom.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // byte[] m_rgchURL internal byte[] URL; // m_rgchURL char [256] @@ -2761,6 +2772,31 @@ namespace Steamworks.Data #endregion } + [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] + internal struct BroadcastUploadStart_t : ICallbackData + { + [MarshalAs(UnmanagedType.I1)] + internal bool IsRTMP; // m_bIsRTMP bool + + #region SteamCallback + public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(BroadcastUploadStart_t) ); + public int DataSize => _datasize; + public CallbackType CallbackType => CallbackType.BroadcastUploadStart; + #endregion + } + + [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] + internal struct BroadcastUploadStop_t : ICallbackData + { + internal BroadcastUploadResult Result; // m_eResult EBroadcastUploadResult + + #region SteamCallback + public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(BroadcastUploadStop_t) ); + public int DataSize => _datasize; + public CallbackType CallbackType => CallbackType.BroadcastUploadStop; + #endregion + } + [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] internal struct SteamParentalSettingsChanged_t : ICallbackData { @@ -2799,7 +2835,7 @@ namespace Steamworks.Data [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] internal struct SteamRemotePlayTogetherGuestInvite_t : ICallbackData { - internal string ConnectURLUTF8() => System.Text.Encoding.UTF8.GetString( ConnectURL, 0, System.Array.IndexOf( ConnectURL, 0 ) ); + internal string ConnectURLUTF8() => Steamworks.Utility.Utf8NoBom.GetString( ConnectURL, 0, System.Array.IndexOf( ConnectURL, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)] // byte[] m_szConnectURL internal byte[] ConnectURL; // m_szConnectURL char [1024] @@ -2852,7 +2888,7 @@ namespace Steamworks.Data internal struct SteamNetAuthenticationStatus_t : ICallbackData { internal SteamNetworkingAvailability Avail; // m_eAvail ESteamNetworkingAvailability - internal string DebugMsgUTF8() => System.Text.Encoding.UTF8.GetString( DebugMsg, 0, System.Array.IndexOf( DebugMsg, 0 ) ); + internal string DebugMsgUTF8() => Steamworks.Utility.Utf8NoBom.GetString( DebugMsg, 0, System.Array.IndexOf( DebugMsg, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // byte[] m_debugMsg internal byte[] DebugMsg; // m_debugMsg char [256] @@ -2870,7 +2906,7 @@ namespace Steamworks.Data internal int PingMeasurementInProgress; // m_bPingMeasurementInProgress int internal SteamNetworkingAvailability AvailNetworkConfig; // m_eAvailNetworkConfig ESteamNetworkingAvailability internal SteamNetworkingAvailability AvailAnyRelay; // m_eAvailAnyRelay ESteamNetworkingAvailability - internal string DebugMsgUTF8() => System.Text.Encoding.UTF8.GetString( DebugMsg, 0, System.Array.IndexOf( DebugMsg, 0 ) ); + internal string DebugMsgUTF8() => Steamworks.Utility.Utf8NoBom.GetString( DebugMsg, 0, System.Array.IndexOf( DebugMsg, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // byte[] m_debugMsg internal byte[] DebugMsg; // m_debugMsg char [256] @@ -2899,7 +2935,7 @@ namespace Steamworks.Data { internal ulong SteamID; // m_SteamID CSteamID internal DenyReason DenyReason; // m_eDenyReason EDenyReason - internal string OptionalTextUTF8() => System.Text.Encoding.UTF8.GetString( OptionalText, 0, System.Array.IndexOf( OptionalText, 0 ) ); + internal string OptionalTextUTF8() => Steamworks.Utility.Utf8NoBom.GetString( OptionalText, 0, System.Array.IndexOf( OptionalText, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] // byte[] m_rgchOptionalText internal byte[] OptionalText; // m_rgchOptionalText char [128] @@ -2927,7 +2963,7 @@ namespace Steamworks.Data internal struct GSClientAchievementStatus_t : ICallbackData { internal ulong SteamID; // m_SteamID uint64 - internal string PchAchievementUTF8() => System.Text.Encoding.UTF8.GetString( PchAchievement, 0, System.Array.IndexOf( PchAchievement, 0 ) ); + internal string PchAchievementUTF8() => Steamworks.Utility.Utf8NoBom.GetString( PchAchievement, 0, System.Array.IndexOf( PchAchievement, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] // byte[] m_pchAchievement internal byte[] PchAchievement; // m_pchAchievement char [128] [MarshalAs(UnmanagedType.I1)] diff --git a/Libraries/Facepunch.Steamworks/Generated/SteamConstants.cs b/Libraries/Facepunch.Steamworks/Generated/SteamConstants.cs index fcdd12f1c..d3e073bae 100644 --- a/Libraries/Facepunch.Steamworks/Generated/SteamConstants.cs +++ b/Libraries/Facepunch.Steamworks/Generated/SteamConstants.cs @@ -11,16 +11,20 @@ namespace Steamworks.Data internal static readonly AppId k_uAppIdInvalid = 0x0; internal static readonly DepotId_t k_uDepotIdInvalid = 0x0; internal static readonly SteamAPICall_t k_uAPICallInvalid = 0x0; + internal static readonly AccountID_t k_uAccountIdInvalid = 0; internal static readonly PartyBeaconID_t k_ulPartyBeaconIdInvalid = 0; internal static readonly HAuthTicket k_HAuthTicketInvalid = 0; internal static readonly uint k_unSteamAccountIDMask = 0xFFFFFFFF; internal static readonly uint k_unSteamAccountInstanceMask = 0x000FFFFF; internal static readonly uint k_unSteamUserDefaultInstance = 1; internal static readonly int k_cchGameExtraInfoMax = 64; + internal static readonly int k_cchMaxSteamErrMsg = 1024; internal static readonly int k_cchMaxFriendsGroupName = 64; internal static readonly int k_cFriendsGroupLimit = 100; internal static readonly FriendsGroupID_t k_FriendsGroupID_Invalid = - 1; internal static readonly int k_cEnumerateFollowersMax = 50; + internal static readonly ushort k_usFriendGameInfoQueryPort_NotInitialized = 0xFFFF; + internal static readonly ushort k_usFriendGameInfoQueryPort_Error = 0xFFFE; internal static readonly uint k_cubChatMetadataMax = 8192; internal static readonly int k_cbMaxGameServerGameDir = 32; internal static readonly int k_cbMaxGameServerMapName = 32; @@ -58,6 +62,10 @@ namespace Steamworks.Data internal static readonly InventoryItemId k_SteamItemInstanceIDInvalid = ~default(ulong); internal static readonly SteamInventoryResult_t k_SteamInventoryResultInvalid = - 1; internal static readonly SteamInventoryUpdateHandle_t k_SteamInventoryUpdateHandleInvalid = 0xffffffffffffffff; + internal static readonly uint k_unMaxTimelinePriority = 1000; + internal static readonly uint k_unTimelinePriority_KeepCurrentValue = 1000000; + internal static readonly float k_flMaxTimelineEventDuration = 600.0f; + internal static readonly uint k_cchMaxPhaseIDLength = 64; internal static readonly Connection k_HSteamNetConnection_Invalid = 0; internal static readonly Socket k_HSteamListenSocket_Invalid = 0; internal static readonly HSteamNetPollGroup k_HSteamNetPollGroup_Invalid = 0; diff --git a/Libraries/Facepunch.Steamworks/Generated/SteamEnums.cs b/Libraries/Facepunch.Steamworks/Generated/SteamEnums.cs index 0dc3329ac..f1df9ef88 100644 --- a/Libraries/Facepunch.Steamworks/Generated/SteamEnums.cs +++ b/Libraries/Facepunch.Steamworks/Generated/SteamEnums.cs @@ -160,6 +160,9 @@ namespace Steamworks ChargerRequired = 125, CachedCredentialInvalid = 126, K_EResultPhoneNumberIsVOIP = 127, + NotSupported = 128, + FamilySizeLimitExceeded = 129, + OfflineAppCacheInvalid = 130, } // @@ -421,6 +424,19 @@ namespace Steamworks OnlineHighPri = 3, } + // + // EBetaBranchFlags + // + internal enum BetaBranchFlags : int + { + None = 0, + Default = 1, + Available = 2, + Private = 4, + Selected = 8, + Installed = 16, + } + // // EGameSearchErrorCode_t // @@ -800,7 +816,8 @@ namespace Steamworks SteamworksAccessInvite = 13, SteamVideo = 14, GameManagedItem = 15, - Max = 16, + Clip = 16, + Max = 17, } // @@ -1460,7 +1477,11 @@ namespace Steamworks SteamDeck_Reserved18 = 403, SteamDeck_Reserved19 = 404, SteamDeck_Reserved20 = 405, - Count = 406, + Horipad_M1 = 406, + Horipad_M2 = 407, + Horipad_L4 = 408, + Horipad_R4 = 409, + Count = 410, MaximumPossibleValue = 32767, } @@ -1997,7 +2018,11 @@ namespace Steamworks PS5_RightGrip = 383, PS5_LeftFn = 384, PS5_RightFn = 385, - Count = 386, + Horipad_M1 = 386, + Horipad_M2 = 387, + Horipad_L4 = 388, + Horipad_R4 = 389, + Count = 390, MaximumPossibleValue = 32767, } @@ -2113,6 +2138,7 @@ namespace Steamworks NeedsUpdate = 8, Downloading = 16, DownloadPending = 32, + DisabledLocally = 64, } // @@ -2145,6 +2171,7 @@ namespace Steamworks Sketchfab = 2, EnvironmentMap_HorizontalCross = 3, EnvironmentMap_LatLong = 4, + Clip = 5, ReservedMax = 255, } @@ -2170,6 +2197,30 @@ namespace Steamworks Consumed = 512, } + // + // ETimelineGameMode + // + public enum TimelineGameMode : int + { + Invalid = 0, + Playing = 1, + Staging = 2, + Menus = 3, + LoadingScreen = 4, + Max = 5, + } + + // + // ETimelineEventClipPriority + // + public enum TimelineEventClipPriority : int + { + Invalid = 0, + None = 1, + Standard = 2, + Featured = 3, + } + // // EParentalFeature // @@ -2189,8 +2240,9 @@ namespace Steamworks Library = 11, Test = 12, SiteLicense = 13, - KioskMode = 14, - Max = 15, + KioskMode_Deprecated = 14, + BlockAlways = 15, + Max = 16, } // @@ -2203,6 +2255,7 @@ namespace Steamworks Tablet = 2, Computer = 3, TV = 4, + VRHeadset = 5, } // @@ -2231,7 +2284,6 @@ namespace Steamworks SteamID = 16, XboxPairwiseID = 17, SonyPSN = 18, - GoogleStadia = 19, IPAddress = 1, GenericString = 2, GenericBytes = 3, @@ -2337,11 +2389,16 @@ namespace Steamworks TimeoutInitial = 24, TimeoutConnected = 25, SendBufferSize = 9, + RecvBufferSize = 47, + RecvBufferMessages = 48, + RecvMaxMessageSize = 49, + RecvMaxSegmentsPerPacket = 50, ConnectionUserData = 40, SendRateMin = 10, SendRateMax = 11, NagleTime = 12, IP_AllowWithoutAuth = 23, + IPLocalHost_AllowWithoutAuth = 52, MTU_PacketSize = 32, MTU_DataSize = 33, Unencrypted = 34, @@ -2349,10 +2406,17 @@ namespace Steamworks LocalVirtualPort = 38, DualWifi_Enable = 39, EnableDiagnosticsUI = 46, + SendTimeSincePreviousPacket = 59, FakePacketLoss_Send = 2, FakePacketLoss_Recv = 3, FakePacketLag_Send = 4, FakePacketLag_Recv = 5, + FakePacketJitter_Send_Avg = 53, + FakePacketJitter_Send_Max = 54, + FakePacketJitter_Send_Pct = 55, + FakePacketJitter_Recv_Avg = 56, + FakePacketJitter_Recv_Max = 57, + FakePacketJitter_Recv_Pct = 58, FakePacketReorder_Send = 6, FakePacketReorder_Recv = 7, FakePacketReorder_Time = 8, @@ -2364,6 +2428,7 @@ namespace Steamworks FakeRateLimit_Send_Burst = 43, FakeRateLimit_Recv_Rate = 44, FakeRateLimit_Recv_Burst = 45, + OutOfOrderCorrectionWindowMicroseconds = 51, Callback_ConnectionStatusChanged = 201, Callback_AuthStatusChanged = 202, Callback_RelayNetworkStatusChanged = 203, @@ -2384,15 +2449,17 @@ namespace Steamworks SDRClient_MinPingsBeforePingAccurate = 21, SDRClient_SingleSocket = 22, SDRClient_ForceRelayCluster = 29, - SDRClient_DebugTicketAddress = 30, + SDRClient_DevTicket = 30, SDRClient_ForceProxyAddr = 31, SDRClient_FakeClusterPing = 36, + SDRClient_LimitPingProbesToNearestN = 60, LogLevel_AckRTT = 13, LogLevel_PacketDecode = 14, LogLevel_Message = 15, LogLevel_PacketGaps = 16, LogLevel_P2PRendezvous = 17, LogLevel_SDRRelayPings = 18, + ECN = 999, DELETED_EnumerateDevVars = 35, } @@ -2424,6 +2491,17 @@ namespace Steamworks Everything = 8, } + // + // ESteamAPIInitResult + // + internal enum SteamAPIInitResult : int + { + OK = 0, + FailedGeneric = 1, + NoSteamClient = 2, + VersionMismatch = 3, + } + // // EServerMode // diff --git a/Libraries/Facepunch.Steamworks/Generated/SteamStructFunctions.cs b/Libraries/Facepunch.Steamworks/Generated/SteamStructFunctions.cs index e7f5294bc..c2af43c46 100644 --- a/Libraries/Facepunch.Steamworks/Generated/SteamStructFunctions.cs +++ b/Libraries/Facepunch.Steamworks/Generated/SteamStructFunctions.cs @@ -15,7 +15,7 @@ namespace Steamworks.Data internal static extern Utf8StringPointer InternalGetName( ref gameserveritem_t self ); [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_gameserveritem_t_SetName", CallingConvention = Platform.CC)] - internal static extern void InternalSetName( ref gameserveritem_t self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pName ); + internal static extern void InternalSetName( ref gameserveritem_t self, IntPtr pName ); } @@ -103,7 +103,7 @@ namespace Steamworks.Data internal static extern void InternalSetPtr( ref NetKeyValue self, NetConfig eVal, IntPtr data ); [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingConfigValue_t_SetString", CallingConvention = Platform.CC)] - internal static extern void InternalSetString( ref NetKeyValue self, NetConfig eVal, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string data ); + internal static extern void InternalSetString( ref NetKeyValue self, NetConfig eVal, IntPtr data ); } @@ -130,7 +130,7 @@ namespace Steamworks.Data [return: MarshalAs( UnmanagedType.I1 )] [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIdentity_SetXboxPairwiseID", CallingConvention = Platform.CC)] - internal static extern bool InternalSetXboxPairwiseID( ref NetIdentity self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszString ); + internal static extern bool InternalSetXboxPairwiseID( ref NetIdentity self, IntPtr pszString ); [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIdentity_GetXboxPairwiseID", CallingConvention = Platform.CC)] internal static extern Utf8StringPointer InternalGetXboxPairwiseID( ref NetIdentity self ); @@ -141,12 +141,6 @@ namespace Steamworks.Data [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIdentity_GetPSNID", CallingConvention = Platform.CC)] internal static extern ulong InternalGetPSNID( ref NetIdentity self ); - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIdentity_SetStadiaID", CallingConvention = Platform.CC)] - internal static extern void InternalSetStadiaID( ref NetIdentity self, ulong id ); - - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIdentity_GetStadiaID", CallingConvention = Platform.CC)] - internal static extern ulong InternalGetStadiaID( ref NetIdentity self ); - [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIdentity_SetIPAddr", CallingConvention = Platform.CC)] internal static extern void InternalSetIPAddr( ref NetIdentity self, ref NetAddress addr ); @@ -175,7 +169,7 @@ namespace Steamworks.Data [return: MarshalAs( UnmanagedType.I1 )] [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIdentity_SetGenericString", CallingConvention = Platform.CC)] - internal static extern bool InternalSetGenericString( ref NetIdentity self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszString ); + internal static extern bool InternalSetGenericString( ref NetIdentity self, IntPtr pszString ); [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIdentity_GetGenericString", CallingConvention = Platform.CC)] internal static extern Utf8StringPointer InternalGetGenericString( ref NetIdentity self ); @@ -196,7 +190,7 @@ namespace Steamworks.Data [return: MarshalAs( UnmanagedType.I1 )] [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIdentity_ParseString", CallingConvention = Platform.CC)] - internal static extern bool InternalParseString( ref NetIdentity self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszStr ); + internal static extern bool InternalParseString( ref NetIdentity self, IntPtr pszStr ); } @@ -234,7 +228,7 @@ namespace Steamworks.Data [return: MarshalAs( UnmanagedType.I1 )] [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIPAddr_ParseString", CallingConvention = Platform.CC)] - internal static extern bool InternalParseString( ref NetAddress self, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pszStr ); + internal static extern bool InternalParseString( ref NetAddress self, IntPtr pszStr ); [return: MarshalAs( UnmanagedType.I1 )] [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_SteamNetworkingIPAddr_IsEqualTo", CallingConvention = Platform.CC)] diff --git a/Libraries/Facepunch.Steamworks/Generated/SteamStructs.cs b/Libraries/Facepunch.Steamworks/Generated/SteamStructs.cs index 8724de68a..f4314023d 100644 --- a/Libraries/Facepunch.Steamworks/Generated/SteamStructs.cs +++ b/Libraries/Facepunch.Steamworks/Generated/SteamStructs.cs @@ -35,13 +35,13 @@ namespace Steamworks.Data internal bool HadSuccessfulResponse; // m_bHadSuccessfulResponse bool [MarshalAs(UnmanagedType.I1)] internal bool DoNotRefresh; // m_bDoNotRefresh bool - internal string GameDirUTF8() => System.Text.Encoding.UTF8.GetString( GameDir, 0, System.Array.IndexOf( GameDir, 0 ) ); + internal string GameDirUTF8() => Steamworks.Utility.Utf8NoBom.GetString( GameDir, 0, System.Array.IndexOf( GameDir, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] // byte[] m_szGameDir internal byte[] GameDir; // m_szGameDir char [32] - internal string MapUTF8() => System.Text.Encoding.UTF8.GetString( Map, 0, System.Array.IndexOf( Map, 0 ) ); + internal string MapUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Map, 0, System.Array.IndexOf( Map, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] // byte[] m_szMap internal byte[] Map; // m_szMap char [32] - internal string GameDescriptionUTF8() => System.Text.Encoding.UTF8.GetString( GameDescription, 0, System.Array.IndexOf( GameDescription, 0 ) ); + internal string GameDescriptionUTF8() => Steamworks.Utility.Utf8NoBom.GetString( GameDescription, 0, System.Array.IndexOf( GameDescription, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] // byte[] m_szGameDescription internal byte[] GameDescription; // m_szGameDescription char [64] internal uint AppID; // m_nAppID uint32 @@ -54,10 +54,10 @@ namespace Steamworks.Data internal bool Secure; // m_bSecure bool internal uint TimeLastPlayed; // m_ulTimeLastPlayed uint32 internal int ServerVersion; // m_nServerVersion int - internal string ServerNameUTF8() => System.Text.Encoding.UTF8.GetString( ServerName, 0, System.Array.IndexOf( ServerName, 0 ) ); + internal string ServerNameUTF8() => Steamworks.Utility.Utf8NoBom.GetString( ServerName, 0, System.Array.IndexOf( ServerName, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] // byte[] m_szServerName internal byte[] ServerName; // m_szServerName char [64] - internal string GameTagsUTF8() => System.Text.Encoding.UTF8.GetString( GameTags, 0, System.Array.IndexOf( GameTags, 0 ) ); + internal string GameTagsUTF8() => Steamworks.Utility.Utf8NoBom.GetString( GameTags, 0, System.Array.IndexOf( GameTags, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] // byte[] m_szGameTags internal byte[] GameTags; // m_szGameTags char [128] internal ulong SteamID; // m_steamID CSteamID @@ -105,30 +105,6 @@ namespace Steamworks.Data } - [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] - internal struct InputMotionDataV2_t - { - internal float DriftCorrectedQuatX; // driftCorrectedQuatX float - internal float DriftCorrectedQuatY; // driftCorrectedQuatY float - internal float DriftCorrectedQuatZ; // driftCorrectedQuatZ float - internal float DriftCorrectedQuatW; // driftCorrectedQuatW float - internal float SensorFusionQuatX; // sensorFusionQuatX float - internal float SensorFusionQuatY; // sensorFusionQuatY float - internal float SensorFusionQuatZ; // sensorFusionQuatZ float - internal float SensorFusionQuatW; // sensorFusionQuatW float - internal float DeferredSensorFusionQuatX; // deferredSensorFusionQuatX float - internal float DeferredSensorFusionQuatY; // deferredSensorFusionQuatY float - internal float DeferredSensorFusionQuatZ; // deferredSensorFusionQuatZ float - internal float DeferredSensorFusionQuatW; // deferredSensorFusionQuatW float - internal float GravityX; // gravityX float - internal float GravityY; // gravityY float - internal float GravityZ; // gravityZ float - internal float DegreesPerSecondX; // degreesPerSecondX float - internal float DegreesPerSecondY; // degreesPerSecondY float - internal float DegreesPerSecondZ; // degreesPerSecondZ float - - } - [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] internal struct SteamInputActionEvent_t { @@ -146,10 +122,10 @@ namespace Steamworks.Data internal WorkshopFileType FileType; // m_eFileType EWorkshopFileType internal AppId CreatorAppID; // m_nCreatorAppID AppId_t internal AppId ConsumerAppID; // m_nConsumerAppID AppId_t - internal string TitleUTF8() => System.Text.Encoding.UTF8.GetString( Title, 0, System.Array.IndexOf( Title, 0 ) ); + internal string TitleUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Title, 0, System.Array.IndexOf( Title, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 129)] // byte[] m_rgchTitle internal byte[] Title; // m_rgchTitle char [129] - internal string DescriptionUTF8() => System.Text.Encoding.UTF8.GetString( Description, 0, System.Array.IndexOf( Description, 0 ) ); + internal string DescriptionUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Description, 0, System.Array.IndexOf( Description, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8000)] // byte[] m_rgchDescription internal byte[] Description; // m_rgchDescription char [8000] internal ulong SteamIDOwner; // m_ulSteamIDOwner uint64 @@ -163,23 +139,24 @@ namespace Steamworks.Data internal bool AcceptedForUse; // m_bAcceptedForUse bool [MarshalAs(UnmanagedType.I1)] internal bool TagsTruncated; // m_bTagsTruncated bool - internal string TagsUTF8() => System.Text.Encoding.UTF8.GetString( Tags, 0, System.Array.IndexOf( Tags, 0 ) ); + internal string TagsUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Tags, 0, System.Array.IndexOf( Tags, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1025)] // byte[] m_rgchTags internal byte[] Tags; // m_rgchTags char [1025] internal ulong File; // m_hFile UGCHandle_t internal ulong PreviewFile; // m_hPreviewFile UGCHandle_t - internal string PchFileNameUTF8() => System.Text.Encoding.UTF8.GetString( PchFileName, 0, System.Array.IndexOf( PchFileName, 0 ) ); + internal string PchFileNameUTF8() => Steamworks.Utility.Utf8NoBom.GetString( PchFileName, 0, System.Array.IndexOf( PchFileName, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)] // byte[] m_pchFileName internal byte[] PchFileName; // m_pchFileName char [260] internal int FileSize; // m_nFileSize int32 internal int PreviewFileSize; // m_nPreviewFileSize int32 - internal string URLUTF8() => System.Text.Encoding.UTF8.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); + internal string URLUTF8() => Steamworks.Utility.Utf8NoBom.GetString( URL, 0, System.Array.IndexOf( URL, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // byte[] m_rgchURL internal byte[] URL; // m_rgchURL char [256] internal uint VotesUp; // m_unVotesUp uint32 internal uint VotesDown; // m_unVotesDown uint32 internal float Score; // m_flScore float internal uint NumChildren; // m_unNumChildren uint32 + internal ulong TotalFilesSize; // m_ulTotalFilesSize uint64 } @@ -197,7 +174,7 @@ namespace Steamworks.Data internal partial struct SteamDatagramHostedAddress { internal int CbSize; // m_cbSize int - internal string DataUTF8() => System.Text.Encoding.UTF8.GetString( Data, 0, System.Array.IndexOf( Data, 0 ) ); + internal string DataUTF8() => Steamworks.Utility.Utf8NoBom.GetString( Data, 0, System.Array.IndexOf( Data, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] // byte[] m_data internal byte[] Data; // m_data char [128] @@ -211,7 +188,7 @@ namespace Steamworks.Data internal AppId AppID; // m_nAppID AppId_t internal uint Time; // m_rtime RTime32 internal int CbAppData; // m_cbAppData int - internal string AppDataUTF8() => System.Text.Encoding.UTF8.GetString( AppData, 0, System.Array.IndexOf( AppData, 0 ) ); + internal string AppDataUTF8() => Steamworks.Utility.Utf8NoBom.GetString( AppData, 0, System.Array.IndexOf( AppData, 0 ) ); [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2048)] // byte[] m_appData internal byte[] AppData; // m_appData char [2048] diff --git a/Libraries/Facepunch.Steamworks/Generated/SteamTypes.cs b/Libraries/Facepunch.Steamworks/Generated/SteamTypes.cs index 5f5085f04..9aaaa645f 100644 --- a/Libraries/Facepunch.Steamworks/Generated/SteamTypes.cs +++ b/Libraries/Facepunch.Steamworks/Generated/SteamTypes.cs @@ -598,6 +598,22 @@ namespace Steamworks.Data public int CompareTo( SteamInventoryUpdateHandle_t other ) => Value.CompareTo( other.Value ); } + public struct TimelineEventHandle : IEquatable, IComparable + { + // Name: TimelineEventHandle_t, Type: unsigned long long + public ulong Value; + + public static implicit operator TimelineEventHandle( ulong value ) => new TimelineEventHandle(){ Value = value }; + public static implicit operator ulong( TimelineEventHandle value ) => value.Value; + public override string ToString() => Value.ToString(); + public override int GetHashCode() => Value.GetHashCode(); + public override bool Equals( object p ) => this.Equals( (TimelineEventHandle) p ); + public bool Equals( TimelineEventHandle p ) => p.Value == Value; + public static bool operator ==( TimelineEventHandle a, TimelineEventHandle b ) => a.Equals( b ); + public static bool operator !=( TimelineEventHandle a, TimelineEventHandle b ) => !a.Equals( b ); + public int CompareTo( TimelineEventHandle other ) => Value.CompareTo( other.Value ); + } + internal struct RemotePlaySessionID_t : IEquatable, IComparable { // Name: RemotePlaySessionID_t, Type: unsigned int diff --git a/Libraries/Facepunch.Steamworks/Interfaces/ISteamMatchmakingServers.cs b/Libraries/Facepunch.Steamworks/Interfaces/ISteamMatchmakingServers.cs new file mode 100644 index 000000000..070ee12ef --- /dev/null +++ b/Libraries/Facepunch.Steamworks/Interfaces/ISteamMatchmakingServers.cs @@ -0,0 +1,42 @@ +using Steamworks.Data; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace Steamworks +{ + internal partial class ISteamMatchmakingServers + { + // Cached offset of gameserveritem_t.m_bHadSuccessfulResponse + private static int hasSuccessfulResponseOffset; + + /// + /// Read gameserveritem_t.m_bHadSuccessfulResponse without allocating the struct on the heap + /// + /// + /// + /// + internal bool HasServerResponded( HServerListRequest hRequest, int iServer ) + { + IntPtr returnValue = _GetServerDetails( Self, hRequest, iServer ); + + // Return false if steam returned null + if ( returnValue == IntPtr.Zero ) return false; + + // Cache the offset of m_bHadSuccessfulResponse + if ( hasSuccessfulResponseOffset == 0 ) + { + hasSuccessfulResponseOffset = Marshal.OffsetOf( nameof( gameserveritem_t.HadSuccessfulResponse ) ).ToInt32(); + + if ( hasSuccessfulResponseOffset == 0 ) + { + throw new Exception( "Failed to get offset of gameserveritem_t.HadSuccessfulResponse" ); + } + } + + // Read byte m_bHadSuccessfulResponse + return Marshal.ReadByte( IntPtr.Add( returnValue, hasSuccessfulResponseOffset ) ) == 1; + } + } +} diff --git a/Libraries/Facepunch.Steamworks/Networking/Connection.cs b/Libraries/Facepunch.Steamworks/Networking/Connection.cs index d1069a693..f64e298ce 100644 --- a/Libraries/Facepunch.Steamworks/Networking/Connection.cs +++ b/Libraries/Facepunch.Steamworks/Networking/Connection.cs @@ -124,7 +124,7 @@ namespace Steamworks.Data /// public unsafe Result SendMessage( string str, SendType sendType = SendType.Reliable, ushort laneIndex = 0 ) { - var bytes = System.Text.Encoding.UTF8.GetBytes( str ); + var bytes = Utility.Utf8NoBom.GetBytes( str ); return SendMessage( bytes, sendType, laneIndex ); } diff --git a/Libraries/Facepunch.Steamworks/Networking/ConnectionManager.cs b/Libraries/Facepunch.Steamworks/Networking/ConnectionManager.cs index 3116fa804..f996ac53d 100644 --- a/Libraries/Facepunch.Steamworks/Networking/ConnectionManager.cs +++ b/Libraries/Facepunch.Steamworks/Networking/ConnectionManager.cs @@ -249,7 +249,7 @@ namespace Steamworks /// public void SendMessages( Connection[] connections, int connectionCount, string str, SendType sendType = SendType.Reliable, Result[]? results = null ) { - var bytes = System.Text.Encoding.UTF8.GetBytes( str ); + var bytes = Utility.Utf8NoBom.GetBytes( str ); SendMessages( connections, connectionCount, bytes, sendType, results ); } diff --git a/Libraries/Facepunch.Steamworks/ServerList/Base.cs b/Libraries/Facepunch.Steamworks/ServerList/Base.cs index eaf66449a..40057b9fe 100644 --- a/Libraries/Facepunch.Steamworks/ServerList/Base.cs +++ b/Libraries/Facepunch.Steamworks/ServerList/Base.cs @@ -46,6 +46,7 @@ namespace Steamworks.ServerList /// public List Unresponsive = new List(); + public List Unqueried = new List(); public Base() { @@ -139,7 +140,7 @@ namespace Steamworks.ServerList } } - public void Dispose() + public virtual void Dispose() { ReleaseQuery(); } @@ -167,12 +168,18 @@ namespace Steamworks.ServerList watchList.RemoveAll( x => { if (Internal is null) { return true; } - - var info = Internal.GetServerDetails( request, x ); - if ( info.HadSuccessfulResponse ) + + // First check if the server has responded without allocating server info + bool hasResponded = Internal.HasServerResponded( request, x ); + if ( hasResponded ) { - OnServer( ServerInfo.From( info ), info.HadSuccessfulResponse ); - return true; + // Now get all server info + var info = Internal.GetServerDetails( request, x ); + if ( info.HadSuccessfulResponse ) + { + OnServer( ServerInfo.From( info ), info.HadSuccessfulResponse ); + return true; + } } return false; @@ -185,8 +192,10 @@ namespace Steamworks.ServerList { if (Internal is null) { return true; } - var info = Internal.GetServerDetails( request, x ); - OnServer( ServerInfo.From( info ), info.HadSuccessfulResponse ); + var details = Internal.GetServerDetails( request, x ); + var info = ServerInfo.From( details ); + info.Ping = int.MaxValue; + Unqueried.Add( info ); return true; } ); } @@ -206,4 +215,4 @@ namespace Steamworks.ServerList } } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/ServerList/Favourites.cs b/Libraries/Facepunch.Steamworks/ServerList/Favourites.cs index e71394570..22d4c426b 100644 --- a/Libraries/Facepunch.Steamworks/ServerList/Favourites.cs +++ b/Libraries/Facepunch.Steamworks/ServerList/Favourites.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace Steamworks.ServerList { @@ -11,8 +7,8 @@ namespace Steamworks.ServerList internal override void LaunchQuery() { if (Internal is null) { return; } - var filters = GetFilters(); - request = Internal.RequestFavoritesServerList( AppId.Value, ref filters, (uint)filters.Length, IntPtr.Zero ); + using var filters = new ServerFilterMarshaler( GetFilters() ); + request = Internal.RequestFavoritesServerList( AppId.Value, filters.Pointer, (uint)filters.Count, IntPtr.Zero ); } } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/ServerList/Friends.cs b/Libraries/Facepunch.Steamworks/ServerList/Friends.cs index 60d72e31c..6365d3b87 100644 --- a/Libraries/Facepunch.Steamworks/ServerList/Friends.cs +++ b/Libraries/Facepunch.Steamworks/ServerList/Friends.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace Steamworks.ServerList { @@ -11,8 +7,8 @@ namespace Steamworks.ServerList internal override void LaunchQuery() { if (Internal is null) { return; } - var filters = GetFilters(); - request = Internal.RequestFriendsServerList( AppId.Value, ref filters, (uint)filters.Length, IntPtr.Zero ); + using var filters = new ServerFilterMarshaler( GetFilters() ); + request = Internal.RequestFriendsServerList( AppId.Value, filters.Pointer, (uint)filters.Count, IntPtr.Zero ); } } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/ServerList/History.cs b/Libraries/Facepunch.Steamworks/ServerList/History.cs index 3d059767e..d73ccf8b3 100644 --- a/Libraries/Facepunch.Steamworks/ServerList/History.cs +++ b/Libraries/Facepunch.Steamworks/ServerList/History.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace Steamworks.ServerList { @@ -11,8 +7,8 @@ namespace Steamworks.ServerList internal override void LaunchQuery() { if (Internal is null) { return; } - var filters = GetFilters(); - request = Internal.RequestHistoryServerList( AppId.Value, ref filters, (uint)filters.Length, IntPtr.Zero ); + using var filters = new ServerFilterMarshaler( GetFilters() ); + request = Internal.RequestHistoryServerList( AppId.Value, filters.Pointer, (uint)filters.Count, IntPtr.Zero ); } } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/ServerList/Internet.cs b/Libraries/Facepunch.Steamworks/ServerList/Internet.cs index c493b26fa..c138d3977 100644 --- a/Libraries/Facepunch.Steamworks/ServerList/Internet.cs +++ b/Libraries/Facepunch.Steamworks/ServerList/Internet.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace Steamworks.ServerList { @@ -10,9 +6,9 @@ namespace Steamworks.ServerList { internal override void LaunchQuery() { - if (Internal is null) { return; } - var filters = GetFilters(); - request = Internal.RequestInternetServerList( AppId.Value, filters, (uint)filters.Length, IntPtr.Zero ); + if ( Internal is null ) { return; } + using var filters = new ServerFilterMarshaler( GetFilters() ); + request = Internal.RequestInternetServerList( AppId.Value, filters.Pointer, (uint)filters.Count, IntPtr.Zero ); } } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/ServerList/IpList.cs b/Libraries/Facepunch.Steamworks/ServerList/IpList.cs index c76e88cf7..aa7412bca 100644 --- a/Libraries/Facepunch.Steamworks/ServerList/IpList.cs +++ b/Libraries/Facepunch.Steamworks/ServerList/IpList.cs @@ -1,9 +1,5 @@ -using Steamworks.Data; -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; -using System.Text; using System.Threading.Tasks; namespace Steamworks.ServerList @@ -30,15 +26,17 @@ namespace Steamworks.ServerList var ips = Ips.ToArray(); - while ( true ) + wantsCancel = false; + + while ( !wantsCancel ) { - var sublist = ips.Skip( pointer ).Take( blockSize ); - if ( sublist.Count() == 0 ) + var sublist = ips.Skip( pointer ).Take( blockSize ).ToList(); + if ( sublist.Count == 0 ) break; using ( var list = new ServerList.Internet() ) { - list.AddFilter( "or", sublist.Count().ToString() ); + list.AddFilter( "or", sublist.Count.ToString() ); foreach ( var server in sublist ) { @@ -47,9 +45,6 @@ namespace Steamworks.ServerList await list.RunQueryAsync( timeoutSeconds ); - if ( wantsCancel ) - return false; - Responsive.AddRange( list.Responsive ); Responsive = Responsive.Distinct().ToList(); Unresponsive.AddRange( list.Unresponsive ); @@ -64,9 +59,17 @@ namespace Steamworks.ServerList return true; } + // note: Cancel doesn't get called in Dispose because request is always null for this class public override void Cancel() { wantsCancel = true; } + + public override void Dispose() + { + base.Dispose(); + + wantsCancel = true; + } } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/ServerList/LocalNetwork.cs b/Libraries/Facepunch.Steamworks/ServerList/LocalNetwork.cs index ed5d476c8..3c3dfbd6f 100644 --- a/Libraries/Facepunch.Steamworks/ServerList/LocalNetwork.cs +++ b/Libraries/Facepunch.Steamworks/ServerList/LocalNetwork.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace Steamworks.ServerList { @@ -14,4 +10,4 @@ namespace Steamworks.ServerList request = Internal.RequestLANServerList( AppId.Value, IntPtr.Zero ); } } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/ServerList/ServerFilterMarshaler.cs b/Libraries/Facepunch.Steamworks/ServerList/ServerFilterMarshaler.cs new file mode 100644 index 000000000..c0baf6d6c --- /dev/null +++ b/Libraries/Facepunch.Steamworks/ServerList/ServerFilterMarshaler.cs @@ -0,0 +1,58 @@ +using System; +using System.Runtime.InteropServices; +using Steamworks.Data; + +namespace Steamworks.ServerList; + +internal struct ServerFilterMarshaler : IDisposable +{ + private static readonly int SizeOfPointer = Marshal.SizeOf(); + private static readonly int SizeOfKeyValuePair = Marshal.SizeOf(); + + private IntPtr _arrayPtr; + private IntPtr _itemsPtr; + + public int Count { get; private set; } + public IntPtr Pointer => _arrayPtr; + + public ServerFilterMarshaler( MatchMakingKeyValuePair[] filters ) + { + if ( filters == null || filters.Length == 0 ) + { + Count = 0; + _arrayPtr = IntPtr.Zero; + _itemsPtr = IntPtr.Zero; + return; + } + + Count = filters.Length; + _arrayPtr = Marshal.AllocHGlobal( SizeOfPointer * filters.Length ); + _itemsPtr = Marshal.AllocHGlobal( SizeOfKeyValuePair * filters.Length ); + + var arrayDst = _arrayPtr; + var itemDst = _itemsPtr; + foreach ( var filter in filters ) + { + Marshal.WriteIntPtr( arrayDst, itemDst ); + arrayDst += SizeOfPointer; + + Marshal.StructureToPtr( filter, itemDst, false ); + itemDst += SizeOfKeyValuePair; + } + } + + public void Dispose() + { + if ( _arrayPtr != IntPtr.Zero ) + { + Marshal.FreeHGlobal( _arrayPtr ); + _arrayPtr = IntPtr.Zero; + } + + if ( _itemsPtr != IntPtr.Zero ) + { + Marshal.FreeHGlobal( _itemsPtr ); + _itemsPtr = IntPtr.Zero; + } + } +} diff --git a/Libraries/Facepunch.Steamworks/SteamApps.cs b/Libraries/Facepunch.Steamworks/SteamApps.cs index 80359e884..002a724ba 100644 --- a/Libraries/Facepunch.Steamworks/SteamApps.cs +++ b/Libraries/Facepunch.Steamworks/SteamApps.cs @@ -20,6 +20,8 @@ namespace Steamworks SetInterface( server, new ISteamApps( server ) ); if ( Interface is null || Interface.Self == IntPtr.Zero ) return false; + InstallEvents(); + return true; } diff --git a/Libraries/Facepunch.Steamworks/SteamClient.cs b/Libraries/Facepunch.Steamworks/SteamClient.cs index f0db1417c..1e8537e38 100644 --- a/Libraries/Facepunch.Steamworks/SteamClient.cs +++ b/Libraries/Facepunch.Steamworks/SteamClient.cs @@ -23,9 +23,32 @@ namespace Steamworks System.Environment.SetEnvironmentVariable( "SteamAppId", appid.ToString() ); System.Environment.SetEnvironmentVariable( "SteamGameId", appid.ToString() ); - if ( !SteamAPI.Init() ) + var interfaceVersions = Helpers.BuildVersionString( + ISteamApps.Version, + ISteamFriends.Version, + ISteamInput.Version, + ISteamInventory.Version, + ISteamMatchmaking.Version, + ISteamMatchmakingServers.Version, + ISteamMusic.Version, + ISteamNetworking.Version, + ISteamNetworkingSockets.Version, + ISteamNetworkingUtils.Version, + ISteamParentalSettings.Version, + ISteamParties.Version, + ISteamRemoteStorage.Version, + ISteamScreenshots.Version, + ISteamUGC.Version, + ISteamUser.Version, + ISteamUserStats.Version, + ISteamUtils.Version, + ISteamVideo.Version, + ISteamRemotePlay.Version, + ISteamTimeline.Version ); + var result = SteamAPI.Init( interfaceVersions, out var error ); + if ( result != SteamAPIInitResult.OK ) { - throw new System.Exception( "SteamApi_Init returned false. Steam isn't running, couldn't find Steam, App ID is ureleased, Don't own App ID." ); + throw new System.Exception( $"SteamApi_Init failed with {result} - error: {error}" ); } AppId = appid; @@ -33,12 +56,12 @@ namespace Steamworks initialized = true; // - // Dispatch is responsible for pumping the - // event loop. + // Dispatch is responsible for pumping the event loop. // Dispatch.Init(); Dispatch.ClientPipe = SteamAPI.GetHSteamPipe(); + // Note: don't forget to add the interface version to SteamAPI.Init above!!! AddInterface(); AddInterface(); AddInterface(); @@ -59,6 +82,8 @@ namespace Steamworks AddInterface(); AddInterface(); AddInterface(); + AddInterface(); + // Note: don't forget to add the interface version to SteamAPI.Init above!!! initialized = openInterfaces.Count > 0; diff --git a/Libraries/Facepunch.Steamworks/SteamNetworkingUtils.cs b/Libraries/Facepunch.Steamworks/SteamNetworkingUtils.cs index 4de408956..15927df32 100644 --- a/Libraries/Facepunch.Steamworks/SteamNetworkingUtils.cs +++ b/Libraries/Facepunch.Steamworks/SteamNetworkingUtils.cs @@ -457,7 +457,7 @@ namespace Steamworks internal unsafe static bool SetConfigString( NetConfig type, string value ) { - var bytes = Encoding.UTF8.GetBytes( value ); + var bytes = Utility.Utf8NoBom.GetBytes( value ); fixed ( byte* ptr = bytes ) { @@ -500,7 +500,7 @@ namespace Steamworks internal unsafe static bool SetConnectionConfig( uint con, NetConfig type, string value ) { - var bytes = Encoding.UTF8.GetBytes( value ); + var bytes = Utility.Utf8NoBom.GetBytes( value ); fixed ( byte* ptr = bytes ) { diff --git a/Libraries/Facepunch.Steamworks/SteamServer.cs b/Libraries/Facepunch.Steamworks/SteamServer.cs index e4543dd1e..c9b4b2866 100644 --- a/Libraries/Facepunch.Steamworks/SteamServer.cs +++ b/Libraries/Facepunch.Steamworks/SteamServer.cs @@ -83,14 +83,24 @@ namespace Steamworks // // Get other interfaces // - if ( !SteamInternal.GameServer_Init( ipaddress, 0, init.GamePort, init.QueryPort, (int)init.Mode, init.VersionString ) ) + var interfaceVersions = Helpers.BuildVersionString( + ISteamGameServer.Version, + ISteamUtils.Version, + ISteamNetworking.Version, + ISteamGameServerStats.Version, + ISteamInventory.Version, + ISteamUGC.Version, + ISteamApps.Version, + ISteamNetworkingUtils.Version, + ISteamNetworkingSockets.Version ); + var result = SteamInternal.GameServer_Init( ipaddress, init.GamePort, init.QueryPort, (int)init.Mode, init.VersionString, interfaceVersions, out var error ); + if ( result != SteamAPIInitResult.OK ) { - throw new System.Exception( $"InitGameServer returned false ({ipaddress},{0},{init.GamePort},{init.QueryPort},{init.Mode},\"{init.VersionString}\")" ); + throw new System.Exception( $"InitGameServer({ipaddress},{init.GamePort},{init.QueryPort},{init.Mode},\"{init.VersionString}\") returned false - error: {error}" ); } // - // Dispatch is responsible for pumping the - // event loop. + // Dispatch is responsible for pumping the event loop. // Dispatch.Init(); Dispatch.ServerPipe = SteamGameServer.GetHSteamPipe(); diff --git a/Libraries/Facepunch.Steamworks/SteamTimeline.cs b/Libraries/Facepunch.Steamworks/SteamTimeline.cs new file mode 100644 index 000000000..f7a30b895 --- /dev/null +++ b/Libraries/Facepunch.Steamworks/SteamTimeline.cs @@ -0,0 +1,221 @@ +using System; +using System.Threading.Tasks; +using Steamworks.Data; + +namespace Steamworks; + +public class SteamTimeline : SteamClientClass +{ + internal static ISteamTimeline? Internal => Interface as ISteamTimeline; + + internal override bool InitializeInterface( bool server ) + { + SetInterface( server, new ISteamTimeline( server ) ); + if ( Interface is null ) return false; + if ( Interface.Self == IntPtr.Zero ) return false; + + InstallEvents(); + return true; + } + + internal static void InstallEvents() + { + } + + /// + /// Sets a description for the current game state in the timeline. These help the user to find specific moments in the timeline when saving clips. Setting a + /// new state description replaces any previous description. + /// + public static void SetTimelineTooltip( string description, float timeOffsetSeconds ) + { + if ( Internal is null ) { return; } + Internal.SetTimelineTooltip( description, timeOffsetSeconds ); + } + + /// + /// Clears the previous set game state in the timeline. + /// + public static void ClearTimelineTooltip( float timeOffsetSeconds ) + { + if ( Internal is null ) { return; } + Internal.ClearTimelineTooltip( timeOffsetSeconds ); + } + + /// + /// Use this to mark an event on the Timeline. This event will be instantaneous. (See to add events that happened over time.) + /// + public static TimelineEventHandle AddInstantaneousTimelineEvent( string title, string description, string icon, + uint priority, float startOffsetSeconds, TimelineEventClipPriority possibleClip ) + { + if ( Internal is null ) { return default; } + return Internal.AddInstantaneousTimelineEvent( title, description, icon, priority, startOffsetSeconds, + possibleClip ); + } + + /// + /// Use this to mark an event on the Timeline that takes some amount of time to complete. + /// + public static TimelineEventHandle AddRangeTimelineEvent( string title, string description, string icon, + uint priority, float startOffsetSeconds, float durationSeconds, TimelineEventClipPriority possibleClip ) + { + if ( Internal is null ) { return default; } + return Internal.AddRangeTimelineEvent( title, description, icon, priority, startOffsetSeconds, durationSeconds, + possibleClip ); + } + + /// + /// Use this to mark the start of an event on the Timeline that takes some amount of time to complete. The duration of the event is determined by a matching call + /// to . If the game wants to cancel an event in progress, they can do that with a call to . + /// + public static TimelineEventHandle StartRangeTimelineEvent( string title, string description, string icon, + uint priority, + float startOffsetSeconds, TimelineEventClipPriority possibleClip ) + { + if ( Internal is null ) { return default; } + return Internal.StartRangeTimelineEvent( title, description, icon, priority, startOffsetSeconds, possibleClip ); + } + + /// + /// Use this to update the details of an event that was started with . + /// + public static void UpdateRangeTimelineEvent( TimelineEventHandle handle, string title, string description, + string icon, uint priority, TimelineEventClipPriority possibleClip ) + { + if ( Internal is null ) { return; } + Internal.UpdateRangeTimelineEvent( handle, title, description, icon, priority, possibleClip ); + } + + /// + /// Use this to identify the end of an event that was started with . + /// + public static void EndRangeTimelineEvent( TimelineEventHandle handle, float endOffsetSeconds ) + { + if ( Internal is null ) { return; } + Internal.EndRangeTimelineEvent( handle, endOffsetSeconds ); + } + + /// + /// Use this to remove a Timeline event that was previously added. + /// + public static void RemoveTimelineEvent( TimelineEventHandle handle ) + { + if ( Internal is null ) { return; } + Internal.RemoveTimelineEvent( handle ); + } + + /// + /// Use this to determine if video recordings exist for the specified event. This can be useful when the game needs to decide whether or not to show a control + /// that will call . + /// + public static async Task DoesEventRecordingExist( TimelineEventHandle handle ) + { + if ( Internal is null ) { return false; } + var result = await Internal.DoesEventRecordingExist( handle ); + return result?.RecordingExists ?? false; + } + + /// + /// Use this to start a game phase. Game phases allow the user to navigate their background recordings and clips. Exactly what a game phase means will vary game + /// to game, but the game phase should be a section of gameplay that is usually between 10 minutes and a few hours in length, and should be the main way a user + /// would think to divide up the game. These are presented to the user in a UI that shows the date the game was played, with one row per game slice. Game phases + /// should be used to mark sections of gameplay that the user might be interested in watching. + /// + public static void StartGamePhase() + { + if ( Internal is null ) { return; } + Internal.StartGamePhase(); + } + + /// + /// Use this to end a game phase that was started with . + /// + public static void EndGamePhase() + { + if ( Internal is null ) { return; } + Internal.EndGamePhase(); + } + + /// + /// The phase ID is used to let the game identify which phase it is referring to in calls to or + /// . It may also be used to associated multiple phases with each other. + /// + /// A game-provided persistent ID for a game phase. This could be a the match ID in a multiplayer game, a chapter name in a single player game, the ID of a character, etc. + public static void SetGamePhaseId( string phaseId ) + { + if ( Internal is null ) { return; } + Internal.SetGamePhaseID( phaseId ); + } + + /// + /// Use this to determine if video recordings exist for the specified game phase. This can be useful when the game needs to decide whether or not to show a control that will call . + /// + public static async Task DoesGamePhaseRecordingExist( string phaseId ) + { + if ( Internal is null ) { return null; } + var result = await Internal.DoesGamePhaseRecordingExist( phaseId ); + if ( !result.HasValue ) + { + return null; + } + + var info = result.Value; + return new GamePhaseRecordingInfo + { + PhaseId = info.PhaseIDUTF8(), + RecordingMs = info.RecordingMS, + LongestClipMs = info.LongestClipMS, + ClipCount = info.ClipCount, + ScreenshotCount = info.ScreenshotCount, + }; + } + + /// + /// Use this to add a game phase tag. Phase tags represent data with a well defined set of options, which could be data such as match resolution, hero played, game mode, etc. Tags can have an icon + /// in addition to a text name. Multiple tags within the same group may be added per phase and all will be remembered. For example, this may be called multiple times for a "Bosses Defeated" group, + /// with different names and icons for each boss defeated during the phase, all of which will be shown to the user. + /// + public static void AddGamePhaseTag( string tagName, string icon, string tagGroup, uint priority ) + { + if ( Internal is null ) { return; } + Internal.AddGamePhaseTag( tagName, icon, tagGroup, priority ); + } + + /// + /// Use this to add a game phase attribute. Phase attributes represent generic text fields that can be updated throughout the duration of the phase. They are meant to be used for phase metadata + /// that is not part of a well defined set of options. For example, a KDA attribute that starts with the value "0/0/0" and updates as the phase progresses, or something like a played-entered character + /// name. Attributes can be set as many times as the game likes with SetGamePhaseAttribute, and only the last value will be shown to the user. + /// + public static void SetGamePhaseAttribute( string attributeGroup, string attributeValue, uint priority ) + { + if ( Internal is null ) { return; } + Internal.SetGamePhaseAttribute( attributeGroup, attributeValue, priority ); + } + + /// + /// Changes the color of the timeline bar. See for how to use each value. + /// + public static void SetTimelineGameMode( TimelineGameMode gameMode ) + { + if ( Internal is null ) { return; } + Internal.SetTimelineGameMode( gameMode ); + } + + /// + /// Opens the Steam overlay to the section of the timeline represented by the game phase. + /// + public static void OpenOverlayToGamePhase( string phaseId ) + { + if ( Internal is null ) { return; } + Internal.OpenOverlayToGamePhase( phaseId ); + } + + /// + /// Opens the Steam overlay to the section of the timeline represented by the timeline event. This event must be in the current game session, since values are not + /// valid for future runs of the game. + /// + public static void OpenOverlayToTimelineEvent( TimelineEventHandle handle ) + { + if ( Internal is null ) { return; } + Internal.OpenOverlayToTimelineEvent( handle ); + } +} diff --git a/Libraries/Facepunch.Steamworks/SteamUser.cs b/Libraries/Facepunch.Steamworks/SteamUser.cs index a0afe522a..0a9fc758b 100644 --- a/Libraries/Facepunch.Steamworks/SteamUser.cs +++ b/Libraries/Facepunch.Steamworks/SteamUser.cs @@ -43,7 +43,7 @@ namespace Steamworks Dispatch.Install( x => OnMicroTxnAuthorizationResponse?.Invoke( x.AppID, x.OrderID, x.Authorized != 0 ) ); Dispatch.Install( x => OnGameWebCallback?.Invoke( x.URLUTF8() ) ); Dispatch.Install( x => OnGetAuthSessionTicketResponse?.Invoke( x ) ); - Dispatch.Install( x => OnGetAuthTicketForWebApiResponse?.Invoke( x ) ); + Dispatch.Install( x => OnGetTicketForWebApiResponse?.Invoke( x ) ); Dispatch.Install( x => OnDurationControl?.Invoke( new DurationControl { _inner = x } ) ); } @@ -90,7 +90,7 @@ namespace Steamworks public static event Action? OnValidateAuthTicketResponse; /// - /// Used internally for . + /// Used internally for . /// internal static event Action? OnGetAuthSessionTicketResponse; @@ -99,6 +99,11 @@ namespace Steamworks /// internal static event Action? OnGetAuthTicketForWebApiResponse; + /// + /// Used internally for . + /// + internal static event Action? OnGetTicketForWebApiResponse; + /// /// Invoked when a user has responded to a microtransaction authorization request. /// ( appid, orderid, user authorized ) @@ -332,49 +337,6 @@ namespace Steamworks } } - public static async Task GetAuthTicketForWebApi( string identity ) - { - if ( Internal is null ) { return null; } - - HAuthTicket handle = default; - AuthTicketForWebApi? ticket = null; - Result result = Result.Pending; - - Action responseHandler = response => - { - if ( response.Ticket == handle ) { return; } - - result = response.Result == Result.Pending - ? Result.Fail - : response.Result; - ticket = result == Result.OK - ? new AuthTicketForWebApi( - response.GubTicket.Take( response.Ticket ).ToArray(), - response.AuthTicket ) - : null; - }; - - OnGetAuthTicketForWebApiResponse += responseHandler; - try - { - handle = Internal.GetAuthTicketForWebApi( identity ); - - if ( handle == 0 ) { return null; } - - var timeout = DateTime.Now + TimeSpan.FromSeconds( 60f ); - while ( result == Result.Pending && DateTime.Now < timeout ) - { - await Task.Delay( 10 ); - } - } - finally - { - OnGetAuthTicketForWebApiResponse -= responseHandler; - } - - return ticket; - } - /// /// Retrieve a authentication ticket to be sent to the entity who wishes to authenticate you. /// This waits for a positive response from the backend before returning the ticket. This means @@ -424,6 +386,74 @@ namespace Steamworks } } + /// + /// Retrieve an authentication ticket to be sent to the entity who wishes to authenticate you. + /// + private static unsafe AuthTicket? GetAuthTicketForWebApi( string identity ) + { + if ( Internal is null ) { return null; } + uint ticket = Internal.GetAuthTicketForWebApi( identity ); + + if ( ticket == 0 ) + return null; + + return new AuthTicket() + { + Handle = ticket + }; + } + + /// + /// Retrieve a authentication ticket to be sent to the entity who wishes to authenticate you. + /// This waits for a positive response from the backend before returning the ticket. This means + /// the ticket is definitely ready to go as soon as it returns. Will return if the callback + /// times out or returns negatively. + /// + public static async Task GetAuthTicketForWebApiAsync( string identity, double timeoutSeconds = 10.0f ) + { + if ( Internal is null ) { return null; } + var result = Result.Pending; + AuthTicket? ticket = null; + var stopwatch = Stopwatch.StartNew(); + + void f( GetTicketForWebApiResponse_t t ) + { + if ( ticket is null || t.AuthTicket != ticket.Handle ) return; + result = t.Result; + ticket.Data = t.GubTicket; + } + + OnGetTicketForWebApiResponse += f; + + try + { + ticket = GetAuthTicketForWebApi( identity ); + if ( ticket == null ) + return null; + + while ( result == Result.Pending ) + { + await Task.Delay( 10 ); + + if ( stopwatch.Elapsed.TotalSeconds > timeoutSeconds ) + { + ticket.Cancel(); + return null; + } + } + + if ( result == Result.OK ) + return ticket; + + ticket.Cancel(); + return null; + } + finally + { + OnGetTicketForWebApiResponse -= f; + } + } + public static unsafe BeginAuthResult BeginAuthSession( byte[] ticketData, SteamId steamid ) { fixed ( byte* ptr = ticketData ) diff --git a/Libraries/Facepunch.Steamworks/SteamUserStats.cs b/Libraries/Facepunch.Steamworks/SteamUserStats.cs index b4a4aa4e7..4cc07038a 100644 --- a/Libraries/Facepunch.Steamworks/SteamUserStats.cs +++ b/Libraries/Facepunch.Steamworks/SteamUserStats.cs @@ -137,14 +137,13 @@ namespace Steamworks } /// - /// Asynchronously request the user's current stats and achievements from the server. - /// You must always call this first to get the initial status of stats and achievements. - /// Only after the resulting callback comes back can you start calling the rest of the stats - /// and achievement functions for the current user. + /// This call is no longer required as it is managed by the Steam client. The game stats and achievements + /// will be synchronized with Steam before the game process begins. /// + [Obsolete( "No longer required. Automatically handled by the Steam client.", false )] public static bool RequestCurrentStats() { - return Internal != null && Internal.RequestCurrentStats(); + return true; } /// diff --git a/Libraries/Facepunch.Steamworks/Structs/Friend.cs b/Libraries/Facepunch.Steamworks/Structs/Friend.cs index abf2593c7..1f9628b6a 100644 --- a/Libraries/Facepunch.Steamworks/Structs/Friend.cs +++ b/Libraries/Facepunch.Steamworks/Structs/Friend.cs @@ -40,7 +40,7 @@ namespace Steamworks /// /// Return true if this user is playing the game we're running /// - public bool IsPlayingThisGame => GameInfo?.GameID == SteamClient.AppId; + public bool IsPlayingThisGame => GameInfo?.GameID is { Type: GameIdType.App } && GameInfo.Value.GameID.AppId == SteamClient.AppId; /// /// Returns true if this friend is online @@ -75,7 +75,26 @@ namespace Steamworks public Relationship Relationship => SteamFriends.Internal?.GetFriendRelationship( Id ) ?? Relationship.None; public FriendState State => SteamFriends.Internal?.GetFriendPersonaState( Id ) ?? FriendState.Offline; + + /// + /// Returns the player's current Steam name. + /// + /// Steam returns nicknames here if "Append nicknames to friends' names" is disabled in the Steam client. + /// + /// public string? Name => SteamFriends.Internal?.GetFriendPersonaName( Id ); + + /// + /// Returns the nickname that was set for this Steam player, if any. + /// + /// Steam will never return nicknames if "Append nicknames to friends' names" is disabled in the Steam client. + /// + /// + public string? Nickname => SteamFriends.Internal?.GetPlayerNickname( Id ); + + /// + /// Returns the player's Steam name history. + /// public IEnumerable NameHistory { get @@ -114,10 +133,10 @@ namespace Steamworks public struct FriendGameInfo { - public ulong GameID; // m_gameID class CGameID - public uint GameIP; // m_unGameIP uint32 - public ulong SteamIDLobby; // m_steamIDLobby class CSteamID + internal uint GameIP; // m_unGameIP uint32 + internal ulong SteamIDLobby; // m_steamIDLobby class CSteamID + public GameId GameID; public int ConnectionPort; public int QueryPort; diff --git a/Libraries/Facepunch.Steamworks/Structs/GameId.cs b/Libraries/Facepunch.Steamworks/Structs/GameId.cs index e5940c715..02b4f7d68 100644 --- a/Libraries/Facepunch.Steamworks/Structs/GameId.cs +++ b/Libraries/Facepunch.Steamworks/Structs/GameId.cs @@ -1,25 +1,18 @@ using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; - namespace Steamworks.Data { - public struct GameId + public enum GameIdType : byte + { + App = 0, + GameMod = 1, + Shortcut = 2, + P2P = 3, + } + + public struct GameId : IEquatable { - // TODO - Be able to access these vars - /* - - enum EGameIDType - { - k_EGameIDTypeApp = 0, - k_EGameIDTypeGameMod = 1, - k_EGameIDTypeShortcut = 2, - k_EGameIDTypeP2P = 3, - }; - # ifdef VALVE_BIG_ENDIAN unsigned int m_nModID : 32; unsigned int m_nType : 8; @@ -30,8 +23,31 @@ namespace Steamworks.Data unsigned int m_nModID : 32; #endif */ + + // 0xAAAAAAAA_BBCCCCCC + // A = m_nModID + // B = m_nType + // C = m_nAppID public ulong Value; + public GameIdType Type + { + get => (GameIdType)(byte)( Value >> 24 ); + set => Value = ( Value & 0xFFFFFFFF_00FFFFFF ) | ( (ulong)(byte)value << 24 ); + } + + public uint AppId + { + get => (uint)( Value & 0x00000000_00FFFFFF ); + set => Value = ( Value & 0xFFFFFFFF_FF000000 ) | (value & 0x00000000_00FFFFFF); + } + + public uint ModId + { + get => (uint)( Value >> 32 ); + set => Value = ( Value & 0x00000000_FFFFFFFF ) | ( (ulong)value << 32 ); + } + public static implicit operator GameId( ulong value ) { return new GameId { Value = value }; @@ -41,5 +57,30 @@ namespace Steamworks.Data { return value.Value; } + + public bool Equals(GameId other) + { + return Value == other.Value; + } + + public override bool Equals(object obj) + { + return obj is GameId other && Equals(other); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static bool operator ==(GameId left, GameId right) + { + return left.Equals(right); + } + + public static bool operator !=(GameId left, GameId right) + { + return !left.Equals(right); + } } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/Structs/GamePhaseRecordingInfo.cs b/Libraries/Facepunch.Steamworks/Structs/GamePhaseRecordingInfo.cs new file mode 100644 index 000000000..e44a14bbe --- /dev/null +++ b/Libraries/Facepunch.Steamworks/Structs/GamePhaseRecordingInfo.cs @@ -0,0 +1,10 @@ +namespace Steamworks; + +public struct GamePhaseRecordingInfo +{ + public string PhaseId; + public ulong RecordingMs; + public ulong LongestClipMs; + public uint ClipCount; + public uint ScreenshotCount; +} diff --git a/Libraries/Facepunch.Steamworks/Structs/InventoryDef.cs b/Libraries/Facepunch.Steamworks/Structs/InventoryDef.cs index 25afbd570..0ae3ae3ce 100644 --- a/Libraries/Facepunch.Steamworks/Structs/InventoryDef.cs +++ b/Libraries/Facepunch.Steamworks/Structs/InventoryDef.cs @@ -100,7 +100,7 @@ namespace Steamworks uint _ = (uint)Helpers.MemoryBufferSize; - if ( SteamInventory.Internal is null || !SteamInventory.Internal.GetItemDefinitionProperty( Id, name, out var vl, ref _ ) ) + if ( SteamInventory.Internal is null || !SteamInventory.Internal.GetItemDefinitionProperty( Id, name, out var vl ) ) return null; if (name == null) //return keys string diff --git a/Libraries/Facepunch.Steamworks/Structs/InventoryItem.cs b/Libraries/Facepunch.Steamworks/Structs/InventoryItem.cs index aeee9a5e3..fc9846401 100644 --- a/Libraries/Facepunch.Steamworks/Structs/InventoryItem.cs +++ b/Libraries/Facepunch.Steamworks/Structs/InventoryItem.cs @@ -100,18 +100,14 @@ namespace Steamworks internal static Dictionary? GetProperties( SteamInventoryResult_t result, int index ) { - var strlen = (uint) Helpers.MemoryBufferSize; - - if ( SteamInventory.Internal is null || !SteamInventory.Internal.GetResultItemProperty( result, (uint)index, null, out var propNames, ref strlen ) ) + if ( SteamInventory.Internal is null || !SteamInventory.Internal.GetResultItemProperty( result, (uint)index, null, out var propNames ) ) return null; var props = new Dictionary(); foreach ( var propertyName in propNames.Split( ',' ) ) { - strlen = (uint)Helpers.MemoryBufferSize; - - if ( SteamInventory.Internal.GetResultItemProperty( result, (uint)index, propertyName, out var strVal, ref strlen ) ) + if ( SteamInventory.Internal.GetResultItemProperty( result, (uint)index, propertyName, out var strVal ) ) { props.Add( propertyName, strVal ); } @@ -179,4 +175,4 @@ namespace Steamworks public override int GetHashCode() => _id.GetHashCode(); public bool Equals( InventoryItem p ) => p._id == _id; } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/Structs/Lobby.cs b/Libraries/Facepunch.Steamworks/Structs/Lobby.cs index c16a390ec..e9d0ad108 100644 --- a/Libraries/Facepunch.Steamworks/Structs/Lobby.cs +++ b/Libraries/Facepunch.Steamworks/Structs/Lobby.cs @@ -143,7 +143,7 @@ namespace Steamworks.Data public bool SendChatString( string message ) { //adding null terminator as it's used in Helpers.MemoryToString - var data = System.Text.Encoding.UTF8.GetBytes( message + '\0' ); + var data = Utility.Utf8NoBom.GetBytes( message + '\0' ); return SendChatBytes( data ); } diff --git a/Libraries/Facepunch.Steamworks/Structs/MatchMakingKeyValuePair.cs b/Libraries/Facepunch.Steamworks/Structs/MatchMakingKeyValuePair.cs index 401eb0568..033950acd 100644 --- a/Libraries/Facepunch.Steamworks/Structs/MatchMakingKeyValuePair.cs +++ b/Libraries/Facepunch.Steamworks/Structs/MatchMakingKeyValuePair.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Steamworks.Data { - [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPackSize )] + [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPackSize, CharSet = CharSet.Ansi )] internal partial struct MatchMakingKeyValuePair { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] diff --git a/Libraries/Facepunch.Steamworks/Structs/UgcEditor.cs b/Libraries/Facepunch.Steamworks/Structs/UgcEditor.cs index 6a8403853..c32f459d9 100644 --- a/Libraries/Facepunch.Steamworks/Structs/UgcEditor.cs +++ b/Libraries/Facepunch.Steamworks/Structs/UgcEditor.cs @@ -209,7 +209,7 @@ namespace Steamworks.Ugc using ( var a = SteamParamStringArray.From( Tags.ToArray() ) ) { var val = a.Value; - SteamUGC.Internal.SetItemTags( handle, ref val ); + SteamUGC.Internal.SetItemTags( handle, ref val, false ); } } diff --git a/Libraries/Facepunch.Steamworks/Structs/UgcItem.cs b/Libraries/Facepunch.Steamworks/Structs/UgcItem.cs index b02a8d7ab..ebfb6b8ec 100644 --- a/Libraries/Facepunch.Steamworks/Structs/UgcItem.cs +++ b/Libraries/Facepunch.Steamworks/Structs/UgcItem.cs @@ -116,10 +116,10 @@ namespace Steamworks.Ugc /// The number of downvotes of this item /// public uint VotesDown => details.VotesDown; - /// - /// Dependencies/children of this item or collection, available only from WithChildren(true) queries - /// - public PublishedFileId[]? Children; + /// + /// Dependencies/children of this item or collection, available only from WithDependencies(true) queries + /// + public PublishedFileId[]? Children; /// /// Additional previews of this item or collection, available only from WithAdditionalPreviews(true) queries diff --git a/Libraries/Facepunch.Steamworks/Utility/Helpers.cs b/Libraries/Facepunch.Steamworks/Utility/Helpers.cs index 644d1a31f..72ade86d6 100644 --- a/Libraries/Facepunch.Steamworks/Utility/Helpers.cs +++ b/Libraries/Facepunch.Steamworks/Utility/Helpers.cs @@ -99,7 +99,19 @@ namespace Steamworks if ( len == 0 ) return string.Empty; - return UTF8Encoding.UTF8.GetString( (byte*)ptr, len ); + return Utility.Utf8NoBom.GetString( (byte*)ptr, len ); + } + + internal static string BuildVersionString( params string[] interfaceVersions ) + { + var sb = new StringBuilder(); + foreach ( var version in interfaceVersions ) + { + sb.Append( version ).Append( '\0' ); + } + + sb.Append( '\0' ); + return sb.ToString(); } } diff --git a/Libraries/Facepunch.Steamworks/Utility/SteamInterface.cs b/Libraries/Facepunch.Steamworks/Utility/SteamInterface.cs index c2c30b490..dcf6acc4d 100644 --- a/Libraries/Facepunch.Steamworks/Utility/SteamInterface.cs +++ b/Libraries/Facepunch.Steamworks/Utility/SteamInterface.cs @@ -143,4 +143,4 @@ namespace Steamworks } } -} \ No newline at end of file +} diff --git a/Libraries/Facepunch.Steamworks/Utility/Utf8String.cs b/Libraries/Facepunch.Steamworks/Utility/Utf8String.cs index 59d1517f9..3b02317ee 100644 --- a/Libraries/Facepunch.Steamworks/Utility/Utf8String.cs +++ b/Libraries/Facepunch.Steamworks/Utility/Utf8String.cs @@ -9,38 +9,39 @@ using System.Text; namespace Steamworks { - internal unsafe class Utf8StringToNative : ICustomMarshaler + internal struct Utf8StringToNative : IDisposable { - public IntPtr MarshalManagedToNative(object managedObj) + public IntPtr Pointer { get; private set; } + + public unsafe Utf8StringToNative( string? value ) { - if ( managedObj == null ) - return IntPtr.Zero; - - if ( managedObj is string str ) + if ( value == null ) { - fixed ( char* strPtr = str ) - { - int len = Encoding.UTF8.GetByteCount( str ); - var mem = Marshal.AllocHGlobal( len + 1 ); - - var wlen = System.Text.Encoding.UTF8.GetBytes( strPtr, str.Length, (byte*)mem, len + 1 ); - - ( (byte*)mem )[wlen] = 0; - - return mem; - } + Pointer = IntPtr.Zero; + return; } - return IntPtr.Zero; + fixed ( char* strPtr = value ) + { + var len = Utility.Utf8NoBom.GetByteCount( value ); + var mem = Marshal.AllocHGlobal( len + 1 ); + + var wlen = Utility.Utf8NoBom.GetBytes( strPtr, value.Length, (byte*)mem, len + 1 ); + + ( (byte*)mem )[wlen] = 0; + + Pointer = mem; + } } - public object MarshalNativeToManaged(IntPtr pNativeData) => throw new System.NotImplementedException(); - public void CleanUpNativeData(IntPtr pNativeData) => Marshal.FreeHGlobal( pNativeData ); - public void CleanUpManagedData(object managedObj) => throw new System.NotImplementedException(); - public int GetNativeDataSize() => -1; - - [Preserve] - public static ICustomMarshaler GetInstance(string cookie) => new Utf8StringToNative(); + public void Dispose() + { + if ( Pointer != IntPtr.Zero ) + { + Marshal.FreeHGlobal( Pointer ); + Pointer = IntPtr.Zero; + } + } } internal struct Utf8StringPointer @@ -71,7 +72,7 @@ namespace Steamworks dataLen++; } - return Encoding.UTF8.GetString(bytes, dataLen); + return Utility.Utf8NoBom.GetString( bytes, dataLen ); } } } diff --git a/Libraries/Facepunch.Steamworks/Utility/Utility.cs b/Libraries/Facepunch.Steamworks/Utility/Utility.cs index ad77d63d2..0fc1fea87 100644 --- a/Libraries/Facepunch.Steamworks/Utility/Utility.cs +++ b/Libraries/Facepunch.Steamworks/Utility/Utility.cs @@ -10,6 +10,8 @@ namespace Steamworks { public static partial class Utility { + public static readonly Encoding Utf8NoBom = new UTF8Encoding( false, false ); + static internal T? ToType( this IntPtr ptr ) { if ( ptr == IntPtr.Zero ) @@ -111,7 +113,7 @@ namespace Steamworks i++; } - return Encoding.UTF8.GetString( readBuffer, 0, i ); + return Utf8NoBom.GetString( readBuffer, 0, i ); } } } diff --git a/Libraries/Facepunch.Steamworks/libsteam_api64.dylib b/Libraries/Facepunch.Steamworks/libsteam_api64.dylib index 5237be0b2..a3df92732 100644 Binary files a/Libraries/Facepunch.Steamworks/libsteam_api64.dylib and b/Libraries/Facepunch.Steamworks/libsteam_api64.dylib differ diff --git a/Libraries/Facepunch.Steamworks/libsteam_api64.so b/Libraries/Facepunch.Steamworks/libsteam_api64.so index 4217a2e2d..59831c8f0 100644 Binary files a/Libraries/Facepunch.Steamworks/libsteam_api64.so and b/Libraries/Facepunch.Steamworks/libsteam_api64.so differ diff --git a/Libraries/Facepunch.Steamworks/steam_api.dll b/Libraries/Facepunch.Steamworks/steam_api.dll new file mode 100644 index 000000000..a05e4454a Binary files /dev/null and b/Libraries/Facepunch.Steamworks/steam_api.dll differ diff --git a/Libraries/Facepunch.Steamworks/steam_api64.dll b/Libraries/Facepunch.Steamworks/steam_api64.dll index 2b4281209..0224579a1 100644 Binary files a/Libraries/Facepunch.Steamworks/steam_api64.dll and b/Libraries/Facepunch.Steamworks/steam_api64.dll differ diff --git a/changelogs/combine_changelogs.bat b/changelogs/combine_changelogs.bat new file mode 100644 index 000000000..0b738bf93 --- /dev/null +++ b/changelogs/combine_changelogs.bat @@ -0,0 +1,56 @@ +@echo off +setlocal EnableDelayedExpansion +rem This file combines all the other .txt files in this directory into changelog.txt, +rem or another file name given as optional argument. + +rem Determine script's absolute directory and use that for paths +set "SCRIPT_DIR=%~dp0" +set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%" +set "DEFAULT_TARGET_FILE=changelog.txt" + +rem A different target file can be provided as argument +if "%~1"=="" ( + set "TARGET_FILE=%SCRIPT_DIR%\%DEFAULT_TARGET_FILE%" +) else ( + set "TARGET_FILE=%SCRIPT_DIR%\%~1" +) + +if not exist "%TARGET_FILE%" ( + echo %TARGET_FILE% doesn't exist yet, creating it + type nul > "%TARGET_FILE%" +) + +echo. +echo Appending *.txt into %TARGET_FILE%: +for %%F in ("%SCRIPT_DIR%\*.txt") do ( + if exist "%%F" ( + rem Skip target file(s) that we're appending into + set "BASENAME=%%~nxF" + if /i "!BASENAME!"=="%DEFAULT_TARGET_FILE%" ( + echo - Skipping target file: %%~nxF + ) else ( + for %%T in ("%TARGET_FILE%") do ( + if /i "!BASENAME!"=="%%~nxT" ( + echo - Skipping target file: %%~nxF + ) else ( + echo + %%~nxF + rem Strip path and extension from the file name and append as heading + echo %%~nF>> "%TARGET_FILE%" + rem Append file content + type "%%F" >> "%TARGET_FILE%" + rem Add a newline after each file's content + echo.>> "%TARGET_FILE%" + ) + ) + ) + ) else ( + echo ERROR: %%F is missing or not a proper file, skipping! + ) +) + +echo. +rem Finished file name in upper case and without path as heading +for %%T in ("%TARGET_FILE%") do echo === %%~nxT: +echo. +rem Print the finished file itself +type "%TARGET_FILE%" diff --git a/changelogs/combine_changelogs.sh b/changelogs/combine_changelogs.sh new file mode 100644 index 000000000..9732d789e --- /dev/null +++ b/changelogs/combine_changelogs.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +## +# This file combines all the other .txt files in this directory into changelog.txt, +# or another file name given as optional argument. +# + + +# Determine script's absolute directory and use that for the paths, so the +# script can be executed from anywhere +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +DEFAULT_TARGET_FILE="changelog.txt" + +# A different target file can be provided as argument +if [ $# -eq 1 ]; then + TARGET_FILE="$SCRIPT_DIR/$1" +else + TARGET_FILE="$SCRIPT_DIR/$DEFAULT_TARGET_FILE" +fi + +if [ ! -f "$TARGET_FILE" ]; then + echo "$TARGET_FILE doesn't exist yet, creating it" + touch "$TARGET_FILE" +fi + + +echo "" +echo "Appending *.txt into $(basename "$TARGET_FILE"):" +for file in "$SCRIPT_DIR"/*.txt; do + if [ -f "$file" ]; then + # Skip target file(s) that we're appending into + if [[ "$(basename "$file")" = "$DEFAULT_TARGET_FILE" || + "$(basename "$file")" = "$(basename "$TARGET_FILE")" ]]; then + echo " - Skipping target file: $(basename "$file")" + continue + fi + echo " + $(basename "$file")" + + # Strip path and extension from the file name and append it as heading for the entry + echo "$(basename "${file%.*}")" >> "$TARGET_FILE" + # Append file content + cat "$file" >> "$TARGET_FILE" + # Add a newline after each file's content + echo "" >> "$TARGET_FILE" + else + echo "ERROR: $file is missing or not a proper file, skipping!" + fi +done +echo "" + +# Finished file name in upper case and without path as heading +echo "=== $(basename "$TARGET_FILE"):" +echo "" + +# Print the finished file itself +cat "$TARGET_FILE"