project-meteor-server/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs
Tahir Akhlaq c5cc7c2f00 fixed auto attack messing up cast anim
- fixed auto attack anim for mobs (<3 u ion)
- added hotbar timer updates (<3 u azia)
- fixed actor block bug
- cleaned up substate retardation
- fixed some targetfind issues
- added despawn state
- added messages for in progress commands
- added fields for aoe target, range, battleAnimation to server_battle_commands table
2017-08-31 06:01:26 +01:00

235 lines
7.6 KiB
C#

using FFXIVClassic_Map_Server.Actors;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Map_Server.actors.chara.player;
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.packets.send.actor;
using FFXIVClassic_Map_Server.actors.chara.ai.utils;
namespace FFXIVClassic_Map_Server.actors.chara.ai
{
public enum BattleCommandRequirements : ushort
{
None,
DiscipleOfWar = 0x01,
DiscipeOfMagic = 0x02,
HandToHand = 0x04,
Sword = 0x08,
Shield = 0x10,
Axe = 0x20,
Archery = 0x40,
Polearm = 0x80,
Thaumaturgy = 0x100,
Conjury = 0x200
}
public enum BattleCommandPositionBonus : byte
{
None,
Front = 0x01,
Rear = 0x02,
Flank = 0x04
}
public enum BattleCommandProcRequirement : byte
{
None,
Evade = 0x01,
Block = 0x02,
Parry = 0x04,
Miss = 0x08
}
class BattleCommand
{
public ushort id;
public string name;
public byte job;
public byte level;
public BattleCommandRequirements requirements;
public ValidTarget validTarget;
public TargetFindAOEType aoeType;
public TargetFindAOETarget aoeTarget;
public byte numHits;
public BattleCommandPositionBonus positionBonus;
public BattleCommandProcRequirement procRequirement;
public int range;
public uint debuffDurationSeconds;
public uint buffDurationSeconds;
public byte castType;
public uint castTimeSeconds;
public uint recastTimeSeconds;
public ushort mpCost;
public ushort tpCost;
public byte animationType;
public ushort effectAnimation;
public ushort modelAnimation;
public ushort animationDurationSeconds;
public uint battleAnimation;
public ushort worldMasterTextId;
public int aoeRange;
public TargetFind targetFind;
public BattleCommand(ushort id, string name)
{
this.id = id;
this.name = name;
this.range = 0;
}
public BattleCommand Clone()
{
return (BattleCommand)MemberwiseClone();
}
public bool IsSpell()
{
return mpCost != 0 || castTimeSeconds != 0;
}
public bool IsInstantCast()
{
return castTimeSeconds == 0;
}
public bool IsValidTarget(Character user, Character target)
{
// todo: set box length..
targetFind = new TargetFind(user);
if (aoeType == TargetFindAOEType.Box)
{
// todo: read box width from sql
targetFind.SetAOEBox(validTarget, aoeTarget, range, aoeRange);
}
else
{
targetFind.SetAOEType(validTarget, aoeType, aoeTarget, range, aoeRange);
}
/*
worldMasterTextId
32512 cannot be performed on a KO'd target.
32513 can only be performed on a KO'd target.
32514 cannot be performed on yourself.
32515 can only be performed on yourself.
32516 cannot be performed on a friendly target.
32517 can only be performed on a friendly target.
32518 cannot be performed on an enemy.
32519 can only be performed on an enemy,
*/
// cant target dead
if ((validTarget & (ValidTarget.Corpse | ValidTarget.CorpseOnly)) == 0 && target.IsDead())
{
// cannot be perfomed on
if (user is Player)
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32512, 0x20, (uint)id);
return false;
}
if (level > user.charaWork.parameterSave.state_mainSkillLevel)
{
if (user is Player)
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32527, 0x20, (uint)id);
return false;
}
if (tpCost > user.GetTP())
{
if (user is Player)
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32546, 0x20, (uint)id);
return false;
}
// todo: calculate cost based on modifiers also (probably in BattleUtils)
if (BattleUtils.CalculateSpellCost(user, target, this) > user.GetMP())
{
// todo: error message
if (user is Player)
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32545, 0x20, (uint)id);
return false;
}
// todo: check target requirements
if (requirements != BattleCommandRequirements.None)
{
if (false)
{
// Unable to execute [@SHEET(xtx/command,$E8(1),2)]. Conditions for use are not met.
if (user is Player)
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32556, 0x20, (uint)id);
return false;
}
}
// todo: i dont care to message for each scenario, just the most common ones..
if ((validTarget & ValidTarget.CorpseOnly) != 0)
{
if (target != null && target.IsAlive())
{
if (user is Player)
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32513, 0x20, (uint)id);
return false;
}
}
if ((validTarget & ValidTarget.Enemy) != 0)
{
if (target == user || target != null &&
user.allegiance == target.allegiance)
{
if (user is Player)
((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32519, 0x20, (uint)id);
return false;
}
}
if ((validTarget & (ValidTarget.PartyMember | ValidTarget.Player | ValidTarget.Ally)) != 0)
{
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, true);
}
public ushort CalculateCost(uint level)
{
// todo: use precalculated costs instead
ushort cost = 0;
if (level <= 10)
cost = (ushort)(100 + level * 10);
else if (level <= 20)
cost = (ushort)(200 + (level - 10) * 20);
else if (level <= 30)
cost = (ushort)(400 + (level - 20) * 40);
else if (level <= 40)
cost = (ushort)(800 + (level - 30) * 70);
else if (level <= 50)
cost = (ushort)(1500 + (level - 40) * 130);
else if (level <= 60)
cost = (ushort)(2800 + (level - 50) * 200);
else if (level <= 70)
cost = (ushort)(4800 + (level - 60) * 320);
else
cost = (ushort)(8000 + (level - 70) * 500);
if (mpCost != 0)
return (ushort)Math.Ceiling((cost * mpCost * 0.001));
return tpCost;
}
}
}