diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs
index 1990683b..32b8fcae 100644
--- a/FFXIVClassic Map Server/Database.cs
+++ b/FFXIVClassic Map Server/Database.cs
@@ -1884,6 +1884,7 @@ namespace FFXIVClassic_Map_Server
ability.effectAnimation = reader.GetUInt16(19);
ability.modelAnimation = reader.GetUInt16(20);
ability.animationDurationSeconds = reader.GetUInt16(21);
+ ability.battleAnimation = (uint)((ability.animationType << 24) | (ability.modelAnimation << 12) | (ability.effectAnimation));
abilities.Add(id, ability);
}
diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
index 6c873d1a..5292fbf7 100644
--- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
+++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
@@ -98,6 +98,7 @@
+
diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs
index dd1e61bf..0895f0a8 100644
--- a/FFXIVClassic Map Server/actors/Actor.cs
+++ b/FFXIVClassic Map Server/actors/Actor.cs
@@ -670,7 +670,7 @@ namespace FFXIVClassic_Map_Server.Actors
return false;
}
- return IsFacing(target.positionX, target.positionY, angle);
+ return IsFacing(target.positionX, target.positionZ, angle);
}
public void QueuePositionUpdate(Vector3 pos)
diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs
index 761afcb0..5f886131 100644
--- a/FFXIVClassic Map Server/actors/chara/Character.cs
+++ b/FFXIVClassic Map Server/actors/chara/Character.cs
@@ -24,6 +24,14 @@ namespace FFXIVClassic_Map_Server.Actors
class Character : Actor
{
+ public const int CLASSID_PUG = 2;
+ public const int CLASSID_GLA = 3;
+ public const int CLASSID_MRD = 4;
+ public const int CLASSID_ARC = 7;
+ public const int CLASSID_LNC = 8;
+ public const int CLASSID_THM = 22;
+ public const int CLASSID_CNJ = 23;
+
public const int SIZE = 0;
public const int COLORINFO = 1;
public const int FACEINFO = 2;
@@ -77,14 +85,16 @@ namespace FFXIVClassic_Map_Server.Actors
public AIContainer aiContainer;
public StatusEffectContainer statusEffects;
- public float meleeRange;
- protected uint attackDelayMs;
public CharacterTargetingAllegiance allegiance;
public Pet pet;
- public Dictionary modifiers = new Dictionary();
+ public Dictionary modifiers = new Dictionary();
+
+ protected ushort hpBase, hpMaxBase, mpBase, mpMaxBase, tpBase;
+ protected BattleTemp baseStats = new BattleTemp();
+ public ushort currentJob;
public Character(uint actorID) : base(actorID)
{
@@ -95,9 +105,10 @@ namespace FFXIVClassic_Map_Server.Actors
this.statusEffects = new StatusEffectContainer(this);
// todo: move this somewhere more appropriate
- attackDelayMs = 4200;
- meleeRange = 2.5f;
ResetMoveSpeeds();
+ // todo: base this on equip and shit
+ SetMod((uint)Modifier.AttackRange, 3);
+ SetMod((uint)Modifier.AttackDelay, 4200);
}
public SubPacket CreateAppearancePacket()
@@ -148,6 +159,7 @@ namespace FFXIVClassic_Map_Server.Actors
foreach (var effect in statusEffects.GetStatusEffects())
{
propPacketUtil.AddProperty($"charaWork.statusShownTime[{i}]");
+ propPacketUtil.AddProperty(String.Format("charaWork.statusShownTime[{0}]", i));
i++;
}
return propPacketUtil.Done();
@@ -192,8 +204,9 @@ namespace FFXIVClassic_Map_Server.Actors
public Int64 GetMod(uint modifier)
{
Int64 res;
- modifiers.TryGetValue((Modifier)modifier, out res);
- return res;
+ if (modifiers.TryGetValue((Modifier)modifier, out res))
+ return res;
+ return 0;
}
public void SetMod(uint modifier, Int64 val)
@@ -253,7 +266,12 @@ namespace FFXIVClassic_Map_Server.Actors
public virtual uint GetAttackDelayMs()
{
- return attackDelayMs;
+ return (uint)GetMod((uint)Modifier.AttackDelay);
+ }
+
+ public virtual uint GetAttackRange()
+ {
+ return (uint)GetMod((uint)Modifier.AttackRange);
}
public bool Engage(uint targid = 0)
@@ -279,10 +297,23 @@ namespace FFXIVClassic_Map_Server.Actors
return false;
}
+ public void Cast(uint spellId)
+ {
+ aiContainer.Cast(Server.GetWorldManager().GetActorInWorld(currentTarget) as Character, spellId);
+ }
+
+ public void WeaponSkill(uint skillId)
+ {
+ aiContainer.WeaponSkill(Server.GetWorldManager().GetActorInWorld(currentTarget) as Character, skillId);
+ }
+
public virtual void Spawn(DateTime tick)
{
// todo: reset hp/mp/tp etc here
- RecalculateHpMpTp();
+ ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE);
+ charaWork.parameterSave.hp = charaWork.parameterSave.hpMax;
+ charaWork.parameterSave.mp = charaWork.parameterSave.mpMax;
+ RecalculateStats();
}
public virtual void Die(DateTime tick)
@@ -306,47 +337,105 @@ namespace FFXIVClassic_Map_Server.Actors
return !IsDead();
}
- public virtual short GetHP()
+ public short GetHP()
{
// todo:
- return charaWork.parameterSave.hp[0];
+ return charaWork.parameterSave.hp[currentJob];
}
- public virtual short GetMaxHP()
+ public short GetMaxHP()
{
- return charaWork.parameterSave.hpMax[0];
+ return charaWork.parameterSave.hpMax[currentJob];
}
- public virtual byte GetHPP()
+ public short GetMP()
{
- return (byte)(charaWork.parameterSave.hp[0] / charaWork.parameterSave.hpMax[0]);
+ return charaWork.parameterSave.mp;
}
- public virtual void AddHP(short hp)
+ public ushort GetTP()
+ {
+ return tpBase;
+ }
+
+ public short GetMaxMP()
+ {
+ return charaWork.parameterSave.mpMax;
+ }
+
+ public byte GetMPP()
+ {
+ return (byte)((charaWork.parameterSave.mp / charaWork.parameterSave.mpMax) * 100);
+ }
+
+ public byte GetTPP()
+ {
+ return (byte)((tpBase / 3000) * 100);
+ }
+
+ public byte GetHPP()
+ {
+ return (byte)((charaWork.parameterSave.hp[0] / charaWork.parameterSave.hpMax[0]) * 100);
+ }
+
+ // todo: the following functions are virtuals since we want to check hidden item bonuses etc on player for certain conditions
+ public virtual void AddHP(int hp)
{
// todo: +/- hp and die
// todo: battlenpcs probably have way more hp?
- var addHp = charaWork.parameterSave.hp[0] + hp;
- addHp = addHp.Clamp(short.MinValue, charaWork.parameterSave.hpMax[0]);
- charaWork.parameterSave.hp[0] = (short)addHp;
+ var addHp = charaWork.parameterSave.hp[currentJob] + hp;
+ addHp = addHp.Clamp(ushort.MinValue, charaWork.parameterSave.hpMax[currentJob]);
+ charaWork.parameterSave.hp[currentJob] = (short)addHp;
- if (charaWork.parameterSave.hp[0] < 1)
+ if (charaWork.parameterSave.hp[currentJob] < 1)
Die(Program.Tick);
updateFlags |= ActorUpdateFlags.HpTpMp;
}
- public virtual void DelHP(short hp)
+ public void AddMP(int mp)
+ {
+ charaWork.parameterSave.mp = (short)(charaWork.parameterSave.mp + mp).Clamp(ushort.MinValue, charaWork.parameterSave.mpMax);
+
+ // todo: check hidden effects and shit
+
+ updateFlags |= ActorUpdateFlags.HpTpMp;
+ }
+
+ public void AddTP(int tp)
+ {
+ tpBase = (ushort)((tpBase + tp).Clamp(0, 3000));
+
+ updateFlags |= ActorUpdateFlags.HpTpMp;
+ }
+
+ public void DelHP(int hp)
{
AddHP((short)-hp);
}
- // todo: should this include stats too?
- public virtual void RecalculateHpMpTp()
+ public void DelMP(int mp)
{
- // legit fuck c#
- // todo: other shit too..
- meleeRange = GetMod((uint)Modifier.AttackRange);
+ AddMP(-mp);
+ }
+
+ public void DelTP(int tp)
+ {
+ AddTP(-tp);
+ }
+
+ public void CalculateBaseStats()
+ {
+ // todo: apply mods and shit here, get race/level/job and shit
+ // baseStats.generalParameter[ASIDHOASID] =
+ }
+ // todo: should this include stats too?
+ public void RecalculateStats()
+ {
+ if (GetMod((uint)Modifier.Hp) != 0)
+ {
+
+ }
// todo: recalculate stats and crap
updateFlags |= ActorUpdateFlags.HpTpMp;
}
@@ -354,7 +443,7 @@ namespace FFXIVClassic_Map_Server.Actors
public virtual float GetSpeed()
{
// todo: for battlenpc/player calculate speed
- return moveSpeeds[2];
+ return moveSpeeds[2] + GetMod((uint)Modifier.Speed);
}
}
diff --git a/FFXIVClassic Map Server/actors/chara/Modifier.cs b/FFXIVClassic Map Server/actors/chara/Modifier.cs
index e8073de1..08558d88 100644
--- a/FFXIVClassic Map Server/actors/chara/Modifier.cs
+++ b/FFXIVClassic Map Server/actors/chara/Modifier.cs
@@ -42,16 +42,14 @@ namespace FFXIVClassic_Map_Server.actors.chara
ResistWater = 31, // <3 u jorge
AttackRange = 32,
Speed = 33,
-
+ AttackDelay = 34,
- /* fuck off
- CRAFT_PROCESSING = 30,
- CRAFT_MAGIC_PROCESSING = 31,
- CRAFT_PROCESS_CONTROL = 32,
+ CraftProcessing = 35,
+ CraftMagicProcessing = 36,
+ CraftProcessControl = 37,
- HARVEST_POTENCY = 33,
- HARVEST_LIMIT = 34,
- HARVEST_RATE = 35,
- */
+ HarvestPotency = 38,
+ HarvestLimit = 39,
+ HarvestRate = 40
}
}
diff --git a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs
index 1fdc36f0..0883ef51 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs
@@ -295,12 +295,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
public void InternalCast(Character target, uint spellId)
{
-
+ ChangeState(new MagicState(owner, target, (ushort)spellId));
}
public void InternalWeaponSkill(Character target, uint weaponSkillId)
{
-
+ ChangeState(new WeaponSkillState(owner, target, (ushort)weaponSkillId));
}
public void InternalMobSkill(Character target, uint mobSkillId)
@@ -310,7 +310,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
public void InternalDie(DateTime tick, uint timeToFadeout)
{
- if (true) return;
ClearStates();
Disengage();
ForceChangeState(new DeathState(owner, tick, timeToFadeout));
diff --git a/FFXIVClassic Map Server/actors/chara/ai/Ability.cs b/FFXIVClassic Map Server/actors/chara/ai/Ability.cs
index 3bfeb94b..e94f10dd 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/Ability.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/Ability.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using FFXIVClassic_Map_Server.actors.chara.player;
namespace FFXIVClassic_Map_Server.actors.chara.ai
{
@@ -42,7 +43,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
class Ability
{
- public ushort abilityId;
+ public ushort id;
public string name;
public byte job;
public byte level;
@@ -65,14 +66,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
public ushort modelAnimation;
public ushort animationDurationSeconds;
+ public uint battleAnimation;
+ public ushort worldMasterTextId;
+ public uint param;
public TargetFind targetFind;
public Ability(ushort id, string name)
{
- this.abilityId = id;
+ this.id = id;
this.name = name;
- this.range = -1;
+ this.range = 0;
}
public Ability Clone()
@@ -90,10 +94,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
return castTimeSeconds == 0;
}
- public bool CanPlayerUse(Character user, Character target)
+ 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
@@ -103,7 +108,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
{
targetFind.SetAOEType(validTarget, aoeType, range, 40);
}
- return false;
+ return targetFind.CanTarget(target, true, true);
}
}
}
diff --git a/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs b/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs
index b1d9973e..cba1a8fc 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/PathFind.cs
@@ -91,7 +91,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
{
var dest = owner.FindRandomPoint(x, y, z, minRange, maxRange);
// todo: this is dumb..
- distanceFromPoint = owner.meleeRange;
+ distanceFromPoint = owner.GetAttackRange();
PreparePath(dest.X, dest.Y, dest.Z);
}
@@ -148,14 +148,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
{
float speed = GetSpeed();
- float stepDistance = speed;
+ float stepDistance = speed / 3;
float distanceTo = Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, point.X, point.Y, point.Z);
owner.LookAt(point.X, point.Y);
if (distanceTo <= distanceFromPoint + stepDistance)
{
- if (distanceFromPoint <= 1.5f)
+ if (distanceFromPoint <= owner.GetAttackRange())
{
owner.QueuePositionUpdate(point);
}
diff --git a/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs b/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs
index bdb3baf6..d6c90d73 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs
@@ -419,7 +419,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
public bool Update(DateTime tick)
{
// todo: maybe not tick if already reached duration?
- if (tickMs != 0 && (lastTick - startTime).TotalMilliseconds >= tickMs)
+ if (tickMs != 0 && (tick - lastTick).TotalMilliseconds >= tickMs)
{
// todo: call effect's onTick
// todo: maybe keep a global lua object instead of creating a new one each time we wanna call a script
@@ -443,7 +443,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
public Character GetSource()
{
- return source;
+ return source ?? owner;
}
public uint GetStatusEffectId()
diff --git a/FFXIVClassic Map Server/actors/chara/ai/StatusEffectContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/StatusEffectContainer.cs
index fd3a2562..17af439c 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/StatusEffectContainer.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/StatusEffectContainer.cs
@@ -46,18 +46,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
if (sendUpdate)
{
- var propPacketUtil = new ActorPropertyPacketUtil("charaWork.status", owner);
-
- //Status Times
- for (int i = 0; i < owner.charaWork.statusShownTime.Length; i++)
- {
- if (owner.charaWork.status[i] != 0xFFFF && owner.charaWork.status[i] != 0)
- propPacketUtil.AddProperty(String.Format("charaWork.status[{0}]", i));
-
- if (owner.charaWork.statusShownTime[i] != 0xFFFFFFFF)
- propPacketUtil.AddProperty(String.Format("charaWork.statusShownTime[{0}]", i));
- }
- owner.zone.BroadcastPacketsAroundActor(owner, propPacketUtil.Done());
+ owner.zone.BroadcastPacketsAroundActor(owner, owner.GetActorStatusPackets());
sendUpdate = false;
}
}
@@ -97,7 +86,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
{
// todo: send packet to client with effect added message
foreach (var player in owner.zone.GetActorsAroundActor(owner, 50))
- player.QueuePacket(packets.send.actor.battle.BattleActionX01Packet.BuildPacket(player.actorId, owner.actorId, owner.actorId, 0, newEffect.GetStatusEffectId(), 0, newEffect.GetStatusId(), 0, 0));
+ player.QueuePacket(packets.send.actor.battle.BattleActionX01Packet.BuildPacket(player.actorId, newEffect.GetSource().actorId, newEffect.GetOwner().actorId, 0x7678, 0, 0, newEffect.GetStatusId(), 0, 0));
}
// wont send a message about losing effect here
@@ -115,8 +104,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
owner.charaWork.statusShownTime[index] = Utils.UnixTimeStampUTC() + (newEffect.GetDurationMs() / 1000);
this.owner.zone.BroadcastPacketAroundActor(this.owner, SetActorStatusPacket.BuildPacket(this.owner.actorId, (ushort)index, (ushort)newEffect.GetStatusId()));
}
- owner.RecalculateHpMpTp();
- sendUpdate = true;
+ {
+ owner.zone.BroadcastPacketsAroundActor(owner, owner.GetActorStatusPackets());
+ }
+ owner.RecalculateStats();
}
return true;
}
@@ -130,9 +121,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
// send packet to client with effect remove message
if (!silent || !effect.GetSilent() || (effect.GetFlags() & (uint)StatusEffectFlags.Silent) == 0)
{
- // todo: send packet to client with effect removed message
- //foreach (var player in owner.zone.GetActorsAroundActor(owner, 50))
- // player.QueuePacket(packets.send.actor.battle.BattleActionX01Packet.BuildPacket(player.actorId, effect.GetSource().actorId, owner.actorId, 0, effect.GetStatusEffectId(), 0, effect.GetStatusId(), 0, 0));
+ // todo: send packet to client with effect added message
+ foreach (var player in owner.zone.GetActorsAroundActor(owner, 50))
+ player.QueuePacket(packets.send.actor.battle.BattleActionX01Packet.BuildPacket(player.actorId, owner.actorId, owner.actorId, 0x7679, 0, 0, effect.GetStatusId(), 0, 0));
}
// todo: this is retarded..
@@ -145,7 +136,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
// function onLose(actor, effect)
LuaEngine.CallLuaStatusEffectFunction(this.owner, effect, "onLose", this.owner, effect);
effects.Remove(effect.GetStatusEffectId());
- owner.RecalculateHpMpTp();
+ owner.RecalculateStats();
sendUpdate = true;
}
}
diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs
index 032699f7..3f0d47e5 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs
@@ -82,7 +82,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
if (Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) > 10)
{
owner.aiContainer.pathFind.SetPathFlags(PathFindFlags.None);
- owner.aiContainer.pathFind.PathInRange(target.positionX, target.positionY, target.positionZ, 1.5f, owner.meleeRange);
+ owner.aiContainer.pathFind.PathInRange(target.positionX, target.positionY, target.positionZ, 1.5f, owner.GetAttackRange());
ChangeTarget(target);
return false;
}
@@ -114,22 +114,22 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
public override void Cast(Character target, uint spellId)
{
-
+ // todo:
}
public override void Ability(Character target, uint abilityId)
{
-
+ // todo:
}
public override void RangedAttack(Character target)
{
-
+ // todo:
}
public override void MonsterSkill(Character target, uint mobSkillId)
{
-
+ // todo:
}
private void DoRoamTick(DateTime tick)
@@ -202,7 +202,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
var targetPos = new Vector3(owner.target.positionX, owner.target.positionY, owner.target.positionZ);
var distance = Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, targetPos.X, targetPos.Y, targetPos.Z);
- if (distance > owner.meleeRange - 0.2f || owner.aiContainer.CanFollowPath())
+ if (distance > owner.GetAttackRange() - 0.2f || owner.aiContainer.CanFollowPath())
{
if (CanMoveForward(distance))
{
diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs
index ce82c0d1..83e662ee 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs
@@ -63,7 +63,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
public override void Cast(Character target, uint spellId)
{
-
+ owner.aiContainer.InternalCast(target, spellId);
}
public override void Ability(Character target, uint abilityId)
diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs
index a5f987f5..28aa9f9b 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs
@@ -100,7 +100,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
player.QueuePacket(packet);
}
}
- //target.DelHP((short)damage);
+ target.DelHP((short)damage);
attackTime = attackTime.AddMilliseconds(owner.GetAttackDelayMs());
owner.LookAt(target);
//this.errorPacket = BattleActionX01Packet.BuildPacket(target.actorId, owner.actorId, target.actorId, 0, effectId, 0, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, 0);
@@ -150,9 +150,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
return false;
}
// todo: use a mod for melee range
- else if (Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) > owner.meleeRange)
+ else if (Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) > owner.GetAttackRange())
{
- //owner.aiContainer.GetpathFind?.PreparePath(target.positionX, target.positionY, target.positionZ, 2.5f, 4);
+ if (owner.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER)
+ {
+ ((Player)owner).SendGameMessage(Server.GetWorldManager().GetActor(), 32539, 0x20);
+ }
return false;
}
return true;
diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs
index 19e7b7e7..6b78eb0b 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs
@@ -28,7 +28,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
if (owner.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER)
{
// todo: mark for zoning and remove after main loop
- owner.ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE);
+ owner.Spawn(Program.Tick);
//Server.GetWorldManager().DoZoneChange(((Player)owner), 244, null, 0, 15, -160.048f, 0, -165.737f, 0.0f);
}
else
diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs
index 5c407dd1..85c6bade 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs
@@ -13,6 +13,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
{
private Ability spell;
+ private uint cost;
+ private Vector3 startPos;
public MagicState(Character owner, Character target, ushort spellId) :
base(owner, target)
@@ -20,53 +22,132 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
this.startTime = DateTime.Now;
// todo: lookup spell from global table
this.spell = Server.GetWorldManager().GetAbility(spellId);
+ var returnCode = lua.LuaEngine.CallLuaAbilityFunction(owner, spell, "spells", "onSpellPrepare", owner, target, spell);
- if (spell != null)
+ if (spell != null && returnCode == 0)
{
- if (spell.CanPlayerUse(owner, target))
+ // todo: hp/mp shit should be taken care of in scripts, not here
+ // todo: Azia can fix, check the recast time and send error
+
+ if (!spell.IsValidTarget(owner, target))
+ {
+ // todo: error message
+ interrupt = true;
+ }
+ else if ((spell.mpCost = (ushort)Math.Ceiling((8000 + (owner.charaWork.parameterSave.state_mainSkillLevel - 70) * 500) * (spell.mpCost * 0.001))) > owner.GetMP())
+ {
+ // todo: error message
+ interrupt = true;
+ }
+ else if (spell.level > owner.charaWork.parameterSave.state_mainSkillLevel)
+ {
+ // todo: error message
+ }
+ else if (false /*spell.requirements & */)
+ {
+ // todo: error message
+ }
+ else
+ {
OnStart();
+ }
+ }
+ else
+ {
+ // todo: fuckin retarded. enum log messages somewhere (prolly isnt even right param)
+ if (owner is Player)
+ ((Player)owner).SendGameMessage(Server.GetWorldManager().GetActor(), (ushort)(returnCode == -1 ? 32539 : returnCode), 0x20);
+ interrupt = true;
}
}
public override void OnStart()
{
- // todo: check within attack range
+ var returnCode = lua.LuaEngine.CallLuaAbilityFunction(owner, spell, "spells", "onSpellStart", owner, target, spell);
- owner.LookAt(target);
+ if (returnCode != 0)
+ {
+ interrupt = true;
+ errorPacket = BattleActionX01Packet.BuildPacket(owner.actorId, owner.actorId, owner.actorId, 0, 0, (ushort)(returnCode == -1 ? 32539 : returnCode), spell.id, 0, 1);
+ }
+ else
+ {
+ // todo: check within attack range
+ startPos = owner.GetPosAsVector3();
+ owner.LookAt(target);
+
+ foreach (var player in owner.zone.GetActorsAroundActor(owner, 50))
+ {
+ // todo: this is retarded, prolly doesnt do what i think its gonna do
+ player.QueuePacket(BattleActionX01Packet.BuildPacket(player.actorId, owner.actorId, target != null ? target.actorId : 0xC0000000, spell.battleAnimation, spell.effectAnimation, 0, spell.id, 0, (byte)spell.castTimeSeconds));
+ }
+ }
}
public override bool Update(DateTime tick)
{
- TryInterrupt();
-
- if (interrupt)
+ if (spell != null)
{
- OnInterrupt();
- return true;
- }
+ TryInterrupt();
- // todo: check weapon delay/haste etc and use that
- if ((tick - startTime).TotalMilliseconds >= 0)
- {
- OnComplete();
- return true;
+ if (interrupt)
+ {
+ OnInterrupt();
+ return true;
+ }
+
+ // todo: check weapon delay/haste etc and use that
+ var actualCastTime = spell.castTimeSeconds;
+
+ if ((tick - startTime).TotalSeconds >= spell.castTimeSeconds)
+ {
+ OnComplete();
+ return true;
+ }
+ return false;
}
- return false;
+ return true;
}
public override void OnInterrupt()
{
// todo: send paralyzed/sleep message etc.
+ if (errorPacket != null)
+ {
+ owner.zone.BroadcastPacketAroundActor(owner, errorPacket);
+ }
}
public override void OnComplete()
{
- //this.errorPacket = BattleActionX01Packet.BuildPacket(target.actorId, owner.actorId, target.actorId, 0, effectId, 0, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, 0);
+ spell.targetFind.FindWithinArea(target, spell.validTarget);
isCompleted = true;
+
+ List packets = new List();
+ foreach (var chara in spell.targetFind.GetTargets())
+ {
+ // todo: calculate shit, do shit
+ bool landed = true;
+ var amount = lua.LuaEngine.CallLuaAbilityFunction(owner, spell, "spells", "onSpellFinish", owner, target, spell);
+
+ foreach (var player in owner.zone.GetActorsAroundActor(owner, 50))
+ {
+ player.QueuePacket(BattleActionX01Packet.BuildPacket(player.actorId, owner.actorId, chara.actorId, spell.battleAnimation, spell.effectAnimation, spell.worldMasterTextId, spell.id, (ushort)spell.param, 1));
+ }
+
+ if (chara is BattleNpc)
+ {
+ ((BattleNpc)chara).hateContainer.UpdateHate(owner, amount);
+ }
+ }
+
}
public override void TryInterrupt()
{
+ if (interrupt)
+ return;
+
if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventAction))
{
// todo: sometimes paralyze can let you attack, get random percentage of actually letting you attack
@@ -85,10 +166,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
return;
}
- interrupt = !CanAttack();
+ if (Utils.DistanceSquared(owner.GetPosAsVector3(), startPos) > 4.0f)
+ {
+ // todo: send interrupt packet
+ interrupt = true;
+ return;
+ }
+
+ interrupt = !CanCast();
}
- private bool CanAttack()
+ private bool CanCast()
{
if (target == null)
{
@@ -103,9 +191,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
{
return false;
}
- else if (Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) >= 7.5f)
+ else if (Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) > spell.range)
{
- owner.aiContainer.pathFind?.PreparePath(target.positionX, target.positionY, target.positionZ, 2.5f, 4);
+ if (owner.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER)
+ {
+ ((Player)owner).SendGameMessage(Server.GetWorldManager().GetActor(), 32539, 0x20);
+ }
return false;
}
return true;
diff --git a/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs b/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs
index 35e70338..1194d7f2 100644
--- a/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs
+++ b/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs
@@ -23,5 +23,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
}
defender.DelHP((short)damage);
}
+
+ public static int CalculateSpellDamage(Character attacker, Character defender, Ability spell)
+ {
+ // todo: spell formulas and stuff (stoneskin, mods, stats, etc)
+ return 69;
+ }
}
}
diff --git a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs
index 6aa840fd..5d77a095 100644
--- a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs
+++ b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs
@@ -58,8 +58,9 @@ namespace FFXIVClassic_Map_Server.Actors
aggroType = AggroType.Sight;
this.moveState = 2;
ResetMoveSpeeds();
- this.meleeRange = 1.5f;
despawnTime = 10;
+
+ CalculateBaseStats();
}
public override void Update(DateTime tick)
@@ -85,7 +86,7 @@ namespace FFXIVClassic_Map_Server.Actors
propPacketUtil.AddProperty("charaWork.battleTemp.castGauge_speed[1]");
packets.AddRange(propPacketUtil.Done());
}
- base.PostUpdate(tick);
+ base.PostUpdate(tick, packets);
}
public override bool CanAttack()
diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs
index bc41e524..f09ea137 100644
--- a/FFXIVClassic Map Server/actors/chara/player/Player.cs
+++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs
@@ -26,14 +26,6 @@ namespace FFXIVClassic_Map_Server.Actors
{
class Player : Character
{
- public const int CLASSID_PUG = 2;
- public const int CLASSID_GLA = 3;
- public const int CLASSID_MRD = 4;
- public const int CLASSID_ARC = 7;
- public const int CLASSID_LNC = 8;
- public const int CLASSID_THM = 22;
- public const int CLASSID_CNJ = 23;
-
public const int CLASSID_CRP = 29;
public const int CLASSID_BSM = 30;
public const int CLASSID_ARM = 31;
@@ -97,7 +89,6 @@ namespace FFXIVClassic_Map_Server.Actors
public uint destinationZone;
public ushort destinationSpawnType;
public uint[] timers = new uint[20];
- public ushort currentJob;
public uint currentTitle;
public uint playTime;
public uint lastPlayTimeUpdate;
@@ -1729,30 +1720,15 @@ namespace FFXIVClassic_Map_Server.Actors
packets.AddRange(propPacketUtil.Done());
}
- base.PostUpdate(tick);
+ base.PostUpdate(tick, packets);
}
- public override short GetHP()
- {
- return charaWork.parameterSave.hp[currentJob];
- }
-
- public override short GetMaxHP()
- {
- return charaWork.parameterSave.hpMax[currentJob];
- }
-
- public override byte GetHPP()
- {
- return (byte)(charaWork.parameterSave.hp[currentJob] / charaWork.parameterSave.hpMax[currentJob]);
- }
-
- public override void AddHP(short hp)
+ public override void AddHP(int hp)
{
// todo: +/- hp and die
- // todo: battlenpcs probably have way more hp?
+ // todo: check hidden effects and shit
var addHp = charaWork.parameterSave.hp[currentJob] + hp;
- addHp = addHp.Clamp(short.MinValue, charaWork.parameterSave.hpMax[currentJob]);
+ addHp = addHp.Clamp(ushort.MinValue, charaWork.parameterSave.hpMax[currentJob]);
charaWork.parameterSave.hp[currentJob] = (short)addHp;
if (charaWork.parameterSave.hp[currentJob] < 1)
@@ -1761,17 +1737,6 @@ namespace FFXIVClassic_Map_Server.Actors
updateFlags |= ActorUpdateFlags.HpTpMp;
}
- public override void DelHP(short hp)
- {
- AddHP((short)-hp);
- }
-
- // todo: should this include stats too?
- public override void RecalculateHpMpTp()
- {
- // todo: recalculate stats and crap
- updateFlags |= ActorUpdateFlags.HpTpMp;
- }
public override void Die(DateTime tick)
{
@@ -1833,7 +1798,6 @@ namespace FFXIVClassic_Map_Server.Actors
QueuePackets(recastPacketUtil.Done());
}
-
public void EquipAbility(ushort hotbarSlot, ushort commandId)
{
//if (charaWork.commandAcquired[commandId])
@@ -1947,7 +1911,7 @@ namespace FFXIVClassic_Map_Server.Actors
public void SendBattleActionX01Packet(uint anim, uint effect, uint text = 0x756D, uint command = 27260, uint param = 0x01, uint idek = 0x01)
{
- var packet = BattleActionX01Packet.BuildPacket(actorId, actorId, actorId, (uint)anim, (uint)effect, (ushort)text, (ushort)command, (ushort)param, (byte)idek);
+ var packet = BattleActionX01Packet.BuildPacket(actorId, actorId, currentTarget != 0xC0000000 ? currentTarget : currentLockedTarget, (uint)anim, (uint)effect, (ushort)text, (ushort)command, (ushort)param, (byte)idek);
QueuePacket(packet);
}
}
diff --git a/FFXIVClassic Map Server/lua/LuaEngine.cs b/FFXIVClassic Map Server/lua/LuaEngine.cs
index 2e68c63e..01322ce9 100644
--- a/FFXIVClassic Map Server/lua/LuaEngine.cs
+++ b/FFXIVClassic Map Server/lua/LuaEngine.cs
@@ -167,7 +167,8 @@ namespace FFXIVClassic_Map_Server.lua
public static int CallLuaStatusEffectFunction(Character actor, StatusEffect effect, string functionName, params object[] args)
{
- string path = $"./scripts/effects/{effect.GetName()}.lua";
+ var name = ((StatusEffectId)effect.GetStatusEffectId()).ToString().ToLower();
+ string path = $"./scripts/effects/{name}.lua";
if (File.Exists(path))
{
@@ -186,7 +187,36 @@ namespace FFXIVClassic_Map_Server.lua
if (!script.Globals.Get(functionName).IsNil())
{
res = script.Call(script.Globals.Get(functionName), args);
- return (int)res.Number;
+ if (res != null)
+ return (int)res.Number;
+ }
+ }
+ return -1;
+ }
+
+ public static int CallLuaAbilityFunction(Character actor, Ability ability, string folder, string functionName, params object[] args)
+ {
+ string path = $"./scripts/{folder}/{ability.name}.lua";
+
+ if (File.Exists(path))
+ {
+ var script = LoadGlobals();
+
+ try
+ {
+ script.DoFile(path);
+ }
+ catch (Exception e)
+ {
+ Program.Log.Error($"LuaEngine.CallLuaSpellFunction [{functionName}] {e.Message}");
+ }
+ DynValue res = new DynValue();
+
+ if (!script.Globals.Get(functionName).IsNil())
+ {
+ res = script.Call(script.Globals.Get(functionName), args);
+ if (res != null)
+ return (int)res.Number;
}
}
return -1;
diff --git a/data/scripts/commands/ActivateCommand.lua b/data/scripts/commands/ActivateCommand.lua
index 3e5a3be9..43d48273 100644
--- a/data/scripts/commands/ActivateCommand.lua
+++ b/data/scripts/commands/ActivateCommand.lua
@@ -11,9 +11,10 @@ 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:ChangeState(0);
player.Disengage();
end
player:endEvent();
diff --git a/data/scripts/commands/AttackMagic.lua b/data/scripts/commands/AttackMagic.lua
new file mode 100644
index 00000000..43a95f5a
--- /dev/null
+++ b/data/scripts/commands/AttackMagic.lua
@@ -0,0 +1,36 @@
+require ("global")
+require ("utils")
+
+--[[
+
+AttackWeaponSkill Script
+
+Finds the correct weaponskill subscript to fire when a weaponskill actor is activated.
+
+--]]
+
+local attackMagicHandlers = {
+
+}
+
+function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8)
+ print(command.actorId)
+ --Are they in active mode?
+ if (player:GetState() != 2) then
+ player:SendGameMessage(GetWorldMaster(), 32503, 0x20);
+ player:endEvent();
+ return;
+ end
+
+ --Does the target exist
+ target = player:getZone():FindActorInArea(targetActor);
+ if (target == nil) then
+ player:SendGameMessage(GetWorldMaster(), 30203, 0x20);
+ player:endEvent();
+ return;
+ end
+
+ player.Cast(command.actorId);
+ player:endEvent();
+
+end
\ No newline at end of file
diff --git a/data/scripts/commands/AttackWeaponSkill.lua b/data/scripts/commands/AttackWeaponSkill.lua
index 61fe969f..8cebd1bb 100644
--- a/data/scripts/commands/AttackWeaponSkill.lua
+++ b/data/scripts/commands/AttackWeaponSkill.lua
@@ -9,80 +9,6 @@ Finds the correct weaponskill subscript to fire when a weaponskill actor is acti
--]]
-local function handlePummel(player, target)
- player:SendMessage(0x20, "", "DOING PUMMEL!!!!");
-
- params = {};
- params.range = 10.0;
- params.recast = 10;
-
- params.hpCost = 0;
- params.mpCost = 0;
- params.tpCost = 1000;
-
- params.targetType = 2;
- params.canCrit = true;
- params.animationId = 0x12312312;
-
-
-end
-
-local function handleSkullSunder(player)
- player:SendMessage(0x20, "", "DOING SKULL SUNDER!!!!");
-end
-
-local weaponskillHandlers = {
- [0xA0F069E6] = handlePummel,
- [0xA0F069E7] = nil,
- [0xA0F069E8] = nil,
- [0xA0F069E9] = nil,
- [0xA0F069EA] = nil,
- [0xA0F069EB] = nil,
- [0xA0F069EC] = nil,
- [0xA0F069ED] = nil,
- [0xA0F069EE] = nil,
- [0xA0F069EF] = nil,
- [0xA0F06A0E] = nil,
- [0xA0F06A0F] = nil,
- [0xA0F06A10] = nil,
- [0xA0F06A11] = nil,
- [0xA0F06A12] = nil,
- [0xA0F06A13] = nil,
- [0xA0F06A14] = nil,
- [0xA0F06A15] = nil,
- [0xA0F06A16] = nil,
- [0xA0F06A17] = nil,
- [0xA0F06A36] = nil,
- [0xA0F06A37] = handleSkullSunder,
- [0xA0F06A38] = nil,
- [0xA0F06A39] = nil,
- [0xA0F06A3A] = nil,
- [0xA0F06A3B] = nil,
- [0xA0F06A3C] = nil,
- [0xA0F06A3D] = nil,
- [0xA0F06A3E] = nil,
- [0xA0F06A3F] = nil,
- [0xA0F06A5C] = nil,
- [0xA0F06A5D] = nil,
- [0xA0F06A5E] = nil,
- [0xA0F06A60] = nil,
- [0xA0F06A61] = nil,
- [0xA0F06A62] = nil,
- [0xA0F06A63] = nil,
- [0xA0F06A64] = nil,
- [0xA0F06A85] = nil,
- [0xA0F06A86] = nil,
- [0xA0F06A87] = nil,
- [0xA0F06A88] = nil,
- [0xA0F06A89] = nil,
- [0xA0F06A8A] = nil,
- [0xA0F06A8B] = nil,
- [0xA0F06A8C] = nil,
- [0xA0F06A8D] = nil,
- [0xA0F06A8E] = nil,
- [0xA0F06A8F] = nil
-}
-
function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8)
--Are they in active mode?
@@ -100,19 +26,7 @@ function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, ta
return;
end
- --Are you too far away?
- if (getDistanceBetweenActors(player, target) > 7) then
- player:SendGameMessage(GetWorldMaster(), 32539, 0x20);
- player:endEvent();
- return;
- end
-
- if (weaponskillHandlers[command.actorId] ~= nil) then
- weaponskillHandlers[command.actorId](player);
- else
- player:SendMessage(0x20, "", "That weaponskill is not implemented yet.");
- end
-
+ player.WeaponSkill(command.actorId);
player:endEvent();
end
\ No newline at end of file
diff --git a/data/scripts/commands/gm/effect.lua b/data/scripts/commands/gm/effect.lua
new file mode 100644
index 00000000..95157072
--- /dev/null
+++ b/data/scripts/commands/gm/effect.lua
@@ -0,0 +1,31 @@
+require("global");
+require("bit32");
+
+properties = {
+ permissions = 0,
+ parameters = "iiii",
+ description =
+[[
+effect
+]],
+}
+
+function onTrigger(player, argc, effectId, magnitude, tick, duration)
+ local messageId = MESSAGE_TYPE_SYSTEM_ERROR;
+ local sender = "effect";
+
+ if player then
+ player.AddHP(100000);
+ player.DelHP(500);
+
+ effectId = tonumber(effectId) or 223180;
+ magnitude = tonumber(magnitude) or 300;
+ tick = tonumber(tick) or 3;
+ duration = tonumber(duration) or 60;
+
+ while player.statusEffects.HasStatusEffect(effectId) do
+ player.statusEffects.RemoveStatusEffect(effectId);
+ end;
+ player.statusEffects.AddStatusEffect(effectId, magnitude, tick, duration);
+ end;
+end;
\ No newline at end of file
diff --git a/data/scripts/effects/regen.lua b/data/scripts/effects/regen.lua
new file mode 100644
index 00000000..b3b1f463
--- /dev/null
+++ b/data/scripts/effects/regen.lua
@@ -0,0 +1,30 @@
+require("global")
+messageId = MESSAGE_TYPE_SYSTEM_ERROR;
+sender = "regen";
+
+function onGain(target, effect)
+ messageId = MESSAGE_TYPE_SYSTEM_ERROR;
+ sender = "regen";
+
+ target.SendMessage(messageId, sender, "dicks");
+end;
+
+function onTick(target, effect)
+ messageId = MESSAGE_TYPE_SYSTEM_ERROR;
+ sender = "regen";
+
+ local ability = GetWorldManager().GetAbility(27346);
+ local anim = bit32.bxor(bit32.lshift(ability.animationType, 24), bit32.lshift(tonumber(1), 12) , 101);
+ local addHp = effect.GetMagnitude();
+
+ target.AddHP(addHp);
+ target.SendBattleActionX01Packet(anim, 101, 0, 0, addHp);
+ target.SendMessage(messageId, sender, string.format("ate %u dicks", addHp));
+end;
+
+function onLose(target, effect)
+ messageId = MESSAGE_TYPE_SYSTEM_ERROR;
+ sender = "regen";
+
+ target.SendMessage(messageId, sender, "dicks gon");
+end;
\ No newline at end of file
diff --git a/sql/abilities.sql b/sql/abilities.sql
index f60b4703..e729ad2d 100644
--- a/sql/abilities.sql
+++ b/sql/abilities.sql
@@ -82,7 +82,7 @@ INSERT INTO `abilities` VALUES (27142,'rampart',3,2,3,1,0,1,0,2,5,0,60,0,0,120,0
INSERT INTO `abilities` VALUES (27143,'tempered_will',3,42,8,1,0,1,0,0,5,0,20,0,0,180,0,0,14,515,2,2);
INSERT INTO `abilities` VALUES (27144,'outmaneuver',3,34,8,1,0,1,0,0,5,0,30,0,0,90,0,0,14,512,21,2);
INSERT INTO `abilities` VALUES (27145,'flash',3,14,3,32,0,1,0,0,5,0,0,0,0,30,0,0,14,696,2,2);
-INSERT INTO `abilities` VALUES (27146,'cover',16,30,0,0,0,1,0,0,5,0,15,0,0,60,0,0,14,1,2,2);
+INSERT INTO `abilities` VALUES (27146,'cover',16,30,0,0,0,1,0,0,5,0,15,0,0,60,0,0,14,725,2,2);
INSERT INTO `abilities` VALUES (27147,'divine_veil',16,35,0,0,0,1,0,0,5,0,20,0,0,60,0,0,14,713,2,2);
INSERT INTO `abilities` VALUES (27148,'hallowed_ground',16,50,0,0,0,1,0,0,5,0,0,0,0,900,0,0,14,709,2,2);
INSERT INTO `abilities` VALUES (27149,'holy_succor',16,40,0,0,0,1,0,0,15,0,0,0,2,10,100,0,1,701,1,2);