mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-08-01 16:28:36 -04:00
https://sbox.game/dev/doc/gameplay/inventory-weapons/ Added InventoryComponent - a slot based inventory, hotbar or shared bucket slots, with a starting loadout BaseInventoryItem - base class for inventory items, with hooks to veto switching, holstering and dropping BaseWeapon - a GMod style base weapon - fire modes, magazines and reserve ammo, reloading, dry fire, ballistics, spread, crosshair and HUD BaseWeaponModel - view/world model base with muzzle flash, brass ejection, tracers and animation hooks Ammo types are resources (.ammo) - weapons share reserve pools by type, clamped to a max reserve BaseWeapon.ShootBullets / ShootBullet - bullet tracing with damage, impulse and hitbox tags Weapon hold types editor Animgraph enum parameters can be set by option name - Renderer.Set( "holdtype", "pistol" ) NpcUsage on BaseWeapon - engagement range and burst behaviour so NPCs can use any weapon ICameraModifier - cameras compose their view through an ordered modifier chain, read the result from CameraComponent.View CameraEffectSystem - screen shake, directional punches and tilts, with world epicenter falloff - camera.AddShake / AddPunch / AddTilt EnvShake component - shakes the view, rumbles controllers, kicks physics TracerEffect - an animated tracer line, ITargetedEffect for effects that fly at a target SceneAnchor - a world position that can follow an object or bone SkinnedModelRenderer.GetClosestBone( worldPosition ) Vector3.WithAimCone can take a custom Random for reproducible spread Improve/Fix PlayerController searches parents for IPressable when pressing PlayerController camera runs through the modifier chain - IEvents.PostCameraSetup is obsolete SoundPlayer preview uses shortcuts - Space toggles playback, Ctrl+Space restarts Fixed Radius( 0 ) traces missing everything Fixed NRE in AssetPreviewWidget Removed Removed CameraComponent.ISceneCameraSetup (obsolete since October, nothing uses)
423 lines
12 KiB
C#
423 lines
12 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
|
|
namespace Sandbox;
|
|
|
|
public sealed partial class SkinnedModelRenderer
|
|
{
|
|
/// <summary>
|
|
/// If something sets parameters before the model is spawned, then we store them
|
|
/// and apply them when it does spawn. This isn't ideal, but it is what it is.
|
|
/// Stored per-type so setting a value type every frame doesn't box.
|
|
/// </summary>
|
|
Dictionary<string, float> _floatParams;
|
|
Dictionary<string, int> _intParams;
|
|
Dictionary<string, bool> _boolParams;
|
|
Dictionary<string, Vector3> _vectorParams;
|
|
Dictionary<string, Rotation> _rotationParams;
|
|
Dictionary<string, string> _enumParams;
|
|
|
|
// A parameter can be reached through more than one overload - an enum by index or by option
|
|
// name, a bool by float. Every write drops the other stores' entry for the name, so the last
|
|
// write wins when the stored parameters reapply.
|
|
|
|
public void Set( string v, Vector3 value )
|
|
{
|
|
ClearParameter( v );
|
|
(_vectorParams ??= new( StringComparer.OrdinalIgnoreCase ))[v] = value;
|
|
SceneModel?.SetAnimParameter( v, value );
|
|
}
|
|
|
|
public void Set( string v, int value )
|
|
{
|
|
ClearParameter( v );
|
|
(_intParams ??= new( StringComparer.OrdinalIgnoreCase ))[v] = value;
|
|
SceneModel?.SetAnimParameter( v, value );
|
|
}
|
|
|
|
public void Set( string v, float value )
|
|
{
|
|
ClearParameter( v );
|
|
(_floatParams ??= new( StringComparer.OrdinalIgnoreCase ))[v] = value;
|
|
SceneModel?.SetAnimParameter( v, value );
|
|
}
|
|
public void Set( string v, bool value )
|
|
{
|
|
ClearParameter( v );
|
|
(_boolParams ??= new( StringComparer.OrdinalIgnoreCase ))[v] = value;
|
|
SceneModel?.SetAnimParameter( v, value );
|
|
}
|
|
|
|
public void Set( string v, Rotation value )
|
|
{
|
|
ClearParameter( v );
|
|
(_rotationParams ??= new( StringComparer.OrdinalIgnoreCase ))[v] = value;
|
|
SceneModel?.SetAnimParameter( v, value );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set an enum parameter by option name (e.g. Set( "holdtype", "pistol" )).
|
|
/// </summary>
|
|
public void Set( string v, string option )
|
|
{
|
|
ClearParameter( v );
|
|
(_enumParams ??= new( StringComparer.OrdinalIgnoreCase ))[v] = option;
|
|
SceneModel?.SetAnimParameter( v, option );
|
|
}
|
|
|
|
void ApplyStoredAnimParameters()
|
|
{
|
|
if ( _vectorParams is not null ) foreach ( var p in _vectorParams ) SceneModel.SetAnimParameter( p.Key, p.Value );
|
|
if ( _floatParams is not null ) foreach ( var p in _floatParams ) SceneModel.SetAnimParameter( p.Key, p.Value );
|
|
if ( _intParams is not null ) foreach ( var p in _intParams ) SceneModel.SetAnimParameter( p.Key, p.Value );
|
|
if ( _boolParams is not null ) foreach ( var p in _boolParams ) SceneModel.SetAnimParameter( p.Key, p.Value );
|
|
if ( _rotationParams is not null ) foreach ( var p in _rotationParams ) SceneModel.SetAnimParameter( p.Key, p.Value );
|
|
if ( _enumParams is not null ) foreach ( var p in _enumParams ) SceneModel.SetAnimParameter( p.Key, p.Value );
|
|
|
|
// Tick the animation by a frame so we're fully up to date on the first frame.
|
|
if ( Scene.IsEditor && !CanUpdateInEditor() )
|
|
{
|
|
SceneModel.UpdateToBindPose( ReadBonesFromGameObjects );
|
|
}
|
|
else
|
|
{
|
|
SceneModel.Update( Time.Delta, ReadBonesFromGameObjects );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove any stored parameters
|
|
/// </summary>
|
|
public void ClearParameters()
|
|
{
|
|
_floatParams?.Clear();
|
|
_intParams?.Clear();
|
|
_boolParams?.Clear();
|
|
_vectorParams?.Clear();
|
|
_rotationParams?.Clear();
|
|
_enumParams?.Clear();
|
|
|
|
if ( SceneModel.IsValid() )
|
|
{
|
|
SceneModel.ResetAnimParameters();
|
|
}
|
|
}
|
|
|
|
internal void ClearParameter( string name )
|
|
{
|
|
_floatParams?.Remove( name );
|
|
_intParams?.Remove( name );
|
|
_boolParams?.Remove( name );
|
|
_vectorParams?.Remove( name );
|
|
_rotationParams?.Remove( name );
|
|
_enumParams?.Remove( name );
|
|
}
|
|
|
|
internal bool ContainsParameter( string name )
|
|
{
|
|
return (_floatParams?.ContainsKey( name ) ?? false)
|
|
|| (_intParams?.ContainsKey( name ) ?? false)
|
|
|| (_boolParams?.ContainsKey( name ) ?? false)
|
|
|| (_vectorParams?.ContainsKey( name ) ?? false)
|
|
|| (_rotationParams?.ContainsKey( name ) ?? false)
|
|
|| (_enumParams?.ContainsKey( name ) ?? false);
|
|
}
|
|
|
|
/// <summary>Total number of stored (modified) anim-graph parameters across all types.</summary>
|
|
internal int StoredParameterCount => (_floatParams?.Count ?? 0) + (_intParams?.Count ?? 0) + (_boolParams?.Count ?? 0) + (_vectorParams?.Count ?? 0) + (_rotationParams?.Count ?? 0) + (_enumParams?.Count ?? 0);
|
|
|
|
// public void Set( string v, Enum value ) => _sceneObject.SetAnimParameter( v, value );
|
|
|
|
public bool GetBool( string v ) => SceneModel?.GetBool( v ) ?? false;
|
|
public int GetInt( string v ) => SceneModel?.GetInt( v ) ?? 0;
|
|
public float GetFloat( string v ) => SceneModel?.GetFloat( v ) ?? 0.0f;
|
|
public Vector3 GetVector( string v ) => SceneModel?.GetVector3( v ) ?? Vector3.Zero;
|
|
public Rotation GetRotation( string v ) => SceneModel?.GetRotation( v ) ?? Rotation.Identity;
|
|
|
|
/// <summary>
|
|
/// Converts value to vector local to this entity's eyepos and passes it to SetAnimVector
|
|
/// </summary>
|
|
public void SetLookDirection( string name, Vector3 eyeDirectionWorld )
|
|
{
|
|
var delta = eyeDirectionWorld * WorldRotation.Inverse;
|
|
Set( name, delta );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts value to vector local to this entity's eyepos and passes it to SetAnimVector.
|
|
/// This also sets {name}_weight to the weight value.
|
|
/// </summary>
|
|
public void SetLookDirection( string name, Vector3 eyeDirectionWorld, float weight )
|
|
{
|
|
var delta = eyeDirectionWorld * WorldRotation.Inverse;
|
|
Set( name, delta );
|
|
Set( $"{name}_weight", weight );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets an IK parameter. This sets 3 variables that should be set in the animgraph:
|
|
/// 1. ik.{name}.enabled
|
|
/// 2. ik.{name}.position
|
|
/// 3. ik.{name}.rotation
|
|
/// </summary>
|
|
public void SetIk( string name, Transform tx )
|
|
{
|
|
// convert local to model
|
|
tx = WorldTransform.ToLocal( tx );
|
|
|
|
Set( $"ik.{name}.enabled", true );
|
|
Set( $"ik.{name}.position", tx.Position );
|
|
Set( $"ik.{name}.rotation", tx.Rotation );
|
|
}
|
|
|
|
/// <summary>
|
|
/// This sets ik.{name}.enabled to false.
|
|
/// </summary>
|
|
public void ClearIk( string name )
|
|
{
|
|
Set( $"ik.{name}.enabled", false );
|
|
}
|
|
|
|
ParameterAccessor _parameters;
|
|
|
|
/// <summary>
|
|
/// Access to the animgraph parameters for this model
|
|
/// </summary>
|
|
[Property, Group( "Parameters", StartFolded = true ), ShowIf( nameof( ShouldShowParametersEditor ), true )]
|
|
public ParameterAccessor Parameters
|
|
{
|
|
get
|
|
{
|
|
_parameters ??= new( this );
|
|
return _parameters;
|
|
}
|
|
}
|
|
|
|
public bool ShouldShowParametersEditor
|
|
{
|
|
get
|
|
{
|
|
if ( !UseAnimGraph ) return false;
|
|
if ( !SceneModel.IsValid() ) return false;
|
|
|
|
var graph = SceneModel.AnimationGraph;
|
|
if ( graph is null ) return false;
|
|
if ( graph.ParamCount <= 0 ) return false;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public sealed class ParameterAccessor : IJsonPopulator
|
|
{
|
|
public AnimationGraph Graph => _renderer.IsValid() && _renderer.SceneModel.IsValid() ?
|
|
_renderer.SceneModel.AnimationGraph : null;
|
|
|
|
readonly SkinnedModelRenderer _renderer;
|
|
readonly Dictionary<string, bool> _bools = new( StringComparer.OrdinalIgnoreCase );
|
|
readonly Dictionary<string, int> _ints = new( StringComparer.OrdinalIgnoreCase );
|
|
readonly Dictionary<string, float> _floats = new( StringComparer.OrdinalIgnoreCase );
|
|
readonly Dictionary<string, Vector3> _vectors = new( StringComparer.OrdinalIgnoreCase );
|
|
readonly Dictionary<string, Rotation> _rotations = new( StringComparer.OrdinalIgnoreCase );
|
|
|
|
internal ParameterAccessor( SkinnedModelRenderer renderer )
|
|
{
|
|
_renderer = renderer;
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
_bools.Clear();
|
|
_ints.Clear();
|
|
_floats.Clear();
|
|
_vectors.Clear();
|
|
_rotations.Clear();
|
|
|
|
_renderer.ClearParameters();
|
|
}
|
|
|
|
public void Reset( string name )
|
|
{
|
|
var parameter = Graph.GetParameterFromList( name );
|
|
if ( parameter.IsNull )
|
|
return;
|
|
|
|
var defaultValue = parameter.GetDefaultValue();
|
|
|
|
switch ( parameter.GetParameterType() )
|
|
{
|
|
case NativeEngine.AnimParamType.Float:
|
|
Set( name, defaultValue.GetValue<float>() );
|
|
break;
|
|
case NativeEngine.AnimParamType.Int:
|
|
Set( name, defaultValue.GetValue<int>() );
|
|
break;
|
|
case NativeEngine.AnimParamType.Enum:
|
|
Set( name, defaultValue.GetValue<byte>() );
|
|
break;
|
|
case NativeEngine.AnimParamType.Bool:
|
|
Set( name, defaultValue.GetValue<bool>() );
|
|
break;
|
|
case NativeEngine.AnimParamType.Vector:
|
|
Set( name, defaultValue.GetValue<Vector3>() );
|
|
break;
|
|
case NativeEngine.AnimParamType.Rotation:
|
|
Set( name, defaultValue.GetValue<Rotation>() );
|
|
break;
|
|
default:
|
|
throw new NotSupportedException( $"Unsupported parameter type: {parameter.GetParameterType()}" );
|
|
}
|
|
}
|
|
|
|
public void Clear( string name )
|
|
{
|
|
Reset( name );
|
|
|
|
_bools.Remove( name );
|
|
_ints.Remove( name );
|
|
_floats.Remove( name );
|
|
_vectors.Remove( name );
|
|
_rotations.Remove( name );
|
|
|
|
_renderer.ClearParameter( name );
|
|
}
|
|
|
|
public bool Contains( string name )
|
|
{
|
|
return _renderer.ContainsParameter( name );
|
|
}
|
|
|
|
public bool GetBool( string v ) => _renderer.GetBool( v );
|
|
public int GetInt( string v ) => _renderer.GetInt( v );
|
|
public float GetFloat( string v ) => _renderer.GetFloat( v );
|
|
public Vector3 GetVector( string v ) => _renderer.GetVector( v );
|
|
public Rotation GetRotation( string v ) => _renderer.GetRotation( v );
|
|
|
|
public void Set( string v, Vector3 value )
|
|
{
|
|
_vectors[v] = value;
|
|
_renderer.Set( v, value );
|
|
}
|
|
|
|
public void Set( string v, int value )
|
|
{
|
|
_ints[v] = value;
|
|
_renderer.Set( v, value );
|
|
}
|
|
|
|
public void Set( string v, float value )
|
|
{
|
|
_floats[v] = value;
|
|
_renderer.Set( v, value );
|
|
}
|
|
|
|
public void Set( string v, bool value )
|
|
{
|
|
_bools[v] = value;
|
|
_renderer.Set( v, value );
|
|
}
|
|
|
|
public void Set( string v, Rotation value )
|
|
{
|
|
_rotations[v] = value;
|
|
_renderer.Set( v, value );
|
|
}
|
|
|
|
JsonNode IJsonPopulator.Serialize()
|
|
{
|
|
var obj = new JsonObject();
|
|
|
|
var boolsObj = new JsonObject();
|
|
var intsObj = new JsonObject();
|
|
var floatsObj = new JsonObject();
|
|
var vectorsObj = new JsonObject();
|
|
var rotationsObj = new JsonObject();
|
|
|
|
foreach ( var value in _bools )
|
|
{
|
|
boolsObj.Add( value.Key, value.Value );
|
|
}
|
|
|
|
foreach ( var value in _ints )
|
|
{
|
|
intsObj.Add( value.Key, value.Value );
|
|
}
|
|
|
|
foreach ( var value in _floats )
|
|
{
|
|
floatsObj.Add( value.Key, value.Value );
|
|
}
|
|
|
|
foreach ( var value in _vectors )
|
|
{
|
|
vectorsObj.Add( value.Key, JsonSerializer.SerializeToNode( value.Value ) );
|
|
}
|
|
|
|
foreach ( var value in _rotations )
|
|
{
|
|
rotationsObj.Add( value.Key, JsonSerializer.SerializeToNode( value.Value ) );
|
|
}
|
|
|
|
obj.Add( "bools", boolsObj );
|
|
obj.Add( "ints", intsObj );
|
|
obj.Add( "floats", floatsObj );
|
|
obj.Add( "vectors", vectorsObj );
|
|
obj.Add( "rotations", rotationsObj );
|
|
|
|
return obj;
|
|
}
|
|
|
|
void IJsonPopulator.Deserialize( JsonNode e )
|
|
{
|
|
if ( e is not JsonObject jso )
|
|
return;
|
|
|
|
_bools.Clear();
|
|
_ints.Clear();
|
|
_floats.Clear();
|
|
_vectors.Clear();
|
|
_rotations.Clear();
|
|
|
|
if ( jso.TryGetPropertyValue( "bools", out var boolsNode ) && boolsNode is JsonObject boolsObj )
|
|
{
|
|
foreach ( var o in boolsObj )
|
|
{
|
|
Set( o.Key, o.Value.GetValue<bool>() );
|
|
}
|
|
}
|
|
|
|
if ( jso.TryGetPropertyValue( "ints", out var intsNode ) && intsNode is JsonObject intsObj )
|
|
{
|
|
foreach ( var o in intsObj )
|
|
{
|
|
Set( o.Key, o.Value.GetValue<int>() );
|
|
}
|
|
}
|
|
|
|
if ( jso.TryGetPropertyValue( "floats", out var floatsNode ) && floatsNode is JsonObject floatsObj )
|
|
{
|
|
foreach ( var o in floatsObj )
|
|
{
|
|
Set( o.Key, o.Value.GetValue<float>() );
|
|
}
|
|
}
|
|
|
|
if ( jso.TryGetPropertyValue( "vectors", out var vectorsNode ) && vectorsNode is JsonObject vectorsObj )
|
|
{
|
|
foreach ( var o in vectorsObj )
|
|
{
|
|
Set( o.Key, JsonSerializer.Deserialize<Vector3>( o.Value ) );
|
|
}
|
|
}
|
|
|
|
if ( jso.TryGetPropertyValue( "rotations", out var rotationsNode ) && rotationsNode is JsonObject rotationsObj )
|
|
{
|
|
foreach ( var o in rotationsObj )
|
|
{
|
|
Set( o.Key, JsonSerializer.Deserialize<Rotation>( o.Value ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|