diff --git a/.sbproj b/.sbproj index f6de3ac..1a078c6 100644 --- a/.sbproj +++ b/.sbproj @@ -37,7 +37,20 @@ "GameSettings": {}, "Addons": "", "PreLaunchCommand": "", - "PostLaunchCommand": "" + "PostLaunchCommand": "lucker_minigames_per_round 1" + } + ], + "PackageSettings": [ + { + "DisplayType": "Integer", + "Choices": [], + "ConVarName": "lucker_minigames_per_round", + "DisplayName": "Minigames Per Round", + "DefaultValue": "1", + "Description": "The number of minigames played per round", + "Group": "Other", + "Minimum": 1, + "Maximum": 12 } ] } diff --git a/code/Entities/Lucker.cs b/code/Entities/Lucker.cs index 3394cd4..996c762 100644 --- a/code/Entities/Lucker.cs +++ b/code/Entities/Lucker.cs @@ -1,22 +1,39 @@ using LuckerGame.Components.Lucker.Cameras; +using LuckerGame.EntityComponents.Lucker; using LuckerGame.Events; using Sandbox; namespace LuckerGame.Entities; /// -/// Represents a Player. +/// Represents a person playing the game. /// This could belong to a Client or a Bot and represents a common entity to operate on for games and keeping score /// public partial class Lucker : Entity { /// - /// The entity this Player currently controls + /// The entity this lucker controls. This value is networked and should be accessed through . /// - public Entity Pawn { get; set; } + [Net] private Entity InternalPawn { get; set; } /// - /// Before the round has started, this player indicated they were ready + /// Accesses or sets the entity this lucker currently controls + /// + public Entity Pawn + { + get => InternalPawn; + set + { + InternalPawn = value; + if ( value != null ) + { + value.Owner = this; + } + } + } + + /// + /// Before the round has started, this lucker indicated they were ready /// [Net] public bool Ready { get; set; } @@ -24,21 +41,24 @@ public partial class Lucker : Entity /// This Lucker's camera /// [BindComponent] public AbstractCamera Camera { get; } + + [BindComponent] public LuckerStats Stats { get; } /// - /// Creates and properly sets up a Player entity for a given client + /// Creates and properly sets up a entity for a given client /// - /// the client to own the player - /// the newly created player + /// the client to own the lucker + /// the newly created lucker public static Lucker CreateLuckerForClient( IClient client ) { - var player = new Lucker(); - client.Pawn = player; - player.Owner = client as Entity; - player.Name = client.Name; - var camera = player.Components.Create(); + var lucker = new Lucker(); + client.Pawn = lucker; + lucker.Owner = client as Entity; + lucker.Name = client.Name; + lucker.Components.Create(); + lucker.Components.Create(); - return player; + return lucker; } /// @@ -49,12 +69,12 @@ public partial class Lucker : Entity public static void ReadyUpCommand(bool readyState) { var client = ConsoleSystem.Caller; - var player = client.Pawn as Lucker; - player.SetReady( readyState ); + var lucker = client.Pawn as Lucker; + lucker.SetReady( readyState ); } /// - /// Sets this player's ready state + /// Sets this lucker's ready state /// /// the ready state being set public void SetReady(bool ready) @@ -62,7 +82,7 @@ public partial class Lucker : Entity Ready = ready; if ( Game.IsServer ) { - Event.Run( LuckerEvent.PlayerReady, this, ready ); + Event.Run( LuckerEvent.LuckerReady, this, ready ); } } diff --git a/code/Entities/MinigameManager.cs b/code/Entities/MinigameManager.cs index b3b8fa5..d2ee6d8 100644 --- a/code/Entities/MinigameManager.cs +++ b/code/Entities/MinigameManager.cs @@ -12,8 +12,20 @@ namespace LuckerGame.Entities; /// public partial class MinigameManager : Entity { + /// + /// The currently loaded minigame + /// [Net] public Minigame LoadedMinigame { get; private set; } - private List AvailableMinigames { get; set; } + + /// + /// A cached list of available minigames. Gets reloaded on a hotreload + /// + private List AvailableMinigames { get; set; } + + /// + /// The luckers involved in the current minigame + /// + private List InvolvedLuckers { get; set; } public override void Spawn() { @@ -21,13 +33,17 @@ public partial class MinigameManager : Entity FindMinigames(); } - public void StartMinigame(List players, string minigameName = null) + public void StartMinigame(List luckers, string minigameName = null) { + InvolvedLuckers = luckers.ToList(); if (CheckForMinigames()) { - LoadedMinigame = string.IsNullOrEmpty( minigameName ) ? AvailableMinigames.OrderBy( _ => Guid.NewGuid() ).FirstOrDefault() : TypeLibrary.Create( minigameName ); + LoadedMinigame = string.IsNullOrEmpty( minigameName ) + ? TypeLibrary.Create( AvailableMinigames.OrderBy( _ => Guid.NewGuid() ).FirstOrDefault() + .TargetType ) + : TypeLibrary.Create( minigameName ); ChatBox.AddInformation( To.Everyone, $"Starting {LoadedMinigame.Name}" ); - LoadedMinigame.Initialize( players ); + LoadedMinigame.Initialize( luckers ); } } @@ -46,7 +62,7 @@ public partial class MinigameManager : Entity { AvailableMinigames = TypeLibrary.GetTypes() .Where( type => !type.IsAbstract && !type.IsInterface ) - .Select( td => TypeLibrary.Create( td.TargetType ) ).ToList(); + .ToList(); } [Event.Hotload] @@ -54,13 +70,49 @@ public partial class MinigameManager : Entity { FindMinigames(); } + + /// + /// Goes through the luckers included in the loaded minigame and deletes and nulls out any pawns assigned to them + /// + private void CleanupLuckerPawns() + { + if ( LoadedMinigame is not { IsValid: true } || InvolvedLuckers == null) + { + Log.Warning( "Attempted to clean up players without a minigame loaded!" ); + return; + } + InvolvedLuckers.ForEach( lucker => + { + lucker.Pawn?.Delete(); + lucker.Pawn = null; + } ); + } - public void Tick() + /// + /// Called once per tick by the RoundManager. Ticks any running minigame. + /// + /// true if the current minigame has ended, else false + public bool Tick() { if ( LoadedMinigame is not { IsValid: true } ) { - return; + return false; } - LoadedMinigame.Tick(); + var ended = LoadedMinigame.Tick(); + if ( !ended ) + { + return false; + } + EndMinigame(); + return true; + } + + private void EndMinigame() + { + LoadedMinigame.Cleanup(); + CleanupLuckerPawns(); + LoadedMinigame.Delete(); + LoadedMinigame = null; + InvolvedLuckers = null; } } diff --git a/code/Entities/RoundManager.cs b/code/Entities/RoundManager.cs index e4f3780..00ba688 100644 --- a/code/Entities/RoundManager.cs +++ b/code/Entities/RoundManager.cs @@ -47,11 +47,21 @@ public partial class RoundManager : Entity #region In Progress State - private const int MinigamesPerRound = 1; + /// + /// The number of minigames that should be played per round, settable via a convar + /// + [ConVar.Replicated("lucker_minigames_per_round")] + private static int MinigamesPerRound { get; set; } - private int MinigamesLeftInRound { get; set; } + /// + /// The number of minigames left in the current round + /// + public int MinigamesLeftInRound { get; set; } - private List Players { get; set; } + /// + /// The luckers playing in the current round + /// + private List Luckers { get; set; } #endregion /// @@ -76,16 +86,29 @@ public partial class RoundManager : Entity if ( RoundState == RoundState.InProgress ) { - MinigameManager.Tick(); + var ended = MinigameManager.Tick(); + if ( ended ) + { + MinigamesLeftInRound--; + if ( MinigamesLeftInRound > 0 ) + { + MinigameManager.StartMinigame( Luckers ); + } + else + { + RoundState = RoundState.NotStarted; + } + } } } /// - /// Is triggered whenever a player readies up + /// Is triggered whenever a lucker readies up or readies down /// - /// the player that readied up, discarded - [LuckerEvent.PlayerReady] - public void HandlePlayerReady( Lucker readyLucker, bool ready ) + /// the lucker that readied up + /// the lucker's ready state + [LuckerEvent.LuckerReady] + public void HandleLuckerReady( Lucker readyLucker, bool ready ) { if ( RoundState != RoundState.NotStarted && RoundState != RoundState.StartCountdown ) { @@ -94,9 +117,9 @@ public partial class RoundManager : Entity Log.Info( $"{readyLucker.Client.Name} set ready to {ready}" ); var message = $"{readyLucker.Client.Name} is {(ready ? "now ready." : "no longer ready.")}"; ChatBox.AddInformation( To.Everyone, message ); - var players = All.OfType().ToList(); - var readiedCount = players.Count( player => player.Ready ); - var totalCount = players.Count; + var luckers = All.OfType().ToList(); + var readiedCount = luckers.Count( lucker => lucker.Ready ); + var totalCount = luckers.Count; if ( (float)readiedCount / totalCount > RequiredReadyPercent && RoundState == RoundState.NotStarted ) { Log.Info( "Countdown started" ); @@ -119,8 +142,13 @@ public partial class RoundManager : Entity } RoundState = RoundState.InProgress; - Players = All.OfType().ToList(); - MinigameManager.StartMinigame( Players, minigameName ); + Luckers = All.OfType().ToList(); + Luckers.ForEach( lucker => + { + lucker.Ready = false; + } ); + MinigamesLeftInRound = MinigamesPerRound; + MinigameManager.StartMinigame( Luckers, minigameName ); } [ConCmd.Server( "start_round" )] diff --git a/code/Entities/Weapons/Weapon.cs b/code/Entities/Weapons/Weapon.cs index 95ec514..e6658b0 100644 --- a/code/Entities/Weapons/Weapon.cs +++ b/code/Entities/Weapons/Weapon.cs @@ -76,7 +76,7 @@ public partial class Weapon : AnimatedEntity } /// - /// Called when the weapon is either removed from the player, or holstered. + /// Called when the weapon is either removed from the pawn, or holstered. /// public void OnHolster() { diff --git a/code/EntityComponents/Lucker/LuckerClientInput.cs b/code/EntityComponents/Lucker/LuckerClientInput.cs deleted file mode 100644 index 0f0575d..0000000 --- a/code/EntityComponents/Lucker/LuckerClientInput.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Sandbox; - -namespace LuckerGame.EntityComponents.Lucker; - -/// -/// A component for capturing and passing around a client's input for an attached Lucker -/// -public class LuckerClientInput : EntityComponent -{ - [ClientInput] - public Vector3 InputDirection { get; set; } - - [ClientInput] - public Angles ViewAngles { get; set; } -} diff --git a/code/EntityComponents/Lucker/LuckerStats.cs b/code/EntityComponents/Lucker/LuckerStats.cs new file mode 100644 index 0000000..3e801ca --- /dev/null +++ b/code/EntityComponents/Lucker/LuckerStats.cs @@ -0,0 +1,48 @@ +using Sandbox; +using Sandbox.UI; + +namespace LuckerGame.EntityComponents.Lucker; + +/// +/// Handles the stats associated with a lucker during a lobby. +/// +public partial class LuckerStats : EntityComponent, ISingletonComponent +{ + /// + /// The lucker's current score. + /// + [Net] private long Score { get; set; } + + /// + /// Adds points to this lucker's score + /// + /// points to add (or remove if negative) + public void AddScore( long points ) + { + Score += points; + if ( points == 0 ) + { + return; + } + + var message = $"{Entity.Name} {(points > 0 ? "gained" : "lost")} {points} points!"; + ChatBox.AddInformation( To.Everyone, message ); + } + + /// + /// Resets this lucker's score to zero + /// + public void ResetScore() + { + Score = 0; + } + + /// + /// Gets this lucker's current score + /// + /// this lucker's current score + public long GetScore() + { + return Score; + } +} diff --git a/code/Events/PlayerReadyEvent.cs b/code/Events/PlayerReadyEvent.cs index 2bfde23..d587f9c 100644 --- a/code/Events/PlayerReadyEvent.cs +++ b/code/Events/PlayerReadyEvent.cs @@ -4,16 +4,16 @@ namespace LuckerGame.Events; public static partial class LuckerEvent { - public const string PlayerReady = "lucker.playerReady"; + public const string LuckerReady = "lucker.luckerReady"; /// - /// Event is run on the server whenever a player changes ready state - /// The event handler is given the player that readied up and their new ready state + /// Event is run on the server whenever a lucker changes ready state + /// The event handler is given the lucker that readied up and their new ready state /// [MethodArguments(typeof(Entities.Lucker), typeof(bool))] - public class PlayerReadyAttribute : EventAttribute + public class LuckerReadyAttribute : EventAttribute { - public PlayerReadyAttribute() : base(PlayerReady) + public LuckerReadyAttribute() : base(LuckerReady) { } } diff --git a/code/Minigames/Minigame.cs b/code/Minigames/Minigame.cs index c526597..200691a 100644 --- a/code/Minigames/Minigame.cs +++ b/code/Minigames/Minigame.cs @@ -15,13 +15,14 @@ public abstract class Minigame : Entity /// /// Initializes the minigame with a list of luckers playing it. /// - /// the players who made it into the minigame - public abstract void Initialize(List players); + /// the luckers who made it into the minigame + public abstract void Initialize(List luckers); /// /// Once a minigame is loaded and initialized, this method is called once per server tick. /// - public abstract void Tick(); + /// true if the minigame has ended, false otherwise + public abstract bool Tick(); /// /// Cleans up any entities and components created by this minigame. diff --git a/code/Minigames/RussianRoulette/RussianPistol.cs b/code/Minigames/RussianRoulette/RussianPistol.cs index 0b314f2..e64dfc1 100644 --- a/code/Minigames/RussianRoulette/RussianPistol.cs +++ b/code/Minigames/RussianRoulette/RussianPistol.cs @@ -41,7 +41,7 @@ public partial class RussianPistol : Weapon } else { - Pawn.PlaySound( "denyundo" ); + Pawn.PlaySound( "player_use_fail" ); } Ammo--; } diff --git a/code/Minigames/RussianRoulette/RussianRouletteMinigame.cs b/code/Minigames/RussianRoulette/RussianRouletteMinigame.cs index 55ab89f..e9a0a33 100644 --- a/code/Minigames/RussianRoulette/RussianRouletteMinigame.cs +++ b/code/Minigames/RussianRoulette/RussianRouletteMinigame.cs @@ -14,87 +14,102 @@ namespace LuckerGame.Minigames.RussianRoulette; public class RussianRouletteMinigame : Minigame { public override string Name => "Russian Roulette"; - private List Players { get; set; } + private List Luckers { get; set; } private Pawn Shooter { get; set; } + private const float ShooterDistance = 80f; private const float TimeBetweenShots = 7f; + private const float TimeBetweenDeathAndEnd = 5f; + private const string ShooterName = "The Russian"; private int Taunted = 0; + private Pawn ShooterTarget; - private List DeadVictims => Players - .Select( player => player.Pawn as Pawn ) - .Where( pawn => !pawn.IsValid || pawn.LifeState != LifeState.Alive ) + private List DeadVictims => Luckers + .Select( lucker => lucker.Pawn as Pawn ) + .Where( pawn => pawn is not { IsValid: true } || pawn.LifeState != LifeState.Alive ) .ToList(); private TimeSince TimeSinceShot { get; set; } + private TimeSince TimeSinceDeadVictim { get; set; } - public override void Initialize( List players ) + public override void Initialize( List luckers ) { - Players = players; + Luckers = luckers; Shooter = new Pawn(); + Shooter.Name = ShooterName; var shooterInventory = Shooter.Components.Create(); shooterInventory.AddWeapon( new RussianPistol() ); - // Setup cameras for players - Players.ForEach( player => + // Setup cameras for luckers + Luckers.ForEach( lucker => { - player.Components.Create(); - player.Position = Shooter.Position; + lucker.Components.Create(); + lucker.Position = Shooter.Position; } ); - Players.Select((player, i) => (Player: player, Index: i) ).ToList().ForEach( pair => + Luckers.Select((lucker, i) => (Lucker: lucker, Index: i) ).ToList().ForEach( pair => { - var player = pair.Player; + var lucker = pair.Lucker; var index = pair.Index; var pawn = new Pawn(); - pawn.Name = player.Name; + pawn.Name = lucker.Name; pawn.Tags.Add( "victim" ); pawn.Health = 1; - player.Pawn = pawn; - pawn.DressFromClient( player.Client ); + lucker.Pawn = pawn; + pawn.DressFromClient( lucker.Client ); var pawnOffset = ShooterDistance * (index % 2 == 0 ? Vector3.Forward : Vector3.Right) * (index % 4 >= 2 ? -1 : 1); - player.Pawn.Position = Shooter.Position + pawnOffset; + lucker.Pawn.Position = Shooter.Position + pawnOffset; pawn.LookAt(Shooter.Position); } ); TimeSinceShot = 0; + Taunted = 0; } - public override void Tick() + public override bool Tick() { + // Someone is dead, we're getting ready to end if ( DeadVictims.Any() ) { if ( Taunted != int.MaxValue ) { - ChatBox.AddChatEntry( To.Everyone, "Shooter", "Heh, nothing personnel, kid." ); + ChatBox.AddChatEntry( To.Everyone, Shooter.Name, "Heh, nothing personnel, kid." ); Taunted = int.MaxValue; + TimeSinceDeadVictim = 0; + } + else if(TimeSinceDeadVictim > TimeBetweenDeathAndEnd) + { + return true; } - return; } - if ( TimeSinceShot > TimeBetweenShots ) + else if ( TimeSinceShot > TimeBetweenShots ) { TimeSinceShot = 0; Taunted = 0; Shooter.Inventory.ActiveWeapon.PrimaryAttack(); if ( !DeadVictims.Any() ) { - ChatBox.AddChatEntry( To.Everyone, "Shooter", "Fucking lag..." ); + ChatBox.AddChatEntry( To.Everyone, Shooter.Name, "Fucking lag..." ); } } else if ( TimeSinceShot > TimeBetweenShots * .8f && Taunted == 1) { - var victim = Players.Select( player => player.Pawn as Pawn ) + ShooterTarget = Luckers.Select( lucker => lucker.Pawn as Pawn ) .OrderBy( _ => Guid.NewGuid() ) .FirstOrDefault(); - Shooter.LookAt( victim.Position ); - ChatBox.AddChatEntry( To.Everyone, "Shooter", $"I'm gonna eat you up, {victim.Name}" ); + Shooter.LookAt( ShooterTarget.Position ); + var chance = 1f / Shooter.Inventory.ActiveWeapon.Ammo; + ChatBox.AddChatEntry( To.Everyone, Shooter.Name, $"Good luck, {ShooterTarget.Name}! You have a {chance:P0} chance to die!" ); Taunted++; } else if ( TimeSinceShot > TimeBetweenShots / 2 && Taunted == 0) { - ChatBox.AddChatEntry( To.Everyone, "Shooter", "Im gettin' ready!" ); + ChatBox.AddChatEntry( To.Everyone, Shooter.Name, "Im gettin' ready!" ); Taunted++; } + + return false; } public override void Cleanup() diff --git a/code/UI/HudComponents/CameraCursor.razor b/code/UI/HudComponents/CameraCursor.razor index e32e736..93284e7 100644 --- a/code/UI/HudComponents/CameraCursor.razor +++ b/code/UI/HudComponents/CameraCursor.razor @@ -5,10 +5,13 @@ @attribute [StyleSheet] @inherits Panel -@if (ShouldShowCursor) -{ - -} + + @if (ShouldShowCursor) + { +
+ } + + @code { diff --git a/code/UI/HudComponents/CameraCursor.razor.scss b/code/UI/HudComponents/CameraCursor.razor.scss index 82e3b95..a8d2142 100644 --- a/code/UI/HudComponents/CameraCursor.razor.scss +++ b/code/UI/HudComponents/CameraCursor.razor.scss @@ -1,3 +1,5 @@ CameraCursor { - pointer-events: all; + .camera-cursor { + pointer-events: all; + } } \ No newline at end of file diff --git a/code/UI/HudComponents/Scoreboard.razor b/code/UI/HudComponents/Scoreboard.razor index 113d83c..ebf704c 100644 --- a/code/UI/HudComponents/Scoreboard.razor +++ b/code/UI/HudComponents/Scoreboard.razor @@ -10,9 +10,9 @@
- @foreach (var player in Luckers) + @foreach (var lucker in Luckers) { - + }
@@ -23,7 +23,7 @@ protected override int BuildHash() { - return HashCode.Combine(Luckers.Select(player => player.Name).ToList()); + return HashCode.Combine(Luckers.Select(lucker => lucker.Name).ToList()); } } \ No newline at end of file diff --git a/code/UI/MainMenu/ActiveLobby.razor b/code/UI/MainMenu/ActiveLobby.razor new file mode 100644 index 0000000..9f5a775 --- /dev/null +++ b/code/UI/MainMenu/ActiveLobby.razor @@ -0,0 +1,121 @@ +@using Sandbox; +@using System; +@using System.Linq; +@using System.Threading.Tasks; +@using Sandbox.Menu; +@using Sandbox.UI; + +@namespace LuckerGame.UI.MainMenu +@inherits Panel + + + + + @if ( Lobby == null ) + { +
+ Loading... + + Return +
+ } + else + { + +
+
+ + +
+ @foreach (var member in Lobby.Members) + { + + } +
+
+ + @if ( Lobby.Owner.IsMe ) + { +
+ @if ( MaxPlayersSupported > 1 ) + { + + + + + + + } + + + + + + + +
+ } + +
+ + + Leave Lobby + + Start + + Return +
+ } + + +@code +{ + Friend Owner => Lobby.Owner; + ILobby Lobby => Game.Menu.Lobby; + + int MaxPlayersSupported { get; set; } = 1; + Package MapPackage { get; set; } + + void OnMapClicked() + { + Game.Overlay.ShowPackageSelector( "type:map sort:popular", OnMapSelected ); + StateHasChanged(); + } + + void OnMapSelected( Package map ) + { + MapPackage = map; + Game.Menu.Lobby.Map = map.FullIdent; + + StateHasChanged(); + } + + public void LeaveLobby() + { + Lobby?.Leave(); + + this.Navigate( "/lobby/list" ); + } + + async Task Start() + { + await Game.Menu.StartServerAsync( Game.Menu.Lobby.MaxMembers, $"{Game.Menu.Lobby.Owner.Name}'s game", Game.Menu.Lobby.Map ); + } + + async void FetchPackage() + { + MapPackage = await Package.FetchAsync( Game.Menu.Lobby?.Map ?? "facepunch.square", true ); + } + + protected override void OnAfterTreeRender( bool firstTime ) + { + FetchPackage(); + } + + protected override void OnParametersSet() + { + MaxPlayersSupported = Game.Menu.Package.GetMeta( "MaxPlayers", 1 ); + } +} \ No newline at end of file diff --git a/code/UI/MainMenu/FrontPage.razor b/code/UI/MainMenu/FrontPage.razor new file mode 100644 index 0000000..8d9d690 --- /dev/null +++ b/code/UI/MainMenu/FrontPage.razor @@ -0,0 +1,40 @@ +@using Sandbox; +@using System.Linq; +@using System.Threading.Tasks; +@using Sandbox.Menu; +@using Sandbox.UI; +@namespace LuckerGame.UI.MainMenu + + +
+ @Game.Menu.Package.Title +
+ +
+ @if (Game.InGame) + { + Leave + } + else + { + Play + + Lobbies + + @if ( Game.Menu.Package.SupportsSavedGames && Game.Menu.SavedGames.Any()) + { + Load Save + } + } + + Quit +
+
+ +@code +{ + void LeaveGame() + { + Game.Menu.LeaveServer( "Leaving" ); + } +} \ No newline at end of file diff --git a/code/UI/MainMenu/LoadingScreen.razor b/code/UI/MainMenu/LoadingScreen.razor new file mode 100644 index 0000000..2ec5690 --- /dev/null +++ b/code/UI/MainMenu/LoadingScreen.razor @@ -0,0 +1,31 @@ +@using Sandbox; +@using Sandbox.UI; +@using System.Linq; +@using System.Threading.Tasks; +@using Sandbox.Menu; + +@inherits RootPanel +@implements Sandbox.Menu.ILoadingScreenPanel +@attribute [StyleSheet] +@namespace LuckerGame.UI.MainMenu + + +
+ +
+ + + + +@code +{ + public LoadingProgress Progress; + + public void OnLoadingProgress( LoadingProgress progress ) + { + Progress = progress; + StateHasChanged(); + } +} diff --git a/code/UI/MainMenu/LoadingScreen.razor.scss b/code/UI/MainMenu/LoadingScreen.razor.scss new file mode 100644 index 0000000..6c5893a --- /dev/null +++ b/code/UI/MainMenu/LoadingScreen.razor.scss @@ -0,0 +1,50 @@ + +LoadingScreen +{ + background-color: #262934; + padding: 128px 128px; + opacity: 1; + flex-direction: column; + font-size: 25px; + width: 100%; + height: 100%; + position: absolute; + transition: all 0.3s ease-out; + color: rgba( white, 0.8 ); + + .background + { + position: absolute; + width: 100%; + height: 100%; + background-image: url( https://files.facepunch.com/tony/1b1311b1/boxes.webm ); + opacity: 0.2; + background-size: contain; + filter: blur( 20px ); + mask: linear-gradient( 45deg, white, white, black ); + mask-scope: filter; + } + + &:intro + { + opacity: 0; + transform: scaleX( 1.1 ); + } + + .game-title + { + font-family: Roboto; + font-weight: 700; + font-size: 70px; + color: rgba( white, 1 ); + } + + .controls + { + flex-direction: column; + gap: 50px; + align-items: flex-start; + font-family: Roboto; + text-transform: uppercase; + } +} diff --git a/code/UI/MainMenu/LobbyBrowser.razor b/code/UI/MainMenu/LobbyBrowser.razor new file mode 100644 index 0000000..fae7358 --- /dev/null +++ b/code/UI/MainMenu/LobbyBrowser.razor @@ -0,0 +1,66 @@ +@using Sandbox; +@using System; +@using System.Linq; +@using System.Threading.Tasks; +@using Sandbox.Menu; +@using Sandbox.UI; +@namespace LuckerGame.UI.MainMenu +@inherits Panel + + + + +
+
+ + refresh +
+ + + + + + +@code +{ + public async void CreateLobbyAsync() + { + await Game.Menu.CreateLobbyAsync( 64, "game", true ); + Game.Menu.Lobby.Map = "facepunch.square"; + this.Navigate( "/lobby/active" ); + } + + public async void JoinLobby( ILobby lobby ) + { + if ( lobby == null ) return; + + // don't exist in two lobbies at once + Game.Menu.Lobby?.Leave(); + + await lobby.JoinAsync(); + this.Navigate( "/lobby/active" ); + } + + public async void Refresh() + { + await Game.Menu.QueryLobbiesAsync( null, 1 ); + + StateHasChanged(); + } + + protected override int BuildHash() + { + return HashCode.Combine( Game.Menu.Lobbies.Count() ); + } +} \ No newline at end of file diff --git a/code/UI/MainMenu/MainMenu.razor b/code/UI/MainMenu/MainMenu.razor new file mode 100644 index 0000000..1b9f997 --- /dev/null +++ b/code/UI/MainMenu/MainMenu.razor @@ -0,0 +1,40 @@ +@using System; +@using Sandbox; +@using Sandbox.UI; + +@inherits Sandbox.UI.NavHostPanel +@implements Sandbox.Menu.IGameMenuPanel +@attribute [StyleSheet] +@namespace LuckerGame.UI.MainMenu + + +
+