diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index 8a0f3188..1014bfbe 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -964,7 +964,7 @@ namespace FFXIVClassic_Map_Server var effect = Server.GetWorldManager().GetStatusEffect(id); if (effect != null) { - effect.SetDurationMs(duration); + effect.SetDuration(duration); effect.SetMagnitude(magnitude); effect.SetTickMs(tick); effect.SetTier(tier); @@ -1276,8 +1276,7 @@ namespace FFXIVClassic_Map_Server } public static void EquipAbility(Player player, byte classId, ushort hotbarSlot, uint commandId, uint recastTime) { - //2700083201 is where abilities start. 2700083200 is for unequipping abilities. Trying to put this in the hotbar will crash the game, need to put 0 instead - if (commandId > 2700083200) + if (commandId > 0) { using (MySqlConnection conn = new MySqlConnection( String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", @@ -1321,7 +1320,7 @@ namespace FFXIVClassic_Map_Server UnequipAbility(player, hotbarSlot); } - //Unequipping is done by sending an equip packet with 2700083200 as the ability and the hotbar slot of the action being unequipped + //Unequipping is done by sending an equip packet with 0xA0F00000 as the ability and the hotbar slot of the action being unequipped public static void UnequipAbility(Player player, ushort hotbarSlot) { using (MySqlConnection conn = new MySqlConnection( @@ -1338,8 +1337,6 @@ namespace FFXIVClassic_Map_Server MySqlCommand cmd; string query = ""; - //Drop - List> hotbarList = new List>(); query = @" DELETE FROM characters_hotbar WHERE characterId = @charId AND classId = @classId AND hotbarSlot = @hotbarSlot @@ -1377,7 +1374,7 @@ namespace FFXIVClassic_Map_Server SELECT hotbarSlot, commandId, - recastTime + recastTime FROM characters_hotbar WHERE characterId = @charId AND classId = @classId ORDER BY hotbarSlot"; @@ -1385,7 +1382,7 @@ namespace FFXIVClassic_Map_Server cmd.Parameters.AddWithValue("@charId", player.actorId); cmd.Parameters.AddWithValue("@classId", player.GetCurrentClassOrJob()); - player.charaWork.commandBorder = 32; + player.charaWork.commandBorder = 32; using (MySqlDataReader reader = cmd.ExecuteReader()) { @@ -1393,13 +1390,13 @@ namespace FFXIVClassic_Map_Server { int hotbarSlot = reader.GetUInt16("hotbarSlot"); uint commandId = reader.GetUInt32("commandId"); - player.charaWork.command[hotbarSlot + player.charaWork.commandBorder] = commandId | 0xA0F00000; + player.charaWork.command[hotbarSlot + player.charaWork.commandBorder] = 0xA0F00000 | commandId; player.charaWork.commandCategory[hotbarSlot + player.charaWork.commandBorder] = 1; player.charaWork.parameterSave.commandSlot_recastTime[hotbarSlot] = reader.GetUInt32("recastTime"); //Recast timer BattleCommand ability = Server.GetWorldManager().GetBattleCommand((ushort)(commandId)); - player.charaWork.parameterTemp.maxCommandRecastTime[hotbarSlot] = (ushort) (ability != null ? ability.recastTimeSeconds : 1); + player.charaWork.parameterTemp.maxCommandRecastTime[hotbarSlot] = (ushort) (ability != null ? ability.maxRecastTimeSeconds : 1); } } } @@ -1448,7 +1445,7 @@ namespace FFXIVClassic_Map_Server while (reader.Read()) { if (slot != reader.GetUInt16("hotbarSlot")) - return slot; + break; slot++; } @@ -1550,7 +1547,7 @@ namespace FFXIVClassic_Map_Server string query = @" - INSERT INTO server_items + INSERT INTO server_items (itemId, quality, itemType, durability) VALUES (@itemId, @quality, @itemType, @durability); @@ -1804,7 +1801,7 @@ namespace FFXIVClassic_Map_Server conn.Open(); string query = @" - INSERT INTO server_linkshells + INSERT INTO server_linkshells (name, master, crest) VALUES (@lsName, @master, @crest) @@ -2160,7 +2157,7 @@ namespace FFXIVClassic_Map_Server SET chocoboAppearance=@chocoboAppearance WHERE - characterId = @characterId"; + characterId = @characterId"; cmd = new MySqlCommand(query, conn); cmd.Parameters.AddWithValue("@characterId", player.actorId); @@ -2189,7 +2186,7 @@ namespace FFXIVClassic_Map_Server { conn.Open(); - var query = @"SELECT id, name, flags, overwrite FROM server_statuseffects;"; + var query = @"SELECT id, name, flags, overwrite, tickMs FROM server_statuseffects;"; MySqlCommand cmd = new MySqlCommand(query, conn); @@ -2201,8 +2198,9 @@ namespace FFXIVClassic_Map_Server var name = reader.GetString("name"); var flags = reader.GetUInt32("flags"); var overwrite = reader.GetByte("overwrite"); - - var effect = new StatusEffect(id, name, flags, overwrite); + var tickMs = reader.GetUInt32("tickMs"); + var effect = new StatusEffect(id, name, flags, overwrite, tickMs); + lua.LuaEngine.LoadStatusEffectScript(effect); effects.Add(id, effect); } } @@ -2231,7 +2229,7 @@ namespace FFXIVClassic_Map_Server string queries = ""; foreach (var effect in player.statusEffects.GetStatusEffects()) { - var duration = effect.GetDurationMs() + effect.GetStartTime().Millisecond - Program.Tick.Millisecond; + var duration = effect.GetDuration() + effect.GetStartTime().Second - Program.Tick.Second; queries += Environment.NewLine + $"REPLACE INTO characters_statuseffect(characterId, statusId, magnitude, duration, tick, tier, extra) VALUES ({player.actorId}, {effect.GetStatusEffectId()}, {effect.GetMagnitude()}, {duration}, {effect.GetTickMs()}, {effect.GetTier()}, {effect.GetExtra()});"; } @@ -2259,10 +2257,11 @@ namespace FFXIVClassic_Map_Server { try { + int count = 0; conn.Open(); - var query = ("SELECT `id`, name, classJob, lvl, requirements, validTarget, aoeType, aoeRange, aoeTarget, numHits, positionBonus, procRequirement, `range`, buffDuration, debuffDuration, " + - "castType, castTime, recastTime, mpCost, tpCost, animationType, effectAnimation, modelAnimation, animationDuration, battleAnimation, validUser FROM server_battle_commands;"); + var query = ("SELECT `id`, name, classJob, lvl, requirements, mainTarget, validTarget, aoeType, aoeRange, aoeTarget, basePotency, numHits, positionBonus, procRequirement, `range`, statusId, statusDuration, statusChance, " + + "castType, castTime, recastTime, mpCost, tpCost, animationType, effectAnimation, modelAnimation, animationDuration, battleAnimation, validUser, comboId1, comboId2, comboStep, accuracyMod, worldMasterTextId FROM server_battle_commands;"); MySqlCommand cmd = new MySqlCommand(query, conn); @@ -2277,17 +2276,21 @@ namespace FFXIVClassic_Map_Server battleCommand.job = reader.GetByte("classJob"); battleCommand.level = reader.GetByte("lvl"); battleCommand.requirements = (BattleCommandRequirements)reader.GetUInt16("requirements"); + battleCommand.mainTarget = (ValidTarget)reader.GetByte("mainTarget"); battleCommand.validTarget = (ValidTarget)reader.GetByte("validTarget"); battleCommand.aoeType = (TargetFindAOEType)reader.GetByte("aoeType"); + battleCommand.basePotency = reader.GetUInt16("basePotency"); battleCommand.numHits = reader.GetByte("numHits"); battleCommand.positionBonus = (BattleCommandPositionBonus)reader.GetByte("positionBonus"); battleCommand.procRequirement = (BattleCommandProcRequirement)reader.GetByte("procRequirement"); battleCommand.range = reader.GetInt32("range"); - battleCommand.debuffDurationSeconds = reader.GetUInt32("debuffDuration"); - battleCommand.buffDurationSeconds = reader.GetUInt32("buffDuration"); + battleCommand.statusId = reader.GetUInt32("statusId"); + battleCommand.statusDuration = reader.GetUInt32("statusDuration"); + battleCommand.statusChance = reader.GetFloat("statusChance"); battleCommand.castType = reader.GetByte("castType"); - battleCommand.castTimeSeconds = reader.GetUInt32("castTime"); - battleCommand.recastTimeSeconds = reader.GetUInt32("recastTime"); + battleCommand.castTimeMs = reader.GetUInt32("castTime"); + battleCommand.maxRecastTimeSeconds = reader.GetUInt32("recastTime"); + battleCommand.recastTimeMs = battleCommand.maxRecastTimeSeconds * 1000; battleCommand.mpCost = reader.GetUInt16("mpCost"); battleCommand.tpCost = reader.GetUInt16("tpCost"); battleCommand.animationType = reader.GetByte("animationType"); @@ -2299,7 +2302,13 @@ namespace FFXIVClassic_Map_Server battleCommand.battleAnimation = reader.GetUInt32("battleAnimation"); battleCommand.validUser = (BattleCommandValidUser)reader.GetByte("validUser"); - + + battleCommand.comboNextCommandId[0] = reader.GetInt32("comboId1"); + battleCommand.comboNextCommandId[1] = reader.GetInt32("comboId2"); + battleCommand.comboStep = reader.GetInt16("comboStep"); + battleCommand.accuracyModifier = reader.GetFloat("accuracyMod"); + battleCommand.worldMasterTextId = reader.GetUInt16("worldMasterTextId"); + lua.LuaEngine.LoadBattleCommandScript(battleCommand, "weaponskill"); battleCommandDict.Add(id, battleCommand); Tuple tuple = Tuple.Create(battleCommand.job, battleCommand.level); @@ -2312,8 +2321,11 @@ namespace FFXIVClassic_Map_Server List list = new List() { id | 0xA0F00000 }; battleCommandIdByLevel.Add(tuple, list); } + count++; } } + + Program.Log.Info(String.Format("Loaded {0} battle commands.", count)); } catch (MySqlException e) { diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index e5f93603..7dbf36e9 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -95,6 +95,7 @@ + diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 2ab4afff..7f3d7684 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -1334,7 +1334,7 @@ namespace FFXIVClassic_Map_Server { Program.Tick = DateTime.Now; foreach (Zone zone in zoneList.Values) - { + { zone.Update(Program.Tick); } Program.LastTick = Program.Tick; diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index 4e397668..1291e742 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -364,11 +364,13 @@ namespace FFXIVClassic_Map_Server.Actors return classParams; } + //character's newMainState kind of messes with this public void ChangeState(ushort newState) { - //if (newState != currentMainState) + if (newState != currentMainState) { currentMainState = newState; + updateFlags |= (ActorUpdateFlags.State | ActorUpdateFlags.Position); } } @@ -403,20 +405,23 @@ namespace FFXIVClassic_Map_Server.Actors if (positionUpdates != null && positionUpdates.Count > 0) { var pos = positionUpdates[0]; - oldPositionX = positionX; - oldPositionY = positionY; - oldPositionZ = positionZ; - oldRotation = rotation; + if (pos != null) + { + oldPositionX = positionX; + oldPositionY = positionY; + oldPositionZ = positionZ; + oldRotation = rotation; - positionX = pos.X; - positionY = pos.Y; - positionZ = pos.Z; + positionX = pos.X; + positionY = pos.Y; + positionZ = pos.Z; - zone.UpdateActorPosition(this); - - //Program.Server.GetInstance().mLuaEngine.OnPath(actor, position, positionUpdates) + zone.UpdateActorPosition(this); + //Program.Server.GetInstance().mLuaEngine.OnPath(actor, position, positionUpdates) + } positionUpdates.Remove(pos); + } packets.Add(CreatePositionUpdatePacket()); } diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index 68db3467..d6b2d35f 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -141,29 +141,30 @@ namespace FFXIVClassic_Map_Server.Actors public void RemoveActorFromZone(Actor actor) { - lock (mActorList) - { - mActorList.Remove(actor.actorId); + if (actor != null) + lock (mActorList) + { + mActorList.Remove(actor.actorId); - int gridX = (int)actor.positionX / boundingGridSize; - int gridY = (int)actor.positionZ / boundingGridSize; + int gridX = (int)actor.positionX / boundingGridSize; + int gridY = (int)actor.positionZ / boundingGridSize; - gridX += halfWidth; - gridY += halfHeight; + gridX += halfWidth; + gridY += halfHeight; - //Boundries - if (gridX < 0) - gridX = 0; - if (gridX >= numXBlocks) - gridX = numXBlocks - 1; - if (gridY < 0) - gridY = 0; - if (gridY >= numYBlocks) - gridY = numYBlocks - 1; + //Boundries + if (gridX < 0) + gridX = 0; + if (gridX >= numXBlocks) + gridX = numXBlocks - 1; + if (gridY < 0) + gridY = 0; + if (gridY >= numYBlocks) + gridY = numYBlocks - 1; - lock (mActorBlock) - mActorBlock[gridX, gridY].Remove(actor); - } + lock (mActorBlock) + mActorBlock[gridX, gridY].Remove(actor); + } } public void UpdateActorPosition(Actor actor) @@ -487,7 +488,6 @@ namespace FFXIVClassic_Map_Server.Actors return null; uint zoneId; - if (this is PrivateArea) zoneId = ((PrivateArea)this).GetParentZone().actorId; else @@ -500,8 +500,9 @@ namespace FFXIVClassic_Map_Server.Actors npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null); npc.LoadEventConditions(actorClass.eventConditions); - //npc.SetMaxHP(3000); - //npc.SetHP(3000); + npc.SetMaxHP(30000); + npc.SetHP(30000); + npc.ResetMoveSpeeds(); AddActorToZone(npc); @@ -668,7 +669,7 @@ namespace FFXIVClassic_Map_Server.Actors lock (mActorList) { foreach (Actor a in mActorList.Values.ToList()) - a.Update(tick); + a.Update(tick); if ((tick - lastUpdateScript).TotalMilliseconds > 1500) { diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 098392c2..831c1316 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -125,13 +125,18 @@ namespace FFXIVClassic_Map_Server.Actors public ushort newMainState; public float spawnX, spawnY, spawnZ; + //I needed some values I could reuse for random stuff, delete later + public int extraInt; + public uint extraUint; + public float extraFloat; + protected Dictionary tempVars = new Dictionary(); public Character(uint actorID) : base(actorID) { //Init timer array to "notimer" for (int i = 0; i < charaWork.statusShownTime.Length; i++) - charaWork.statusShownTime[i] = 0xFFFFFFFF; + charaWork.statusShownTime[i] = 0; this.statusEffects = new StatusEffectContainer(this); @@ -192,9 +197,12 @@ namespace FFXIVClassic_Map_Server.Actors var i = 0; foreach (var effect in statusEffects.GetStatusEffects()) { - propPacketUtil.AddProperty($"charaWork.statusShownTime[{i}]"); - propPacketUtil.AddProperty(String.Format("charaWork.statusShownTime[{0}]", i)); - i++; + if (!effect.GetHidden()) + { + propPacketUtil.AddProperty($"charaWork.statusShownTime[{i}]"); + propPacketUtil.AddProperty(String.Format("charaWork.statusShownTime[{0}]", i)); + i++; + } } return propPacketUtil.Done(); } @@ -230,6 +238,7 @@ namespace FFXIVClassic_Map_Server.Actors { int currentIndex = 0; //AoE abilities only ever hit 16 people, so we probably won't need this loop anymore + //Apparently aoe are limited to 8? while (true) { if (actions.Length - currentIndex >= 10) @@ -255,7 +264,7 @@ namespace FFXIVClassic_Map_Server.Actors while (true) { - if (actions.Count - currentIndex >= 18) + if (actions.Count - currentIndex >= 10) zone.BroadcastPacketAroundActor(this, BattleActionX18Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex)); else if (actions.Count - currentIndex > 1) zone.BroadcastPacketAroundActor(this, BattleActionX10Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex)); @@ -586,12 +595,15 @@ namespace FFXIVClassic_Map_Server.Actors public void AddTP(int tp) { - charaWork.parameterTemp.tp = (short)((charaWork.parameterTemp.tp + tp).Clamp(0, 3000)); - tpBase = (ushort) charaWork.parameterTemp.tp; - updateFlags |= ActorUpdateFlags.HpTpMp; + if (IsAlive()) + { + charaWork.parameterTemp.tp = (short)((charaWork.parameterTemp.tp + tp).Clamp(0, 3000)); + tpBase = (ushort)charaWork.parameterTemp.tp; + updateFlags |= ActorUpdateFlags.HpTpMp; - if (tpBase >= 1000) - lua.LuaEngine.GetInstance().OnSignal("tpOver1000"); + if (tpBase >= 1000) + lua.LuaEngine.GetInstance().OnSignal("tpOver1000"); + } } public void DelHP(int hp) @@ -609,14 +621,9 @@ namespace FFXIVClassic_Map_Server.Actors AddTP(-tp); } - public void CalculateBaseStats() + virtual public void CalculateBaseStats() { // todo: apply mods and shit here, get race/level/job and shit - - } - - public void RecalculateStats() - { uint hpMod = (uint) GetMod((uint)Modifier.Hp); if (hpMod != 0) { @@ -644,6 +651,14 @@ namespace FFXIVClassic_Map_Server.Actors } // todo: recalculate stats and crap updateFlags |= ActorUpdateFlags.HpTpMp; + + + SetMod((uint)Modifier.HitCount, 1); + } + + public void RecalculateStats() + { + //CalculateBaseStats(); } public void SetStat(uint statId, uint val) @@ -664,57 +679,65 @@ namespace FFXIVClassic_Map_Server.Actors public virtual void OnAttack(State state, BattleAction action, ref BattleAction error) { - // todo: change animation based on equipped weapon - action.effectId |= (uint)HitEffect.HitVisual1; // melee - var target = state.GetTarget(); + // todo: change animation based on equipped weapon // todo: get hitrate and shit, handle protect effect and whatever if (BattleUtils.TryAttack(this, target, action, ref error)) { - action.amount = BattleUtils.CalculateAttackDamage(this, target, action); //var packet = BattleActionX01Packet.BuildPacket(owner.actorId, owner.actorId, target.actorId, (uint)0x19001000, (uint)0x8000604, (ushort)0x765D, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, (byte)0x1); } // todo: call onAttack/onDamageTaken BattleUtils.DamageTarget(this, target, action, DamageTakenType.Attack); - AddTP(115); + AddTP(200); target.AddTP(100); } - public virtual void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors) + public virtual void OnCast(State state, BattleAction[] actions, BattleCommand spell, ref BattleAction[] errors) { - var spell = ((MagicState)state).GetSpell(); // damage is handled in script - var spellCost = spell.CalculateCost((uint)this.GetLevel()); + var spellCost = spell.CalculateMpCost(this); this.DelMP(spellCost); // mpCost can be set in script e.g. if caster has something for free spells foreach (BattleAction action in actions) + { if (zone.FindActorInArea(action.targetId) is Character chara) - BattleUtils.DamageTarget(this, chara, action, DamageTakenType.Magic); - + { + //BattleUtils.HandleHitType(this, chara, action); + BattleUtils.DoAction(this, chara, action, DamageTakenType.Magic); + } + } lua.LuaEngine.GetInstance().OnSignal("spellUsed"); } - public virtual void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors) + public virtual void OnWeaponSkill(State state, BattleAction[] actions, BattleCommand skill, ref BattleAction[] errors) { - var skill = ((WeaponSkillState)state).GetWeaponSkill(); // damage is handled in script - this.DelTP(skill.tpCost); foreach (BattleAction action in actions) + { + //Should we just store the character insteado f having to find it again? if (zone.FindActorInArea(action.targetId) is Character chara) - BattleUtils.DamageTarget(this, chara, action, DamageTakenType.Weaponskill); + { + BattleUtils.DoAction(this, chara, action, DamageTakenType.Weaponskill); + } + } + this.DelTP(skill.tpCost); + //Do procs reset on weaponskills? + ResetProcs(); lua.LuaEngine.GetInstance().OnSignal("weaponskillUsed"); } - public virtual void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors) + public virtual void OnAbility(State state, BattleAction[] actions, BattleCommand ability, ref BattleAction[] errors) { - if (target is BattleNpc) - ((BattleNpc)target).lastAttacker = this; - foreach (var action in actions) - zone.FindActorInArea(action.targetId)?.OnDamageTaken(this, action, DamageTakenType.Ability); + { + if (zone.FindActorInArea(action.targetId) is Character chara) + { + BattleUtils.DoAction(this, chara, action, DamageTakenType.Ability); + } + } } public virtual void OnSpawn() @@ -810,6 +833,97 @@ namespace FFXIVClassic_Map_Server.Actors } #endregion lua helpers #endregion ai stuff - } -} + //Reset procs. Only send packet if any procs were actually reset. + //This assumes you can't use weaponskills between getting a proc and using the procced ability + public void ResetProcs() + { + var propPacketUtil = new ActorPropertyPacketUtil("charaWork/timingCommand", this); + bool shouldSend = false; + for (int i = 0; i < 4; i++) + { + if (charaWork.battleTemp.timingCommandFlag[i]) + { + shouldSend = true; + charaWork.battleTemp.timingCommandFlag[i] = false; + propPacketUtil.AddProperty($"charaWork.battleTemp.timingCommandFlag[{i}]"); + } + } + + if (shouldSend && this is Player player) + player.QueuePackets(propPacketUtil.Done()); + } + + //Set given proc to true and send packet if this is a player + // todo: hidden status effects for timing when the procs fall off + public void SetProc(int procId, bool val = true) + { + charaWork.battleTemp.timingCommandFlag[procId] = val; + uint effectId = (uint)StatusEffectId.EvadeProc + (uint)procId; + + //If a proc just occurred, add a hidden effect effect + if (val) + { + StatusEffect procEffect = Server.GetWorldManager().GetStatusEffect(effectId); + procEffect.SetDuration(5000); + statusEffects.AddStatusEffect(procEffect, this, true, true); + + string procFunc = ""; + + //is there any reason we need this + switch(procId) + { + case (0): + procFunc = "onEvade"; + break; + case (1): + procFunc = "onBlock"; + break; + case (2): + procFunc = "onParry"; + break; + case (3): + procFunc = "onMiss"; + break; + } + + //lua.LuaEngine.CallLuaBattleFunction(this, procFunc, this); + } + //Otherwise we're reseting a proc, remove the status + else + { + statusEffects.RemoveStatusEffect(statusEffects.GetStatusEffectById((uint)effectId)); + } + + if (this is Player player) + { + var propPacketUtil = new ActorPropertyPacketUtil("charaWork/timingCommand", this); + propPacketUtil.AddProperty($"charaWork.battleTemp.timingCommandFlag[{procId}]"); + player.QueuePackets(propPacketUtil.Done()); + } + } + + public HitDirection GetHitDirection(Actor target) + { + //Get between taget's position and our position + double angle = Vector3.GetAngle(target.GetPosAsVector3(), GetPosAsVector3()); + //Add to the target's rotation, mod by 2pi. This is the angle relative to where the target is looking + //Actor's rotation is 0 degrees on their left side, rotate it by 45 degrees so that quadrants line up with sides + angle = (angle + target.rotation - (.25 * Math.PI)) % (2 * Math.PI); + //Make positive + if (angle < 0) + angle = angle + (2 * Math.PI); + + //Get the side we're on. 0 is front, 1 is right, 2 is rear, 3 is left + var side = (int) (angle / (.5 * Math.PI)) % 4; + + return (HitDirection) (1 << side); + } + + //Called when this character evades attacker's action + public void OnEvade(Character attacker, BattleAction action) + { + } + + } +} \ No newline at end of file diff --git a/FFXIVClassic Map Server/actors/chara/Modifier.cs b/FFXIVClassic Map Server/actors/chara/Modifier.cs index c58b3266..51a45a01 100644 --- a/FFXIVClassic Map Server/actors/chara/Modifier.cs +++ b/FFXIVClassic Map Server/actors/chara/Modifier.cs @@ -8,51 +8,58 @@ namespace FFXIVClassic_Map_Server.actors.chara { enum Modifier : UInt32 { - None = 0, - Hp = 1, - HpPercent = 2, - Mp = 3, - MpPercent = 4, - Tp = 5, - TpPercent = 6, - Regen = 7, - Refresh = 8, - Strength = 9, - Vitality = 10, - Dexterity = 11, - Intelligence = 12, - Mind = 13, - Piety = 14, - Attack = 15, - Accuracy = 16, - Defense = 17, - Evasion = 18, - MagicAttack = 19, - MagicHeal = 20, // is this needed? shouldnt it just be calc'd from mind - MagicAccuracy = 21, - MagicEvasion = 22, - MagicDefense = 23, - MagicEnhancePotency = 24, - MagicEnfeeblingPotency = 25, - ResistFire = 26, - ResistIce = 27, - ResistWind = 28, - ResistLightning = 29, - ResistEarth = 30, - ResistWater = 31, // <3 u jorge - AttackRange = 32, - Speed = 33, - AttackDelay = 34, - CraftProcessing = 35, - CraftMagicProcessing = 36, - CraftProcessControl = 37, + None = 0, + Hp = 1, + HpPercent = 2, + Mp = 3, + MpPercent = 4, + Tp = 5, + TpPercent = 6, + Regen = 7, + Refresh = 8, + Strength = 9, + Vitality = 10, + Dexterity = 11, + Intelligence = 12, + Mind = 13, + Piety = 14, + Attack = 15, + Accuracy = 16, + Defense = 17, + Evasion = 18, + MagicAttack = 19, + MagicHeal = 20, // is this needed? shouldnt it just be calc'd from mind + MagicAccuracy = 21, + MagicEvasion = 22, + MagicDefense = 23, + MagicEnhancePotency = 24, + MagicEnfeeblingPotency = 25, + ResistFire = 26, + ResistIce = 27, + ResistWind = 28, + ResistLightning = 29, + ResistEarth = 30, + ResistWater = 31, // <3 u jorge + AttackRange = 32, + Speed = 33, + AttackDelay = 34, - HarvestPotency = 38, - HarvestLimit = 39, - HarvestRate = 40, + CraftProcessing = 35, + CraftMagicProcessing = 36, + CraftProcessControl = 37, - Raise = 41, - MinimumHpLock = 42, // hp cannot fall below this value + HarvestPotency = 38, + HarvestLimit = 39, + HarvestRate = 40, + + Raise = 41, + MinimumHpLock = 42, // hp cannot fall below this value + AttackType = 43, // slashing, piercing, etc + BlockRate = 44, + Block = 45, + CritRating = 46, + HasShield = 47, // Need this because shields are required for blocks. Could have used BlockRate or Block but BlockRate is provided by Gallant Sollerets and Block is provided by some buffs. + HitCount = 48 // Amount of hits in an auto attack. Usually 1, 2 for h2h, 3 with spinning heel } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs index 86a211ce..38c636a1 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/AIContainer.cs @@ -35,9 +35,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai actionQueue = new ActionQueue(owner); } - public void UpdateLastActionTime() + public void UpdateLastActionTime(uint delay = 0) { - lastActionTime = DateTime.Now; + lastActionTime = DateTime.Now.AddSeconds(delay); } public DateTime GetLastActionTime() @@ -62,6 +62,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai controller.Update(tick); State top; + while (states.Count > 0 && (top = states.Peek()).Update(tick)) { if (top == GetCurrentState()) @@ -330,7 +331,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai { if (CanChangeState()) { - + ChangeState(new AbilityState(owner, target, (ushort)abilityId)); } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs b/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs index dc04bb46..dc00a505 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs @@ -8,6 +8,7 @@ using FFXIVClassic_Map_Server.actors.chara.player; using FFXIVClassic.Common; using FFXIVClassic_Map_Server.packets.send.actor; using FFXIVClassic_Map_Server.actors.chara.ai.utils; +using MoonSharp.Interpreter; namespace FFXIVClassic_Map_Server.actors.chara.ai { @@ -38,10 +39,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public enum BattleCommandProcRequirement : byte { None, - Evade = 0x01, - Block = 0x02, - Parry = 0x04, - Miss = 0x08 + Miss, + Evade, + Parry, + Block } public enum BattleCommandValidUser : byte @@ -51,6 +52,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai Monster } + public enum BattleCommandCastType : ushort + { + None, + Weaponskill = 1, + Weaponskill2 = 2, + BlackMagic = 3, + WhiteMagic = 4, + SongMagic = 8 + } + class BattleCommand { public ushort id; @@ -58,28 +69,41 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public byte job; public byte level; public BattleCommandRequirements requirements; - public ValidTarget validTarget; - public TargetFindAOEType aoeType; - public TargetFindAOETarget aoeTarget; - public byte numHits; - public BattleCommandPositionBonus positionBonus; - public BattleCommandProcRequirement procRequirement; - public int range; - public uint debuffDurationSeconds; - public uint buffDurationSeconds; - public byte castType; - public uint castTimeSeconds; - public uint recastTimeSeconds; + public ValidTarget mainTarget; //what the skill has to be used on. ie self for flare, enemy for ring of talons even though both are self-centere aoe + public ValidTarget validTarget; //what type of character the skill can hit + public TargetFindAOEType aoeType; //shape of aoe + public TargetFindAOETarget aoeTarget; //where the center of the aoe is (target/self) + public byte numHits; //amount of hits in the skill + public BattleCommandPositionBonus positionBonus; //bonus for front/flank/rear + public BattleCommandProcRequirement procRequirement;//if the skill requires a block/parry/evade before using + public int range; //max distance to use skill + + public uint statusId; //id of statuseffect that the skill might inflict + public uint statusDuration; //duration of statuseffect in milliseconds + public float statusChance; //percent chance of status landing, 0-1.0. Usually 1.0 for buffs + public byte castType; //casting animation, 2 for blm, 3 for whm, 8 for brd + public uint castTimeMs; //cast time in milliseconds + public uint recastTimeMs; //recast time in milliseconds + public uint maxRecastTimeSeconds; //maximum recast time in seconds public ushort mpCost; public ushort tpCost; public byte animationType; public ushort effectAnimation; public ushort modelAnimation; public ushort animationDurationSeconds; - public uint battleAnimation; public ushort worldMasterTextId; - public int aoeRange; + public int aoeRange; //size of aoe effect. (how will this work for box aoes?) + public int[] comboNextCommandId = new int[2]; //next two skills in a combo + public short comboStep; //Where in a combo string this skill is + + + public byte statusTier; //tief of status to put on target + public ushort basePotency; //damage variable + public float enmityModifier; //multiples by damage done to get final enmity + public float accuracyModifier; //modifies accuracy + public bool isCombo; + public lua.LuaScript script; //cached script public TargetFind targetFind; public BattleCommandValidUser validUser; @@ -89,6 +113,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.id = id; this.name = name; this.range = 0; + this.enmityModifier = 1; + this.accuracyModifier = 1; + this.statusTier = 1; + this.statusChance = 50; + this.recastTimeMs = (uint) maxRecastTimeSeconds * 1000; + this.isCombo = false; } public BattleCommand Clone() @@ -96,17 +126,31 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return (BattleCommand)MemberwiseClone(); } + public int CallLuaFunction(Character chara, string functionName, params object[] args) + { + if (script != null && !script.Globals.Get(functionName).IsNil()) + { + DynValue res = new DynValue(); + res = script.Call(script.Globals.Get(functionName), args); + if (res != null) + return (int)res.Number; + } + + return -1; + } + public bool IsSpell() { - return mpCost != 0 || castTimeSeconds != 0; + return mpCost != 0 || castTimeMs != 0; } public bool IsInstantCast() { - return castTimeSeconds == 0; + return castTimeMs == 0; } - public bool IsValidTarget(Character user, Character target) + //Checks whether the skill can be used on the given target + public bool IsValidMainTarget(Character user, Character target) { targetFind = new TargetFind(user); @@ -122,7 +166,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai /* worldMasterTextId - 32512 cannot be performed on a KO'd target. + 32512 cannot be performed on a KO'd target. 32513 can only be performed on a KO'd target. 32514 cannot be performed on yourself. 32515 can only be performed on yourself. @@ -130,24 +174,36 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai 32517 can only be performed on a friendly target. 32518 cannot be performed on an enemy. 32519 can only be performed on an enemy, + 32556 unable to execute [weaponskill]. Conditions for use are not met. */ // cant target dead - if ((validTarget & (ValidTarget.Corpse | ValidTarget.CorpseOnly)) == 0 && target.IsDead()) + if ((mainTarget & (ValidTarget.Corpse | ValidTarget.CorpseOnly)) == 0 && target.IsDead()) { // cannot be perfomed on if (user is Player) ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32512, 0x20, (uint)id); return false; } - if (level > user.charaWork.parameterSave.state_mainSkillLevel) + + //level too high + if (level > user.GetLevel()) { if (user is Player) ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32527, 0x20, (uint)id); return false; } - if (tpCost > user.GetTP()) + //Proc requirement + if (procRequirement != BattleCommandProcRequirement.None && !user.charaWork.battleTemp.timingCommandFlag[(int) procRequirement - 1]) + { + if (user is Player) + ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32556, 0x20, (uint)id); + return false; + } + + //costs too much tp + if (CalculateTpCost(user) > user.GetTP()) { if (user is Player) ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32546, 0x20, (uint)id); @@ -155,7 +211,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai } // todo: calculate cost based on modifiers also (probably in BattleUtils) - if (BattleUtils.CalculateSpellCost(user, target, this) > user.GetMP()) { if (user is Player) @@ -175,8 +230,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai } } + // todo: i dont care to message for each scenario, just the most common ones.. - if ((validTarget & ValidTarget.CorpseOnly) != 0) + if ((mainTarget & ValidTarget.CorpseOnly) != 0) { if (target != null && target.IsAlive()) { @@ -186,7 +242,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai } } - if ((validTarget & ValidTarget.Enemy) != 0) + if ((mainTarget & ValidTarget.Enemy) != 0) { if (target == user || target != null && user.allegiance == target.allegiance) @@ -197,21 +253,43 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai } } - if ((validTarget & (ValidTarget.PartyMember | ValidTarget.Player | ValidTarget.Ally)) != 0) + if ((mainTarget & ValidTarget.Ally) != 0) { if (target == null || target.allegiance != user.allegiance) { if (user is Player) - ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32516, 0x20, (uint)id); + ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32517, 0x20, (uint)id); return false; } } - return targetFind.CanTarget(target, true, true, true); + + if ((mainTarget & ValidTarget.PartyMember) != 0) + { + if (target == null || target.currentParty != user.currentParty) + { + if (user is Player) + ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32547, 0x20, (uint)id); + return false; + } + } + + if ((mainTarget & ValidTarget.Player) != 0) + { + if (!(target is Player)) + { + if (user is Player) + ((Player)user).SendGameMessage(Server.GetWorldManager().GetActor(), 32517, 0x20, (uint)id); + return false; + } + } + + return true;// targetFind.CanTarget(target, true, true, true); //this will be done later } - public ushort CalculateCost(uint level) + public ushort CalculateMpCost(Character user) { // todo: use precalculated costs instead + var level = user.GetLevel(); ushort cost = 0; if (level <= 10) cost = (ushort)(100 + level * 10); @@ -230,15 +308,59 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai else cost = (ushort)(8000 + (level - 70) * 500); - if (mpCost != 0) - return (ushort)Math.Ceiling((cost * mpCost * 0.001)); + //scale the mpcost by level + cost = (ushort)Math.Ceiling((cost * mpCost * 0.001)); - return mpCost != 0 ? (ushort)Math.Ceiling((cost * mpCost * 0.001)) : (ushort)0; + //if user is player, check if spell is a part of combo + if (user is Player) + { + var player = user as Player; + if (player.playerWork.comboNextCommandId[0] == id || player.playerWork.comboNextCommandId[1] == id) + cost = (ushort)Math.Ceiling(cost * (1 - player.playerWork.comboCostBonusRate)); + } + + return mpCost != 0 ? cost : (ushort)0; + } + + //Calculate TP cost taking into considerating the combo bonus rate for players + //Should this set tpCost or should it be called like CalculateMp where it gets calculated each time? + //Might cause issues with the delay between starting and finishing a WS + public ushort CalculateTpCost(Character user) + { + ushort tp = tpCost; + //Calculate tp cost + if (user is Player) + { + var player = user as Player; + if (player.playerWork.comboNextCommandId[0] == id || player.playerWork.comboNextCommandId[1] == id) + tp = (ushort)Math.Ceiling((float)tpCost * (1 - player.playerWork.comboCostBonusRate)); + } + + return tp; } public List GetTargets() { return targetFind?.GetTargets(); } + + //Handles setting the correct target for self-targeted spells + public Character GetMainTarget(Character caster, Character target) + { + //If skill can only be used on self + if (mainTarget == ValidTarget.Self) + return caster; + //If skill can only be used on party members and the target is not a party member + else if (((mainTarget & ValidTarget.PartyMember) != 0) && (target.currentParty != caster.currentParty)) + return caster; + //If skill can only be used on allys and the target is not an ally + else if (((mainTarget & ValidTarget.Ally) != 0) && (target.allegiance != caster.allegiance)) + return caster; + //If skill can only be used on players and the target is not a player + else if (((mainTarget & ValidTarget.Player) != 0) && (!(target is Player))) + return caster; + + return target; + } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs b/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs index 7ea9f9d7..34b64bf3 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using MoonSharp.Interpreter; namespace FFXIVClassic_Map_Server.actors.chara.ai { @@ -322,24 +323,50 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai // custom effects here + // status for having procs fall off + EvadeProc = 253003, + BlockProc = 253004, + ParryProc = 253005, + MissProc = 253006 } [Flags] enum StatusEffectFlags : uint { - None = 0x00, - Silent = 0x01, // dont display effect loss message - LoseOnDeath = 0x02, // effects removed on death - LoseOnZoning = 0x04, // effects removed on zoning - LoseOnEsuna = 0x08, // effects which can be removed with esuna (debuffs) - LoseOnDispel = 0x10, // some buffs which player might be able to dispel from mob - LoseOnLogout = 0x20, // effects removed on logging out - LoseOnAttacking = 0x40, // effects removed when owner attacks another entity - LoseOnCasting = 0x80, // effects removed when owner starts casting - LoseOnDamageTaken = 0x100, // effects removed when owner takes damage + None = 0, + Silent = 1 << 0, // dont display effect loss message - PreventAction = 0x200, // effects which prevent actions such as sleep/paralyze/petrify - Stealth = 0x400, // sneak/invis + //Loss flags + LoseOnDeath = 1 << 1, // effects removed on death + LoseOnZoning = 1 << 2, // effects removed on zoning + LoseOnEsuna = 1 << 3, // effects which can be removed with esuna (debuffs) + LoseOnDispel = 1 << 4, // some buffs which player might be able to dispel from mob + LoseOnLogout = 1 << 5, // effects removed on logging out + LoseOnAttacking = 1 << 6, // effects removed when owner attacks another entity + LoseOnCasting = 1 << 7, // effects removed when owner starts casting + LoseOnDamageTaken = 1 << 8, // effects removed when owner takes damage + LoseOnParry = 1 << 9, // effects removed when owner parries an attack (foresight) + LoseOnEvade = 1 << 10, // effects removed when owner evades an attack (decoy) + LoseOnCrit = 1 << 11, // effects removed when owner deals a critical hit (excruciate) + LoseOnAggro = 1 << 12, // effects removed when owner gains enmity (swiftsong) + + //Activate flags, do we need the LoseOn flags if we have these? could just remove itself in the activate function + ActivateOnAttack = 1 << 13, + ActivateOnSpell = 1 << 14, + ActivateOnDamageTaken = 1 << 15, + ActivateOnBlock = 1 << 16, + ActivateOnMiss = 1 << 17, + + //Prevent flags. Sleep/stun/petrify/etc combine these + PreventSpell= 1 << 18, // effects which prevent using spells, such as silence + PreventWeaponSkill = 1 << 19, // effects which prevent using weaponskills, such as pacification + PreventAbility = 1 << 20, // effects which prevent using abilities, such as amnesia + PreventAttack = 1 << 21, // effects which prevent basic attacks + PreventMovement = 1 << 22, // effects which prevent movement such as bind, still allows turning in place + PreventTurn = 1 << 23, // effects which prevent turning such as fixation, still allows movement and actions + + Stealth = 1 << 24, // sneak/invis + Stance = 1 << 25, // effects that do not have a timer } enum StatusEffectOverwrite : byte @@ -360,7 +387,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private string name; // name of this effect private DateTime startTime; // when was this effect added private DateTime lastTick; // when did this effect last tick - private uint durationMs; // how long should this effect last in ms + private uint duration; // how long should this effect last in seconds private uint tickMs; // how often should this effect proc private UInt64 magnitude; // a value specified by scripter which is guaranteed to be used by all effects private byte tier; // same effect with higher tier overwrites this @@ -368,17 +395,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private StatusEffectFlags flags; // death/erase/dispel etc private StatusEffectOverwrite overwrite; // how to handle adding an effect with same id (see StatusEfectOverwrite) private bool silent = false; // do i send a message on losing effect + private bool hidden = false; + public LuaScript script; HitEffect animationEffect; - public StatusEffect(Character owner, uint id, UInt64 magnitude, uint tickMs, uint durationMs, byte tier = 0) + public StatusEffect(Character owner, uint id, UInt64 magnitude, uint tickMs, uint duration, byte tier = 0) { this.owner = owner; this.source = owner; this.id = (StatusEffectId)id; this.magnitude = magnitude; this.tickMs = tickMs; - this.durationMs = durationMs; + this.duration = duration; this.tier = tier; this.startTime = DateTime.Now; @@ -392,7 +421,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.id = effect.id; this.magnitude = effect.magnitude; this.tickMs = effect.tickMs; - this.durationMs = effect.durationMs; + this.duration = effect.duration; this.tier = effect.tier; this.startTime = effect.startTime; this.lastTick = effect.lastTick; @@ -401,14 +430,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.flags = effect.flags; this.overwrite = effect.overwrite; this.extra = effect.extra; + this.script = effect.script; } - public StatusEffect(uint id, string name, uint flags, uint overwrite) + public StatusEffect(uint id, string name, uint flags, uint overwrite, uint tickMs) { this.id = (StatusEffectId)id; this.name = name; this.flags = (StatusEffectFlags)flags; this.overwrite = (StatusEffectOverwrite)overwrite; + this.tickMs = tickMs; } // return true when duration has elapsed @@ -420,13 +451,28 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai LuaEngine.CallLuaStatusEffectFunction(this.owner, this, "onTick", this.owner, this); } - if (durationMs != 0 && (tick - startTime).TotalMilliseconds >= durationMs) + if (duration != 0xFFFFFFFF && (tick - startTime).TotalSeconds >= duration) { return true; } return false; } + public int CallLuaFunction(Character chara, string functionName, params object[] args) + { + + 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; + } + public Character GetOwner() { return owner; @@ -457,9 +503,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return name; } - public uint GetDurationMs() + public uint GetDuration() { - return durationMs; + return duration; } public uint GetTickMs() @@ -497,6 +543,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return silent; } + public bool GetHidden() + { + return hidden; + } + public void SetStartTime(DateTime time) { this.startTime = time; @@ -523,9 +574,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.magnitude = magnitude; } - public void SetDurationMs(uint durationMs) + public void SetDuration(uint duration) { - this.durationMs = durationMs; + this.duration = duration; } public void SetTickMs(uint tickMs) @@ -558,6 +609,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.silent = silent; } + public void SetHidden(bool hidden) + { + this.hidden = hidden; + } + public void SetAnimation(uint hitEffect) { animationEffect = (HitEffect)hitEffect; diff --git a/FFXIVClassic Map Server/actors/chara/ai/StatusEffectContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/StatusEffectContainer.cs index db9f6596..ed23ba03 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/StatusEffectContainer.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/StatusEffectContainer.cs @@ -9,6 +9,7 @@ using FFXIVClassic_Map_Server.lua; using FFXIVClassic_Map_Server.actors.area; using FFXIVClassic_Map_Server.packets.send; using FFXIVClassic_Map_Server.packets.send.actor; +using FFXIVClassic_Map_Server.packets.send.actor.battle; using System.Collections.ObjectModel; using FFXIVClassic_Map_Server.utils; @@ -61,32 +62,47 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return effects.ContainsKey((uint)id); } - public bool AddStatusEffect(uint id, UInt64 magnitude, double tickMs, double durationMs, byte tier = 0) + public bool AddStatusEffect(uint id, UInt64 magnitude, uint tickMs, uint duration, byte tier = 0) { - return AddStatusEffect(new StatusEffect(this.owner, id, magnitude, (uint)(tickMs * 1000), (uint)(durationMs * 1000), tier), owner); + var se = Server.GetWorldManager().GetStatusEffect(id); + if (se != null) + { + se.SetDuration(duration); + se.SetStartTime(DateTime.Now); + se.SetOwner(owner); + } + return AddStatusEffect(se ?? new StatusEffect(this.owner, id, magnitude, tickMs, duration, tier), owner); } - public bool AddStatusEffect(StatusEffect newEffect, Character source, bool silent = false) + public bool AddStatusEffect(StatusEffect newEffect, Character source, bool silent = false, bool hidden = false) { /* worldMasterTextId 32001 [@2B([@IF($E4($EB(1),$EB(2)),you,[@IF($E9(7),[@SHEETEN(xtx/displayName,2,$E9(7),1,1)],$EB(2))])])] [@IF($E4($EB(1),$EB(2)),resist,resists)] the effect of [@SHEET(xtx/status,$E8(11),3)]. 32002 [@SHEET(xtx/status,$E8(11),3)] fails to take effect. */ + + if (HasStatusEffect(newEffect.GetStatusEffectId()) && (newEffect.GetFlags() & (uint)StatusEffectFlags.Stance) != 0) + { + RemoveStatusEffect(newEffect); + return false; + } + var effect = GetStatusEffectById(newEffect.GetStatusEffectId()); + bool canOverwrite = false; if (effect != null) { var overwritable = effect.GetOverwritable(); canOverwrite = (overwritable == (uint)StatusEffectOverwrite.Always) || - (overwritable == (uint)StatusEffectOverwrite.GreaterOnly && (effect.GetDurationMs() < newEffect.GetDurationMs() || effect.GetMagnitude() < newEffect.GetMagnitude())) || - (overwritable == (uint)StatusEffectOverwrite.GreaterOrEqualTo && (effect.GetDurationMs() <= newEffect.GetDurationMs() || effect.GetMagnitude() <= newEffect.GetMagnitude())); + (overwritable == (uint)StatusEffectOverwrite.GreaterOnly && (effect.GetDuration() < newEffect.GetDuration() || effect.GetMagnitude() < newEffect.GetMagnitude())) || + (overwritable == (uint)StatusEffectOverwrite.GreaterOrEqualTo && (effect.GetDuration() <= newEffect.GetDuration() || effect.GetMagnitude() <= newEffect.GetMagnitude())); } if (canOverwrite || effect == null) { // send packet to client with effect added message - if (!silent || !effect.GetSilent() || (effect.GetFlags() & (uint)StatusEffectFlags.Silent) == 0) + if (effect != null && (!silent || !effect.GetSilent() || (effect.GetFlags() & (uint)StatusEffectFlags.Silent) == 0)) { // todo: send packet to client with effect added message } @@ -95,16 +111,36 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (canOverwrite) effects.Remove(newEffect.GetStatusEffectId()); + newEffect.SetStartTime(DateTime.Now); + newEffect.SetOwner(owner); + if (effects.Count < MAX_EFFECTS) { + if(newEffect.script != null) + newEffect.CallLuaFunction(this.owner, "onGain", this.owner, newEffect); + else + LuaEngine.CallLuaStatusEffectFunction(this.owner, newEffect, "onGain", this.owner, newEffect); effects.Add(newEffect.GetStatusEffectId(), newEffect); newEffect.SetSilent(silent); + newEffect.SetHidden(hidden); + + if (!newEffect.GetHidden()) { - var index = Array.IndexOf(effects.Values.ToArray(), newEffect); + int index = 0; + if (owner.charaWork.status.Contains(newEffect.GetStatusId())) + index = Array.IndexOf(owner.charaWork.status, newEffect.GetStatusId()); + else + index = Array.IndexOf(owner.charaWork.status, (ushort) 0); + owner.charaWork.status[index] = newEffect.GetStatusId(); - owner.charaWork.statusShownTime[index] = Utils.UnixTimeStampUTC() + (newEffect.GetDurationMs() / 1000); + + //Stance statuses need their time set to an extremely high number so their icon doesn't flash + //Adding getduration with them doesn't work because it overflows + uint time = (newEffect.GetFlags() & (uint) StatusEffectFlags.Stance) == 0 ? Utils.UnixTimeStampUTC() + (newEffect.GetDuration()) : 0xFFFFFFFF; + owner.charaWork.statusShownTime[index] = time; this.owner.zone.BroadcastPacketAroundActor(this.owner, SetActorStatusPacket.BuildPacket(this.owner.actorId, (ushort)index, (ushort)newEffect.GetStatusId())); } + { owner.zone.BroadcastPacketsAroundActor(owner, owner.GetActorStatusPackets()); } @@ -117,24 +153,29 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public void RemoveStatusEffect(StatusEffect effect, bool silent = false) { - if (effects.ContainsKey(effect.GetStatusEffectId())) + if (effect != null && effects.ContainsKey(effect.GetStatusEffectId()) ) { // 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 added message - } + //hidden effects not in charawork + if(!effect.GetHidden()) { - var index = Array.IndexOf(effects.Values.ToArray(), effect); + var index = Array.IndexOf(owner.charaWork.status, effect.GetStatusId()); + owner.charaWork.status[index] = 0; - owner.charaWork.statusShownTime[index] = uint.MaxValue; + owner.charaWork.statusShownTime[index] = 0; this.owner.zone.BroadcastPacketAroundActor(this.owner, SetActorStatusPacket.BuildPacket(owner.actorId, (ushort)index, (ushort)0)); } // function onLose(actor, effect) - LuaEngine.CallLuaStatusEffectFunction(this.owner, effect, "onLose", this.owner, effect); effects.Remove(effect.GetStatusEffectId()); + if(effect.script != null) + effect.CallLuaFunction(owner, "onLose", owner, effect); + else + LuaEngine.CallLuaStatusEffectFunction(this.owner, effect, "onLose", this.owner, effect); 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 6533680e..e36aec88 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs @@ -53,7 +53,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers } //Only move if owner isn't dead and is either too far away from their spawn point or is meant to roam - else if (!owner.IsDead() && (owner.isMovingToSpawn || owner.GetMobMod((uint)MobModifier.Roams) > 0)) + if (!owner.IsDead() && (owner.isMovingToSpawn || owner.GetMobMod((uint)MobModifier.Roams) > 0)) { DoRoamTick(tick); } @@ -83,8 +83,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { foreach (var chara in owner.zone.GetActorsAroundActor(owner, 50)) { - if (owner.allegiance == chara.allegiance) - continue; + if (chara.allegiance == owner.allegiance) + continue; if (owner.aiContainer.pathFind.AtPoint() && owner.detectionType != DetectionType.None) { @@ -119,8 +119,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers if (owner.GetState() != SetActorStatePacket.MAIN_STATE_ACTIVE) owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE); - owner.moveState = 2; - //owner.SetMod((uint)Modifier.Speed, 5); lastActionTime = DateTime.Now; battleStartTime = lastActionTime; // todo: adjust cooldowns with modifiers @@ -138,7 +136,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { var target = owner.target; base.Disengage(); - owner.statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnDeath, true); // todo: lastActionTime = lastUpdate.AddSeconds(5); owner.isMovingToSpawn = true; @@ -213,13 +210,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers Disengage(); return; } - - - Move(); - - if ((tick - lastCombatTickScript).TotalSeconds > 2) + owner.SetMod((uint)Modifier.Speed, 5); + if ((tick - lastCombatTickScript).TotalSeconds > 3)//Program.Random.Next(10, 15)) { - lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas); + Move(); + //if (owner.aiContainer.CanChangeState()) + //owner.aiContainer.WeaponSkill(owner.zone.FindActorInArea(owner.target.actorId), 27155); + //lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas); lastCombatTickScript = tick; } } @@ -239,7 +236,6 @@ 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.GetAttackRange() - 0.2f || owner.aiContainer.CanFollowPath()) { if (CanMoveForward(distance)) @@ -276,12 +272,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { FaceTarget(); } + lastRoamUpdate = DateTime.Now; } protected void FaceTarget() { // todo: check if stunned etc - if (owner.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.PreventAction)) + if (owner.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.PreventTurn) ) { } else @@ -392,14 +389,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers public override void ChangeTarget(Character target) { - owner.target = target; - owner.currentLockedTarget = target?.actorId ?? Actor.INVALID_ACTORID; - owner.currentTarget = target?.actorId ?? Actor.INVALID_ACTORID; + if (target != owner.target) + { + owner.target = target; + owner.currentLockedTarget = target?.actorId ?? Actor.INVALID_ACTORID; + owner.currentTarget = target?.actorId ?? Actor.INVALID_ACTORID; - foreach (var player in owner.zone.GetActorsAroundActor(owner, 50)) - player.QueuePacket(owner.GetHateTypePacket(player)); + foreach (var player in owner.zone.GetActorsAroundActor(owner, 50)) + player.QueuePacket(owner.GetHateTypePacket(player)); - base.ChangeTarget(target); + base.ChangeTarget(target); + } } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs b/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs index ab374ea3..9f2f6048 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs @@ -177,61 +177,68 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (aoeType != TargetFindAOEType.None) { - if (IsPlayer(owner)) - { - if (masterTarget is Player) - { - findType = TargetFindCharacterType.PlayerToPlayer; - - if (masterTarget.currentParty != null) - { - if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0) - AddAllInAlliance(masterTarget, withPet); - else - AddAllInParty(masterTarget, withPet); - } - else - { - AddTarget(masterTarget, withPet); - } - } - else - { - findType = TargetFindCharacterType.PlayerToBattleNpc; - AddAllBattleNpcs(masterTarget, false); - } - } - else - { - // todo: this needs checking.. - if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player) - findType = TargetFindCharacterType.BattleNpcToPlayer; - else - findType = TargetFindCharacterType.BattleNpcToBattleNpc; - - // todo: configurable pet aoe buff - if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null) - withPet = true; - - // todo: does ffxiv have call for help flag? - //if ((findFlags & ValidTarget.HitAll) != 0) - //{ - // AddAllInZone(masterTarget, withPet); - //} - - AddAllInAlliance(target, withPet); - - if (findType == TargetFindCharacterType.BattleNpcToPlayer) - { - if (owner.allegiance == CharacterTargetingAllegiance.Player) - AddAllInZone(masterTarget, withPet); - else - AddAllInHateList(); - } - } + AddAllInRange(target, withPet); } - if(targets.Count > 16) - targets.RemoveRange(16, targets.Count - 16); + /* + if (aoeType != TargetFindAOEType.None) + { + if (IsPlayer(owner)) + { + if (masterTarget is Player) + { + findType = TargetFindCharacterType.PlayerToPlayer; + + if (masterTarget.currentParty != null) + { + if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0) + AddAllInAlliance(masterTarget, withPet); + else + AddAllInParty(masterTarget, withPet); + } + else + { + AddTarget(masterTarget, withPet); + } + } + else + { + findType = TargetFindCharacterType.PlayerToBattleNpc; + AddAllBattleNpcs(masterTarget, false); + } + } + else + { + // todo: this needs checking.. + if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player) + findType = TargetFindCharacterType.BattleNpcToPlayer; + else + findType = TargetFindCharacterType.BattleNpcToBattleNpc; + + // todo: configurable pet aoe buff + if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null) + withPet = true; + + // todo: does ffxiv have call for help flag? + //if ((findFlags & ValidTarget.HitAll) != 0) + //{ + // AddAllInZone(masterTarget, withPet); + //} + + AddAllInAlliance(target, withPet); + + if (findType == TargetFindCharacterType.BattleNpcToPlayer) + { + if (owner.allegiance == CharacterTargetingAllegiance.Player) + AddAllInZone(masterTarget, withPet); + else + AddAllInHateList(); + } + } + }*/ + + + if (targets.Count > 16) + targets.RemoveRange(16, targets.Count - 16); } /// @@ -311,6 +318,18 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai } } + private void AddAllInRange(Character target, bool withPet) + { + int dist = (int)maxDistance; + var actors = owner.zone.GetActorsAroundActor(target, dist); + + foreach (Character actor in actors) + { + AddTarget(actor, false); + } + + } + private void AddAllInHateList() { if (!(owner is BattleNpc)) @@ -342,6 +361,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if ((validTarget & ValidTarget.Enemy) != 0 && target.allegiance == owner.allegiance) return false; + if ((validTarget & ValidTarget.PartyMember) == 0 && target.currentParty == owner.currentParty) + return false; + + if ((validTarget & ValidTarget.PartyMember) != 0 && target.currentParty != owner.currentParty) + return false; + if ((validTarget & ValidTarget.NPC) != 0 && target.isStatic) return false; diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/AbilityState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/AbilityState.cs new file mode 100644 index 00000000..27e25452 --- /dev/null +++ b/FFXIVClassic Map Server/actors/chara/ai/state/AbilityState.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server.packets.send.actor; +using FFXIVClassic_Map_Server.packets.send.actor.battle; +using FFXIVClassic_Map_Server.packets.send; + +namespace FFXIVClassic_Map_Server.actors.chara.ai.state +{ + class AbilityState : State + { + + private BattleCommand skill; + + public AbilityState(Character owner, Character target, ushort skillId) : + base(owner, target) + { + this.startTime = DateTime.Now; + this.skill = Server.GetWorldManager().GetBattleCommand(skillId); + var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "ability", "onAbilityPrepare", owner, target, skill); + + this.target = skill.GetMainTarget(owner, target); + + if (returnCode == 0) + { + OnStart(); + } + else + { + errorResult = null; + interrupt = true; + } + } + + public override void OnStart() + { + var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "ability", "onAbilityStart", owner, target, skill); + + if (returnCode != 0) + { + interrupt = true; + errorResult = new BattleAction(owner.actorId, (ushort)(returnCode == -1 ? 32558 : returnCode), 0); + } + else + { + //owner.LookAt(target); + + //If owner already has this status effect and it's a stance that gets removed on reuse, remove it and don't continue + var effect = owner.statusEffects.GetStatusEffectById(skill.statusId); + if (effect!= null && (effect.GetFlags() & (uint) StatusEffectFlags.Stance) != 0) + { + owner.statusEffects.RemoveStatusEffect(effect); + interrupt = true; + } + + } + } + + public override bool Update(DateTime tick) + { + if (skill != null) + { + TryInterrupt(); + + if (interrupt) + { + OnInterrupt(); + return true; + } + + // todo: check weapon delay/haste etc and use that + var actualCastTime = skill.castTimeMs; + + if ((tick - startTime).Milliseconds >= skill.castTimeMs) + { + OnComplete(); + return true; + } + return false; + } + return true; + } + + public override void OnInterrupt() + { + // todo: send paralyzed/sleep message etc. + if (errorResult != null) + { + owner.DoBattleAction(skill.id, errorResult.animation, errorResult); + errorResult = null; + } + } + + public override void OnComplete() + { + bool hitTarget = false; + + skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget); + isCompleted = true; + var targets = skill.targetFind.GetTargets(); + + List actions = new List(); + List effects = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.ActivateOnAttack); + + foreach (var chara in targets) + { + for (int hitNum = 0; hitNum < skill.numHits; hitNum++) + { + //30328 - Your [ability] grants you the effect of [status] + //30320 - You use [ability]. You recover x HP. + var action = new BattleAction(chara.actorId, skill.worldMasterTextId, 0, 0, 1, 1); + + //uncached + lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "ability", "onAbilityFinish", owner, target, skill, action); + //cached + //skill.CallLuaFunction(owner, "onAbilityFinish", owner, target, skill, action); + + //if hit type isn't evade or miss + if (((action.hitType & HitType.Evade) | (action.hitType & HitType.Miss)) == 0) + hitTarget = true; + + actions.AddRange(action.GetAllActions()); + } + } + // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action + BattleAction[] errors = (BattleAction[])actions.ToArray().Clone(); + owner.OnAbility(this, actions.ToArray(), skill, ref errors); + owner.DoBattleAction(skill.id, skill.battleAnimation, actions); + } + + public override void TryInterrupt() + { + if (interrupt) + return; + + if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventAbility)) + { + // todo: sometimes paralyze can let you attack, get random percentage of actually letting you attack + var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventAbility); + uint effectId = 0; + if (list.Count > 0) + { + // todo: actually check proc rate/random chance of whatever effect + effectId = list[0].GetStatusEffectId(); + } + interrupt = true; + return; + } + + interrupt = !CanUse(); + } + + private bool CanUse() + { + return skill.IsValidMainTarget(owner, target); + } + + public BattleCommand GetWeaponSkill() + { + return skill; + } + + public override void Cleanup() + { + owner.aiContainer.UpdateLastActionTime(skill.animationDurationSeconds); + } + } +} diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs index 79f63178..cc72b8c8 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs @@ -82,7 +82,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override void OnComplete() { - BattleAction action = new BattleAction(target.actorId, 0x765D, (uint) HitEffect.Hit, 0, (byte) HitDirection.None); + //BattleAction action = new BattleAction(target.actorId, 0x765D, (uint) HitEffect.Hit, 0, (byte) HitDirection.None); errorResult = null; // todo: implement auto attack damage bonus in Character.OnAttack @@ -99,16 +99,35 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state * The above damage bonus also applies to “Shot” attacks by archers. */ // handle paralyze/intimidate/sleep/whatever in Character.OnAttack - owner.OnAttack(this, action, ref errorResult); - owner.DoBattleAction((ushort)BattleActionX01PacketCommand.Attack, action.animation, errorResult == null ? action : errorResult); + + + List actions = new List(); + + var i = 0; + for (int hitNum = 0; hitNum < owner.GetMod((uint) Modifier.HitCount); hitNum++) + { + BattleAction action = new BattleAction(target.actorId, 0x765D, (uint)HitEffect.Hit, 100, (byte)HitDirection.None, (byte) hitNum); + action.battleActionType = BattleActionType.AttackPhysical; + // evasion, miss, dodge, etc to be handled in script, calling helpers from scripts/weaponskills.lua + // temporary evade/miss/etc function to test hit effects + utils.BattleUtils.CalcHitType(owner, target, null, action); + actions.AddRange(action.GetAllActions()); + } + + // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action + BattleAction[] errors = (BattleAction[])actions.ToArray().Clone(); + + owner.OnAttack(this, actions[0], ref errorResult); + owner.DoBattleAction(22104, 0x19001000, actions); + target.SetMod((uint) Modifier.MinimumHpLock, 0); } public override void TryInterrupt() { - if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventAction)) + if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventAttack)) { // todo: sometimes paralyze can let you attack, calculate proc rate - var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventAction); + var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventAttack); uint statusId = 0; if (list.Count > 0) { @@ -124,7 +143,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state private bool IsAttackReady() { // todo: this enforced delay should really be changed if it's not retail.. - return Program.Tick >= attackTime && Program.Tick >= owner.aiContainer.GetLastActionTime().AddSeconds(1); + return Program.Tick >= attackTime && Program.Tick >= owner.aiContainer.GetLastActionTime(); } private bool CanAttack() diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs index 673b42a4..efe06436 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/DeathState.cs @@ -15,9 +15,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state : base(owner, null) { owner.Disengage(); - //owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD); - var deathStatePacket = SetActorStatePacket.BuildPacket(owner.actorId, SetActorStatePacket.MAIN_STATE_DEAD, owner.currentSubState); - owner.zone.BroadcastPacketAroundActor(owner, deathStatePacket); + owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD); + owner.statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnDeath, true); + //var deathStatePacket = SetActorStatePacket.BuildPacket(owner.actorId, SetActorStatePacket.MAIN_STATE_DEAD2, owner.currentSubState); + //owner.zone.BroadcastPacketAroundActor(owner, deathStatePacket); canInterrupt = false; startTime = tick; despawnTime = startTime.AddSeconds(timeToFadeOut); diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/InactiveState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/InactiveState.cs index e0439e37..735d7765 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/InactiveState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/InactiveState.cs @@ -27,7 +27,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state if (owner.IsDead()) return true; - if (!owner.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.PreventAction)) + if (!owner.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.PreventMovement)) return true; } diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs index 8e28554e..e3c6c1cc 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/MagicState.cs @@ -26,7 +26,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state this.spell = Server.GetWorldManager().GetBattleCommand(spellId); var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onMagicPrepare", owner, target, spell); - if (returnCode == 0 && owner.CanCast(target, spell)) + this.target = spell.GetMainTarget(owner, target); + + if (returnCode == 0 && owner.CanCast(this.target, spell)) { OnStart(); } @@ -51,16 +53,28 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state // todo: check within attack range float[] baseCastDuration = { 1.0f, 0.25f }; - float spellSpeed = spell.castTimeSeconds; + //Check combo stuff here because combos can impact spell cast times - // command casting duration - if (owner is Player) + float spellSpeed = spell.castTimeMs; + + //There are no positional spells, so just check onCombo, need to check first because certain spells change aoe type/accuracy + //If owner is a player and the spell being used is part of the current combo + if (spell.comboStep == 1 || ((owner is Player p) && (p.playerWork.comboNextCommandId[0] == spell.id || p.playerWork.comboNextCommandId[1] == spell.id))) { - // todo: modify spellSpeed based on modifiers and stuff - ((Player)owner).SendStartCastbar(spell.id, Utils.UnixTimeStampUTC(DateTime.Now.AddSeconds(spellSpeed))); + lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onCombo", owner, target, spell); + spell.isCombo = true; + } + if (!spell.IsInstantCast()) + { + // command casting duration + if (owner is Player) + { + // todo: modify spellSpeed based on modifiers and stuff + ((Player)owner).SendStartCastbar(spell.id, Utils.UnixTimeStampUTC(DateTime.Now.AddMilliseconds(spellSpeed))); + } + owner.SendChant(0xf, 0x0); + owner.DoBattleAction(spell.id, (uint) 0x6F000000 | spell.castType, new BattleAction(target.actorId, 30128, 1, 0, 1)); //You begin casting (6F000002: BLM, 6F000003: WHM, 0x6F000008: BRD) } - owner.SendChant(0xF, 0x0); - owner.DoBattleAction(spell.id, 0x6F000002, new BattleAction(target.actorId, 30128, 1, 0, 1)); //You begin casting (6F000002: BLM, 6F000003: WHM) } } @@ -77,9 +91,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state } // todo: check weapon delay/haste etc and use that - var actualCastTime = spell.castTimeSeconds; + var actualCastTime = spell.castTimeMs; - if ((tick - startTime).TotalSeconds >= spell.castTimeSeconds) + if ((tick - startTime).TotalMilliseconds >= spell.castTimeMs) { OnComplete(); return true; @@ -102,24 +116,58 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override void OnComplete() { + //How do combos/hitdirs work for aoe abilities or does that not matter for aoe? + HitDirection hitDir = owner.GetHitDirection(target); + bool hitTarget = false; + spell.targetFind.FindWithinArea(target, spell.validTarget, spell.aoeTarget); isCompleted = true; - var targets = spell.targetFind.GetTargets(); - BattleAction[] actions = new BattleAction[targets.Count]; - var i = 0; - foreach (var chara in targets) + + + List actions = new List(); + if (targets.Count > 0) { - var action = new BattleAction(chara.actorId, spell.worldMasterTextId, spell.battleAnimation, 0, (byte)HitDirection.None, 1); - action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onMagicFinish", owner, chara, spell, action); - actions[i++] = action; + List effects = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.ActivateOnSpell); + + //modify skill based on status effects + foreach (var effect in effects) + lua.LuaEngine.CallLuaStatusEffectFunction(owner, effect, "onWeaponSkill", owner, effect, spell); + + //Now that combos and positionals bonuses are done, we can calculate hits/crits/etc and damage + foreach (var chara in targets) + { + for (int hitNum = 0; hitNum < spell.numHits; hitNum++) + { + var action = new BattleAction(chara.actorId, spell.worldMasterTextId, 0, 0, (byte) hitDir, (byte) hitNum); + lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onMagicFinish", owner, chara, spell, action); + + //if hit type isn't evade or miss + if (action.hitType > HitType.Evade) + hitTarget = true; + + actions.AddRange(action.GetAllActions()); + } + } + } + else + { + //No targets hit, cast failed + actions.Add(new BattleAction(target.actorId, 30202, (uint) (0))); } - // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action - var errors = (BattleAction[])actions.Clone(); - owner.OnCast(this, actions, ref errors); - owner.DoBattleAction(spell.id, spell.battleAnimation, actions); + // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action + BattleAction[] errors = (BattleAction[])actions.ToArray().Clone(); + owner.OnCast(this, actions.ToArray(), spell, ref errors); + owner.DoBattleAction(spell.id, spell.battleAnimation, actions); + owner.statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnCasting); + //Now that we know if we hit the target we can check if the combo continues + if (owner is Player player) + if (spell.isCombo && hitTarget) + player.SetCombos(spell.comboNextCommandId); + else + player.SetCombos(); } public override void TryInterrupt() @@ -127,10 +175,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state if (interrupt) return; - if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventAction)) + if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventSpell)) { // todo: sometimes paralyze can let you attack, get random percentage of actually letting you attack - var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventAction); + var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventSpell); uint effectId = 0; if (list.Count > 0) { @@ -154,7 +202,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state private bool CanCast() { - return owner.CanCast(target, spell) && spell.IsValidTarget(owner, target) && !HasMoved(); + return owner.CanCast(target, spell) && spell.IsValidMainTarget(owner, target) && !HasMoved(); } private bool HasMoved() @@ -170,7 +218,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state { ((Player)owner).SendEndCastbar(); } - owner.aiContainer.UpdateLastActionTime(); + owner.aiContainer.UpdateLastActionTime(spell.animationDurationSeconds); } public BattleCommand GetSpell() diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs index 65ce04c6..a8c3c15e 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs @@ -15,11 +15,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state { private BattleCommand skill; - + private HitDirection hitDirection; public WeaponSkillState(Character owner, Character target, ushort skillId) : base(owner, target) { this.startTime = DateTime.Now; + //this.target = skill.targetFind. this.skill = Server.GetWorldManager().GetBattleCommand(skillId); var returnCode = lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillPrepare", owner, target, skill); @@ -46,6 +47,37 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state else { owner.LookAt(target); + hitDirection = owner.GetHitDirection(target); + + //Do positionals and combo effects first because these can influence accuracy and amount of targets/numhits, which influence the rest of the steps + //If there is no positon required or if the position bonus should be activated + if ((skill.positionBonus & utils.BattleUtils.ConvertHitDirToPosition(hitDirection)) == skill.positionBonus) + { + //If there is a position bonus + if (skill.positionBonus != BattleCommandPositionBonus.None) + //lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onPositional", owner, target, skill); + skill.CallLuaFunction(owner, "onPositional", owner, target, skill); + + //Combo stuff + if (owner is Player p) + { + //If skill is part of owner's class/job, it can be used in a combo + if (skill.job == p.GetClass() || skill.job == p.GetCurrentClassOrJob()) + { + //If owner is a player and the skill being used is part of the current combo + if (p.playerWork.comboNextCommandId[0] == skill.id || p.playerWork.comboNextCommandId[1] == skill.id) + { + lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onCombo", owner, target, skill); + skill.CallLuaFunction(owner, "onCombo", owner, target, skill); + skill.isCombo = true; + } + //or if this just the start of a combo + else if (skill.comboStep == 1) + skill.isCombo = true; + } + } + } + } } @@ -62,9 +94,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state } // todo: check weapon delay/haste etc and use that - var actualCastTime = skill.castTimeSeconds; + var actualCastTime = skill.castTimeMs; - if ((tick - startTime).TotalSeconds >= skill.castTimeSeconds) + if ((tick - startTime).Milliseconds >= skill.castTimeMs) { OnComplete(); return true; @@ -86,28 +118,55 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override void OnComplete() { + bool hitTarget = false; + skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget); isCompleted = true; var targets = skill.targetFind.GetTargets(); - BattleAction[] actions = new BattleAction[targets.Count]; + //Need a variable size list of actions because status effects may add extra actions + //BattleAction[] actions = new BattleAction[targets.Count * skill.numHits]; + List actions = new List(); + List effects = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.ActivateOnAttack); - var i = 0; + //modify skill based on status effects + foreach (var effect in effects) + effect.CallLuaFunction(owner, "onWeaponSkill", skill); + + //Now that combos and positionals bonuses are done, we can calculate hits/crits/etc and damage for each action foreach (var chara in targets) { - var action = new BattleAction(chara.actorId, skill.worldMasterTextId, (uint)HitEffect.Hit, 0, 1, 1); - // evasion, miss, dodge, etc to be handled in script, calling helpers from scripts/weaponskills.lua - action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillFinish", owner, target, skill, action); - actions[i++] = action; - chara.Engage(chara.actorId, 1); + for (int hitNum = 0; hitNum < skill.numHits; hitNum++) + { + var action = new BattleAction(chara.actorId, skill.worldMasterTextId, 0, 0, (byte) hitDirection, 1); + //For older versions this will need to be in the database for magic weaponskills + action.battleActionType = BattleActionType.AttackPhysical; + //uncached script + lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillFinish", owner, target, skill, action); + + //cached script + //skill.CallLuaFunction(owner, "onSkillFinish", owner, target, skill, action); + action.hitNum = (byte)hitNum; + + if (action.hitType > HitType.Evade) + hitTarget = true; + + actions.AddRange(action.GetAllActions()); + } } // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action - var errors = (BattleAction[])actions.Clone(); - owner.OnWeaponSkill(this, actions, ref errors); + BattleAction[] errors = (BattleAction[]) actions.ToArray().Clone(); + owner.OnWeaponSkill(this, actions.ToArray(), skill, ref errors); owner.DoBattleAction(skill.id, skill.battleAnimation, actions); - + owner.statusEffects.RemoveStatusEffectsByFlags((uint) StatusEffectFlags.LoseOnAttacking); + //Now that we know if we hit the target we can check if the combo continues + if (owner is Player player) + if (skill.isCombo && hitTarget) + player.SetCombos(skill.comboNextCommandId); + else + player.SetCombos(); } public override void TryInterrupt() @@ -115,10 +174,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state if (interrupt) return; - if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventAction)) + if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventWeaponSkill)) { // todo: sometimes paralyze can let you attack, get random percentage of actually letting you attack - var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventAction); + var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventWeaponSkill); uint effectId = 0; if (list.Count > 0) { @@ -134,7 +193,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state private bool CanUse() { - return owner.CanWeaponSkill(target, skill) && skill.IsValidTarget(owner, target); + return owner.CanWeaponSkill(target, skill) && skill.IsValidMainTarget(owner, target); } public BattleCommand GetWeaponSkill() @@ -144,7 +203,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override void Cleanup() { - owner.aiContainer.UpdateLastActionTime(); + owner.aiContainer.UpdateLastActionTime(skill.animationDurationSeconds); } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs b/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs index ed2a1050..8ff06ff6 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs @@ -7,22 +7,161 @@ using System.Threading.Tasks; using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.packets.send.actor; using FFXIVClassic_Map_Server.packets.send.actor.battle; +using FFXIVClassic_Map_Server.actors.chara.player; +using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic.Common; namespace FFXIVClassic_Map_Server.actors.chara.ai.utils { static class BattleUtils { + + public static Dictionary HitTypeTextIds = new Dictionary() + { + { HitType.Miss, 30311 }, + { HitType.Evade, 30310 }, + { HitType.Parry, 30308 }, + { HitType.Block, 30306 }, + { HitType.Resist, 30306 },//I can't find what the actual textid for resists is + { HitType.Hit, 30301 }, + { HitType.Crit, 30302 } + }; + + public static Dictionary HitTypeEffects = new Dictionary() + { + { HitType.Miss, 0 }, + { HitType.Evade, HitEffect.Evade }, + { HitType.Parry, HitEffect.Parry }, + { HitType.Block, HitEffect.Block }, + { HitType.Resist, HitEffect.RecoilLv1 },//Probably don't need this, resists are handled differently to the rest + { HitType.Hit, HitEffect.Hit }, + { HitType.Crit, HitEffect.Crit } + }; + + public static bool TryAttack(Character attacker, Character defender, BattleAction action, ref BattleAction error) { // todo: get hit rate, hit count, set hit effect - action.effectId |= (uint)(HitEffect.RecoilLv2 | HitEffect.Hit | HitEffect.HitVisual1); + //action.effectId |= (uint)(HitEffect.RecoilLv2 | HitEffect.Hit | HitEffect.HitVisual1); return true; } + private static double CalculateDlvlModifier(short dlvl) + { + //this is just a really, really simplified version of the graph from http://kanican.livejournal.com/55915.html + //actual formula is definitely more complicated + //I'm going to assum these formulas are linear, and they're clamped so the modifier never goes below 0. + double modifier = 0; + + + if (dlvl >= 0) + modifier = (.35 * dlvl) + .225; + else + modifier = (.01 * dlvl) + .25; + + return modifier.Clamp(0, 1); + } + + //Damage calculations + //Calculate damage of action + //We could probably just do this when determining the action's hit type + public static void CalculateDamage(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + + short dlvl = (short)(defender.GetLevel() - attacker.GetLevel()); + + switch (action.hitType) + { + //Misses and evades deal no damage. + case (HitType.Miss): + case (HitType.Evade): + action.amount = 0; + break; + // todo: figure out parry damage reduction. For now assume 25% reduction + case (HitType.Parry): + CalculateParryDamage(attacker, defender, skill, action); + break; + case (HitType.Block): + CalculateBlockDamage(attacker, defender, skill, action); + break; + //There are 3 (or 4?) tiers of resists, each decreasing damage dealt by 25%. For now just assume level 2 resist (50% reduction) + // todo: figure out resist tiers + case (HitType.Resist): + CalculateResistDamage(attacker, defender, skill, action); + break; + case (HitType.Crit): + CalculateCritDamage(attacker, defender, skill, action); + break; + } + ushort finalAmount = action.amount; + + //dlvl, Defense, and Vitality all effect how much damage is taken after hittype takes effect + //player attacks cannot do more than 9999 damage. + action.amount = (ushort) (finalAmount - CalculateDlvlModifier(dlvl) * (defender.GetMod((uint)Modifier.Defense) + 0.67 * defender.GetMod((uint)Modifier.Vitality))).Clamp(0, 9999); + + + } + + public static void CalculateBlockDamage(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + double percentBlocked = defender.GetMod((uint)Modifier.Block) * .2;//Every point of Block adds .2% to how much is blocked + percentBlocked += defender.GetMod((uint)Modifier.Vitality) * .1;//Every point of vitality adds .1% to how much is blocked + + percentBlocked = 1 - percentBlocked; + action.amount = (ushort)(action.amount * percentBlocked); + } + + //don't know crit formula + public static void CalculateCritDamage(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + short dlvl = (short)(defender.GetLevel() - attacker.GetLevel()); + double bonus = (.04 * (dlvl * dlvl)) - 2 * dlvl; + bonus += 1.20; + double potencyModifier = (-.075 * dlvl) + 1.73; + + // + potency bonus + //bonus += attacker.GetMod((uint) Modifier.CriticalPotency) * potencyModifier; + // - Crit resilience + //bonus -= attacker.GetMod((uint)Modifier.CriticalResilience) * potencyModifier; + + //need to add something for bonus potency as a part of skill (ie thundara) + action.amount = (ushort)(action.amount * bonus.Clamp(1.15, 1.75));//min bonus of 115, max bonus of 175 + } + + public static void CalculateParryDamage(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + double percentParry = .75; + + action.amount = (ushort)(action.amount * percentParry); + } + + //There are 3 or 4 tiers of resist that are flat 25% decreases in damage. + //It's possible we could just calculate the damage at the same time as we determine the hit type (the same goes for the rest of the hit types) + //Or we could have HitTypes for DoubleResist, TripleResist, and FullResist that get used here. + public static void CalculateResistDamage(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + double percentResist = .5; + + action.amount = (ushort)(action.amount * percentResist); + } + + //Used for attacks and abilities like Jump that deal damage public static ushort CalculateAttackDamage(Character attacker, Character defender, BattleAction action) { - ushort damage = (ushort)(Program.Random.Next(10) * 10); + ushort damage = (ushort)100; + + if (attacker is Player p) + { + var weapon = p.GetEquipment().GetItemAtSlot(Equipment.SLOT_MAINHAND); + if (weapon != null) + { + var weaponData = Server.GetItemGamedata(weapon.itemId); + + //just some numbers from https://www.bluegartr.com/threads/107403-Stats-and-how-they-work/page24 + damage += (ushort) (2.225 * (weaponData as WeaponItem).damagePower + (attacker.GetMod((uint) Modifier.Attack) * .38)); + + } + } // todo: handle all other crap before protect/stoneskin @@ -60,23 +199,18 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils if (defender.statusEffects.HasStatusEffect(StatusEffectId.Shell)) { - // todo: shell probably only shows on magic.. if (defender.statusEffects.HasStatusEffect(StatusEffectId.Shell)) { - if (action != null) - action.effectId |= (uint)HitEffect.Shell; } } if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin)) { - if (action != null) - action.effectId |= (uint)HitEffect.Stoneskin; } return damage; } - public static void DamageTarget(Character attacker, Character defender, BattleAction action, DamageTakenType type) + public static void DamageTarget(Character attacker, Character defender, BattleAction action, DamageTakenType type, bool sendBattleAction = false) { if (defender != null) { @@ -88,14 +222,289 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils { bnpc.hateContainer.AddBaseHate(attacker); } - bnpc.hateContainer.UpdateHate(attacker, action.amount); + bnpc.hateContainer.UpdateHate(attacker, action.enmity); bnpc.lastAttacker = attacker; } - defender.DelHP((short)action.amount); + defender.DelHP((short) action.amount); defender.OnDamageTaken(attacker, action, type); } } + public static void DoAction(Character user, Character receiver, BattleAction action, DamageTakenType type = DamageTakenType.None) + { + switch(action.battleActionType) + { + //split attack into phys/mag? + case (BattleActionType.AttackMagic)://not sure if these use different damage taken formulas + case (BattleActionType.AttackPhysical): + DamageTarget(user, receiver, action, type, false); + break; + case (BattleActionType.Heal): + receiver.AddHP(action.amount); + break; + } + + + if ((type == DamageTakenType.Ability || type == DamageTakenType.Attack) && action.amount != 0) + { + receiver.AddTP(150); + user.AddTP(200); + } + } + + /* + * Rate functions + */ + //How is accuracy actually calculated? + public static double GetHitRate(Character attacker, Character defender, BattleCommand skill) + { + double hitRate = .80; + //Certain skills have lower or higher accuracy rates depending on position/combo + return hitRate * (skill != null ? skill.accuracyModifier : 1); + } + + //Whats the parry formula? + public static double GetParryRate(Character attacker, Character defender, BattleCommand skill) + { + //Can't parry with shield, must be facing attacker + if (defender.GetMod((uint)Modifier.HasShield) > 0 || !defender.IsFacing(attacker)) + return 0; + + return .10; + } + + public static double GetCritRate(Character attacker, Character defender, BattleCommand skill) + { + double critRate = 10;// .0016 * attacker.GetMod((uint)Modifier.CritRating);//Crit rating adds .16% per point + return Math.Min(critRate, .20);//Crit rate is capped at 20% + } + + //http://kanican.livejournal.com/55370.html + // todo: figure that out + public static double GetResistRate(Character attacker, Character defender, BattleCommand skill) + { + + return .95; + } + + //Block Rate follows 4 simple rules: + //(1) Every point in DEX gives +0.1% rate + //(2) Every point in "Block Rate" gives +0.2% rate + //(3) True block proc rate is capped at 75%. No clue on a possible floor. + //(4) The baseline rate is based on dLVL only(mob stats play no role). The baseline rate is summarized in this raw data sheet: https://imgbox.com/aasLyaJz + public static double GetBlockRate(Character attacker, Character defender, BattleCommand skill) + { + //Shields are required to block. + if (defender.GetMod((uint)Modifier.HasShield) == 0)//|| !defender.IsFacing(attacker)) + return 0; + + short dlvl = (short) (attacker.GetLevel() - defender.GetLevel()); + double blockRate = (-2.5 * dlvl) - 5; // Base block rate + blockRate += attacker.GetMod((uint) Modifier.Dexterity) * .1;// .1% for every dex + blockRate += attacker.GetMod((uint) Modifier.BlockRate) * .2;// .2% for every block rate + return Math.Min(blockRate, 25); + } + + + /* + * HitType helpers. Used for determining if attacks are hits, crits, blocks, etc. and changing their damage based on that + */ + + public static bool TryCrit(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + if (Program.Random.NextDouble() < GetCritRate(attacker, defender, skill)) + { + action.hitType = HitType.Crit; + CalculateCritDamage(attacker, defender, skill, action); + return true; + } + + return false; + } + + public static bool TryResist(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + if (Program.Random.NextDouble() < GetResistRate(attacker, defender, skill)) + { + action.hitType = HitType.Resist; + CalculateResistDamage(attacker, defender, skill, action); + return true; + } + + return false; + } + + public static bool TryBlock(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + if (Program.Random.NextDouble() < GetBlockRate(attacker, defender, skill)) + { + action.hitType = HitType.Block; + defender.SetProc((int)HitType.Block); + CalculateBlockDamage(attacker, defender, skill, action); + return true; + } + + return false; + } + + public static bool TryParry(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + if (Program.Random.NextDouble() < GetParryRate(attacker, defender, skill)) + { + action.hitType = HitType.Parry; + defender.SetProc((int)HitType.Parry); + CalculateParryDamage(attacker, defender, skill, action); + return true; + } + + return false; + } + + //TryMiss instead of tryHit because hits are the default and don't change damage + public static bool TryMiss(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + if (Program.Random.NextDouble() > GetHitRate(attacker, defender, skill)) + { + action.hitType = HitType.Miss; + action.amount = 0; + defender.SetProc((int)HitType.Evade); + attacker.SetProc((int)HitType.Miss); + return true; + } + return false; + } + + /* + * Hit Effecthelpers. Different types of hit effects hits use some flags for different things, so they're split into physical, magical, heal, and status + */ + public static void CalcHitType(Character caster, Character target, BattleCommand skill, BattleAction action) + { + //Might be a simpler way to do this? + switch(action.battleActionType) + { + case (BattleActionType.AttackPhysical): + SetHitEffectPhysical(caster, target, skill, action); + break; + case (BattleActionType.AttackMagic): + SetHitEffectMagical(caster, target, skill, action); + break; + case (BattleActionType.Heal): + SetHitEffectHeal(caster, target, skill, action); + break; + case (BattleActionType.Status): + SetHitEffectStatus(caster, target, skill, action); + break; + } + } + + public static void SetHitEffectPhysical(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + //Determine the hittype of the action and change amount of damage it does based on that + if (!TryMiss(attacker, defender, skill, action)) + if (!TryCrit(attacker, defender, skill, action)) + if (!TryBlock(attacker, defender, skill, action)) + TryParry(attacker, defender, skill, action); + + var hitEffect = HitEffect.HitEffectType; + + //Don't know what recoil is actually based on, just guessing + //Crit is 2 and 3 together + if (action.hitType == HitType.Crit) + hitEffect |= HitEffect.CriticalHit; + else + { + double percentDealt = (100.0 * (action.amount / defender.GetMaxHP())); + if (percentDealt > 5.0) + hitEffect |= HitEffect.RecoilLv2; + else if(percentDealt > 10) + hitEffect |= HitEffect.RecoilLv3; + } + action.worldMasterTextId = HitTypeTextIds[action.hitType]; + hitEffect |= HitTypeEffects[action.hitType]; + + if (skill != null && skill.isCombo && action.hitType > HitType.Evade) + hitEffect |= (HitEffect)(skill.comboStep << 15); + + //if attack hit the target, take into account protective status effects + if (action.hitType >= HitType.Parry) + { + //Protect / Shell only show on physical/ magical attacks respectively. + if (defender.statusEffects.HasStatusEffect(StatusEffectId.Protect)) + if (action != null) + hitEffect |= HitEffect.Protect; + + + if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin)) + if (action != null) + hitEffect |= HitEffect.Stoneskin; + } + action.effectId = (uint) hitEffect; + } + + public static void SetHitEffectMagical(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + //Determine the hit type of the action + if (!TryMiss(attacker, defender, skill, action)) + if (!TryCrit(attacker, defender, skill, action)) + TryResist(attacker, defender, skill, action); + + var hitEffect = HitEffect.MagicEffectType; + + //Recoil levels for spells are a bit different than physical. Recoil levels are used for resists. + //Lv1 is for larger resists, Lv2 is for smaller resists and Lv3 is for no resists. Crit is still used for crits + if (action.hitType == HitType.Resist) + { + //todo: calculate resist levels and figure out what the difference between Lv1 and 2 in retail was. For now assuming a full resist with 0 damage dealt is Lv1, all other resists Lv2 + if (action.amount == 0) + hitEffect |= HitEffect.RecoilLv1; + else + hitEffect |= HitEffect.RecoilLv2; + } + else if (action.hitType == HitType.Crit) + hitEffect |= HitEffect.Crit; + else + hitEffect |= HitEffect.RecoilLv3; + + action.worldMasterTextId = HitTypeTextIds[action.hitType]; + hitEffect |= HitTypeEffects[action.hitType]; + + if (skill != null && skill.isCombo) + hitEffect |= (HitEffect)(skill.comboStep << 15); + + //if attack hit the target, take into account protective status effects + if (action.hitType >= HitType.Block) + { + //Protect / Shell only show on physical/ magical attacks respectively. + if (defender.statusEffects.HasStatusEffect(StatusEffectId.Shell)) + if (action != null) + hitEffect |= HitEffect.Shell; + + + if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin)) + if (action != null) + hitEffect |= HitEffect.Stoneskin; + } + action.effectId = (uint)hitEffect; + } + + + public static void SetHitEffectHeal(Character caster, Character receiver, BattleCommand skill, BattleAction action) + { + var hitEffect = HitEffect.MagicEffectType | HitEffect.Heal; + //Heals use recoil levels in some way as well. Possibly for very low health clutch heals or based on percentage of current health healed (not max health). + // todo: figure recoil levels out for heals + hitEffect |= HitEffect.RecoilLv3; + //do heals crit? + + action.effectId = (uint)hitEffect; + } + + public static void SetHitEffectStatus(Character caster, Character receiver, BattleCommand skill, BattleAction action) + { + var hitEffect = (uint) HitEffect.StatusEffectType | skill.statusId; + action.effectId = hitEffect; + } + public static int CalculateSpellDamage(Character attacker, Character defender, BattleCommand spell) { // todo: spell formulas and stuff (stoneskin, mods, stats, etc) @@ -104,7 +513,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils public static uint CalculateSpellCost(Character caster, Character target, BattleCommand spell) { - var scaledCost = spell.CalculateCost((uint)caster.charaWork.parameterSave.state_mainSkillLevel); + var scaledCost = spell.CalculateMpCost(caster); // todo: calculate cost for mob/player if (caster is BattleNpc) @@ -117,5 +526,66 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils } return scaledCost; } + + //Convert a HitDirection to a BattleCommandPositionBonus. Basically just combining left/right into flank + public static BattleCommandPositionBonus ConvertHitDirToPosition(HitDirection hitDir) + { + BattleCommandPositionBonus position = BattleCommandPositionBonus.None; + + switch(hitDir) + { + case (HitDirection.Front): + position = BattleCommandPositionBonus.Front; + break; + case (HitDirection.Right): + case (HitDirection.Left): + position = BattleCommandPositionBonus.Flank; + break; + case (HitDirection.Rear): + position = BattleCommandPositionBonus.Rear; + break; + } + return position; + } + + //IsAdditional is needed because additional actions may be required for some actions' effects + //For instance, Goring Blade's bleed effect requires another action so the first action can still show damage numbers + //Sentinel doesn't require an additional action because it doesn't need to show those numbers + public static void TryStatus(Character caster, Character target, BattleCommand skill, BattleAction action, bool isAdditional = true) + { + double rand = Program.Random.NextDouble(); + (caster as Player).SendMessage(0x20, "", rand.ToString()); + if (skill != null && action.amount < target.GetHP() && skill.statusId != 0 && action.hitType > HitType.Evade && rand < skill.statusChance) + { + StatusEffect effect = Server.GetWorldManager().GetStatusEffect(skill.statusId); + //Because combos might change duration or tier + if (effect != null) + { + effect.SetDuration(skill.statusDuration); + effect.SetTier(skill.statusTier); + effect.SetOwner(target); + if (target.statusEffects.AddStatusEffect(effect, caster)) + { + //If we need an extra action to show the status text + if (isAdditional) + action.AddStatusAction(target.actorId, skill.statusId); + } + else + action.worldMasterTextId = 32002;//Is this right? + } + else + { + if (target.statusEffects.AddStatusEffect(skill.statusId, 1, 3000, skill.statusDuration, skill.statusTier)) + { + //If we need an extra action to show the status text + if (isAdditional) + action.AddStatusAction(target.actorId, skill.statusId); + } + else + action.worldMasterTextId = 32002;//Is this right? + } + + } + } } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs index 928aedd3..23fbf8a3 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs @@ -118,34 +118,33 @@ namespace FFXIVClassic_Map_Server.Actors return subpackets; } + //This might need more work + //I think there migh be something that ties mobs to parties + //and the client checks if any mobs are tied to the current party + //and bases the color on that. Adding mob to party obviously doesn't work + //Based on depictionjudge script: + //HATE_TYPE_NONE is for passive + //HATE_TYPE_ENGAGED is for aggroed mobs + //HATE_TYPE_ENGAGED_PARTY is for claimed mobs, client uses occupancy group to determine if mob is claimed by player's party + //for now i'm just going to assume that occupancygroup will be BattleNpc's currentparties when they're in combat, + //so if party isn't null, they're claimed. public SubPacket GetHateTypePacket(Player player) { - npcWork.hateType = 1; - + npcWork.hateType = NpcWork.HATE_TYPE_NONE; if (player != null) { if (aiContainer.IsEngaged()) { - npcWork.hateType = 2; - } + npcWork.hateType = NpcWork.HATE_TYPE_ENGAGED; - if (player.actorId == this.currentLockedTarget) - { - npcWork.hateType = NpcWork.HATE_TYPE_ENGAGED_PARTY; - } - else if (player.currentParty != null) - { - foreach (var memberId in ((Party)player.currentParty).members) + if (this.currentParty != null) { - if (this.currentLockedTarget == memberId) - { - npcWork.hateType = NpcWork.HATE_TYPE_ENGAGED_PARTY; - break; - } + npcWork.hateType = NpcWork.HATE_TYPE_ENGAGED_PARTY; } } } - var propPacketUtil = new ActorPropertyPacketUtil("npcWork", this); + npcWork.hateType = 2; + var propPacketUtil = new ActorPropertyPacketUtil("npcWork/hate", this); propPacketUtil.AddProperty("npcWork.hateType"); return propPacketUtil.Done()[0]; } @@ -198,7 +197,7 @@ namespace FFXIVClassic_Map_Server.Actors public override bool CanWeaponSkill(Character target, BattleCommand skill) { // todo: - return false; + return true; } public override bool CanUseAbility(Character target, BattleCommand ability) @@ -359,18 +358,18 @@ namespace FFXIVClassic_Map_Server.Actors lua.LuaEngine.CallLuaBattleFunction(this, "onAttack", this, state.GetTarget(), action.amount); } - public override void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors) + public override void OnCast(State state, BattleAction[] actions, BattleCommand spell, ref BattleAction[] errors) { - base.OnCast(state, actions, ref errors); + base.OnCast(state, actions, spell, ref errors); if (GetMobMod((uint)MobModifier.SpellScript) != 0) foreach (var action in actions) lua.LuaEngine.CallLuaBattleFunction(this, "onCast", this, zone.FindActorInArea(action.targetId), ((MagicState)state).GetSpell(), action); } - public override void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors) + public override void OnAbility(State state, BattleAction[] actions, BattleCommand ability, ref BattleAction[] errors) { - base.OnAbility(state, actions, ref errors); + base.OnAbility(state, actions, ability, ref errors); /* if (GetMobMod((uint)MobModifier.AbilityScript) != 0) @@ -379,9 +378,9 @@ namespace FFXIVClassic_Map_Server.Actors */ } - public override void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors) + public override void OnWeaponSkill(State state, BattleAction[] actions, BattleCommand skill, ref BattleAction[] errors) { - base.OnWeaponSkill(state, actions, ref errors); + base.OnWeaponSkill(state, actions, skill, ref errors); if (GetMobMod((uint)MobModifier.WeaponSkillScript) != 0) foreach (var action in actions) diff --git a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs index 31c9541d..a534f6d6 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs @@ -194,8 +194,8 @@ namespace FFXIVClassic_Map_Server.Actors subpackets.Add(CreateSpeedPacket()); subpackets.Add(CreateSpawnPositonPacket(0x0)); - if (isMapObj) - subpackets.Add(SetActorBGPropertiesPacket.BuildPacket(actorId, instance, layout)); + if (isMapObj) + subpackets.Add(SetActorBGPropertiesPacket.BuildPacket(actorId, instance, layout)); else subpackets.Add(CreateAppearancePacket()); @@ -245,7 +245,7 @@ namespace FFXIVClassic_Map_Server.Actors //Status Times for (int i = 0; i < charaWork.statusShownTime.Length; i++) { - if (charaWork.statusShownTime[i] != 0xFFFFFFFF) + if (charaWork.statusShownTime[i] != 0) propPacketUtil.AddProperty(String.Format("charaWork.statusShownTime[{0}]", i)); } diff --git a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs b/FFXIVClassic Map Server/actors/chara/player/Equipment.cs index 702af284..71c54389 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Equipment.cs @@ -163,6 +163,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); list[slot] = item; + owner.CalculateBaseStats();// RecalculateStats(); } public void ToggleDBWrite(bool flag) @@ -189,6 +190,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); list[slot] = null; + owner.RecalculateStats(); } private void SendEquipmentPackets(ushort equipSlot, InventoryItem item) diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 5ac7fd6d..691bf244 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -26,6 +26,7 @@ using FFXIVClassic_Map_Server.packets.send.actor.battle; using FFXIVClassic_Map_Server.actors.chara.ai.utils; using FFXIVClassic_Map_Server.actors.chara.ai.state; using FFXIVClassic_Map_Server.actors.chara.npc; +using FFXIVClassic_Map_Server.actors.chara; namespace FFXIVClassic_Map_Server.Actors { @@ -228,6 +229,7 @@ namespace FFXIVClassic_Map_Server.Actors this.aiContainer = new AIContainer(this, new PlayerController(this), null, new TargetFind(this)); allegiance = CharacterTargetingAllegiance.Player; + CalculateBaseStats(); } public List Create0x132Packets() @@ -361,7 +363,7 @@ namespace FFXIVClassic_Map_Server.Actors //Status Times for (int i = 0; i < charaWork.statusShownTime.Length; i++) { - if (charaWork.statusShownTime[i] != 0xFFFFFFFF) + if (charaWork.statusShownTime[i] != 0) propPacketUtil.AddProperty(String.Format("charaWork.statusShownTime[{0}]", i)); } @@ -614,6 +616,7 @@ namespace FFXIVClassic_Map_Server.Actors catch (Exception e) { this.SendMessage(SendMessagePacket.MESSAGE_TYPE_SYSTEM_ERROR, "[SendPacket]", "Unable to send packet."); + this.SendMessage(SendMessagePacket.MESSAGE_TYPE_SYSTEM_ERROR, "[SendPacket]", e.Message); } } @@ -1705,20 +1708,22 @@ namespace FFXIVClassic_Map_Server.Actors Party partyGroup = (Party) currentParty; - for (int i = 0; i < partyGroup.members.Count; i++) - { - if (partyGroup.members[i] == actorId) - { - partyGroup.members.RemoveAt(i); - break; - } - } + partyGroup.RemoveMember(actorId); + + //for (int i = 0; i < partyGroup.members.Count; i++) + //{ + // if (partyGroup.members[i] == actorId) + // { + // partyGroup.members.RemoveAt(i); + // break; + // } + //} //currentParty.members.Remove(this); if (partyGroup.members.Count == 0) Server.GetWorldManager().NoMembersInParty((Party)currentParty); - - currentParty = null; + + //currentParty = new Party(0, actorId); } public void IssueChocobo(byte appearanceId, string nameResponse) @@ -1842,27 +1847,23 @@ namespace FFXIVClassic_Map_Server.Actors //If the class we're equipping for is the current class, we can just look at charawork.command if(classId == charaWork.parameterSave.state_mainSkill[0]) - { hotbarSlot = FindFirstCommandSlotById(0); - } //Otherwise, we need to check the database. else - { hotbarSlot = (ushort) (Database.FindFirstCommandSlot(this, classId) + charaWork.commandBorder); - } EquipAbility(classId, commandId, hotbarSlot, printMessage); } //Add commandId to classId's hotbar at hotbarSlot. //If classId is not the current class, do it in the database - //hotbarSlot is 32-indexed + //hotbarSlot starts at 32 public void EquipAbility(byte classId, uint commandId, ushort hotbarSlot, bool printMessage = true) { var ability = Server.GetWorldManager().GetBattleCommand(commandId); - uint trueCommandId = commandId | 0xA0F00000; + uint trueCommandId = 0xA0F00000 + commandId; ushort lowHotbarSlot = (ushort)(hotbarSlot - charaWork.commandBorder); - ushort maxRecastTime = (ushort)ability.recastTimeSeconds; + ushort maxRecastTime = (ushort)(ability != null ? ability.maxRecastTimeSeconds : 5); uint recastEnd = Utils.UnixTimeStampUTC() + maxRecastTime; List slotsToUpdate = new List(); @@ -1889,6 +1890,7 @@ namespace FFXIVClassic_Map_Server.Actors //hotbarSlot 1 and 2 are 32-indexed. public void SwapAbilities(ushort hotbarSlot1, ushort hotbarSlot2) { + //0 indexed hotbar slots for saving to database and recast timers uint lowHotbarSlot1 = (ushort)(hotbarSlot1 - charaWork.commandBorder); uint lowHotbarSlot2 = (ushort)(hotbarSlot2 - charaWork.commandBorder); @@ -1906,10 +1908,12 @@ namespace FFXIVClassic_Map_Server.Actors charaWork.command[hotbarSlot2] = commandId; charaWork.parameterTemp.maxCommandRecastTime[lowHotbarSlot2] = recastMax; charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot2] = recastEnd; - - //Save changes - Database.EquipAbility(this, charaWork.parameterSave.state_mainSkill[0], (ushort)(lowHotbarSlot1), charaWork.command[hotbarSlot1], charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot1]); + //Save changes to both slots + Database.EquipAbility(this, GetCurrentClassOrJob(), (ushort)(lowHotbarSlot1), 0xA0F00000 ^ charaWork.command[hotbarSlot1], charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot1]); + Database.EquipAbility(this, GetCurrentClassOrJob(), (ushort)(lowHotbarSlot2), 0xA0F00000 ^ charaWork.command[hotbarSlot2], charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot2]); + + //Update slots on client List slotsToUpdate = new List(); slotsToUpdate.Add(hotbarSlot1); slotsToUpdate.Add(hotbarSlot2); @@ -1926,7 +1930,7 @@ namespace FFXIVClassic_Map_Server.Actors slotsToUpdate.Add(trueHotbarSlot); if(printMessage) - SendGameMessage(Server.GetWorldManager().GetActor(), 30604, 0x20, 0, commandId ^ 0xA0F00000); + SendGameMessage(Server.GetWorldManager().GetActor(), 30604, 0x20, 0, 0xA0F00000 ^ commandId); UpdateHotbar(slotsToUpdate); } @@ -1952,10 +1956,10 @@ namespace FFXIVClassic_Map_Server.Actors return firstSlot; } - private void UpdateHotbarTimer(uint commandId, uint recastTimeSeconds) + private void UpdateHotbarTimer(uint commandId, uint recastTimeMs) { ushort slot = FindFirstCommandSlotById(commandId); - charaWork.parameterSave.commandSlot_recastTime[slot - charaWork.commandBorder] = Utils.UnixTimeStampUTC(DateTime.Now.AddSeconds(recastTimeSeconds)); + charaWork.parameterSave.commandSlot_recastTime[slot - charaWork.commandBorder] = Utils.UnixTimeStampUTC(DateTime.Now.AddMilliseconds(recastTimeMs)); var slots = new List(); slots.Add(slot); UpdateRecastTimers(slots); @@ -2123,7 +2127,7 @@ namespace FFXIVClassic_Map_Server.Actors SendGameMessage(Server.GetWorldManager().GetActor(), 32539, 0x20, (uint)spell.id); return false; } - if (!IsValidTarget(target, spell.validTarget) || !spell.IsValidTarget(this, target)) + if (!IsValidTarget(target, spell.mainTarget) || !spell.IsValidMainTarget(this, target)) { // error packet is set in IsValidTarget return false; @@ -2141,29 +2145,36 @@ namespace FFXIVClassic_Map_Server.Actors SendGameMessage(Server.GetWorldManager().GetActor(), 32535, 0x20, (uint)skill.id); return false; } + if (target == null) { // Target does not exist. SendGameMessage(Server.GetWorldManager().GetActor(), 32511, 0x20, (uint)skill.id); return false; } + if (Utils.Distance(positionX, positionY, positionZ, target.positionX, target.positionY, target.positionZ) > skill.range) { // The target is out of range. SendGameMessage(Server.GetWorldManager().GetActor(), 32539, 0x20, (uint)skill.id); return false; } - if (!IsValidTarget(target, skill.validTarget) || !skill.IsValidTarget(this, target)) + + if (!IsValidTarget(target, skill.validTarget) || !skill.IsValidMainTarget(this, target)) { // error packet is set in IsValidTarget return false; } + return true; } public override void OnAttack(State state, BattleAction action, ref BattleAction error) { + var target = state.GetTarget(); + base.OnAttack(state, action, ref error); + // todo: switch based on main weap (also probably move this anim assignment somewhere else) action.animation = 0x19001000; if (error == null) @@ -2171,42 +2182,39 @@ namespace FFXIVClassic_Map_Server.Actors // melee attack animation //action.animation = 0x19001000; } - var target = state.GetTarget(); if (target is BattleNpc) { - ((BattleNpc)target).hateContainer.UpdateHate(this, action.amount); + ((BattleNpc)target).hateContainer.UpdateHate(this, action.enmity); } LuaEngine.GetInstance().OnSignal("playerAttack"); } - public override void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors) + public override void OnCast(State state, BattleAction[] actions, BattleCommand spell, ref BattleAction[] errors) { // todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap) - base.OnCast(state, actions, ref errors); - - var spell = ((MagicState)state).GetSpell(); + base.OnCast(state, actions, spell, ref errors); // todo: should just make a thing that updates the one slot cause this is dumb as hell - UpdateHotbarTimer(spell.id, spell.recastTimeSeconds); - LuaEngine.GetInstance().OnSignal("spellUse"); + UpdateHotbarTimer(spell.id, spell.recastTimeMs); + //LuaEngine.GetInstance().OnSignal("spellUse"); } - public override void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors) + public override void OnWeaponSkill(State state, BattleAction[] actions, BattleCommand skill, ref BattleAction[] errors) { // todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap) - base.OnWeaponSkill(state, actions, ref errors); - var skill = ((WeaponSkillState)state).GetWeaponSkill(); + base.OnWeaponSkill(state, actions, skill, ref errors); + // todo: should just make a thing that updates the one slot cause this is dumb as hell - UpdateHotbarTimer(skill.id, skill.recastTimeSeconds); + UpdateHotbarTimer(skill.id, skill.recastTimeMs); // todo: this really shouldnt be called on each ws? lua.LuaEngine.CallLuaBattleFunction(this, "onWeaponSkill", this, state.GetTarget(), skill); LuaEngine.GetInstance().OnSignal("weaponskillUse"); } - public override void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors) + public override void OnAbility(State state, BattleAction[] actions, BattleCommand ability, ref BattleAction[] errors) { - base.OnAbility(state, actions, ref errors); - + base.OnAbility(state, actions, ability, ref errors); + UpdateHotbarTimer(ability.id, ability.recastTimeMs); LuaEngine.GetInstance().OnSignal("abilityUse"); } @@ -2307,6 +2315,7 @@ namespace FFXIVClassic_Map_Server.Actors currentJob = jobId; BroadcastPacket(SetCurrentJobPacket.BuildPacket(actorId, jobId), true); Database.LoadHotbar(this); + SendCharaExpInfo(); } //Gets the id of the player's current job. If they aren't a job, gets the id of their class @@ -2328,5 +2337,66 @@ namespace FFXIVClassic_Map_Server.Actors updateFlags |= ActorUpdateFlags.HpTpMp; } + public void SetCombos(int comboId1 = 0, int comboId2 = 0) + { + SetCombos(new int[] { comboId1, comboId2 }); + } + + public void SetCombos(int[] comboIds) + { + Array.Copy(comboIds, playerWork.comboNextCommandId, 2); + + //If we're starting or continuing a combo chain, add the status effect and combo cost bonus + if (comboIds[0] != 0) + { + StatusEffect comboEffect = new StatusEffect(this, (uint) StatusEffectId.Combo, 1, 0, 13); + comboEffect.SetOverwritable(1); + statusEffects.AddStatusEffect(comboEffect, this, true); + playerWork.comboCostBonusRate = 1; + } + //Otherwise we're ending a combo, remove the status + else + { + statusEffects.RemoveStatusEffect(statusEffects.GetStatusEffectById((uint) StatusEffectId.Combo)); + playerWork.comboCostBonusRate = 0; + } + + ActorPropertyPacketUtil comboPropertyPacket = new ActorPropertyPacketUtil("playerWork/combo", this); + comboPropertyPacket.AddProperty($"playerWork.comboCostBonusRate"); + comboPropertyPacket.AddProperty($"playerWork.comboNextCommandId[{0}]"); + comboPropertyPacket.AddProperty($"playerWork.comboNextCommandId[{1}]"); + QueuePackets(comboPropertyPacket.Done()); + } + + public override void CalculateBaseStats() + { + base.CalculateBaseStats(); + //Add weapon property mod + var equip = GetEquipment(); + var mainHandItem = equip.GetItemAtSlot(Equipment.SLOT_MAINHAND); + var damageAttribute = 0; + var attackDelay = 3000; + var hitCount = 1; + GetAttackDelayMs(); + if (mainHandItem != null) + { + var mainHandWeapon = (Server.GetItemGamedata(mainHandItem.itemId) as WeaponItem); + damageAttribute = mainHandWeapon.damageAttributeType1; + attackDelay = (int) (mainHandWeapon.damageInterval * 1000); + hitCount = mainHandWeapon.frequency; + } + + var hasShield = equip.GetItemAtSlot(Equipment.SLOT_OFFHAND) != null ? 1 : 0; + SetMod((uint)Modifier.HasShield, hasShield); + + SetMod((uint)Modifier.AttackType, damageAttribute); + SetMod((uint)Modifier.AttackDelay, attackDelay); + SetMod((uint)Modifier.HitCount, hitCount); + } + public void SetCurrentJob(ushort jobId) + { + currentJob = jobId; + BroadcastPacket(SetCurrentJobPacket.BuildPacket(actorId, jobId), true); + } } } diff --git a/FFXIVClassic Map Server/actors/group/MonsterParty.cs b/FFXIVClassic Map Server/actors/group/MonsterParty.cs index b560fd38..511eca6d 100644 --- a/FFXIVClassic Map Server/actors/group/MonsterParty.cs +++ b/FFXIVClassic Map Server/actors/group/MonsterParty.cs @@ -17,8 +17,9 @@ namespace FFXIVClassic_Map_Server.actors.group public MonsterParty(ulong groupIndex, uint[] initialMonsterMembers) : base(groupIndex) { - for (int i = 0; i < initialMonsterMembers.Length; i++) - monsterMembers.Add(initialMonsterMembers[i]); + if(initialMonsterMembers != null) + for (int i = 0; i < initialMonsterMembers.Length; i++) + monsterMembers.Add(initialMonsterMembers[i]); } public void AddMember(uint memberId) diff --git a/FFXIVClassic Map Server/actors/group/Party.cs b/FFXIVClassic Map Server/actors/group/Party.cs index 385c5a77..58e438dd 100644 --- a/FFXIVClassic Map Server/actors/group/Party.cs +++ b/FFXIVClassic Map Server/actors/group/Party.cs @@ -81,6 +81,8 @@ namespace FFXIVClassic_Map_Server.actors.group { members.Remove(memberId); SendGroupPacketsAll(members); + if (members.Count == 0) + Server.GetWorldManager().NoMembersInParty(this); } } } diff --git a/FFXIVClassic Map Server/dataobjects/ItemData.cs b/FFXIVClassic Map Server/dataobjects/ItemData.cs index f72d73dc..0b9e5505 100644 --- a/FFXIVClassic Map Server/dataobjects/ItemData.cs +++ b/FFXIVClassic Map Server/dataobjects/ItemData.cs @@ -479,7 +479,7 @@ namespace FFXIVClassic_Map_Server.dataobjects public readonly short craftMagicProcessing; public readonly short harvestPotency; public readonly short harvestLimit; - public readonly byte frequency; + public readonly byte frequency; // hit count, 2 for h2h weapons public readonly short rate; public readonly short magicRate; public readonly short craftProcessControl; @@ -488,7 +488,7 @@ namespace FFXIVClassic_Map_Server.dataobjects public readonly short magicCritical; public readonly short parry; - public readonly int damageAttributeType1; + public readonly int damageAttributeType1; // 1 slashing, 2 piercing, 3 blunt, 4 projectile public readonly float damageAttributeValue1; public readonly int damageAttributeType2; public readonly float damageAttributeValue2; @@ -498,6 +498,7 @@ namespace FFXIVClassic_Map_Server.dataobjects public readonly short damagePower; public readonly float damageInterval; public readonly short ammoVirtualDamagePower; + public readonly float dps; public WeaponItem(MySqlDataReader reader) : base(reader) @@ -534,6 +535,7 @@ namespace FFXIVClassic_Map_Server.dataobjects damagePower = reader.GetInt16("damagePower"); damageInterval = reader.GetFloat("damageInterval"); ammoVirtualDamagePower = reader.GetInt16("ammoVirtualDamagePower"); + dps = damagePower / damageInterval;// this is wrong for bows, might need to store this in db because dps is used for weaponskill damage } } diff --git a/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs b/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs index 68c8952a..f4a377e9 100644 --- a/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs +++ b/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs @@ -19,7 +19,6 @@ namespace FFXIVClassic_Map_Server.dataobjects public void QueuePacket(SubPacket subpacket) { - //Temporary fix for r0 if(SendPacketQueue.Count == 1000) FlushQueuedSendPackets(); diff --git a/FFXIVClassic Map Server/lua/LuaEngine.cs b/FFXIVClassic Map Server/lua/LuaEngine.cs index d4f50f72..72f628ad 100644 --- a/FFXIVClassic Map Server/lua/LuaEngine.cs +++ b/FFXIVClassic Map Server/lua/LuaEngine.cs @@ -247,7 +247,7 @@ namespace FFXIVClassic_Map_Server.lua Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{functionName}] {e.Message}"); } DynValue res = new DynValue(); - DynValue r = script.Globals.Get(functionName); + // DynValue r = script.Globals.Get(functionName); if (!script.Globals.Get(functionName).IsNil()) { @@ -259,6 +259,82 @@ namespace FFXIVClassic_Map_Server.lua return -1; } + + public static void LoadBattleCommandScript(BattleCommand command, string folder) + { + string path = $"./scripts/commands/{folder}/{command.name}.lua"; + + if (File.Exists(path)) + { + var script = LoadGlobals(); + + try + { + script.DoFile(path); + } + catch (Exception e) + { + Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction {e.Message}"); + } + command.script = script; + } + else + { + path = $"./scripts/commands/{folder}/default.lua"; + //Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{command.name}] Unable to find script {path}"); + var script = LoadGlobals(); + + try + { + script.DoFile(path); + } + catch (Exception e) + { + Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction {e.Message}"); + } + + command.script = script; + } + } + + public static void LoadStatusEffectScript(StatusEffect effect) + { + string path = $"./scripts/effects/{effect.GetName()}.lua"; + + if (File.Exists(path)) + { + var script = LoadGlobals(); + + try + { + script.DoFile(path); + } + catch (Exception e) + { + Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction {e.Message}"); + } + effect.script = script; + } + else + { + path = $"./scripts/effects/default.lua"; + //Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{command.name}] Unable to find script {path}"); + var script = LoadGlobals(); + + try + { + script.DoFile(path); + } + catch (Exception e) + { + Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction {e.Message}"); + } + + effect.script = script; + } + } + + public static string GetScriptPath(Actor target) { if (target is Player) diff --git a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleAction.cs b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleAction.cs index 620f3c34..5a1cc77c 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleAction.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleAction.cs @@ -1,5 +1,10 @@ using FFXIVClassic.Common; using System; +using System.Collections.Generic; +using FFXIVClassic_Map_Server.actors.chara.ai; +using FFXIVClassic_Map_Server.actors.chara.ai.utils; +using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server.packets.send.actor.battle; namespace FFXIVClassic_Map_Server.packets.send.actor.battle { @@ -9,13 +14,18 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle { //All HitEffects have the last byte 0x8 HitEffectType = 8 << 24, + //Status effects use 32 << 24 + StatusEffectType = 32 << 24, + //Magic effects use 48 << 24 + MagicEffectType = 48 << 24, //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 | HitEffectType, - RecoilLv2 = 1 << 0 | HitEffectType, - RecoilLv3 = 1 << 1 | HitEffectType, + //These also have a visual effect with heals but in reverse. RecoilLv1 has a large effect, Lv3 has none. Crit is very large + 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, @@ -24,10 +34,19 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle //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 | HitEffectType, - HitVisual2 = 1 << 3 | HitEffectType, - HitVisual3 = 1 << 4 | HitEffectType, - HitVisual4 = 1 << 5 | HitEffectType, + + //HitVisual is probably based on attack property. + //HitVisual1 is for slashing attacks + //HitVisual2 is for piercing attacks + //HitVisual1 | Hitvisual2 is for blunt attacks + //HitVisual3 is for projectile attacks + //Basically take the attack property of a weapon and shift it left 2 + //For auto attacks attack property is weapon's damageAttributeType1 + //Still not totally sure how this works with weaponskills or what hitvisual4 or the other combinations are for + 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. @@ -40,19 +59,27 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle Shell = 1 << 7 | HitEffectType, ProtectShellSpecial = Protect | Shell, - //Unknown = 1 << 8, -- Not sure what this flag does. + // Required for heal text to be blue, not sure if that's all it's used for + Heal = 1 << 8, //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 | HitEffectType, - HitEffect2 = 1 << 10 | HitEffectType, //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 | HitEffectType, - HitEffect4 = 1 << 12 | HitEffectType, - HitEffect5 = 1 << 13 | HitEffectType, + HitEffect1 = 1 << 9, + HitEffect2 = 1 << 10, //Plays the standard hit visual effect, but with no sound if used alone. + HitEffect3 = 1 << 11, //Yellow effect, crit? + HitEffect4 = 1 << 12, //Plays the blocking animation + HitEffect5 = 1 << 13, GustyHitEffect = HitEffect3 | HitEffect2, GreenTintedHitEffect = HitEffect4 | HitEffect1, + //For specific animations + Miss = 0, + Evade = HitEffect1, + Hit = HitEffect1 | HitEffect2, + Parry = Hit | HitEffect3, + Block = HitEffect4, + Crit = HitEffect3, + //Knocks you back away from the attacker. KnockbackLv1 = HitEffect4 | HitEffect2 | HitEffect1, KnockbackLv2 = HitEffect4 | HitEffect3, @@ -80,10 +107,10 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle //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 | HitEffectType, - SkillCombo2 = 1 << 16 | HitEffectType, + SkillCombo1 = 1 << 15, + SkillCombo2 = 1 << 16, SkillCombo3 = SkillCombo1 | SkillCombo2, - SkillCombo4 = 1 << 17 | HitEffectType + SkillCombo4 = 1 << 17 //Flags beyond here are unknown/untested. } @@ -100,19 +127,45 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle Left = 1 << 3 } + public enum HitType : ushort + { + Miss = 0, + Evade = 1, + Parry = 2, + Block = 3, + Resist = 4, + Hit = 5, + Crit = 6 + } + + public enum BattleActionType + { + None = 0, + AttackPhysical = 1, + AttackMagic = 2, + Heal = 3, + Status = 4 + } + class BattleAction { public uint targetId; public ushort amount; + public ushort enmity; //Seperate from amount for abilities that cause a different amount of enmity than damage public ushort worldMasterTextId; - public uint effectId; - public byte param; - public byte unknown; + public uint effectId; //Impact effect, damage/heal/status numbers or name + public byte param; //Which side the battle action is coming from + public byte hitNum; //Which hit in a sequence of hits this is + public HitType hitType; + + //Need a list of actions for commands that may both deal damage and inflict status effects + public List actionsList; /// /// this field is not actually part of the packet struct /// public uint animation; + public BattleActionType battleActionType; public BattleAction(uint targetId, ushort worldMasterTextId, uint effectId, ushort amount = 0, byte param = 0, byte unknown = 1) { @@ -121,7 +174,38 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle this.effectId = effectId; this.amount = amount; this.param = param; - this.unknown = unknown; + this.hitNum = unknown; + this.hitType = HitType.Hit; + this.enmity = amount; + this.actionsList = new List(); + this.battleActionType = BattleActionType.None; + actionsList.Add(this); + } + + public void AddStatusAction(uint targetId, uint effectId) + { + actionsList.Add(new BattleAction(targetId, 30328, effectId | (uint) HitEffect.StatusEffectType)); + } + + public void AddHealAction(uint targetId, ushort amount) + { + var a = new BattleAction(targetId, 30320, (uint)(HitEffect.MagicEffectType | HitEffect.RecoilLv3 | HitEffect.Heal), amount); + actionsList.Add(a); + } + + public void CalcHitType(Character caster, Character target, BattleCommand skill) + { + BattleUtils.CalcHitType(caster, target, skill, this); + } + + public void TryStatus(Character caster, Character target, BattleCommand skill, bool isAdditional = true) + { + BattleUtils.TryStatus(caster, target, skill, this, isAdditional); + } + + public List GetAllActions() + { + return actionsList; } } } diff --git a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX01Packet.cs b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX01Packet.cs index 006a2740..1a129183 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX01Packet.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX01Packet.cs @@ -2,7 +2,7 @@ using System; using System.IO; -namespace FFXIVClassic_Map_Server.packets.send.actor.battle +namespace FFXIVClassic_Map_Server.packets.send.actor.battle { // see xtx_command enum BattleActionX01PacketCommand : ushort @@ -25,7 +25,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle using (BinaryWriter binWriter = new BinaryWriter(mem)) { binWriter.Write((UInt32)sourceActorId); - + binWriter.Write((UInt32)animationId); //Missing... last value is float, string in here as well? @@ -39,15 +39,15 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle binWriter.Write((UInt16)action.amount); binWriter.Write((UInt16)action.worldMasterTextId); + Program.Log.Info(action.worldMasterTextId); binWriter.Write((UInt32)action.effectId); - binWriter.Write((Byte)action.param); binWriter.Write((Byte)1); //? } } - + new SubPacket(OPCODE, sourceActorId, data).DebugPrintSubPacket(); return new SubPacket(OPCODE, sourceActorId, data); } } -} +} \ No newline at end of file diff --git a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX10Packet.cs b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX10Packet.cs index 04403201..f89b2801 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX10Packet.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX10Packet.cs @@ -57,7 +57,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle binWriter.Seek(0xAA, SeekOrigin.Begin); for (int i = 0; i < max; i++) - binWriter.Write((Byte)actionList[listOffset + i].unknown); + binWriter.Write((Byte)actionList[listOffset + i].hitNum); listOffset += max; } @@ -112,7 +112,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle binWriter.Seek(0xAA, SeekOrigin.Begin); for (int i = 0; i < max; i++) - binWriter.Write((Byte)actionList[listOffset + i].unknown); + binWriter.Write((Byte)actionList[listOffset + i].hitNum); listOffset += max; } diff --git a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs index d2411857..4dbfa17e 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs @@ -57,7 +57,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle binWriter.Seek(0x112, SeekOrigin.Begin); for (int i = 0; i < max; i++) - binWriter.Write((Byte)actionList[listOffset + i].unknown); + binWriter.Write((Byte)actionList[listOffset + i].hitNum); listOffset += max; } @@ -86,33 +86,33 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle //Missing... last value is float, string in here as well? binWriter.Seek(0x20, SeekOrigin.Begin); - binWriter.Write((UInt32)max); //Num actions + binWriter.Write((UInt32)actionList.Count); //Num actions binWriter.Write((UInt16)commandId); binWriter.Write((UInt16)0x810); //? - binWriter.Seek(0x58, SeekOrigin.Begin); + binWriter.Seek(0x28, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((UInt32)actionList[listOffset + i].targetId); - binWriter.Seek(0xA0, SeekOrigin.Begin); + binWriter.Seek(0x70, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((UInt16)actionList[listOffset + i].amount); - binWriter.Seek(0xC4, SeekOrigin.Begin); + binWriter.Seek(0x94, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((UInt16)actionList[listOffset + i].worldMasterTextId); - binWriter.Seek(0xE8, SeekOrigin.Begin); + binWriter.Seek(0xB8, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((UInt32)actionList[listOffset + i].effectId); - binWriter.Seek(0x130, SeekOrigin.Begin); + binWriter.Seek(0x100, SeekOrigin.Begin); for (int i = 0; i < max; i++) binWriter.Write((Byte)actionList[listOffset + i].param); - binWriter.Seek(0x142, SeekOrigin.Begin); + binWriter.Seek(0x112, SeekOrigin.Begin); for (int i = 0; i < max; i++) - binWriter.Write((Byte)actionList[listOffset + i].unknown); + binWriter.Write((Byte)actionList[listOffset + i].hitNum); listOffset += max; } diff --git a/data/scripts/ability.lua b/data/scripts/ability.lua new file mode 100644 index 00000000..be4c0dcb --- /dev/null +++ b/data/scripts/ability.lua @@ -0,0 +1,60 @@ +-- todo: add enums for status effects in global.lua +require("global") +require("battleutils") + +--[[ + statId - see BattleTemp.cs + modifier - Modifier.Intelligence, Modifier.Mind (see Modifier.cs) + multiplier - + ]] +function HandleHealingSkill(caster, target, skill, action, statId, modifierId, multiplier, baseAmount) + potency = potency or 1.0; + healAmount = baseAmount; + + -- todo: shit based on mnd + local mind = caster.GetMod(Modifier.Mind); +end; + +function HandleAttackSkill(caster, target, skill, action, statId, modifierId, multiplier, baseAmount) + -- todo: actually handle this + damage = baseAmount or math.random(1,10) * 10; + + return damage; +end; + +function HandleStoneskin(caster, target, skill, action, statId, modifierId, damage) + --[[ + if target.statusEffects.HasStatusEffect(StatusEffect.Stoneskin) then + -- todo: damage reduction + return true; + end; + ]] + return false; +end; + +--For abilities that inflict statuses, like aegis boon or taunt +function onStatusAbilityFinish(caster, target, skill, action) + action.battleActionType = BattleActionType.Status; + action.CalcHitType(caster, target, skill); + action.TryStatus(caster, target, skill, false); + + return action.amount; +end; + +function onAttackAbilityFinish(caster, target, skill, action) + action.battleActionType = BattleActionType.AttackPhysical; + local damage = math.random(50, 150); + action.amount = damage; + action.CalcHitType(caster, target, skill); + + return action.amount; +end; + +function onHealAbilityFinish(caster, target, skill, action) + action.battleActionType = BattleActionType.Heal; + local amount = math.random(150, 250); + action.amount = amount; + action.CalcHitType(caster, target, skill); + action.TryStatus(caster, target, skill, true); + return action.amount; +end; \ No newline at end of file diff --git a/data/scripts/ally.lua b/data/scripts/ally.lua index c90ae2b8..e1e06d30 100644 --- a/data/scripts/ally.lua +++ b/data/scripts/ally.lua @@ -34,23 +34,27 @@ function allyGlobal.onDespawn(ally) end ---tryAggro serves the same purpose for now, keeping this around just in case function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget) + print("helpPlayers"); if contentGroupCharas and not ally.IsEngaged() then + print("contentGroup exists"); for chara in contentGroupCharas do + print("looping"); if chara then -- probably a player, or another ally -- todo: queue support actions, heal, try pull hate off player etc - if chara:IsPlayer() then + if chara.IsPlayer() then + print("chara is a player"); -- do stuff if not ally.IsEngaged() then if chara.IsEngaged() then allyGlobal.EngageTarget(ally, chara.target, nil); break; end - end + end elseif chara.IsMonster() and chara.IsEngaged() then if not ally.IsEngaged() then + print("Engaging monster that is engaged"); allyGlobal.EngageTarget(ally, chara, nil); break; end @@ -60,7 +64,6 @@ function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget) end end ---Iterate over characters in contentGroup, if a player is in combat, assist them. function allyGlobal.tryAggro(ally, contentGroupCharas) local count = 0; if contentGroupCharas and not ally.IsEngaged() then @@ -76,14 +79,12 @@ function allyGlobal.tryAggro(ally, contentGroupCharas) break; end end - - --[[ elseif contentGroupCharas[i].IsMonster() and contentGroupCharas[i].IsEngaged() then if not ally.IsEngaged() then print("Engaging monster that is engaged"); allyGlobal.EngageTarget(ally, contentGroupCharas[i], nil); break; - end]] + end end end end @@ -109,6 +110,7 @@ function allyGlobal.EngageTarget(ally, target, contentGroupCharas) end end elseif target then + print("Engaging"); ally.Engage(target) ally.hateContainer.AddBaseHate(target); end diff --git a/data/scripts/battlenpc.lua b/data/scripts/battlenpc.lua new file mode 100644 index 00000000..542e0aae --- /dev/null +++ b/data/scripts/battlenpc.lua @@ -0,0 +1,175 @@ +local initClassItems, initRaceItems; + +function onBeginLogin(player) + --New character, set the initial quest + if (player:GetPlayTime(false) == 0) then + initialTown = player:GetInitialTown(); + + if (initialTown == 1 and player:HasQuest(110001) == false) then + player:AddQuest(110001); + player:SetHomePoint(1280001); + elseif (initialTown == 2 and player:HasQuest(110005) == false) then + player:AddQuest(110005); + player:SetHomePoint(1280061); + elseif (initialTown == 3 and player:HasQuest(110009) == false) then + player:AddQuest(110009); + player:SetHomePoint(1280031); + end + + end + + --For Opening. Set Director and reset position incase d/c + if (player:HasQuest(110001) == true and player:GetZoneID() == 193) then + director = player:GetZone():CreateDirector("OpeningDirector", false); + player:AddDirector(director); + director:StartDirector(true); + player:SetLoginDirector(director); + player:KickEvent(director, "noticeEvent", true); + + player.positionX = 0.016; + player.positionY = 10.35; + player.positionZ = -36.91; + player.rotation = 0.025; + player:GetQuest(110001):ClearQuestData(); + player:GetQuest(110001):ClearQuestFlags(); + elseif (player:HasQuest(110005) == true and player:GetZoneID() == 166) then + director = player:GetZone():CreateDirector("OpeningDirector", false); + player:AddDirector(director); + director:StartDirector(false); + player:SetLoginDirector(director); + player:KickEvent(director, "noticeEvent", true); + + player.positionX = 369.5434; + player.positionY = 4.21; + player.positionZ = -706.1074; + player.rotation = -1.26721; + player:GetQuest(110005):ClearQuestData(); + player:GetQuest(110005):ClearQuestFlags(); + elseif (player:HasQuest(110009) == true and player:GetZoneID() == 184) then + --director = player:GetZone():CreateDirector("OpeningDirector", false); + --player:AddDirector(director); + --director:StartDirector(false); + --player:SetLoginDirector(director); + --player:KickEvent(director, "noticeEvent", true); + -- + player.positionX = 5.364327; + player.positionY = 196.0; + player.positionZ = 133.6561; + player.rotation = -2.849384; + player:GetQuest(110009):ClearQuestData(); + player:GetQuest(110009):ClearQuestFlags(); + end +end + +function onLogin(player) + + if (player:GetPlayTime(false) == 0) then + player:SendMessage(0x1D,"",">PlayTime == 0, new player!"); + + initClassItems(player); + initRaceItems(player); + + player:SavePlayTime(); + end + +end + +function initClassItems(player) + + local slotTable; + local invSlotTable; + + --DoW + if (player.charaWork.parameterSave.state_mainSkill[0] == 2) then --PUG + player:GetInventory(0):AddItem({4020001, 8030701, 8050728, 8080601, 8090307}); + player:GetEquipment():SetEquipment({0, 10, 12, 14, 15},{0, 1, 2, 3, 4}); + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 3) then --GLA + player:GetInventory(0):AddItem({4030010, 8031120, 8050245, 8080601, 8090307}); + player:GetEquipment():SetEquipment({0, 10, 12, 14, 15},{0, 1, 2, 3, 4}); + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 4) then --MRD + player:GetInventory(0):AddItem({4040001, 8011001, 8050621, 8070346, 8090307}); + player:GetEquipment():SetEquipment({0, 8, 12, 13, 15},{0, 1, 2, 3, 4}); + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 7) then --ARC + player:GetInventory(0):AddItem({4070001, 8030601, 8050622, 8080601, 8090307}); + player:GetEquipment():SetEquipment({0, 10, 12, 14, 15},{0, 1, 2, 3, 4}); + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 8) then --LNC + player:GetInventory(0):AddItem({4080201, 8030801, 8051015, 8080501, 8090307}); + player:GetEquipment():SetEquipment({0, 10, 12, 14, 15},{0, 1, 2, 3, 4}); + --DoM + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 22) then --THM + player:GetInventory(0):AddItem({5020001, 8030245, 8050346, 8080346, 8090208}); + player:GetEquipment():SetEquipment({0, 10, 12, 14, 15},{0, 1, 2, 3, 4}); + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 23) then --CNJ + player:GetInventory(0):AddItem({5030101, 8030445, 8050031, 8080246, 8090208}); + player:GetEquipment():SetEquipment({0, 10, 12, 14, 15},{0, 1, 2, 3, 4}); + + --DoH + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 29) then -- + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 30) then -- + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 31) then -- + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 32) then -- + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 33) then -- + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 34) then -- + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 35) then -- + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 36) then -- + + --DoL + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 39) then --MIN + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 40) then --BTN + elseif (player.charaWork.parameterSave.state_mainSkill[0] == 41) then --FSH + end + +end + +function initRaceItems(player) + + if (player.playerWork.tribe == 1) then --Hyur Midlander Male + player:GetInventory(0):AddItem(8040001); + player:GetInventory(0):AddItem(8060001); + elseif (player.playerWork.tribe == 2) then --Hyur Midlander Female + player:GetInventory(0):AddItem(8040002); + player:GetInventory(0):AddItem(8060002); + elseif (player.playerWork.tribe == 3) then --Hyur Highlander Male + player:GetInventory(0):AddItem(8040003); + player:GetInventory(0):AddItem(8060003); + elseif (player.playerWork.tribe == 4) then --Elezen Wildwood Male + player:GetInventory(0):AddItem(8040004); + player:GetInventory(0):AddItem(8060004); + elseif (player.playerWork.tribe == 5) then --Elezen Wildwood Female + player:GetInventory(0):AddItem(8040006); + player:GetInventory(0):AddItem(8060006); + elseif (player.playerWork.tribe == 6) then --Elezen Duskwight Male + player:GetInventory(0):AddItem(8040005); + player:GetInventory(0):AddItem(8060005); + elseif (player.playerWork.tribe == 7) then --Elezen Duskwight Female + player:GetInventory(0):AddItem(8040007); + player:GetInventory(0):AddItem(8060007); + elseif (player.playerWork.tribe == 8) then --Lalafell Plainsfolk Male + player:GetInventory(0):AddItem(8040008); + player:GetInventory(0):AddItem(8060008); + elseif (player.playerWork.tribe == 9) then --Lalafell Plainsfolk Female + player:GetInventory(0):AddItem(8040010); + player:GetInventory(0):AddItem(8060010); + elseif (player.playerWork.tribe == 10) then --Lalafell Dunesfolk Male + player:GetInventory(0):AddItem(8040009); + player:GetInventory(0):AddItem(8060009); + elseif (player.playerWork.tribe == 11) then --Lalafell Dunesfolk Female + player:GetInventory(0):AddItem(8040011); + player:GetInventory(0):AddItem(8060011); + elseif (player.playerWork.tribe == 12) then --Miqo'te Seekers of the Sun + player:GetInventory(0):AddItem(8040012); + player:GetInventory(0):AddItem(8060012); + elseif (player.playerWork.tribe == 13) then --Miqo'te Seekers of the Moon + player:GetInventory(0):AddItem(8040013); + player:GetInventory(0):AddItem(8060013); + elseif (player.playerWork.tribe == 14) then --Roegadyn Sea Wolf + player:GetInventory(0):AddItem(8040014); + player:GetInventory(0):AddItem(8060014); + elseif (player.playerWork.tribe == 15) then --Roegadyn Hellsguard + player:GetInventory(0):AddItem(8040015); + player:GetInventory(0):AddItem(8060015); + end + + player:GetEquipment():SetEquipment({9, 11},{5,6}); + +end \ No newline at end of file diff --git a/data/scripts/battleutils.lua b/data/scripts/battleutils.lua new file mode 100644 index 00000000..c9c1bdc9 --- /dev/null +++ b/data/scripts/battleutils.lua @@ -0,0 +1,8 @@ +BattleActionType = +{ + None = 0, + AttackPhysical = 1, + AttackMagic = 2, + Heal = 3, + Status = 4 +} \ No newline at end of file diff --git a/data/scripts/commands/Ability.lua b/data/scripts/commands/Ability.lua index e1704149..47b04053 100644 --- a/data/scripts/commands/Ability.lua +++ b/data/scripts/commands/Ability.lua @@ -14,14 +14,6 @@ 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 - player.Ability(command.actorId, targetActor); player:endEvent(); diff --git a/data/scripts/commands/AbilityCure.lua b/data/scripts/commands/AbilityCure.lua new file mode 100644 index 00000000..98e2eec7 --- /dev/null +++ b/data/scripts/commands/AbilityCure.lua @@ -0,0 +1,5 @@ +require("global") + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + +end \ No newline at end of file diff --git a/data/scripts/commands/AttackAbility.lua b/data/scripts/commands/AttackAbility.lua new file mode 100644 index 00000000..47b04053 --- /dev/null +++ b/data/scripts/commands/AttackAbility.lua @@ -0,0 +1,20 @@ +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) + player.Ability(command.actorId, targetActor); + player:endEvent(); + +end \ No newline at end of file diff --git a/data/scripts/commands/CureMagic.lua b/data/scripts/commands/CureMagic.lua new file mode 100644 index 00000000..a349c17a --- /dev/null +++ b/data/scripts/commands/CureMagic.lua @@ -0,0 +1,5 @@ +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + player.Cast(command.actorId, targetActor); + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/CuregaMagic.lua b/data/scripts/commands/CuregaMagic.lua new file mode 100644 index 00000000..a349c17a --- /dev/null +++ b/data/scripts/commands/CuregaMagic.lua @@ -0,0 +1,5 @@ +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + player.Cast(command.actorId, targetActor); + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/DevideAttackWeaponSkill.lua b/data/scripts/commands/DevideAttackWeaponSkill.lua new file mode 100644 index 00000000..3a0e8ff2 --- /dev/null +++ b/data/scripts/commands/DevideAttackWeaponSkill.lua @@ -0,0 +1,26 @@ +require ("global") +require ("utils") + +--[[ + +AttackWeaponSkill Script + +Finds the correct weaponskill subscript to fire when a weaponskill actor is activated. + +--]] + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + + --Are they in active mode? + if (player:GetState() != 2) then + player:SendGameMessage(GetWorldMaster(), 32503, 0x20); + player:endEvent(); + return; + end + + if not player.aiContainer.IsEngaged() then + player.Engage(targetActor); + end; + player.WeaponSkill(command.actorId, targetActor); + player:endEvent(); +end; \ No newline at end of file diff --git a/data/scripts/commands/EffectMagic.lua b/data/scripts/commands/EffectMagic.lua new file mode 100644 index 00000000..a349c17a --- /dev/null +++ b/data/scripts/commands/EffectMagic.lua @@ -0,0 +1,5 @@ +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + player.Cast(command.actorId, targetActor); + + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/commands/EquipAbilityCommand.lua b/data/scripts/commands/EquipAbilityCommand.lua index 6b2251d8..6127a8cf 100644 --- a/data/scripts/commands/EquipAbilityCommand.lua +++ b/data/scripts/commands/EquipAbilityCommand.lua @@ -61,7 +61,7 @@ function onEventStarted(player, equipAbilityWidget, triggername, slot, commandid player:SwapAbilities(oldSlot, slot + player.charaWork.commandBorder); else local tslot = slot + player.charaWork.commandBorder; - player:EquipAbility(player.GetJob(), commandid, tslot, true); + player:EquipAbility(player.GetCurrentClassOrJob(), commandid, tslot, true); end --Unequip @@ -70,7 +70,7 @@ function onEventStarted(player, equipAbilityWidget, triggername, slot, commandid ability = worldManager.GetBattleCommand(commandid); --Is the ability a part of the player's current class? --This check isn't correct because of jobs having different ids - local classId = player:GetJob(); + local classId = player:GetClass(); local jobId = player:ConvertClassIdToJobId(classId); if(ability.job == classId or ability.job == jobId) then diff --git a/data/scripts/commands/EquipCommand.lua b/data/scripts/commands/EquipCommand.lua index 966939ac..210ea0ea 100644 --- a/data/scripts/commands/EquipCommand.lua +++ b/data/scripts/commands/EquipCommand.lua @@ -145,6 +145,12 @@ function equipItem(player, equipSlot, item) --Item Equipped message player:SendGameMessage(player, worldMaster, 30601, 0x20, equipSlot+1, item.itemId, item.quality, 0, 0, 1); + --Load gearset for new class and begin class change + if (classId ~= nil) then + loadGearset(player, classId); + player:DoClassChange(classId); + end + player:GetEquipment():Equip(equipSlot, item); if (equipSlot == EQUIPSLOT_MAINHAND and gItem:IsNailWeapon() == false) then graphicSlot = GRAPHICSLOT_MAINHAND; @@ -170,14 +176,7 @@ function equipItem(player, equipSlot, item) elseif (equipSlot == EQUIPSLOT_EARS) then player:GraphicChange(GRAPHICSLOT_R_EAR, item); player:GraphicChange(GRAPHICSLOT_L_EAR, item); - end - - --Load gearset for new class and begin class change - if (classId ~= nil) then - loadGearset(player, classId); - player:DoClassChange(classId); - end - + end end end diff --git a/data/scripts/commands/ShotCommand.lua b/data/scripts/commands/ShotCommand.lua new file mode 100644 index 00000000..8397d8c9 --- /dev/null +++ b/data/scripts/commands/ShotCommand.lua @@ -0,0 +1,15 @@ +require ("global") +require ("utils") + +--[[ + +AttackWeaponSkill Script + +Finds the correct weaponskill subscript to fire when a weaponskill actor is activated. + +--]] + +function onEventStarted(player, command, triggerName, arg1, arg2, arg3, arg4, targetActor, arg5, arg6, arg7, arg8) + player.Ability(command.actorId, targetActor); + player:endEvent(); +end; \ No newline at end of file diff --git a/data/scripts/commands/SongMagic.lua b/data/scripts/commands/SongMagic.lua new file mode 100644 index 00000000..b5538141 --- /dev/null +++ b/data/scripts/commands/SongMagic.lua @@ -0,0 +1,19 @@ +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) + player.Cast(command.actorId, targetActor); + player:endEvent(); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/default.lua b/data/scripts/commands/ability/default.lua new file mode 100644 index 00000000..4e71a193 --- /dev/null +++ b/data/scripts/commands/ability/default.lua @@ -0,0 +1,14 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, skill) + return 0; +end; + +function onAbilityStart(caster, target, skill) + return 0; +end; + +function onAbilityFinish(caster, target, skill, action) + return onStatusAbilityFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/dragonfire_dive.lua b/data/scripts/commands/ability/dragonfire_dive.lua new file mode 100644 index 00000000..91bf8017 --- /dev/null +++ b/data/scripts/commands/ability/dragonfire_dive.lua @@ -0,0 +1,14 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onAbilityFinish(caster, target, skill, action) + return onAttackAbilityFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/invigorate.lua b/data/scripts/commands/ability/invigorate.lua new file mode 100644 index 00000000..dc7b46af --- /dev/null +++ b/data/scripts/commands/ability/invigorate.lua @@ -0,0 +1,14 @@ +require("global"); +require("Ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onAbilityFinish(caster, target, ability, action) + return onStatusAbilityFinish(caster, target, ability, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/jump.lua b/data/scripts/commands/ability/jump.lua new file mode 100644 index 00000000..91bf8017 --- /dev/null +++ b/data/scripts/commands/ability/jump.lua @@ -0,0 +1,14 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onAbilityFinish(caster, target, skill, action) + return onAttackAbilityFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/light_shot.lua b/data/scripts/commands/ability/light_shot.lua new file mode 100644 index 00000000..91bf8017 --- /dev/null +++ b/data/scripts/commands/ability/light_shot.lua @@ -0,0 +1,14 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onAbilityFinish(caster, target, skill, action) + return onAttackAbilityFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/second_wind.lua b/data/scripts/commands/ability/second_wind.lua new file mode 100644 index 00000000..7d629434 --- /dev/null +++ b/data/scripts/commands/ability/second_wind.lua @@ -0,0 +1,14 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onAbilityFinish(caster, target, ability, action) + return onHealAbilityFinish(caster, target, ability, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/gm/addtoparty.lua b/data/scripts/commands/gm/addtoparty.lua new file mode 100644 index 00000000..fe01fb46 --- /dev/null +++ b/data/scripts/commands/gm/addtoparty.lua @@ -0,0 +1,29 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "sss", + description = +[[ +Adds target to party +]] +} + +function onTrigger(player, argc) + local sender = "[addtoparty] "; + + if player then + if player.target then + print("hi") + local id = player.target.actorId + print("hi") + player.currentParty:AddMember(id); + player.target.currentParty = player.currentParty; + print("hi") + else + print(sender.." no target") + end + else + print(sender.." no player"); + end; +end; \ No newline at end of file diff --git a/data/scripts/commands/gm/eaction.lua b/data/scripts/commands/gm/eaction.lua index 597c4952..1c6d9cb9 100644 --- a/data/scripts/commands/gm/eaction.lua +++ b/data/scripts/commands/gm/eaction.lua @@ -11,7 +11,7 @@ Equips in the first open slot without checking if you can. } function onTrigger(player, argc, commandid) - local sender = "[givegil] "; + local sender = "[eaction] "; print(commandid); if name then diff --git a/data/scripts/commands/gm/graphic.lua b/data/scripts/commands/gm/graphic.lua index 33e70754..1a813b13 100644 --- a/data/scripts/commands/gm/graphic.lua +++ b/data/scripts/commands/gm/graphic.lua @@ -21,11 +21,14 @@ function onTrigger(player, argc, slot, wId, eId, vId, cId) cId = tonumber(cId) or 0; if player and argc > 0 then - player:GraphicChange(slot, wId, eId, vId, cId); + if argc > 2 then + player:GraphicChange(slot, wId, eId, vId, cId); + player:SendMessage(messageID, sender, string.format("Changing appearance on slot %u", slot)); + else + player:GraphicChange(slot, wId); + end player:SendAppearance(); - player:SendMessage(messageID, sender, string.format("Changing appearance on slot %u", slot)); else player:SendMessage(messageID, sender, "No parameters sent! Usage: "..properties.description); end; - end; \ No newline at end of file diff --git a/data/scripts/commands/gm/setappearance.lua b/data/scripts/commands/gm/setappearance.lua new file mode 100644 index 00000000..88ee7869 --- /dev/null +++ b/data/scripts/commands/gm/setappearance.lua @@ -0,0 +1,25 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "s", + description = +[[ +Changes appearance for equipment with given parameters. +!graphic +]], +} + +function onTrigger(player, argc, appearanceId) + local messageID = MESSAGE_TYPE_SYSTEM_ERROR; + local sender = "[setappearance] "; + + app = tonumber(appearanceId) or 0; + player:SendMessage(messageID, sender, string.format("appearance %u", app)); + + if player and player.target then + player.target.ChangeNpcAppearance(app); + player:SendMessage(messageID, sender, string.format("appearance %u", app)); + end; + +end; \ No newline at end of file diff --git a/data/scripts/commands/gm/setjob.lua b/data/scripts/commands/gm/setjob.lua new file mode 100644 index 00000000..b6a78e31 --- /dev/null +++ b/data/scripts/commands/gm/setjob.lua @@ -0,0 +1,21 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "sss", + description = +[[ +Adds experience to player or . +!giveexp | +!giveexp | +]], +} + +function onTrigger(player, argc, jobId) + local sender = "[setjob] "; + + jobId = tonumber(jobId) + if player then + player:SetCurrentJob(jobId); + end; +end; \ No newline at end of file diff --git a/data/scripts/commands/gm/setproc.lua b/data/scripts/commands/gm/setproc.lua new file mode 100644 index 00000000..b8aa1e3c --- /dev/null +++ b/data/scripts/commands/gm/setproc.lua @@ -0,0 +1,18 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "sss", + description = +[[ +Adds experience to player or . +!giveexp | +!giveexp | +]], +} + +function onTrigger(player, argc, procid) + local sender = "[giveexp] "; + local pid = tonumber(procid) + player:SetProc(pid, true); +end; \ No newline at end of file diff --git a/data/scripts/commands/gm/settp.lua b/data/scripts/commands/gm/settp.lua new file mode 100644 index 00000000..37ae85fa --- /dev/null +++ b/data/scripts/commands/gm/settp.lua @@ -0,0 +1,27 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "sss", + description = +[[ +Sets player or 's maximum tp to and heals them to full. +!setmaxtp | +!setmaxtp +]], +} + +function onTrigger(player, argc, tp) + local sender = "[setmaxtp] "; + + + + if player then + tp = tonumber(tp) or 0; + location = INVENTORY_CURRENCY; + + player:SetTP(tp); + else + print(sender.."unable to add experience, ensure player name is valid."); + end; +end; \ No newline at end of file diff --git a/data/scripts/commands/gm/spawn.lua b/data/scripts/commands/gm/spawn.lua index 1ff4d900..1f3f1852 100644 --- a/data/scripts/commands/gm/spawn.lua +++ b/data/scripts/commands/gm/spawn.lua @@ -6,7 +6,7 @@ properties = { description = "Spawns a actor", } -function onTrigger(player, argc, actorClassId) +function onTrigger(player, argc, actorClassId, width, height) if (actorClassId == nil) then player:SendMessage(0x20, "", "No actor class id provided."); @@ -24,7 +24,16 @@ function onTrigger(player, argc, actorClassId) if (actorClassId ~= nil) then zone = player:GetZone(); - actor = zone:SpawnActor(actorClassId, "test", pos[0], pos[1], pos[2], pos[3]); + local w = tonumber(width) or 0; + local h = tonumber(height) or 0; + printf("%f %f %f", x, y, z); + --local x, y, z = player.GetPos(); + for i = 0, w do + for j = 0, h do + actor = zone:SpawnActor(actorClassId, "test", pos[0] + (i - (w / 2) * 3), pos[1], pos[2] + (j - (h / 2) * 3), pos[3]); + actor.SetAppearance(1001149) + end + end end if (actor == nil) then diff --git a/data/scripts/commands/gm/yolo.lua b/data/scripts/commands/gm/yolo.lua index 668cc9af..ea971d13 100644 --- a/data/scripts/commands/gm/yolo.lua +++ b/data/scripts/commands/gm/yolo.lua @@ -128,7 +128,7 @@ function onTrigger(player, argc, id, level, weight) end; ]] -function onTrigger(player, argc, skillName, level) +function onTrigger(player, argc, width, height, blockCount) local messageId = MESSAGE_TYPE_SYSTEM_ERROR; local sender = "yolo"; @@ -147,24 +147,24 @@ function onTrigger(player, argc, skillName, level) local z = tonumber(pos[2]); local rot = tonumber(pos[3]); local zone = pos[4]; - + local w = tonumber(width) or 0; + local h = tonumber(height) or 0; printf("%f %f %f", x, y, z); --local x, y, z = player.GetPos(); - for i = 1, 1 do - - local actor = player.GetZone().SpawnActor(2104001, 'ass', x, y, z, rot, 0, 0, true ); - - if player.currentContentGroup then - player.currentContentGroup:AddMember(actor.actorId) + for i = 0, blockCount do + for i = 0, w do + for j = 0, h do + local actor = player.GetZone().SpawnActor(2104001, 'ass', x + (i - (w / 2) * 3), y, z + (j - (h / 2) * 3), rot, 0, 0, true); + actor.ChangeNpcAppearance(1001149) + actor.SetLevel(50); + end + --actor.FollowTarget(player, 3.2); + end + + x = x + 500 end - --actor.FollowTarget(player, 3.2); - end; return; end - level = tonumber(level) or 1; - if player then - player.SendMessage(messageId, sender, string.format("name %s | cost %d | level %u", skillName, calculateCommandCost(player, skillName, level), level)); - end; end; function calculateCommandCost(player, skillName, level) diff --git a/data/scripts/commands/gm/zonecount.lua b/data/scripts/commands/gm/zonecount.lua new file mode 100644 index 00000000..74e9a838 --- /dev/null +++ b/data/scripts/commands/gm/zonecount.lua @@ -0,0 +1,21 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "sss", + description = +[[ +Set movement speed for player. Enter no value to reset to default. +!speed | +!speed | +]] + +} + +function onTrigger(player, argc, stop, walk, run) + + local message = tostring(player.zone.GetAllActors().Count); + + player.SendMessage(0x20, "", message); + +end \ No newline at end of file diff --git a/data/scripts/commands/magic/aero.lua b/data/scripts/commands/magic/aero.lua new file mode 100644 index 00000000..30039450 --- /dev/null +++ b/data/scripts/commands/magic/aero.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/aerora.lua b/data/scripts/commands/magic/aerora.lua new file mode 100644 index 00000000..03c40474 --- /dev/null +++ b/data/scripts/commands/magic/aerora.lua @@ -0,0 +1,27 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +--Increased damage and conversion to single target +function onCombo(caster, target, spell) + spell.aoeType = 0; + spell.potency = spell.potency * 1.5; +end; + +function onMagicFinish(caster, target, spell, action) + local damage = math.random(10, 100); + + --Dispels an effect on each target. + local effects = target.statusEffects.GetStatusEffectsByFlag(16); --lose on dispel + if effects != nil then + target.statusEffects.RemoveStatusEffect(effects[0]); + end; + return damage; +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/ballad_of_magi.lua b/data/scripts/commands/magic/ballad_of_magi.lua new file mode 100644 index 00000000..9b3636fe --- /dev/null +++ b/data/scripts/commands/magic/ballad_of_magi.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onBuffMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/blizzara.lua b/data/scripts/commands/magic/blizzara.lua index 8631a116..d12d70a3 100644 --- a/data/scripts/commands/magic/blizzara.lua +++ b/data/scripts/commands/magic/blizzara.lua @@ -10,15 +10,5 @@ function onMagicStart(caster, target, spell) end; function onMagicFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - return damage; + return magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/blizzard.lua b/data/scripts/commands/magic/blizzard.lua index e6716947..36db2947 100644 --- a/data/scripts/commands/magic/blizzard.lua +++ b/data/scripts/commands/magic/blizzard.lua @@ -9,13 +9,5 @@ function onMagicStart(caster, target, spell) end; function onMagicFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - return damage; + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/burst.lua b/data/scripts/commands/magic/burst.lua index a847f5d8..d3f98329 100644 --- a/data/scripts/commands/magic/burst.lua +++ b/data/scripts/commands/magic/burst.lua @@ -9,16 +9,11 @@ function onMagicStart(caster, target, spell) return 0; end; +--Increased damage with lesser current hp +function onCombo(caster, target, spell) + +end; + function onMagicFinish(caster, target, spell, action) - local damage = math.random(1000, 2500); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - return damage; + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/cura.lua b/data/scripts/commands/magic/cura.lua new file mode 100644 index 00000000..51e8820d --- /dev/null +++ b/data/scripts/commands/magic/cura.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onCureMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/curaga.lua b/data/scripts/commands/magic/curaga.lua new file mode 100644 index 00000000..7456ca1e --- /dev/null +++ b/data/scripts/commands/magic/curaga.lua @@ -0,0 +1,15 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +--http://forum.square-enix.com/ffxiv/threads/41900-White-Mage-A-Guide read +function onMagicFinish(caster, target, spell, action) + magic.onCureMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/cure.lua b/data/scripts/commands/magic/cure.lua new file mode 100644 index 00000000..51e8820d --- /dev/null +++ b/data/scripts/commands/magic/cure.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onCureMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/default.lua b/data/scripts/commands/magic/default.lua new file mode 100644 index 00000000..51e8820d --- /dev/null +++ b/data/scripts/commands/magic/default.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onCureMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/fira.lua b/data/scripts/commands/magic/fira.lua index 8631a116..3c7c0942 100644 --- a/data/scripts/commands/magic/fira.lua +++ b/data/scripts/commands/magic/fira.lua @@ -9,16 +9,11 @@ function onMagicStart(caster, target, spell) return 0; end; -function onMagicFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); +--Increased Damage and reduced recast time in place of stun +function onCombo(caster, target, spell) + spell.castTimeMs = spell.castTimeMs / 2; +end; - return damage; +function onMagicFinish(caster, target, spell, action) + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/firaga.lua b/data/scripts/commands/magic/firaga.lua index c446e177..2658db16 100644 --- a/data/scripts/commands/magic/firaga.lua +++ b/data/scripts/commands/magic/firaga.lua @@ -9,16 +9,11 @@ function onMagicStart(caster, target, spell) return 0; end; -function onMagicFinish(caster, target, spell, action) - local damage = math.random(1000, 2500); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); +--Increased critical damage +function onCombo(caster, target, spell) + spell.castTimeMs = spell.castTimeMs / 2; +end; - return damage; +function onMagicFinish(caster, target, spell, action) + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/fire.lua b/data/scripts/commands/magic/fire.lua index fc035356..30039450 100644 --- a/data/scripts/commands/magic/fire.lua +++ b/data/scripts/commands/magic/fire.lua @@ -10,18 +10,5 @@ function onMagicStart(caster, target, spell) end; function onMagicFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/flare.lua b/data/scripts/commands/magic/flare.lua index a847f5d8..eba41392 100644 --- a/data/scripts/commands/magic/flare.lua +++ b/data/scripts/commands/magic/flare.lua @@ -10,15 +10,7 @@ function onMagicStart(caster, target, spell) end; function onMagicFinish(caster, target, spell, action) - local damage = math.random(1000, 2500); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - return damage; + --increase potency based on proximity to target + + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/freeze.lua b/data/scripts/commands/magic/freeze.lua index 8129043c..30039450 100644 --- a/data/scripts/commands/magic/freeze.lua +++ b/data/scripts/commands/magic/freeze.lua @@ -10,15 +10,5 @@ function onMagicStart(caster, target, spell) end; function onMagicFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - return damage; + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/holy.lua b/data/scripts/commands/magic/holy.lua index c446e177..30039450 100644 --- a/data/scripts/commands/magic/holy.lua +++ b/data/scripts/commands/magic/holy.lua @@ -10,15 +10,5 @@ function onMagicStart(caster, target, spell) end; function onMagicFinish(caster, target, spell, action) - local damage = math.random(1000, 2500); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - return damage; + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/holy_succor.lua b/data/scripts/commands/magic/holy_succor.lua new file mode 100644 index 00000000..5bf11269 --- /dev/null +++ b/data/scripts/commands/magic/holy_succor.lua @@ -0,0 +1,21 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + spell.statusId = 228011; + spell.statusDuration = 25; + spell.statusChance = 1.0; + magic.onCureMagicFinish(caster, target, spell, action) + + if caster != target then + action.AddHealAction(caster.actorId, (action.amount / 2)); + end +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/minuet_of_rigor.lua b/data/scripts/commands/magic/minuet_of_rigor.lua new file mode 100644 index 00000000..9b3636fe --- /dev/null +++ b/data/scripts/commands/magic/minuet_of_rigor.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onBuffMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/paeon_of_war.lua b/data/scripts/commands/magic/paeon_of_war.lua new file mode 100644 index 00000000..9b3636fe --- /dev/null +++ b/data/scripts/commands/magic/paeon_of_war.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onBuffMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/protect.lua b/data/scripts/commands/magic/protect.lua new file mode 100644 index 00000000..71670940 --- /dev/null +++ b/data/scripts/commands/magic/protect.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onStatusMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/repose.lua b/data/scripts/commands/magic/repose.lua new file mode 100644 index 00000000..30039450 --- /dev/null +++ b/data/scripts/commands/magic/repose.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/sanguine_rite.lua b/data/scripts/commands/magic/sanguine_rite.lua new file mode 100644 index 00000000..71670940 --- /dev/null +++ b/data/scripts/commands/magic/sanguine_rite.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onStatusMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/sleep.lua b/data/scripts/commands/magic/sleep.lua new file mode 100644 index 00000000..71670940 --- /dev/null +++ b/data/scripts/commands/magic/sleep.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onStatusMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/stone.lua b/data/scripts/commands/magic/stone.lua new file mode 100644 index 00000000..30039450 --- /dev/null +++ b/data/scripts/commands/magic/stone.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/stonera.lua b/data/scripts/commands/magic/stonera.lua new file mode 100644 index 00000000..74593a56 --- /dev/null +++ b/data/scripts/commands/magic/stonera.lua @@ -0,0 +1,20 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +--Increased damage and conversion to single target +function onCombo(caster, target, spell) + spell.aoeType = 0; + spell.potency = spell.potency * 1.5; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/stoneskin.lua b/data/scripts/commands/magic/stoneskin.lua new file mode 100644 index 00000000..71670940 --- /dev/null +++ b/data/scripts/commands/magic/stoneskin.lua @@ -0,0 +1,14 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onMagicFinish(caster, target, spell, action) + magic.onStatusMagicFinish(caster, target, spell, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/thundaga.lua b/data/scripts/commands/magic/thundaga.lua index e6716947..862f1846 100644 --- a/data/scripts/commands/magic/thundaga.lua +++ b/data/scripts/commands/magic/thundaga.lua @@ -8,14 +8,11 @@ function onMagicStart(caster, target, spell) return 0; end; -function onMagicFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); +--Increased critical damage +function onCombo(caster, target, spell) + spell.critDamageModifier = 1.5; +end; - return damage; +function onMagicFinish(caster, target, spell, action) + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/thundara.lua b/data/scripts/commands/magic/thundara.lua index e6716947..b163bc19 100644 --- a/data/scripts/commands/magic/thundara.lua +++ b/data/scripts/commands/magic/thundara.lua @@ -8,14 +8,13 @@ function onMagicStart(caster, target, spell) return 0; end; -function onMagicFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); +--Increased Damage and reduced recast time in place of stun +function onCombo(caster, target, spell) + spell.statusChance = 0; + spell.basePotency = spell.basePotency * 1.5; + spell.recastTimeMs = spell.recastTimeMs / 2; +end; - return damage; +function onMagicFinish(caster, target, spell, action) + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/magic/thunder.lua b/data/scripts/commands/magic/thunder.lua index e6716947..36db2947 100644 --- a/data/scripts/commands/magic/thunder.lua +++ b/data/scripts/commands/magic/thunder.lua @@ -9,13 +9,5 @@ function onMagicStart(caster, target, spell) end; function onMagicFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - return damage; + magic.onMagicFinish(caster, target, spell, action) end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/aura_pulse.lua b/data/scripts/commands/weaponskill/aura_pulse.lua new file mode 100644 index 00000000..eeb6b6a0 --- /dev/null +++ b/data/scripts/commands/weaponskill/aura_pulse.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Chance to inflict slow +function onCombo(caster, target, skill) + skill.statusChance = 0.50; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/bloodletter.lua b/data/scripts/commands/weaponskill/bloodletter.lua new file mode 100644 index 00000000..4764eb61 --- /dev/null +++ b/data/scripts/commands/weaponskill/bloodletter.lua @@ -0,0 +1,20 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Inflicts additional damage when bleed ends +--Note for later, going to set bleed tier to 2, when bleed get scripted, check if tier is 2 and add additional damage at the end +function onCombo(caster, target, skill) + skill.statusTier = 2; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/brutal_swing.lua b/data/scripts/commands/weaponskill/brutal_swing.lua new file mode 100644 index 00000000..5c3b09e4 --- /dev/null +++ b/data/scripts/commands/weaponskill/brutal_swing.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased damage +function onPositional(caster, target, skill) + skill.basePotency = skill.basePotency * 1.5; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/chaos_thrust.lua b/data/scripts/commands/weaponskill/chaos_thrust.lua index 98ea45f1..118ec4e9 100644 --- a/data/scripts/commands/weaponskill/chaos_thrust.lua +++ b/data/scripts/commands/weaponskill/chaos_thrust.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,11 @@ function onSkillStart(caster, target, skill) return 0; end; +--Increased crit hit rating +function onCombo(caster, target, skill) + skill.critRateModifier = 1.5; +end; + function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/concussive_blow.lua b/data/scripts/commands/weaponskill/concussive_blow.lua new file mode 100644 index 00000000..b974bb40 --- /dev/null +++ b/data/scripts/commands/weaponskill/concussive_blow.lua @@ -0,0 +1,25 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Chance to inflict blind on flank +function onPositional(caster, target, skill) + skill.statusChance = 0.50; + skill.statusDuration = 10; +end; + +function onCombo(caster, target, skill) + skill.basePotency = skill.basePotency * 1.5; +end; + + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/default.lua b/data/scripts/commands/weaponskill/default.lua index 965bcb8d..ffe6d0fc 100644 --- a/data/scripts/commands/weaponskill/default.lua +++ b/data/scripts/commands/weaponskill/default.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -9,18 +10,5 @@ function onSkillStart(caster, target, skill) end; function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, skill.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/demolish.lua b/data/scripts/commands/weaponskill/demolish.lua new file mode 100644 index 00000000..0bef1791 --- /dev/null +++ b/data/scripts/commands/weaponskill/demolish.lua @@ -0,0 +1,23 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Dispel +--Does dispel have a text id? +function onCombo(caster, target, skill) + local effects = target.statusEffects.GetStatusEffectsByFlag(16); --lose on dispel + if effects != nil then + target.statusEffects.RemoveStatusEffect(effects[0]); + end; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/disembowel.lua b/data/scripts/commands/weaponskill/disembowel.lua new file mode 100644 index 00000000..4bbcaddc --- /dev/null +++ b/data/scripts/commands/weaponskill/disembowel.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased paralysis duration +function onCombo(caster, target, skill) + skill.statusDuration = skill.statusDuration * 2; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/doom_spike.lua b/data/scripts/commands/weaponskill/doom_spike.lua index 98ea45f1..b6b39efa 100644 --- a/data/scripts/commands/weaponskill/doom_spike.lua +++ b/data/scripts/commands/weaponskill/doom_spike.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,11 @@ function onSkillStart(caster, target, skill) return 0; end; +--Increased accuracy +function onCombo(caster, target, skill) + skill.accuracyModifier = 1.25; +end; + function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/dragon_kick.lua b/data/scripts/commands/weaponskill/dragon_kick.lua new file mode 100644 index 00000000..dc0ce4bd --- /dev/null +++ b/data/scripts/commands/weaponskill/dragon_kick.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Chance to render target unable to use weaponskills (pacification) +function onPositional(caster, target, skill) + skill.statusChance = 0.50; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/dread_spike.lua b/data/scripts/commands/weaponskill/dread_spike.lua index 98ea45f1..ffe6d0fc 100644 --- a/data/scripts/commands/weaponskill/dread_spike.lua +++ b/data/scripts/commands/weaponskill/dread_spike.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -9,18 +10,5 @@ function onSkillStart(caster, target, skill) end; function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/fast_blade.lua b/data/scripts/commands/weaponskill/fast_blade.lua index e44a637d..b712e316 100644 --- a/data/scripts/commands/weaponskill/fast_blade.lua +++ b/data/scripts/commands/weaponskill/fast_blade.lua @@ -1,27 +1,18 @@ require("global"); require("weaponskill"); -function onSkillPrepare(caster, target, spell) +function onSkillPrepare(caster, target, skill) return 0; end; -function onSkillStart(caster, target, spell) +function onSkillStart(caster, target, skill) return 0; end; -function onSkillFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackSkill(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; +function onPositional(caster, target, skill) + skill.basePotency = skill.basePotency * 1.25; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/feint.lua b/data/scripts/commands/weaponskill/feint.lua index 98ea45f1..ffe6d0fc 100644 --- a/data/scripts/commands/weaponskill/feint.lua +++ b/data/scripts/commands/weaponskill/feint.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -9,18 +10,5 @@ function onSkillStart(caster, target, skill) end; function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/flat_blade.lua b/data/scripts/commands/weaponskill/flat_blade.lua index fa1c4b1f..2408e76b 100644 --- a/data/scripts/commands/weaponskill/flat_blade.lua +++ b/data/scripts/commands/weaponskill/flat_blade.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,10 @@ function onSkillStart(caster, target, skill) return 0; end; +function onCombo(caster, target, skill) + skill.enmityModifier = skill.enmityModifier * 2 +end; + function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/fracture.lua b/data/scripts/commands/weaponskill/fracture.lua new file mode 100644 index 00000000..0a4b6193 --- /dev/null +++ b/data/scripts/commands/weaponskill/fracture.lua @@ -0,0 +1,14 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, spell) + return 0; +end; + +function onSkillStart(caster, target, spell) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/full_thrust.lua b/data/scripts/commands/weaponskill/full_thrust.lua index 98ea45f1..a7e64ba2 100644 --- a/data/scripts/commands/weaponskill/full_thrust.lua +++ b/data/scripts/commands/weaponskill/full_thrust.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,7 @@ function onSkillStart(caster, target, skill) return 0; end; -function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; +function onSkillFinish(caster, target, skill, action) + caster.AddTP(1000); + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/gloom_arrow.lua b/data/scripts/commands/weaponskill/gloom_arrow.lua new file mode 100644 index 00000000..409ec54d --- /dev/null +++ b/data/scripts/commands/weaponskill/gloom_arrow.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Chance to inflict blind +function onCombo(caster, target, skill) + skill.statusChance = 0.90; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/godsbane.lua b/data/scripts/commands/weaponskill/godsbane.lua new file mode 100644 index 00000000..b61ca390 --- /dev/null +++ b/data/scripts/commands/weaponskill/godsbane.lua @@ -0,0 +1,20 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased crit rate +function onCombo(caster, target, skill) + --Get Berserk statuseffect + skill.critRateModifier = 1.5; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/goring_blade.lua b/data/scripts/commands/weaponskill/goring_blade.lua new file mode 100644 index 00000000..002b45fb --- /dev/null +++ b/data/scripts/commands/weaponskill/goring_blade.lua @@ -0,0 +1,24 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Chance to increase defense when executed from behind the target +function onPositional(caster, target, skill) + skill.statusChance = 0.90; +end; + +--Increases bleed damage +function onCombo(caster, target, skill) + skill.statusTier = 2; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/haymaker.lua b/data/scripts/commands/weaponskill/haymaker.lua new file mode 100644 index 00000000..ffe6d0fc --- /dev/null +++ b/data/scripts/commands/weaponskill/haymaker.lua @@ -0,0 +1,14 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/heavy_shot.lua b/data/scripts/commands/weaponskill/heavy_shot.lua new file mode 100644 index 00000000..ffe6d0fc --- /dev/null +++ b/data/scripts/commands/weaponskill/heavy_shot.lua @@ -0,0 +1,14 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/heavy_swing.lua b/data/scripts/commands/weaponskill/heavy_swing.lua new file mode 100644 index 00000000..6cbf7811 --- /dev/null +++ b/data/scripts/commands/weaponskill/heavy_swing.lua @@ -0,0 +1,20 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased accuracy +function onCombo(caster, target, skill) + skill.accuracyModifier = 1.5; +end; + + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/heavy_thrust.lua b/data/scripts/commands/weaponskill/heavy_thrust.lua index 9c72078a..8a77dc73 100644 --- a/data/scripts/commands/weaponskill/heavy_thrust.lua +++ b/data/scripts/commands/weaponskill/heavy_thrust.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,20 +9,11 @@ function onSkillStart(caster, target, skill) return 0; end; -function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); +--Increased stun duration +function onCombo(caster, target, skill) + skill.statusDuration = 10; +end; - action.effectId = bit32.bxor(0x8000000, skill.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/howling_fist.lua b/data/scripts/commands/weaponskill/howling_fist.lua new file mode 100644 index 00000000..47692865 --- /dev/null +++ b/data/scripts/commands/weaponskill/howling_fist.lua @@ -0,0 +1,24 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased accuracy +function onCombo(caster, target, skill) + skill.accuracyModifier = 1; +end; + +--Increased damage +function onCombo(caster, target, skill) + skill.potency = skill.potency * 1.5; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/impulse_drive.lua b/data/scripts/commands/weaponskill/impulse_drive.lua index 98ea45f1..d5a6b5f5 100644 --- a/data/scripts/commands/weaponskill/impulse_drive.lua +++ b/data/scripts/commands/weaponskill/impulse_drive.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,16 @@ function onSkillStart(caster, target, skill) return 0; end; +--Increased damage +function onPositional(caster, target, skill) + skill.potency = skill.potency * 1.25 +end; + +--Increased crit hit rating +function onCombo(caster, target, skill) + skill.critRateModifier = 1.25; +end; + function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/leaden_arrow.lua b/data/scripts/commands/weaponskill/leaden_arrow.lua new file mode 100644 index 00000000..6386e890 --- /dev/null +++ b/data/scripts/commands/weaponskill/leaden_arrow.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased Heavy duration, becomes 60 seconds +function onCombo(caster, target, skill) + skill.statusDuration = 60; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/leg_sweep.lua b/data/scripts/commands/weaponskill/leg_sweep.lua index 98ea45f1..753d8f40 100644 --- a/data/scripts/commands/weaponskill/leg_sweep.lua +++ b/data/scripts/commands/weaponskill/leg_sweep.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,11 @@ function onSkillStart(caster, target, skill) return 0; end; +--Chance to inflict stun +function onCombo(caster, target, skill) + skill.statusChance = 0.50; +end; + function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/maim.lua b/data/scripts/commands/weaponskill/maim.lua new file mode 100644 index 00000000..e145272b --- /dev/null +++ b/data/scripts/commands/weaponskill/maim.lua @@ -0,0 +1,18 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onCombo(caster, target, skill) + skill.accuracyModifier = 1.5; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/path_of_the_storm.lua b/data/scripts/commands/weaponskill/path_of_the_storm.lua new file mode 100644 index 00000000..710484ed --- /dev/null +++ b/data/scripts/commands/weaponskill/path_of_the_storm.lua @@ -0,0 +1,20 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Chance to inflict heavy when executed from behind +function onPositional(caster, target, skill) + skill.statusChance = 0.50; + skill.statusDuration = 5; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/piercing_arrow.lua b/data/scripts/commands/weaponskill/piercing_arrow.lua new file mode 100644 index 00000000..ffe6d0fc --- /dev/null +++ b/data/scripts/commands/weaponskill/piercing_arrow.lua @@ -0,0 +1,14 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/pounce.lua b/data/scripts/commands/weaponskill/pounce.lua new file mode 100644 index 00000000..64d41eb1 --- /dev/null +++ b/data/scripts/commands/weaponskill/pounce.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onPositional(caster, target, skill) + skill.statusChance = 0.50; + skill.statusDuration = 5; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/pummel.lua b/data/scripts/commands/weaponskill/pummel.lua index 98ea45f1..b9802c0b 100644 --- a/data/scripts/commands/weaponskill/pummel.lua +++ b/data/scripts/commands/weaponskill/pummel.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,10 @@ function onSkillStart(caster, target, skill) return 0; end; +function onPositional(caster, target, skill) + skill.basePotency = skill.basePotency * 1.25; +end; + function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action) end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/quick_nock.lua b/data/scripts/commands/weaponskill/quick_nock.lua new file mode 100644 index 00000000..a08d59fd --- /dev/null +++ b/data/scripts/commands/weaponskill/quick_nock.lua @@ -0,0 +1,22 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--fivefold attack/conversion +function onCombo(caster, target, skill) + skill.numHits = 5; + skill.aoeType = 0; + skill.aoeTarget = 2; + --animation change? +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/rage_of_halone.lua b/data/scripts/commands/weaponskill/rage_of_halone.lua new file mode 100644 index 00000000..e5bf1634 --- /dev/null +++ b/data/scripts/commands/weaponskill/rage_of_halone.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Accuracy increase +function onCombo(caster, target, skill) + skill.accuracyModifier = 1; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/rain_of_death.lua b/data/scripts/commands/weaponskill/rain_of_death.lua new file mode 100644 index 00000000..a7fb10ac --- /dev/null +++ b/data/scripts/commands/weaponskill/rain_of_death.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Chance to inflict stun +function onCombo(caster, target, skill) + skill.statusChance = 0.90 +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/ring_of_talons.lua b/data/scripts/commands/weaponskill/ring_of_talons.lua new file mode 100644 index 00000000..0972277c --- /dev/null +++ b/data/scripts/commands/weaponskill/ring_of_talons.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased critical hit rate +function onCombo(caster, target, skill) + skill.critRateModifier = 1.5; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/riot_blade.lua b/data/scripts/commands/weaponskill/riot_blade.lua new file mode 100644 index 00000000..85477b46 --- /dev/null +++ b/data/scripts/commands/weaponskill/riot_blade.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Chance to decrease defense when executed from behind the target +function onPositional(caster, target, skill) + skill.statusChance = 0.75; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/savage_blade.lua b/data/scripts/commands/weaponskill/savage_blade.lua new file mode 100644 index 00000000..0a83c4e2 --- /dev/null +++ b/data/scripts/commands/weaponskill/savage_blade.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onCombo(caster, target, skill) + skill.basePotency = skill.basePotency * 1.25; +end; + + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/shadowbind.lua b/data/scripts/commands/weaponskill/shadowbind.lua new file mode 100644 index 00000000..b251906a --- /dev/null +++ b/data/scripts/commands/weaponskill/shadowbind.lua @@ -0,0 +1,20 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased Bind duration +function onCombo(caster, target, skill) + skill.statusDuration = 10; +end; + + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/shield_bash.lua b/data/scripts/commands/weaponskill/shield_bash.lua new file mode 100644 index 00000000..1cfc66c2 --- /dev/null +++ b/data/scripts/commands/weaponskill/shield_bash.lua @@ -0,0 +1,15 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/shoulder_tackle.lua b/data/scripts/commands/weaponskill/shoulder_tackle.lua new file mode 100644 index 00000000..7bd4c304 --- /dev/null +++ b/data/scripts/commands/weaponskill/shoulder_tackle.lua @@ -0,0 +1,18 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) + --chance to influct stun only when target has no enmity towards you + if !(target.hateContainer.HasHateForTarget(caster)) then + skill.statusChance = 0.50; + end + return weaponskill.onSkillFinish(caster, target, skill, action); +end; diff --git a/data/scripts/commands/weaponskill/simian_thrash.lua b/data/scripts/commands/weaponskill/simian_thrash.lua index 0b85c48f..1c44710f 100644 --- a/data/scripts/commands/weaponskill/simian_thrash.lua +++ b/data/scripts/commands/weaponskill/simian_thrash.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,11 @@ function onSkillStart(caster, target, skill) return 0; end; +--Increased accuracy +function onCombo(caster, target, skill) + skill.basePotency = skill.basePotency * 1.5; +end; + function onSkillFinish(caster, target, skill, action) - local damage = math.random(0, 0); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/skull_sunder.lua b/data/scripts/commands/weaponskill/skull_sunder.lua index e44a637d..bfa679d4 100644 --- a/data/scripts/commands/weaponskill/skull_sunder.lua +++ b/data/scripts/commands/weaponskill/skull_sunder.lua @@ -9,19 +9,11 @@ function onSkillStart(caster, target, spell) return 0; end; -function onSkillFinish(caster, target, spell, action) - local damage = math.random(10, 100); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackSkill(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; +--Increased enmity +function onCombo(caster, target, skill) + skill.enmityModifier = 1.5; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/spirits_within.lua b/data/scripts/commands/weaponskill/spirits_within.lua new file mode 100644 index 00000000..ab7c74fc --- /dev/null +++ b/data/scripts/commands/weaponskill/spirits_within.lua @@ -0,0 +1,23 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased enmity +function onCombo(caster, target, skill) + skill.enmityModifier = 1.5; +end; + +function onSkillFinish(caster, target, skill, action) + local damage = math.random(10, 100); + --Increased damage with higher hp + local potencyModifier = caster:GetHPP() + 25; + skill.basePotency = skill.basePotency * (potencyModifier / 100); + return weaponskill.onSkillFinish(caster, target, skill, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/steel_cyclone.lua b/data/scripts/commands/weaponskill/steel_cyclone.lua new file mode 100644 index 00000000..330b25e8 --- /dev/null +++ b/data/scripts/commands/weaponskill/steel_cyclone.lua @@ -0,0 +1,20 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased critical hit rate +function onCombo(caster, target, skill) + skill.critRateModifier = 1.5; +end; + + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/true_thrust.lua b/data/scripts/commands/weaponskill/true_thrust.lua index 965bcb8d..33b4b40c 100644 --- a/data/scripts/commands/weaponskill/true_thrust.lua +++ b/data/scripts/commands/weaponskill/true_thrust.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,11 @@ function onSkillStart(caster, target, skill) return 0; end; +--Increased accuracy from in front +function onPositional(caster, target, skill) + skill.accuracyModifier = 1.25; +end; + function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - action.effectId = bit32.bxor(0x8000000, skill.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/vorpal_thrust.lua b/data/scripts/commands/weaponskill/vorpal_thrust.lua index 98ea45f1..7cc84955 100644 --- a/data/scripts/commands/weaponskill/vorpal_thrust.lua +++ b/data/scripts/commands/weaponskill/vorpal_thrust.lua @@ -1,4 +1,5 @@ require("global"); +require("weaponskill"); function onSkillPrepare(caster, target, skill) return 0; @@ -8,19 +9,11 @@ function onSkillStart(caster, target, skill) return 0; end; +--Increased crit rating from behind +function onPositional(caster, target, skill) + skill.critRateModifier = 1.25; +end; + function onSkillFinish(caster, target, skill, action) - local damage = math.random(100, 200); - - -- todo: populate a global script with statuses and modifiers - action.worldMasterTextId = 0x765D; - - -- todo: populate a global script with statuses and modifiers - -- magic.HandleAttackMagic(caster, target, spell, action) - -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - --action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; + return weaponskill.onSkillFinish(caster, target, skill, action); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/whirlwind.lua b/data/scripts/commands/weaponskill/whirlwind.lua new file mode 100644 index 00000000..6c1f4678 --- /dev/null +++ b/data/scripts/commands/weaponskill/whirlwind.lua @@ -0,0 +1,28 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Reset Berserk effect, increase damage? +function onCombo(caster, target, skill) + --Get Berserk statuseffect + local berserk = caster.statusEffects.GetStatusEffectById(223160); + + --if it isn't nil + if berserk != nil then + berserk.SetTier(1); + skill.basePotency = skill.basePotency * 1.5; + end; + + +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; diff --git a/data/scripts/commands/weaponskill/wide_volley.lua b/data/scripts/commands/weaponskill/wide_volley.lua new file mode 100644 index 00000000..bb937e55 --- /dev/null +++ b/data/scripts/commands/weaponskill/wide_volley.lua @@ -0,0 +1,19 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Increased Accuracy +function onCombo(caster, target, skill) + skill.accuracyModifier = 1.25; +end; + +function onSkillFinish(caster, target, skill, action) + return weaponskill.onSkillFinish(caster, target, skill, action); +end; \ No newline at end of file diff --git a/data/scripts/content/SimpleContent30010.lua b/data/scripts/content/SimpleContent30010.lua index 01e8d699..f510c173 100644 --- a/data/scripts/content/SimpleContent30010.lua +++ b/data/scripts/content/SimpleContent30010.lua @@ -1,5 +1,4 @@ require ("global") -require ("ally") require ("modifiers") function onCreate(starterPlayer, contentArea, director) @@ -34,48 +33,10 @@ function onCreate(starterPlayer, contentArea, director) end -function onUpdate(area, tick) - local players = area:GetPlayers() - local mobs = area:GetMonsters() - local allies = area:GetAllies() - local resumeChecks = true - for player in players do - if player then - local exitLoop = false - for ally in allies do - if ally then - if not ally:IsEngaged() then - if player:IsEngaged() then - ally.neutral = false - ally.isAutoAttackEnabled = true - ally:SetMod(modifiersGlobal.Speed, 8) - allyGlobal.EngageTarget(ally, player.target) - exitLoop = true - break - -- todo: support scripted paths - elseif ally:GetSpeed() > 0 then - - end - end - end - end - if exitLoop then - resumeChecks = false - break - end - end - end - if not resumeChecks then - return - end -end - function onDestroy() end ---Iterating over allies using the for...in syntax throws an except and lags the server. ---This function probably isn't needed either way, it does the smae thing as TryAggro in ally function onUpdate(tick, area) if area then local players = area:GetPlayers() @@ -88,7 +49,7 @@ function onUpdate(tick, area) if allies then for i = 0, #allies - 1 do - if allies[i] then + if allies[i] then if not allies[i]:IsEngaged() then if player:IsEngaged() and player.target then @@ -99,7 +60,7 @@ function onUpdate(tick, area) exitLoop = true break -- todo: support scripted paths - elseif allies[i]:GetSpeed() > 0 then + elseif allies[i]:GetSpeed() > 0 then end end end diff --git a/data/scripts/directors/Quest/QuestDirectorMan0g001.lua b/data/scripts/directors/Quest/QuestDirectorMan0g001.lua index 38870d2e..000bad4d 100644 --- a/data/scripts/directors/Quest/QuestDirectorMan0g001.lua +++ b/data/scripts/directors/Quest/QuestDirectorMan0g001.lua @@ -10,7 +10,6 @@ function init() return "/Director/Quest/QuestDirectorMan0g001"; end ---Should we be using this to spawn mobs and drop Simplecontent? function onCreateContentArea(players, director, contentArea, contentGroup) director:StartContentGroup(); end @@ -30,8 +29,10 @@ function onEventStarted(player, actor, triggerName) player:SendMessage(0x20, "", "processTtrBtl002 called"); player:EndEvent(); - --Combat portion of tutorial + --Combat portion of tutorial + if player:IsDiscipleOfWar() then + player:SendMessage(0x20, "", "Is DoW"); waitForSignal("playerAttack"); closeTutorialWidget(player); showTutorialSuccessWidget(player, 9055); --Open TutorialSuccessWidget for attacking enemy @@ -43,6 +44,7 @@ function onEventStarted(player, actor, triggerName) closeTutorialWidget(player); showTutorialSuccessWidget(player, 9065); --Open TutorialSuccessWidget for weapon skill elseif player:IsDiscipleOfMagic() then + player:SendMessage(0x20, "", "Is DoM"); openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_CASTING); waitForSignal("spellUsed"); closeTutorialWidget(player); @@ -52,27 +54,40 @@ function onEventStarted(player, actor, triggerName) waitForSignal("abilityUsed"); end - --Currently this requires the player to pass all the other signals first, need a way for signals to work out of order - waitForSignal("mobkill"); + player:SendMessage(0x20, "", "Waiting for mobkill1"); + waitForSignal("mobkill"); --Should be wait for mobkill + player:SendMessage(0x20, "", "Waiting for mobkill2"); waitForSignal("mobkill"); + player:SendMessage(0x20, "", "Waiting for mobkill3"); waitForSignal("mobkill"); worldMaster = GetWorldMaster(); - wait(5);--Debug waits, get rid of these later - player:Disengage(0x0000); - wait(5); - man0g0Quest:NextPhase(10); - wait(5); - --This doesn't work - player:RemoveFromCurrentPartyAndCleanup(); + player:SendMessage(0x20, "", "Sending data packet 'attention'"); player:SendDataPacket("attention", worldMaster, "", 51073, 2); wait(5); + player:SendMessage(0x20, "", "Disengaging"); + player:Disengage(0x0000); + wait(5); + player:SendMessage(0x20, "", "NextPhase(10)"); + man0g0Quest:NextPhase(10); + wait(5); + player:SendMessage(0x20, "", "ProcessEvent020_1"); + callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent020_1", nil, nil, nil); + + wait(5); + + player:SendMessage(0x20, "", "Changing music"); player:ChangeMusic(7); wait(5); + + player:SendMessage(0x20, "", "Kick notice event"); kickEventContinue(player, actor, "noticeEvent", "noticeEvent"); wait(5); - callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent020_1", nil, nil, nil); - wait(5); + + player:SendMessage(0x20, "", "ContentFinished"); player:GetZone():ContentFinished(); + wait(5); + player:SendMessage(0x20, "", "Remove from party"); + player:RemoveFromCurrentPartyAndCleanup(); --player:EndEvent(); --GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1); --[[ @@ -88,8 +103,10 @@ function onEventStarted(player, actor, triggerName) OpenWidget (DEFEAT ENEMY) ]] - player:EndEvent(); + player:EndEvent(); + wait(5); + player:SendMessage(0x20, "", "Zone change"); GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1); end diff --git a/data/scripts/effects/bind.lua b/data/scripts/effects/bind.lua new file mode 100644 index 00000000..fa322f97 --- /dev/null +++ b/data/scripts/effects/bind.lua @@ -0,0 +1,11 @@ +require("modifiers"); + +function onGain(target, effect) + local currEvade = target.GetMod(modifierGlobals.Evasion); + target.SetMod(modifierGlobals.Evasion, currEvade + 15); +end; + +function onLose(target, effect) + local currEvade = target.GetMod(modifierGlobals.Evasion); + target.SetMod(modifierGlobals.Evasion, currEvade - 15); +end; \ No newline at end of file diff --git a/data/scripts/effects/block_proc.lua b/data/scripts/effects/block_proc.lua new file mode 100644 index 00000000..1f6d7161 --- /dev/null +++ b/data/scripts/effects/block_proc.lua @@ -0,0 +1,3 @@ +function onLose(target, effect) + target:SetProc(1, false); +end; \ No newline at end of file diff --git a/data/scripts/effects/combo.lua b/data/scripts/effects/combo.lua new file mode 100644 index 00000000..44503843 --- /dev/null +++ b/data/scripts/effects/combo.lua @@ -0,0 +1,3 @@ +function onLose(target, effect) + target:SetCombos(); +end; \ No newline at end of file diff --git a/data/scripts/effects/default.lua b/data/scripts/effects/default.lua new file mode 100644 index 00000000..8337c090 --- /dev/null +++ b/data/scripts/effects/default.lua @@ -0,0 +1,2 @@ +function onLose(target, effect) +end; \ No newline at end of file diff --git a/data/scripts/effects/evade_proc.lua b/data/scripts/effects/evade_proc.lua new file mode 100644 index 00000000..5e6a46e2 --- /dev/null +++ b/data/scripts/effects/evade_proc.lua @@ -0,0 +1,3 @@ +function onLose(target, effect) + target:SetProc(0, false); +end; \ No newline at end of file diff --git a/data/scripts/effects/featherfoot.lua b/data/scripts/effects/featherfoot.lua new file mode 100644 index 00000000..fa322f97 --- /dev/null +++ b/data/scripts/effects/featherfoot.lua @@ -0,0 +1,11 @@ +require("modifiers"); + +function onGain(target, effect) + local currEvade = target.GetMod(modifierGlobals.Evasion); + target.SetMod(modifierGlobals.Evasion, currEvade + 15); +end; + +function onLose(target, effect) + local currEvade = target.GetMod(modifierGlobals.Evasion); + target.SetMod(modifierGlobals.Evasion, currEvade - 15); +end; \ No newline at end of file diff --git a/data/scripts/effects/fists_of_earth.lua b/data/scripts/effects/fists_of_earth.lua new file mode 100644 index 00000000..eadc0f32 --- /dev/null +++ b/data/scripts/effects/fists_of_earth.lua @@ -0,0 +1,4 @@ +function onGain(target, effect) + target.statusEffects.RemoveStatusEffect(223209) + target.statusEffects.RemoveStatusEffect(223211) +end; \ No newline at end of file diff --git a/data/scripts/effects/fists_of_fire.lua b/data/scripts/effects/fists_of_fire.lua new file mode 100644 index 00000000..36442318 --- /dev/null +++ b/data/scripts/effects/fists_of_fire.lua @@ -0,0 +1,4 @@ +function onGain(target, effect) + target.statusEffects.RemoveStatusEffect(223210) + target.statusEffects.RemoveStatusEffect(223211) +end; \ No newline at end of file diff --git a/data/scripts/effects/fists_of_wind.lua b/data/scripts/effects/fists_of_wind.lua new file mode 100644 index 00000000..06eaf213 --- /dev/null +++ b/data/scripts/effects/fists_of_wind.lua @@ -0,0 +1,4 @@ +function onGain(target, effect) + target.statusEffects.RemoveStatusEffect(223210) + target.statusEffects.RemoveStatusEffect(223209) +end; \ No newline at end of file diff --git a/data/scripts/effects/invigorate.lua b/data/scripts/effects/invigorate.lua new file mode 100644 index 00000000..33e1e720 --- /dev/null +++ b/data/scripts/effects/invigorate.lua @@ -0,0 +1,3 @@ +function onTick(target, effect) + target.AddTP(100); +end; \ No newline at end of file diff --git a/data/scripts/effects/miss_proc.lua b/data/scripts/effects/miss_proc.lua new file mode 100644 index 00000000..2983289a --- /dev/null +++ b/data/scripts/effects/miss_proc.lua @@ -0,0 +1,3 @@ +function onLose(target, effect) + target:SetProc(3, false); +end; \ No newline at end of file diff --git a/data/scripts/effects/parry_proc.lua b/data/scripts/effects/parry_proc.lua new file mode 100644 index 00000000..97e6911a --- /dev/null +++ b/data/scripts/effects/parry_proc.lua @@ -0,0 +1,3 @@ +function onLose(target, effect) + target:SetProc(2, false); +end; \ No newline at end of file diff --git a/data/scripts/effects/spinning_heel.lua b/data/scripts/effects/spinning_heel.lua new file mode 100644 index 00000000..d8531b82 --- /dev/null +++ b/data/scripts/effects/spinning_heel.lua @@ -0,0 +1,10 @@ +require("modifiers") + +function onGain(target, effect) + target.SetMod(modifiersGlobal.HitCount, 3); +end; + +function onLose(target, effect) + target.SetMod(modifiersGlobal.HitCount, 2); +end; + diff --git a/data/scripts/hiteffect.lua b/data/scripts/hiteffect.lua new file mode 100644 index 00000000..2928f4ed --- /dev/null +++ b/data/scripts/hiteffect.lua @@ -0,0 +1,105 @@ +HitEffect = +{ + --All HitEffects have the last byte 0x8 + HitEffectType = 8 << 24, + --Status effects use 32 << 24 + StatusEffectType = 32 << 24, + --Heal effects use 48 << 24 + MagicEffectType = 48 << 24, + + --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. + --These are used for resists for spells. RecoilLV1 is a resist, RecoilLv2 is a partial resist. + --Don't know what CriticalHit is for but it has a larger effect than Lv1 + 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. + + --HitVisual is probably based on attack property. + --HitVisual1 is for slashing attacks + --HitVisual2 is for piercing attacks + --HitVisual1 | Hitvisual2 is for blunt attacks + --HitVisual3 is for projectile attacks + --Basically, takes the attack property as defined by the weapon and shifts it left 2 + --For auto attacks attack property is weapon's damageAttributeType1 + --Still not totally sure how this works with weaponskills or what hitvisual4 or the other combinations are for + 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 | HitEffectType, + Shell = 1 << 7 | HitEffectType, + ProtectShellSpecial = Protect | Shell, + + Heal = 1 << 8,-- Required for heal text to be blue along with HealEffectType, not sure if that's all it's used for + + --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. + HitEffect3 = 1 << 11, --Yellow effect, crit? + HitEffect4 = 1 << 12, --Plays the blocking animation + HitEffect5 = 1 << 13, + GustyHitEffect = HitEffect3 | HitEffect2, + GreenTintedHitEffect = HitEffect4 | HitEffect1, + + --For specific animations + Miss = 0, + Evade = HitEffect1, + Hit = HitEffect1 | HitEffect2, + Parry = Hit | HitEffect3, + Block = HitEffect4, + Crit = HitEffect3, + + --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. +} \ No newline at end of file diff --git a/data/scripts/magic.lua b/data/scripts/magic.lua index a2965fa4..62b15504 100644 --- a/data/scripts/magic.lua +++ b/data/scripts/magic.lua @@ -1,5 +1,6 @@ -- todo: add enums for status effects in global.lua require("global") +require("battleutils") magic = { @@ -39,4 +40,32 @@ function magic.HandleStoneskin(caster, target, spell, action, statId, modifierId end; ]] return false; +end; + +function magic.onMagicFinish(caster, target, spell, action) + action.battleActionType = BattleActionType.AttackMagic; + local damage = math.random(50, 150); + action.amount = damage; + action.CalcHitType(caster, target, spell); + action.TryStatus(caster, target, spell, true); + return action.amount; +end; + +--For healing magic +function magic.onCureMagicFinish(caster, target, spell, action) + action.battleActionType = BattleActionType.Heal; + local amount = math.random(200, 450); + action.amount = amount; + action.CalcHitType(caster, target, spell); + action.TryStatus(caster, target, spell, true); + return action.amount; +end; + +--For status magic +function magic.onStatusMagicFinish(caster, target, spell, action) + action.battleActionType = BattleActionType.Status; + action.amount = 0; + action.CalcHitType(caster, target, spell); + action.TryStatus(caster, target, spell, false); + return action.amount; end; \ No newline at end of file diff --git a/data/scripts/modifiers.lua b/data/scripts/modifiers.lua index 8783e26c..793b8645 100644 --- a/data/scripts/modifiers.lua +++ b/data/scripts/modifiers.lua @@ -1,51 +1,57 @@ -modifiersGlobal = +modifiersGlobal = { - None = 0, - Hp = 1, - HpPercent = 2, - Mp = 3, - MpPercent = 4, - Tp = 5, - TpPercent = 6, - Regen = 7, - Refresh = 8, - Strength = 9, - Vitality = 10, - Dexterity = 11, - Intelligence = 12, - Mind = 13, - Piety = 14, - Attack = 15, - Accuracy = 16, - Defense = 17, - Evasion = 18, - MagicAttack = 19, - MagicHeal = 20, -- is this needed? shouldnt it just be calc'd from mind - MagicAccuracy = 21, - MagicEvasion = 22, - MagicDefense = 23, - MagicEnhancePotency = 24, - MagicEnfeeblingPotency = 25, - ResistFire = 26, - ResistIce = 27, - ResistWind = 28, - ResistLightning = 29, - ResistEarth = 30, - ResistWater = 31, -- <3 u jorge - AttackRange = 32, - Speed = 33, - AttackDelay = 34, - - CraftProcessing = 35, - CraftMagicProcessing = 36, - CraftProcessControl = 37, + None = 0, + Hp = 1, + HpPercent = 2, + Mp = 3, + MpPercent = 4, + Tp = 5, + TpPercent = 6, + Regen = 7, + Refresh = 8, + Strength = 9, + Vitality = 10, + Dexterity = 11, + Intelligence = 12, + Mind = 13, + Piety = 14, + Attack = 15, + Accuracy = 16, + Defense = 17, + Evasion = 18, + MagicAttack = 19, + MagicHeal = 20, -- is this needed? shouldnt it just be calc'd from mind + MagicAccuracy = 21, + MagicEvasion = 22, + MagicDefense = 23, + MagicEnhancePotency = 24, + MagicEnfeeblingPotency = 25, + ResistFire = 26, + ResistIce = 27, + ResistWind = 28, + ResistLightning = 29, + ResistEarth = 30, + ResistWater = 31, -- <3 u jorge + AttackRange = 32, + Speed = 33, + AttackDelay = 34, - HarvestPotency = 38, - HarvestLimit = 39, - HarvestRate = 40, + CraftProcessing = 35, + CraftMagicProcessing = 36, + CraftProcessControl = 37, - Raise = 41, - MinimumHpLock = 42, -- hp cannot fall below this value + HarvestPotency = 38, + HarvestLimit = 39, + HarvestRate = 40, + + Raise = 41, + MinimumHpLock = 42, -- hp cannot fall below this value + AttackType = 43, -- slashing, piercing, etc + BlockRate = 44, + Block = 45, + CritRating = 46, + HasShield = 47, + HitCount = 48 } mobModifiersGlobal = diff --git a/data/scripts/player.lua b/data/scripts/player.lua index 542e0aae..21e8ba52 100644 --- a/data/scripts/player.lua +++ b/data/scripts/player.lua @@ -2,6 +2,7 @@ local initClassItems, initRaceItems; function onBeginLogin(player) --New character, set the initial quest + --[[ if (player:GetPlayTime(false) == 0) then initialTown = player:GetInitialTown(); @@ -17,7 +18,7 @@ function onBeginLogin(player) end end - + ]] --For Opening. Set Director and reset position incase d/c if (player:HasQuest(110001) == true and player:GetZoneID() == 193) then director = player:GetZone():CreateDirector("OpeningDirector", false); diff --git a/data/scripts/spells/blizzara.lua b/data/scripts/spells/blizzara.lua deleted file mode 100644 index 72ccc0bd..00000000 --- a/data/scripts/spells/blizzara.lua +++ /dev/null @@ -1,19 +0,0 @@ -function onSpellPrepare(caster, target, spell) - return 0; -end; - -function onSpellStart(caster, target, spell) - return 0; -end; - -function onSpellFinish(caster, target, spell, action) - local damage = math.random(10, 100); - print("fuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuckkk") - - action.param = damage; - if target.hateContainer then - target.hateContainer.AddBaseHate(caster); - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; -end; \ No newline at end of file diff --git a/data/scripts/spells/thunder.lua b/data/scripts/spells/thunder.lua deleted file mode 100644 index 1418061a..00000000 --- a/data/scripts/spells/thunder.lua +++ /dev/null @@ -1,18 +0,0 @@ -function onSpellPrepare(caster, target, spell) - return 0; -end; - -function onSpellStart(caster, target, spell) - return 0; -end; - -function onSpellFinish(caster, target, spell, action) - local damage = math.random(10, 100); - print("fuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuckkk") - - if target.hateContainer then - target.hateContainer.AddBaseHate(caster); - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; -end; \ No newline at end of file diff --git a/data/scripts/statuseffectids.lua b/data/scripts/statuseffectids.lua new file mode 100644 index 00000000..fcf5cf6a --- /dev/null +++ b/data/scripts/statuseffectids.lua @@ -0,0 +1,318 @@ +StatusEffectId = +{ + RageofHalone = 221021, + + Quick = 223001, + Haste = 223002, + Slow = 223003, + Petrification = 223004, + Paralysis = 223005, + Silence = 223006, + Blind = 223007, + Mute = 223008, + Slowcast = 223009, + Glare = 223010, + Poison = 223011, + Transfixion = 223012, + Pacification = 223013, + Amnesia = 223014, + Stun = 223015, + Daze = 223016, + ExposedFront = 223017, + ExposedRight = 223018, + ExposedRear = 223019, + ExposedLeft = 223020, + Incapacitation = 223021, + Incapacitation2 = 223022, + Incapacitation3 = 223023, + Incapacitation4 = 223024, + Incapacitation5 = 223025, + Incapacitation6 = 223026, + Incapacitation7 = 223027, + Incapacitation8 = 223028, + HPBoost = 223029, + HPPenalty = 223030, + MPBoost = 223031, + MPPenalty = 223032, + AttackUp = 223033, + AttackDown = 223034, + AccuracyUp = 223035, + AccuracyDown = 223036, + DefenseUp = 223037, + DefenseDown = 223038, + EvasionUp = 223039, + EvasionDown = 223040, + MagicPotencyUp = 223041, + MagicPotencyDown = 223042, + MagicAccuracyUp = 223043, + MagicAccuracyDown = 223044, + MagicDefenseUp = 223045, + MagicDefenseDown = 223046, + MagicResistanceUp = 223047, + MagicResistanceDown = 223048, + CombatFinesse = 223049, + CombatHindrance = 223050, + MagicFinesse = 223051, + MagicHindrance = 223052, + CombatResilience = 223053, + CombatVulnerability = 223054, + MagicVulnerability = 223055, + MagicResilience = 223056, + Inhibited = 223057, + AegisBoon = 223058, + Deflection = 223059, + Outmaneuver = 223060, + Provoked = 223061, + Sentinel = 223062, + Cover = 223063, + Rampart = 223064, + StillPrecision = 223065, + Cadence = 223066, + DiscerningEye = 223067, + TemperedWill = 223068, + Obsess = 223069, + Ambidexterity = 223070, + BattleCalm = 223071, + MasterofArms = 223072, + Taunted = 223073, + Blindside = 223074, + Featherfoot = 223075, + PresenceofMind = 223076, + CoeurlStep = 223077, + EnduringMarch = 223078, + MurderousIntent = 223079, + Entrench = 223080, + Bloodbath = 223081, + Retaliation = 223082, + Foresight = 223083, + Defender = 223084, + Rampage = 223085, + Enraged = 223086, + Warmonger = 223087, + Disorientx1 = 223088, + Disorientx2 = 223089, + Disorientx3 = 223090, + KeenFlurry = 223091, + ComradeinArms = 223092, + Ferocity = 223093, + Invigorate = 223094, + LineofFire = 223095, + Jump = 223096, + Collusion = 223097, + Diversion = 223098, + SpeedSurge = 223099, + LifeSurge = 223100, + SpeedSap = 223101, + LifeSap = 223102, + Farshot = 223103, + QuellingStrike = 223104, + RagingStrike = 223105, + HawksEye = 223106, + SubtleRelease = 223107, + Decoy = 223108, + Profundity = 223109, + TranceChant = 223110, + RoamingSoul = 223111, + Purge = 223112, + Spiritsong = 223113, + Resonance = 223114, + Soughspeak = 223115, + PresenceofMind2 = 223116, + SanguineRite = 223117, + PunishingBarbs = 223118, + DarkSeal = 223119, + Emulate = 223120, + ParadigmShift = 223121, + ConcussiveBlowx1 = 223123, + ConcussiveBlowx2 = 223124, + ConcussiveBlowx3 = 223125, + SkullSunder = 223126, + Bloodletter = 223127, + Levinbolt = 223128, + Protect = 223129, + Shell = 223130, + Reraise = 223131, + ShockSpikes = 223132, + Stoneskin = 223133, + Scourge = 223134, + Bio = 223135, + Dia = 223136, + Banish = 223137, + StygianSpikes = 223138, + ATKAbsorbed = 223139, + DEFAbsorbed = 223140, + ACCAbsorbed = 223141, + EVAAbsorbed = 223142, + AbsorbATK = 223143, + AbsorbDEF = 223144, + AbsorbACC = 223145, + AbsorbEVA = 223146, + SoulWard = 223147, + Burn = 223148, + Frost = 223149, + Shock = 223150, + Drown = 223151, + Choke = 223152, + Rasp = 223153, + Flare = 223154, + Freeze = 223155, + Burst = 223156, + Flood = 223157, + Tornado = 223158, + Quake = 223159, + Berserk = 223160, + RegimenofRuin = 223161, + RegimenofTrauma = 223162, + RegimenofDespair = 223163, + RegimenofConstraint = 223164, + Weakness = 223165, + Scavenge = 223166, + Fastcast = 223167, + MidnightHowl = 223168, + Outlast = 223169, + Steadfast = 223170, + DoubleNock = 223171, + TripleNock = 223172, + Covered = 223173, + PerfectDodge = 223174, + ExpertMining = 223175, + ExpertLogging = 223176, + ExpertHarvesting = 223177, + ExpertFishing = 223178, + ExpertSpearfishing = 223179, + Regen = 223180, + Refresh = 223181, + Regain = 223182, + TPBleed = 223183, + Empowered = 223184, + Imperiled = 223185, + Adept = 223186, + Inept = 223187, + Quick2 = 223188, + Quick3 = 223189, + WristFlick = 223190, + Glossolalia = 223191, + SonorousBlast = 223192, + Comradery = 223193, + StrengthinNumbers = 223194, + + BrinkofDeath = 223197, + CraftersGrace = 223198, + GatherersGrace = 223199, + Rebirth = 223200, + Stealth = 223201, + StealthII = 223202, + StealthIII = 223203, + StealthIV = 223204, + Combo = 223205, + GoringBlade = 223206, + Berserk2 = 223207, + Rampage2 = 223208, + FistsofFire = 223209, + FistsofEarth = 223210, + FistsofWind = 223211, + PowerSurgeI = 223212, + PowerSurgeII = 223213, + PowerSurgeIII = 223214, + LifeSurgeI = 223215, + LifeSurgeII = 223216, + LifeSurgeIII = 223217, + DreadSpike = 223218, + BloodforBlood = 223219, + Barrage = 223220, + RagingStrike2 = 223221, + + Swiftsong = 223224, + SacredPrism = 223225, + ShroudofSaints = 223226, + ClericStance = 223227, + BlissfulMind = 223228, + DarkSeal2 = 223229, + Resonance2 = 223230, + Excruciate = 223231, + Necrogenesis = 223232, + Parsimony = 223233, + SanguineRite2 = 223234, + Aero = 223235, + Outmaneuver2 = 223236, + Blindside2 = 223237, + Decoy2 = 223238, + Protect2 = 223239, + SanguineRite3 = 223240, + Bloodletter2 = 223241, + FullyBlissfulMind = 223242, + MagicEvasionDown = 223243, + HundredFists = 223244, + SpinningHeel = 223245, + DivineVeil = 223248, + HallowedGround = 223249, + Vengeance = 223250, + Antagonize = 223251, + MightyStrikes = 223252, + BattleVoice = 223253, + BalladofMagi = 223254, + PaeonofWar = 223255, + MinuetofRigor = 223256, + GoldLung = 223258, + Goldbile = 223259, + AurumVeil = 223260, + AurumVeilII = 223261, + Flare2 = 223262, + Resting = 223263, + DivineRegen = 223264, + DefenseAndEvasionUp = 223265, + MagicDefenseAndEvasionUp = 223266, + AttackUp2 = 223267, + MagicPotencyUp2 = 223268, + DefenseAndEvasionDown = 223269, + MagicDefenseAndEvasionDown = 223270, + Poison2 = 223271, + DeepBurn = 223272, + LunarCurtain = 223273, + DefenseUp2 = 223274, + AttackDown2 = 223275, + Sanction = 223992, + IntactPodlingToting = 223993, + RedRidingHooded = 223994, + Medicated = 223998, + WellFed = 223999, + + Sleep = 228001, + Bind = 228011, + Fixation = 228012, + Bind2 = 228013, + Heavy = 228021, + Charm = 228031, + Flee = 228041, + Doom = 228051, + SynthesisSupport = 230001, + WoodyardAccess = 230002, + SmithsForgeAccess = 230003, + ArmorersForgeAccess = 230004, + GemmaryAccess = 230005, + TanneryAccess = 230006, + ClothshopAccess = 230007, + LaboratoryAccess = 230008, + CookeryAccess = 230009, + MinersSupport = 230010, + BotanistsSupport = 230011, + FishersSupport = 230012, + GearChange = 230013, + GearDamage = 230014, + HeavyGearDamage = 230015, + Lamed = 230016, + Lamed2 = 230017, + Lamed3 = 230018, + Poison3 = 231002, + Envenom = 231003, + Berserk4 = 231004, + GuardiansAspect = 253002, + + + -- custom effects here + -- status for having procs fall off + EvadeProc = 253003, + BlockProc = 253004, + ParryProc = 253005, + MissProc = 253006 +} \ No newline at end of file diff --git a/data/scripts/unique/fst0Battle03/Monster/bloodthirsty_wolf.lua b/data/scripts/unique/fst0Battle03/Monster/bloodthirsty_wolf.lua index a4e24895..e69de29b 100644 --- a/data/scripts/unique/fst0Battle03/Monster/bloodthirsty_wolf.lua +++ b/data/scripts/unique/fst0Battle03/Monster/bloodthirsty_wolf.lua @@ -1,22 +0,0 @@ -require ("modifiers") - -function onSpawn(mob) - mob:SetMod(modifiersGlobal.Speed, 0) -end - -function onDamageTaken(mob, attacker, damage) - if not attacker:IsPlayer() and mob:GetHP() - damage < 0 then - mob:addHP(damage) - end -end - -function onCombatTick(mob, target, tick, contentGroupCharas) - if mob:GetSpeed() == 0 then - mob:SetMod(modifiersGlobal.Speed, 8) - end -end - -function onDisengage(mob) - mob:SetMod(modifiersGlobal.Speed, 0) - mob:Despawn() -end \ No newline at end of file diff --git a/data/scripts/unique/fst0Battle03/Monster/papalymo.lua b/data/scripts/unique/fst0Battle03/Monster/papalymo.lua index 6ce72e68..afdd66d9 100644 --- a/data/scripts/unique/fst0Battle03/Monster/papalymo.lua +++ b/data/scripts/unique/fst0Battle03/Monster/papalymo.lua @@ -1,5 +1,4 @@ require ("global") -require ("modifiers") require ("ally") function onSpawn(ally) diff --git a/data/scripts/unique/fst0Battle03/Monster/yda.lua b/data/scripts/unique/fst0Battle03/Monster/yda.lua index c494f047..4462774b 100644 --- a/data/scripts/unique/fst0Battle03/Monster/yda.lua +++ b/data/scripts/unique/fst0Battle03/Monster/yda.lua @@ -1,12 +1,12 @@ require ("global") + require ("ally") function onSpawn(ally) ally:SetMaxHP(69420) ally:SetHP(ally:GetMaxHP()) - ally.isAutoAttackEnabled = false; + ally.isAutoAttackEnabled = false ally.neutral = false - ally:SetMod(modifiersGlobal.Speed, 0) end function onCombatTick(ally, target, tick, contentGroupCharas) @@ -23,4 +23,4 @@ function onRoam(ally, contentGroupCharas) ally.neutral = false ally.animationId = 0 --allyGlobal.onCombatTick(ally, contentGroupCharas) -end +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua b/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua index 6bfcb1fc..e0e54fdf 100644 --- a/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua +++ b/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua @@ -8,7 +8,8 @@ end function onEventStarted(player, npc, triggerName) man0g0Quest = player:GetQuest("Man0g0"); print("Got Quest Man0g0"); - if (man0g0Quest ~= nil) then + if (man0g0Quest ~= nil) then + print("Man0g0Quest is not nil"); if (triggerName == "pushDefault") then callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrNomal002", nil, nil, nil); @@ -36,13 +37,14 @@ function onEventStarted(player, npc, triggerName) end director = contentArea:GetContentDirector(); - player:AddDirector(director); + --player:AddDirector(director); director:StartDirector(false); player:KickEvent(director, "noticeEvent", true); player:SetLoginDirector(director); print("Content area and director made"); + player:ChangeState(0); GetWorldManager():DoZoneChangeContent(player, contentArea, 362.4087, 4, -703.8168, 1.5419, 16); print("Zone Change"); return; diff --git a/data/scripts/unique/fst0Town01/Monster/ass.lua b/data/scripts/unique/fst0Town01/Monster/ass.lua index 3ab4e56a..21e27867 100644 --- a/data/scripts/unique/fst0Town01/Monster/ass.lua +++ b/data/scripts/unique/fst0Town01/Monster/ass.lua @@ -1,3 +1,2 @@ function onDeath(monster, player, lastAttacker) - end \ No newline at end of file diff --git a/data/scripts/weaponskill.lua b/data/scripts/weaponskill.lua index 84d79cc7..6f18f5fa 100644 --- a/data/scripts/weaponskill.lua +++ b/data/scripts/weaponskill.lua @@ -1,17 +1,17 @@ -- todo: add enums for status effects in global.lua require("global") - -weaponskill = -{ - -}; - +require("battleutils") --[[ statId - see BattleTemp.cs modifier - Modifier.Intelligence, Modifier.Mind (see Modifier.cs) multiplier - ]] -function weaponskill.HandleHealingSkill(caster, target, spell, action, statId, modifierId, multiplier, baseAmount) +weaponskill = +{ + +} + +function HandleHealingSkill(caster, target, skill, action, statId, modifierId, multiplier, baseAmount) potency = potency or 1.0; healAmount = baseAmount; @@ -19,19 +19,14 @@ function weaponskill.HandleHealingSkill(caster, target, spell, action, statId, m local mind = caster.GetMod(Modifier.Mind); end; -function weaponskill.HandleAttackSkill(caster, target, spell, action, statId, modifierId, multiplier, baseAmount) +function HandleAttackSkill(caster, target, skill, action, statId, modifierId, multiplier, baseAmount) -- todo: actually handle this damage = baseAmount or math.random(1,10) * 10; return damage; end; -function weaponskill.HandleEvasion(caster, target, spell, action, statId, modifierId) - - return false; -end; - -function weaponskill.HandleStoneskin(caster, target, spell, action, statId, modifierId, damage) +function HandleStoneskin(caster, target, skill, action, statId, modifierId, damage) --[[ if target.statusEffects.HasStatusEffect(StatusEffect.Stoneskin) then -- todo: damage reduction @@ -39,4 +34,15 @@ function weaponskill.HandleStoneskin(caster, target, spell, action, statId, modi end; ]] return false; +end; + +--The default onskillfinish for weaponskills. +function weaponskill.onSkillFinish(caster, target, skill, action) + action.battleActionType = BattleActionType.AttackPhysical; + local damage = math.random(50, 150); + action.amount = damage; + + action.CalcHitType(caster, target, skill); + action.TryStatus(caster, target, skill); + return action.amount; end; \ No newline at end of file diff --git a/sql/characters_hotbar.sql b/sql/characters_hotbar.sql index b9c2ce4d..1952ef93 100644 --- a/sql/characters_hotbar.sql +++ b/sql/characters_hotbar.sql @@ -1,8 +1,8 @@ --- MySQL dump 10.13 Distrib 5.7.10, for Win64 (x86_64) +-- MySQL dump 10.13 Distrib 5.7.11, for Win64 (x86_64) -- --- Host: localhost Database: ffxiv_database +-- Host: localhost Database: ffxiv_server -- ------------------------------------------------------ --- Server version 5.7.10-log +-- Server version 5.7.11 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -27,8 +27,8 @@ CREATE TABLE `characters_hotbar` ( `classId` smallint(5) unsigned NOT NULL, `hotbarSlot` smallint(5) unsigned NOT NULL, `commandId` int(10) unsigned NOT NULL, - `recastTime` int(10) unsigned DEFAULT NULL, - PRIMARY KEY (`characterId`, `classId`, `hotbarSlot`) + `recastTime` int(10) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`characterId`,`classId`,`hotbarSlot`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; @@ -50,4 +50,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2016-06-07 22:54:46 +-- Dump completed on 2018-02-15 0:04:39 diff --git a/sql/characters_statuseffect.sql b/sql/characters_statuseffect.sql index f43f4c79..7f9df064 100644 --- a/sql/characters_statuseffect.sql +++ b/sql/characters_statuseffect.sql @@ -1,8 +1,8 @@ --- MySQL dump 10.13 Distrib 5.7.10, for Win64 (x86_64) +-- MySQL dump 10.13 Distrib 5.7.11, for Win64 (x86_64) -- --- Host: localhost Database: ffxiv_database +-- Host: localhost Database: ffxiv_server -- ------------------------------------------------------ --- Server version 5.7.10-log +-- Server version 5.7.11 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -24,13 +24,13 @@ DROP TABLE IF EXISTS `characters_statuseffect`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `characters_statuseffect` ( `characterId` int(10) unsigned NOT NULL, - `statusId` smallint(5) unsigned NOT NULL, + `statusId` mediumint(8) unsigned NOT NULL, `magnitude` bigint(20) unsigned NOT NULL, `duration` int(10) unsigned NOT NULL, `tick` int(10) unsigned NOT NULL, `tier` tinyint(3) unsigned NOT NULL, `extra` bigint(20) unsigned NOT NULL, - PRIMARY KEY (`characterId`, `statusId`) + PRIMARY KEY (`characterId`,`statusId`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; @@ -52,4 +52,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2016-06-07 22:54:49 +-- Dump completed on 2018-02-15 0:04:42 diff --git a/sql/server_battle_commands.sql b/sql/server_battle_commands.sql index a95edb12..b1f9e2d8 100644 --- a/sql/server_battle_commands.sql +++ b/sql/server_battle_commands.sql @@ -1,8 +1,8 @@ --- MySQL dump 10.13 Distrib 5.7.18, for Win64 (x86_64) +-- MySQL dump 10.13 Distrib 5.7.11, for Win64 (x86_64) -- -- Host: localhost Database: ffxiv_server -- ------------------------------------------------------ --- Server version 5.7.18-log +-- Server version 5.7.11 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -28,16 +28,20 @@ CREATE TABLE `server_battle_commands` ( `classJob` tinyint(3) unsigned NOT NULL, `lvl` tinyint(3) unsigned NOT NULL, `requirements` smallint(5) unsigned NOT NULL, + `mainTarget` tinyint(3) unsigned NOT NULL, `validTarget` tinyint(3) unsigned NOT NULL, `aoeType` tinyint(3) unsigned NOT NULL, `aoeRange` int(10) NOT NULL DEFAULT '0', `aoeTarget` tinyint(3) NOT NULL, + `basePotency` smallint(5) unsigned NOT NULL, `numHits` tinyint(3) unsigned NOT NULL, `positionBonus` tinyint(3) unsigned NOT NULL, `procRequirement` tinyint(3) unsigned NOT NULL, `range` int(10) unsigned NOT NULL, - `buffDuration` int(10) unsigned NOT NULL, - `debuffDuration` int(10) unsigned NOT NULL, + `bestRange` int(10) unsigned NOT NULL DEFAULT '0', + `statusId` int(10) NOT NULL, + `statusDuration` int(10) unsigned NOT NULL, + `statusChance` float NOT NULL DEFAULT '0.5', `castType` tinyint(3) unsigned NOT NULL, `castTime` int(10) unsigned NOT NULL, `recastTime` int(10) unsigned NOT NULL, @@ -49,6 +53,11 @@ CREATE TABLE `server_battle_commands` ( `animationDuration` smallint(5) unsigned NOT NULL, `battleAnimation` int(10) unsigned NOT NULL DEFAULT '0', `validUser` tinyint(3) unsigned NOT NULL DEFAULT '0', + `comboId1` int(11) NOT NULL DEFAULT '0', + `comboId2` int(11) NOT NULL DEFAULT '0', + `comboStep` tinyint(4) NOT NULL DEFAULT '0', + `accuracyMod` float unsigned NOT NULL DEFAULT '1', + `worldMasterTextId` smallint(5) unsigned DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -60,148 +69,148 @@ CREATE TABLE `server_battle_commands` ( LOCK TABLES `server_battle_commands` WRITE; /*!40000 ALTER TABLE `server_battle_commands` DISABLE KEYS */; set autocommit=0; -INSERT INTO `server_battle_commands` VALUES (27100,'second_wind',2,6,3,1,0,0,0,1,0,0,5,0,0,0,0,45,0,0,14,519,2,2,234889735,0); -INSERT INTO `server_battle_commands` VALUES (27101,'blindside',2,14,3,1,0,0,0,1,0,0,5,0,60,0,0,60,0,0,14,635,2,2,234889851,0); -INSERT INTO `server_battle_commands` VALUES (27102,'taunt',2,42,4,32,0,0,0,1,0,0,5,5,0,0,0,60,0,0,14,517,2,2,234889733,0); -INSERT INTO `server_battle_commands` VALUES (27103,'featherfoot',2,2,3,1,0,0,0,1,0,0,5,0,30,0,0,60,0,0,14,535,2,2,234889751,0); -INSERT INTO `server_battle_commands` VALUES (27104,'fists_of_fire',2,34,4,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,684,2,2,234889900,0); -INSERT INTO `server_battle_commands` VALUES (27105,'fists_of_earth',2,22,4,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,685,2,2,234889901,0); -INSERT INTO `server_battle_commands` VALUES (27106,'hundred_fists',15,50,0,0,0,0,0,1,0,0,5,0,0,0,0,900,0,0,14,712,2,2,234889928,0); -INSERT INTO `server_battle_commands` VALUES (27107,'spinning_heel',15,35,0,0,0,0,0,1,0,0,5,0,0,0,0,120,0,0,14,718,2,2,234889934,0); -INSERT INTO `server_battle_commands` VALUES (27108,'shoulder_tackle',15,30,0,0,0,0,0,1,0,0,5,0,0,0,0,60,0,0,18,1048,205,2,302830616,0); -INSERT INTO `server_battle_commands` VALUES (27109,'fists_of_wind',15,40,0,0,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,720,2,2,234889936,0); -INSERT INTO `server_battle_commands` VALUES (27110,'pummel',2,1,1,32,0,0,0,1,1,0,5,0,0,0,0,10,0,1000,18,1027,1,2,301995011,0); -INSERT INTO `server_battle_commands` VALUES (27111,'concussive_blow',2,10,1,32,0,0,0,1,4,0,5,30,0,0,0,30,0,1500,18,20,3,2,302002196,0); -INSERT INTO `server_battle_commands` VALUES (27112,'simian_thrash',2,50,4,32,0,0,0,1,0,0,5,0,0,0,0,80,0,2000,18,1003,202,2,302818283,0); -INSERT INTO `server_battle_commands` VALUES (27113,'aura_pulse',2,38,4,32,1,0,0,1,0,0,5,30,0,0,0,40,0,1500,18,66,203,2,302821442,0); -INSERT INTO `server_battle_commands` VALUES (27114,'pounce',2,4,4,32,0,0,0,1,2,0,5,10,0,0,0,20,0,1500,18,8,3,2,302002184,0); -INSERT INTO `server_battle_commands` VALUES (27115,'demolish',2,30,1,32,0,0,0,1,0,0,5,0,0,0,0,30,0,1500,18,1028,2,2,301999108,0); -INSERT INTO `server_battle_commands` VALUES (27116,'howling_fist',2,46,4,32,0,0,0,1,4,0,5,0,0,0,0,80,0,3000,18,1029,2,2,301999109,0); -INSERT INTO `server_battle_commands` VALUES (27117,'sucker_punch',2,26,1,32,0,0,0,1,4,0,5,0,0,0,0,15,0,1000,18,73,3,2,302002249,0); -INSERT INTO `server_battle_commands` VALUES (27118,'dragon_kick',15,45,0,0,0,0,0,1,0,0,5,0,0,0,0,60,0,2000,18,1041,204,2,302826513,0); -INSERT INTO `server_battle_commands` VALUES (27119,'haymaker',2,18,4,32,0,0,0,1,0,1,5,0,0,0,0,5,0,250,18,23,201,2,302813207,0); -INSERT INTO `server_battle_commands` VALUES (27140,'sentinel',3,22,3,1,0,0,0,1,0,0,5,0,15,0,0,90,0,0,14,526,2,2,234889742,0); -INSERT INTO `server_battle_commands` VALUES (27141,'aegis_boon',3,6,8,1,0,0,0,1,0,0,5,0,30,0,0,60,0,0,14,583,21,2,234967623,0); -INSERT INTO `server_battle_commands` VALUES (27142,'rampart',3,2,3,1,0,0,0,1,0,2,5,0,60,0,0,120,0,0,14,536,2,2,234889752,0); -INSERT INTO `server_battle_commands` VALUES (27143,'tempered_will',3,42,8,1,0,0,0,1,0,0,5,0,20,0,0,180,0,0,14,515,2,2,234889731,0); -INSERT INTO `server_battle_commands` VALUES (27144,'outmaneuver',3,34,8,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,512,21,2,234967552,0); -INSERT INTO `server_battle_commands` VALUES (27145,'flash',3,14,3,32,0,0,0,1,0,0,5,0,0,0,0,30,0,0,14,696,2,2,234889912,0); -INSERT INTO `server_battle_commands` VALUES (27146,'cover',16,30,0,0,0,0,0,1,0,0,5,0,15,0,0,60,0,0,14,725,2,2,234889941,0); -INSERT INTO `server_battle_commands` VALUES (27147,'divine_veil',16,35,0,0,0,0,0,1,0,0,5,0,20,0,0,60,0,0,14,713,2,2,234889929,0); -INSERT INTO `server_battle_commands` VALUES (27148,'hallowed_ground',16,50,0,0,0,0,0,1,0,0,5,0,0,0,0,900,0,0,14,709,2,2,234889925,0); -INSERT INTO `server_battle_commands` VALUES (27149,'holy_succor',16,40,0,0,0,0,0,1,0,0,15,0,0,0,2,10,100,0,1,701,1,2,16782013,0); -INSERT INTO `server_battle_commands` VALUES (27150,'fast_blade',3,1,1,32,0,0,0,1,1,0,5,0,0,0,0,10,0,1000,18,1023,1,2,301995007,0); -INSERT INTO `server_battle_commands` VALUES (27151,'flat_blade',3,26,1,32,0,0,0,1,0,0,5,0,0,0,0,10,0,1500,18,1024,2,2,301999104,0); -INSERT INTO `server_battle_commands` VALUES (27152,'savage_blade',3,10,1,32,0,0,0,1,0,0,5,0,0,0,0,30,0,1000,18,1025,1,2,301995009,0); -INSERT INTO `server_battle_commands` VALUES (27153,'goring_blade',3,50,8,32,0,0,0,1,2,0,5,30,0,0,0,60,0,3000,18,1026,301,2,303223810,0); -INSERT INTO `server_battle_commands` VALUES (27154,'riot_blade',3,30,8,32,0,0,0,1,2,0,5,30,0,0,0,80,0,2000,18,75,2,2,301998155,0); -INSERT INTO `server_battle_commands` VALUES (27155,'rage_of_halone',3,46,8,32,0,0,0,1,0,0,5,0,0,0,0,20,0,1500,18,1008,302,2,303227888,0); -INSERT INTO `server_battle_commands` VALUES (27156,'shield_bash',3,18,17,32,0,0,0,1,0,0,5,5,0,0,0,30,0,250,18,5,26,2,302096389,0); -INSERT INTO `server_battle_commands` VALUES (27157,'war_drum',3,38,24,32,1,0,0,1,0,2,5,0,0,0,0,60,0,500,14,502,21,2,234967542,0); -INSERT INTO `server_battle_commands` VALUES (27158,'phalanx',3,4,8,1,0,0,0,1,0,0,5,0,0,0,0,5,0,250,18,32,1,2,301994016,0); -INSERT INTO `server_battle_commands` VALUES (27159,'spirits_within',16,45,0,0,0,0,0,1,0,0,5,0,0,0,0,60,0,3000,18,1044,304,2,303236116,0); -INSERT INTO `server_battle_commands` VALUES (27180,'provoke',4,14,3,32,0,0,0,1,0,0,5,0,0,0,0,30,0,0,14,600,2,2,234889816,0); -INSERT INTO `server_battle_commands` VALUES (27181,'foresight',4,2,3,1,0,0,0,1,0,0,5,0,30,0,0,60,0,0,14,545,2,2,234889761,0); -INSERT INTO `server_battle_commands` VALUES (27182,'bloodbath',4,6,3,1,0,0,0,1,0,0,5,0,30,0,0,60,0,0,14,581,2,2,234889797,0); -INSERT INTO `server_battle_commands` VALUES (27183,'berserk',4,22,32,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,682,2,2,234889898,0); -INSERT INTO `server_battle_commands` VALUES (27184,'rampage',4,34,32,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,546,2,2,234889762,0); -INSERT INTO `server_battle_commands` VALUES (27185,'enduring_march',4,42,32,1,0,0,0,1,0,0,5,0,20,0,0,180,0,0,14,539,2,2,234889755,0); -INSERT INTO `server_battle_commands` VALUES (27186,'vengeance',17,30,0,1,0,0,0,1,0,0,5,0,0,0,0,150,0,0,14,714,2,2,234889930,0); -INSERT INTO `server_battle_commands` VALUES (27187,'antagonize',17,35,0,1,0,0,0,1,0,0,5,0,0,0,0,120,0,0,14,715,2,2,234889931,0); -INSERT INTO `server_battle_commands` VALUES (27188,'collusion',17,40,0,4,0,0,0,1,0,0,5,0,0,0,0,90,0,0,14,711,2,2,234889927,0); -INSERT INTO `server_battle_commands` VALUES (27189,'mighty_strikes',17,50,0,1,0,0,0,1,0,0,5,0,0,0,0,900,0,0,14,716,2,2,234889932,0); -INSERT INTO `server_battle_commands` VALUES (27190,'heavy_swing',4,1,1,32,0,0,0,1,1,0,5,0,0,0,0,10,0,1000,18,14,1,2,301993998,0); -INSERT INTO `server_battle_commands` VALUES (27191,'skull_sunder',4,10,1,32,0,0,0,1,0,0,5,0,0,0,0,30,0,1500,18,43,1,2,301994027,0); -INSERT INTO `server_battle_commands` VALUES (27192,'steel_cyclone',17,45,0,32,1,0,0,1,0,0,5,0,0,0,0,30,0,2000,18,1040,404,2,303645712,0); -INSERT INTO `server_battle_commands` VALUES (27193,'brutal_swing',4,4,1,32,0,0,0,1,4,0,5,0,0,0,0,20,0,1500,18,15,3,2,302002191,0); -INSERT INTO `server_battle_commands` VALUES (27194,'maim',4,26,1,32,0,0,0,1,0,0,5,0,0,0,0,30,0,1500,18,88,1,2,301994072,0); -INSERT INTO `server_battle_commands` VALUES (27195,'godsbane',4,50,32,32,0,0,0,1,0,0,5,0,0,0,0,60,0,3000,18,1014,402,2,303637494,0); -INSERT INTO `server_battle_commands` VALUES (27196,'path_of_the_storm',4,38,32,32,0,0,0,1,2,0,5,0,0,0,0,30,0,1500,18,44,401,2,303632428,0); -INSERT INTO `server_battle_commands` VALUES (27197,'whirlwind',4,46,32,32,1,0,0,1,0,0,5,0,0,0,0,80,0,3000,18,1015,403,2,303641591,0); -INSERT INTO `server_battle_commands` VALUES (27198,'fracture',4,18,32,32,0,0,0,1,0,4,5,8,0,0,0,40,0,500,18,42,3,2,302002218,0); -INSERT INTO `server_battle_commands` VALUES (27199,'overpower',4,30,1,32,2,0,0,1,0,4,5,0,0,0,0,5,0,250,18,89,1,2,301994073,0); -INSERT INTO `server_battle_commands` VALUES (27220,'hawks_eye',7,6,3,1,0,0,0,1,0,0,5,0,15,0,0,90,0,0,14,516,2,2,234889732,0); -INSERT INTO `server_battle_commands` VALUES (27221,'quelling_strikes',7,22,3,1,0,0,0,1,0,0,5,30,0,0,0,60,0,0,14,614,2,2,234889830,0); -INSERT INTO `server_battle_commands` VALUES (27222,'decoy',7,2,3,1,0,0,0,1,0,0,15,0,60,0,0,90,100,0,14,565,2,2,234889781,0); -INSERT INTO `server_battle_commands` VALUES (27223,'chameleon',7,42,3,1,0,0,0,1,0,0,5,0,0,0,0,180,0,0,14,504,2,2,234889720,0); -INSERT INTO `server_battle_commands` VALUES (27224,'barrage',7,34,64,1,0,0,0,1,0,0,5,0,60,0,0,90,0,0,14,683,2,2,234889899,0); -INSERT INTO `server_battle_commands` VALUES (27225,'raging_strikes',7,14,64,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,632,2,2,234889848,0); -INSERT INTO `server_battle_commands` VALUES (27226,'swiftsong',7,26,64,1,1,0,0,1,0,0,15,0,180,0,0,10,100,0,1,150,1,2,16781462,0); -INSERT INTO `server_battle_commands` VALUES (27227,'battle_voice',18,50,0,1,1,0,0,1,0,0,5,0,0,0,0,900,0,0,14,721,2,2,234889937,0); -INSERT INTO `server_battle_commands` VALUES (27228,'heavy_shot',7,1,1,32,0,0,0,1,0,0,5,0,0,0,0,10,0,1000,18,1036,4,2,302007308,0); -INSERT INTO `server_battle_commands` VALUES (27229,'leaden_arrow',7,10,1,32,0,0,0,1,0,0,5,30,0,0,0,30,0,1500,18,1035,4,2,302007307,0); -INSERT INTO `server_battle_commands` VALUES (27230,'wide_volley',7,50,64,32,1,0,0,1,0,0,5,0,0,0,0,80,0,2000,18,18,703,2,304869394,0); -INSERT INTO `server_battle_commands` VALUES (27231,'quick_nock',7,38,64,32,2,0,0,1,0,0,5,0,0,0,0,180,0,1000,18,1017,702,2,304866297,0); -INSERT INTO `server_battle_commands` VALUES (27232,'rain_of_death',18,45,0,32,1,0,0,1,0,0,5,0,0,0,0,30,0,3000,18,1037,704,2,304874509,0); -INSERT INTO `server_battle_commands` VALUES (27233,'piercing_arrow',7,4,1,32,0,0,0,1,0,0,5,0,0,0,0,20,0,1000,18,1038,1,2,301995022,0); -INSERT INTO `server_battle_commands` VALUES (27234,'gloom_arrow',7,30,1,32,0,0,0,1,0,0,5,30,0,0,0,10,0,1000,18,1039,4,2,302007311,0); -INSERT INTO `server_battle_commands` VALUES (27235,'bloodletter',7,46,64,32,0,0,0,1,0,0,5,30,0,0,0,80,0,1500,18,53,701,2,304861237,0); -INSERT INTO `server_battle_commands` VALUES (27236,'shadowbind',7,18,64,32,0,0,0,1,0,0,5,30,0,0,0,40,0,250,18,17,4,2,302006289,0); -INSERT INTO `server_battle_commands` VALUES (27237,'ballad_of_magi',18,30,0,1,1,0,0,1,0,0,15,0,0,0,3,10,100,0,1,709,1,2,16782021,0); -INSERT INTO `server_battle_commands` VALUES (27238,'paeon_of_war',18,40,0,1,1,0,0,1,0,0,15,0,0,0,3,10,50,1000,1,710,1,2,16782022,0); -INSERT INTO `server_battle_commands` VALUES (27239,'minuet_of_rigor',18,35,0,1,1,0,0,1,0,0,15,0,0,0,3,10,100,0,1,711,1,2,16782023,0); -INSERT INTO `server_battle_commands` VALUES (27258,'refill',7,1,0,1,0,0,0,1,0,0,5,0,0,0,0,0,0,0,0,0,0,2,0,0); -INSERT INTO `server_battle_commands` VALUES (27259,'light_shot',7,1,0,32,0,0,0,1,0,0,5,0,0,0,0,0,0,0,0,0,0,2,0,0); -INSERT INTO `server_battle_commands` VALUES (27260,'invigorate',8,14,3,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,575,2,2,234889791,0); -INSERT INTO `server_battle_commands` VALUES (27261,'power_surge',8,34,128,1,0,0,0,1,0,0,5,0,0,0,0,10,0,0,14,686,2,2,234889902,0); -INSERT INTO `server_battle_commands` VALUES (27262,'life_surge',8,22,128,1,0,0,0,1,0,0,5,0,0,0,0,15,0,250,14,687,2,2,234889903,0); -INSERT INTO `server_battle_commands` VALUES (27263,'dread_spike',8,42,128,1,0,0,0,1,0,0,5,0,0,0,0,120,0,0,14,686,2,2,234889902,0); -INSERT INTO `server_battle_commands` VALUES (27264,'blood_for_blood',8,6,3,1,0,0,0,1,0,0,5,0,0,0,0,60,0,0,14,689,2,2,234889905,0); -INSERT INTO `server_battle_commands` VALUES (27265,'keen_flurry',8,26,3,1,0,0,0,1,0,0,5,0,0,0,0,90,0,0,14,569,2,2,234889785,0); -INSERT INTO `server_battle_commands` VALUES (27266,'jump',19,30,0,32,0,0,0,1,0,0,5,0,0,0,0,60,0,0,18,1045,804,2,305284117,0); -INSERT INTO `server_battle_commands` VALUES (27267,'elusive_jump',19,40,0,1,0,0,0,1,0,0,5,0,0,0,0,180,0,0,18,1046,806,2,305292310,0); -INSERT INTO `server_battle_commands` VALUES (27268,'dragonfire_dive',19,50,0,32,1,0,0,1,0,0,5,0,0,0,0,900,0,0,18,1045,804,2,305284117,0); -INSERT INTO `server_battle_commands` VALUES (27269,'true_thrust',8,1,1,32,0,0,0,1,1,0,5,0,0,0,0,10,0,1000,18,1030,2,2,301999110,0); -INSERT INTO `server_battle_commands` VALUES (27270,'leg_sweep',8,30,1,32,1,0,0,1,0,0,5,8,0,0,0,30,0,1000,18,37,1,2,301994021,0); -INSERT INTO `server_battle_commands` VALUES (27271,'doom_spike',8,46,128,32,3,0,0,1,0,0,5,0,0,0,0,60,0,3000,18,83,801,2,305270867,0); -INSERT INTO `server_battle_commands` VALUES (27272,'disembowel',19,35,0,32,0,0,0,1,0,0,5,0,0,0,0,30,0,750,18,1042,2,2,301999122,0); -INSERT INTO `server_battle_commands` VALUES (27273,'heavy_thrust',8,10,1,32,0,0,0,1,0,0,5,4,0,0,0,20,0,1500,18,1031,1,2,301995015,0); -INSERT INTO `server_battle_commands` VALUES (27274,'vorpal_thrust',8,2,1,32,0,0,0,1,2,0,5,0,0,0,0,20,0,1500,18,1032,2,2,301999112,0); -INSERT INTO `server_battle_commands` VALUES (27275,'impulse_drive',8,18,1,32,0,0,0,1,4,0,5,0,0,0,0,30,0,1500,18,1033,2,2,301999113,0); -INSERT INTO `server_battle_commands` VALUES (27276,'chaos_thrust',8,50,128,32,0,0,0,1,0,0,5,0,0,0,0,80,0,3000,18,40,802,2,305274920,0); -INSERT INTO `server_battle_commands` VALUES (27277,'ring_of_talons',19,45,0,32,1,0,0,1,0,0,5,0,0,0,0,60,0,2000,18,1009,803,2,305279985,0); -INSERT INTO `server_battle_commands` VALUES (27278,'feint',8,4,1,32,0,0,0,1,0,8,5,0,0,0,0,10,0,250,18,39,2,2,301998119,0); -INSERT INTO `server_battle_commands` VALUES (27279,'full_thrust',8,38,128,32,0,0,0,1,0,8,5,0,0,0,0,30,0,250,18,1034,801,2,305271818,0); -INSERT INTO `server_battle_commands` VALUES (27300,'dark_seal',22,14,3,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,518,2,2,234889734,0); -INSERT INTO `server_battle_commands` VALUES (27301,'resonance',22,22,3,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,669,2,2,234889885,0); -INSERT INTO `server_battle_commands` VALUES (27302,'excruciate',22,38,256,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,694,2,2,234889910,0); -INSERT INTO `server_battle_commands` VALUES (27303,'necrogenesis',22,6,3,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,695,2,2,234889911,0); -INSERT INTO `server_battle_commands` VALUES (27304,'parsimony',22,2,256,1,0,0,0,1,0,0,5,0,30,0,0,90,0,0,14,568,2,2,234889784,0); -INSERT INTO `server_battle_commands` VALUES (27305,'convert',26,30,0,1,0,0,0,1,0,0,5,0,0,0,0,450,0,0,14,724,2,2,234889940,0); -INSERT INTO `server_battle_commands` VALUES (27306,'sleep',22,42,256,32,0,0,0,1,0,0,15,60,0,0,3,0,75,0,1,651,1,2,16781963,0); -INSERT INTO `server_battle_commands` VALUES (27307,'sanguine_rite',22,30,3,1,0,0,0,1,0,0,15,0,20,0,0,60,120,0,1,152,1,2,16781464,0); -INSERT INTO `server_battle_commands` VALUES (27308,'blizzard',22,4,256,32,0,0,0,1,0,0,15,30,0,0,3,10,90,0,1,502,1,2,16781814,0); -INSERT INTO `server_battle_commands` VALUES (27309,'blizzara',22,26,256,32,1,0,0,1,0,0,15,30,0,0,0,40,150,0,1,506,1,2,16781818,0); -INSERT INTO `server_battle_commands` VALUES (27310,'fire',22,10,3,32,1,0,0,1,0,0,15,0,0,0,3,8,105,0,1,501,1,2,16781813,0); -INSERT INTO `server_battle_commands` VALUES (27311,'fira',22,34,3,32,1,0,0,1,0,0,15,0,0,0,5,16,180,0,1,504,1,2,16781816,0); -INSERT INTO `server_battle_commands` VALUES (27312,'firaga',22,50,256,32,1,0,0,1,0,0,15,0,0,0,8,7,255,0,1,700,1,2,16782012,0); -INSERT INTO `server_battle_commands` VALUES (27313,'thunder',22,1,3,32,0,0,0,1,0,0,15,0,0,0,2,6,75,0,1,503,1,2,16781815,0); -INSERT INTO `server_battle_commands` VALUES (27314,'thundara',22,18,256,32,0,0,0,1,0,0,15,4,0,0,0,30,135,0,1,508,1,2,16781820,0); -INSERT INTO `server_battle_commands` VALUES (27315,'thundaga',22,46,256,32,0,0,0,1,0,0,15,0,0,0,5,45,195,0,1,509,1,2,16781821,0); -INSERT INTO `server_battle_commands` VALUES (27316,'burst',26,50,0,32,0,0,0,1,0,0,15,0,0,0,4,900,90,0,1,705,1,2,16782017,0); -INSERT INTO `server_battle_commands` VALUES (27317,'sleepga',26,45,0,32,1,0,0,1,0,0,15,0,0,0,4,0,100,0,1,704,1,2,16782016,0); -INSERT INTO `server_battle_commands` VALUES (27318,'flare',26,40,0,32,1,0,0,1,0,0,15,0,0,0,8,120,200,0,1,706,1,2,16782018,0); -INSERT INTO `server_battle_commands` VALUES (27319,'freeze',26,35,0,32,0,0,0,1,0,0,15,0,0,0,5,120,120,0,1,707,1,2,16782019,0); -INSERT INTO `server_battle_commands` VALUES (27340,'sacred_prism',23,34,3,1,0,0,0,1,0,0,5,0,60,0,0,90,0,0,14,690,2,2,234889906,0); -INSERT INTO `server_battle_commands` VALUES (27341,'shroud_of_saints',23,38,512,1,0,0,0,1,0,0,5,0,20,0,0,180,0,0,14,691,2,2,234889907,0); -INSERT INTO `server_battle_commands` VALUES (27342,'cleric_stance',23,10,512,1,0,0,0,1,0,0,5,0,0,0,0,30,0,0,14,692,2,2,234889908,0); -INSERT INTO `server_battle_commands` VALUES (27343,'blissful_mind',23,14,512,1,0,0,0,1,0,0,5,0,0,0,0,30,0,0,14,693,2,2,234889909,0); -INSERT INTO `server_battle_commands` VALUES (27344,'presence_of_mind',27,30,0,1,0,0,0,1,0,0,5,0,0,0,0,300,0,0,14,722,2,2,234889938,0); -INSERT INTO `server_battle_commands` VALUES (27345,'benediction',27,50,0,1,1,0,0,1,0,0,5,0,0,0,0,900,0,0,14,723,2,2,234889939,0); -INSERT INTO `server_battle_commands` VALUES (27346,'cure',23,2,3,2,0,0,0,1,0,0,15,0,0,0,2,5,40,0,1,101,1,2,16781413,0); -INSERT INTO `server_battle_commands` VALUES (27347,'cura',23,30,512,2,0,0,0,1,0,0,15,0,0,0,2,5,100,0,1,103,1,2,16781415,0); -INSERT INTO `server_battle_commands` VALUES (27348,'curaga',23,46,512,4,1,0,0,1,0,0,15,0,0,0,3,10,150,0,1,146,1,2,16781458,0); -INSERT INTO `server_battle_commands` VALUES (27349,'raise',23,18,512,2,0,0,0,1,0,0,15,0,0,0,10,300,150,0,1,148,1,2,16781460,0); -INSERT INTO `server_battle_commands` VALUES (27350,'stoneskin',23,26,3,2,0,0,0,1,0,0,15,0,300,0,3,30,50,0,1,133,1,2,16781445,0); -INSERT INTO `server_battle_commands` VALUES (27351,'protect',23,6,3,4,1,0,0,1,0,0,15,0,300,0,3,30,80,0,1,1085,1,2,16782397,0); -INSERT INTO `server_battle_commands` VALUES (27352,'repose',23,50,0,32,0,0,0,1,0,0,15,0,0,0,3,0,80,0,1,151,1,2,16781463,0); -INSERT INTO `server_battle_commands` VALUES (27353,'aero',23,4,3,32,0,0,0,1,0,0,15,0,0,0,3,6,75,0,1,510,1,2,16781822,0); -INSERT INTO `server_battle_commands` VALUES (27354,'aerora',23,42,512,32,1,0,0,1,0,0,15,0,0,0,4,20,150,0,1,511,1,2,16781823,0); -INSERT INTO `server_battle_commands` VALUES (27355,'stone',23,1,3,32,0,0,0,1,0,0,15,0,0,0,2,6,75,0,1,513,1,2,16781825,0); -INSERT INTO `server_battle_commands` VALUES (27356,'stonera',23,22,512,32,1,0,0,1,0,0,15,0,0,0,3,30,150,0,1,514,1,2,16781826,0); -INSERT INTO `server_battle_commands` VALUES (27357,'esuna',27,40,0,2,0,0,0,1,0,0,15,0,0,0,2,10,40,0,1,702,1,2,16782014,0); -INSERT INTO `server_battle_commands` VALUES (27358,'regen',27,35,0,2,0,0,0,1,0,0,15,0,0,0,2,5,20,0,1,703,1,2,16782015,0); -INSERT INTO `server_battle_commands` VALUES (27359,'holy',27,45,0,32,1,0,0,1,0,0,15,0,0,0,0,300,100,0,1,708,1,2,16782020,0); +INSERT INTO `server_battle_commands` VALUES (27100,'second_wind',2,6,3,1,1,0,0,0,100,1,0,0,0,0,0,0,0,0,0,45,0,0,14,519,2,3,234889735,0,0,0,0,1,30320); +INSERT INTO `server_battle_commands` VALUES (27101,'blindside',2,14,3,1,1,0,0,0,100,1,0,0,0,0,223074,60,1,0,0,60,0,0,14,635,2,3,234889851,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27102,'taunt',2,42,4,32,32,0,0,0,100,1,0,0,25,0,223073,0,1,0,0,60,0,0,14,517,2,3,234889733,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27103,'featherfoot',2,2,3,1,1,0,0,0,100,1,0,0,0,0,223075,30,1,0,0,60,0,0,14,535,2,3,234889751,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27104,'fists_of_fire',2,34,4,1,1,0,0,0,100,1,0,0,0,0,223209,4294967295,1,0,0,10,0,0,14,684,2,3,234889900,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27105,'fists_of_earth',2,22,4,1,1,0,0,0,100,1,0,0,0,0,223210,4294967295,1,0,0,10,0,0,14,685,2,3,234889901,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27106,'hundred_fists',15,50,0,1,1,0,0,0,100,1,0,0,0,0,223244,15,1,0,0,900,0,0,14,712,2,3,234889928,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27107,'spinning_heel',15,35,0,1,1,0,0,0,100,1,0,0,0,0,223245,20,1,0,0,120,0,0,14,718,2,3,234889934,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27108,'shoulder_tackle',15,30,0,32,32,0,0,0,100,1,0,0,15,0,223015,0,0.75,0,0,60,0,0,18,1048,205,3,302830616,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27109,'fists_of_wind',15,40,0,1,1,0,0,0,100,1,0,0,0,0,223211,4294967295,1,0,0,10,0,0,14,720,2,3,234889936,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27110,'pummel',2,1,1,32,32,0,0,0,100,1,1,0,5,0,0,0,0,0,0,10,0,1000,18,1027,1,3,301995011,0,27111,27113,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27111,'concussive_blow',2,10,1,32,32,0,0,0,100,1,4,0,5,0,223007,0,0.75,0,0,30,0,1500,18,20,3,3,302002196,0,27112,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27112,'simian_thrash',2,50,4,32,32,0,0,0,100,9,0,0,5,0,0,0,0,0,0,80,0,2000,18,1003,202,3,302818283,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27113,'aura_pulse',2,38,4,32,32,1,8,1,100,1,0,0,5,0,223003,0,0,0,0,40,0,1500,18,66,203,3,302821442,0,0,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27114,'pounce',2,4,4,32,32,0,0,0,100,1,2,0,5,0,223015,0,0.75,0,0,20,0,1500,18,8,3,3,302002184,0,27115,27117,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27115,'demolish',2,30,1,32,32,0,0,0,100,1,0,0,5,0,0,0,0,0,0,30,0,1500,18,1028,2,3,301999108,0,27116,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27116,'howling_fist',2,46,4,32,32,0,0,0,100,1,4,0,5,0,0,0,0,0,0,80,0,3000,18,1029,2,3,301999109,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27117,'sucker_punch',2,26,1,32,32,0,0,0,100,1,4,0,5,0,0,0,0,0,0,15,0,1000,18,73,3,3,302002249,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27118,'dragon_kick',15,45,0,32,32,0,0,0,100,1,0,0,5,0,223013,0,0,0,0,60,0,2000,18,1041,204,3,302826513,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27119,'haymaker',2,18,4,32,32,0,0,0,100,1,0,2,5,0,223015,0,0.75,0,0,5,0,250,18,23,201,3,302813207,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27140,'sentinel',3,22,3,1,1,0,0,0,100,1,0,0,0,0,223062,15,1,0,0,90,0,0,14,526,2,3,234889742,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27141,'aegis_boon',3,6,8,1,1,0,0,0,100,1,0,0,0,0,223058,30,1,0,0,60,0,0,14,583,21,3,234967623,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27142,'rampart',3,2,3,1,1,0,0,0,100,1,0,0,0,0,223064,60,1,0,0,120,0,0,14,536,2,3,234889752,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27143,'tempered_will',3,42,8,1,1,0,0,0,100,1,0,0,0,0,223068,20,1,0,0,180,0,0,14,515,2,3,234889731,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27144,'outmaneuver',3,34,8,1,1,0,0,0,100,1,0,0,0,0,223060,30,1,0,0,90,0,0,14,512,21,3,234967552,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27145,'flash',3,14,3,32,32,0,0,0,100,1,0,0,25,0,223007,0,0,0,0,30,0,0,14,696,2,3,234889912,0,0,0,0,1,30101); +INSERT INTO `server_battle_commands` VALUES (27146,'cover',16,30,0,4,4,0,0,0,100,1,0,0,15,0,223063,15,1,0,0,60,0,0,14,725,2,3,234889941,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27147,'divine_veil',16,35,0,1,1,0,0,0,100,1,0,0,0,0,223248,20,1,0,0,60,0,0,14,713,2,3,234889929,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27148,'hallowed_ground',16,50,0,1,1,0,0,0,100,1,0,0,0,0,223249,20,1,0,0,900,0,0,14,709,2,3,234889925,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27149,'holy_succor',16,40,0,2,2,0,0,0,100,1,0,0,15,0,0,0,0,3,2000,10,100,0,1,701,1,3,16782013,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27150,'fast_blade',3,1,1,32,32,0,0,0,100,1,1,0,5,0,0,0,0,0,0,10,0,1000,18,1023,1,3,301995007,0,27151,27152,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27151,'flat_blade',3,26,1,32,32,0,0,0,100,1,0,0,5,0,0,0,0,0,0,10,0,1500,18,1024,2,3,301999104,0,0,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27152,'savage_blade',3,10,1,32,32,0,0,0,100,1,0,0,5,0,0,0,0,0,0,30,0,1000,18,1025,1,3,301995009,0,27153,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27153,'goring_blade',3,50,8,32,32,0,0,0,100,1,2,0,5,0,223206,30,0,0,0,60,0,3000,18,1026,301,3,303223810,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27154,'riot_blade',3,30,8,32,32,0,0,0,100,1,2,0,5,0,223038,0,0,0,0,80,0,2000,18,75,2,3,301998155,0,27155,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27155,'rage_of_halone',3,46,8,32,32,0,0,0,100,5,0,0,5,0,0,0,0,0,0,20,0,1500,18,1008,302,3,303227888,0,0,0,2,0.5,30301); +INSERT INTO `server_battle_commands` VALUES (27156,'shield_bash',3,18,17,32,32,0,0,0,100,1,0,0,5,0,223015,0,0.75,0,0,30,0,250,18,5,26,3,302096389,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27157,'war_drum',3,38,24,32,32,1,8,0,100,1,0,4,5,0,0,0,0,0,0,60,0,500,14,502,21,3,234967542,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27158,'phalanx',3,4,8,32,32,0,0,0,100,1,0,4,5,0,0,0,0,0,0,5,0,250,18,32,1,3,301994016,0,27159,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27159,'spirits_within',16,45,0,32,32,0,0,0,100,1,0,0,5,0,0,0,0,0,0,60,0,3000,18,1044,304,3,303236116,0,0,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27180,'provoke',4,14,3,32,32,0,0,0,100,1,0,0,25,0,223034,0,0,0,0,30,0,0,14,600,2,3,234889816,0,0,0,0,1,30101); +INSERT INTO `server_battle_commands` VALUES (27181,'foresight',4,2,3,1,1,0,0,0,100,1,0,0,0,0,223083,30,1,0,0,60,0,0,14,545,2,3,234889761,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27182,'bloodbath',4,6,3,1,1,0,0,0,100,1,0,0,0,0,223081,30,1,0,0,60,0,0,14,581,2,3,234889797,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27183,'berserk',4,22,32,1,1,0,0,0,100,1,0,0,0,0,223160,4294967295,1,0,0,10,0,0,14,682,2,3,234889898,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27184,'rampage',4,34,32,1,1,0,0,0,100,1,0,0,0,0,223064,4294967295,1,0,0,10,0,0,14,546,2,3,234889762,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27185,'enduring_march',4,42,32,1,1,0,0,0,100,1,0,0,0,0,223078,20,1,0,0,180,0,0,14,539,2,3,234889755,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27186,'vengeance',17,30,0,1,1,0,0,0,100,1,0,0,0,0,223250,15,1,0,0,150,0,0,14,714,2,3,234889930,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27187,'antagonize',17,35,0,1,1,0,0,0,100,1,0,0,0,0,223251,15,1,0,0,120,0,0,14,715,2,3,234889931,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27188,'collusion',17,40,0,4,4,0,0,0,100,1,0,0,15,0,223097,15,1,0,0,90,0,0,14,711,2,3,234889927,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27189,'mighty_strikes',17,50,0,1,1,0,0,0,100,1,0,0,0,0,223252,15,1,0,0,900,0,0,14,716,2,3,234889932,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27190,'heavy_swing',4,1,1,32,32,0,0,0,100,1,1,0,5,0,0,0,0,0,0,10,0,1000,18,14,1,3,301993998,0,27191,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27191,'skull_sunder',4,10,1,32,32,0,0,0,100,1,0,0,5,0,0,0,0,0,0,30,0,1500,18,43,1,3,301994027,0,27192,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27192,'steel_cyclone',17,45,0,32,32,1,8,0,100,1,0,0,5,0,223015,0,0.75,0,0,30,0,2000,18,1040,404,3,303645712,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27193,'brutal_swing',4,4,1,32,32,0,0,0,100,1,4,0,5,0,0,0,0,0,0,20,0,1500,18,15,3,3,302002191,0,27194,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27194,'maim',4,26,1,32,32,0,0,0,100,1,0,0,5,0,0,0,0,0,0,30,0,1500,18,88,1,3,301994072,0,27195,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27195,'godsbane',4,50,32,32,32,0,0,0,100,3,0,0,5,0,0,0,0,0,0,60,0,3000,18,1014,402,3,303637494,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27196,'path_of_the_storm',4,38,32,32,32,0,0,0,100,1,2,0,5,0,228021,0,0,0,0,30,0,1500,18,44,401,3,303632428,0,27197,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27197,'whirlwind',4,46,32,32,32,1,8,0,100,1,0,0,5,0,0,0,0,0,0,80,0,3000,18,1015,403,3,303641591,0,0,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27198,'fracture',4,18,32,32,32,0,0,0,100,1,0,3,5,0,223013,0,0.75,0,0,40,0,500,18,42,3,3,302002218,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27199,'overpower',4,30,1,32,32,2,0,0,100,1,0,3,5,0,0,0,0,0,0,5,0,250,18,89,1,3,301994073,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27220,'hawks_eye',7,6,3,1,1,0,0,0,100,1,0,0,0,0,223106,15,1,0,0,90,0,0,14,516,2,3,234889732,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27221,'quelling_strikes',7,22,3,1,1,0,0,0,100,1,0,0,0,0,223104,30,1,0,0,60,0,0,14,614,2,3,234889830,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27222,'decoy',7,2,3,1,1,0,0,0,100,1,0,0,0,0,223108,60,1,0,0,90,100,0,14,565,2,3,234889781,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27223,'chameleon',7,42,3,1,1,0,0,0,100,1,0,0,0,0,0,0,0,0,0,180,0,0,14,504,2,3,234889720,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27224,'barrage',7,34,64,1,1,0,0,0,100,1,0,0,0,0,223220,60,1,0,0,90,0,0,14,683,2,3,234889899,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27225,'raging_strikes',7,14,64,1,1,0,0,0,100,1,0,0,0,0,223105,4294967295,1,0,0,10,0,0,14,632,2,3,234889848,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27226,'swiftsong',7,26,64,1,1,1,20,0,100,1,0,0,0,0,223224,180,1,0,0,10,100,0,1,150,1,3,16781462,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27227,'battle_voice',18,50,0,1,1,1,0,0,100,1,0,0,0,0,223253,60,1,0,0,900,0,0,14,721,2,3,234889937,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27228,'heavy_shot',7,1,1,32,32,0,0,0,100,1,0,0,25,8,0,0,0,0,0,10,0,1000,18,1036,4,3,302007308,0,27229,27231,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27229,'leaden_arrow',7,10,1,32,32,0,0,0,100,1,0,0,25,0,228021,0,0.75,0,0,30,0,1500,18,1035,4,3,302007307,0,27230,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27230,'wide_volley',7,50,64,32,32,1,8,0,100,1,0,0,25,0,0,0,0,0,0,80,0,2000,18,18,703,3,304869394,0,0,0,3,0.5,30301); +INSERT INTO `server_battle_commands` VALUES (27231,'quick_nock',7,38,64,32,32,2,0,0,100,1,0,0,10,0,0,0,0,0,0,180,0,1000,18,1017,702,3,304866297,0,27232,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27232,'rain_of_death',18,45,0,32,32,1,8,0,100,1,0,0,25,0,223015,0,0.75,0,0,30,0,3000,18,1037,704,3,304874509,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27233,'piercing_arrow',7,4,1,32,32,0,0,0,100,1,0,0,25,8,0,0,0,0,0,20,0,1000,18,1038,1,3,301995022,0,27234,27236,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27234,'gloom_arrow',7,30,1,32,32,0,0,0,100,1,0,0,25,0,223007,0,0,0,0,10,0,1000,18,1039,4,3,302007311,0,27235,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27235,'bloodletter',7,46,64,32,32,0,0,0,100,1,0,0,25,0,223127,0,0.75,0,0,80,0,1500,18,53,701,3,304861237,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27236,'shadowbind',7,18,64,32,32,0,0,0,100,1,0,0,25,0,228011,0,0.9,0,0,40,0,250,18,17,4,3,302006289,0,0,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27237,'ballad_of_magi',18,30,0,1,1,1,20,0,100,1,0,0,0,0,223254,180,1,8,3000,10,100,0,1,709,1,3,16782021,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27238,'paeon_of_war',18,40,0,1,1,1,20,0,100,1,0,0,0,0,223255,180,1,8,3000,10,50,1000,1,710,1,3,16782022,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27239,'minuet_of_rigor',18,35,0,1,1,1,20,0,100,1,0,0,0,0,223256,180,1,8,3000,10,100,0,1,711,1,3,16782023,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27258,'refill',7,1,0,1,1,0,0,0,100,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,1,32613); +INSERT INTO `server_battle_commands` VALUES (27259,'light_shot',7,1,0,32,32,0,0,0,100,1,0,0,25,0,0,0,0,0,0,0,0,0,17,0,1,3,285216768,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27260,'invigorate',8,14,3,1,1,0,0,0,100,1,0,0,0,0,223094,30,1,0,0,90,0,0,14,575,2,3,234889791,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27261,'power_surge',8,34,128,1,1,0,0,0,100,1,0,0,0,0,223212,4294967295,1,0,0,10,0,0,14,686,2,3,234889902,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27262,'life_surge',8,22,128,1,1,0,0,0,100,1,0,0,0,0,223215,4294967295,1,0,0,15,0,250,14,687,2,3,234889903,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27263,'dread_spike',8,42,128,1,1,0,0,0,100,1,0,0,0,0,223218,30,1,0,0,120,0,0,14,686,2,3,234889902,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27264,'blood_for_blood',8,6,3,1,1,0,0,0,100,1,0,0,0,0,223219,60,1,0,0,60,0,0,14,689,2,3,234889905,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27265,'keen_flurry',8,26,3,1,1,0,0,0,100,1,0,0,0,0,223091,30,1,0,0,90,0,0,14,569,2,3,234889785,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27266,'jump',19,30,0,32,32,0,0,0,100,1,0,0,25,0,0,0,0,0,0,60,0,0,18,1045,804,3,305284117,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27267,'elusive_jump',19,40,0,1,1,0,0,0,100,1,0,0,0,0,0,0,0,0,0,180,0,0,18,1046,806,3,305292310,0,0,0,0,1,30101); +INSERT INTO `server_battle_commands` VALUES (27268,'dragonfire_dive',19,50,0,32,32,1,0,0,100,1,0,0,25,0,0,0,0,0,0,900,0,0,18,1045,804,3,305284117,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27269,'true_thrust',8,1,1,32,32,0,0,0,100,1,1,0,5,0,0,0,0,0,0,10,0,1000,18,1030,2,3,301999110,0,27270,27273,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27270,'leg_sweep',8,30,1,32,32,1,0,0,100,1,0,0,5,0,223015,0,0,0,0,30,0,1000,18,37,1,3,301994021,0,27271,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27271,'doom_spike',8,46,128,32,32,3,0,0,100,1,0,0,5,0,0,0,0,0,0,60,0,3000,18,83,801,3,305270867,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27272,'disembowel',19,35,0,32,32,0,0,0,100,1,0,0,5,0,223005,0,0.75,0,0,30,0,750,18,1042,2,3,301999122,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27273,'heavy_thrust',8,10,1,32,32,0,0,0,100,1,0,0,5,0,223015,0,0.75,0,0,20,0,1500,18,1031,1,3,301995015,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27274,'vorpal_thrust',8,2,1,32,32,0,0,0,100,1,2,0,5,0,0,0,0,0,0,20,0,1500,18,1032,2,3,301999112,0,27275,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27275,'impulse_drive',8,18,1,32,32,0,0,0,100,1,4,0,5,0,0,0,0,0,0,30,0,1500,18,1033,2,3,301999113,0,27276,27277,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27276,'chaos_thrust',8,50,128,32,32,0,0,0,100,6,0,0,5,0,0,0,0,0,0,80,0,3000,18,40,802,3,305274920,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27277,'ring_of_talons',19,45,0,32,32,1,8,1,100,1,0,0,5,0,0,0,0,0,0,60,0,2000,18,1009,803,3,305279985,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27278,'feint',8,4,1,32,32,0,0,0,100,1,0,1,5,0,0,0,0,0,0,10,0,250,18,39,2,3,301998119,0,27272,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27279,'full_thrust',8,38,128,32,32,0,0,0,100,1,0,1,5,0,0,0,0,0,0,30,0,250,18,1034,801,3,305271818,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27300,'dark_seal',22,14,3,1,1,0,0,0,100,1,0,0,0,0,223119,30,1,0,0,90,0,0,14,518,2,3,234889734,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27301,'resonance',22,22,3,1,1,0,0,0,100,1,0,0,0,0,223114,30,1,0,0,90,0,0,14,669,2,3,234889885,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27302,'excruciate',22,38,256,1,1,0,0,0,100,1,0,0,0,0,223231,30,1,0,0,90,0,0,14,694,2,3,234889910,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27303,'necrogenesis',22,6,3,1,1,0,0,0,100,1,0,0,0,0,223232,30,1,0,0,90,0,0,14,695,2,3,234889911,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27304,'parsimony',22,2,256,1,1,0,0,0,100,1,0,0,0,0,223233,30,1,0,0,90,0,0,14,568,2,3,234889784,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27305,'convert',26,30,0,1,1,0,0,0,100,1,0,0,0,0,0,0,0,0,0,450,0,0,14,724,2,3,234889940,0,0,0,0,1,30101); +INSERT INTO `server_battle_commands` VALUES (27306,'sleep',22,42,256,32,32,0,0,0,100,1,0,0,25,0,228001,0,0.9,2,3000,0,75,0,1,651,1,3,16781963,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27307,'sanguine_rite',22,30,3,1,1,0,0,0,100,1,0,0,0,0,223117,20,1,0,0,60,120,0,1,152,1,3,16781464,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27308,'blizzard',22,4,256,32,32,0,0,0,100,1,0,0,25,0,228021,0,0.75,2,3000,10,90,0,1,502,1,3,16781814,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27309,'blizzara',22,26,256,32,32,1,0,0,100,1,0,0,25,0,228011,0,0.75,0,0,40,150,0,1,506,1,3,16781818,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27310,'fire',22,10,3,32,32,1,0,0,100,1,0,0,25,0,0,0,0,2,3000,8,105,0,1,501,1,3,16781813,0,27311,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27311,'fira',22,34,3,32,32,1,0,0,100,1,0,0,25,0,0,0,0,2,5000,16,180,0,1,504,1,3,16781816,0,27312,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27312,'firaga',22,50,256,32,32,1,0,0,100,1,0,0,25,0,0,0,0,2,8000,7,255,0,1,700,1,3,16782012,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27313,'thunder',22,1,3,32,32,0,0,0,100,1,0,0,25,0,0,0,0,2,2000,6,75,0,1,503,1,3,16781815,0,27314,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27314,'thundara',22,18,256,32,32,0,0,0,100,1,0,0,25,0,223015,0,0.75,0,0,30,135,0,1,508,1,3,16781820,0,27315,27316,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27315,'thundaga',22,46,256,32,32,0,0,0,100,1,0,0,25,0,0,0,0,2,5000,45,195,0,1,509,1,3,16781821,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27316,'burst',26,50,0,32,32,0,0,0,100,1,0,0,25,0,0,0,0,2,4000,900,90,0,1,705,1,3,16782017,0,0,0,3,1,30301); +INSERT INTO `server_battle_commands` VALUES (27317,'sleepga',26,45,0,32,32,1,8,0,100,1,0,0,25,0,228001,0,0.9,2,4000,0,100,0,1,704,1,3,16782016,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27318,'flare',26,40,0,1,32,1,8,1,100,1,0,0,25,0,223154,0,0.75,2,8000,120,200,0,1,706,1,3,16782018,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27319,'freeze',26,35,0,32,32,0,0,0,100,1,0,0,25,0,0,0,0,2,5000,120,120,0,1,707,1,3,16782019,0,0,0,0,1,30301); +INSERT INTO `server_battle_commands` VALUES (27340,'sacred_prism',23,34,3,1,1,0,0,0,100,1,0,0,0,0,223225,60,1,0,0,90,0,0,14,690,2,3,234889906,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27341,'shroud_of_saints',23,38,512,1,1,0,0,0,100,1,0,0,0,0,223226,20,1,0,0,180,0,0,14,691,2,3,234889907,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27342,'cleric_stance',23,10,512,1,1,0,0,0,100,1,0,0,0,0,223227,4294967295,1,0,0,30,0,0,14,692,2,3,234889908,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27343,'blissful_mind',23,14,512,1,1,0,0,0,100,1,0,0,0,0,223228,4294967295,1,0,0,30,0,0,14,693,2,3,234889909,0,0,0,0,1,30321); +INSERT INTO `server_battle_commands` VALUES (27344,'presence_of_mind',27,30,0,1,1,0,0,0,100,1,0,0,0,0,223076,0,1,0,0,300,0,0,14,722,2,3,234889938,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27345,'benediction',27,50,0,1,4,1,20,1,100,1,0,0,0,0,0,0,0,0,0,900,0,0,14,723,2,3,234889939,0,0,0,0,1,30320); +INSERT INTO `server_battle_commands` VALUES (27346,'cure',23,2,3,2,2,0,0,0,100,1,0,0,25,0,0,0,0,3,2000,5,40,0,1,101,1,3,16781413,0,0,0,0,1,30320); +INSERT INTO `server_battle_commands` VALUES (27347,'cura',23,30,512,2,2,0,0,0,100,1,0,0,25,0,0,0,0,3,2000,5,100,0,1,103,1,3,16781415,0,0,0,0,1,30320); +INSERT INTO `server_battle_commands` VALUES (27348,'curaga',23,46,512,4,4,1,15,0,100,1,0,0,25,0,0,0,0,3,3000,10,150,0,1,146,1,3,16781458,0,0,0,0,1,30320); +INSERT INTO `server_battle_commands` VALUES (27349,'raise',23,18,512,128,128,0,0,0,100,1,0,0,25,0,0,0,0,3,10000,300,150,0,1,148,1,3,16781460,0,0,0,0,1,30101); +INSERT INTO `server_battle_commands` VALUES (27350,'stoneskin',23,26,3,2,2,0,0,0,100,1,0,0,25,0,223133,300,1,3,3000,30,50,0,1,133,1,3,16781445,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27351,'protect',23,6,3,4,4,1,20,0,100,1,0,0,25,0,223129,300,1,3,3000,30,80,0,1,1085,1,3,16782397,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27352,'repose',23,50,0,32,32,0,0,0,100,1,0,0,25,0,228001,0,0.9,3,3000,0,80,0,1,151,1,3,16781463,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27353,'aero',23,4,3,32,32,0,0,0,100,1,0,0,25,0,223235,0,0.75,3,3000,6,75,0,1,510,1,3,16781822,0,27354,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27354,'aerora',23,42,512,32,32,1,0,0,100,1,0,0,25,0,0,0,0,3,4000,20,150,0,1,511,1,3,16781823,0,0,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27355,'stone',23,1,3,32,32,0,0,0,100,1,0,0,25,0,223243,0,0.75,3,2000,6,75,0,1,513,1,3,16781825,0,27356,0,1,1,30301); +INSERT INTO `server_battle_commands` VALUES (27356,'stonera',23,22,512,32,32,1,0,0,100,1,0,0,25,0,228021,0,0.75,3,3000,30,150,0,1,514,1,3,16781826,0,0,0,2,1,30301); +INSERT INTO `server_battle_commands` VALUES (27357,'esuna',27,40,0,2,2,0,0,0,100,1,0,0,25,0,0,0,0,3,2000,10,40,0,1,702,1,3,16782014,0,0,0,0,1,30329); +INSERT INTO `server_battle_commands` VALUES (27358,'regen',27,35,0,2,2,0,0,0,100,1,0,0,25,0,223180,45,1,3,2000,5,20,0,1,703,1,3,16782015,0,0,0,0,1,30328); +INSERT INTO `server_battle_commands` VALUES (27359,'holy',27,45,0,1,32,1,8,1,100,1,0,0,25,0,228011,0,0.9,0,0,300,100,0,1,708,1,3,16782020,0,0,0,0,1,30301); /*!40000 ALTER TABLE `server_battle_commands` ENABLE KEYS */; UNLOCK TABLES; commit; @@ -215,4 +224,4 @@ commit; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2017-08-31 5:52:52 +-- Dump completed on 2018-02-15 0:04:47 diff --git a/sql/server_battlenpc_spawn_mods.sql b/sql/server_battlenpc_spawn_mods.sql index 190ab72e..de13cced 100644 --- a/sql/server_battlenpc_spawn_mods.sql +++ b/sql/server_battlenpc_spawn_mods.sql @@ -1,8 +1,8 @@ --- MySQL dump 10.13 Distrib 5.7.18, for Win64 (x86_64) +-- MySQL dump 10.13 Distrib 5.7.11, for Win64 (x86_64) -- -- Host: localhost Database: ffxiv_server -- ------------------------------------------------------ --- Server version 5.7.18-log +-- Server version 5.7.11 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -37,6 +37,9 @@ CREATE TABLE `server_battlenpc_spawn_mods` ( LOCK TABLES `server_battlenpc_spawn_mods` WRITE; /*!40000 ALTER TABLE `server_battlenpc_spawn_mods` DISABLE KEYS */; set autocommit=0; +INSERT INTO `server_battlenpc_spawn_mods` VALUES (3,25,30,1); +INSERT INTO `server_battlenpc_spawn_mods` VALUES (4,25,35,1); +INSERT INTO `server_battlenpc_spawn_mods` VALUES (5,25,40,1); /*!40000 ALTER TABLE `server_battlenpc_spawn_mods` ENABLE KEYS */; UNLOCK TABLES; commit; @@ -50,4 +53,4 @@ commit; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2017-09-16 2:43:42 +-- Dump completed on 2018-02-15 0:04:49 diff --git a/sql/server_statuseffects.sql b/sql/server_statuseffects.sql index 03dc60ec..ff412d0b 100644 --- a/sql/server_statuseffects.sql +++ b/sql/server_statuseffects.sql @@ -1,8 +1,8 @@ --- MySQL dump 10.13 Distrib 5.7.10, for Win64 (x86_64) +-- MySQL dump 10.13 Distrib 5.7.11, for Win64 (x86_64) -- --- Host: localhost Database: ffxiv_database +-- Host: localhost Database: ffxiv_server -- ------------------------------------------------------ --- Server version 5.7.10-log +-- Server version 5.7.11 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -23,10 +23,11 @@ DROP TABLE IF EXISTS `server_statuseffects`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `server_statuseffects` ( - `id` smallint(5) unsigned NOT NULL, + `id` int(10) unsigned NOT NULL, `name` varchar(128) NOT NULL, - `flags` int(10) unsigned NOT NULL, - `overwrite` tinyint(3) unsigned NOT NULL, + `flags` int(10) unsigned NOT NULL DEFAULT '10', + `overwrite` tinyint(3) unsigned NOT NULL DEFAULT '1', + `tickMs` int(10) unsigned NOT NULL DEFAULT '3000', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; @@ -37,8 +38,43 @@ CREATE TABLE `server_statuseffects` ( LOCK TABLES `server_statuseffects` WRITE; /*!40000 ALTER TABLE `server_statuseffects` DISABLE KEYS */; +set autocommit=0; +INSERT INTO `server_statuseffects` VALUES (223001,'quick',10,2,0); +INSERT INTO `server_statuseffects` VALUES (223002,'haste',18,2,0); +INSERT INTO `server_statuseffects` VALUES (223004,'petrification',16515074,2,0); +INSERT INTO `server_statuseffects` VALUES (223005,'paralysis',3932162,2,3000); +INSERT INTO `server_statuseffects` VALUES (223006,'silence',262154,2,0); +INSERT INTO `server_statuseffects` VALUES (223007,'blind',10,2,0); +INSERT INTO `server_statuseffects` VALUES (223008,'mute',262154,2,0); +INSERT INTO `server_statuseffects` VALUES (223010,'glare',10,2,0); +INSERT INTO `server_statuseffects` VALUES (223011,'poison',10,2,3000); +INSERT INTO `server_statuseffects` VALUES (223012,'transfixion',8388610,2,0); +INSERT INTO `server_statuseffects` VALUES (223013,'pacification',2621450,2,0); +INSERT INTO `server_statuseffects` VALUES (223014,'amnesia',1048586,2,0); +INSERT INTO `server_statuseffects` VALUES (223015,'stun',16515082,2,0); +INSERT INTO `server_statuseffects` VALUES (223016,'daze',16515082,2,0); +INSERT INTO `server_statuseffects` VALUES (223029,'hp_boost',18,2,0); +INSERT INTO `server_statuseffects` VALUES (223030,'hp_penalty',10,2,0); +INSERT INTO `server_statuseffects` VALUES (223058,'aegis_boon',65554,2,3000); +INSERT INTO `server_statuseffects` VALUES (223060,'outmaneuver',65554,2,3000); +INSERT INTO `server_statuseffects` VALUES (223062,'sentinel',18,2,3000); +INSERT INTO `server_statuseffects` VALUES (223068,'tempered_will',18,2,3000); +INSERT INTO `server_statuseffects` VALUES (223075,'featherfoot',1042,2,0); +INSERT INTO `server_statuseffects` VALUES (223094,'invigorate',18,2,2000); +INSERT INTO `server_statuseffects` VALUES (223129,'protect',10,2,0); +INSERT INTO `server_statuseffects` VALUES (223205,'combo',10,2,3000); +INSERT INTO `server_statuseffects` VALUES (223206,'goring_blade',10,2,3000); +INSERT INTO `server_statuseffects` VALUES (223209,'fists_of_fire',33554466,2,0); +INSERT INTO `server_statuseffects` VALUES (223210,'fists_of_earth',33554466,2,0); +INSERT INTO `server_statuseffects` VALUES (223211,'fists_of_wind',33554466,2,0); +INSERT INTO `server_statuseffects` VALUES (223245,'spinning_heel',18,1,0); +INSERT INTO `server_statuseffects` VALUES (253003,'evade_proc',15,1,0); +INSERT INTO `server_statuseffects` VALUES (253004,'block_proc',15,1,0); +INSERT INTO `server_statuseffects` VALUES (253005,'parry_proc',15,1,0); +INSERT INTO `server_statuseffects` VALUES (253006,'miss_proc',15,1,0); /*!40000 ALTER TABLE `server_statuseffects` ENABLE KEYS */; UNLOCK TABLES; +commit; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; @@ -49,4 +85,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2016-06-07 22:54:49 +-- Dump completed on 2018-02-15 0:04:51