mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-04-23 21:27:46 +00:00
added ion's and showmo's enums
- added nullable DateTime param to UnixTimeStampUTC
This commit is contained in:
parent
1ae15df64c
commit
c070f5b80e
10 changed files with 188 additions and 19 deletions
|
@ -84,10 +84,10 @@ namespace FFXIVClassic.Common
|
||||||
return sb.ToString().TrimEnd(Environment.NewLine.ToCharArray());
|
return sb.ToString().TrimEnd(Environment.NewLine.ToCharArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static uint UnixTimeStampUTC()
|
public static uint UnixTimeStampUTC(DateTime? time = null)
|
||||||
{
|
{
|
||||||
uint unixTimeStamp;
|
uint unixTimeStamp;
|
||||||
var currentTime = DateTime.Now;
|
var currentTime = time ?? DateTime.Now;
|
||||||
var zuluTime = currentTime.ToUniversalTime();
|
var zuluTime = currentTime.ToUniversalTime();
|
||||||
var unixEpoch = new DateTime(1970, 1, 1);
|
var unixEpoch = new DateTime(1970, 1, 1);
|
||||||
unixTimeStamp = (uint)zuluTime.Subtract(unixEpoch).TotalSeconds;
|
unixTimeStamp = (uint)zuluTime.Subtract(unixEpoch).TotalSeconds;
|
||||||
|
@ -95,10 +95,10 @@ namespace FFXIVClassic.Common
|
||||||
return unixTimeStamp;
|
return unixTimeStamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong MilisUnixTimeStampUTC()
|
public static ulong MilisUnixTimeStampUTC(DateTime? time = null)
|
||||||
{
|
{
|
||||||
ulong unixTimeStamp;
|
ulong unixTimeStamp;
|
||||||
var currentTime = DateTime.Now;
|
var currentTime = time ?? DateTime.Now;
|
||||||
var zuluTime = currentTime.ToUniversalTime();
|
var zuluTime = currentTime.ToUniversalTime();
|
||||||
var unixEpoch = new DateTime(1970, 1, 1);
|
var unixEpoch = new DateTime(1970, 1, 1);
|
||||||
unixTimeStamp = (ulong)zuluTime.Subtract(unixEpoch).TotalMilliseconds;
|
unixTimeStamp = (ulong)zuluTime.Subtract(unixEpoch).TotalMilliseconds;
|
||||||
|
|
|
@ -98,7 +98,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
public ushort currentJob;
|
public ushort currentJob;
|
||||||
|
|
||||||
public Character(uint actorID) : base(actorID)
|
public Character(uint actorID) : base(actorID)
|
||||||
{
|
{
|
||||||
//Init timer array to "notimer"
|
//Init timer array to "notimer"
|
||||||
for (int i = 0; i < charaWork.statusShownTime.Length; i++)
|
for (int i = 0; i < charaWork.statusShownTime.Length; i++)
|
||||||
charaWork.statusShownTime[i] = 0xFFFFFFFF;
|
charaWork.statusShownTime[i] = 0xFFFFFFFF;
|
||||||
|
@ -109,7 +109,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
ResetMoveSpeeds();
|
ResetMoveSpeeds();
|
||||||
// todo: base this on equip and shit
|
// todo: base this on equip and shit
|
||||||
SetMod((uint)Modifier.AttackRange, 3);
|
SetMod((uint)Modifier.AttackRange, 3);
|
||||||
SetMod((uint)Modifier.AttackDelay, (Program.Random.Next(30,60) * 100));
|
SetMod((uint)Modifier.AttackDelay, (Program.Random.Next(30, 60) * 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SubPacket CreateAppearancePacket()
|
public SubPacket CreateAppearancePacket()
|
||||||
|
@ -120,7 +120,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
|
|
||||||
public SubPacket CreateInitStatusPacket()
|
public SubPacket CreateInitStatusPacket()
|
||||||
{
|
{
|
||||||
return (SetActorStatusAllPacket.BuildPacket(actorId, charaWork.status));
|
return (SetActorStatusAllPacket.BuildPacket(actorId, charaWork.status));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SubPacket CreateSetActorIconPacket()
|
public SubPacket CreateSetActorIconPacket()
|
||||||
|
@ -148,7 +148,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
currentContentGroup = group;
|
currentContentGroup = group;
|
||||||
|
|
||||||
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/currentContentGroup", this);
|
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/currentContentGroup", this);
|
||||||
propPacketUtil.AddProperty("charaWork.currentContentGroup");
|
propPacketUtil.AddProperty("charaWork.currentContentGroup");
|
||||||
zone.BroadcastPacketsAroundActor(this, propPacketUtil.Done());
|
zone.BroadcastPacketsAroundActor(this, propPacketUtil.Done());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayAnimation(uint animId, bool onlySelf = false)
|
public void PlayAnimation(uint animId, bool onlySelf = false)
|
||||||
{
|
{
|
||||||
if (onlySelf)
|
if (onlySelf)
|
||||||
{
|
{
|
||||||
if (this is Player)
|
if (this is Player)
|
||||||
|
@ -236,7 +236,7 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
if (updateFlags != ActorUpdateFlags.None)
|
if (updateFlags != ActorUpdateFlags.None)
|
||||||
{
|
{
|
||||||
packets = packets ?? new List<SubPacket>();
|
packets = packets ?? new List<SubPacket>();
|
||||||
|
|
||||||
if ((updateFlags & ActorUpdateFlags.Appearance) != 0)
|
if ((updateFlags & ActorUpdateFlags.Appearance) != 0)
|
||||||
{
|
{
|
||||||
packets.Add(new SetActorAppearancePacket(modelId, appearanceIds).BuildPacket(actorId));
|
packets.Add(new SetActorAppearancePacket(modelId, appearanceIds).BuildPacket(actorId));
|
||||||
|
@ -322,6 +322,11 @@ namespace FFXIVClassic_Map_Server.Actors
|
||||||
aiContainer.Cast(Server.GetWorldManager().GetActorInWorld(targetId == 0 ? currentTarget : targetId) as Character, spellId);
|
aiContainer.Cast(Server.GetWorldManager().GetActorInWorld(targetId == 0 ? currentTarget : targetId) as Character, spellId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Ability(uint abilityId, uint targetId = 0)
|
||||||
|
{
|
||||||
|
aiContainer.Ability(Server.GetWorldManager().GetActorInWorld(targetId == 0 ? currentTarget : targetId) as Character, abilityId);
|
||||||
|
}
|
||||||
|
|
||||||
public void WeaponSkill(uint skillId)
|
public void WeaponSkill(uint skillId)
|
||||||
{
|
{
|
||||||
aiContainer.WeaponSkill(Server.GetWorldManager().GetActorInWorld(currentTarget) as Character, skillId);
|
aiContainer.WeaponSkill(Server.GetWorldManager().GetActorInWorld(currentTarget) as Character, skillId);
|
||||||
|
|
|
@ -113,7 +113,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
|
|
||||||
public bool CanChangeState()
|
public bool CanChangeState()
|
||||||
{
|
{
|
||||||
return states.Count == 0 || states.Peek().CanInterrupt();
|
return GetCurrentState() == null || states.Peek().CanInterrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeTarget(Character target)
|
public void ChangeTarget(Character target)
|
||||||
|
@ -217,6 +217,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
InternalDisengage();
|
InternalDisengage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Ability(Character target, uint abilityId)
|
||||||
|
{
|
||||||
|
if (controller != null)
|
||||||
|
controller.Ability(target, abilityId);
|
||||||
|
else
|
||||||
|
InternalAbility(target, abilityId);
|
||||||
|
}
|
||||||
|
|
||||||
public void Cast(Character target, uint spellId)
|
public void Cast(Character target, uint spellId)
|
||||||
{
|
{
|
||||||
if (controller != null)
|
if (controller != null)
|
||||||
|
@ -293,6 +301,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
|
||||||
ClearStates();
|
ClearStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InternalAbility(Character target, uint abilityId)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void InternalCast(Character target, uint spellId)
|
public void InternalCast(Character target, uint spellId)
|
||||||
{
|
{
|
||||||
ChangeState(new MagicState(owner, target, (ushort)spellId));
|
ChangeState(new MagicState(owner, target, (ushort)spellId));
|
||||||
|
|
|
@ -68,7 +68,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
|
||||||
|
|
||||||
public override void Ability(Character target, uint abilityId)
|
public override void Ability(Character target, uint abilityId)
|
||||||
{
|
{
|
||||||
|
owner.aiContainer.InternalAbility(target, abilityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void RangedAttack(Character target)
|
public override void RangedAttack(Character target)
|
||||||
|
|
|
@ -18,9 +18,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||||
public AttackState(Character owner, Character target) :
|
public AttackState(Character owner, Character target) :
|
||||||
base(owner, target)
|
base(owner, target)
|
||||||
{
|
{
|
||||||
|
this.canInterrupt = true;
|
||||||
|
this.startTime = DateTime.Now;
|
||||||
|
|
||||||
owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE);
|
owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE);
|
||||||
owner.aiContainer.ChangeTarget(target);
|
owner.aiContainer.ChangeTarget(target);
|
||||||
this.startTime = DateTime.Now;
|
|
||||||
attackTime = startTime;
|
attackTime = startTime;
|
||||||
owner.aiContainer.pathFind?.Clear();
|
owner.aiContainer.pathFind?.Clear();
|
||||||
// todo: should handle everything here instead of on next tick..
|
// todo: should handle everything here instead of on next tick..
|
||||||
|
|
|
@ -8,6 +8,7 @@ using FFXIVClassic_Map_Server.Actors;
|
||||||
using FFXIVClassic_Map_Server.packets.send.actor;
|
using FFXIVClassic_Map_Server.packets.send.actor;
|
||||||
using FFXIVClassic_Map_Server.packets.send.actor.battle;
|
using FFXIVClassic_Map_Server.packets.send.actor.battle;
|
||||||
using FFXIVClassic_Map_Server.packets.send;
|
using FFXIVClassic_Map_Server.packets.send;
|
||||||
|
using FFXIVClassic_Map_Server.utils;
|
||||||
|
|
||||||
namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||||
{
|
{
|
||||||
|
@ -56,12 +57,21 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||||
// todo: check within attack range
|
// todo: check within attack range
|
||||||
startPos = owner.GetPosAsVector3();
|
startPos = owner.GetPosAsVector3();
|
||||||
owner.LookAt(target);
|
owner.LookAt(target);
|
||||||
|
float[] baseCastDuration = { 1.0f, 0.25f };
|
||||||
|
|
||||||
foreach (var player in owner.zone.GetActorsAroundActor<Player>(owner, 50))
|
float spellSpeed = spell.castTimeSeconds;
|
||||||
|
List<SubPacket> packets = new List<SubPacket>();
|
||||||
|
|
||||||
|
// command casting duration
|
||||||
|
if (owner.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER)
|
||||||
{
|
{
|
||||||
// todo: this is retarded, prolly doesnt do what i think its gonna do
|
// todo: modify spellSpeed based on modifiers and stuff
|
||||||
//player.QueuePacket(BattleActionX01Packet.BuildPacket(player.actorId, owner.actorId, target != null ? target.actorId : 0xC0000000, spell.battleAnimation, spell.effectAnimation, 0, spell.id, 0, (byte)spell.castTimeSeconds));
|
// ((Player)owner).SendStartCastBar(spell.id, Utils.UnixTimeStampUTC(DateTime.Now.AddSeconds(spellSpeed)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// todo: change
|
||||||
|
|
||||||
|
owner.zone.BroadcastPacketsAroundActor(owner, packets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,5 +170,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
|
||||||
{
|
{
|
||||||
return (Utils.DistanceSquared(owner.GetPosAsVector3(), startPos) > 4.0f);
|
return (Utils.DistanceSquared(owner.GetPosAsVector3(), startPos) > 4.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Cleanup()
|
||||||
|
{
|
||||||
|
// command casting duration
|
||||||
|
var packets = new List<SubPacket>();
|
||||||
|
if (owner.currentSubState == SetActorStatePacket.SUB_STATE_PLAYER)
|
||||||
|
{
|
||||||
|
// ((Player)owner).SendStartCastBar(0, 0);
|
||||||
|
}
|
||||||
|
owner.zone.BroadcastPacketsAroundActor(owner, packets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
using FFXIVClassic.Common;
|
using FFXIVClassic.Common;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
using FFXIVClassic.Common;
|
|
||||||
|
|
||||||
namespace FFXIVClassic_Map_Server.packets.send.actor
|
namespace FFXIVClassic_Map_Server.packets.send.actor
|
||||||
{
|
{
|
||||||
class SetActorStatePacket
|
class SetActorStatePacket
|
||||||
|
|
|
@ -6,10 +6,21 @@ using FFXIVClassic.Common;
|
||||||
namespace FFXIVClassic_Map_Server.packets.send.actor
|
namespace FFXIVClassic_Map_Server.packets.send.actor
|
||||||
{
|
{
|
||||||
class SetActorSubStatPacket
|
class SetActorSubStatPacket
|
||||||
{
|
{
|
||||||
public const ushort OPCODE = 0x144;
|
public const ushort OPCODE = 0x144;
|
||||||
public const uint PACKET_SIZE = 0x28;
|
public const uint PACKET_SIZE = 0x28;
|
||||||
|
|
||||||
|
enum SubStat : int
|
||||||
|
{
|
||||||
|
Breakage = 0x00, // (index goes high to low, bitflags)
|
||||||
|
Chant = 0x01, // [Nibbles: left / right hand = value]) (AKA SubStatObject)
|
||||||
|
Guard = 0x02, // [left / right hand = true] 0,1,2,3) ||| High byte also defines how many bools to use as flags for byte 0x4.
|
||||||
|
Waste = 0x03, // (High Nibble)
|
||||||
|
Mode = 0x04, // ???
|
||||||
|
Unknown = 0x05, // ???
|
||||||
|
SubStatMotionPack = 0x06,
|
||||||
|
Unknown2 = 0x07,
|
||||||
|
}
|
||||||
public static SubPacket BuildPacket(uint sourceActorId, byte breakage, int leftChant, int rightChant, int guard, int wasteStat, int statMode, uint idleAnimationId)
|
public static SubPacket BuildPacket(uint sourceActorId, byte breakage, int leftChant, int rightChant, int guard, int wasteStat, int statMode, uint idleAnimationId)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[PACKET_SIZE - 0x20];
|
byte[] data = new byte[PACKET_SIZE - 0x20];
|
||||||
|
|
|
@ -10,6 +10,89 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
|
||||||
Disengage = 12002,
|
Disengage = 12002,
|
||||||
Attack = 22104,
|
Attack = 22104,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//These flags can be stacked and mixed, but the client will prioritize certain flags over others.
|
||||||
|
[Flags]
|
||||||
|
public enum HitEffect : uint
|
||||||
|
{
|
||||||
|
//Not setting RecoilLv2 or RecoilLv3 results in the weaker RecoilLv1.
|
||||||
|
//These are the recoil animations that play on the target, ranging from weak to strong.
|
||||||
|
//The recoil that gets set was likely based on the percentage of HP lost from the attack.
|
||||||
|
RecoilLv1 = 0,
|
||||||
|
RecoilLv2 = 1 << 0,
|
||||||
|
RecoilLv3 = 1 << 1,
|
||||||
|
|
||||||
|
//Setting both recoil flags triggers the "Critical!" pop-up text and hit visual effect.
|
||||||
|
CriticalHit = RecoilLv2 | RecoilLv3,
|
||||||
|
|
||||||
|
//Hit visual and sound effects when connecting with the target.
|
||||||
|
//Mixing these flags together will yield different results.
|
||||||
|
//Each visual likely relates to a specific weapon.
|
||||||
|
//Ex: HitVisual4 flag alone appears to be the visual and sound effect for hand-to-hand attacks.
|
||||||
|
HitVisual1 = 1 << 2,
|
||||||
|
HitVisual2 = 1 << 3,
|
||||||
|
HitVisual3 = 1 << 4,
|
||||||
|
HitVisual4 = 1 << 5,
|
||||||
|
|
||||||
|
//An additional visual effect that plays on the target when attacked if:
|
||||||
|
//The attack is physical and they have the protect buff on.
|
||||||
|
//The attack is magical and they have the shell buff on.
|
||||||
|
//Special Note: Shell was removed in later versions of the game.
|
||||||
|
//Another effect plays when both Protect and Shell flags are activated.
|
||||||
|
//Not sure what this effect is.
|
||||||
|
//Random guess: if the attack was a hybrid of both physical and magical and the target had both Protect and Shell buffs applied.
|
||||||
|
Protect = 1 << 6,
|
||||||
|
Shell = 1 << 7,
|
||||||
|
ProtectShellSpecial = Protect | Shell,
|
||||||
|
|
||||||
|
//Unknown = 1 << 8, -- Not sure what this flag does.
|
||||||
|
|
||||||
|
//If only HitEffect1 is set out of the hit effects, the "Evade!" pop-up text triggers along with the evade visual.
|
||||||
|
//If no hit effects are set, the "Miss!" pop-up is triggered and no hit visual is played.
|
||||||
|
HitEffect1 = 1 << 9,
|
||||||
|
HitEffect2 = 1 << 10, //Plays the standard hit visual effect, but with no sound if used alone.
|
||||||
|
Hit = HitEffect1 | HitEffect2, //A standard hit effect with sound effect.
|
||||||
|
HitEffect3 = 1 << 11,
|
||||||
|
HitEffect4 = 1 << 12,
|
||||||
|
HitEffect5 = 1 << 13,
|
||||||
|
GustyHitEffect = HitEffect3 | HitEffect2,
|
||||||
|
GreenTintedHitEffect = HitEffect4 | HitEffect1,
|
||||||
|
|
||||||
|
//Knocks you back away from the attacker.
|
||||||
|
KnockbackLv1 = HitEffect4 | HitEffect2 | HitEffect1,
|
||||||
|
KnockbackLv2 = HitEffect4 | HitEffect3,
|
||||||
|
KnockbackLv3 = HitEffect4 | HitEffect3 | HitEffect1,
|
||||||
|
KnockbackLv4 = HitEffect4 | HitEffect3 | HitEffect2,
|
||||||
|
KnockbackLv5 = HitEffect4 | HitEffect3 | HitEffect2 | HitEffect1,
|
||||||
|
|
||||||
|
//Knocks you away from the attacker in a counter-clockwise direction.
|
||||||
|
KnockbackCounterClockwiseLv1 = HitEffect5,
|
||||||
|
KnockbackCounterClockwiseLv2 = HitEffect5 | HitEffect1,
|
||||||
|
|
||||||
|
//Knocks you away from the attacker in a clockwise direction.
|
||||||
|
KnockbackClockwiseLv1 = HitEffect5 | HitEffect2,
|
||||||
|
KnockbackClockwiseLv2 = HitEffect5 | HitEffect2 | HitEffect1,
|
||||||
|
|
||||||
|
//Completely drags target to the attacker, even across large distances.
|
||||||
|
DrawIn = HitEffect5 | HitEffect3,
|
||||||
|
|
||||||
|
//An additional visual effect that plays on the target based on according buff.
|
||||||
|
UnknownShieldEffect = HitEffect5 | HitEffect4,
|
||||||
|
Stoneskin = HitEffect5 | HitEffect4 | HitEffect1,
|
||||||
|
|
||||||
|
//Unknown = 1 << 14, -- Not sure what this flag does; might be another HitEffect.
|
||||||
|
|
||||||
|
//A special effect when performing appropriate skill combos in succession.
|
||||||
|
//Ex: Thunder (SkillCombo1 Effect) -> Thundara (SkillCombo2 Effect) -> Thundaga (SkillCombo3 Effect)
|
||||||
|
//Special Note: SkillCombo4 was never actually used in 1.0 since combos only chained up to 3 times maximum.
|
||||||
|
SkillCombo1 = 1 << 15,
|
||||||
|
SkillCombo2 = 1 << 16,
|
||||||
|
SkillCombo3 = SkillCombo1 | SkillCombo2,
|
||||||
|
SkillCombo4 = 1 << 17
|
||||||
|
|
||||||
|
//Flags beyond here are unknown/untested.
|
||||||
|
}
|
||||||
|
|
||||||
class BattleActionX01Packet
|
class BattleActionX01Packet
|
||||||
{
|
{
|
||||||
public const ushort OPCODE = 0x0139;
|
public const ushort OPCODE = 0x0139;
|
||||||
|
|
36
data/scripts/commands/Ability.lua
Normal file
36
data/scripts/commands/Ability.lua
Normal file
|
@ -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.Ability(command.actorId, targetActor);
|
||||||
|
player:endEvent();
|
||||||
|
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue