/// <summary> /// Gets the melee damages inflicted by an attacker to a defender. /// </summary> /// <returns><see cref="AttackResult"/></returns> public AttackResult OnDamage() { var attackResult = new AttackResult { Flags = this.GetAttackFlags() }; if (attackResult.Flags.HasFlag(AttackFlags.AF_MISS)) { return(attackResult); } if (this._attacker is IPlayerEntity player) { Item rightWeapon = player.Inventory.GetItem(x => x.Slot == InventorySystem.RightWeaponSlot) ?? InventorySystem.Hand; // TODO: GetDamagePropertyFactor() int weaponAttack = BattleHelper.GetWeaponAttackDamages(rightWeapon.Data.WeaponType, player); attackResult.AttackMin = rightWeapon.Data.AbilityMin * 2 + weaponAttack; attackResult.AttackMax = rightWeapon.Data.AbilityMax * 2 + weaponAttack; } else if (this._attacker is IMonsterEntity monster) { attackResult.AttackMin = monster.Data.AttackMin; attackResult.AttackMax = monster.Data.AttackMax; } if (this.IsCriticalAttack(this._attacker, attackResult.Flags)) { attackResult.Flags |= AttackFlags.AF_CRITICAL; this.CalculateCriticalDamages(attackResult); if (this.IsKnockback(attackResult.Flags)) { attackResult.Flags |= AttackFlags.AF_FLYING; } } attackResult.Damages = RandomHelper.Random(attackResult.AttackMin, attackResult.AttackMax); attackResult.Damages -= this.GetDefenderDefense(attackResult); if (attackResult.Damages > 0) { float blockFactor = this.GetDefenderBlockFactor(); if (blockFactor < 1f) { attackResult.Flags |= AttackFlags.AF_BLOCKING; attackResult.Damages = (int)(attackResult.Damages * blockFactor); } } else { attackResult.Damages = 0; attackResult.Flags &= ~AttackFlags.AF_CRITICAL; attackResult.Flags &= ~AttackFlags.AF_FLYING; } return(attackResult); }
/// <summary> /// Process the melee attack algorithm. /// </summary> /// <param name="attacker">Attacker</param> /// <param name="e">Melee attack event arguments</param> private void ProcessMeleeAttack(ILivingEntity attacker, MeleeAttackEventArgs e) { ILivingEntity defender = e.Target; if (defender.Health.IsDead) { Logger.LogError($"{attacker.Object.Name} cannot attack {defender.Object.Name} because target is already dead."); this.ClearBattleTargets(defender); this.ClearBattleTargets(attacker); return; } attacker.Battle.Target = defender; defender.Battle.Target = attacker; AttackResult meleeAttackResult = new MeleeAttackArbiter(attacker, defender).OnDamage(); Logger.LogDebug($"{attacker.Object.Name} inflicted {meleeAttackResult.Damages} to {defender.Object.Name}"); if (meleeAttackResult.Flags.HasFlag(AttackFlags.AF_FLYING)) { BattleHelper.KnockbackEntity(defender); } WorldPacketFactory.SendAddDamage(defender, attacker, meleeAttackResult.Flags, meleeAttackResult.Damages); WorldPacketFactory.SendMeleeAttack(attacker, e.AttackType, defender.Id, e.UnknownParameter, meleeAttackResult.Flags); defender.Health.Hp -= meleeAttackResult.Damages; if (defender.Health.IsDead) { Logger.LogDebug($"{attacker.Object.Name} killed {defender.Object.Name}."); defender.Health.Hp = 0; this.ClearBattleTargets(defender); this.ClearBattleTargets(attacker); WorldPacketFactory.SendDie(attacker as IPlayerEntity, defender, attacker, e.AttackType); if (defender is IMonsterEntity deadMonster) { var worldServerConfiguration = DependencyContainer.Instance.Resolve <WorldConfiguration>(); var itemsData = DependencyContainer.Instance.Resolve <ItemLoader>(); var expTable = DependencyContainer.Instance.Resolve <ExpTableLoader>(); deadMonster.Timers.DespawnTime = Time.TimeInSeconds() + 5; // Drop items int itemCount = 0; foreach (DropItemData dropItem in deadMonster.Data.DropItems) { if (itemCount >= deadMonster.Data.MaxDropItem) { break; } long dropChance = RandomHelper.LongRandom(0, DropSystem.MaxDropChance); if (dropItem.Probability * worldServerConfiguration.Rates.Drop >= dropChance) { var item = new Item(dropItem.ItemId, 1, -1, -1, -1, (byte)RandomHelper.Random(0, dropItem.ItemMaxRefine)); deadMonster.NotifySystem <DropSystem>(new DropItemEventArgs(item, attacker)); itemCount++; } } // Drop item kinds foreach (DropItemKindData dropItemKind in deadMonster.Data.DropItemsKind) { var itemsDataByItemKind = itemsData.GetItems(x => x.ItemKind3 == dropItemKind.ItemKind && x.Rare >= dropItemKind.UniqueMin && x.Rare <= dropItemKind.UniqueMax); if (!itemsDataByItemKind.Any()) { continue; } var itemData = itemsDataByItemKind.ElementAt(RandomHelper.Random(0, itemsDataByItemKind.Count() - 1)); int itemRefine = RandomHelper.Random(0, 10); for (int i = itemRefine; i >= 0; i--) { long itemDropProbability = (long)(expTable.GetDropLuck(itemData.Level > 120 ? 119 : itemData.Level, itemRefine) * (deadMonster.Data.CorrectionValue / 100f)); long dropChance = RandomHelper.LongRandom(0, DropSystem.MaxDropChance); if (dropChance < itemDropProbability * worldServerConfiguration.Rates.Drop) { var item = new Item(itemData.Id, 1, -1, -1, -1, (byte)itemRefine); deadMonster.NotifySystem <DropSystem>(new DropItemEventArgs(item, attacker)); break; } } } // Drop gold int goldDropped = RandomHelper.Random(deadMonster.Data.DropGoldMin, deadMonster.Data.DropGoldMax); deadMonster.NotifySystem <DropSystem>(new DropGoldEventArgs(goldDropped, attacker)); // TODO: give exp } } }