A cash pickup lying on the ground for the Horizon RP gamemode. Spawned by the host, syncs its amount, builds a scaled model with a trigger hitbox, can be pressed with E to request pickup, and destroys itself after 10 minutes.
namespace HorizonRP;
/// <summary>
/// Une liasse au sol : de l'argent posé (Cash.RequestDrop) ou laissé par un mort. Créée par
/// l'hôte ; n'importe qui la ramasse avec E, et la somme va droit au portefeuille. Disparaît au
/// bout de 10 minutes, comme les objets au sol.
/// </summary>
[Title( "Horizon RP – Argent au sol" )]
[Icon( "payments" )]
public sealed class CashDrop : Component, Component.IPressable
{
public const float Lifetime = 600f;
/// <summary>L'échelle de la liasse au sol (en main, elle garde sa vraie taille).</summary>
public const float GroundScale = 1.8f;
[Sync( SyncFlags.FromHost )] public int Amount { get; set; }
/// <summary>Hôte : d'où vient l'argent (« posé par … », « laissé par … mort »), pour le journal.</summary>
public string Origin { get; set; }
RealTimeSince sinceSpawn;
bool built;
/// <summary>Hôte : une liasse posée sur le sol sous <paramref name="near"/>.</summary>
public static void Spawn( int amount, Vector3 near, string origin )
{
var tr = Game.ActiveScene.Trace.Ray( near + Vector3.Up * 30, near + Vector3.Down * 120 ).WithoutTags( "player", "trigger", "ragdoll" ).Run();
var go = new GameObject( true, $"Argent au sol : {Format.Money( amount )}" );
go.WorldPosition = (tr.Hit ? tr.HitPosition : near) + Vector3.Up * 0.2f;
go.WorldRotation = Rotation.FromYaw( Random.Shared.Float( 0, 360 ) );
var drop = go.Components.Create<CashDrop>();
drop.Amount = amount;
drop.Origin = origin;
go.NetworkSpawn();
}
protected override void OnStart() => sinceSpawn = 0;
protected override void OnUpdate()
{
if ( !built )
{
built = true;
// Le modèle et sa zone de visée dans un enfant local (rien à synchroniser).
var holder = new GameObject( GameObject, true, "Liasse" );
holder.NetworkMode = NetworkMode.Never;
// Grossie au sol : à sa vraie taille (15,6 cm) elle se voyait à peine debout (Julien).
// La zone de visée, posée sur le même enfant, grandit avec.
holder.LocalScale = GroundScale;
var model = Model.Load( Cash.ModelPath );
holder.Components.Create<ModelRenderer>().Model = model;
// Une zone de visée un peu plus grande que la liasse (qui ne fait qu'un centimètre
// d'épaisseur), en déclencheur : on la vise sans buter dedans.
var box = holder.Components.Create<BoxCollider>();
box.Center = model.Bounds.Center + Vector3.Up * 2;
box.Scale = model.Bounds.Size + new Vector3( 4, 4, 4 );
box.IsTrigger = true;
}
if ( Networking.IsHost && sinceSpawn > Lifetime )
GameObject.Destroy();
}
public bool Press( IPressable.Event e )
{
Cash.RequestPickup( GameObject.Id );
return true;
}
public IPressable.Tooltip? GetTooltip( IPressable.Event e ) =>
Amount > 0 ? new IPressable.Tooltip( $"{Format.Money( Amount )} en liquide", "payments", "Ramasser" ) : null;
}