Skip to content

Commit 68a99e7

Browse files
committed
Merge branch 'beta' of https://github.com/swiftly-solution/swiftlys2 into beta
2 parents 3b81d2e + 417eae1 commit 68a99e7

File tree

11 files changed

+693
-48
lines changed

11 files changed

+693
-48
lines changed

managed/src/SwiftlyS2.Core/Bootstrap.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ private static IntPtr SteamAPIDLLResolver( string libraryName, Assembly assembly
4040
public static void Start( IntPtr nativeTable, int nativeTableSize, string basePath, string logPath )
4141
{
4242
Environment.SetEnvironmentVariable("SWIFTLY_MANAGED_ROOT", basePath);
43+
Environment.SetEnvironmentVariable("SWIFTLY_MANAGED_LOG", logPath);
4344
NativeBinding.BindNatives(nativeTable, nativeTableSize);
4445
NativeLibrary.SetDllImportResolver(typeof(NativeMethods).Assembly, SteamAPIDLLResolver);
4546

managed/src/SwiftlyS2.Core/Misc/SwiftlyLogger.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ public void Log<TState>( LogLevel logLevel, EventId eventId, TState state, Excep
5252
if (exception != null)
5353
{
5454
FileLogger.LogException(exception, exception.Message);
55+
// Temporarily use a reasonable width for exception output to avoid visual empty lines
56+
AnsiConsole.Profile.Width = 200;
5557
AnsiConsole.WriteException(exception);
58+
AnsiConsole.Profile.Width = 13337;
5659
}
5760

5861
AnsiConsole.Reset();

managed/src/SwiftlyS2.Core/Modules/Menus/MenuAPI.cs

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using SwiftlyS2.Shared;
33
using SwiftlyS2.Shared.Menus;
44
using SwiftlyS2.Core.Natives;
5+
using SwiftlyS2.Core.Menus.OptionsBase;
56
using SwiftlyS2.Shared.Players;
67
using SwiftlyS2.Shared.SchemaDefinitions;
78

@@ -41,6 +42,11 @@ internal sealed class MenuAPI : IMenuAPI, IDisposable
4142
/// </summary>
4243
public IMenuBuilderAPI? Builder { get; init; }
4344

45+
/// <summary>
46+
/// Gets or sets the default comment text to use when a menu option's Comment is not set.
47+
/// </summary>
48+
public string DefaultComment { get; set; } = $"Powered by <font color='#ff3c00'>❤️</font> {HtmlGradient.GenerateGradientText("SwiftlyS2", "#ffffff", "#96d5ff")}";
49+
4450
/// <summary>
4551
/// Gets or sets an object that contains data about this menu.
4652
/// </summary>
@@ -252,9 +258,10 @@ private void OnRender()
252258

253259
foreach (var option in options)
254260
{
255-
if (option is OptionsBase.MenuOptionBase optionBase)
261+
if (option is MenuOptionBase optionBase)
256262
{
257263
optionBase.UpdateDynamicText(now);
264+
optionBase.UpdateCustomAnimations(now);
258265
}
259266
}
260267

@@ -418,22 +425,47 @@ private string BuildMenuHtml( IPlayer player, IReadOnlyList<IMenuOption> visible
418425
option.GetDisplayText(player, 0)
419426
)));
420427

428+
var currentOption = visibleOptions.Count > 0 ? visibleOptions[arrowPosition] : null;
429+
var optionBase = currentOption as MenuOptionBase;
430+
431+
var comment = !string.IsNullOrWhiteSpace(optionBase?.Comment)
432+
? string.Concat(
433+
"<br>",
434+
guideLine,
435+
"<br>",
436+
$"<font class='fontSize-s'>{optionBase.Comment}</font><br>"
437+
)
438+
: string.Concat(
439+
"<br>",
440+
guideLine,
441+
"<br>",
442+
$"<font class='fontSize-s'>{DefaultComment}</font><br>"
443+
);
444+
445+
var claimInfo = optionBase?.InputClaimInfo ?? MenuInputClaimInfo.Empty;
446+
421447
var footerSection = Configuration.HideFooter ? string.Empty :
422448
core.MenusAPI.Configuration.InputMode switch {
423449
"wasd" => string.Concat(
424-
"<br>", guideLine, "<br>",
425450
"<font class='fontSize-s' color='#FFFFFF'>",
426451
$"<font color='{footerColor}'>Move:</font> W/S",
427-
$" | <font color='{footerColor}'>Use:</font> D",
428-
Configuration.DisableExit ? string.Empty : $" | <font color='{footerColor}'>Exit:</font> A",
452+
claimInfo.ClaimsUse
453+
? $" | <font color='{footerColor}'>{claimInfo.UseLabel ?? "Use"}:</font> D"
454+
: $" | <font color='{footerColor}'>Use:</font> D",
455+
claimInfo.ClaimsExit
456+
? $" | <font color='{footerColor}'>{claimInfo.ExitLabel ?? "Exit"}:</font> A"
457+
: (Configuration.DisableExit ? string.Empty : $" | <font color='{footerColor}'>Exit:</font> A"),
429458
"</font>"
430459
),
431460
_ => string.Concat(
432-
"<br>", guideLine, "<br>",
433461
"<font class='fontSize-s' color='#FFFFFF'>",
434462
$"<font color='{footerColor}'>Move:</font> {KeybindOverrides.Move?.ToString() ?? core.MenusAPI.Configuration.ButtonsScroll.ToUpper()}/{KeybindOverrides.MoveBack?.ToString() ?? core.MenusAPI.Configuration.ButtonsScrollBack.ToUpper()}",
435-
$" | <font color='{footerColor}'>Use:</font> {KeybindOverrides.Select?.ToString() ?? core.MenusAPI.Configuration.ButtonsUse.ToUpper()}",
436-
Configuration.DisableExit ? string.Empty : $" | <font color='{footerColor}'>Exit:</font> {KeybindOverrides.Exit?.ToString() ?? core.MenusAPI.Configuration.ButtonsExit.ToUpper()}",
463+
claimInfo.ClaimsUse
464+
? $" | <font color='{footerColor}'>{claimInfo.UseLabel ?? "Use"}:</font> {KeybindOverrides.Select?.ToString() ?? core.MenusAPI.Configuration.ButtonsUse.ToUpper()}"
465+
: $" | <font color='{footerColor}'>Use:</font> {KeybindOverrides.Select?.ToString() ?? core.MenusAPI.Configuration.ButtonsUse.ToUpper()}",
466+
claimInfo.ClaimsExit
467+
? $" | <font color='{footerColor}'>{claimInfo.ExitLabel ?? "Exit"}:</font> {KeybindOverrides.Exit?.ToString() ?? core.MenusAPI.Configuration.ButtonsExit.ToUpper()}"
468+
: (Configuration.DisableExit ? string.Empty : $" | <font color='{footerColor}'>Exit:</font> {KeybindOverrides.Exit?.ToString() ?? core.MenusAPI.Configuration.ButtonsExit.ToUpper()}"),
437469
"</font>"
438470
)
439471
};
@@ -443,6 +475,7 @@ private string BuildMenuHtml( IPlayer player, IReadOnlyList<IMenuOption> visible
443475
"<font color='#FFFFFF' class='fontSize-sm'>",
444476
menuItems,
445477
"</font>",
478+
comment,
446479
footerSection
447480
);
448481
}
@@ -499,7 +532,7 @@ public void ShowForPlayer( IPlayer player )
499532

500533
lock (optionsLock)
501534
{
502-
options.OfType<OptionsBase.MenuOptionBase>().ToList().ForEach(option => option.ResumeTextAnimation());
535+
options.OfType<MenuOptionBase>().ToList().ForEach(option => option.ResumeTextAnimation());
503536
}
504537
}
505538
}
@@ -556,7 +589,7 @@ public void HideForPlayer( IPlayer player )
556589

557590
lock (optionsLock)
558591
{
559-
options.OfType<OptionsBase.MenuOptionBase>().ToList().ForEach(option => option.PauseTextAnimation());
592+
options.OfType<MenuOptionBase>().ToList().ForEach(option => option.PauseTextAnimation());
560593
}
561594
}
562595
}
@@ -588,7 +621,7 @@ public void AddOption( IMenuOption option )
588621
// {
589622
// submenuOption.SubmenuRequested += OnSubmenuRequested;
590623
// }
591-
if (option is OptionsBase.MenuOptionBase baseOption)
624+
if (option is MenuOptionBase baseOption)
592625
{
593626
baseOption.Menu = this;
594627
}
@@ -667,17 +700,20 @@ public int GetCurrentOptionIndex( IPlayer player )
667700
// return selectedDisplayLine.TryGetValue(player, out var line) ? line : -1;
668701
// }
669702

670-
private static void SetFreezeState( IPlayer player, bool freeze )
703+
private void SetFreezeState( IPlayer player, bool freeze )
671704
{
672705
if (!player.IsValid || player.IsFakeClient || !(player.PlayerPawn?.IsValid ?? false))
673706
{
674707
return;
675708
}
676709

677-
var moveType = freeze ? MoveType_t.MOVETYPE_NONE : MoveType_t.MOVETYPE_WALK;
678-
player.PlayerPawn.MoveType = moveType;
679-
player.PlayerPawn.ActualMoveType = moveType;
680-
player.PlayerPawn.MoveTypeUpdated();
710+
core.Scheduler.NextTick(() =>
711+
{
712+
var moveType = freeze ? MoveType_t.MOVETYPE_NONE : MoveType_t.MOVETYPE_WALK;
713+
player.PlayerPawn.MoveType = moveType;
714+
player.PlayerPawn.ActualMoveType = moveType;
715+
player.PlayerPawn.MoveTypeUpdated();
716+
});
681717
}
682718

683719
// private ValueTask OnOptionClick( object? sender, MenuOptionClickEventArgs args )
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
namespace SwiftlyS2.Core.Menus;
2+
3+
[Flags]
4+
internal enum MenuInputClaim
5+
{
6+
/// <summary>
7+
/// No input claimed.
8+
/// </summary>
9+
None = 0,
10+
11+
/// <summary>
12+
/// Claims the Exit key (A in WASD mode).
13+
/// </summary>
14+
Exit = 1 << 0,
15+
16+
/// <summary>
17+
/// Claims the Use/Select key (D in WASD mode).
18+
/// </summary>
19+
Use = 1 << 1
20+
}
21+
22+
internal readonly record struct MenuInputClaimInfo
23+
{
24+
/// <summary>
25+
/// The input types being claimed.
26+
/// </summary>
27+
public MenuInputClaim Claims { get; init; }
28+
29+
/// <summary>
30+
/// The display label for the Exit key when claimed.
31+
/// Shown in the menu footer instead of "Exit".
32+
/// </summary>
33+
public string? ExitLabel { get; init; }
34+
35+
/// <summary>
36+
/// The display label for the Use key when claimed.
37+
/// Shown in the menu footer instead of "Use".
38+
/// </summary>
39+
public string? UseLabel { get; init; }
40+
41+
/// <summary>
42+
/// Returns true if any input is claimed.
43+
/// </summary>
44+
public bool HasClaims => Claims != MenuInputClaim.None;
45+
46+
/// <summary>
47+
/// Returns true if the Exit input is claimed.
48+
/// </summary>
49+
public bool ClaimsExit => (Claims & MenuInputClaim.Exit) != 0;
50+
51+
/// <summary>
52+
/// Returns true if the Use input is claimed.
53+
/// </summary>
54+
public bool ClaimsUse => (Claims & MenuInputClaim.Use) != 0;
55+
56+
/// <summary>
57+
/// Creates an empty claim info with no claims.
58+
/// </summary>
59+
public static MenuInputClaimInfo Empty => new() { Claims = MenuInputClaim.None };
60+
}

managed/src/SwiftlyS2.Core/Modules/Menus/MenuManagerAPI.cs

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Concurrent;
33
using SwiftlyS2.Shared;
44
using SwiftlyS2.Core.Natives;
5+
using SwiftlyS2.Core.Menus.OptionsBase;
56
using SwiftlyS2.Shared.Menus;
67
using SwiftlyS2.Shared.Events;
78
using SwiftlyS2.Shared.Sounds;
@@ -113,9 +114,13 @@ internal void OnClientKeyStateChanged( IOnClientKeyStateChangedEvent @event )
113114
}
114115

115116
var player = Core.PlayerManager.GetPlayer(@event.PlayerId);
116-
var menu = GetCurrentMenu(player);
117+
if (player == null || !player.IsValid || player.IsFakeClient || player.IsFakeClient || !@event.Pressed)
118+
{
119+
return;
120+
}
117121

118-
if (menu == null || !player.IsValid || player.IsFakeClient || !@event.Pressed)
122+
var menu = GetCurrentMenu(player);
123+
if (menu == null)
119124
{
120125
return;
121126
}
@@ -151,7 +156,22 @@ internal void OnClientKeyStateChanged( IOnClientKeyStateChangedEvent @event )
151156
}
152157
else if (exitKey.HasFlag(@event.Key.ToKeyBind()))
153158
{
154-
if (!menu.Configuration.DisableExit)
159+
var option = menu.GetCurrentOption(player);
160+
var optionBase = option as MenuOptionBase;
161+
var claimInfo = optionBase?.InputClaimInfo ?? MenuInputClaimInfo.Empty;
162+
163+
if (claimInfo.ClaimsExit && optionBase != null)
164+
{
165+
optionBase.OnClaimedExit(player);
166+
167+
if (menu.Configuration.PlaySound && option!.PlaySound)
168+
{
169+
useSound.Recipients.AddRecipient(@event.PlayerId);
170+
_ = useSound.Emit();
171+
useSound.Recipients.RemoveRecipient(@event.PlayerId);
172+
}
173+
}
174+
else if (!menu.Configuration.DisableExit)
155175
{
156176
CloseMenuForPlayerInternal(player, menu, true);
157177

@@ -166,7 +186,21 @@ internal void OnClientKeyStateChanged( IOnClientKeyStateChangedEvent @event )
166186
else if (useKey.HasFlag(@event.Key.ToKeyBind()))
167187
{
168188
var option = menu.GetCurrentOption(player);
169-
if (option != null && option.Enabled && option.GetEnabled(player) && option.IsClickTaskCompleted(player))
189+
var optionBase = option as MenuOptionBase;
190+
var claimInfo = optionBase?.InputClaimInfo ?? MenuInputClaimInfo.Empty;
191+
192+
if (claimInfo.ClaimsUse && optionBase != null)
193+
{
194+
optionBase.OnClaimedUse(player);
195+
196+
if (menu.Configuration.PlaySound && option!.PlaySound)
197+
{
198+
useSound.Recipients.AddRecipient(@event.PlayerId);
199+
_ = useSound.Emit();
200+
useSound.Recipients.RemoveRecipient(@event.PlayerId);
201+
}
202+
}
203+
else if (option != null && option.Enabled && option.GetEnabled(player) && option.IsClickTaskCompleted(player))
170204
{
171205
_ = Task.Run(async () => await option.OnClickAsync(player));
172206

@@ -205,7 +239,22 @@ internal void OnClientKeyStateChanged( IOnClientKeyStateChangedEvent @event )
205239
}
206240
else if (KeyBind.A.HasFlag(@event.Key.ToKeyBind()))
207241
{
208-
if (!menu.Configuration.DisableExit)
242+
var option = menu.GetCurrentOption(player);
243+
var optionBase = option as MenuOptionBase;
244+
var claimInfo = optionBase?.InputClaimInfo ?? MenuInputClaimInfo.Empty;
245+
246+
if (claimInfo.ClaimsExit && optionBase != null)
247+
{
248+
optionBase.OnClaimedExit(player);
249+
250+
if (menu.Configuration.PlaySound && option!.PlaySound)
251+
{
252+
useSound.Recipients.AddRecipient(@event.PlayerId);
253+
_ = useSound.Emit();
254+
useSound.Recipients.RemoveRecipient(@event.PlayerId);
255+
}
256+
}
257+
else if (!menu.Configuration.DisableExit)
209258
{
210259
CloseMenuForPlayerInternal(player, menu, true);
211260

@@ -220,7 +269,21 @@ internal void OnClientKeyStateChanged( IOnClientKeyStateChangedEvent @event )
220269
else if (KeyBind.D.HasFlag(@event.Key.ToKeyBind()))
221270
{
222271
var option = menu.GetCurrentOption(player);
223-
if (option != null && option.Enabled && option.GetEnabled(player) && option.IsClickTaskCompleted(player))
272+
var optionBase = option as MenuOptionBase;
273+
var claimInfo = optionBase?.InputClaimInfo ?? MenuInputClaimInfo.Empty;
274+
275+
if (claimInfo.ClaimsUse && optionBase != null)
276+
{
277+
optionBase.OnClaimedUse(player);
278+
279+
if (menu.Configuration.PlaySound && option!.PlaySound)
280+
{
281+
useSound.Recipients.AddRecipient(@event.PlayerId);
282+
_ = useSound.Emit();
283+
useSound.Recipients.RemoveRecipient(@event.PlayerId);
284+
}
285+
}
286+
else if (option != null && option.Enabled && option.GetEnabled(player) && option.IsClickTaskCompleted(player))
224287
{
225288
_ = Task.Run(async () => await option.OnClickAsync(player));
226289

@@ -376,8 +439,11 @@ public void CloseAllMenus()
376439
while (currentMenu != null)
377440
{
378441
var player = Core.PlayerManager.GetPlayer(kvp.Key);
379-
currentMenu.HideForPlayer(player);
380-
MenuClosed?.Invoke(this, new MenuManagerEventArgs { Player = player, Menu = currentMenu });
442+
if (player?.IsValid ?? false)
443+
{
444+
currentMenu.HideForPlayer(player);
445+
MenuClosed?.Invoke(this, new MenuManagerEventArgs { Player = player, Menu = currentMenu });
446+
}
381447
currentMenu = currentMenu.Parent.ParentMenu;
382448
}
383449
_ = openMenus.TryRemove(kvp.Key, out _);

0 commit comments

Comments
 (0)