public void TryShoot(int time, Position pos, float attackAngle, bool ability, int numShots) { if (!ValidTime(time)) { #if DEBUG Program.Print(PrintType.Error, "Invalid time for player shoot"); #endif Client.Disconnect(); return; } if (AwaitingGoto.Count > 0) { Client.Random.Drop(numShots); return; } if (!ValidMove(time, pos)) { #if DEBUG Program.Print(PrintType.Error, "Invalid move for player shoot"); #endif Client.Disconnect(); return; } int startId = NextProjectileId; NextProjectileId -= numShots; ItemDesc desc = ability ? GetItem(1) : GetItem(0); if (desc == null) { #if DEBUG Program.Print(PrintType.Error, "Undefined item descriptor"); #endif Client.Random.Drop(numShots); return; } if (numShots != desc.NumProjectiles) { #if DEBUG Program.Print(PrintType.Error, "Manipulated num shots"); #endif Client.Random.Drop(numShots); return; } if (HasConditionEffect(ConditionEffectIndex.Stunned)) { #if DEBUG Program.Print(PrintType.Error, "Stunned..."); #endif Client.Random.Drop(numShots); return; } if (ability) { if (ShootAEs.TryDequeue(out ushort aeItemType)) { if (aeItemType != desc.Type) { Client.Random.Drop(numShots); return; } float arcGap = (desc.ArcGap * MathUtils.ToRadians); float totalArc = arcGap * (numShots - 1); float angle = attackAngle - (totalArc / 2f); for (int i = 0; i < numShots; i++) { int damage = (int)(GetNextDamageSeeded(desc.Projectile.MinDamage, desc.Projectile.MaxDamage, ItemDatas[1]) * GetAttackMultiplier()); Projectile projectile = new Projectile(this, desc.Projectile, startId - i, time, angle + (arcGap * i), pos, damage); ShotProjectiles.Add(projectile.Id, projectile); } byte[] packet = GameServer.AllyShoot(Id, desc.Type, attackAngle); foreach (Entity en in Parent.PlayerChunks.HitTest(Position, SightRadius)) { if (en is Player player && player.Client.Account.AllyShots && !player.Equals(this)) { player.Client.Send(packet); } } FameStats.Shots += numShots; } else { #if DEBUG Program.Print(PrintType.Error, "Invalid ShootAE"); #endif Client.Random.Drop(numShots); } } else { if (time > ShotTime + ShotDuration) { float arcGap = (desc.ArcGap * MathUtils.ToRadians); float totalArc = arcGap * (numShots - 1); float angle = attackAngle - (totalArc / 2f); for (int i = 0; i < numShots; i++) { int damage = (int)(GetNextDamageSeeded(desc.Projectile.MinDamage, desc.Projectile.MaxDamage, ItemDatas[0]) * GetAttackMultiplier()); Projectile projectile = new Projectile(this, desc.Projectile, startId - i, time, angle + (arcGap * i), pos, damage); ShotProjectiles.Add(projectile.Id, projectile); } byte[] packet = GameServer.AllyShoot(Id, desc.Type, attackAngle); foreach (Entity en in Parent.PlayerChunks.HitTest(Position, SightRadius)) { if (en is Player player && player.Client.Account.AllyShots && !player.Equals(this)) { player.Client.Send(packet); } } FameStats.Shots += numShots; float rateOfFireMod = ItemDesc.GetStat(ItemDatas[0], ItemData.RateOfFire, ItemDesc.RateOfFireMultiplier); float rateOfFire = desc.RateOfFire; rateOfFire *= 1 + rateOfFireMod; ShotDuration = (int)((1f / GetAttackFrequency() * (1f / rateOfFire)) * (1f / RateOfFireThreshold)); ShotTime = time; } else { #if DEBUG Program.Print(PrintType.Error, "Shot too early, ignored"); #endif Client.Random.Drop(numShots); } } }