diff --git a/.sbproj b/.sbproj index f6de3ac..0e6ffda 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": "0", + "Description": "The number of minigames played per round", + "Group": "Other", + "Minimum": 1, + "Maximum": 12 } ] } diff --git a/code/Entities/MinigameManager.cs b/code/Entities/MinigameManager.cs index 111cd39..a9f6650 100644 --- a/code/Entities/MinigameManager.cs +++ b/code/Entities/MinigameManager.cs @@ -57,8 +57,16 @@ public partial class MinigameManager : Entity FindMinigames(); } - private void cleanupPlayerPawns() + /// + /// Goes through the players included in the loaded minigame and deletes and nulls out any pawns assigned to them + /// + private void CleanupPlayerPawns() { + if ( LoadedMinigame is not { IsValid: true } || InvolvedPlayers == null) + { + Log.Warning( "Attempted to clean up players without a minigame loaded!" ); + return; + } InvolvedPlayers.ForEach( player => { player.Pawn.Delete(); diff --git a/code/Entities/RoundManager.cs b/code/Entities/RoundManager.cs index 65b21a3..5a384b6 100644 --- a/code/Entities/RoundManager.cs +++ b/code/Entities/RoundManager.cs @@ -49,7 +49,8 @@ public partial class RoundManager : Entity private const int MinigamesPerRound = 1; - private int MinigamesLeftInRound { get; set; } + [ConVar.Replicated("lucker_minigames_per_round")] + public static int MinigamesLeftInRound { get; set; } private List Players { get; set; } #endregion 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 + + + + @Game.Menu.Package.Title + + + @if ( Lobby == null ) + { + + Loading... + + Return + + } + else + { + + + + Members (@Lobby.MemberCount/@Lobby.MaxMembers) + + + @foreach (var member in Lobby.Members) + { + + } + + + + @if ( Lobby.Owner.IsMe ) + { + + @if ( MaxPlayersSupported > 1 ) + { + + Maximum Players + + + + + } + + + Map + + + + + + } + + + + + 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 + + + + + + + + @( Progress.Title ?? "Loading..." ) + + + +@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 + + + + @Game.Menu.Package.Title + + + + + Showing @Game.Menu.Lobbies.Count() @(Game.Menu.Lobbies.Count() == 1 ? "lobby" : "lobbies") + refresh + + + + @foreach ( var lobby in Game.Menu.Lobbies ) + { + JoinLobby( lobby ) ) >@(lobby.Owner.Name)'s lobby (@lobby.MemberCount/@lobby.MaxMembers) + } + + + + + Create Lobby + Return + + + +@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 + + + + + + +@code +{ + public MainMenu() + { + DefaultUrl = "/"; + + AddDestination( "/", typeof( FrontPage ) ); + AddDestination( "/setup", typeof( SetupGame ) ); + + AddDestination( "/lobby/list", typeof( LobbyBrowser ) ); + AddDestination( "/lobby/active", typeof( ActiveLobby ) ); + + BindClass( "ingame", () => Game.InGame ); + } + + [GameEvent.Menu.ServerJoined] + public void OnServerJoined() => Navigate( "/" ); + + [GameEvent.Menu.ServerLeave] + public void OnServerLeave() => Navigate ("/" ); + + protected override int BuildHash() + { + return HashCode.Combine( Game.InGame, Game.Menu.Lobby, Game.Menu.Lobby?.Map ); + } +} diff --git a/code/UI/MainMenu/MainMenu.razor.scss b/code/UI/MainMenu/MainMenu.razor.scss new file mode 100644 index 0000000..745df88 --- /dev/null +++ b/code/UI/MainMenu/MainMenu.razor.scss @@ -0,0 +1,195 @@ + +MainMenu +{ + 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: cover; + filter: blur( 20px ); + mask: linear-gradient( 45deg, white, white, black ); + mask-scope: filter; + background-repeat: no-repeat; + } + + &:intro + { + opacity: 0; + transform: scaleX( 1.1 ); + } + + &.ingame + { + background-color: #262934ee; + backdrop-filter: blur(10px); + + .background + { + opacity: 0; + } + } + + .scroll + { + flex-direction: column; + max-height: 386px; + overflow: scroll; + gap: 50px; + } + + .spacer + { + height: 1px; + background-image: linear-gradient( to right, rgba( white, 0.4 ), rgba( white, 0 ) ); + width: 512px; + margin: 16px 0px; + } + + .game-title + { + font-family: Roboto Condensed; + font-weight: 700; + font-size: 70px; + color: rgba( white, 1 ); + padding-bottom: 64px; + } + + .col + { + flex-direction: column; + gap: 16px; + } + + .controls + { + flex-direction: column; + gap: 50px; + align-items: flex-start; + text-transform: uppercase; + + a, .button + { + font-family: Roboto; + + &:hover + { + color: rgba( white, 1 ); + font-weight: 900; + sound-in: ui.button.over; + cursor: pointer; + } + + &:active + { + sound-in: ui.button.press; + } + } + + .span + { + gap: 128px; + } + } + + .navigator-canvas + { + flex-direction: column; + height: 100%; + flex-grow: 1; + flex-shrink: 0; + } + + .navigator-body + { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + flex-direction: column; + justify-content: center; + transition: opacity 0.15s ease-out; + + &.hidden + { + opacity: 0; + } + } +} + +FormGroup +{ + flex-direction: column; + min-width: 300px; + gap:16px; +} + +SlimPackageCard +{ + flex-direction: row; + gap: 12px; + align-items: center; + + > i + { + &:hover + { + cursor: pointer; + color: white; + transform: scale( 1.1 ); + sound-in: ui.button.over; + } + + &:active + { + sound-in: ui.button.press; + } + } +} + +i +{ + font-family: Material Icons; + text-transform: lowercase; + + &.with-click + { + &:hover + { + cursor: pointer; + color: white; + transform: scale( 1.1 ); + sound-in: ui.button.over; + } + + &:active + { + sound-in: ui.button.press; + } + } +} + +.avatar +{ + width: 64px; + height: 64px; + + &:hover + { + border: 1px solid white; + } +} \ No newline at end of file diff --git a/code/UI/MainMenu/SavedGames.razor b/code/UI/MainMenu/SavedGames.razor new file mode 100644 index 0000000..1e1903c --- /dev/null +++ b/code/UI/MainMenu/SavedGames.razor @@ -0,0 +1,44 @@ +@using Sandbox; +@using System; +@using System.Linq; +@using Sandbox.UI; +@namespace LuckerGame.UI.MainMenu +@inherits Panel + + + + @Game.Menu.Package.Title + + + + @foreach ( var save in Game.Menu.SavedGames.OrderByDescending( x => x.Time ) ) + { + LoadSavedGame( save ))>@save.Name - @save.Time + } + + + + Return + + + +@code +{ + async void LoadSavedGame( SavedGame save ) + { + if ( save != null ) + { + Game.Menu.Lobby.SavedGame = save.Name; + + if ( !string.IsNullOrEmpty( save.Map ) ) + Game.Menu.Lobby.Map = save.Map; + + await Game.Menu.StartServerAsync( Game.Menu.Lobby.MaxMembers, Game.Menu.Lobby.Title, Game.Menu.Lobby.Map ?? "facepunch.square" ); + } + } + + protected override int BuildHash() + { + return HashCode.Combine( Game.Menu.Lobby, Game.Menu.Lobby?.Map ); + } +} \ No newline at end of file diff --git a/code/UI/MainMenu/SetupGame.razor b/code/UI/MainMenu/SetupGame.razor new file mode 100644 index 0000000..6b70ddd --- /dev/null +++ b/code/UI/MainMenu/SetupGame.razor @@ -0,0 +1,78 @@ +@using Sandbox; +@using System; +@using System.Linq; +@using System.Threading.Tasks; +@using Sandbox.Menu; +@using Sandbox.UI; +@namespace LuckerGame.UI.MainMenu +@inherits Panel + + + + @Game.Menu.Package.Title + + + + + @if ( MaxPlayersSupported > 1 ) + { + + Maximum Players + + + + + } + + + Map + + + + + + + + + Start + Return + + + +@code +{ + int MaxPlayersSupported { get; set; } = 1; + int MaxPlayers { get; set; } = 1; + Package MapPackage { get; set; } + + void OnMapClicked() + { + Game.Overlay.ShowPackageSelector( "type:map sort:popular", OnMapSelected ); + StateHasChanged(); + } + + void OnMapSelected( Package map ) + { + MapPackage = map; + StateHasChanged(); + } + + protected override async Task OnParametersSetAsync() + { + MaxPlayersSupported = Game.Menu.Package.GetMeta( "MaxPlayers", 1 ); + MaxPlayers = MaxPlayersSupported; + + MapPackage = await Package.FetchAsync( "facepunch.square", false ); + StateHasChanged(); + } + + async Task Play() + { + await Game.Menu.StartServerAsync( MaxPlayers, $"My game", MapPackage.FullIdent ); + } + + protected override int BuildHash() + { + return HashCode.Combine( MaxPlayers, MapPackage ); + } +} \ No newline at end of file diff --git a/code/UI/MainMenu/SlimPackageCard.razor b/code/UI/MainMenu/SlimPackageCard.razor new file mode 100644 index 0000000..7bd4cf5 --- /dev/null +++ b/code/UI/MainMenu/SlimPackageCard.razor @@ -0,0 +1,32 @@ +@using System; +@using Sandbox; +@namespace LuckerGame.UI.MainMenu +@inherits Sandbox.UI.Panel + + + @if ( Package == null ) + { + Select Package + } + else + { + @Package.Title + Game.Overlay.ShowPackageModal( Package.FullIdent ) )>info + } + + +@code +{ + public Package Package { get; set; } + public System.Action OnLaunch { get; set; } + + void OnCardClicked() + { + OnLaunch?.Invoke(); + } + + protected override int BuildHash() + { + return HashCode.Combine( Package ); + } +}