mirror of
				https://bitbucket.org/Ioncannon/project-meteor-server.git
				synced 2025-05-20 08:26:59 -04:00 
			
		
		
		
	Add ActionLanded to make some checks cleaner.
Split physical and magic text id dictionaries. Add calculation for resists.
This commit is contained in:
		| @@ -17,37 +17,55 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|     static class BattleUtils | ||||
|     { | ||||
|  | ||||
|         public static Dictionary<HitType, ushort> SingleHitTypeTextIds = new Dictionary<HitType, ushort>() | ||||
|         public static Dictionary<HitType, ushort> PhysicalHitTypeTextIds = new Dictionary<HitType, ushort>() | ||||
|         { | ||||
|             { HitType.Miss,     30311 }, | ||||
|             { HitType.Evade,    30310 }, | ||||
|             { HitType.Parry,    30308 }, | ||||
|             { HitType.Block,    30306 }, | ||||
|             { HitType.Resist,   30310 },    //Resists seem to use the evade text id | ||||
|             { HitType.Hit,      30301 }, | ||||
|             { HitType.Crit,     30302 } | ||||
|         }; | ||||
|  | ||||
|         public static Dictionary<HitType, ushort> MagicalHitTypeTextIds = new Dictionary<HitType, ushort>() | ||||
|         { | ||||
|             { HitType.SingleResist,30318 }, | ||||
|             { HitType.DoubleResist,30317 }, | ||||
|             { HitType.TripleResist, 30316 },//Triple Resists seem to use the same text ID as full resists | ||||
|             { HitType.FullResist,30316 }, | ||||
|             { HitType.Hit,      30319 }, | ||||
|             { HitType.Crit,     30392 }     //Unsure why crit is separated from the rest of the ids | ||||
|         }; | ||||
|  | ||||
|         public static Dictionary<HitType, ushort> MultiHitTypeTextIds = new Dictionary<HitType, ushort>() | ||||
|         { | ||||
|             { HitType.Miss,     30449 },    //The attack misses. | ||||
|             { HitType.Evade,    0 },        //Evades were removed before multi hit skills got their own messages, so this doesnt exist | ||||
|             { HitType.Parry,    30448 },    //[Target] parries, taking x points of damage. | ||||
|             { HitType.Block,    30447 },    //[Target] blocks, taking x points of damage. | ||||
|             { HitType.Resist,   0 },        //No spells are multi-hit, so this doesn't exist | ||||
|             { HitType.Hit,      30443 },    //[Target] tales x points of damage | ||||
|             { HitType.Crit,     30444 }     //Critical! [Target] takes x points of damage. | ||||
|         }; | ||||
|  | ||||
|         public static Dictionary<HitType, HitEffect> HitTypeEffects = new Dictionary<HitType, HitEffect>() | ||||
|         public static Dictionary<HitType, HitEffect> HitTypeEffectsPhysical = new Dictionary<HitType, HitEffect>() | ||||
|         { | ||||
|             { HitType.Miss,     0 }, | ||||
|             { HitType.Evade,    HitEffect.Evade }, | ||||
|             { HitType.Parry,    HitEffect.Parry }, | ||||
|             { HitType.Block,    HitEffect.Block }, | ||||
|             { HitType.Resist,   HitEffect.RecoilLv1 },//Probably don't need this, resists are handled differently to the rest | ||||
|             { HitType.Hit,      HitEffect.Hit }, | ||||
|             { HitType.Crit,     HitEffect.Crit } | ||||
|             { HitType.Crit,     HitEffect.Crit | HitEffect.CriticalHit } | ||||
|         }; | ||||
|  | ||||
|         //Magic attacks can't miss, be blocked, or parried. Resists are technically evades | ||||
|         public static Dictionary<HitType, HitEffect> HitTypeEffectsMagical = new Dictionary<HitType, HitEffect>() | ||||
|         { | ||||
|             { HitType.SingleResist,     HitEffect.WeakResist }, | ||||
|             { HitType.DoubleResist,     HitEffect.WeakResist }, | ||||
|             { HitType.TripleResist,     HitEffect.WeakResist }, | ||||
|             { HitType.FullResist,       HitEffect.FullResist }, | ||||
|             { HitType.Hit,              HitEffect.NoResist }, | ||||
|             { HitType.Crit,             HitEffect.Crit } | ||||
|         }; | ||||
|  | ||||
|         public static Dictionary<KnockbackType, HitEffect> KnockbackEffects = new Dictionary<KnockbackType, HitEffect>() | ||||
| @@ -206,7 +224,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|         //Or we could have HitTypes for DoubleResist, TripleResist, and FullResist that get used here. | ||||
|         public static void CalculateResistDamage(Character attacker, Character defender, BattleCommand skill, CommandResult action) | ||||
|         { | ||||
|             double percentResist = 0.5; | ||||
|             //Every tier of resist is a 25% reduction in damage. ie SingleResist is 25% damage taken down, Double is 50% damage taken down, etc | ||||
|             double percentResist = 0.25 * (action.hitType - HitType.SingleResist + 1); | ||||
|  | ||||
|             action.amountMitigated = (ushort)(action.amount * (1 - percentResist)); | ||||
|             action.amount = (ushort)(action.amount * percentResist); | ||||
| @@ -360,11 +379,26 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         //This probably isn't totally correct but it's close enough for now.  | ||||
|         //Full Resists seem to be calculated in a different way because the resist rates don't seem to line up with kanikan's testing (their tests didn't show any full resists) | ||||
|         //Non-spells with elemental damage can be resisted, it just doesnt say in the chat that they were. As far as I can tell, all mob-specific attacks are considered not to be spells | ||||
|         public static bool TryResist(Character attacker, Character defender, BattleCommand skill, CommandResult action) | ||||
|         { | ||||
|             if ((Program.Random.NextDouble() * 100) <= action.resistRate) | ||||
|             //The rate degrades for each check. Meaning with 100% resist, the attack will always be resisted, but it won't necessarily be a triple or full resist | ||||
|             //Rates beyond 100 still increase the chance for higher resist tiers though | ||||
|             double rate = action.resistRate; | ||||
|  | ||||
|             int i = -1; | ||||
|  | ||||
|             while ((Program.Random.NextDouble() * 100) <= rate && i < 4) | ||||
|             { | ||||
|                 action.hitType = HitType.Resist; | ||||
|                 rate /= 2; | ||||
|                 i++; | ||||
|             } | ||||
|  | ||||
|             if (i != -1) | ||||
|             { | ||||
|                 action.hitType = (HitType) ((int) HitType.SingleResist + i); | ||||
|                 CalculateResistDamage(attacker, defender, skill, action); | ||||
|                 return true; | ||||
|             } | ||||
| @@ -455,7 +489,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|             } | ||||
|  | ||||
|             //Actions have different text ids depending on whether they're a part of a multi-hit ws or not. | ||||
|             Dictionary<HitType, ushort> textIds = SingleHitTypeTextIds; | ||||
|             Dictionary<HitType, ushort> textIds = PhysicalHitTypeTextIds; | ||||
|  | ||||
|             //If this is the first hit of a multi hit command, add the "You use [command] on [target]" action | ||||
|             //Needs to be done here because certain buff messages appear before it. | ||||
| @@ -484,17 +518,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|  | ||||
|         public static void FinishActionSpell(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null) | ||||
|         { | ||||
|             //I'm assuming that like physical attacks stoneskin is taken into account before mitigation | ||||
|             HandleStoneskin(defender, action); | ||||
|  | ||||
|             //Determine the hit type of the action | ||||
|             if (!TryMiss(attacker, defender, skill, action)) | ||||
|             //Spells don't seem to be able to miss, instead magic acc/eva is used for resists (which are generally called evades in game) | ||||
|             //Unlike blocks and parries, crits do not go through resists. | ||||
|             if (!TryResist(attacker, defender, skill, action)) | ||||
|             { | ||||
|                 HandleStoneskin(defender, action); | ||||
|                 if (!TryCrit(attacker, defender, skill, action)) | ||||
|                     if (!TryResist(attacker, defender, skill, action)) | ||||
|                         action.hitType = HitType.Hit; | ||||
|                     action.hitType = HitType.Hit; | ||||
|             } | ||||
|  | ||||
|             //There are no multi-hit spells | ||||
|             action.worldMasterTextId = SingleHitTypeTextIds[action.hitType]; | ||||
|             //There are no multi-hit spells, so we don't need to take that into account | ||||
|             action.worldMasterTextId = MagicalHitTypeTextIds[action.hitType]; | ||||
|  | ||||
|             //Set the hit effect | ||||
|             SetHitEffectSpell(attacker, defender, skill, action); | ||||
| @@ -547,10 +584,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|                     hitEffect |= HitEffect.RecoilLv3; | ||||
|             } | ||||
|  | ||||
|             hitEffect |= HitTypeEffects[hitType]; | ||||
|             hitEffect |= HitTypeEffectsPhysical[hitType]; | ||||
|  | ||||
|             //For combos that land, add the combo effect | ||||
|             if (skill != null && skill.isCombo && hitType > HitType.Evade && hitType != HitType.Evade && !skill.comboEffectAdded) | ||||
|             if (skill != null && skill.isCombo && action.ActionLanded() && !skill.comboEffectAdded) | ||||
|             { | ||||
|                 hitEffect |= (HitEffect)(skill.comboStep << 15); | ||||
|                 skill.comboEffectAdded = true; | ||||
| @@ -560,7 +597,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|             if (hitType >= HitType.Parry) | ||||
|             { | ||||
|                 //Protect / Shell only show on physical/ magical attacks respectively. | ||||
|                 if (defender.statusEffects.HasStatusEffect(StatusEffectId.Protect)) | ||||
|                 if (defender.statusEffects.HasStatusEffect(StatusEffectId.Protect) || defender.statusEffects.HasStatusEffect(StatusEffectId.Protect2)) | ||||
|                     if (action != null) | ||||
|                         hitEffect |= HitEffect.Protect; | ||||
|  | ||||
| @@ -577,20 +614,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|             var hitEffect = HitEffect.MagicEffectType; | ||||
|             HitType hitType = action.hitType; | ||||
|  | ||||
|             //Recoil levels for spells are a bit different than physical. Recoil levels are used for resists.  | ||||
|             //Lv1 is for larger resists, Lv2 is for smaller resists and Lv3 is for no resists. Crit is still used for crits | ||||
|             if (hitType == HitType.Resist) | ||||
|             { | ||||
|                 //todo: calculate resist levels and figure out what the difference between Lv1 and 2 in retail was. For now assuming a full resist with 0 damage dealt is Lv1, all other resists Lv2 | ||||
|                 if (action.amount == 0) | ||||
|                     hitEffect |= HitEffect.RecoilLv1; | ||||
|                 else | ||||
|                     hitEffect |= HitEffect.RecoilLv2; | ||||
|             } | ||||
|             else | ||||
|                 hitEffect |= HitEffect.RecoilLv3; | ||||
|  | ||||
|             hitEffect |= HitTypeEffects[hitType]; | ||||
|             hitEffect |= HitTypeEffectsMagical[hitType]; | ||||
|  | ||||
|             if (skill != null && skill.isCombo && !skill.comboEffectAdded) | ||||
|             { | ||||
| @@ -599,16 +624,15 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|             } | ||||
|  | ||||
|             //if attack hit the target, take into account protective status effects | ||||
|             if (hitType >= HitType.Block) | ||||
|             if (action.ActionLanded()) | ||||
|             { | ||||
|                 //Protect / Shell only show on physical/ magical attacks respectively. | ||||
|                 //The magic hit effect category only has a flag for shell (and another shield effect that seems unused) | ||||
|                 //Even though traited protect gives magic defense, the shell effect doesn't play on attacks | ||||
|                 //This also means stoneskin doesnt show, but it does reduce damage | ||||
|                 if (defender.statusEffects.HasStatusEffect(StatusEffectId.Shell)) | ||||
|                     if (action != null) | ||||
|                         hitEffect |= HitEffect.Shell; | ||||
|  | ||||
|                 if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin)) | ||||
|                     if (action != null) | ||||
|                         hitEffect |= HitEffect.Stoneskin; | ||||
|                         hitEffect |= HitEffect.MagicShell; | ||||
|             } | ||||
|             action.effectId = (uint)hitEffect; | ||||
|         } | ||||
| @@ -659,7 +683,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils | ||||
|             double rand = Program.Random.NextDouble(); | ||||
|  | ||||
|             //Statuses only land for non-resisted attacks and attacks that hit | ||||
|             if (skill != null && skill.statusId != 0 && (action.hitType > HitType.Evade && action.hitType != HitType.Resist) && rand < skill.statusChance) | ||||
|             if (skill != null && skill.statusId != 0 && (action.ActionLanded()) && rand < skill.statusChance) | ||||
|             { | ||||
|                 StatusEffect effect = Server.GetWorldManager().GetStatusEffect(skill.statusId); | ||||
|                 //Because combos might change duration or tier | ||||
|   | ||||
		Reference in New Issue
	
	Block a user