mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-04-02 19:42:05 -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:
parent
d0dca62a91
commit
f6104812a5
@ -1109,7 +1109,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
|||||||
lua.LuaEngine.CallLuaBattleCommandFunction(this, command, folder, "onSkillFinish", this, chara, command, action, actions);
|
lua.LuaEngine.CallLuaBattleCommandFunction(this, command, folder, "onSkillFinish", this, chara, command, action, actions);
|
||||||
//cached script
|
//cached script
|
||||||
//skill.CallLuaFunction(owner, "onSkillFinish", this, chara, command, action, actions);
|
//skill.CallLuaFunction(owner, "onSkillFinish", this, chara, command, action, actions);
|
||||||
if (action.hitType > HitType.Evade && action.hitType != HitType.Resist)
|
if (action.ActionLanded())
|
||||||
{
|
{
|
||||||
hitTarget = true;
|
hitTarget = true;
|
||||||
hitCount++;
|
hitCount++;
|
||||||
|
@ -17,36 +17,54 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
|||||||
static class BattleUtils
|
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.Miss, 30311 },
|
||||||
{ HitType.Evade, 30310 },
|
{ HitType.Evade, 30310 },
|
||||||
{ HitType.Parry, 30308 },
|
{ HitType.Parry, 30308 },
|
||||||
{ HitType.Block, 30306 },
|
{ HitType.Block, 30306 },
|
||||||
{ HitType.Resist, 30310 }, //Resists seem to use the evade text id
|
|
||||||
{ HitType.Hit, 30301 },
|
{ HitType.Hit, 30301 },
|
||||||
{ HitType.Crit, 30302 }
|
{ 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>()
|
public static Dictionary<HitType, ushort> MultiHitTypeTextIds = new Dictionary<HitType, ushort>()
|
||||||
{
|
{
|
||||||
{ HitType.Miss, 30449 }, //The attack misses.
|
{ 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.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.Parry, 30448 }, //[Target] parries, taking x points of damage.
|
||||||
{ HitType.Block, 30447 }, //[Target] blocks, 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.Hit, 30443 }, //[Target] tales x points of damage
|
||||||
{ HitType.Crit, 30444 } //Critical! [Target] takes 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.Miss, 0 },
|
||||||
{ HitType.Evade, HitEffect.Evade },
|
{ HitType.Evade, HitEffect.Evade },
|
||||||
{ HitType.Parry, HitEffect.Parry },
|
{ HitType.Parry, HitEffect.Parry },
|
||||||
{ HitType.Block, HitEffect.Block },
|
{ 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.Hit, HitEffect.Hit },
|
||||||
|
{ 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 }
|
{ HitType.Crit, HitEffect.Crit }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -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.
|
//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)
|
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.amountMitigated = (ushort)(action.amount * (1 - percentResist));
|
||||||
action.amount = (ushort)(action.amount * percentResist);
|
action.amount = (ushort)(action.amount * percentResist);
|
||||||
@ -360,11 +379,26 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
|||||||
return false;
|
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)
|
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);
|
CalculateResistDamage(attacker, defender, skill, action);
|
||||||
return true;
|
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.
|
//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
|
//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.
|
//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)
|
public static void FinishActionSpell(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||||
{
|
{
|
||||||
//Determine the hit type of the action
|
//I'm assuming that like physical attacks stoneskin is taken into account before mitigation
|
||||||
if (!TryMiss(attacker, defender, skill, action))
|
|
||||||
{
|
|
||||||
HandleStoneskin(defender, action);
|
HandleStoneskin(defender, action);
|
||||||
if (!TryCrit(attacker, defender, skill, action))
|
|
||||||
|
//Determine the hit type of the 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))
|
if (!TryResist(attacker, defender, skill, action))
|
||||||
|
{
|
||||||
|
if (!TryCrit(attacker, defender, skill, action))
|
||||||
action.hitType = HitType.Hit;
|
action.hitType = HitType.Hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
//There are no multi-hit spells
|
//There are no multi-hit spells, so we don't need to take that into account
|
||||||
action.worldMasterTextId = SingleHitTypeTextIds[action.hitType];
|
action.worldMasterTextId = MagicalHitTypeTextIds[action.hitType];
|
||||||
|
|
||||||
//Set the hit effect
|
//Set the hit effect
|
||||||
SetHitEffectSpell(attacker, defender, skill, action);
|
SetHitEffectSpell(attacker, defender, skill, action);
|
||||||
@ -547,10 +584,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
|||||||
hitEffect |= HitEffect.RecoilLv3;
|
hitEffect |= HitEffect.RecoilLv3;
|
||||||
}
|
}
|
||||||
|
|
||||||
hitEffect |= HitTypeEffects[hitType];
|
hitEffect |= HitTypeEffectsPhysical[hitType];
|
||||||
|
|
||||||
//For combos that land, add the combo effect
|
//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);
|
hitEffect |= (HitEffect)(skill.comboStep << 15);
|
||||||
skill.comboEffectAdded = true;
|
skill.comboEffectAdded = true;
|
||||||
@ -560,7 +597,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
|||||||
if (hitType >= HitType.Parry)
|
if (hitType >= HitType.Parry)
|
||||||
{
|
{
|
||||||
//Protect / Shell only show on physical/ magical attacks respectively.
|
//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)
|
if (action != null)
|
||||||
hitEffect |= HitEffect.Protect;
|
hitEffect |= HitEffect.Protect;
|
||||||
|
|
||||||
@ -577,20 +614,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
|||||||
var hitEffect = HitEffect.MagicEffectType;
|
var hitEffect = HitEffect.MagicEffectType;
|
||||||
HitType hitType = action.hitType;
|
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)
|
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 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.
|
//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 (defender.statusEffects.HasStatusEffect(StatusEffectId.Shell))
|
||||||
if (action != null)
|
if (action != null)
|
||||||
hitEffect |= HitEffect.Shell;
|
hitEffect |= HitEffect.MagicShell;
|
||||||
|
|
||||||
if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin))
|
|
||||||
if (action != null)
|
|
||||||
hitEffect |= HitEffect.Stoneskin;
|
|
||||||
}
|
}
|
||||||
action.effectId = (uint)hitEffect;
|
action.effectId = (uint)hitEffect;
|
||||||
}
|
}
|
||||||
@ -659,7 +683,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
|
|||||||
double rand = Program.Random.NextDouble();
|
double rand = Program.Random.NextDouble();
|
||||||
|
|
||||||
//Statuses only land for non-resisted attacks and attacks that hit
|
//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);
|
StatusEffect effect = Server.GetWorldManager().GetStatusEffect(skill.statusId);
|
||||||
//Because combos might change duration or tier
|
//Because combos might change duration or tier
|
||||||
|
@ -195,9 +195,12 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
|||||||
Evade = 1,
|
Evade = 1,
|
||||||
Parry = 2,
|
Parry = 2,
|
||||||
Block = 3,
|
Block = 3,
|
||||||
Resist = 4,
|
SingleResist = 4,
|
||||||
Hit = 5,
|
DoubleResist = 5,
|
||||||
Crit = 6
|
TripleResist = 6,
|
||||||
|
FullResist = 7,
|
||||||
|
Hit = 8,
|
||||||
|
Crit = 9
|
||||||
}
|
}
|
||||||
|
|
||||||
//Type of action
|
//Type of action
|
||||||
@ -387,5 +390,11 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
|||||||
{
|
{
|
||||||
return (ushort)hitType;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user