Compare commits
10 Commits
5f804f5c24
...
terry-race
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5647e33dd9 | ||
|
|
bbf0a765d5 | ||
| cc877492d5 | |||
|
|
bce1f8e1f7 | ||
|
|
3e9d64cb00 | ||
|
|
90cc343560 | ||
|
|
5735087889 | ||
|
|
b9dbe07e1f | ||
|
|
1b34af21ee | ||
|
|
4a338d9502 |
2
.sbproj
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
|
||||
{
|
||||
@@ -25,12 +25,15 @@ public partial class Lucker : Entity
|
||||
set
|
||||
{
|
||||
InternalPawn = value;
|
||||
if ( value != null )
|
||||
{
|
||||
value.Owner = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
|
||||
@@ -42,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>();
|
||||
|
||||
return player;
|
||||
return lucker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,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)
|
||||
@@ -79,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; }
|
||||
|
||||
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 )
|
||||
? 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,10 +103,16 @@ public partial class MinigameManager : Entity
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadedMinigame.Cleanup();
|
||||
LoadedMinigame = null;
|
||||
EndMinigame();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void EndMinigame()
|
||||
{
|
||||
LoadedMinigame.Cleanup();
|
||||
CleanupLuckerPawns();
|
||||
LoadedMinigame.Delete();
|
||||
LoadedMinigame = null;
|
||||
InvolvedLuckers = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,12 +47,21 @@ public partial class RoundManager : Entity
|
||||
|
||||
#region In Progress State
|
||||
|
||||
private const int MinigamesPerRound = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The number of minigames that should be played per round, settable via a convar
|
||||
/// </summary>
|
||||
[ConVar.Replicated("lucker_minigames_per_round")]
|
||||
public static int MinigamesLeftInRound { get; set; }
|
||||
private static int MinigamesPerRound { get; set; }
|
||||
|
||||
private List<Lucker> Players { get; set; }
|
||||
/// <summary>
|
||||
/// The number of minigames left in the current round
|
||||
/// </summary>
|
||||
public int MinigamesLeftInRound { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The luckers playing in the current round
|
||||
/// </summary>
|
||||
private List<Lucker> Luckers { get; set; }
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -83,7 +92,7 @@ public partial class RoundManager : Entity
|
||||
MinigamesLeftInRound--;
|
||||
if ( MinigamesLeftInRound > 0 )
|
||||
{
|
||||
MinigameManager.StartMinigame( Players );
|
||||
MinigameManager.StartMinigame( Luckers );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -94,11 +103,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 +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<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 +142,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()
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@ using Sandbox;
|
||||
|
||||
namespace LuckerGame.Components.Lucker.Cameras;
|
||||
|
||||
public abstract class AbstractCamera : EntityComponent<Entities.Lucker>, ISingletonComponent
|
||||
public abstract partial class AbstractCamera : EntityComponent<Entities.Lucker>, ISingletonComponent
|
||||
{
|
||||
public virtual bool ShouldShowCursor => false;
|
||||
protected Vector3 CameraPosition { get; set; }
|
||||
public Vector3 CameraPosition { get; set; }
|
||||
protected Rotation CameraRotation { get; set; }
|
||||
protected float FieldOfView { get; set; }
|
||||
protected IEntity FirstPersonViewer { get; set; }
|
||||
@@ -20,7 +20,7 @@ public abstract class AbstractCamera : EntityComponent<Entities.Lucker>, ISingle
|
||||
/// <summary>
|
||||
/// Handles any input dependent camera updates (ie moving around a top down cam)
|
||||
/// </summary>
|
||||
public abstract void BuildInput();
|
||||
public virtual void BuildInput() { }
|
||||
|
||||
/// <summary>
|
||||
/// Applies Camera parameters to the static Camera
|
||||
|
||||
41
code/EntityComponents/Lucker/Cameras/FixedCamera.cs
Normal file
41
code/EntityComponents/Lucker/Cameras/FixedCamera.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace LuckerGame.Components.Lucker.Cameras;
|
||||
|
||||
public partial class FixedCamera : AbstractCamera, ISingletonComponent
|
||||
{
|
||||
[Net] public float FieldOfViewValue { get; set; } = 70f;
|
||||
public override bool ShouldShowCursor => true;
|
||||
protected override void UpdateCameraParameters()
|
||||
{
|
||||
FieldOfView = FieldOfViewValue;
|
||||
FirstPersonViewer = null;
|
||||
SoundSource = new Transform { Position = CameraPosition };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ClientRpc doesn't allow for nullable versions of non-nullable types
|
||||
/// so here we are
|
||||
/// </summary>
|
||||
public void UpdateCameraPositionRotation( Vector3? position = null, Rotation? rotation = null )
|
||||
{
|
||||
CameraPosition = (Vector3)(position.Equals( null ) ? CameraPosition : position);
|
||||
CameraRotation = (Rotation)(rotation.Equals( null ) ? CameraRotation : rotation);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void LookAt( Vector3 position )
|
||||
{
|
||||
UpdateCameraPositionRotation( position );
|
||||
}
|
||||
[ClientRpc]
|
||||
public void LookAt( Rotation rotation )
|
||||
{
|
||||
UpdateCameraPositionRotation( rotation: rotation );
|
||||
}
|
||||
[ClientRpc]
|
||||
public void LookAt( Vector3 position, Rotation rotation )
|
||||
{
|
||||
UpdateCameraPositionRotation( position, rotation );
|
||||
}
|
||||
}
|
||||
@@ -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,50 +14,53 @@ 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;
|
||||
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<Pawn> DeadVictims => Players
|
||||
.Select( player => player.Pawn as Pawn )
|
||||
.Where( pawn => !pawn.IsValid || pawn.LifeState != LifeState.Alive )
|
||||
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;
|
||||
@@ -66,11 +69,12 @@ public class RussianRouletteMinigame : Minigame
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -86,21 +90,22 @@ public class RussianRouletteMinigame : Minigame
|
||||
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++;
|
||||
}
|
||||
|
||||
|
||||
23
code/Minigames/TerryRaces/HoveringTextCreator.cs
Normal file
23
code/Minigames/TerryRaces/HoveringTextCreator.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using LuckerGame.UI;
|
||||
using Sandbox;
|
||||
|
||||
public class HoveringTextCreator : Entity
|
||||
{
|
||||
private string Text { get; set; }
|
||||
private Entity TargetEntity { get; set; }
|
||||
|
||||
public HoveringTextCreator()
|
||||
{
|
||||
|
||||
}
|
||||
public HoveringTextCreator(string text, Entity targetEntity)
|
||||
{
|
||||
Text = text;
|
||||
TargetEntity = targetEntity;
|
||||
}
|
||||
public override void ClientSpawn()
|
||||
{
|
||||
base.ClientSpawn();
|
||||
var hoveringText = new HoveringText(Text, TargetEntity);
|
||||
}
|
||||
}
|
||||
64
code/Minigames/TerryRaces/Racer.cs
Normal file
64
code/Minigames/TerryRaces/Racer.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using LuckerGame.Entities;
|
||||
using LuckerGame.UI;
|
||||
using Sandbox;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
|
||||
public class Racer : Pawn
|
||||
{
|
||||
public float Speed;
|
||||
private float SpeedModifier = 1f;
|
||||
private Random Random { get; set; }
|
||||
private HoveringText NameTag { get; set; }
|
||||
|
||||
public Racer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Racer( Random random )
|
||||
{
|
||||
Random = random;
|
||||
}
|
||||
|
||||
public override void ClientSpawn()
|
||||
{
|
||||
base.ClientSpawn();
|
||||
NameTag = new( Name, this );
|
||||
}
|
||||
|
||||
public void ContinueRacing()
|
||||
{
|
||||
SetAnimParameter( "move_x", Speed * 500f * SpeedModifier );
|
||||
ModifySpeed();
|
||||
Position = Position.WithY( Position.y - (Speed * SpeedModifier) );
|
||||
|
||||
}
|
||||
|
||||
private void ModifySpeed()
|
||||
{
|
||||
var roll = Random.NextDouble();
|
||||
switch ( roll )
|
||||
{
|
||||
case >= .90 and < .95:
|
||||
SpeedModifier = 2f;
|
||||
break;
|
||||
case >= .95:
|
||||
SpeedModifier = 0.5f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void StopRacing()
|
||||
{
|
||||
SetAnimParameter( "move_x", 0 );
|
||||
}
|
||||
|
||||
public void GenerateSpeed( double minSpeed, double maxSpeed )
|
||||
{
|
||||
Speed = (float)(RandomExtensions.NextDouble( Random, minSpeed, maxSpeed ));
|
||||
}
|
||||
}
|
||||
123
code/Minigames/TerryRaces/TerryRacesMinigame.cs
Normal file
123
code/Minigames/TerryRaces/TerryRacesMinigame.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using LuckerGame.Components.Lucker.Cameras;
|
||||
using LuckerGame.Components.Pawn;
|
||||
using LuckerGame.Entities;
|
||||
using Sandbox;
|
||||
using Sandbox.UI;
|
||||
|
||||
namespace LuckerGame.Minigames.TerryRaces;
|
||||
|
||||
[Library( "mg_terry_races" )]
|
||||
public class TerryRaces : Minigame
|
||||
{
|
||||
public override string Name => "Terry Races";
|
||||
private Random random = new Random();
|
||||
private HoveringTextCreator NameTag { get; set; }
|
||||
private Racer WinningRacer = null;
|
||||
private FixedCamera Camera;
|
||||
private List<Lucker> Players { get; set; }
|
||||
private List<Racer> Racers { get; set; } = new List<Racer>();
|
||||
private List<string> RacerNames = new List<string>
|
||||
{
|
||||
"Terrance",
|
||||
"Terrie",
|
||||
"TearBear",
|
||||
"Theresa"
|
||||
};
|
||||
private float StartingY = 290f;
|
||||
private float FinishLineY = -300f;
|
||||
private float StartingX = 90f;
|
||||
private float RacerXOffset = 60f;
|
||||
private float MinimumSpeed = .90f;
|
||||
private float MaximumSpeed = 1.1f;
|
||||
|
||||
public override void Initialize( List<Lucker> players )
|
||||
{
|
||||
Players = players;
|
||||
|
||||
// Setup cameras for players
|
||||
Players.ForEach( player =>
|
||||
{
|
||||
Camera = player.Components.Create<FixedCamera>();
|
||||
Camera.LookAt( new Vector3(0f, 0f, 400f), Rotation.FromPitch( -270 ) );
|
||||
Camera.FieldOfViewValue = 100f;
|
||||
} );
|
||||
|
||||
SpawnRacers();
|
||||
}
|
||||
|
||||
public override void Tick()
|
||||
{
|
||||
Racers.ForEach( racer =>
|
||||
{
|
||||
if (WinningRacer == null)
|
||||
{
|
||||
GetWinningRacer();
|
||||
racer.ContinueRacing();
|
||||
}
|
||||
else
|
||||
{
|
||||
racer.StopRacing();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
private void SpawnRacers()
|
||||
{
|
||||
for ( int i = 0; i < RacerNames.Count; i++ )
|
||||
{
|
||||
var clothing = new ClothingContainer();
|
||||
var racer = new Racer(random);
|
||||
var x = StartingX - RacerXOffset * i;
|
||||
|
||||
clothing.Toggle( GetRandomBottomClothing() );
|
||||
clothing.Toggle( GetRandomHatClothing() );
|
||||
clothing.DressEntity( racer );
|
||||
|
||||
racer.Name = RacerNames[i];
|
||||
racer.Position = new Vector3( x, StartingY, 0 );
|
||||
racer.Rotation = Rotation.FromYaw( -90 );
|
||||
racer.GenerateSpeed(MinimumSpeed, MaximumSpeed );
|
||||
|
||||
Racers.Add( racer );
|
||||
}
|
||||
}
|
||||
|
||||
private Clothing GetRandomBottomClothing()
|
||||
{
|
||||
return ResourceLibrary
|
||||
.GetAll<Clothing>()
|
||||
.Where( c => c.Category == Clothing.ClothingCategory.Bottoms )
|
||||
.OrderBy( _ => Guid.NewGuid() )
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private Clothing GetRandomHatClothing()
|
||||
{
|
||||
return ResourceLibrary
|
||||
.GetAll<Clothing>()
|
||||
.Where( c => c.Category == Clothing.ClothingCategory.Hat )
|
||||
.OrderBy( _ => Guid.NewGuid() )
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private void GetWinningRacer()
|
||||
{
|
||||
foreach ( Racer racer in Racers )
|
||||
{
|
||||
if ( racer.Position.y <= FinishLineY )
|
||||
{
|
||||
WinningRacer = racer;
|
||||
Log.Info( $"Winning racer: {racer.Name}" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
code/RandomExtensions.cs
Normal file
11
code/RandomExtensions.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
public static class RandomExtensions
|
||||
{
|
||||
public static double NextDouble(
|
||||
this Random random,
|
||||
double minValue,
|
||||
double maxValue )
|
||||
{
|
||||
return random.NextDouble() * (maxValue - minValue) + minValue;
|
||||
}
|
||||
}
|
||||
30
code/UI/HoveringText.razor
Normal file
30
code/UI/HoveringText.razor
Normal file
@@ -0,0 +1,30 @@
|
||||
@using Sandbox;
|
||||
@using Sandbox.UI;
|
||||
|
||||
@namespace LuckerGame.UI
|
||||
@attribute [StyleSheet]
|
||||
@inherits WorldPanel
|
||||
|
||||
<root class="card">
|
||||
<label class="text">@Text</label>
|
||||
</root>
|
||||
|
||||
@code {
|
||||
private Entity TargetEntity;
|
||||
private string Text;
|
||||
|
||||
public HoveringText(string text, Entity targetEntity)
|
||||
{
|
||||
Text = text;
|
||||
TargetEntity = targetEntity;
|
||||
}
|
||||
|
||||
[GameEvent.Client.Frame]
|
||||
private void OnFrame()
|
||||
{
|
||||
if (!TargetEntity.IsValid()) return;
|
||||
Log.Info($"{Text} to {TargetEntity.Position}");
|
||||
Position = TargetEntity.Position + Vector3.Up * 65f;
|
||||
Rotation = Rotation.LookAt(-Screen.GetDirection(new Vector2(Screen.Width * 0.5f, Screen.Height * 0.5f)));
|
||||
}
|
||||
}
|
||||
7
code/UI/HoveringText.razor.scss
Normal file
7
code/UI/HoveringText.razor.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
hoveringtext {
|
||||
white-space: nowrap;
|
||||
.text {
|
||||
font-size: 150px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@
|
||||
<VoiceList/>
|
||||
<VotingLobby/>
|
||||
<Scoreboard/>
|
||||
<CameraCursor/>
|
||||
</root>
|
||||
|
||||
@code
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
@attribute [StyleSheet]
|
||||
@inherits Panel
|
||||
|
||||
<root>
|
||||
@if (ShouldShowCursor)
|
||||
{
|
||||
<root/>
|
||||
<div class="camera-cursor"/>
|
||||
}
|
||||
</root>
|
||||
|
||||
|
||||
|
||||
@code {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
CameraCursor {
|
||||
.camera-cursor {
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user