From 11bbb023d996c1749249895a1af98355c923dc6d Mon Sep 17 00:00:00 2001 From: Tahir Akhlaq Date: Fri, 25 Aug 2017 03:52:43 +0100 Subject: [PATCH] abilities now use correct animation id (<3 azia) - did stuff with magicstate/attackstate - fixed status effect tick - added regen status (todo: actually populate the table and use that name instead of enum's) - added baseStats to char (todo: add bonuses and stuff on top of those, set charaWork values to the calculated ones + bonus) --- FFXIVClassic Map Server/Database.cs | 1 + .../FFXIVClassic Map Server.csproj | 1 + FFXIVClassic Map Server/actors/Actor.cs | 2 +- .../actors/chara/Character.cs | 143 ++++++++++++++---- .../actors/chara/Modifier.cs | 16 +- .../actors/chara/ai/AIContainer.cs | 5 +- .../actors/chara/ai/Ability.cs | 15 +- .../actors/chara/ai/PathFind.cs | 6 +- .../actors/chara/ai/StatusEffect.cs | 4 +- .../actors/chara/ai/StatusEffectContainer.cs | 29 ++-- .../ai/controllers/BattleNpcController.cs | 12 +- .../chara/ai/controllers/PlayerController.cs | 2 +- .../actors/chara/ai/state/AttackState.cs | 9 +- .../actors/chara/ai/state/DeathState.cs | 2 +- .../actors/chara/ai/state/MagicState.cs | 133 +++++++++++++--- .../actors/chara/ai/utils/BattleUtils.cs | 6 + .../actors/chara/npc/BattleNpc.cs | 5 +- .../actors/chara/player/Player.cs | 46 +----- FFXIVClassic Map Server/lua/LuaEngine.cs | 34 ++++- data/scripts/commands/ActivateCommand.lua | 3 +- data/scripts/commands/AttackMagic.lua | 36 +++++ data/scripts/commands/AttackWeaponSkill.lua | 88 +---------- data/scripts/commands/gm/effect.lua | 31 ++++ data/scripts/effects/regen.lua | 30 ++++ sql/abilities.sql | 2 +- 25 files changed, 426 insertions(+), 235 deletions(-) create mode 100644 data/scripts/commands/AttackMagic.lua create mode 100644 data/scripts/commands/gm/effect.lua create mode 100644 data/scripts/effects/regen.lua 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);