feature/Lucker-misc_RoundFramework #2

Merged
para merged 8 commits from feature/Lucker-misc_RoundFramework into master 2023-08-07 05:13:13 +00:00
10 changed files with 85 additions and 76 deletions
Showing only changes of commit 5735087889 - Show all commits

View File

@@ -46,7 +46,7 @@
"Choices": [],
"ConVarName": "lucker_minigames_per_round",
"DisplayName": "Minigames Per Round",
"DefaultValue": "0",
"DefaultValue": "1",
"Description": "The number of minigames played per round",
"Group": "Other",
Review

Why is minimum 1 but default is 0?

Also why can't I comment on multiple lines in Gitea? LAME

Why is minimum 1 but default is 0? Also why can't I comment on multiple lines in Gitea? LAME
Review

Should now be fixed, looks like something weird with S&box convars and setting them via GUI

Should now be fixed, looks like something weird with S&box convars and setting them via GUI
"Minimum": 1,

View File

@@ -6,18 +6,18 @@ using Sandbox;
namespace LuckerGame.Entities;
/// <summary>
/// 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
/// </summary>
public partial class Lucker : Entity
{
/// <summary>
/// The entity this player controls. This value is networked and should be accessed through <see cref="Pawn"/>.
/// The entity this lucker controls. This value is networked and should be accessed through <see cref="Pawn"/>.
/// </summary>
[Net] private Entity InternalPawn { get; set; }
/// <summary>
/// Accesses or sets the entity this player current controls
/// Accesses or sets the entity this lucker currently controls
/// </summary>
public Entity Pawn
{
@@ -33,7 +33,7 @@ public partial class Lucker : Entity
}
/// <summary>
/// Before the round has started, this player indicated they were ready
/// Before the round has started, this lucker indicated they were ready
/// </summary>
[Net] public bool Ready { get; set; }
@@ -45,20 +45,20 @@ public partial class Lucker : Entity
[BindComponent] public LuckerStats Stats { get; }
/// <summary>
/// Creates and properly sets up a Player entity for a given client
/// Creates and properly sets up a <see cref="Lucker"/> entity for a given client
/// </summary>
/// <param name="client">the client to own the player</param>
/// <returns>the newly created player</returns>
/// <param name="client">the client to own the lucker</param>
/// <returns>the newly created lucker</returns>
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<RTSCamera>();
var stats = player.Components.Create<LuckerStats>();
var lucker = new Lucker();
client.Pawn = lucker;
lucker.Owner = client as Entity;
lucker.Name = client.Name;
lucker.Components.Create<RTSCamera>();
lucker.Components.Create<LuckerStats>();
Outdated
Review

nit: unused locals

nit: unused locals
Outdated
Review

Should be fixed

Should be fixed
return player;
return lucker;
}
/// <summary>
@@ -69,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 );
}
/// <summary>
/// Sets this player's ready state
/// Sets this lucker's ready state
/// </summary>
/// <param name="ready">the ready state being set</param>
public void SetReady(bool ready)
@@ -82,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 );
}
}

View File

@@ -23,9 +23,9 @@ public partial class MinigameManager : Entity
private List<TypeDescription> AvailableMinigames { get; set; }
/// <summary>
/// The players involved in the current minigame
/// The luckers involved in the current minigame
/// </summary>
private List<Lucker> InvolvedPlayers { get; set; }
private List<Lucker> InvolvedLuckers { get; set; }
Outdated
Review

I think this correctly caches the Lucker list when the minigame is created, which is nice. Although I'm wondering how we deal with Luckers who leave before a minigame is ended. If there's other logic that deletes a Lucker in response to the client disconnecting, will the logic in CleanupPlayerPawns() fail / crash?

Also, I feel like it'd be nice to standardize the naming of these variables to disambiguate references to Players and Luckers. Might as well just name variables after the type to avoid clashing the names.

I think this correctly caches the `Lucker` list when the minigame is created, which is nice. Although I'm wondering how we deal with Luckers who leave before a minigame is ended. If there's other logic that deletes a `Lucker` in response to the client disconnecting, will the logic in `CleanupPlayerPawns()` fail / crash? Also, I feel like it'd be nice to standardize the naming of these variables to disambiguate references to `Player`s and `Lucker`s. Might as well just name variables after the type to avoid clashing the names.
Outdated
Review

I agree with the variable naming, and will go through and do that.

I think an investigation into handling client disconnects should be done in another ticket however, as I believe there's both an investigation in how S&box handles a client disconnect by default, and also a discussion in how we handle it, such as keeping the player around for the remainder of the round but assigning them to a bot, only a minigame, leave it up to the minigames, etc.

I agree with the variable naming, and will go through and do that. I think an investigation into handling client disconnects should be done in another ticket however, as I believe there's both an investigation in how S&box handles a client disconnect by default, and also a discussion in how we handle it, such as keeping the player around for the remainder of the round but assigning them to a bot, only a minigame, leave it up to the minigames, etc.
Outdated
Review

Variables should be renamed now based on the naming of 'Lucker' as opposed to player for the most part

Variables should be renamed now based on the naming of 'Lucker' as opposed to player for the most part
public override void Spawn()
{
@@ -33,14 +33,17 @@ public partial class MinigameManager : Entity
FindMinigames();
}
public void StartMinigame(List<Lucker> players, string minigameName = null)
public void StartMinigame(List<Lucker> luckers, string minigameName = null)
{
InvolvedPlayers = players.ToList();
InvolvedLuckers = luckers.ToList();
if (CheckForMinigames())
{
LoadedMinigame = string.IsNullOrEmpty( minigameName ) ? TypeLibrary.Create<Minigame>(AvailableMinigames.OrderBy( _ => Guid.NewGuid() ).FirstOrDefault().TargetType) : TypeLibrary.Create<Minigame>( minigameName );
LoadedMinigame = string.IsNullOrEmpty( minigameName )
Outdated
Review

nit: more readable on separate lines

nit: more readable on separate lines
Outdated
Review

Done

Done
? TypeLibrary.Create<Minigame>( AvailableMinigames.OrderBy( _ => Guid.NewGuid() ).FirstOrDefault()
.TargetType )
: TypeLibrary.Create<Minigame>( minigameName );
ChatBox.AddInformation( To.Everyone, $"Starting {LoadedMinigame.Name}" );
LoadedMinigame.Initialize( players );
LoadedMinigame.Initialize( luckers );
}
}
@@ -69,19 +72,19 @@ public partial class MinigameManager : Entity
}
/// <summary>
/// Goes through the players included in the loaded minigame and deletes and nulls out any pawns assigned to them
/// Goes through the luckers included in the loaded minigame and deletes and nulls out any pawns assigned to them
/// </summary>
private void CleanupPlayerPawns()
private void CleanupLuckerPawns()
{
if ( LoadedMinigame is not { IsValid: true } || InvolvedPlayers == null)
if ( LoadedMinigame is not { IsValid: true } || InvolvedLuckers == null)
{
Log.Warning( "Attempted to clean up players without a minigame loaded!" );
return;
}
InvolvedPlayers.ForEach( player =>
InvolvedLuckers.ForEach( lucker =>
{
player.Pawn?.Delete();
player.Pawn = null;
lucker.Pawn?.Delete();
lucker.Pawn = null;
} );
}
@@ -100,11 +103,16 @@ public partial class MinigameManager : Entity
{
return false;
}
LoadedMinigame.Cleanup();
CleanupPlayerPawns();
LoadedMinigame = null;
EndMinigame();
Outdated
Review

Should we clear InvolvedPlayers here?

Should we clear `InvolvedPlayers` here?
Outdated
Review

we can, but I don't believe it would be necessary. Since LoadedMinigame gets nulled out (which actually should be deleted before we do so, need to fix), the manager shouldn't necessarily be doing anything with the list until a new one is loaded (along with a new list), although some additional checks would be nice.

we can, but I don't believe it would be necessary. Since LoadedMinigame gets nulled out (which actually should be deleted before we do so, need to fix), the manager shouldn't necessarily be doing anything with the list until a new one is loaded (along with a new list), although some additional checks would be nice.
Outdated
Review

Done

Done
return true;
}
private void EndMinigame()
{
LoadedMinigame.Cleanup();
CleanupLuckerPawns();
LoadedMinigame.Delete();
LoadedMinigame = null;
InvolvedLuckers = null;
}
}

View File

@@ -52,7 +52,7 @@ public partial class RoundManager : Entity
[ConVar.Replicated("lucker_minigames_per_round")]
Outdated
Review

Shouldn't this be on MinigamesPerRound and not MinigamesLeftInRound?

Shouldn't this be on `MinigamesPerRound` and not `MinigamesLeftInRound`?
Outdated
Review

Yep, should be fixed now

Yep, should be fixed now
public static int MinigamesLeftInRound { get; set; }
private List<Lucker> Players { get; set; }
private List<Lucker> Luckers { get; set; }
#endregion
/// <inheritdoc/>
@@ -83,7 +83,7 @@ public partial class RoundManager : Entity
MinigamesLeftInRound--;
if ( MinigamesLeftInRound > 0 )
{
MinigameManager.StartMinigame( Players );
MinigameManager.StartMinigame( Luckers );
}
else
{
@@ -94,11 +94,12 @@ public partial class RoundManager : Entity
}
/// <summary>
/// Is triggered whenever a player readies up
/// Is triggered whenever a lucker readies up or readies down
/// </summary>
/// <param name="readyLucker">the player that readied up, discarded</param>
[LuckerEvent.PlayerReady]
public void HandlePlayerReady( Lucker readyLucker, bool ready )
/// <param name="readyLucker">the lucker that readied up</param>
/// <param name="ready">the lucker's ready state</param>
[LuckerEvent.LuckerReady]
public void HandleLuckerReady( Lucker readyLucker, bool ready )
{
if ( RoundState != RoundState.NotStarted && RoundState != RoundState.StartCountdown )
{
@@ -107,9 +108,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<Lucker>().ToList();
var readiedCount = players.Count( player => player.Ready );
var totalCount = players.Count;
var luckers = All.OfType<Lucker>().ToList();
var readiedCount = luckers.Count( lucker => lucker.Ready );
var totalCount = luckers.Count;
if ( (float)readiedCount / totalCount > RequiredReadyPercent && RoundState == RoundState.NotStarted )
{
Log.Info( "Countdown started" );
@@ -132,13 +133,13 @@ public partial class RoundManager : Entity
}
RoundState = RoundState.InProgress;
Players = All.OfType<Lucker>().ToList();
Players.ForEach( player =>
Luckers = All.OfType<Lucker>().ToList();
Luckers.ForEach( lucker =>
{
player.Ready = false;
lucker.Ready = false;
} );
MinigamesLeftInRound = MinigamesPerRound;
MinigameManager.StartMinigame( Players, minigameName );
MinigameManager.StartMinigame( Luckers, minigameName );
}
[ConCmd.Server( "start_round" )]

View File

@@ -76,7 +76,7 @@ public partial class Weapon : AnimatedEntity
}
/// <summary>
/// Called when the weapon is either removed from the player, or holstered.
/// Called when the weapon is either removed from the pawn, or holstered.
/// </summary>
public void OnHolster()
{

View File

@@ -4,16 +4,16 @@ namespace LuckerGame.Events;
public static partial class LuckerEvent
{
public const string PlayerReady = "lucker.playerReady";
public const string LuckerReady = "lucker.luckerReady";
/// <summary>
/// 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
/// </summary>
[MethodArguments(typeof(Entities.Lucker), typeof(bool))]
public class PlayerReadyAttribute : EventAttribute
public class LuckerReadyAttribute : EventAttribute
{
public PlayerReadyAttribute() : base(PlayerReady)
public LuckerReadyAttribute() : base(LuckerReady)
{
}
}

View File

@@ -15,8 +15,8 @@ public abstract class Minigame : Entity
/// <summary>
/// Initializes the minigame with a list of luckers playing it.
/// </summary>
/// <param name="players">the players who made it into the minigame</param>
public abstract void Initialize(List<Lucker> players);
/// <param name="luckers">the luckers who made it into the minigame</param>
public abstract void Initialize(List<Lucker> luckers);
/// <summary>
/// Once a minigame is loaded and initialized, this method is called once per server tick.

View File

@@ -14,7 +14,7 @@ namespace LuckerGame.Minigames.RussianRoulette;
public class RussianRouletteMinigame : Minigame
{
public override string Name => "Russian Roulette";
private List<Lucker> Players { get; set; }
private List<Lucker> Luckers { get; set; }
private Pawn Shooter { get; set; }
private const float ShooterDistance = 80f;
@@ -24,43 +24,43 @@ public class RussianRouletteMinigame : Minigame
private int Taunted = 0;
private Pawn ShooterTarget;
private List<Pawn> DeadVictims => Players
.Select( player => player.Pawn as Pawn )
private List<Pawn> 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<Lucker> players )
public override void Initialize( List<Lucker> luckers )
{
Players = players;
Luckers = luckers;
Shooter = new Pawn();
Shooter.Name = ShooterName;
var shooterInventory = Shooter.Components.Create<PawnInventory>();
shooterInventory.AddWeapon( new RussianPistol() );
// Setup cameras for players
Players.ForEach( player =>
// Setup cameras for luckers
Luckers.ForEach( lucker =>
{
player.Components.Create<RTSCamera>();
player.Position = Shooter.Position;
lucker.Components.Create<RTSCamera>();
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;
@@ -95,7 +95,7 @@ public class RussianRouletteMinigame : Minigame
}
else if ( TimeSinceShot > TimeBetweenShots * .8f && Taunted == 1)
{
ShooterTarget = Players.Select( player => player.Pawn as Pawn )
ShooterTarget = Luckers.Select( lucker => lucker.Pawn as Pawn )
.OrderBy( _ => Guid.NewGuid() )
.FirstOrDefault();
Shooter.LookAt( ShooterTarget.Position );

View File

@@ -10,9 +10,9 @@
<root>
<div class="scoreboard-panel">
@foreach (var player in Luckers)
@foreach (var lucker in Luckers)
{
<label>@player.Name</label>
<label>@lucker.Name</label>
}
</div>
</root>
@@ -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());
}
}

View File

@@ -27,7 +27,7 @@
<div class="voting-panel primary-color-translucent-background">
@if (RoundManager.RoundState == RoundState.NotStarted)
{
<label class="header">Waiting for players...</label>
<label class="header">Waiting for luckers...</label>
}
else
{