Combat changes and bug fixes

Added the combo and proc systems
Added scripts for most weaponskill and spells as well as some abilities and status effects
Added support for multihit attacks
Added AbilityState for abilities
Added hiteffects that change based on an attack's parameters
Added positionals

Changed how targeting works for battlecommands

Fixed bug that occurred when moving or swapping hotbar commands
Fixed bug that occurred when losing status effects
This commit is contained in:
yogurt 2018-02-15 13:20:46 -06:00
parent 837c7a9223
commit b8d6a943aa
175 changed files with 4361 additions and 1213 deletions

View file

@ -1,5 +1,10 @@
using FFXIVClassic.Common;
using System;
using System.Collections.Generic;
using FFXIVClassic_Map_Server.actors.chara.ai;
using FFXIVClassic_Map_Server.actors.chara.ai.utils;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.packets.send.actor.battle;
namespace FFXIVClassic_Map_Server.packets.send.actor.battle
{
@ -9,13 +14,18 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
{
//All HitEffects have the last byte 0x8
HitEffectType = 8 << 24,
//Status effects use 32 << 24
StatusEffectType = 32 << 24,
//Magic effects use 48 << 24
MagicEffectType = 48 << 24,
//Not setting RecoilLv2 or RecoilLv3 results in the weaker RecoilLv1.
//These are the recoil animations that play on the target, ranging from weak to strong.
//The recoil that gets set was likely based on the percentage of HP lost from the attack.
RecoilLv1 = 0 | HitEffectType,
RecoilLv2 = 1 << 0 | HitEffectType,
RecoilLv3 = 1 << 1 | HitEffectType,
//These also have a visual effect with heals but in reverse. RecoilLv1 has a large effect, Lv3 has none. Crit is very large
RecoilLv1 = 0,
RecoilLv2 = 1 << 0,
RecoilLv3 = 1 << 1,
//Setting both recoil flags triggers the "Critical!" pop-up text and hit visual effect.
CriticalHit = RecoilLv2 | RecoilLv3,
@ -24,10 +34,19 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
//Mixing these flags together will yield different results.
//Each visual likely relates to a specific weapon.
//Ex: HitVisual4 flag alone appears to be the visual and sound effect for hand-to-hand attacks.
HitVisual1 = 1 << 2 | HitEffectType,
HitVisual2 = 1 << 3 | HitEffectType,
HitVisual3 = 1 << 4 | HitEffectType,
HitVisual4 = 1 << 5 | HitEffectType,
//HitVisual is probably based on attack property.
//HitVisual1 is for slashing attacks
//HitVisual2 is for piercing attacks
//HitVisual1 | Hitvisual2 is for blunt attacks
//HitVisual3 is for projectile attacks
//Basically take the attack property of a weapon and shift it left 2
//For auto attacks attack property is weapon's damageAttributeType1
//Still not totally sure how this works with weaponskills or what hitvisual4 or the other combinations are for
HitVisual1 = 1 << 2,
HitVisual2 = 1 << 3,
HitVisual3 = 1 << 4,
HitVisual4 = 1 << 5,
//An additional visual effect that plays on the target when attacked if:
//The attack is physical and they have the protect buff on.
@ -40,19 +59,27 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
Shell = 1 << 7 | HitEffectType,
ProtectShellSpecial = Protect | Shell,
//Unknown = 1 << 8, -- Not sure what this flag does.
// Required for heal text to be blue, not sure if that's all it's used for
Heal = 1 << 8,
//If only HitEffect1 is set out of the hit effects, the "Evade!" pop-up text triggers along with the evade visual.
//If no hit effects are set, the "Miss!" pop-up is triggered and no hit visual is played.
HitEffect1 = 1 << 9 | HitEffectType,
HitEffect2 = 1 << 10 | HitEffectType, //Plays the standard hit visual effect, but with no sound if used alone.
Hit = HitEffect1 | HitEffect2, //A standard hit effect with sound effect.
HitEffect3 = 1 << 11 | HitEffectType,
HitEffect4 = 1 << 12 | HitEffectType,
HitEffect5 = 1 << 13 | HitEffectType,
HitEffect1 = 1 << 9,
HitEffect2 = 1 << 10, //Plays the standard hit visual effect, but with no sound if used alone.
HitEffect3 = 1 << 11, //Yellow effect, crit?
HitEffect4 = 1 << 12, //Plays the blocking animation
HitEffect5 = 1 << 13,
GustyHitEffect = HitEffect3 | HitEffect2,
GreenTintedHitEffect = HitEffect4 | HitEffect1,
//For specific animations
Miss = 0,
Evade = HitEffect1,
Hit = HitEffect1 | HitEffect2,
Parry = Hit | HitEffect3,
Block = HitEffect4,
Crit = HitEffect3,
//Knocks you back away from the attacker.
KnockbackLv1 = HitEffect4 | HitEffect2 | HitEffect1,
KnockbackLv2 = HitEffect4 | HitEffect3,
@ -80,10 +107,10 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
//A special effect when performing appropriate skill combos in succession.
//Ex: Thunder (SkillCombo1 Effect) -> Thundara (SkillCombo2 Effect) -> Thundaga (SkillCombo3 Effect)
//Special Note: SkillCombo4 was never actually used in 1.0 since combos only chained up to 3 times maximum.
SkillCombo1 = 1 << 15 | HitEffectType,
SkillCombo2 = 1 << 16 | HitEffectType,
SkillCombo1 = 1 << 15,
SkillCombo2 = 1 << 16,
SkillCombo3 = SkillCombo1 | SkillCombo2,
SkillCombo4 = 1 << 17 | HitEffectType
SkillCombo4 = 1 << 17
//Flags beyond here are unknown/untested.
}
@ -100,19 +127,45 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
Left = 1 << 3
}
public enum HitType : ushort
{
Miss = 0,
Evade = 1,
Parry = 2,
Block = 3,
Resist = 4,
Hit = 5,
Crit = 6
}
public enum BattleActionType
{
None = 0,
AttackPhysical = 1,
AttackMagic = 2,
Heal = 3,
Status = 4
}
class BattleAction
{
public uint targetId;
public ushort amount;
public ushort enmity; //Seperate from amount for abilities that cause a different amount of enmity than damage
public ushort worldMasterTextId;
public uint effectId;
public byte param;
public byte unknown;
public uint effectId; //Impact effect, damage/heal/status numbers or name
public byte param; //Which side the battle action is coming from
public byte hitNum; //Which hit in a sequence of hits this is
public HitType hitType;
//Need a list of actions for commands that may both deal damage and inflict status effects
public List<BattleAction> actionsList;
/// <summary>
/// this field is not actually part of the packet struct
/// </summary>
public uint animation;
public BattleActionType battleActionType;
public BattleAction(uint targetId, ushort worldMasterTextId, uint effectId, ushort amount = 0, byte param = 0, byte unknown = 1)
{
@ -121,7 +174,38 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
this.effectId = effectId;
this.amount = amount;
this.param = param;
this.unknown = unknown;
this.hitNum = unknown;
this.hitType = HitType.Hit;
this.enmity = amount;
this.actionsList = new List<BattleAction>();
this.battleActionType = BattleActionType.None;
actionsList.Add(this);
}
public void AddStatusAction(uint targetId, uint effectId)
{
actionsList.Add(new BattleAction(targetId, 30328, effectId | (uint) HitEffect.StatusEffectType));
}
public void AddHealAction(uint targetId, ushort amount)
{
var a = new BattleAction(targetId, 30320, (uint)(HitEffect.MagicEffectType | HitEffect.RecoilLv3 | HitEffect.Heal), amount);
actionsList.Add(a);
}
public void CalcHitType(Character caster, Character target, BattleCommand skill)
{
BattleUtils.CalcHitType(caster, target, skill, this);
}
public void TryStatus(Character caster, Character target, BattleCommand skill, bool isAdditional = true)
{
BattleUtils.TryStatus(caster, target, skill, this, isAdditional);
}
public List<BattleAction> GetAllActions()
{
return actionsList;
}
}
}

View file

@ -2,7 +2,7 @@
using System;
using System.IO;
namespace FFXIVClassic_Map_Server.packets.send.actor.battle
namespace FFXIVClassic_Map_Server.packets.send.actor.battle
{
// see xtx_command
enum BattleActionX01PacketCommand : ushort
@ -25,7 +25,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
using (BinaryWriter binWriter = new BinaryWriter(mem))
{
binWriter.Write((UInt32)sourceActorId);
binWriter.Write((UInt32)animationId);
//Missing... last value is float, string in here as well?
@ -39,15 +39,15 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
binWriter.Write((UInt16)action.amount);
binWriter.Write((UInt16)action.worldMasterTextId);
Program.Log.Info(action.worldMasterTextId);
binWriter.Write((UInt32)action.effectId);
binWriter.Write((Byte)action.param);
binWriter.Write((Byte)1); //?
}
}
new SubPacket(OPCODE, sourceActorId, data).DebugPrintSubPacket();
return new SubPacket(OPCODE, sourceActorId, data);
}
}
}
}

View file

@ -57,7 +57,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
binWriter.Seek(0xAA, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].unknown);
binWriter.Write((Byte)actionList[listOffset + i].hitNum);
listOffset += max;
}
@ -112,7 +112,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
binWriter.Seek(0xAA, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].unknown);
binWriter.Write((Byte)actionList[listOffset + i].hitNum);
listOffset += max;
}

View file

@ -57,7 +57,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
binWriter.Seek(0x112, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].unknown);
binWriter.Write((Byte)actionList[listOffset + i].hitNum);
listOffset += max;
}
@ -86,33 +86,33 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
//Missing... last value is float, string in here as well?
binWriter.Seek(0x20, SeekOrigin.Begin);
binWriter.Write((UInt32)max); //Num actions
binWriter.Write((UInt32)actionList.Count); //Num actions
binWriter.Write((UInt16)commandId);
binWriter.Write((UInt16)0x810); //?
binWriter.Seek(0x58, SeekOrigin.Begin);
binWriter.Seek(0x28, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt32)actionList[listOffset + i].targetId);
binWriter.Seek(0xA0, SeekOrigin.Begin);
binWriter.Seek(0x70, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt16)actionList[listOffset + i].amount);
binWriter.Seek(0xC4, SeekOrigin.Begin);
binWriter.Seek(0x94, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt16)actionList[listOffset + i].worldMasterTextId);
binWriter.Seek(0xE8, SeekOrigin.Begin);
binWriter.Seek(0xB8, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt32)actionList[listOffset + i].effectId);
binWriter.Seek(0x130, SeekOrigin.Begin);
binWriter.Seek(0x100, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].param);
binWriter.Seek(0x142, SeekOrigin.Begin);
binWriter.Seek(0x112, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].unknown);
binWriter.Write((Byte)actionList[listOffset + i].hitNum);
listOffset += max;
}