Пример #1
0
    public override void Shoot(GunComponent gun, List <IShootable> ammo, EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, EntityUid?user = null)
    {
        var fromMap      = fromCoordinates.ToMap(EntityManager);
        var toMap        = toCoordinates.ToMapPos(EntityManager);
        var mapDirection = toMap - fromMap.Position;
        var mapAngle     = mapDirection.ToAngle();
        var angle        = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle());

        // Update shot based on the recoil
        toMap        = fromMap.Position + angle.ToVec() * mapDirection.Length;
        mapDirection = toMap - fromMap.Position;
        var entityDirection = Transform(fromCoordinates.EntityId).InvWorldMatrix.Transform(toMap) - fromCoordinates.Position;

        // I must be high because this was getting tripped even when true.
        // DebugTools.Assert(direction != Vector2.Zero);
        var shotProjectiles = new List <EntityUid>(ammo.Count);

        foreach (var shootable in ammo)
        {
            switch (shootable)
            {
            // Cartridge shoots something else
            case CartridgeAmmoComponent cartridge:
                if (!cartridge.Spent)
                {
                    if (cartridge.Count > 1)
                    {
                        var angles = LinearSpread(mapAngle - cartridge.Spread / 2,
                                                  mapAngle + cartridge.Spread / 2, cartridge.Count);

                        for (var i = 0; i < cartridge.Count; i++)
                        {
                            var uid = Spawn(cartridge.Prototype, fromCoordinates);
                            ShootProjectile(uid, angles[i].ToVec(), user);
                            shotProjectiles.Add(uid);
                        }
                    }
                    else
                    {
                        var uid = Spawn(cartridge.Prototype, fromCoordinates);
                        ShootProjectile(uid, mapDirection, user);
                        shotProjectiles.Add(uid);
                    }

                    SetCartridgeSpent(cartridge, true);
                    MuzzleFlash(gun.Owner, cartridge, user);
                    PlaySound(gun.Owner, gun.SoundGunshot?.GetSound(Random, ProtoManager), user);

                    if (cartridge.DeleteOnSpawn)
                    {
                        Del(cartridge.Owner);
                    }
                }
                else
                {
                    PlaySound(gun.Owner, gun.SoundEmpty?.GetSound(Random, ProtoManager), user);
                }

                // Something like ballistic might want to leave it in the container still
                if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(cartridge.Owner))
                {
                    EjectCartridge(cartridge.Owner);
                }

                Dirty(cartridge);
                break;

            // Ammo shoots itself
            case AmmoComponent newAmmo:
                shotProjectiles.Add(newAmmo.Owner);
                MuzzleFlash(gun.Owner, newAmmo, user);
                PlaySound(gun.Owner, gun.SoundGunshot?.GetSound(Random, ProtoManager), user);

                // Do a throw
                if (!HasComp <ProjectileComponent>(newAmmo.Owner))
                {
                    RemComp <AmmoComponent>(newAmmo.Owner);
                    // TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack.
                    ThrowingSystem.TryThrow(newAmmo.Owner, mapDirection, 20f, user);
                    break;
                }

                ShootProjectile(newAmmo.Owner, mapDirection, user);
                break;

            case HitscanPrototype hitscan:
                var ray            = new CollisionRay(fromMap.Position, mapDirection.Normalized, hitscan.CollisionMask);
                var rayCastResults = Physics.IntersectRay(fromMap.MapId, ray, hitscan.MaxLength, user, false).ToList();

                if (rayCastResults.Count >= 1)
                {
                    var result   = rayCastResults[0];
                    var distance = result.Distance;
                    FireEffects(fromCoordinates, distance, entityDirection.ToAngle(), hitscan, result.HitEntity);

                    var dmg = hitscan.Damage;

                    if (dmg != null)
                    {
                        dmg = Damageable.TryChangeDamage(result.HitEntity, dmg);
                    }

                    if (dmg != null)
                    {
                        PlayImpactSound(result.HitEntity, dmg, hitscan.Sound, hitscan.ForceSound);

                        if (user != null)
                        {
                            Logs.Add(LogType.HitScanHit,
                                     $"{ToPrettyString(user.Value):user} hit {ToPrettyString(result.HitEntity):target} using hitscan and dealt {dmg.Total:damage} damage");
                        }
                        else
                        {
                            Logs.Add(LogType.HitScanHit,
                                     $"Hit {ToPrettyString(result.HitEntity):target} using hitscan and dealt {dmg.Total:damage} damage");
                        }
                    }
                }
                else
                {
                    FireEffects(fromCoordinates, hitscan.MaxLength, entityDirection.ToAngle(), hitscan);
                }
                PlaySound(gun.Owner, gun.SoundGunshot?.GetSound(Random, ProtoManager), user);
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }

        RaiseLocalEvent(gun.Owner, new AmmoShotEvent()
        {
            FiredProjectiles = shotProjectiles,
        }, false);
    }