feature/Lucker-misc_RoundFramework #2
2
.sbproj
@@ -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",
|
||||
|
|
||||
"Minimum": 1,
|
||||
|
||||
@@ -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>();
|
||||
|
para
commented
nit: unused locals nit: unused locals
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
para
commented
I think this correctly caches the Also, I feel like it'd be nice to standardize the naming of these variables to disambiguate references to 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.
conco
commented
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.
conco
commented
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 )
|
||||
|
para
commented
nit: more readable on separate lines nit: more readable on separate lines
|
||||
? 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();
|
||||
|
para
commented
Should we clear Should we clear `InvolvedPlayers` here?
conco
commented
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.
|
||||
return true;
|
||||
}
|
||||
|
||||
private void EndMinigame()
|
||||
{
|
||||
LoadedMinigame.Cleanup();
|
||||
CleanupLuckerPawns();
|
||||
LoadedMinigame.Delete();
|
||||
LoadedMinigame = null;
|
||||
InvolvedLuckers = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public partial class RoundManager : Entity
|
||||
[ConVar.Replicated("lucker_minigames_per_round")]
|
||||
|
para
commented
Shouldn't this be on Shouldn't this be on `MinigamesPerRound` and not `MinigamesLeftInRound`?
conco
commented
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" )]
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Why is minimum 1 but default is 0?
Also why can't I comment on multiple lines in Gitea? LAME
Should now be fixed, looks like something weird with S&box convars and setting them via GUI