1
Fork 0
mirror of https://bitbucket.org/Ioncannon/project-meteor-server.git synced 2025-04-22 20:57:47 +00:00

Add ActionLanded to make some checks cleaner.

Split physical and magic text id dictionaries.

Add calculation for resists.
This commit is contained in:
Yogurt 2019-05-27 16:52:56 -07:00
parent d0dca62a91
commit f6104812a5
3 changed files with 76 additions and 43 deletions

View file

@ -1109,7 +1109,7 @@ namespace FFXIVClassic_Map_Server.Actors
lua.LuaEngine.CallLuaBattleCommandFunction(this, command, folder, "onSkillFinish", this, chara, command, action, actions);
//cached script
//skill.CallLuaFunction(owner, "onSkillFinish", this, chara, command, action, actions);
if (action.hitType > HitType.Evade && action.hitType != HitType.Resist)
if (action.ActionLanded())
{
hitTarget = true;
hitCount++;

View file

@ -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

View file

@ -195,9 +195,12 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
Evade = 1,
Parry = 2,
Block = 3,
Resist = 4,
Hit = 5,
Crit = 6
SingleResist = 4,
DoubleResist = 5,
TripleResist = 6,
FullResist = 7,
Hit = 8,
Crit = 9
}
//Type of action
@ -387,5 +390,11 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
{
return (ushort)hitType;
}
//Whether this action didn't miss, and wasn't evaded or resisted
public bool ActionLanded()
{
return hitType > HitType.Evade && hitType != HitType.SingleResist && hitType != HitType.DoubleResist && hitType != HitType.FullResist;
}
}
}