PaintGun.cs
using System;
using Sandbox;
public sealed class PaintGun : Component
{
[RequireComponent] PlayerController Player { get; set; }
[Property, Category( "Paint" )] public GameObject DecalPrefab { get; set; }
[Property, Category( "Paint" )] public Color PaintColor { get; set; } = new Color( 1f, 0.4f, 0f );
[Property, Category( "Weapon" )] public float FireRate { get; set; } = 0.12f;
[Property, Category( "Weapon" )] public float ProjectileSpeed { get; set; } = 1500f;
[Property, Category( "Weapon" )] public float ArcStrength { get; set; } = 80f;
[Property, Category( "Ammo" ), Sync] public int AmmoInMagazine { get; set; } = 30;
[Property, Category( "Ammo" ), Sync] public int AmmoReserve { get; set; } = 120;
[Property, Category( "Ammo" )] public int MagazineSize { get; set; } = 30;
[Property, Category( "Ammo" )] public int MaxReserve { get; set; } = 120;
[Property, Category( "Ammo" )] public float ReloadTime { get; set; } = 1.5f;
[Property, Category( "Audio" )] public SoundEvent ReloadSound { get; set; }
[Property, Category( "Audio" )] public SoundEvent HitSound { get; set; }
public bool IsReloading { get; private set; }
TimeSince timeSinceReloadStart;
TimeSince timeSinceLastShot;
protected override void OnUpdate()
{
if ( IsProxy ) return;
// Забороняємо стріляти, якщо персонаж у слизі, або якщо з моменту виходу пройшло менше 2 секунд
var swimming = Player.Components.Get<PaintSwimming>();
if ( swimming.IsValid() )
{
if ( swimming.IsDiving ) return;
if ( swimming.TimeSinceStoppedDiving < 2f ) return;
}
if ( IsReloading )
{
if ( timeSinceReloadStart >= ReloadTime )
{
FinishReload();
}
return;
}
if ( Input.Pressed( "Reload" ) && AmmoInMagazine < MagazineSize && AmmoReserve > 0 )
{
StartReload();
return;
}
if ( Input.Down( "Attack1" ) && timeSinceLastShot >= FireRate )
{
if ( AmmoInMagazine > 0 )
{
Shoot();
AmmoInMagazine--;
timeSinceLastShot = 0;
}
else if ( AmmoReserve > 0 )
{
StartReload();
}
}
}
void StartReload()
{
IsReloading = true;
timeSinceReloadStart = 0;
if ( ReloadSound != null )
{
Sound.Play( ReloadSound, WorldPosition );
}
Log.Info( "Reloading..." );
}
void FinishReload()
{
IsReloading = false;
int needed = MagazineSize - AmmoInMagazine;
int toReload = Math.Min( needed, AmmoReserve );
AmmoInMagazine += toReload;
AmmoReserve -= toReload;
Log.Info( $"Reload finished. Ammo: {AmmoInMagazine}/{AmmoReserve}" );
}
void Shoot()
{
Log.Info("НОВИЙ КОД ПРАЦЮЄ!");
var eyePos = Player.EyePosition;
var eyeRot = Player.EyeAngles.ToRotation();
var eyeDir = eyeRot.Forward;
var go = new GameObject();
go.Name = "PaintProjectile";
// Актуальне API: Використовуємо WorldPosition, WorldRotation та LocalScale
go.WorldPosition = eyePos + eyeDir * 40f;
go.WorldRotation = eyeRot;
go.LocalScale = Vector3.One * 0.12f;
var renderer = go.Components.Create<ModelRenderer>();
renderer.Model = Model.Load( "models/dev/sphere.vmdl" );
renderer.Tint = PaintColor;
// Додаємо скрипт снаряда
var paint = go.Components.Create<PaintProjectile>();
paint.DecalPrefab = this.DecalPrefab;
paint.PaintColor = PaintColor;
paint.HitSound = this.HitSound;
// Читаємо команду гравця та передаємо в снаряд
var playerTeam = Player.GameObject.Components.GetAll<PlayerTeam>( FindMode.EverythingInSelfAndDescendants ).FirstOrDefault();
paint.TeamId = playerTeam.IsValid() ? playerTeam.TeamId : 0;
paint.Shooter = Player.GameObject;
paint.Velocity = eyeDir * ProjectileSpeed + Vector3.Up * ArcStrength;
}
}