public static PacketWriter RegionDamage(long skillSN, int userObjectId, int skillObjectId, byte count, byte count2, IFieldObject <Player> player, CoordF direction, CoordS blockPosition, long damage) { PacketWriter pWriter = PacketWriter.Of(SendOp.SKILL_DAMAGE); SkillCast skillCast = SkillUsePacket.SkillCastMap[skillSN]; pWriter.Write(SkillDamageMode.RegionDamage); pWriter.WriteLong(skillCast.SkillSN); pWriter.WriteInt(userObjectId); pWriter.WriteInt(skillObjectId); pWriter.WriteByte(); pWriter.WriteByte(count); for (int i = 0; i < count; i++) { pWriter.WriteInt(player.ObjectId); pWriter.WriteByte(count2); pWriter.Write(blockPosition); pWriter.Write(direction); for (int j = 0; j < count2; j++) { pWriter.Write(skillCast.GetSkillDamageType()); pWriter.WriteLong(damage); } } return(pWriter); }
public static PacketWriter TileSkill(SkillCast skillCast, byte targetCount, IFieldObject <Player> player, byte count2, CoordF position, CoordF direction, long damage) { PacketWriter pWriter = PacketWriter.Of(SendOp.SKILL_DAMAGE); pWriter.Write(SkillDamageMode.RegionDamage); pWriter.WriteLong(skillCast.SkillSN); pWriter.WriteInt(skillCast.SkillId); pWriter.WriteShort(skillCast.SkillLevel); pWriter.WriteByte(targetCount); for (int i = 0; i < targetCount; i++) { pWriter.WriteInt(player.ObjectId); pWriter.WriteByte(count2); pWriter.Write(position); pWriter.Write(direction); for (int j = 0; j < count2; j++) { pWriter.Write(skillCast.GetSkillDamageType()); pWriter.WriteLong(damage); } } return(pWriter); }
public static DamageHandler CalculateDamage(SkillCast skill, IFieldActor source, IFieldActor target, double luckCoefficient) { // TODO: get accuracyWeakness from enemy stats from enemy buff. new stat recommended const double AccuracyWeakness = 0; double hitRate = (source.Stats[StatAttribute.Accuracy].Total + AccuracyWeakness) / Math.Max(target.Stats[StatAttribute.Evasion].Total, 0.1); if (Random.Shared.NextDouble() > hitRate) { return(new(source, target, 0, HitType.Miss)); // we missed } bool isCrit = skill.IsGuaranteedCrit() || RollCrit(source, target, luckCoefficient); double finalCritDamage = 1; if (isCrit) { // TODO: get critResist from enemy stats from enemy buff. new stat recommended const double CritResist = 1; double critDamage = 1000 + source.Stats[StatAttribute.CritDamage].Total; finalCritDamage = CritResist * ((critDamage / 1000) - 1) + 1; } double damageBonus = 1 + FetchMultiplier(source.Stats, StatAttribute.TotalDamage); damageBonus *= finalCritDamage; switch (skill.GetElement()) { case Element.Fire: damageBonus += FetchMultiplier(source.Stats, StatAttribute.FireDamage); break; case Element.Ice: damageBonus += FetchMultiplier(source.Stats, StatAttribute.IceDamage); break; case Element.Electric: damageBonus += FetchMultiplier(source.Stats, StatAttribute.ElectricDamage); break; case Element.Holy: damageBonus += FetchMultiplier(source.Stats, StatAttribute.HolyDamage); break; case Element.Dark: damageBonus += FetchMultiplier(source.Stats, StatAttribute.DarkDamage); break; case Element.Poison: damageBonus += FetchMultiplier(source.Stats, StatAttribute.PoisonDamage); break; } SkillRangeType rangeType = skill.GetRangeType(); if (rangeType != SkillRangeType.Special) { damageBonus += FetchMultiplier(source.Stats, rangeType == SkillRangeType.Melee ? StatAttribute.MeleeDamage : StatAttribute.RangedDamage); } bool isBoss = false; if (target is INpc npc) { isBoss = npc.Value.IsBoss(); } damageBonus += isBoss ? FetchMultiplier(source.Stats, StatAttribute.BossDamage) : 0; // TODO: properly fetch enemy attack speed weakness from enemy buff. new stat recommended const double AttackSpeedWeakness = 0; damageBonus += AttackSpeedWeakness * FetchMultiplier(source.Stats, StatAttribute.AttackSpeed); double damageMultiplier = damageBonus * skill.GetDamageRate(); // TODO: properly fetch enemy pierce resistance from enemy buff. new stat recommended const double EnemyPierceResistance = 1; double defensePierce = 1 - Math.Min(0.3, EnemyPierceResistance * FetchMultiplier(source.Stats, StatAttribute.Pierce)); damageMultiplier *= 1 / (Math.Max(target.Stats[StatAttribute.Defense].Total, 1) * defensePierce); bool isPhysical = skill.GetSkillDamageType() == DamageType.Physical; StatAttribute resistanceStat = isPhysical ? StatAttribute.PhysicalRes : StatAttribute.MagicRes; StatAttribute attackStat = isPhysical ? StatAttribute.PhysicalAtk : StatAttribute.MagicAtk; StatAttribute piercingStat = isPhysical ? StatAttribute.PhysicalPiercing : StatAttribute.MagicPiercing; double targetRes = target.Stats[resistanceStat].Total; double attackType = source.Stats[attackStat].Total; double resPierce = FetchMultiplier(source.Stats, piercingStat); double resistance = (1500.0 - Math.Max(0, targetRes - 1500 * resPierce)) / 1500; // does this need to be divided by anything at all to account for raw physical attack? damageMultiplier *= attackType * resistance; // TODO: apply special standalone multipliers like Spicy Maple Noodles buff? it seems to have had it's own multiplier. new stat recommended const double FinalDamageMultiplier = 1; damageMultiplier *= FinalDamageMultiplier; double attackDamage = 300; if (source is IFieldActor <Player> player) { double bonusAttack = player.Stats[StatAttribute.BonusAtk].Total + 0.4 * player.Stats[StatAttribute.PetBonusAtk].Total; // TODO: properly fetch enemy bonus attack weakness from enemy buff. new stat recommended const double BonusAttackWeakness = 1; double minDamage = player.Stats[StatAttribute.MinWeaponAtk].Total + BonusAttackWeakness * bonusAttack; double maxDamage = player.Stats[StatAttribute.MaxWeaponAtk].Total + BonusAttackWeakness * bonusAttack; attackDamage = minDamage + (maxDamage - minDamage) * Random.Shared.NextDouble(); } attackDamage *= damageMultiplier; return(new(source, target, Math.Max(1, attackDamage), isCrit ? HitType.Critical : HitType.Normal)); }