Initial commit
This commit is contained in:
87
code/Entities/Lucker.cs
Normal file
87
code/Entities/Lucker.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using LuckerGame.Components.Lucker.Cameras;
|
||||
using LuckerGame.Events;
|
||||
using Sandbox;
|
||||
|
||||
namespace LuckerGame.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Player.
|
||||
/// 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 currently controls
|
||||
/// </summary>
|
||||
public Entity Pawn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Before the round has started, this player indicated they were ready
|
||||
/// </summary>
|
||||
[Net] public bool Ready { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This Lucker's camera
|
||||
/// </summary>
|
||||
[BindComponent] public AbstractCamera Camera { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates and properly sets up a Player entity for a given client
|
||||
/// </summary>
|
||||
/// <param name="client">the client to own the player</param>
|
||||
/// <returns>the newly created player</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>();
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows clients to request setting their ready state
|
||||
/// </summary>
|
||||
/// <param name="readyState">whether they are readying up or calming down</param>
|
||||
[ConCmd.Server("set_ready")]
|
||||
public static void ReadyUpCommand(bool readyState)
|
||||
{
|
||||
var client = ConsoleSystem.Caller;
|
||||
var player = client.Pawn as Lucker;
|
||||
player.SetReady( readyState );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets this player's ready state
|
||||
/// </summary>
|
||||
/// <param name="ready">the ready state being set</param>
|
||||
public void SetReady(bool ready)
|
||||
{
|
||||
Ready = ready;
|
||||
if ( Game.IsServer )
|
||||
{
|
||||
Event.Run( LuckerEvent.PlayerReady, this, ready );
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Simulate( IClient cl )
|
||||
{
|
||||
base.Simulate( cl );
|
||||
|
||||
}
|
||||
|
||||
public override void FrameSimulate( IClient cl )
|
||||
{
|
||||
base.FrameSimulate( cl );
|
||||
Camera?.Update();
|
||||
}
|
||||
|
||||
public override void BuildInput()
|
||||
{
|
||||
base.BuildInput();
|
||||
Camera?.BuildInput();
|
||||
}
|
||||
}
|
||||
49
code/Entities/MinigameManager.cs
Normal file
49
code/Entities/MinigameManager.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LuckerGame.Minigames;
|
||||
using Sandbox;
|
||||
using Sandbox.UI;
|
||||
|
||||
namespace LuckerGame.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Manages minigames
|
||||
/// </summary>
|
||||
public partial class MinigameManager : Entity
|
||||
{
|
||||
[Net] public Minigame LoadedMinigame { get; private set; }
|
||||
private List<Minigame> AvailableMinigames { get; set; }
|
||||
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
FindMinigames();
|
||||
}
|
||||
|
||||
public void StartRandomMinigame(List<Lucker> players)
|
||||
{
|
||||
if ( (AvailableMinigames?.Count ?? 0) == 0 )
|
||||
{
|
||||
Log.Error( "Attempted to start minigame, but none available" );
|
||||
return;
|
||||
}
|
||||
|
||||
LoadedMinigame = AvailableMinigames.OrderBy( _ => Guid.NewGuid() ).FirstOrDefault();
|
||||
ChatBox.AddInformation( To.Everyone, $"Starting {LoadedMinigame.Name}" );
|
||||
LoadedMinigame.Initialize( players );
|
||||
}
|
||||
|
||||
private void FindMinigames()
|
||||
{
|
||||
AvailableMinigames = TypeLibrary.GetTypes<Minigame>()
|
||||
.Where( type => !type.IsAbstract && !type.IsInterface )
|
||||
.Select( td => TypeLibrary.Create<Minigame>( td.TargetType ) ).ToList();
|
||||
}
|
||||
|
||||
[Event.Hotload]
|
||||
private void Reload()
|
||||
{
|
||||
FindMinigames();
|
||||
}
|
||||
}
|
||||
133
code/Entities/Pawn.cs
Normal file
133
code/Entities/Pawn.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System.ComponentModel;
|
||||
using LuckerGame.Components.Pawn;
|
||||
using Sandbox;
|
||||
|
||||
namespace LuckerGame.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity in the world. Could be controlled by a Lucker or a minigame
|
||||
/// </summary>
|
||||
public partial class Pawn : AnimatedEntity
|
||||
{
|
||||
[ClientInput]
|
||||
public Vector3 InputDirection { get; set; }
|
||||
|
||||
[ClientInput]
|
||||
public Angles ViewAngles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Position a player should be looking from in world space.
|
||||
/// </summary>
|
||||
[Browsable( false )]
|
||||
public Vector3 EyePosition
|
||||
{
|
||||
get => Transform.PointToWorld( EyeLocalPosition );
|
||||
set => EyeLocalPosition = Transform.PointToLocal( value );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Position a player should be looking from in local to the entity coordinates.
|
||||
/// </summary>
|
||||
[Net, Predicted, Browsable( false )]
|
||||
public Vector3 EyeLocalPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rotation of the entity's "eyes", i.e. rotation for the camera when this entity is used as the view entity.
|
||||
/// </summary>
|
||||
[Browsable( false )]
|
||||
public Rotation EyeRotation
|
||||
{
|
||||
get => Transform.RotationToWorld( EyeLocalRotation );
|
||||
set => EyeLocalRotation = Transform.RotationToLocal( value );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotation of the entity's "eyes", i.e. rotation for the camera when this entity is used as the view entity. In local to the entity coordinates.
|
||||
/// </summary>
|
||||
[Net, Predicted, Browsable( false )]
|
||||
public Rotation EyeLocalRotation { get; set; }
|
||||
|
||||
public BBox Hull
|
||||
{
|
||||
get => new
|
||||
(
|
||||
new Vector3( -16, -16, 0 ),
|
||||
new Vector3( 16, 16, 64 )
|
||||
);
|
||||
}
|
||||
|
||||
[BindComponent] public UserPawnController Controller { get; }
|
||||
[BindComponent] public PawnAnimator Animator { get; }
|
||||
|
||||
public override Ray AimRay => new Ray( EyePosition, EyeRotation.Forward );
|
||||
|
||||
/// <summary>
|
||||
/// Called when the entity is first created
|
||||
/// </summary>
|
||||
public override void Spawn()
|
||||
{
|
||||
SetModel( "models/citizen/citizen.vmdl" );
|
||||
|
||||
EnableDrawing = true;
|
||||
EnableHideInFirstPerson = true;
|
||||
EnableShadowInFirstPerson = true;
|
||||
}
|
||||
|
||||
public void Respawn()
|
||||
{
|
||||
Components.Create<UserPawnController>();
|
||||
Components.Create<PawnAnimator>();
|
||||
}
|
||||
|
||||
public void DressFromClient( IClient cl )
|
||||
{
|
||||
var c = new ClothingContainer();
|
||||
c.LoadFromClient( cl );
|
||||
c.DressEntity( this );
|
||||
}
|
||||
|
||||
public override void Simulate( IClient cl )
|
||||
{
|
||||
SimulateRotation();
|
||||
Controller?.Simulate( cl );
|
||||
Animator?.Simulate();
|
||||
}
|
||||
|
||||
public override void BuildInput()
|
||||
{
|
||||
}
|
||||
|
||||
public override void FrameSimulate( IClient cl )
|
||||
{
|
||||
SimulateRotation();
|
||||
}
|
||||
|
||||
public TraceResult TraceBBox( Vector3 start, Vector3 end, float liftFeet = 0.0f )
|
||||
{
|
||||
return TraceBBox( start, end, Hull.Mins, Hull.Maxs, liftFeet );
|
||||
}
|
||||
|
||||
public TraceResult TraceBBox( Vector3 start, Vector3 end, Vector3 mins, Vector3 maxs, float liftFeet = 0.0f )
|
||||
{
|
||||
if ( liftFeet > 0 )
|
||||
{
|
||||
start += Vector3.Up * liftFeet;
|
||||
maxs = maxs.WithZ( maxs.z - liftFeet );
|
||||
}
|
||||
|
||||
var tr = Trace.Ray( start, end )
|
||||
.Size( mins, maxs )
|
||||
.WithAnyTags( "solid", "playerclip", "passbullets" )
|
||||
.Ignore( this )
|
||||
.Run();
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
protected void SimulateRotation()
|
||||
{
|
||||
var idealRotation = ViewAngles.ToRotation();
|
||||
EyeRotation = Rotation.Slerp( Rotation, idealRotation, Time.Delta * 10f );
|
||||
Rotation = EyeRotation;
|
||||
}
|
||||
}
|
||||
120
code/Entities/RoundManager.cs
Normal file
120
code/Entities/RoundManager.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LuckerGame.Enums;
|
||||
using LuckerGame.Events;
|
||||
using Sandbox;
|
||||
using Sandbox.UI;
|
||||
|
||||
namespace LuckerGame.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Manages rounds. Starting, stopping, triggering minigames from the manager, etc
|
||||
/// </summary>
|
||||
public partial class RoundManager : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// The minigame manager we should be using to manage minigames
|
||||
/// </summary>
|
||||
private MinigameManager MinigameManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This percentage of the lobby must be ready to start the countdown for round start
|
||||
/// </summary>
|
||||
private const float RequiredReadyPercent = .5f;
|
||||
|
||||
/// <summary>
|
||||
/// The number of seconds from the timer starting before the round starts
|
||||
/// </summary>
|
||||
private const float RoundStartCountdownSeconds = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// The state of the current round
|
||||
/// </summary>
|
||||
[Net] public RoundState RoundState { get; private set; }
|
||||
|
||||
#region Countdown State
|
||||
/// <summary>
|
||||
/// How long since we started counting down to game start. Only useful if in the StartCountdown state.
|
||||
/// </summary>
|
||||
[Net] public TimeSince TimeSinceCountdownStarted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of seconds left in the countdown. Only useful if in the StartCountdown state.
|
||||
/// </summary>
|
||||
public int SecondsLeftInCountdown => (int)Math.Ceiling( RoundStartCountdownSeconds - TimeSinceCountdownStarted );
|
||||
#endregion
|
||||
|
||||
#region In Progress State
|
||||
|
||||
private const int MinigamesPerRound = 1;
|
||||
|
||||
private int MinigamesLeftInRound { get; set; }
|
||||
|
||||
private List<Lucker> Players { get; set; }
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
RoundState = RoundState.NotStarted;
|
||||
MinigameManager = new MinigameManager();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires once per server tick
|
||||
/// </summary>
|
||||
[GameEvent.Tick.Server]
|
||||
public void Tick()
|
||||
{
|
||||
if ( RoundState == RoundState.StartCountdown && TimeSinceCountdownStarted > RoundStartCountdownSeconds )
|
||||
{
|
||||
Log.Info( "Starting round" );
|
||||
StartRound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is triggered whenever a player readies up
|
||||
/// </summary>
|
||||
/// <param name="readyLucker">the player that readied up, discarded</param>
|
||||
[LuckerEvent.PlayerReady]
|
||||
public void HandlePlayerReady( Lucker readyLucker, bool ready )
|
||||
{
|
||||
if ( RoundState != RoundState.NotStarted && RoundState != RoundState.StartCountdown )
|
||||
{
|
||||
return;
|
||||
}
|
||||
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;
|
||||
if ( (float)readiedCount / totalCount > RequiredReadyPercent && RoundState == RoundState.NotStarted )
|
||||
{
|
||||
Log.Info( "Countdown started" );
|
||||
RoundState = RoundState.StartCountdown;
|
||||
TimeSinceCountdownStarted = 0;
|
||||
}
|
||||
else if ( (float)readiedCount / totalCount <= RequiredReadyPercent && RoundState == RoundState.StartCountdown )
|
||||
{
|
||||
Log.Info( "Countdown ended" );
|
||||
RoundState = RoundState.NotStarted;
|
||||
}
|
||||
}
|
||||
|
||||
private void StartRound()
|
||||
{
|
||||
if ( RoundState == RoundState.InProgress )
|
||||
{
|
||||
Log.Warning( "Attempted to start round while one was in progress" );
|
||||
return;
|
||||
}
|
||||
|
||||
RoundState = RoundState.InProgress;
|
||||
Players = All.OfType<Lucker>().ToList();
|
||||
MinigameManager.StartRandomMinigame( Players );
|
||||
}
|
||||
}
|
||||
250
code/Entities/Weapons/Weapon.cs
Normal file
250
code/Entities/Weapons/Weapon.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using Sandbox;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LuckerGame.Entities.Weapons;
|
||||
|
||||
public partial class Weapon : AnimatedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// The View Model's entity, only accessible clientside.
|
||||
/// </summary>
|
||||
public WeaponViewModel ViewModelEntity { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// An accessor to grab our Pawn.
|
||||
/// </summary>
|
||||
public Pawn Pawn => Owner as Pawn;
|
||||
|
||||
/// <summary>
|
||||
/// This'll decide which entity to fire effects from. If we're in first person, the View Model, otherwise, this.
|
||||
/// </summary>
|
||||
public AnimatedEntity EffectEntity => Camera.FirstPersonViewer == Owner ? ViewModelEntity : this;
|
||||
|
||||
public virtual string ViewModelPath => null;
|
||||
public virtual string ModelPath => null;
|
||||
public virtual string ReloadSoundPath => null;
|
||||
public virtual string ReloadAnimPath => "reload";
|
||||
public virtual string WeaponName => "weapon";
|
||||
public virtual float ReloadDuration => 4f;
|
||||
private bool Reloading => TimeSinceReloadStarted < ReloadDuration;
|
||||
|
||||
/// <summary>
|
||||
/// How often you can shoot this gun.
|
||||
/// </summary>
|
||||
public virtual float PrimaryRate => 5.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How long since we last shot this gun.
|
||||
/// </summary>
|
||||
[Net, Predicted] public TimeSince TimeSincePrimaryAttack { get; set; }
|
||||
|
||||
[Net, Predicted] public TimeSince TimeSinceReloadStarted { get; set; }
|
||||
|
||||
public virtual int MaxAmmo { get; }
|
||||
|
||||
[Net] public int Ammo { get; set; }
|
||||
|
||||
|
||||
public override void Spawn()
|
||||
{
|
||||
EnableHideInFirstPerson = true;
|
||||
EnableShadowInFirstPerson = true;
|
||||
EnableDrawing = false;
|
||||
Ammo = MaxAmmo;
|
||||
|
||||
if ( ModelPath != null )
|
||||
{
|
||||
SetModel( ModelPath );
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when <see cref="Pawn.SetActiveWeapon(Weapon)"/> is called for this weapon.
|
||||
/// </summary>
|
||||
/// <param name="pawn"></param>
|
||||
public void OnEquip( Pawn pawn )
|
||||
{
|
||||
Owner = pawn;
|
||||
SetParent( pawn, true );
|
||||
EnableDrawing = true;
|
||||
CreateViewModel( To.Single( pawn ) );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the weapon is either removed from the player, or holstered.
|
||||
/// </summary>
|
||||
public void OnHolster()
|
||||
{
|
||||
EnableDrawing = false;
|
||||
DestroyViewModel( To.Single( Owner ) );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from <see cref="Pawn.Simulate(IClient)"/>.
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
public override void Simulate( IClient player )
|
||||
{
|
||||
Animate();
|
||||
|
||||
if ( CanPrimaryAttack() )
|
||||
{
|
||||
using ( LagCompensation() )
|
||||
{
|
||||
TimeSincePrimaryAttack = 0;
|
||||
ReduceAmmoAndPrimaryAttack();
|
||||
}
|
||||
}
|
||||
else if ( CanReload() )
|
||||
{
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
protected virtual void ReloadEffects()
|
||||
{
|
||||
Game.AssertClient();
|
||||
ViewModelEntity?.SetAnimParameter( "reload", true );
|
||||
}
|
||||
|
||||
private void Reload()
|
||||
{
|
||||
ReloadEffects();
|
||||
Pawn.PlaySound( ReloadSoundPath );
|
||||
Ammo = MaxAmmo;
|
||||
Log.Info( $"Reload duration: {ReloadDuration}" );
|
||||
TimeSinceReloadStarted = 0;
|
||||
}
|
||||
|
||||
private bool CanReload()
|
||||
{
|
||||
return Owner.IsValid && Input.Down( "reload" ) && Ammo < MaxAmmo && !Reloading;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every <see cref="Simulate(IClient)"/> to see if we can shoot our gun.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual bool CanPrimaryAttack()
|
||||
{
|
||||
if ( !Owner.IsValid() || !Input.Down( "attack1" ) || Ammo == 0 || Reloading ) return false;
|
||||
|
||||
var rate = PrimaryRate;
|
||||
if ( rate <= 0 ) return true;
|
||||
|
||||
return TimeSincePrimaryAttack > (1 / rate);
|
||||
}
|
||||
|
||||
private void ReduceAmmoAndPrimaryAttack()
|
||||
{
|
||||
Ammo--;
|
||||
PrimaryAttack();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when your gun shoots.
|
||||
/// </summary>
|
||||
public virtual void PrimaryAttack()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Useful for setting anim parameters based off the current weapon.
|
||||
/// </summary>
|
||||
protected virtual void Animate()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does a trace from start to end, does bullet impact effects. Coded as an IEnumerable so you can return multiple
|
||||
/// hits, like if you're going through layers or ricocheting or something.
|
||||
/// </summary>
|
||||
public virtual IEnumerable<TraceResult> TraceBullet( Vector3 start, Vector3 end, float radius = 2.0f )
|
||||
{
|
||||
bool underWater = Trace.TestPoint( start, "water" );
|
||||
|
||||
var trace = Trace.Ray( start, end )
|
||||
.UseHitboxes()
|
||||
.WithAnyTags( "solid", "player", "npc" )
|
||||
.Ignore( this )
|
||||
.Size( radius );
|
||||
|
||||
//
|
||||
// If we're not underwater then we can hit water
|
||||
//
|
||||
if ( !underWater )
|
||||
trace = trace.WithAnyTags( "water" );
|
||||
|
||||
var tr = trace.Run();
|
||||
|
||||
if ( tr.Hit )
|
||||
yield return tr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shoot a single bullet
|
||||
/// </summary>
|
||||
public virtual void ShootBullet( Vector3 pos, Vector3 dir, float spread, float force, float damage, float bulletSize )
|
||||
{
|
||||
var forward = dir;
|
||||
forward += (Vector3.Random + Vector3.Random + Vector3.Random + Vector3.Random) * spread * 0.25f;
|
||||
forward = forward.Normal;
|
||||
|
||||
//
|
||||
// ShootBullet is coded in a way where we can have bullets pass through shit
|
||||
// or bounce off shit, in which case it'll return multiple results
|
||||
//
|
||||
foreach ( var tr in TraceBullet( pos, pos + forward * 5000, bulletSize ) )
|
||||
{
|
||||
tr.Surface.DoBulletImpact( tr );
|
||||
|
||||
if ( !Game.IsServer ) continue;
|
||||
if ( !tr.Entity.IsValid() ) continue;
|
||||
|
||||
//
|
||||
// We turn predictiuon off for this, so any exploding effects don't get culled etc
|
||||
//
|
||||
using ( Prediction.Off() )
|
||||
{
|
||||
var damageInfo = DamageInfo.FromBullet( tr.EndPosition, forward * 100 * force, damage )
|
||||
.UsingTraceResult( tr )
|
||||
.WithAttacker( Owner )
|
||||
.WithWeapon( this );
|
||||
|
||||
tr.Entity.TakeDamage( damageInfo );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shoot a single bullet from owners view point
|
||||
/// </summary>
|
||||
public virtual void ShootBullet( float spread, float force, float damage, float bulletSize )
|
||||
{
|
||||
Game.SetRandomSeed( Time.Tick );
|
||||
|
||||
var ray = Owner.AimRay;
|
||||
ShootBullet( ray.Position, ray.Forward, spread, force, damage, bulletSize );
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void CreateViewModel()
|
||||
{
|
||||
if ( ViewModelPath == null ) return;
|
||||
|
||||
var vm = new WeaponViewModel( this );
|
||||
vm.Model = Model.Load( ViewModelPath );
|
||||
ViewModelEntity = vm;
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void DestroyViewModel()
|
||||
{
|
||||
if ( ViewModelEntity.IsValid() )
|
||||
{
|
||||
ViewModelEntity.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
code/Entities/Weapons/WeaponViewModel.cs
Normal file
22
code/Entities/Weapons/WeaponViewModel.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace LuckerGame.Entities.Weapons;
|
||||
|
||||
public partial class WeaponViewModel : BaseViewModel
|
||||
{
|
||||
protected Weapon Weapon { get; init; }
|
||||
|
||||
public WeaponViewModel( Weapon weapon )
|
||||
{
|
||||
Weapon = weapon;
|
||||
EnableShadowCasting = false;
|
||||
EnableViewmodelRendering = true;
|
||||
}
|
||||
|
||||
public override void PlaceViewmodel()
|
||||
{
|
||||
base.PlaceViewmodel();
|
||||
|
||||
Camera.Main.SetViewModelCamera( 80f, 1, 500 );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user