diff --git a/FFXIVClassic Common Class Lib/Vector3.cs b/FFXIVClassic Common Class Lib/Vector3.cs index f07dadb5..60166d7f 100644 --- a/FFXIVClassic Common Class Lib/Vector3.cs +++ b/FFXIVClassic Common Class Lib/Vector3.cs @@ -88,6 +88,9 @@ namespace FFXIVClassic.Common public static float GetAngle(float x, float z, float x2, float z2) { + if (x == x2) + return 0.0f; + var angle = (float)(Math.Atan((z2 - z) / (x2 - x))); return (float)(x > x2 ? angle + Math.PI : angle); } @@ -104,6 +107,9 @@ namespace FFXIVClassic.Common public bool IsWithinCircle(Vector3 centre, float radius) { + if (this.X == centre.X && this.Z == centre.Z) + return true; + float diffX = centre.X - this.X; float diffZ = centre.Z - this.Z; diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index d9b02a2d..efd68294 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -2154,8 +2154,8 @@ namespace FFXIVClassic_Map_Server { conn.Open(); - var query = ("SELECT `id`, name, classJob, lvl, requirements, validTarget, aoeType, numHits, positionBonus, procRequirement, `range`, buffDuration, debuffDuration, " + - "castType, castTime, recastTime, mpCost, tpCost, animationType, effectAnimation, modelAnimation, animationDuration, aoeRange FROM server_battle_commands;"); + var query = ("SELECT `id`, name, classJob, lvl, requirements, validTarget, aoeType, aoeRange, aoeTarget, numHits, positionBonus, procRequirement, `range`, buffDuration, debuffDuration, " + + "castType, castTime, recastTime, mpCost, tpCost, animationType, effectAnimation, modelAnimation, animationDuration, battleAnimation FROM server_battle_commands;"); MySqlCommand cmd = new MySqlCommand(query, conn); @@ -2188,7 +2188,9 @@ namespace FFXIVClassic_Map_Server battleCommand.modelAnimation = reader.GetUInt16("modelAnimation"); battleCommand.animationDurationSeconds = reader.GetUInt16("animationDuration"); battleCommand.aoeRange = reader.GetInt32("aoeRange"); - battleCommand.battleAnimation = (uint)((battleCommand.animationType << 24) | (battleCommand.modelAnimation << 12) | (battleCommand.effectAnimation)); + battleCommand.aoeTarget = (TargetFindAOETarget)reader.GetByte("aoeTarget"); + + battleCommand.battleAnimation = reader.GetUInt32("battleAnimation"); battleCommands.Add(id, battleCommand); } diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 9de0c8ab..d9f7d6b1 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -96,6 +96,7 @@ + diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index 09b7b7ab..3fdad311 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -154,6 +154,8 @@ namespace FFXIVClassic_Map_Server case 0x00CC: LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data); session.GetActor().currentLockedTarget = lockTarget.actorID; + session.GetActor().isAutoAttackEnabled = lockTarget.otherVal == 0x00000040; + break; //Start Event case 0x012D: diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index 78e8d1cc..74ee49e4 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -365,10 +365,10 @@ namespace FFXIVClassic_Map_Server.Actors public void ChangeState(ushort newState) { - if (newState != currentMainState) + //if (newState != currentMainState) { currentMainState = newState; - updateFlags |= ActorUpdateFlags.State; + updateFlags |= (ActorUpdateFlags.State | ActorUpdateFlags.Position); } } @@ -402,7 +402,7 @@ namespace FFXIVClassic_Map_Server.Actors if (positionUpdates != null && positionUpdates.Count > 0) { // push latest for player - var pos = positionUpdates[currentSubState == SetActorStatePacket.SUB_STATE_PLAYER ? positionUpdates.Count - 1 : 0]; + var pos = positionUpdates[0]; oldPositionX = positionX; oldPositionY = positionY; oldPositionZ = positionZ; @@ -417,8 +417,8 @@ namespace FFXIVClassic_Map_Server.Actors //Program.Server.GetInstance().mLuaEngine.OnPath(actor, position, positionUpdates) positionUpdates.Remove(pos); - packets.Add(CreatePositionUpdatePacket()); } + packets.Add(CreatePositionUpdatePacket()); } if ((updateFlags & ActorUpdateFlags.Speed) != 0) @@ -434,8 +434,6 @@ namespace FFXIVClassic_Map_Server.Actors if ((updateFlags & ActorUpdateFlags.State) != 0) { packets.Add(SetActorStatePacket.BuildPacket(actorId, currentMainState, currentSubState)); - if (this is Character) - ((Character)this).DoBattleAction(21001, 0x7C000062, new BattleAction(this.actorId, 0, 1, 0, 0, 1)); //Attack Mode } updateFlags = ActorUpdateFlags.None; diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index 7e86146a..5d2f3ec1 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -321,6 +321,11 @@ namespace FFXIVClassic_Map_Server.Actors } } + public T FindActorInArea(uint id) where T : Actor + { + return FindActorInArea(id) as T; + } + public Actor FindActorInZoneByUniqueID(string uniqueId) { lock (mActorList) diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 28b3db64..444e22be 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -67,6 +67,7 @@ namespace FFXIVClassic_Map_Server.Actors public bool isStatic = false; public bool isMovingToSpawn = false; + public bool isAutoAttackEnabled = true; public uint modelId; public uint[] appearanceIds = new uint[28]; @@ -98,6 +99,8 @@ namespace FFXIVClassic_Map_Server.Actors protected ushort hpBase, hpMaxBase, mpBase, mpMaxBase, tpBase; protected BattleTemp baseStats = new BattleTemp(); public ushort currentJob; + public ushort newMainState; + public float spawnX, spawnY, spawnZ; public Character(uint actorID) : base(actorID) { @@ -112,6 +115,10 @@ namespace FFXIVClassic_Map_Server.Actors // todo: base this on equip and shit SetMod((uint)Modifier.AttackRange, 3); SetMod((uint)Modifier.AttackDelay, (Program.Random.Next(30, 60) * 100)); + + spawnX = positionX; + spawnY = positionY; + spawnZ = positionZ; } public SubPacket CreateAppearancePacket() @@ -305,7 +312,13 @@ namespace FFXIVClassic_Map_Server.Actors if ((updateFlags & ActorUpdateFlags.State) != 0) { + packets.Add(SetActorStatePacket.BuildPacket(actorId, currentMainState, currentSubState)); packets.Add(BattleActionX00Packet.BuildPacket(actorId, 0x72000062, 0)); + packets.Add(BattleActionX01Packet.BuildPacket(actorId, 0x7C000062, 21001, new BattleAction(actorId, 0, 1))); + + // this is silly, but looks like state change goes full retard unless theyre sent in order + updateFlags &= ~ActorUpdateFlags.State; + //DoBattleAction(21001, 0x7C000062, new BattleAction(this.actorId, 0, 1, 0, 0, 1)); //Attack Mode } // todo: should probably add another flag for battleTemp since all this uses reflection @@ -357,26 +370,37 @@ namespace FFXIVClassic_Map_Server.Actors return (uint)GetMod((uint)Modifier.AttackRange); } - public bool Engage(uint targid = 0) + public virtual bool Engage(uint targid = 0, ushort newMainState = 0xFFFF) { // todo: attack the things - if (targid == 0) + if (newMainState != 0xFFFF) { - if (currentTarget != 0xC0000000) - targid = currentTarget; - else if (currentLockedTarget != 0xC0000000) - targid = currentLockedTarget; + this.newMainState = newMainState; } - if (targid != 0) + else if (aiContainer.CanChangeState()) { - aiContainer.Engage(Server.GetWorldManager().GetActorInWorld(targid) as Character); + if (targid == 0) + { + if (currentTarget != 0xC0000000) + targid = currentTarget; + else if (currentLockedTarget != 0xC0000000) + targid = currentLockedTarget; + } + //if (targid != 0) + { + aiContainer.Engage(zone.FindActorInArea(targid)); + } } return false; } - public bool Disengage() + public virtual bool Disengage(ushort newMainState = 0xFFFF) { - if (aiContainer != null) + if (newMainState != 0xFFFF) + { + this.newMainState = newMainState; + } + else { aiContainer.Disengage(); return true; @@ -384,19 +408,22 @@ namespace FFXIVClassic_Map_Server.Actors return false; } - public void Cast(uint spellId, uint targetId = 0) + public virtual void Cast(uint spellId, uint targetId = 0) { - aiContainer.Cast(Server.GetWorldManager().GetActorInWorld(targetId == 0 ? currentTarget : targetId) as Character, spellId); + if (aiContainer.CanChangeState()) + aiContainer.Cast(zone.FindActorInArea(targetId == 0 ? currentTarget : targetId), spellId); } - public void Ability(uint abilityId, uint targetId = 0) + public virtual void Ability(uint abilityId, uint targetId = 0) { - aiContainer.Ability(Server.GetWorldManager().GetActorInWorld(targetId == 0 ? currentTarget : targetId) as Character, abilityId); + if (aiContainer.CanChangeState()) + aiContainer.Ability(zone.FindActorInArea(targetId == 0 ? currentTarget : targetId), abilityId); } - public void WeaponSkill(uint skillId) + public virtual void WeaponSkill(uint skillId, uint targetId = 0) { - aiContainer.WeaponSkill(Server.GetWorldManager().GetActorInWorld(currentTarget) as Character, skillId); + if (aiContainer.CanChangeState()) + aiContainer.WeaponSkill(zone.FindActorInArea(targetId == 0 ? currentTarget : targetId), skillId); } public virtual void Spawn(DateTime tick) @@ -414,9 +441,9 @@ namespace FFXIVClassic_Map_Server.Actors aiContainer.InternalDie(tick, 10); } - protected virtual void Despawn(DateTime tick) + public virtual void Despawn(DateTime tick) { - + } public bool IsDead() @@ -551,20 +578,23 @@ namespace FFXIVClassic_Map_Server.Actors //var packet = BattleActionX01Packet.BuildPacket(owner.actorId, owner.actorId, target.actorId, (uint)0x19001000, (uint)0x8000604, (ushort)0x765D, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, (byte)0x1); } + // todo: call onAttack/onDamageTaken target.DelHP(action.amount); } - public virtual void OnCast(State state, BattleAction action, ref SubPacket errorPacket) + public virtual void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors) { - + var spell = ((MagicState)state).GetSpell(); + this.DelMP(spell.mpCost); // mpCost can be set in script e.g. if caster has something for free spells } - public virtual void OnWeaponSkill(State state, BattleAction action, ref SubPacket errorPacket) + public virtual void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors) { - + var skill = ((WeaponSkillState)state).GetWeaponSkill(); + this.DelTP(skill.tpCost); } - public virtual void OnAbility(State state, BattleAction action, ref SubPacket errorPacket) + public virtual void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors) { } diff --git a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs index be0a7f7e..63457e48 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs @@ -23,6 +23,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public readonly PathFind pathFind; private TargetFind targetFind; private ActionQueue actionQueue; + private DateTime lastActionTime; public AIContainer(Character actor, Controller controller, PathFind pathFind, TargetFind targetFind) { @@ -36,6 +37,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai actionQueue = new ActionQueue(owner); } + public void UpdateLastActionTime() + { + lastActionTime = DateTime.Now; + } + + public DateTime GetLastActionTime() + { + return lastActionTime; + } + public void Update(DateTime tick) { prevUpdate = latestUpdate; @@ -186,13 +197,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public bool IsEngaged() { // todo: check this is legit - return owner.currentMainState == SetActorStatePacket.MAIN_STATE_ACTIVE && owner.target != null; + return owner.currentMainState == SetActorStatePacket.MAIN_STATE_ACTIVE; } public bool IsDead() { return owner.currentMainState == SetActorStatePacket.MAIN_STATE_DEAD || - owner.currentMainState == SetActorStatePacket.MAIN_STATE_DEAD2; + owner.currentMainState == SetActorStatePacket.MAIN_STATE_DEAD2 || owner.GetHP() == 0; } public bool IsRoaming() @@ -253,10 +264,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai { // todo: use invalid target id // todo: this is retarded, call entity's changetarget function - owner.target = target; - owner.currentLockedTarget = target != null ? target.actorId : 0xC0000000; - owner.currentTarget = target != null ? target.actorId : 0xC0000000; - if (IsEngaged() || target == null) { @@ -293,8 +300,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai GetTargetFind()?.Reset(); owner.updateFlags |= ActorUpdateFlags.HpTpMp; - ChangeTarget(null); + owner.ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE); ClearStates(); } diff --git a/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs b/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs index e1eab7ed..cae67bbd 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs @@ -53,6 +53,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public BattleCommandRequirements requirements; public ValidTarget validTarget; public TargetFindAOEType aoeType; + public TargetFindAOETarget aoeTarget; public byte numHits; public BattleCommandPositionBonus positionBonus; public BattleCommandProcRequirement procRequirement; @@ -105,11 +106,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (aoeType == TargetFindAOEType.Box) { // todo: read box width from sql - targetFind.SetAOEBox(validTarget, range, aoeRange); + targetFind.SetAOEBox(validTarget, aoeTarget, range, aoeRange); } else { - targetFind.SetAOEType(validTarget, aoeType, range, aoeRange); + targetFind.SetAOEType(validTarget, aoeType, aoeTarget, range, aoeRange); } @@ -183,8 +184,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if ((validTarget & ValidTarget.Enemy) != 0) { if (target == user || target != null && - target.currentSubState != (user.currentSubState == SetActorStatePacket.SUB_STATE_MONSTER ? - SetActorStatePacket.SUB_STATE_PLAYER : SetActorStatePacket.SUB_STATE_MONSTER)) + user.allegiance == target.allegiance) { if (user is Player) ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32519, 0x20, (uint)id); @@ -194,14 +194,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if ((validTarget & (ValidTarget.PartyMember | ValidTarget.Player | ValidTarget.Ally)) != 0) { - if (target == null || target.currentSubState != user.currentSubState ) + if (target == null || target.allegiance != user.allegiance) { if (user is Player) ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32516, 0x20, (uint)id); return false; } } - return targetFind.CanTarget(target, true, true); + return targetFind.CanTarget(target, true, true, true); } public ushort CalculateCost(uint level) diff --git a/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs b/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs index 250f6ca6..98025a49 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs @@ -195,13 +195,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai float baseSpeed = owner.GetSpeed(); // todo: get actual speed crap - if (owner.currentSubState != SetActorStatePacket.SUB_STATE_NONE) + if (!(owner is Player)) { - if (owner.currentSubState == SetActorStatePacket.SUB_STATE_MONSTER) + if (owner is BattleNpc) { //owner.ChangeSpeed(0.0f, SetActorSpeedPacket.DEFAULT_WALK - 2.0f, SetActorSpeedPacket.DEFAULT_RUN - 2.0f, SetActorSpeedPacket.DEFAULT_ACTIVE - 2.0f); } - // baseSpeed += ConfigConstants.SPEED_MOD; + // baseSpeed += ConfigConstants.NPC_SPEED_MOD; } return baseSpeed; } diff --git a/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs b/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs index 9ca98bc6..4c2a80d6 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/TargetFind.cs @@ -7,6 +7,7 @@ using FFXIVClassic_Map_Server.Actors; using FFXIVClassic.Common; using FFXIVClassic_Map_Server.actors.chara.ai; using FFXIVClassic_Map_Server.actors.chara.ai.controllers; +using FFXIVClassic_Map_Server.actors.group; using FFXIVClassic_Map_Server.packets.send.actor; // port of dsp's ai code https://github.com/DarkstarProject/darkstar/blob/master/src/map/ai/ @@ -48,9 +49,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai enum TargetFindAOEType : byte { None, - /// Really a cylinder, uses extents parameter in SetAOEType + /// Really a cylinder, uses maxDistance parameter in SetAOEType Circle, - /// Create a cone with angle in radians + /// Create a cone with param in radians Cone, /// Box using self/target coords and Box @@ -69,14 +70,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai class TargetFind { private Character owner; - private Character target; private Character masterTarget; // if target is a pet, this is the owner private TargetFindCharacterType findType; private ValidTarget validTarget; + private TargetFindAOETarget aoeTarget; private TargetFindAOEType aoeType; private Vector3 targetPosition; - private float extents; - private float angle; + private float maxDistance; + private float param; private List targets; public TargetFind(Character owner) @@ -87,13 +88,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public void Reset() { - this.target = null; this.findType = TargetFindCharacterType.None; this.validTarget = ValidTarget.Enemy; this.aoeType = TargetFindAOEType.None; + this.aoeTarget = TargetFindAOETarget.Target; this.targetPosition = null; - this.extents = 0.0f; - this.angle = 0.0f; + this.maxDistance = 0.0f; + this.param = 0.0f; this.targets = new List(); } @@ -110,28 +111,29 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai /// /// Call this before /// - /// + /// /// - radius of circle /// - height of cone /// - width of box / 2 (todo: set box length not just between user and target) /// - /// Angle in radians of cone - public void SetAOEType(ValidTarget validTarget, TargetFindAOEType aoeType, float extents = -1.0f, float angle = -1.0f) + /// param in degrees of cone (todo: probably use radians and forget converting at runtime) + public void SetAOEType(ValidTarget validTarget, TargetFindAOEType aoeType, TargetFindAOETarget aoeTarget, float maxDistance = -1.0f, float param = -1.0f) { this.validTarget = validTarget; this.aoeType = aoeType; - this.extents = extents != -1.0f ? extents : 0.0f; - this.angle = angle != -1.0f ? angle : 0.0f; + this.maxDistance = maxDistance != -1.0f ? maxDistance : 0.0f; + this.param = param != -1.0f ? param : 0.0f; } - public void SetAOEBox(ValidTarget validTarget, float length, float width) + public void SetAOEBox(ValidTarget validTarget, TargetFindAOETarget aoeTarget, float length, float width) { this.validTarget = validTarget; this.aoeType = TargetFindAOEType.Box; + this.aoeTarget = aoeTarget; float x = owner.positionX - (float)Math.Cos(owner.rotation + (float)(Math.PI / 2)) * (length); float z = owner.positionZ + (float)Math.Sin(owner.rotation + (float)(Math.PI / 2)) * (length); this.targetPosition = new Vector3(x, owner.positionY, z); - this.extents = width; + this.maxDistance = width; } /// @@ -140,7 +142,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public void FindTarget(Character target, ValidTarget flags) { validTarget = flags; - this.target = null; // todo: maybe this should only be set if successfully added? this.targetPosition = target.GetPosAsVector3(); AddTarget(target, false); @@ -150,21 +151,18 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai /// Call SetAOEType before calling this /// Find targets within area set by /// - public void FindWithinArea(Character target, ValidTarget flags) + public void FindWithinArea(Character target, ValidTarget flags, TargetFindAOETarget aoeTarget) { validTarget = flags; // todo: maybe we should keep a snapshot which is only updated on each tick for consistency // are we creating aoe circles around target or self - if ((aoeType & TargetFindAOEType.Circle) != 0 && validTarget != ValidTarget.Self) + if (aoeTarget == TargetFindAOETarget.Self) this.targetPosition = owner.GetPosAsVector3(); else this.targetPosition = target.GetPosAsVector3(); masterTarget = TryGetMasterTarget(target) ?? target; - // todo: should i set this yet or wait til checked if valid target - this.target = target; - // todo: this is stupid bool withPet = (flags & ValidTarget.Ally) != 0 || masterTarget.allegiance != owner.allegiance; @@ -180,7 +178,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai // todo: handle player parties if (masterTarget.currentParty != null) { - if ((validTarget & ValidTarget.Ally) != 0) + if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0) AddAllInAlliance(masterTarget, withPet); else AddAllInParty(masterTarget, withPet); @@ -229,19 +227,24 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai /// /// Find targets within a box using owner's coordinates and target's coordinates as length - /// with corners being `extents` yalms to either side of self and target + /// with corners being `maxDistance` yalms to either side of self and target /// private bool IsWithinBox(Character target, bool withPet) { + if (aoeTarget == TargetFindAOETarget.Self) + targetPosition = owner.GetPosAsVector3(); + else + targetPosition = target.GetPosAsVector3(); + var myPos = owner.GetPosAsVector3(); var angle = Vector3.GetAngle(myPos, targetPosition); // todo: actually check this works.. - var myCorner = myPos.NewHorizontalVector(angle, extents); - var myCorner2 = myPos.NewHorizontalVector(angle, -extents); + var myCorner = myPos.NewHorizontalVector(angle, maxDistance); + var myCorner2 = myPos.NewHorizontalVector(angle, -maxDistance); - var targetCorner = targetPosition.NewHorizontalVector(angle, extents); - var targetCorner2 = targetPosition.NewHorizontalVector(angle, -extents); + var targetCorner = targetPosition.NewHorizontalVector(angle, maxDistance); + var targetCorner2 = targetPosition.NewHorizontalVector(angle, -maxDistance); return target.GetPosAsVector3().IsWithinBox(targetCorner2, myCorner); } @@ -249,7 +252,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private bool IsWithinCone(Character target, bool withPet) { // todo: make this actual cone - return owner.IsFacing(target, angle) && Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) < extents; + return owner.IsFacing(target, param) && Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) < maxDistance; } private void AddTarget(Character target, bool withPet) @@ -264,25 +267,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private void AddAllInParty(Character target, bool withPet) { // todo: - /* - * foreach (var actor in target.currentParty.GetMembers()) - * { - * AddTarget(actor, withPet); - * } - */ - AddTarget(target, withPet); + var party = target.currentParty as Party; + if (party != null) + { + foreach (var actorId in party.members) + { + AddTarget(owner.zone.FindActorInArea(actorId) as Character, withPet); + } + } } private void AddAllInAlliance(Character target, bool withPet) { // todo: - /* - * foreach (var actor in target.currentParty.GetAllianceMembers()) - * { - * AddTarget(actor, withPet); - * } - */ - AddTarget(target, withPet); + AddAllInParty(target, withPet); } private void AddAllBattleNpcs(Character target, bool withPet) @@ -321,7 +319,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai } } - public bool CanTarget(Character target, bool withPet = false, bool retarget = false) + public bool CanTarget(Character target, bool withPet = false, bool retarget = false, bool ignoreAOE = false) { // already targeted, dont target again if (target == null || !retarget && targets.Contains(target)) @@ -331,21 +329,18 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if ((validTarget & ValidTarget.Corpse) == 0 && target.IsDead()) return false; - bool targetingPlayer = target.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER; - - if ((validTarget & ValidTarget.Ally) != 0 && target.currentSubState != owner.currentSubState) + if ((validTarget & ValidTarget.Ally) != 0 && target.allegiance != owner.allegiance) return false; - if ((validTarget & ValidTarget.Enemy) != 0 && target.currentSubState != (owner.currentSubState == SetActorStatePacket.SUB_STATE_MONSTER ? - SetActorStatePacket.SUB_STATE_PLAYER : SetActorStatePacket.SUB_STATE_MONSTER)) + if ((validTarget & ValidTarget.Enemy) != 0 && target.allegiance == owner.allegiance) return false; - if ((validTarget & ValidTarget.NPC) != 0 && target.currentSubState != SetActorStatePacket.SUB_STATE_NONE) + if ((validTarget & ValidTarget.NPC) != 0 && target.isStatic) return false; // todo: why is player always zoning? // cant target if zoning - if (targetingPlayer && ((Player)target).playerSession.isUpdatesLocked) + if (target is Player && ((Player)target).playerSession.isUpdatesLocked) { owner.aiContainer.ChangeTarget(null); return false; @@ -354,25 +349,36 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (/*target.isZoning || owner.isZoning || */target.zone != owner.zone) return false; - if (validTarget == ValidTarget.Self && aoeType != TargetFindAOEType.None && owner != target) + if (validTarget == ValidTarget.Self && aoeType == TargetFindAOEType.None && owner != target) return false; - // hit everything within zone or within aoe region - if (extents == 255.0f || aoeType == TargetFindAOEType.Circle && target.GetPosAsVector3().IsWithinCircle(targetPosition ?? target.GetPosAsVector3(), extents)) - return true; + // this is fuckin retarded, think of a better way l8r + if (!ignoreAOE) + { + // hit everything within zone or within aoe region + if (maxDistance == -1.0f || aoeType == TargetFindAOEType.Circle && !IsWithinCircle(target, maxDistance)) + return false; - if (aoeType == TargetFindAOEType.Cone && IsWithinCone(target, withPet)) - return true; + if (aoeType == TargetFindAOEType.Cone && !IsWithinCone(target, withPet)) + return false; - if (aoeType == TargetFindAOEType.Box && IsWithinBox(target, withPet)) - return true; - - if (aoeType == TargetFindAOEType.None && targets.Count != 0) - return false; + if (aoeType == TargetFindAOEType.Box && !IsWithinBox(target, withPet)) + return false; + if (aoeType == TargetFindAOEType.None && targets.Count != 0) + return false; + } return true; } + private bool IsWithinCircle(Character target, float maxDistance) + { + if (this.targetPosition == null) + this.targetPosition = aoeTarget == TargetFindAOETarget.Self ? owner.GetPosAsVector3() : masterTarget.GetPosAsVector3(); + + return target.GetPosAsVector3().IsWithinCircle(targetPosition, param); + } + private bool IsPlayer(Character target) { if (target is Player) @@ -397,11 +403,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private bool IsBattleNpcOwner(Character target) { // i know i copied this from dsp but what even - if (owner.currentSubState != SetActorStatePacket.SUB_STATE_PLAYER || target.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER) + if (!(owner is Player) || target is Player) return true; // todo: check hate list - if (owner.currentSubState == SetActorStatePacket.SUB_STATE_MONSTER && ((BattleNpc)owner).hateContainer.GetMostHatedTarget() != target) + if (owner is BattleNpc && ((BattleNpc)owner).hateContainer.GetMostHatedTarget() != target) { return false; } @@ -410,7 +416,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public Character GetValidTarget(Character target, ValidTarget findFlags) { - if (target == null || target.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER && ((Player)target).playerSession.isUpdatesLocked) + if (target == null || target is Player && ((Player)target).playerSession.isUpdatesLocked) return null; if ((findFlags & ValidTarget.Ally) != 0) diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs index eb1a9d22..40712a65 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs @@ -23,6 +23,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers private bool firstSpell = true; private DateTime lastRoamUpdate; + private DateTime battleStartTime; private new BattleNpc owner; public BattleNpcController(BattleNpc owner) : @@ -65,6 +66,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers var canEngage = this.owner.aiContainer.InternalEngage(target); if (canEngage) { + //owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE); + // reset casting firstSpell = true; // todo: find a better place to put this? @@ -77,6 +80,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers // owner.ResetMoveSpeeds(); owner.moveState = 2; lastActionTime = DateTime.Now; + battleStartTime = lastActionTime; // todo: adjust cooldowns with modifiers } return canEngage; @@ -100,7 +104,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers neutralTime = lastActionTime; owner.hateContainer.ClearHate(); owner.moveState = 1; - lua.LuaEngine.CallLuaBattleAction(owner, "onDisengage", owner, target); + lua.LuaEngine.CallLuaBattleAction(owner, "onDisengage", owner, target, Utils.UnixTimeStampUTC(battleStartTime)); } public override void Cast(Character target, uint spellId) @@ -199,6 +203,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers } Move(); + lua.LuaEngine.CallLuaBattleAction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC()); } private void Move() @@ -230,7 +235,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers owner.aiContainer.pathFind.FollowPath(); if (!owner.aiContainer.pathFind.IsFollowingPath()) { - if (owner.target.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER) + if (owner.target is Player) { foreach (var chara in owner.zone.GetActorsAroundActor(owner, 1)) { @@ -355,5 +360,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { ChangeTarget(owner.hateContainer.GetMostHatedTarget()); } + + public override void ChangeTarget(Character target) + { + owner.target = target; + owner.currentLockedTarget = target != null ? target.actorId : 0xC0000000; + owner.currentTarget = target != null ? target.actorId : 0xC0000000; + base.ChangeTarget(target); + } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs index a249d782..5dcf3574 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs @@ -11,19 +11,35 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { class PlayerController : Controller { - public PlayerController(Character owner) : + private new Player owner; + public PlayerController(Player owner) : base(owner) { + this.owner = owner; this.lastUpdate = DateTime.Now; } public override void Update(DateTime tick) { // todo: handle player stuff on tick + if (owner.newMainState != owner.currentMainState) + { + //owner.updateFlags = ActorUpdateFlags.Combat; + if (owner.newMainState == SetActorStatePacket.MAIN_STATE_ACTIVE) + { + owner.Engage(); + } + else + { + owner.Disengage(); + } + owner.currentMainState = (ushort)owner.newMainState; + } } public override void ChangeTarget(Character target) { + owner.target = target; base.ChangeTarget(target); } @@ -32,21 +48,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers var canEngage = this.owner.aiContainer.InternalEngage(target); if (canEngage) { - // todo: find a better place to put this? - if (owner.GetState() != SetActorStatePacket.MAIN_STATE_ACTIVE) - owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE); - - // todo: check speed/is able to move // todo: too far, path to player if mob, message if player - - // todo: actual stat based range - if (Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) > 10) + if (owner.statusEffects.HasStatusEffect(StatusEffectId.Sleep)) { - { - // todo: out of range error - } - ChangeTarget(target); + // That command cannot be performed. + owner.SendGameMessage(Server.GetWorldManager().GetActor(), 32553, 0x20); return false; } // todo: adjust cooldowns with modifiers diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs index 3ed55cf1..870b60f5 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs @@ -20,7 +20,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state this.startTime = DateTime.Now; owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE); - owner.aiContainer.ChangeTarget(target); + ChangeTarget(target); attackTime = startTime; owner.aiContainer.pathFind?.Clear(); // todo: should handle everything here instead of on next tick.. @@ -42,12 +42,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state return true; } */ - if (target == null || owner.target != target || owner.target?.actorId != owner.currentLockedTarget) - owner.aiContainer.ChangeTarget(target = owner.zone.FindActorInArea(owner.currentLockedTarget == 0xC0000000 ? owner.currentTarget : owner.currentLockedTarget) as Character); + + if ((target == null || owner.target != target || owner.target?.actorId != owner.currentLockedTarget) && owner.isAutoAttackEnabled) + owner.aiContainer.ChangeTarget(target = owner.zone.FindActorInArea(owner.currentLockedTarget)); if (target == null || target.IsDead()) { - if (owner.currentSubState == SetActorStatePacket.SUB_STATE_MONSTER) + if (owner is BattleNpc) target = ((BattleNpc)owner).hateContainer.GetMostHatedTarget(); } else @@ -110,7 +111,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state owner.OnAttack(this, action, ref errorResult); // handle paralyze/intimidate/sleep/whatever in character thing - owner.DoBattleAction((ushort)BattleActionX01PacketCommand.Attack, 0x19001000, errorResult == null ? action : errorResult); + owner.DoBattleAction((ushort)BattleActionX01PacketCommand.Attack, action.animation, errorResult == null ? action : errorResult); //this.errorPacket = BattleActionX01Packet.BuildPacket(target.actorId, owner.actorId, target.actorId, 0, effectId, 0, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, 0); } @@ -140,11 +141,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state private bool IsAttackReady() { - return Program.Tick >= attackTime; + // todo: this enforced delay should really be changed if it's not retail.. + return Program.Tick >= attackTime && Program.Tick >= owner.aiContainer.GetLastActionTime().AddSeconds(1); } private bool CanAttack() { + if (!owner.isAutoAttackEnabled) + { + return false; + } + if (target == null) { return false; @@ -162,7 +169,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state // todo: use a mod for melee range else if (Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) > owner.GetAttackRange()) { - if (owner.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER) + if (owner is Player) { ((Player)owner).SendGameMessage(Server.GetWorldManager().GetActor(), 32539, 0x20); } diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs index bd2983e6..bcc3f22a 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs @@ -15,6 +15,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state : base(owner, null) { owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD); + owner.Disengage(); canInterrupt = false; startTime = tick; despawnTime = startTime.AddSeconds(timeToFadeOut); @@ -25,7 +26,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state // todo: handle raise etc if (tick >= despawnTime) { - owner.Spawn(Program.Tick); + if (owner is BattleNpc) + { + owner.Despawn(tick); + } + else + { + // todo: queue a warp for the player + } return true; } return false; diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/DespawnState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/DespawnState.cs new file mode 100644 index 00000000..f16e8fa6 --- /dev/null +++ b/FFXIVClassic Map Server/actors/chara/ai/state/DespawnState.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server.packets.send.actor; + +namespace FFXIVClassic_Map_Server.actors.chara.ai.state +{ + class DespawnState : State + { + private DateTime endTime; + public DespawnState(Character owner, Character target, uint despawnTimeSeconds) : + base(owner, null) + { + startTime = Program.Tick; + endTime = startTime.AddSeconds(despawnTimeSeconds); + } + + public override bool Update(DateTime tick) + { + if (tick >= endTime) + { + // todo: send packet to despawn the npc, set it so npc is despawned when requesting spawn packets + owner.zone.BroadcastPacketAroundActor(owner, RemoveActorPacket.BuildPacket(owner.actorId)); + owner.QueuePositionUpdate(owner.spawnX, owner.spawnY, owner.spawnZ); + lua.LuaEngine.CallLuaBattleAction(owner, "onDespawn", owner); + return true; + } + return false; + } + } +} diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs index 120cc52b..47964a82 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs @@ -57,14 +57,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state float spellSpeed = spell.castTimeSeconds; // command casting duration - if (owner.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER) + if (owner is Player) { // todo: modify spellSpeed based on modifiers and stuff - ((Player)owner).SendStartCastbar(spell.id, Utils.UnixTimeStampUTC(DateTime.Now.AddSeconds(spellSpeed))); - owner.SendChant(0xF, 0x0); - owner.DoBattleAction(spell.id, 0x6F000002, new BattleAction(target.actorId, 30128, 1, 0, 1)); //You begin casting (6F000002: BLM, 6F000003: WHM) + ((Player)owner).SendStartCastbar(spell.id, Utils.UnixTimeStampUTC(DateTime.Now.AddSeconds(spellSpeed))); } - + owner.SendChant(0xF, 0x0); + owner.DoBattleAction(spell.id, 0x6F000002, new BattleAction(target.actorId, 30128, 1, 0, 1)); //You begin casting (6F000002: BLM, 6F000003: WHM) } } @@ -106,7 +105,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override void OnComplete() { - spell.targetFind.FindWithinArea(target, spell.validTarget); + spell.targetFind.FindWithinArea(target, spell.validTarget, spell.aoeTarget); isCompleted = true; var targets = spell.targetFind.GetTargets(); @@ -119,7 +118,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state actions[i++] = action; } - owner.SendChant(0, 0); + // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action + var errors = (BattleAction[])actions.Clone(); + + owner.OnCast(this, actions, ref errors); owner.DoBattleAction(spell.id, spell.battleAnimation, actions); } @@ -138,10 +140,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state // todo: actually check proc rate/random chance of whatever effect effectId = list[0].GetStatusEffectId(); } - // todo: which is actually the swing packet - //this.errorPacket = BattleActionX01Packet.BuildPacket(target.actorId, owner.actorId, target.actorId, 0, effectId, 0, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, 0); - //owner.zone.BroadcastPacketAroundActor(owner, errorPacket); - //errorPacket = null; interrupt = true; return; } @@ -159,7 +157,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state private bool CanCast() { - return owner.CanCast(target, spell) && !HasMoved(); + return owner.CanCast(target, spell) && spell.IsValidTarget(owner, target) && !HasMoved(); } private bool HasMoved() @@ -169,13 +167,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override void Cleanup() { - if (owner.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER) - { + owner.SendChant(0, 0); + + if (owner is Player) + { ((Player)owner).SendEndCastbar(); } - // command casting duration - //var packets = new List(); - //owner.zone.BroadcastPacketsAroundActor(owner, packets); + owner.aiContainer.UpdateLastActionTime(); } public BattleCommand GetSpell() diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs index c54ead1e..3ed288e0 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs @@ -90,7 +90,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override void OnComplete() { - skill.targetFind.FindWithinArea(target, skill.validTarget); + skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget); isCompleted = true; var targets = skill.targetFind.GetTargets(); @@ -104,10 +104,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state // evasion, miss, dodge, etc to be handled in script, calling helpers from scripts/weaponskills.lua action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillFinish", owner, target, skill, action); actions[i++] = action; - - //packets.Add(BattleActionX01Packet.BuildPacket(chara.actorId, owner.actorId, action.targetId, skill.battleAnimation, action.effectId, action.worldMasterTextId, skill.id, action.amount, action.param)); } + // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action + var errors = (BattleAction[])actions.Clone(); + + owner.OnWeaponSkill(this, actions, ref errors); owner.DoBattleAction(skill.id, 0, actions); } @@ -126,10 +128,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state // todo: actually check proc rate/random chance of whatever effect effectId = list[0].GetStatusEffectId(); } - // todo: which is actually the swing packet - //this.errorPacket = BattleActionX01Packet.BuildPacket(target.actorId, owner.actorId, target.actorId, 0, effectId, 0, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, 0); - //owner.zone.BroadcastPacketAroundActor(owner, errorPacket); - //errorPacket = null; interrupt = true; return; } @@ -139,12 +137,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state private bool CanUse() { - return owner.CanWeaponSkill(target, skill); + return owner.CanWeaponSkill(target, skill) && skill.IsValidTarget(owner, target); } public BattleCommand GetWeaponSkill() { return skill; } + + public override void Cleanup() + { + owner.aiContainer.UpdateLastActionTime(); + } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs b/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs index a1529246..37a7197e 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs @@ -101,7 +101,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils var scaledCost = spell.CalculateCost((uint)caster.charaWork.parameterSave.state_mainSkillLevel); // todo: calculate cost for mob/player - if (caster.currentSubState == SetActorStatePacket.SUB_STATE_MONSTER) + if (caster is BattleNpc) { } diff --git a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs index acf9cc2c..37557bfc 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs @@ -36,7 +36,7 @@ namespace FFXIVClassic_Map_Server.Actors private uint despawnTime; private uint spawnDistance; - public float spawnX, spawnY, spawnZ; + public BattleNpc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot, ushort actorState, uint animationId, string customDisplayName) : base(actorNumber, actorClass, uniqueId, spawnedArea, posX, posY, posZ, rot, actorState, animationId, customDisplayName) @@ -174,6 +174,13 @@ namespace FFXIVClassic_Map_Server.Actors } } + public override void Despawn(DateTime tick) + { + aiContainer.ClearStates(); + // todo: probably didnt need to make a new state... + aiContainer.ForceChangeState(new DespawnState(this, null, 0)); + } + public void OnRoam(DateTime tick) { // todo: move this to battlenpccontroller.. @@ -207,6 +214,8 @@ namespace FFXIVClassic_Map_Server.Actors public override void OnAttack(State state, BattleAction action, ref BattleAction error) { base.OnAttack(state, action, ref error); + // todo: move this somewhere else prolly and change based on model/appearance (so maybe in Character.cs instead) + action.animation = 0x11001000; // (temporary) wolf anim } } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 05ed9079..e12bf0b3 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -1757,13 +1757,23 @@ namespace FFXIVClassic_Map_Server.Actors { // todo: should probably add another flag for battleTemp since all this uses reflection packets = new List(); + + // we only want the latest update for the player + if ((updateFlags & ActorUpdateFlags.Position) != 0) + { + if (positionUpdates.Count > 1) + positionUpdates.RemoveRange(1, positionUpdates.Count - 1); + } + if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0) { var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this); - propPacketUtil.AddProperty($"charaWork.parameterSave.hp[{currentJob}]"); - propPacketUtil.AddProperty($"charaWork.parameterSave.hpMax[{currentJob}]"); - propPacketUtil.AddProperty($"charaWork.parameterSave.state_mainSkill[{currentJob}]"); + // todo: should this be using job as index? + propPacketUtil.AddProperty($"charaWork.parameterSave.hp[{0}]"); + propPacketUtil.AddProperty($"charaWork.parameterSave.hpMax[{0}]"); + propPacketUtil.AddProperty($"charaWork.parameterSave.state_mainSkill[{0}]"); + propPacketUtil.AddProperty($"charaWork.parameterSave.state_mainSkillLevel"); packets.AddRange(propPacketUtil.Done()); } @@ -1937,6 +1947,7 @@ namespace FFXIVClassic_Map_Server.Actors //If the returned value is outside the hotbar, it indicates it wasn't found. private ushort FindFirstCommandSlotById(uint commandId) { + commandId |= 0xA0F00000; ushort firstSlot = (ushort)(charaWork.commandBorder + 30); for (ushort i = charaWork.commandBorder; i < charaWork.commandBorder + 30; i++) @@ -1950,13 +1961,51 @@ namespace FFXIVClassic_Map_Server.Actors return firstSlot; } - - /* - public void SendBattleActionX01Packet(uint anim, uint effect, uint text = 0x756D, uint command = 27260, uint param = 0x01, uint idek = 0x01) + + private void UpdateHotbarTimer(uint commandId, uint recastTimeSeconds) { - var packet = BattleActionX01Packet.BuildPacket(actorId, actorId, currentTarget != 0xC0000000 ? currentTarget : currentLockedTarget, (uint)anim, (uint)effect, (ushort)text, (ushort)command, (ushort)param, (byte)idek); - QueuePacket(packet); - }*/ + ushort slot = FindFirstCommandSlotById(commandId); + charaWork.parameterSave.commandSlot_recastTime[slot - charaWork.commandBorder] = Utils.UnixTimeStampUTC(DateTime.Now.AddSeconds(recastTimeSeconds)); + var slots = new List(); + slots.Add(slot); + UpdateRecastTimers(slots); + } + + private uint GetHotbarTimer(uint commandId) + { + ushort slot = FindFirstCommandSlotById(commandId); + return charaWork.parameterSave.commandSlot_recastTime[slot - charaWork.commandBorder]; + } + + public override void Cast(uint spellId, uint targetId = 0) + { + if (aiContainer.CanChangeState()) + aiContainer.Cast(zone.FindActorInArea(targetId == 0 ? currentTarget : targetId), spellId); + else if (aiContainer.GetCurrentState() is MagicState) + // You are already casting. + SendGameMessage(Server.GetWorldManager().GetActor(), 32536, 0x20); + else + // Please wait a moment and try again. + SendGameMessage(Server.GetWorldManager().GetActor(), 32535, 0x20); + } + + public override void Ability(uint abilityId, uint targetId = 0) + { + if (aiContainer.CanChangeState()) + aiContainer.Ability(zone.FindActorInArea(targetId == 0 ? currentTarget : targetId), abilityId); + else + // Please wait a moment and try again. + SendGameMessage(Server.GetWorldManager().GetActor(), 32535, 0x20); + } + + public override void WeaponSkill(uint skillId, uint targetId = 0) + { + if (aiContainer.CanChangeState()) + aiContainer.WeaponSkill(zone.FindActorInArea(targetId == 0 ? currentTarget : targetId), skillId); + else + // Please wait a moment and try again. + SendGameMessage(Server.GetWorldManager().GetActor(), 32535, 0x20); + } public override bool IsValidTarget(Character target, ValidTarget validTarget) { @@ -1977,7 +2026,8 @@ namespace FFXIVClassic_Map_Server.Actors // enemy only if ((validTarget & ValidTarget.Enemy) != 0) { - if (target.currentSubState == SetActorStatePacket.SUB_STATE_NONE) + // todo: this seems ambiguous + if (target.isStatic) { // That command cannot be performed on the current target. SendGameMessage(Server.GetWorldManager().GetActor(), 32547, 0x20); @@ -1990,7 +2040,7 @@ namespace FFXIVClassic_Map_Server.Actors return false; } // todo: pvp? - if (target.currentSubState == currentSubState) + if (target.allegiance == allegiance) { // That command cannot be performed on an ally. SendGameMessage(Server.GetWorldManager().GetActor(), 32549, 0x20); @@ -1998,14 +2048,15 @@ namespace FFXIVClassic_Map_Server.Actors } } - if ((validTarget & ValidTarget.Ally) != 0 && target.currentSubState != currentSubState) + if ((validTarget & ValidTarget.Ally) != 0 && target.allegiance != allegiance) { // That command cannot be performed on the current target. SendGameMessage(Server.GetWorldManager().GetActor(), 32547, 0x20); return false; } - if ((validTarget & ValidTarget.NPC) != 0 && target.currentSubState == SetActorStatePacket.SUB_STATE_NONE) + // todo: isStatic seems ambiguous? + if ((validTarget & ValidTarget.NPC) != 0 && target.isStatic) return true; // todo: why is player always zoning? @@ -2022,7 +2073,13 @@ namespace FFXIVClassic_Map_Server.Actors public override bool CanCast(Character target, BattleCommand spell) { - // todo: move the ability specific stuff to ability.cs + if (GetHotbarTimer(spell.id) > Utils.UnixTimeStampUTC()) + { + // todo: this needs confirming + // Please wait a moment and try again. + SendGameMessage(Server.GetWorldManager().GetActor(), 32535, 0x20, (uint)spell.id); + return false; + } if (target == null) { // Target does not exist. @@ -2046,6 +2103,13 @@ namespace FFXIVClassic_Map_Server.Actors public override bool CanWeaponSkill(Character target, BattleCommand skill) { // todo: see worldmaster ids 32558~32557 for proper ko message and stuff + if (GetHotbarTimer(skill.id) > Utils.UnixTimeStampUTC()) + { + // todo: this needs confirming + // Please wait a moment and try again. + SendGameMessage(Server.GetWorldManager().GetActor(), 32535, 0x20, (uint)skill.id); + return false; + } if (target == null) { // Target does not exist. @@ -2069,7 +2133,8 @@ namespace FFXIVClassic_Map_Server.Actors public override void OnAttack(State state, BattleAction action, ref BattleAction error) { base.OnAttack(state, action, ref error); - + // todo: switch based on main weap (also probably move this anim assignment somewhere else) + action.animation = 0x19001000; if (error == null) { // melee attack animation @@ -2081,5 +2146,24 @@ namespace FFXIVClassic_Map_Server.Actors ((BattleNpc)target).hateContainer.UpdateHate(this, action.amount); } } + + public override void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors) + { + // todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap) + base.OnCast(state, actions, ref errors); + var spell = ((MagicState)state).GetSpell(); + // todo: should just make a thing that updates the one slot cause this is dumb as hell + + UpdateHotbarTimer(spell.id, spell.recastTimeSeconds); + } + + public override void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors) + { + // todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap) + base.OnWeaponSkill(state, actions, ref errors); + var skill = ((WeaponSkillState)state).GetWeaponSkill(); + // todo: should just make a thing that updates the one slot cause this is dumb as hell + UpdateHotbarTimer(skill.id, skill.recastTimeSeconds); + } } } diff --git a/FFXIVClassic Map Server/dataobjects/Session.cs b/FFXIVClassic Map Server/dataobjects/Session.cs index 5c72cabc..13dacb4b 100644 --- a/FFXIVClassic Map Server/dataobjects/Session.cs +++ b/FFXIVClassic Map Server/dataobjects/Session.cs @@ -86,7 +86,7 @@ namespace FFXIVClassic_Map_Server.dataobjects playerActor.rotation = rot; playerActor.moveState = moveState; - GetActor().GetZone().UpdateActorPosition(GetActor()); + //GetActor().GetZone().UpdateActorPosition(GetActor()); playerActor.QueuePositionUpdate(new Vector3(x,y,z)); } diff --git a/data/scripts/commands/ActivateCommand.lua b/data/scripts/commands/ActivateCommand.lua index e456e2f0..cec7da25 100644 --- a/data/scripts/commands/ActivateCommand.lua +++ b/data/scripts/commands/ActivateCommand.lua @@ -8,14 +8,12 @@ Switches between active and passive mode states --]] -function onEventStarted(player, command, triggerName) - - if (player:GetState() == 0) then - player.ChangeState(2); - player.Engage(); - elseif (player:GetState() == 2) then - player:ChangeState(0); - player.Disengage(); +function onEventStarted(player, command, triggerName) + + if (player.newMainState == 0x0000) then + player.Engage(0, 0x0002); + elseif (player.newMainState == 0x0002) then + player.Disengage(0x0000); end player:endEvent(); sendSignal("playerActive"); diff --git a/sql/server_battle_commands.sql b/sql/server_battle_commands.sql index 1b241f8a..b3660ceb 100644 --- a/sql/server_battle_commands.sql +++ b/sql/server_battle_commands.sql @@ -30,6 +30,8 @@ CREATE TABLE `server_battle_commands` ( `requirements` smallint(5) unsigned NOT NULL, `validTarget` tinyint(3) unsigned NOT NULL, `aoeType` tinyint(3) unsigned NOT NULL, + `aoeRange` int(10) NOT NULL DEFAULT '0', + `aoeTarget` tinyint(3) NOT NULL, `numHits` tinyint(3) unsigned NOT NULL, `positionBonus` tinyint(3) unsigned NOT NULL, `procRequirement` tinyint(3) unsigned NOT NULL, @@ -45,7 +47,7 @@ CREATE TABLE `server_battle_commands` ( `effectAnimation` smallint(5) unsigned NOT NULL, `modelAnimation` smallint(5) unsigned NOT NULL, `animationDuration` smallint(5) unsigned NOT NULL, - `aoeRange` int(10) NOT NULL DEFAULT '0', + `battleAnimation` int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -57,148 +59,148 @@ CREATE TABLE `server_battle_commands` ( LOCK TABLES `server_battle_commands` WRITE; /*!40000 ALTER TABLE `server_battle_commands` DISABLE KEYS */; set autocommit=0; -INSERT INTO `server_battle_commands` VALUES (27100,'second_wind',2,6,3,1,0,1,0,0,5,0,0,0,0,45,0,0,14,519,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27101,'blindside',2,14,3,1,0,1,0,0,5,0,60,0,0,60,0,0,14,635,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27102,'taunt',2,42,4,32,0,1,0,0,5,5,0,0,0,60,0,0,14,517,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27103,'featherfoot',2,2,3,1,0,1,0,0,5,0,30,0,0,60,0,0,14,535,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27104,'fists_of_fire',2,34,4,1,0,1,0,0,5,0,0,0,0,10,0,0,14,684,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27105,'fists_of_earth',2,22,4,1,0,1,0,0,5,0,0,0,0,10,0,0,14,685,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27106,'hundred_fists',15,50,0,0,0,1,0,0,5,0,0,0,0,900,0,0,14,712,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27107,'spinning_heel',15,35,0,0,0,1,0,0,5,0,0,0,0,120,0,0,14,718,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27108,'shoulder_tackle',15,30,0,0,0,1,0,0,5,0,0,0,0,60,0,0,18,1048,205,2,0); -INSERT INTO `server_battle_commands` VALUES (27109,'fists_of_wind',15,40,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,720,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27110,'pummel',2,1,1,32,0,1,1,0,5,0,0,0,0,10,0,1000,18,1027,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27111,'concussive_blow',2,10,1,32,0,1,4,0,5,30,0,0,0,30,0,1500,18,20,3,2,0); -INSERT INTO `server_battle_commands` VALUES (27112,'simian_thrash',2,50,4,32,0,1,0,0,5,0,0,0,0,80,0,2000,18,1003,202,2,0); -INSERT INTO `server_battle_commands` VALUES (27113,'aura_pulse',2,38,4,32,1,1,0,0,5,30,0,0,0,40,0,1500,18,66,203,2,0); -INSERT INTO `server_battle_commands` VALUES (27114,'pounce',2,4,4,32,0,1,2,0,5,10,0,0,0,20,0,1500,18,8,3,2,0); -INSERT INTO `server_battle_commands` VALUES (27115,'demolish',2,30,1,32,0,1,0,0,5,0,0,0,0,30,0,1500,18,1028,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27116,'howling_fist',2,46,4,32,0,1,4,0,5,0,0,0,0,80,0,3000,18,1029,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27117,'sucker_punch',2,26,1,32,0,1,4,0,5,0,0,0,0,15,0,1000,18,73,3,2,0); -INSERT INTO `server_battle_commands` VALUES (27118,'dragon_kick',15,45,0,0,0,1,0,0,5,0,0,0,0,60,0,2000,18,1041,204,2,0); -INSERT INTO `server_battle_commands` VALUES (27119,'haymaker',2,18,4,32,0,1,0,1,5,0,0,0,0,5,0,250,18,23,201,2,0); -INSERT INTO `server_battle_commands` VALUES (27140,'sentinel',3,22,3,1,0,1,0,0,5,0,15,0,0,90,0,0,14,526,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27141,'aegis_boon',3,6,8,1,0,1,0,0,5,0,30,0,0,60,0,0,14,583,21,2,0); -INSERT INTO `server_battle_commands` VALUES (27142,'rampart',3,2,3,1,0,1,0,2,5,0,60,0,0,120,0,0,14,536,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27143,'tempered_will',3,42,8,1,0,1,0,0,5,0,20,0,0,180,0,0,14,515,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27144,'outmaneuver',3,34,8,1,0,1,0,0,5,0,30,0,0,90,0,0,14,512,21,2,0); -INSERT INTO `server_battle_commands` VALUES (27145,'flash',3,14,3,32,0,1,0,0,5,0,0,0,0,30,0,0,14,696,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27146,'cover',16,30,0,0,0,1,0,0,5,0,15,0,0,60,0,0,14,725,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27147,'divine_veil',16,35,0,0,0,1,0,0,5,0,20,0,0,60,0,0,14,713,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27148,'hallowed_ground',16,50,0,0,0,1,0,0,5,0,0,0,0,900,0,0,14,709,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27149,'holy_succor',16,40,0,0,0,1,0,0,15,0,0,0,2,10,100,0,1,701,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27150,'fast_blade',3,1,1,32,0,1,1,0,5,0,0,0,0,10,0,1000,18,1023,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27151,'flat_blade',3,26,1,32,0,1,0,0,5,0,0,0,0,10,0,1500,18,1024,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27152,'savage_blade',3,10,1,32,0,1,0,0,5,0,0,0,0,30,0,1000,18,1025,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27153,'goring_blade',3,50,8,32,0,1,2,0,5,30,0,0,0,60,0,3000,18,1026,301,2,0); -INSERT INTO `server_battle_commands` VALUES (27154,'riot_blade',3,30,8,32,0,1,2,0,5,30,0,0,0,80,0,2000,18,75,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27155,'rage_of_halone',3,46,8,32,0,1,0,0,5,0,0,0,0,20,0,1500,18,1008,302,2,0); -INSERT INTO `server_battle_commands` VALUES (27156,'shield_bash',3,18,17,32,0,1,0,0,5,5,0,0,0,30,0,250,18,5,26,2,0); -INSERT INTO `server_battle_commands` VALUES (27157,'war_drum',3,38,24,32,1,1,0,2,5,0,0,0,0,60,0,500,14,502,21,2,0); -INSERT INTO `server_battle_commands` VALUES (27158,'phalanx',3,4,8,1,0,1,0,0,5,0,0,0,0,5,0,250,18,32,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27159,'spirits_within',16,45,0,0,0,1,0,0,5,0,0,0,0,60,0,3000,18,1044,304,2,0); -INSERT INTO `server_battle_commands` VALUES (27180,'provoke',4,14,3,32,0,1,0,0,5,0,0,0,0,30,0,0,14,600,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27181,'foresight',4,2,3,1,0,1,0,0,5,0,30,0,0,60,0,0,14,545,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27182,'bloodbath',4,6,3,1,0,1,0,0,5,0,30,0,0,60,0,0,14,581,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27183,'berserk',4,22,32,1,0,1,0,0,5,0,0,0,0,10,0,0,14,682,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27184,'rampage',4,34,32,1,0,1,0,0,5,0,0,0,0,10,0,0,14,546,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27185,'enduring_march',4,42,32,1,0,1,0,0,5,0,20,0,0,180,0,0,14,539,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27186,'vengeance',17,30,0,1,0,1,0,0,5,0,0,0,0,150,0,0,14,714,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27187,'antagonize',17,35,0,1,0,1,0,0,5,0,0,0,0,120,0,0,14,715,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27188,'collusion',17,40,0,4,0,1,0,0,5,0,0,0,0,90,0,0,14,711,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27189,'mighty_strikes',17,50,0,1,0,1,0,0,5,0,0,0,0,900,0,0,14,716,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27190,'heavy_swing',4,1,1,32,0,1,1,0,5,0,0,0,0,10,0,1000,18,14,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27191,'skull_sunder',4,10,1,32,0,1,0,0,5,0,0,0,0,30,0,1500,18,43,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27192,'steel_cyclone',17,45,0,32,1,1,0,0,5,0,0,0,0,30,0,2000,18,1040,404,2,0); -INSERT INTO `server_battle_commands` VALUES (27193,'brutal_swing',4,4,1,32,0,1,4,0,5,0,0,0,0,20,0,1500,18,15,3,2,0); -INSERT INTO `server_battle_commands` VALUES (27194,'maim',4,26,1,32,0,1,0,0,5,0,0,0,0,30,0,1500,18,88,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27195,'godsbane',4,50,32,32,0,1,0,0,5,0,0,0,0,60,0,3000,18,1014,402,2,0); -INSERT INTO `server_battle_commands` VALUES (27196,'path_of_the_storm',4,38,32,32,0,1,2,0,5,0,0,0,0,30,0,1500,18,44,401,2,0); -INSERT INTO `server_battle_commands` VALUES (27197,'whirlwind',4,46,32,32,1,1,0,0,5,0,0,0,0,80,0,3000,18,1015,403,2,0); -INSERT INTO `server_battle_commands` VALUES (27198,'fracture',4,18,32,32,0,1,0,4,5,8,0,0,0,40,0,500,18,42,3,2,0); -INSERT INTO `server_battle_commands` VALUES (27199,'overpower',4,30,1,32,2,1,0,4,5,0,0,0,0,5,0,250,18,89,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27220,'hawks_eye',7,6,3,1,0,1,0,0,5,0,15,0,0,90,0,0,14,516,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27221,'quelling_strikes',7,22,3,1,0,1,0,0,5,30,0,0,0,60,0,0,14,614,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27222,'decoy',7,2,3,1,0,1,0,0,15,0,60,0,0,90,100,0,14,565,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27223,'chameleon',7,42,3,1,0,1,0,0,5,0,0,0,0,180,0,0,14,504,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27224,'barrage',7,34,64,1,0,1,0,0,5,0,60,0,0,90,0,0,14,683,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27225,'raging_strikes',7,14,64,1,0,1,0,0,5,0,0,0,0,10,0,0,14,632,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27226,'swiftsong',7,26,64,1,1,1,0,0,15,0,180,0,0,10,100,0,1,150,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27227,'battle_voice',18,50,0,1,1,1,0,0,5,0,0,0,0,900,0,0,14,721,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27228,'heavy_shot',7,1,1,32,0,1,0,0,5,0,0,0,0,10,0,1000,18,1036,4,2,0); -INSERT INTO `server_battle_commands` VALUES (27229,'leaden_arrow',7,10,1,32,0,1,0,0,5,30,0,0,0,30,0,1500,18,1035,4,2,0); -INSERT INTO `server_battle_commands` VALUES (27230,'wide_volley',7,50,64,32,1,1,0,0,5,0,0,0,0,80,0,2000,18,18,703,2,0); -INSERT INTO `server_battle_commands` VALUES (27231,'quick_nock',7,38,64,32,2,1,0,0,5,0,0,0,0,180,0,1000,18,1017,702,2,0); -INSERT INTO `server_battle_commands` VALUES (27232,'rain_of_death',18,45,0,32,1,1,0,0,5,0,0,0,0,30,0,3000,18,1037,704,2,0); -INSERT INTO `server_battle_commands` VALUES (27233,'piercing_arrow',7,4,1,32,0,1,0,0,5,0,0,0,0,20,0,1000,18,1038,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27234,'gloom_arrow',7,30,1,32,0,1,0,0,5,30,0,0,0,10,0,1000,18,1039,4,2,0); -INSERT INTO `server_battle_commands` VALUES (27235,'bloodletter',7,46,64,32,0,1,0,0,5,30,0,0,0,80,0,1500,18,53,701,2,0); -INSERT INTO `server_battle_commands` VALUES (27236,'shadowbind',7,18,64,32,0,1,0,0,5,30,0,0,0,40,0,250,18,17,4,2,0); -INSERT INTO `server_battle_commands` VALUES (27237,'ballad_of_magi',18,30,0,1,1,1,0,0,15,0,0,0,3,10,100,0,1,709,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27238,'paeon_of_war',18,40,0,1,1,1,0,0,15,0,0,0,3,10,50,1000,1,710,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27239,'minuet_of_rigor',18,35,0,1,1,1,0,0,15,0,0,0,3,10,100,0,1,711,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27258,'refill',7,1,0,1,0,1,0,0,5,0,0,0,0,0,0,0,0,0,0,2,0); -INSERT INTO `server_battle_commands` VALUES (27259,'light_shot',7,1,0,32,0,1,0,0,5,0,0,0,0,0,0,0,0,0,0,2,0); -INSERT INTO `server_battle_commands` VALUES (27260,'invigorate',8,14,3,1,0,1,0,0,5,0,30,0,0,90,0,0,14,575,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27261,'power_surge',8,34,128,1,0,1,0,0,5,0,0,0,0,10,0,0,14,686,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27262,'life_surge',8,22,128,1,0,1,0,0,5,0,0,0,0,15,0,250,14,687,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27263,'dread_spike',8,42,128,1,0,1,0,0,5,0,0,0,0,120,0,0,14,686,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27264,'blood_for_blood',8,6,3,1,0,1,0,0,5,0,0,0,0,60,0,0,14,689,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27265,'keen_flurry',8,26,3,1,0,1,0,0,5,0,0,0,0,90,0,0,14,569,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27266,'jump',19,30,0,32,0,1,0,0,5,0,0,0,0,60,0,0,18,1045,804,2,0); -INSERT INTO `server_battle_commands` VALUES (27267,'elusive_jump',19,40,0,1,0,1,0,0,5,0,0,0,0,180,0,0,18,1046,806,2,0); -INSERT INTO `server_battle_commands` VALUES (27268,'dragonfire_dive',19,50,0,32,1,1,0,0,5,0,0,0,0,900,0,0,18,1045,804,2,0); -INSERT INTO `server_battle_commands` VALUES (27269,'true_thrust',8,1,1,32,0,1,1,0,5,0,0,0,0,10,0,1000,18,1030,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27270,'leg_sweep',8,30,1,32,1,1,0,0,5,8,0,0,0,30,0,1000,18,37,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27271,'doom_spike',8,46,128,32,3,1,0,0,5,0,0,0,0,60,0,3000,18,83,801,2,0); -INSERT INTO `server_battle_commands` VALUES (27272,'disembowel',19,35,0,32,0,1,0,0,5,0,0,0,0,30,0,750,18,1042,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27273,'heavy_thrust',8,10,1,32,0,1,0,0,5,4,0,0,0,20,0,1500,18,1031,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27274,'vorpal_thrust',8,2,1,32,0,1,2,0,5,0,0,0,0,20,0,1500,18,1032,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27275,'impulse_drive',8,18,1,32,0,1,4,0,5,0,0,0,0,30,0,1500,18,1033,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27276,'chaos_thrust',8,50,128,32,0,1,0,0,5,0,0,0,0,80,0,3000,18,40,802,2,0); -INSERT INTO `server_battle_commands` VALUES (27277,'ring_of_talons',19,45,0,32,1,1,0,0,5,0,0,0,0,60,0,2000,18,1009,803,2,0); -INSERT INTO `server_battle_commands` VALUES (27278,'feint',8,4,1,32,0,1,0,8,5,0,0,0,0,10,0,250,18,39,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27279,'full_thrust',8,38,128,32,0,1,0,8,5,0,0,0,0,30,0,250,18,1034,801,2,0); -INSERT INTO `server_battle_commands` VALUES (27300,'dark_seal',22,14,3,1,0,1,0,0,5,0,30,0,0,90,0,0,14,518,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27301,'resonance',22,22,3,1,0,1,0,0,5,0,30,0,0,90,0,0,14,669,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27302,'excruciate',22,38,256,1,0,1,0,0,5,0,30,0,0,90,0,0,14,694,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27303,'necrogenesis',22,6,3,1,0,1,0,0,5,0,30,0,0,90,0,0,14,695,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27304,'parsimony',22,2,256,1,0,1,0,0,5,0,30,0,0,90,0,0,14,568,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27305,'convert',26,30,0,1,0,1,0,0,5,0,0,0,0,450,0,0,14,724,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27306,'sleep',22,42,256,32,0,1,0,0,15,60,0,0,3,0,75,0,1,651,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27307,'sanguine_rite',22,30,3,1,0,1,0,0,15,0,20,0,0,60,120,0,1,152,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27308,'blizzard',22,4,256,32,0,1,0,0,15,30,0,0,3,10,90,0,1,502,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27309,'blizzara',22,26,256,32,1,1,0,0,15,30,0,0,0,40,150,0,1,506,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27310,'fire',22,10,3,32,1,1,0,0,15,0,0,0,3,8,105,0,1,501,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27311,'fira',22,34,3,32,1,1,0,0,15,0,0,0,5,16,180,0,1,504,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27312,'firaga',22,50,256,32,1,1,0,0,15,0,0,0,8,7,255,0,1,700,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27313,'thunder',22,1,3,32,0,1,0,0,15,0,0,0,2,6,75,0,1,503,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27314,'thundara',22,18,256,32,0,1,0,0,15,4,0,0,0,30,135,0,1,508,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27315,'thundaga',22,46,256,32,0,1,0,0,15,0,0,0,5,45,195,0,1,509,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27316,'burst',26,50,0,32,0,1,0,0,15,0,0,0,4,900,90,0,1,705,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27317,'sleepga',26,45,0,32,1,1,0,0,15,0,0,0,4,0,100,0,1,704,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27318,'flare',26,40,0,32,1,1,0,0,15,0,0,0,8,120,200,0,1,706,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27319,'freeze',26,35,0,32,0,1,0,0,15,0,0,0,5,120,120,0,1,707,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27340,'sacred_prism',23,34,3,1,0,1,0,0,5,0,60,0,0,90,0,0,14,690,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27341,'shroud_of_saints',23,38,512,1,0,1,0,0,5,0,20,0,0,180,0,0,14,691,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27342,'cleric_stance',23,10,512,1,0,1,0,0,5,0,0,0,0,30,0,0,14,692,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27343,'blissful_mind',23,14,512,1,0,1,0,0,5,0,0,0,0,30,0,0,14,693,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27344,'presence_of_mind',27,30,0,1,0,1,0,0,5,0,0,0,0,300,0,0,14,722,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27345,'benediction',27,50,0,1,1,1,0,0,5,0,0,0,0,900,0,0,14,723,2,2,0); -INSERT INTO `server_battle_commands` VALUES (27346,'cure',23,2,3,2,0,1,0,0,15,0,0,0,2,5,40,0,1,101,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27347,'cura',23,30,512,2,0,1,0,0,15,0,0,0,2,5,100,0,1,103,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27348,'curaga',23,46,512,4,1,1,0,0,15,0,0,0,3,10,150,0,1,146,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27349,'raise',23,18,512,2,0,1,0,0,15,0,0,0,10,300,150,0,1,148,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27350,'stoneskin',23,26,3,2,0,1,0,0,15,0,300,0,3,30,50,0,1,133,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27351,'protect',23,6,3,4,1,1,0,0,15,0,300,0,3,30,80,0,1,1085,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27352,'repose',23,50,0,32,0,1,0,0,15,0,0,0,3,0,80,0,1,151,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27353,'aero',23,4,3,32,0,1,0,0,15,0,0,0,3,6,75,0,1,510,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27354,'aerora',23,42,512,32,1,1,0,0,15,0,0,0,4,20,150,0,1,511,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27355,'stone',23,1,3,32,0,1,0,0,15,0,0,0,2,6,75,0,1,513,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27356,'stonera',23,22,512,32,1,1,0,0,15,0,0,0,3,30,150,0,1,514,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27357,'esuna',27,40,0,2,0,1,0,0,15,0,0,0,2,10,40,0,1,702,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27358,'regen',27,35,0,2,0,1,0,0,15,0,0,0,2,5,20,0,1,703,1,2,0); -INSERT INTO `server_battle_commands` VALUES (27359,'holy',27,45,0,32,1,1,0,0,15,0,0,0,0,300,100,0,1,708,1,2,0); +INSERT INTO `server_battle_commands` VALUES (27100,'second_wind',2,6,3,1,0,0,0,1,0,0,5,0,0,0,0,45,0,0,14,519,2,2,234889735); +INSERT INTO `server_battle_commands` VALUES (27101,'blindside',2,14,3,1,0,0,0,1,0,0,5,0,60,0,0,60,0,0,14,635,2,2,234889851); +INSERT INTO `server_battle_commands` VALUES (27102,'taunt',2,42,4,32,0,0,0,1,0,0,5,5,0,0,0,60,0,0,14,517,2,2,234889733); +INSERT INTO `server_battle_commands` VALUES (27103,'featherfoot',2,2,3,1,0,0,0,1,0,0,5,0,30,0,0,60,0,0,14,535,2,2,234889751); +INSERT INTO `server_battle_commands` VALUES (27104,'fists_of_fire',2,34,4,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,684,2,2,234889900); +INSERT INTO `server_battle_commands` VALUES (27105,'fists_of_earth',2,22,4,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,685,2,2,234889901); +INSERT INTO `server_battle_commands` VALUES (27106,'hundred_fists',15,50,0,0,0,0,0,1,0,0,5,0,0,0,0,900,0,0,14,712,2,2,234889928); +INSERT INTO `server_battle_commands` VALUES (27107,'spinning_heel',15,35,0,0,0,0,0,1,0,0,5,0,0,0,0,120,0,0,14,718,2,2,234889934); +INSERT INTO `server_battle_commands` VALUES (27108,'shoulder_tackle',15,30,0,0,0,0,0,1,0,0,5,0,0,0,0,60,0,0,18,1048,205,2,302830616); +INSERT INTO `server_battle_commands` VALUES (27109,'fists_of_wind',15,40,0,0,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,720,2,2,234889936); +INSERT INTO `server_battle_commands` VALUES (27110,'pummel',2,1,1,32,0,0,0,1,1,0,5,0,0,0,0,10,0,1000,18,1027,1,2,301995011); +INSERT INTO `server_battle_commands` VALUES (27111,'concussive_blow',2,10,1,32,0,0,0,1,4,0,5,30,0,0,0,30,0,1500,18,20,3,2,302002196); +INSERT INTO `server_battle_commands` VALUES (27112,'simian_thrash',2,50,4,32,0,0,0,1,0,0,5,0,0,0,0,80,0,2000,18,1003,202,2,302818283); +INSERT INTO `server_battle_commands` VALUES (27113,'aura_pulse',2,38,4,32,1,0,0,1,0,0,5,30,0,0,0,40,0,1500,18,66,203,2,302821442); +INSERT INTO `server_battle_commands` VALUES (27114,'pounce',2,4,4,32,0,0,0,1,2,0,5,10,0,0,0,20,0,1500,18,8,3,2,302002184); +INSERT INTO `server_battle_commands` VALUES (27115,'demolish',2,30,1,32,0,0,0,1,0,0,5,0,0,0,0,30,0,1500,18,1028,2,2,301999108); +INSERT INTO `server_battle_commands` VALUES (27116,'howling_fist',2,46,4,32,0,0,0,1,4,0,5,0,0,0,0,80,0,3000,18,1029,2,2,301999109); +INSERT INTO `server_battle_commands` VALUES (27117,'sucker_punch',2,26,1,32,0,0,0,1,4,0,5,0,0,0,0,15,0,1000,18,73,3,2,302002249); +INSERT INTO `server_battle_commands` VALUES (27118,'dragon_kick',15,45,0,0,0,0,0,1,0,0,5,0,0,0,0,60,0,2000,18,1041,204,2,302826513); +INSERT INTO `server_battle_commands` VALUES (27119,'haymaker',2,18,4,32,0,0,0,1,0,1,5,0,0,0,0,5,0,250,18,23,201,2,302813207); +INSERT INTO `server_battle_commands` VALUES (27140,'sentinel',3,22,3,1,0,0,0,1,0,0,5,0,15,0,0,90,0,0,14,526,2,2,234889742); +INSERT INTO `server_battle_commands` VALUES (27141,'aegis_boon',3,6,8,1,0,0,0,1,0,0,5,0,30,0,0,60,0,0,14,583,21,2,234967623); +INSERT INTO `server_battle_commands` VALUES (27142,'rampart',3,2,3,1,0,0,0,1,0,2,5,0,60,0,0,120,0,0,14,536,2,2,234889752); +INSERT INTO `server_battle_commands` VALUES (27143,'tempered_will',3,42,8,1,0,0,0,1,0,0,5,0,20,0,0,180,0,0,14,515,2,2,234889731); +INSERT INTO `server_battle_commands` VALUES (27144,'outmaneuver',3,34,8,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,512,21,2,234967552); +INSERT INTO `server_battle_commands` VALUES (27145,'flash',3,14,3,32,0,0,0,1,0,0,5,0,0,0,0,30,0,0,14,696,2,2,234889912); +INSERT INTO `server_battle_commands` VALUES (27146,'cover',16,30,0,0,0,0,0,1,0,0,5,0,15,0,0,60,0,0,14,725,2,2,234889941); +INSERT INTO `server_battle_commands` VALUES (27147,'divine_veil',16,35,0,0,0,0,0,1,0,0,5,0,20,0,0,60,0,0,14,713,2,2,234889929); +INSERT INTO `server_battle_commands` VALUES (27148,'hallowed_ground',16,50,0,0,0,0,0,1,0,0,5,0,0,0,0,900,0,0,14,709,2,2,234889925); +INSERT INTO `server_battle_commands` VALUES (27149,'holy_succor',16,40,0,0,0,0,0,1,0,0,15,0,0,0,2,10,100,0,1,701,1,2,16782013); +INSERT INTO `server_battle_commands` VALUES (27150,'fast_blade',3,1,1,32,0,0,0,1,1,0,5,0,0,0,0,10,0,1000,18,1023,1,2,301995007); +INSERT INTO `server_battle_commands` VALUES (27151,'flat_blade',3,26,1,32,0,0,0,1,0,0,5,0,0,0,0,10,0,1500,18,1024,2,2,301999104); +INSERT INTO `server_battle_commands` VALUES (27152,'savage_blade',3,10,1,32,0,0,0,1,0,0,5,0,0,0,0,30,0,1000,18,1025,1,2,301995009); +INSERT INTO `server_battle_commands` VALUES (27153,'goring_blade',3,50,8,32,0,0,0,1,2,0,5,30,0,0,0,60,0,3000,18,1026,301,2,303223810); +INSERT INTO `server_battle_commands` VALUES (27154,'riot_blade',3,30,8,32,0,0,0,1,2,0,5,30,0,0,0,80,0,2000,18,75,2,2,301998155); +INSERT INTO `server_battle_commands` VALUES (27155,'rage_of_halone',3,46,8,32,0,0,0,1,0,0,5,0,0,0,0,20,0,1500,18,1008,302,2,303227888); +INSERT INTO `server_battle_commands` VALUES (27156,'shield_bash',3,18,17,32,0,0,0,1,0,0,5,5,0,0,0,30,0,250,18,5,26,2,302096389); +INSERT INTO `server_battle_commands` VALUES (27157,'war_drum',3,38,24,32,1,0,0,1,0,2,5,0,0,0,0,60,0,500,14,502,21,2,234967542); +INSERT INTO `server_battle_commands` VALUES (27158,'phalanx',3,4,8,1,0,0,0,1,0,0,5,0,0,0,0,5,0,250,18,32,1,2,301994016); +INSERT INTO `server_battle_commands` VALUES (27159,'spirits_within',16,45,0,0,0,0,0,1,0,0,5,0,0,0,0,60,0,3000,18,1044,304,2,303236116); +INSERT INTO `server_battle_commands` VALUES (27180,'provoke',4,14,3,32,0,0,0,1,0,0,5,0,0,0,0,30,0,0,14,600,2,2,234889816); +INSERT INTO `server_battle_commands` VALUES (27181,'foresight',4,2,3,1,0,0,0,1,0,0,5,0,30,0,0,60,0,0,14,545,2,2,234889761); +INSERT INTO `server_battle_commands` VALUES (27182,'bloodbath',4,6,3,1,0,0,0,1,0,0,5,0,30,0,0,60,0,0,14,581,2,2,234889797); +INSERT INTO `server_battle_commands` VALUES (27183,'berserk',4,22,32,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,682,2,2,234889898); +INSERT INTO `server_battle_commands` VALUES (27184,'rampage',4,34,32,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,546,2,2,234889762); +INSERT INTO `server_battle_commands` VALUES (27185,'enduring_march',4,42,32,1,0,0,0,1,0,0,5,0,20,0,0,180,0,0,14,539,2,2,234889755); +INSERT INTO `server_battle_commands` VALUES (27186,'vengeance',17,30,0,1,0,0,0,1,0,0,5,0,0,0,0,150,0,0,14,714,2,2,234889930); +INSERT INTO `server_battle_commands` VALUES (27187,'antagonize',17,35,0,1,0,0,0,1,0,0,5,0,0,0,0,120,0,0,14,715,2,2,234889931); +INSERT INTO `server_battle_commands` VALUES (27188,'collusion',17,40,0,4,0,0,0,1,0,0,5,0,0,0,0,90,0,0,14,711,2,2,234889927); +INSERT INTO `server_battle_commands` VALUES (27189,'mighty_strikes',17,50,0,1,0,0,0,1,0,0,5,0,0,0,0,900,0,0,14,716,2,2,234889932); +INSERT INTO `server_battle_commands` VALUES (27190,'heavy_swing',4,1,1,32,0,0,0,1,1,0,5,0,0,0,0,10,0,1000,18,14,1,2,301993998); +INSERT INTO `server_battle_commands` VALUES (27191,'skull_sunder',4,10,1,32,0,0,0,1,0,0,5,0,0,0,0,30,0,1500,18,43,1,2,301994027); +INSERT INTO `server_battle_commands` VALUES (27192,'steel_cyclone',17,45,0,32,1,0,0,1,0,0,5,0,0,0,0,30,0,2000,18,1040,404,2,303645712); +INSERT INTO `server_battle_commands` VALUES (27193,'brutal_swing',4,4,1,32,0,0,0,1,4,0,5,0,0,0,0,20,0,1500,18,15,3,2,302002191); +INSERT INTO `server_battle_commands` VALUES (27194,'maim',4,26,1,32,0,0,0,1,0,0,5,0,0,0,0,30,0,1500,18,88,1,2,301994072); +INSERT INTO `server_battle_commands` VALUES (27195,'godsbane',4,50,32,32,0,0,0,1,0,0,5,0,0,0,0,60,0,3000,18,1014,402,2,303637494); +INSERT INTO `server_battle_commands` VALUES (27196,'path_of_the_storm',4,38,32,32,0,0,0,1,2,0,5,0,0,0,0,30,0,1500,18,44,401,2,303632428); +INSERT INTO `server_battle_commands` VALUES (27197,'whirlwind',4,46,32,32,1,0,0,1,0,0,5,0,0,0,0,80,0,3000,18,1015,403,2,303641591); +INSERT INTO `server_battle_commands` VALUES (27198,'fracture',4,18,32,32,0,0,0,1,0,4,5,8,0,0,0,40,0,500,18,42,3,2,302002218); +INSERT INTO `server_battle_commands` VALUES (27199,'overpower',4,30,1,32,2,0,0,1,0,4,5,0,0,0,0,5,0,250,18,89,1,2,301994073); +INSERT INTO `server_battle_commands` VALUES (27220,'hawks_eye',7,6,3,1,0,0,0,1,0,0,5,0,15,0,0,90,0,0,14,516,2,2,234889732); +INSERT INTO `server_battle_commands` VALUES (27221,'quelling_strikes',7,22,3,1,0,0,0,1,0,0,5,30,0,0,0,60,0,0,14,614,2,2,234889830); +INSERT INTO `server_battle_commands` VALUES (27222,'decoy',7,2,3,1,0,0,0,1,0,0,15,0,60,0,0,90,100,0,14,565,2,2,234889781); +INSERT INTO `server_battle_commands` VALUES (27223,'chameleon',7,42,3,1,0,0,0,1,0,0,5,0,0,0,0,180,0,0,14,504,2,2,234889720); +INSERT INTO `server_battle_commands` VALUES (27224,'barrage',7,34,64,1,0,0,0,1,0,0,5,0,60,0,0,90,0,0,14,683,2,2,234889899); +INSERT INTO `server_battle_commands` VALUES (27225,'raging_strikes',7,14,64,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,632,2,2,234889848); +INSERT INTO `server_battle_commands` VALUES (27226,'swiftsong',7,26,64,1,1,0,0,1,0,0,15,0,180,0,0,10,100,0,1,150,1,2,16781462); +INSERT INTO `server_battle_commands` VALUES (27227,'battle_voice',18,50,0,1,1,0,0,1,0,0,5,0,0,0,0,900,0,0,14,721,2,2,234889937); +INSERT INTO `server_battle_commands` VALUES (27228,'heavy_shot',7,1,1,32,0,0,0,1,0,0,5,0,0,0,0,10,0,1000,18,1036,4,2,302007308); +INSERT INTO `server_battle_commands` VALUES (27229,'leaden_arrow',7,10,1,32,0,0,0,1,0,0,5,30,0,0,0,30,0,1500,18,1035,4,2,302007307); +INSERT INTO `server_battle_commands` VALUES (27230,'wide_volley',7,50,64,32,1,0,0,1,0,0,5,0,0,0,0,80,0,2000,18,18,703,2,304869394); +INSERT INTO `server_battle_commands` VALUES (27231,'quick_nock',7,38,64,32,2,0,0,1,0,0,5,0,0,0,0,180,0,1000,18,1017,702,2,304866297); +INSERT INTO `server_battle_commands` VALUES (27232,'rain_of_death',18,45,0,32,1,0,0,1,0,0,5,0,0,0,0,30,0,3000,18,1037,704,2,304874509); +INSERT INTO `server_battle_commands` VALUES (27233,'piercing_arrow',7,4,1,32,0,0,0,1,0,0,5,0,0,0,0,20,0,1000,18,1038,1,2,301995022); +INSERT INTO `server_battle_commands` VALUES (27234,'gloom_arrow',7,30,1,32,0,0,0,1,0,0,5,30,0,0,0,10,0,1000,18,1039,4,2,302007311); +INSERT INTO `server_battle_commands` VALUES (27235,'bloodletter',7,46,64,32,0,0,0,1,0,0,5,30,0,0,0,80,0,1500,18,53,701,2,304861237); +INSERT INTO `server_battle_commands` VALUES (27236,'shadowbind',7,18,64,32,0,0,0,1,0,0,5,30,0,0,0,40,0,250,18,17,4,2,302006289); +INSERT INTO `server_battle_commands` VALUES (27237,'ballad_of_magi',18,30,0,1,1,0,0,1,0,0,15,0,0,0,3,10,100,0,1,709,1,2,16782021); +INSERT INTO `server_battle_commands` VALUES (27238,'paeon_of_war',18,40,0,1,1,0,0,1,0,0,15,0,0,0,3,10,50,1000,1,710,1,2,16782022); +INSERT INTO `server_battle_commands` VALUES (27239,'minuet_of_rigor',18,35,0,1,1,0,0,1,0,0,15,0,0,0,3,10,100,0,1,711,1,2,16782023); +INSERT INTO `server_battle_commands` VALUES (27258,'refill',7,1,0,1,0,0,0,1,0,0,5,0,0,0,0,0,0,0,0,0,0,2,0); +INSERT INTO `server_battle_commands` VALUES (27259,'light_shot',7,1,0,32,0,0,0,1,0,0,5,0,0,0,0,0,0,0,0,0,0,2,0); +INSERT INTO `server_battle_commands` VALUES (27260,'invigorate',8,14,3,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,575,2,2,234889791); +INSERT INTO `server_battle_commands` VALUES (27261,'power_surge',8,34,128,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,686,2,2,234889902); +INSERT INTO `server_battle_commands` VALUES (27262,'life_surge',8,22,128,1,0,0,0,1,0,0,5,0,0,0,0,15,0,250,14,687,2,2,234889903); +INSERT INTO `server_battle_commands` VALUES (27263,'dread_spike',8,42,128,1,0,0,0,1,0,0,5,0,0,0,0,120,0,0,14,686,2,2,234889902); +INSERT INTO `server_battle_commands` VALUES (27264,'blood_for_blood',8,6,3,1,0,0,0,1,0,0,5,0,0,0,0,60,0,0,14,689,2,2,234889905); +INSERT INTO `server_battle_commands` VALUES (27265,'keen_flurry',8,26,3,1,0,0,0,1,0,0,5,0,0,0,0,90,0,0,14,569,2,2,234889785); +INSERT INTO `server_battle_commands` VALUES (27266,'jump',19,30,0,32,0,0,0,1,0,0,5,0,0,0,0,60,0,0,18,1045,804,2,305284117); +INSERT INTO `server_battle_commands` VALUES (27267,'elusive_jump',19,40,0,1,0,0,0,1,0,0,5,0,0,0,0,180,0,0,18,1046,806,2,305292310); +INSERT INTO `server_battle_commands` VALUES (27268,'dragonfire_dive',19,50,0,32,1,0,0,1,0,0,5,0,0,0,0,900,0,0,18,1045,804,2,305284117); +INSERT INTO `server_battle_commands` VALUES (27269,'true_thrust',8,1,1,32,0,0,0,1,1,0,5,0,0,0,0,10,0,1000,18,1030,2,2,301999110); +INSERT INTO `server_battle_commands` VALUES (27270,'leg_sweep',8,30,1,32,1,0,0,1,0,0,5,8,0,0,0,30,0,1000,18,37,1,2,301994021); +INSERT INTO `server_battle_commands` VALUES (27271,'doom_spike',8,46,128,32,3,0,0,1,0,0,5,0,0,0,0,60,0,3000,18,83,801,2,305270867); +INSERT INTO `server_battle_commands` VALUES (27272,'disembowel',19,35,0,32,0,0,0,1,0,0,5,0,0,0,0,30,0,750,18,1042,2,2,301999122); +INSERT INTO `server_battle_commands` VALUES (27273,'heavy_thrust',8,10,1,32,0,0,0,1,0,0,5,4,0,0,0,20,0,1500,18,1031,1,2,301995015); +INSERT INTO `server_battle_commands` VALUES (27274,'vorpal_thrust',8,2,1,32,0,0,0,1,2,0,5,0,0,0,0,20,0,1500,18,1032,2,2,301999112); +INSERT INTO `server_battle_commands` VALUES (27275,'impulse_drive',8,18,1,32,0,0,0,1,4,0,5,0,0,0,0,30,0,1500,18,1033,2,2,301999113); +INSERT INTO `server_battle_commands` VALUES (27276,'chaos_thrust',8,50,128,32,0,0,0,1,0,0,5,0,0,0,0,80,0,3000,18,40,802,2,305274920); +INSERT INTO `server_battle_commands` VALUES (27277,'ring_of_talons',19,45,0,32,1,0,0,1,0,0,5,0,0,0,0,60,0,2000,18,1009,803,2,305279985); +INSERT INTO `server_battle_commands` VALUES (27278,'feint',8,4,1,32,0,0,0,1,0,8,5,0,0,0,0,10,0,250,18,39,2,2,301998119); +INSERT INTO `server_battle_commands` VALUES (27279,'full_thrust',8,38,128,32,0,0,0,1,0,8,5,0,0,0,0,30,0,250,18,1034,801,2,305271818); +INSERT INTO `server_battle_commands` VALUES (27300,'dark_seal',22,14,3,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,518,2,2,234889734); +INSERT INTO `server_battle_commands` VALUES (27301,'resonance',22,22,3,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,669,2,2,234889885); +INSERT INTO `server_battle_commands` VALUES (27302,'excruciate',22,38,256,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,694,2,2,234889910); +INSERT INTO `server_battle_commands` VALUES (27303,'necrogenesis',22,6,3,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,695,2,2,234889911); +INSERT INTO `server_battle_commands` VALUES (27304,'parsimony',22,2,256,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,568,2,2,234889784); +INSERT INTO `server_battle_commands` VALUES (27305,'convert',26,30,0,1,0,0,0,1,0,0,5,0,0,0,0,450,0,0,14,724,2,2,234889940); +INSERT INTO `server_battle_commands` VALUES (27306,'sleep',22,42,256,32,0,0,0,1,0,0,15,60,0,0,3,0,75,0,1,651,1,2,16781963); +INSERT INTO `server_battle_commands` VALUES (27307,'sanguine_rite',22,30,3,1,0,0,0,1,0,0,15,0,20,0,0,60,120,0,1,152,1,2,16781464); +INSERT INTO `server_battle_commands` VALUES (27308,'blizzard',22,4,256,32,0,0,0,1,0,0,15,30,0,0,3,10,90,0,1,502,1,2,16781814); +INSERT INTO `server_battle_commands` VALUES (27309,'blizzara',22,26,256,32,1,0,0,1,0,0,15,30,0,0,0,40,150,0,1,506,1,2,16781818); +INSERT INTO `server_battle_commands` VALUES (27310,'fire',22,10,3,32,1,0,0,1,0,0,15,0,0,0,3,8,105,0,1,501,1,2,16781813); +INSERT INTO `server_battle_commands` VALUES (27311,'fira',22,34,3,32,1,0,0,1,0,0,15,0,0,0,5,16,180,0,1,504,1,2,16781816); +INSERT INTO `server_battle_commands` VALUES (27312,'firaga',22,50,256,32,1,0,0,1,0,0,15,0,0,0,8,7,255,0,1,700,1,2,16782012); +INSERT INTO `server_battle_commands` VALUES (27313,'thunder',22,1,3,32,0,0,0,1,0,0,15,0,0,0,2,6,75,0,1,503,1,2,16781815); +INSERT INTO `server_battle_commands` VALUES (27314,'thundara',22,18,256,32,0,0,0,1,0,0,15,4,0,0,0,30,135,0,1,508,1,2,16781820); +INSERT INTO `server_battle_commands` VALUES (27315,'thundaga',22,46,256,32,0,0,0,1,0,0,15,0,0,0,5,45,195,0,1,509,1,2,16781821); +INSERT INTO `server_battle_commands` VALUES (27316,'burst',26,50,0,32,0,0,0,1,0,0,15,0,0,0,4,900,90,0,1,705,1,2,16782017); +INSERT INTO `server_battle_commands` VALUES (27317,'sleepga',26,45,0,32,1,0,0,1,0,0,15,0,0,0,4,0,100,0,1,704,1,2,16782016); +INSERT INTO `server_battle_commands` VALUES (27318,'flare',26,40,0,32,1,0,0,1,0,0,15,0,0,0,8,120,200,0,1,706,1,2,16782018); +INSERT INTO `server_battle_commands` VALUES (27319,'freeze',26,35,0,32,0,0,0,1,0,0,15,0,0,0,5,120,120,0,1,707,1,2,16782019); +INSERT INTO `server_battle_commands` VALUES (27340,'sacred_prism',23,34,3,1,0,0,0,1,0,0,5,0,60,0,0,90,0,0,14,690,2,2,234889906); +INSERT INTO `server_battle_commands` VALUES (27341,'shroud_of_saints',23,38,512,1,0,0,0,1,0,0,5,0,20,0,0,180,0,0,14,691,2,2,234889907); +INSERT INTO `server_battle_commands` VALUES (27342,'cleric_stance',23,10,512,1,0,0,0,1,0,0,5,0,0,0,0,30,0,0,14,692,2,2,234889908); +INSERT INTO `server_battle_commands` VALUES (27343,'blissful_mind',23,14,512,1,0,0,0,1,0,0,5,0,0,0,0,30,0,0,14,693,2,2,234889909); +INSERT INTO `server_battle_commands` VALUES (27344,'presence_of_mind',27,30,0,1,0,0,0,1,0,0,5,0,0,0,0,300,0,0,14,722,2,2,234889938); +INSERT INTO `server_battle_commands` VALUES (27345,'benediction',27,50,0,1,1,0,0,1,0,0,5,0,0,0,0,900,0,0,14,723,2,2,234889939); +INSERT INTO `server_battle_commands` VALUES (27346,'cure',23,2,3,2,0,0,0,1,0,0,15,0,0,0,2,5,40,0,1,101,1,2,16781413); +INSERT INTO `server_battle_commands` VALUES (27347,'cura',23,30,512,2,0,0,0,1,0,0,15,0,0,0,2,5,100,0,1,103,1,2,16781415); +INSERT INTO `server_battle_commands` VALUES (27348,'curaga',23,46,512,4,1,0,0,1,0,0,15,0,0,0,3,10,150,0,1,146,1,2,16781458); +INSERT INTO `server_battle_commands` VALUES (27349,'raise',23,18,512,2,0,0,0,1,0,0,15,0,0,0,10,300,150,0,1,148,1,2,16781460); +INSERT INTO `server_battle_commands` VALUES (27350,'stoneskin',23,26,3,2,0,0,0,1,0,0,15,0,300,0,3,30,50,0,1,133,1,2,16781445); +INSERT INTO `server_battle_commands` VALUES (27351,'protect',23,6,3,4,1,0,0,1,0,0,15,0,300,0,3,30,80,0,1,1085,1,2,16782397); +INSERT INTO `server_battle_commands` VALUES (27352,'repose',23,50,0,32,0,0,0,1,0,0,15,0,0,0,3,0,80,0,1,151,1,2,16781463); +INSERT INTO `server_battle_commands` VALUES (27353,'aero',23,4,3,32,0,0,0,1,0,0,15,0,0,0,3,6,75,0,1,510,1,2,16781822); +INSERT INTO `server_battle_commands` VALUES (27354,'aerora',23,42,512,32,1,0,0,1,0,0,15,0,0,0,4,20,150,0,1,511,1,2,16781823); +INSERT INTO `server_battle_commands` VALUES (27355,'stone',23,1,3,32,0,0,0,1,0,0,15,0,0,0,2,6,75,0,1,513,1,2,16781825); +INSERT INTO `server_battle_commands` VALUES (27356,'stonera',23,22,512,32,1,0,0,1,0,0,15,0,0,0,3,30,150,0,1,514,1,2,16781826); +INSERT INTO `server_battle_commands` VALUES (27357,'esuna',27,40,0,2,0,0,0,1,0,0,15,0,0,0,2,10,40,0,1,702,1,2,16782014); +INSERT INTO `server_battle_commands` VALUES (27358,'regen',27,35,0,2,0,0,0,1,0,0,15,0,0,0,2,5,20,0,1,703,1,2,16782015); +INSERT INTO `server_battle_commands` VALUES (27359,'holy',27,45,0,32,1,0,0,1,0,0,15,0,0,0,0,300,100,0,1,708,1,2,16782020); /*!40000 ALTER TABLE `server_battle_commands` ENABLE KEYS */; UNLOCK TABLES; commit; @@ -212,4 +214,4 @@ commit; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2017-08-28 1:38:36 +-- Dump completed on 2017-08-31 5:52:52