diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index 8a0f3188..465fc1c1 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -12,7 +12,7 @@ using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.actors.chara.player; using FFXIVClassic_Map_Server.packets.receive.supportdesk; using FFXIVClassic_Map_Server.actors.chara.ai; -using FFXIVClassic_Map_Server.Actors.Chara; +using FFXIVClassic_Map_Server.packets.send.actor.battle; using FFXIVClassic_Map_Server.actors.chara; namespace FFXIVClassic_Map_Server @@ -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, commandType, actionType, actionProperty 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,16 @@ 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.commandType = (CommandType) reader.GetInt16("commandType"); + battleCommand.actionProperty = (ActionProperty)reader.GetInt16("actionProperty"); + battleCommand.actionType = (ActionType)reader.GetInt16("actionType"); + 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 +2324,65 @@ 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) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + } + + public static void LoadGlobalBattleTraitList(Dictionary battleTraitDict, Dictionary> battleTraitJobDict) + { + using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + try + { + int count = 0; + conn.Open(); + + var query = ("SELECT `id`, name, classJob, lvl, modifier, bonus FROM server_battle_traits;"); + + MySqlCommand cmd = new MySqlCommand(query, conn); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var id = reader.GetUInt16("id"); + var name = reader.GetString("name"); + var job = reader.GetByte("classJob"); + var level = reader.GetByte("lvl"); + uint modifier = reader.GetUInt32("modifier"); + var bonus = reader.GetInt32("bonus"); + + var trait = new BattleTrait(id, name, job, level, modifier, bonus); + + battleTraitDict.Add(id, trait); + + if(battleTraitJobDict.ContainsKey(job)) + { + battleTraitJobDict[job].Add(id); + } + else + { + battleTraitJobDict[job] = new List(); + battleTraitJobDict[job].Add(id); + } + + count++; + } + } + Program.Log.Info(String.Format("Loaded {0} battle traits.", count)); } catch (MySqlException e) { diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index ad15389b..a4dc7399 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -107,6 +107,7 @@ + @@ -117,6 +118,7 @@ + @@ -217,6 +219,7 @@ + diff --git a/FFXIVClassic Map Server/Server.cs b/FFXIVClassic Map Server/Server.cs index 3d0605ee..97f201f5 100644 --- a/FFXIVClassic Map Server/Server.cs +++ b/FFXIVClassic Map Server/Server.cs @@ -54,9 +54,10 @@ namespace FFXIVClassic_Map_Server mWorldManager.LoadActorClasses(); mWorldManager.LoadSpawnLocations(); mWorldManager.LoadBattleNpcs(); - mWorldManager.SpawnAllActors(); mWorldManager.LoadStatusEffects(); mWorldManager.LoadBattleCommands(); + mWorldManager.LoadBattleTraits(); + mWorldManager.SpawnAllActors(); mWorldManager.StartZoneThread(); IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(ConfigConstants.OPTIONS_BINDIP), int.Parse(ConfigConstants.OPTIONS_PORT)); diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 2ab4afff..4345e8a0 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -41,10 +41,12 @@ namespace FFXIVClassic_Map_Server private Dictionary statusEffectList = new Dictionary(); private Dictionary battleCommandList = new Dictionary(); private Dictionary, List> battleCommandIdByLevel = new Dictionary, List>();//Holds battle command ids keyed by class id and level (in that order) + private Dictionary battleTraitList = new Dictionary(); + private Dictionary> battleTraitIdsForClass = new Dictionary>(); private Dictionary battleNpcGenusMods = new Dictionary(); private Dictionary battleNpcPoolMods = new Dictionary(); private Dictionary battleNpcSpawnMods = new Dictionary(); - + private Server mServer; private const int MILIS_LOOPTIME = 333; @@ -1334,7 +1336,7 @@ namespace FFXIVClassic_Map_Server { Program.Tick = DateTime.Now; foreach (Zone zone in zoneList.Values) - { + { zone.Update(Program.Tick); } Program.LastTick = Program.Tick; @@ -1473,6 +1475,11 @@ namespace FFXIVClassic_Map_Server Database.LoadGlobalBattleCommandList(battleCommandList, battleCommandIdByLevel); } + public void LoadBattleTraits() + { + Database.LoadGlobalBattleTraitList(battleTraitList, battleTraitIdsForClass); + } + public BattleCommand GetBattleCommand(uint id) { BattleCommand battleCommand; @@ -1484,5 +1491,19 @@ namespace FFXIVClassic_Map_Server List ids; return battleCommandIdByLevel.TryGetValue(Tuple.Create(classId, level), out ids) ? ids : new List(); } + + public BattleTrait GetBattleTrait(ushort id) + { + BattleTrait battleTrait; + battleTraitList.TryGetValue(id, out battleTrait); + return battleTrait; + } + + public List GetAllBattleTraitIdsForClass(byte classId) + { + List ids; + return battleTraitIdsForClass.TryGetValue(classId, out ids) ? ids : new List(); + } + } } diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index 4e397668..c7aff919 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -26,6 +26,9 @@ namespace FFXIVClassic_Map_Server.Actors Appearance = 0x10, Speed = 0x20, Work = 0x40, + Stats = 0x80, + Status = 0x100, + StatusTime = 0x200, AllNpc = 0x6F, AllPlayer = 0x9F @@ -364,15 +367,26 @@ 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); } } + public void ModifySpeed(float mod) + { + for (int i = 0; i < 4; i++) + { + moveSpeeds[i] *= mod; + } + updateFlags |= ActorUpdateFlags.Speed; + } + public void ChangeSpeed(int type, float value) { moveSpeeds[type] = value; @@ -403,20 +417,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..99ba2353 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(100); + npc.SetHP(100); + 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/BattleTemp.cs b/FFXIVClassic Map Server/actors/chara/BattleTemp.cs index 0d1c861f..9f46b35f 100644 --- a/FFXIVClassic Map Server/actors/chara/BattleTemp.cs +++ b/FFXIVClassic Map Server/actors/chara/BattleTemp.cs @@ -2,10 +2,11 @@ { class BattleTemp { + //Are these right? public const uint NAMEPLATE_SHOWN = 0; public const uint TARGETABLE = 1; - //public const uint NAMEPLATE_SHOWN2 = 2; - public const uint NAMEPLATE_SHOWN2 = 3; + public const uint NAMEPLATE_SHOWN2 = 2; + //public const uint NAMEPLATE_SHOWN2 = 3; public const uint STAT_STRENGTH = 3; public const uint STAT_VITALITY = 4; @@ -25,13 +26,13 @@ public const uint STAT_ACCURACY = 15; public const uint STAT_NORMALDEFENSE = 18; public const uint STAT_EVASION = 16; - public const uint STAT_ATTACK_MAGIC = 24; - public const uint STAT_HEAL_MAGIC = 25; - public const uint STAT_ENCHANCEMENT_MAGIC_POTENCY = 26; - public const uint STAT_ENFEEBLING_MAGIC_POTENCY = 27; - - public const uint STAT_MAGIC_ACCURACY = 28; - public const uint STAT_MAGIC_EVASION = 29; + public const uint STAT_ATTACK_MAGIC = 23; + public const uint STAT_HEAL_MAGIC = 24; + public const uint STAT_ENCHANCEMENT_MAGIC_POTENCY = 25; + public const uint STAT_ENFEEBLING_MAGIC_POTENCY = 26; + + public const uint STAT_MAGIC_ACCURACY = 27; + public const uint STAT_MAGIC_EVASION = 28; public const uint STAT_CRAFT_PROCESSING = 30; public const uint STAT_CRAFT_MAGIC_PROCESSING = 31; @@ -43,6 +44,6 @@ public float[] castGauge_speed = { 1.0f, 0.25f}; public bool[] timingCommandFlag = new bool[4]; - public ushort[] generalParameter = new ushort[35]; + public short[] generalParameter = new short[35]; } } diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 098392c2..a3f4e5bf 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -117,7 +117,7 @@ namespace FFXIVClassic_Map_Server.Actors public Pet pet; - private Dictionary modifiers = new Dictionary(); + private Dictionary modifiers = new Dictionary(); protected ushort hpBase, hpMaxBase, mpBase, mpMaxBase, tpBase; protected BattleTemp baseStats = new BattleTemp(); @@ -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); @@ -186,15 +191,19 @@ namespace FFXIVClassic_Map_Server.Actors zone.BroadcastPacketsAroundActor(this, propPacketUtil.Done()); } + //This logic isn't correct, order of GetStatusEffects() is not necessarily the same as the actual effects in game. Also sending every time at once isn't needed public List GetActorStatusPackets() { var propPacketUtil = new ActorPropertyPacketUtil("charaWork/status", this); 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 +239,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) @@ -251,11 +261,11 @@ namespace FFXIVClassic_Map_Server.Actors public void DoBattleAction(ushort commandId, uint animationId, List actions) { - int currentIndex = 0; + int currentIndex = 0; 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)); @@ -266,7 +276,11 @@ namespace FFXIVClassic_Map_Server.Actors } else break; - animationId = 0; //If more than one packet is sent out, only send the animation once to avoid double playing. + + //Sending multiple packets at once causes some issues. Setting any combination of these to zero changes what breaks + //animationId = 0; //If more than one packet is sent out, only send the animation once to avoid double playing. + //commandId = 0; + //sourceActorId = 0; } } @@ -282,20 +296,51 @@ namespace FFXIVClassic_Map_Server.Actors PathTo(target.positionX, target.positionY, target.positionZ, stepSize, maxPath, radius); } - public Int64 GetMod(uint modifier) + public double GetMod(Modifier modifier) { - Int64 res; + return GetMod((uint)modifier); + } + + public double GetMod(uint modifier) + { + double res; if (modifiers.TryGetValue((Modifier)modifier, out res)) return res; return 0; } - public void SetMod(uint modifier, Int64 val) + public void SetMod(uint modifier, double val) { if (modifiers.ContainsKey((Modifier)modifier)) modifiers[(Modifier)modifier] = val; else modifiers.Add((Modifier)modifier, val); + + if (modifier <= 35) + updateFlags |= ActorUpdateFlags.Stats; + } + + public void AddMod(Modifier modifier, double val) + { + AddMod((uint)modifier, val); + } + + public void AddMod(uint modifier, double val) + { + + double newVal = GetMod(modifier) + val; + SetMod(modifier, newVal); + } + + public void SubtractMod(Modifier modifier, double val) + { + AddMod((uint)modifier, val); + } + + public void SubtractMod(uint modifier, double val) + { + double newVal = GetMod(modifier) - val; + SetMod(modifier, newVal); } public virtual void OnPath(Vector3 point) @@ -332,6 +377,21 @@ namespace FFXIVClassic_Map_Server.Actors //DoBattleAction(21001, 0x7C000062, new BattleAction(this.actorId, 0, 1, 0, 0, 1)); //Attack Mode } + if ((updateFlags & ActorUpdateFlags.Status) != 0) + { + List statusPackets = statusEffects.GetStatusPackets(); + packets.AddRange(statusPackets); + statusPackets.Clear(); + updateFlags &= ~ActorUpdateFlags.Status; + } + + if ((updateFlags & ActorUpdateFlags.StatusTime) != 0) + { + packets.AddRange(statusEffects.GetStatusTimerPackets()); + updateFlags &= ~ActorUpdateFlags.StatusTime; + + } + if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0) { var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this); @@ -342,6 +402,7 @@ namespace FFXIVClassic_Map_Server.Actors propPacketUtil.AddProperty("charaWork.parameterTemp.tp"); packets.AddRange(propPacketUtil.Done()); } + base.PostUpdate(tick, packets); } } @@ -384,11 +445,12 @@ namespace FFXIVClassic_Map_Server.Actors public virtual bool Engage(uint targid = 0, ushort newMainState = 0xFFFF) { // todo: attack the things - if (newMainState != 0xFFFF) + /*if (newMainState != 0xFFFF) { - this.newMainState = newMainState; + currentMainState = newMainState;// this.newMainState = newMainState; + updateFlags |= ActorUpdateFlags.State; } - else if (aiContainer.CanChangeState()) + else*/ if (aiContainer.CanChangeState()) { if (targid == 0) { @@ -414,11 +476,12 @@ namespace FFXIVClassic_Map_Server.Actors public virtual bool Disengage(ushort newMainState = 0xFFFF) { - if (newMainState != 0xFFFF) + /*if (newMainState != 0xFFFF) { - this.newMainState = newMainState; + currentMainState = newMainState;// this.newMainState = newMainState; + updateFlags |= ActorUpdateFlags.State; } - else if (IsEngaged()) + else*/ if (IsEngaged()) { aiContainer.Disengage(); return true; @@ -454,7 +517,8 @@ namespace FFXIVClassic_Map_Server.Actors RecalculateStats(); } - public virtual void Die(DateTime tick) + //AdditionalActions is the list of actions that EXP/Chain messages are added to + public virtual void Die(DateTime tick, BattleActionContainer actionContainer = null) { // todo: actual despawn timer aiContainer.InternalDie(tick, 10); @@ -514,7 +578,7 @@ namespace FFXIVClassic_Map_Server.Actors public byte GetHPP() { - return (byte)(charaWork.parameterSave.hp[0] == 0 ? 0 : (charaWork.parameterSave.hp[0] / charaWork.parameterSave.hpMax[0]) * 100); + return (byte)(charaWork.parameterSave.hp[0] == 0 ? 0 : (charaWork.parameterSave.hp[0] / (float) charaWork.parameterSave.hpMax[0]) * 100); } public void SetHP(uint hp) @@ -549,8 +613,8 @@ namespace FFXIVClassic_Map_Server.Actors // todo: the following functions are virtuals since we want to check hidden item bonuses etc on player for certain conditions public virtual void AddHP(int hp) { - // dont wanna die ded - if (IsAlive()) + // dont wanna die ded, don't want to send update if hp isn't actually changed + if (IsAlive() && hp != 0) { // todo: +/- hp and die // todo: battlenpcs probably have way more hp? @@ -558,9 +622,6 @@ namespace FFXIVClassic_Map_Server.Actors addHp = addHp.Clamp((short)GetMod((uint)Modifier.MinimumHpLock), charaWork.parameterSave.hpMax[0]); charaWork.parameterSave.hp[0] = (short)addHp; - if (charaWork.parameterSave.hp[0] < 1) - Die(Program.Tick); - updateFlags |= ActorUpdateFlags.HpTpMp; } } @@ -577,21 +638,30 @@ namespace FFXIVClassic_Map_Server.Actors public void AddMP(int mp) { - charaWork.parameterSave.mp = (short)(charaWork.parameterSave.mp + mp).Clamp(ushort.MinValue, charaWork.parameterSave.mpMax); + if (IsAlive() && mp != 0) + { + charaWork.parameterSave.mp = (short)(charaWork.parameterSave.mp + mp).Clamp(ushort.MinValue, charaWork.parameterSave.mpMax); - // todo: check hidden effects and shit + // todo: check hidden effects and shit - updateFlags |= ActorUpdateFlags.HpTpMp; + updateFlags |= ActorUpdateFlags.HpTpMp; + } } 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() && tp != 0) + { + var addTp = charaWork.parameterTemp.tp + tp; + + addTp = addTp.Clamp((int) GetMod(Modifier.MinimumTpLock), 3000); + charaWork.parameterTemp.tp = (short) addTp; + 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 +679,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,14 +709,22 @@ namespace FFXIVClassic_Map_Server.Actors } // todo: recalculate stats and crap updateFlags |= ActorUpdateFlags.HpTpMp; + + + SetMod((uint)Modifier.HitCount, 1); } - public void SetStat(uint statId, uint val) + public void RecalculateStats() { - charaWork.battleTemp.generalParameter[statId] = (ushort)val; + //CalculateBaseStats(); } - public ushort GetStat(uint statId) + public void SetStat(uint statId, int val) + { + charaWork.battleTemp.generalParameter[statId] = (short)val; + } + + public short GetStat(uint statId) { return charaWork.battleTemp.generalParameter[statId]; } @@ -659,62 +732,67 @@ namespace FFXIVClassic_Map_Server.Actors public virtual float GetSpeed() { // todo: for battlenpc/player calculate speed - return GetMod((uint)Modifier.Speed); + return (float) GetMod((uint)Modifier.Speed); } 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); + //BattleUtils.DamageTarget(this, target, DamageTakenType.Attack, action); + 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); + } + } - lua.LuaEngine.GetInstance().OnSignal("weaponskillUsed"); + this.DelTP(skill.tpCost); } - 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() @@ -732,9 +810,55 @@ namespace FFXIVClassic_Map_Server.Actors } - public virtual void OnDamageTaken(Character attacker, BattleAction action, DamageTakenType damageTakenType) + public virtual void OnDamageDealt(Character defender, BattleAction action, BattleActionContainer actionContainer = null) { + switch (action.hitType) + { + case (HitType.Miss): + OnMiss(this, action, actionContainer); + break; + default: + OnHit(defender, action, actionContainer); + break; + } + //TP is only gained from autoattacks and abilities + if (action.commandType == CommandType.AutoAttack || action.commandType == CommandType.Ability) + { + //TP gained on an attack is usually 100 * delay. + //Store TP seems to add .1% per point. + double weaponDelay = GetMod(Modifier.AttackDelay) / 1000.0; + var storeTPPercent = 1 + (GetMod(Modifier.StoreTP) * 0.1); + AddTP((int)(weaponDelay * 100 * storeTPPercent)); + } + } + + public virtual void OnDamageTaken(Character attacker, BattleAction action, BattleActionContainer actionContainer = null) + { + switch (action.hitType) + { + case (HitType.Miss): + OnEvade(attacker, action, actionContainer); + break; + case (HitType.Parry): + OnParry(attacker, action, actionContainer); + break; + case (HitType.Block): + OnBlock(attacker, action, actionContainer); + break; + } + + statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnDamageTaken, "onDamageTaken", attacker, this, action); + + //TP gain formula seems to be something like 5 * e ^ ( -0.667 * [defender's level] ) * damage taken, rounded up + //This should be completely accurate at level 50, but isn't totally accurate at lower levels. + //Don't know if store tp impacts this + double tpModifier = 5 * Math.Pow(Math.E, (-0.0667 * GetLevel())); + AddTP((int)Math.Ceiling(tpModifier * action.amount)); + + + if (charaWork.parameterSave.hp[0] < 1) + Die(Program.Tick, actionContainer); } public UInt64 GetTempVar(string name) @@ -810,6 +934,191 @@ 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(5); + statusEffects.AddStatusEffect(procEffect, this, true, true); + } + //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, BattleActionContainer actionContainer = null) + { + SetProc((ushort)HitType.Evade); + statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnEvade, "onEvade", attacker, this, action, actionContainer); + } + + //Called when this character blocks attacker's action + public void OnBlock(Character attacker, BattleAction action, BattleActionContainer actionContainer = null) + { + SetProc((ushort)HitType.Block); + statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnBlock, "onBlock", attacker, this, action, actionContainer); + } + + //Called when this character parries attacker's action + public void OnParry(Character attacker, BattleAction action, BattleActionContainer actionContainer = null) + { + SetProc((ushort)HitType.Parry); + statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnParry, "onParry", attacker, this, action, actionContainer); + } + + //Called when this character misses + public void OnMiss(Character defender, BattleAction action, BattleActionContainer actionContainer = null) + { + SetProc((ushort)HitType.Miss); + statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnMiss, "onMiss", this, defender, action, actionContainer); + } + + public void OnHit(Character defender, BattleAction action, BattleActionContainer actionContainer = null) + { + statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnHit, "onHit", this, defender, action, actionContainer); + } + + //The order of messages that appears after using a command is: + + //1. Cast start messages. (ie "You begin casting... ") + //2. Messages from buffs that activate before the command actually starts, like Power Surge or Presence of Mind. (This may be wrong and these could be the same as 4.) + //3. If the command is a multi-hit command, this is where the "You use [command] on [target]" message goes + + //Then, for each hit: + //4. Buffs that activate before a command hits, like Blindside + //5. The hit itself. For single hit commands this message is "Your [command] hits [target] for x damage" for multi hits it's "[Target] takes x points of damage" + //6. Stoneskin falling off + //6. Buffs that activate after a command hits, like Aegis Boon and Divine Veil + + //After all hits + //7. If it's a multi-hit command there's a "{numhits]fold attack..." message or if all hits miss an "All attacks missed" message + //8. Buffs that fall off after the skill ends, like Excruciate + + //For every target defeated: + //8. Defeat message + //9. EXP message + //10. EXP chain message + + + //folder is probably temporary until move to cached scripts is complete + public void DoBattleCommand(BattleCommand command, string folder) + { + //List actions = new List(); + BattleActionContainer actions = new BattleActionContainer(); + + var targets = command.targetFind.GetTargets(); + bool hitTarget = false; + + if (targets.Count > 0) + { + statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnCommandStart, "onCommandStart", this, command, actions); + + foreach (var chara in targets) + { + ushort hitCount = 0; + for (int hitNum = 1; hitNum <= command.numHits; hitNum++) + { + var action = new BattleAction(chara.actorId, command, (byte)GetHitDirection(chara), (byte)hitNum); + + //uncached script + lua.LuaEngine.CallLuaBattleCommandFunction(this, command, folder, "onSkillFinish", this, chara, command, action, actions); + //cached script + //skill.CallLuaFunction(owner, "onSkillFinish", this, chara, command, action, actions); + + if (action.hitType > HitType.Evade && action.hitType != HitType.Resist) + { + hitTarget = true; + hitCount++; + } + } + + if (command.numHits > 1) + { + //You use [command] on [target]. + actions.AddAction(new BattleAction(chara.actorId, 30442, 0, 0, (byte)hitCount)); + } + } + + statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnCommandFinish, "onCommandFinish", this, command, actions); + } + else + { + actions.AddAction(new BattleAction(target.actorId, 30202, 0)); + } + + //Now that we know if we hit the target we can check if the combo continues + if (this is Player player && command.commandType == CommandType.WeaponSkill) + if (command.isCombo && hitTarget) + player.SetCombos(command.comboNextCommandId); + else + player.SetCombos(); + + BattleAction error = new BattleAction(actorId, 0, 0); + actions.CombineLists(); + DoBattleAction(command.id, command.battleAnimation, actions.GetList()); + } + + public List GetPartyMembersInRange(uint range) + { + TargetFind targetFind = new TargetFind(this); + targetFind.SetAOEType(ValidTarget.PartyMember, TargetFindAOEType.Circle, TargetFindAOETarget.Self, range); + targetFind.FindWithinArea(this, ValidTarget.PartyMember, TargetFindAOETarget.Self); + return targetFind.GetTargets(); + } + } +} \ 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..1858ef9d 100644 --- a/FFXIVClassic Map Server/actors/chara/Modifier.cs +++ b/FFXIVClassic Map Server/actors/chara/Modifier.cs @@ -6,53 +6,92 @@ using System.Threading.Tasks; namespace FFXIVClassic_Map_Server.actors.chara { + //These will need to be redone at some point. remember to update tables in db. + //Consider using text_paramname sheet. that matches up with the stats on armor, but some things will need special handling + //Also, 0-35 should probably match with up BattleTemp 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, + NAMEPLATE_SHOWN = 0, + TARGETABLE = 1, + NAMEPLATE_SHOWN2 = 2, + //NAMEPLATE_SHOWN2 = 3, - HarvestPotency = 38, - HarvestLimit = 39, - HarvestRate = 40, + Strength = 3, + Vitality = 4, + Dexterity = 5, + Intelligence = 6, + Mind = 7, + Piety = 8, - Raise = 41, - MinimumHpLock = 42, // hp cannot fall below this value + ResistFire = 9, + ResistIce = 10, + ResistWind = 11, + ResistLightning = 12, + ResistEarth = 13, + ResistWater = 14, + + Accuracy = 15, + Evasion = 16, + Attack = 17, + Defense = 18, //Is there a magic defense stat? 19 maybe? + MagicAttack = 23, + MagicHeal = 24, + MagicEnhancePotency = 25, + MagicEnfeeblingPotency = 26, + + MagicAccuracy = 27, + MagicEvasion = 28, + + CraftProcessing = 30, + CraftMagicProcessing = 31, + CraftProcessControl = 32, + + HarvestPotency = 33, + HarvestLimit = 34, + HarvestRate = 35, + + None = 36, + Hp = 37, + HpPercent = 38, + Mp = 39, + MpPercent = 40, + Tp = 41, + TpPercent = 42, + Regen = 43, + Refresh = 44, + + AttackRange = 45, + Speed = 46, + AttackDelay = 47, + + Raise = 48, + MinimumHpLock = 49, // hp cannot fall below this value + AttackType = 50, // slashing, piercing, etc + BlockRate = 51, + Block = 52, + CritRating = 53, + HasShield = 54, // 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 = 55, // Amount of hits in an auto attack. Usually 1, 2 for h2h, 3 with spinning heel + + //Flat percent increases to these rates. Probably a better way to do this + RawEvadeRate = 56, + RawParryRate = 57, + RawBlockRate = 58, + RawResistRate = 59, + RawHitRate = 60, + RawCritRate = 61, + + DamageTakenDown = 62, // Percent damage taken down + StoreTP = 63, //.1% extra tp per point. Lancer trait is 50 StoreTP + PhysicalCritRate = 64, //CritRating but only for physical attacks. Increases chance of critting. + PhysicalCritEvasion = 65, //Opposite of CritRating. Reduces chance of being crit by phyiscal attacks + PhysicalCritAttack = 66, //Increases damage done by Physical Critical hits + PhysicalCritResilience = 67, //Decreases damage taken by Physical Critical hits + Parry = 68, //Increases chance to parry + MagicCritPotency = 69, //Increases + Regain = 70, //TP regen, should be -90 out of combat, Invigorate sets to 100+ depending on traits + RegenDown = 71, //Damage over time effects. Separate from normal Regen because of how they are displayed in game + Stoneskin = 72, //Nullifies damage + MinimumTpLock = 73 } } 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..806dbf39 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs @@ -6,8 +6,9 @@ using System.Text; using System.Threading.Tasks; using FFXIVClassic_Map_Server.actors.chara.player; using FFXIVClassic.Common; -using FFXIVClassic_Map_Server.packets.send.actor; +using FFXIVClassic_Map_Server.packets.send.actor.battle; 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,29 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai Monster } + public enum BattleCommandCastType : ushort + { + None, + Weaponskill = 1, + Weaponskill2 = 2, + BlackMagic = 3, + WhiteMagic = 4, + SongMagic = 8 + } + + + //What type of command it is + [Flags] + public enum CommandType : ushort + { + //Type of action + None = 0, + AutoAttack = 1, + WeaponSkill = 2, + Ability =3, + Spell = 4 + } + class BattleCommand { public ushort id; @@ -58,28 +82,50 @@ 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 CommandType commandType; + public ActionProperty actionProperty; + public ActionType actionType; + + + public byte statusTier; //tier of status to put on target + public ulong statusMagnitude = 0; //magnitude 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 float bonusCritRate; //extra crit rate + public bool isCombo; + public bool isRanged; + + public bool actionCrit; //Whether any actions were critical hits, used for Excruciate + + public lua.LuaScript script; //cached script public TargetFind targetFind; public BattleCommandValidUser validUser; @@ -89,6 +135,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.id = id; this.name = name; this.range = 0; + this.enmityModifier = 1; + this.accuracyModifier = 0; + this.statusTier = 1; + this.statusChance = 50; + this.recastTimeMs = (uint) maxRecastTimeSeconds * 1000; + this.isCombo = false; } public BattleCommand Clone() @@ -96,20 +148,34 @@ 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); - + if (aoeType == TargetFindAOEType.Box) { targetFind.SetAOEBox(validTarget, aoeTarget, range, aoeRange); @@ -119,10 +185,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai targetFind.SetAOEType(validTarget, aoeType, aoeTarget, range, aoeRange); } - /* 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 +195,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; + } + + //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; } - if (tpCost > user.GetTP()) + //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 +232,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 +251,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 +263,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 +274,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 +329,64 @@ 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; + } + + public ushort GetCommandType() + { + return (ushort) commandType; + } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/BattleTrait.cs b/FFXIVClassic Map Server/actors/chara/ai/BattleTrait.cs new file mode 100644 index 00000000..f8f41b48 --- /dev/null +++ b/FFXIVClassic Map Server/actors/chara/ai/BattleTrait.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.actors.chara.ai +{ + class BattleTrait + { + public ushort id; + public string name; + public byte job; + public byte level; + public uint modifier; + public int bonus; + + public BattleTrait(ushort id, string name, byte job, byte level, uint modifier, int bonus) + { + this.id = id; + this.name = name; + this.job = job; + this.level = level; + this.modifier = modifier; + this.bonus = bonus; + } + } +} diff --git a/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs b/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs index 7ea9f9d7..83c16e40 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/StatusEffect.cs @@ -7,13 +7,15 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using MoonSharp.Interpreter; +using FFXIVClassic.Common; namespace FFXIVClassic_Map_Server.actors.chara.ai { enum StatusEffectId : uint { RageofHalone = 221021, - + Quick = 223001, Haste = 223002, Slow = 223003, @@ -98,7 +100,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai Retaliation = 223082, Foresight = 223083, Defender = 223084, - Rampage = 223085, + Rampage = 223085, //old effect Enraged = 223086, Warmonger = 223087, Disorientx1 = 223088, @@ -118,10 +120,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai LifeSap = 223102, Farshot = 223103, QuellingStrike = 223104, - RagingStrike = 223105, + RagingStrike = 223105, //old effect HawksEye = 223106, SubtleRelease = 223107, - Decoy = 223108, + Decoy = 223108, //Untraited Profundity = 223109, TranceChant = 223110, RoamingSoul = 223111, @@ -139,10 +141,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai ConcussiveBlowx2 = 223124, ConcussiveBlowx3 = 223125, SkullSunder = 223126, - Bloodletter = 223127, + Bloodletter = 223127, //comboed effect Levinbolt = 223128, - Protect = 223129, - Shell = 223130, + Protect = 223129, //old Protect + Shell = 223130, //old shell Reraise = 223131, ShockSpikes = 223132, Stoneskin = 223133, @@ -218,8 +220,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai StealthIV = 223204, Combo = 223205, GoringBlade = 223206, - Berserk2 = 223207, - Rampage2 = 223208, + Berserk2 = 223207, //new effect + Rampage2 = 223208, //new effect FistsofFire = 223209, FistsofEarth = 223210, FistsofWind = 223211, @@ -248,10 +250,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai Aero = 223235, Outmaneuver2 = 223236, Blindside2 = 223237, - Decoy2 = 223238, - Protect2 = 223239, + Decoy2 = 223238, //Traited + Protect2 = 223239, //new Protect SanguineRite3 = 223240, - Bloodletter2 = 223241, + Bloodletter2 = 223241, //uncomboed effect FullyBlissfulMind = 223242, MagicEvasionDown = 223243, HundredFists = 223244, @@ -322,24 +324,58 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai // custom effects here + // status for having procs fall off + EvadeProc = 253003, + BlockProc = 253004, + ParryProc = 253005, + MissProc = 253006, + EXPChain = 253007 } [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 + LoseOnCastStart = 1 << 7, // effects removed when owner starts casting + LoseOnAggro = 1 << 8, // effects removed when owner gains enmity (swiftsong) + + //Activate flags + ActivateOnCastStart = 1 << 9, //Activates when a cast starts. + ActivateOnCommandStart = 1 << 10, //Activates when a command is used, before iterating over targets. Used for things like power surge, excruciate. + ActivateOnCommandFinish = 1 << 11, //Activates when the command is finished, after all targets have been iterated over. Used for things like Excruciate and Resonance falling off. + ActivateOnPreactionTarget = 1 << 12, //Activates after initial rates are calculated for an action against owner + ActivateOnPreactionCaster = 1 << 13, //Activates after initial rates are calculated for an action by owner + ActivateOnDamageTaken = 1 << 14, + ActivateOnHealed = 1 << 15, + + //Should these be rolled into DamageTaken? + ActivateOnMiss = 1 << 16, //Activates when owner misses + ActivateOnEvade = 1 << 17, //Activates when owner evades + ActivateOnParry = 1 << 18, //Activates when owner parries + ActivateOnBlock = 1 << 19, //Activates when owner evades + ActivateOnHit = 1 << 20, //Activates when owner hits + ActivateOnCrit = 1 << 21, //Activates when owner crits + + //Prevent flags. Sleep/stun/petrify/etc combine these + PreventSpell = 1 << 22, // effects which prevent using spells, such as silence + PreventWeaponSkill = 1 << 23, // effects which prevent using weaponskills, such as pacification + PreventAbility = 1 << 24, // effects which prevent using abilities, such as amnesia + PreventAttack = 1 << 25, // effects which prevent basic attacks + PreventMovement = 1 << 26, // effects which prevent movement such as bind, still allows turning in place + PreventTurn = 1 << 27, // effects which prevent turning, such as stun + PreventUntarget = 1 << 28, // effects which prevent changing targets, such as fixation + + Stealth = 1 << 29, // sneak/invis + Stance = 1 << 30, // effects that do not have a timer } enum StatusEffectOverwrite : byte @@ -353,32 +389,34 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai class StatusEffect { // todo: probably use get;set; - private Character owner; private Character source; private StatusEffectId id; private string name; // name of this effect private DateTime startTime; // when was this effect added + private DateTime endTime; // when this status falls off 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 double 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 - private UInt64 extra; // optional value + private double extra; // optional value 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, double 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 +430,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 +439,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 @@ -417,16 +457,32 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (tickMs != 0 && (tick - lastTick).TotalMilliseconds >= tickMs) { lastTick = tick; - LuaEngine.CallLuaStatusEffectFunction(this.owner, this, "onTick", this.owner, this); + if (LuaEngine.CallLuaStatusEffectFunction(this.owner, this, "onTick", this.owner, this) > 0) + return true; } - if (durationMs != 0 && (tick - startTime).TotalMilliseconds >= durationMs) + if (duration >= 0 && tick >= endTime) { return true; } return false; } + public int CallLuaFunction(Character chara, string functionName, params object[] args) + { + + DynValue res = new DynValue(); + + return lua.LuaEngine.CallLuaStatusEffectFunction(chara, this, functionName, args); + if (!script.Globals.Get(functionName).IsNil()) + { + res = script.Call(script.Globals.Get(functionName), args); + if (res != null) + return (int)res.Number; + } + + } + public Character GetOwner() { return owner; @@ -452,14 +508,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return startTime; } + public DateTime GetEndTime() + { + return endTime; + } + public string GetName() { return name; } - public uint GetDurationMs() + public uint GetDuration() { - return durationMs; + return duration; } public uint GetTickMs() @@ -467,7 +528,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return tickMs; } - public UInt64 GetMagnitude() + public double GetMagnitude() { return magnitude; } @@ -477,7 +538,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return tier; } - public UInt64 GetExtra() + public double GetExtra() { return extra; } @@ -497,12 +558,32 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return silent; } + public bool GetHidden() + { + return hidden; + } + public void SetStartTime(DateTime time) { this.startTime = time; this.lastTick = time; } + public void SetEndTime(DateTime time) + { + endTime = time; + } + + //Refresh the status, updating the end time based on the duration of the status and broadcasts the new time + public void RefreshTime() + { + endTime = DateTime.Now.AddSeconds(GetDuration()); + int index = Array.IndexOf(owner.charaWork.status, GetStatusId()); + + if (index >= 0) + owner.statusEffects.SetTimeAtIndex(index, (uint) Utils.UnixTimeStampUTC(endTime)); + } + public void SetOwner(Character owner) { this.owner = owner; @@ -518,14 +599,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.name = name; } - public void SetMagnitude(UInt64 magnitude) + public void SetMagnitude(double magnitude) { this.magnitude = magnitude; } - public void SetDurationMs(uint durationMs) + public void SetDuration(uint duration) { - this.durationMs = durationMs; + this.duration = duration; } public void SetTickMs(uint tickMs) @@ -538,7 +619,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai this.tier = tier; } - public void SetExtra(UInt64 val) + public void SetExtra(double val) { this.extra = val; } @@ -558,6 +639,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..c9f9cbe5 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; @@ -20,22 +21,36 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai private readonly Dictionary effects; public static readonly int MAX_EFFECTS = 20; private bool sendUpdate = false; + private DateTime lastTick;// Do all effects tick at the same time like regen? + private List statusSubpackets; + private ActorPropertyPacketUtil statusTimerPropPacketUtil; public StatusEffectContainer(Character owner) { this.owner = owner; this.effects = new Dictionary(); + statusSubpackets = new List(); + statusTimerPropPacketUtil = new ActorPropertyPacketUtil("charawork/Status", owner); } public void Update(DateTime tick) { + //Regen/Refresh/Regain effects tick every 3 seconds + if ((DateTime.Now - lastTick).Seconds >= 3) + { + RegenTick(tick); + lastTick = DateTime.Now; + } // list of effects to remove + + // if (owner is Player) UpdateTimeAtIndex(4, 4294967295); var removeEffects = new List(); - foreach (var effect in effects.Values) + for (int i = 0; i < effects.Values.Count; i++) { // effect's update function returns true if effect has completed - if (effect.Update(tick)) - removeEffects.Add(effect); + if (effects.Values.ElementAt(i).Update(tick)) + removeEffects.Add(effects.Values.ElementAt(i)); + } // remove effects from this list @@ -51,6 +66,31 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai } } + //regen/refresh/regain + public void RegenTick(DateTime tick) + { + ushort dotTick = (ushort) owner.GetMod(Modifier.RegenDown); + ushort regenTick = (ushort) owner.GetMod(Modifier.Regen); + ushort refreshtick = (ushort) owner.GetMod(Modifier.Refresh); + short regainTick = (short) owner.GetMod(Modifier.Regain); + + //DoTs tick before regen and the full dot damage is displayed, even if some or all of it is nullified by regen. Only effects like stoneskin actually alter the number shown + if (dotTick > 0) + { + BattleAction action = new BattleAction(owner.actorId, 30331, (uint)(HitEffect.HitEffectType | HitEffect.Hit), dotTick); + utils.BattleUtils.HandleStoneskin(owner, action); + // todo: figure out how to make red numbers appear for enemies getting hurt by dots + owner.DelHP(action.amount); + + owner.DoBattleAction(0, 0, action); + } + + //DoTs are the only effect to show numbers, so that doesnt need to be handled for these + owner.AddHP(regenTick); + owner.AddMP(refreshtick); + owner.AddTP(regainTick); + } + public bool HasStatusEffect(uint id) { return effects.ContainsKey(id); @@ -61,32 +101,77 @@ 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 BattleAction AddStatusForBattleAction(uint id, byte tier = 1) { - return AddStatusEffect(new StatusEffect(this.owner, id, magnitude, (uint)(tickMs * 1000), (uint)(durationMs * 1000), tier), owner); + BattleAction action = null; + + if (AddStatusEffect(id, tier)) + action = new BattleAction(owner.actorId, 30328, id | (uint)HitEffect.StatusEffectType); + + return action; } - public bool AddStatusEffect(StatusEffect newEffect, Character source, bool silent = false) + public bool AddStatusEffect(uint id) + { + var se = Server.GetWorldManager().GetStatusEffect(id); + + return AddStatusEffect(se, owner); + } + + public bool AddStatusEffect(uint id, byte tier) + { + var se = Server.GetWorldManager().GetStatusEffect(id); + + se.SetTier(tier); + + return AddStatusEffect(se, owner); + } + + public bool AddStatusEffect(uint id, byte tier, UInt64 magnitude) + { + var se = Server.GetWorldManager().GetStatusEffect(id); + + se.SetMagnitude(magnitude); + se.SetTier(tier); + + return AddStatusEffect(se, owner); + } + + public bool AddStatusEffect(uint id, byte tier, UInt64 magnitude, uint duration, int tickMs = 3000) + { + 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, 3000, duration, tier), owner); + } + + 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. */ + 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,18 +180,39 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (canOverwrite) effects.Remove(newEffect.GetStatusEffectId()); + newEffect.SetStartTime(DateTime.Now); + newEffect.SetEndTime(DateTime.Now.AddSeconds(newEffect.GetDuration())); + 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); - owner.charaWork.status[index] = newEffect.GetStatusId(); - owner.charaWork.statusShownTime[index] = Utils.UnixTimeStampUTC() + (newEffect.GetDurationMs() / 1000); - this.owner.zone.BroadcastPacketAroundActor(this.owner, SetActorStatusPacket.BuildPacket(this.owner.actorId, (ushort)index, (ushort)newEffect.GetStatusId())); - } - { - owner.zone.BroadcastPacketsAroundActor(owner, owner.GetActorStatusPackets()); + int index = 0; + + //If effect is already in the list of statuses, get that index, otherwise find the first open index + 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(); + SetStatusAtIndex(index, newEffect.GetStatusId()); + //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.GetEndTime()) : 0xFFFFFFFF; + SetTimeAtIndex(index, time); + //owner.charaWork.statusShownTime[index] = time; + //owner.zone.BroadcastPacketAroundActor(owner, SetActorStatusPacket.BuildPacket(owner.actorId, (ushort)index, newEffect.GetStatusId())); + //owner.zone.BroadcastPacketsAroundActor(owner, owner.GetActorStatusPackets()); } owner.RecalculateStats(); } @@ -115,47 +221,80 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai return false; } - public void RemoveStatusEffect(StatusEffect effect, bool silent = false) + public bool RemoveStatusEffect(StatusEffect effect, bool silent = false) { - if (effects.ContainsKey(effect.GetStatusEffectId())) + bool removedEffect = false; + 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) + if (!silent && !effect.GetSilent() && (effect.GetFlags() & (uint)StatusEffectFlags.Silent) == 0) { - // todo: send packet to client with effect added message - + owner.DoBattleAction(0, 0, new BattleAction(owner.actorId, 30331, effect.GetStatusEffectId())); } + //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; + removedEffect = true; } + + return removedEffect; } - public void RemoveStatusEffect(uint effectId, bool silent = false) + public bool RemoveStatusEffect(uint effectId, bool silent = false) { + bool removedEffect = false; foreach (var effect in effects.Values) { if (effect.GetStatusEffectId() == effectId) { RemoveStatusEffect(effect, effect.GetSilent() || silent); + removedEffect = true; break; } } + + return removedEffect; + } + + + //Remove status effect and return the battleaction message instead of sending it immediately + public BattleAction RemoveStatusEffectForBattleAction(uint effectId, ushort worldMasterTextId = 30331) + { + BattleAction action = null; + if (RemoveStatusEffect(effectId, true)) + action = new BattleAction(owner.actorId, worldMasterTextId, effectId); + + return action; + } + + //Remove status effect and return the battleaction message instead of sending it immediately + public BattleAction RemoveStatusEffectForBattleAction(StatusEffect effect, ushort worldMasterTextId = 30331) + { + BattleAction action = null; + if (RemoveStatusEffect(effect, true)) + action = new BattleAction(owner.actorId, worldMasterTextId, effect.GetStatusEffectId()); + return action; } public StatusEffect CopyEffect(StatusEffect effect) { - var newEffect = new StatusEffect(this.owner, effect); - newEffect.SetOwner(this.owner); + var newEffect = new StatusEffect(owner, effect); + newEffect.SetOwner(owner); // todo: should source be copied too? return AddStatusEffect(newEffect, effect.GetSource()) ? newEffect : null; } @@ -163,10 +302,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public bool RemoveStatusEffectsByFlags(uint flags, bool silent = false) { // build list of effects to remove - var removeEffects = new List(); - foreach (var effect in effects.Values) - if ((effect.GetFlags() & flags) != 0) - removeEffects.Add(effect); + var removeEffects = GetStatusEffectsByFlag(flags); // remove effects from main list foreach (var effect in removeEffects) @@ -190,12 +326,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai { var list = new List(); foreach (var effect in effects.Values) - { if ((effect.GetFlags() & flag) != 0) - { list.Add(effect); - } - } + return list; } @@ -227,5 +360,88 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai Database.SavePlayerStatusEffects((Player)owner); } } + + public void CallLuaFunctionByFlag(uint flag, string function, params object[] args) + { + var effects = GetStatusEffectsByFlag(flag); + + object[] argsWithEffect = new object[args.Length + 1]; + + for (int i = 0; i < args.Length; i++) + argsWithEffect[i + 1] = args[i]; + + foreach (var effect in effects) + { + argsWithEffect[0] = effect; + effect.CallLuaFunction(owner, function, argsWithEffect); + } + } + + //Sets the status id at an index. + //Changing a status to another doesn't seem to work. If updating an index that already has an effect, set it to 0 first then to the correct status + public void SetStatusAtIndex(int index, ushort statusId) + { + owner.charaWork.status[index] = statusId; + //owner.zone.BroadcastPacketAroundActor(owner, SetActorStatusPacket.BuildPacket(owner.actorId, (ushort)index, statusId)); + + statusSubpackets.Add(SetActorStatusPacket.BuildPacket(owner.actorId, (ushort)index, statusId)); + owner.updateFlags |= ActorUpdateFlags.Status; + } + + public void SetTimeAtIndex(int index, uint time) + { + owner.charaWork.statusShownTime[index] = time; + statusTimerPropPacketUtil.AddProperty($"charaWork.statusShownTime[{index}]"); + owner.updateFlags |= ActorUpdateFlags.StatusTime; + } + + public List GetStatusPackets() + { + return statusSubpackets; + } + + public List GetStatusTimerPackets() + { + return statusTimerPropPacketUtil.Done(); + } + + public void ResetPropPacketUtil() + { + statusTimerPropPacketUtil = new ActorPropertyPacketUtil("charaWork/status", owner); + } + + //Overwrites effectToBeReplaced with a new status effect + //Returns the message of the new effect being added + //Doing this instead of simply calling remove then add so that the new effect is in the same slot as the old one + //There should be a better way to do this + //Currently causes the icons to blink whenb eing rpelaced + public BattleAction ReplaceEffect(StatusEffect effectToBeReplaced, uint newEffectId, byte tier, double magnitude, uint duration) + { + StatusEffect newEffect = Server.GetWorldManager().GetStatusEffect(newEffectId); + newEffect.SetTier(tier); + newEffect.SetMagnitude(magnitude); + newEffect.SetDuration(duration); + newEffect.SetOwner(effectToBeReplaced.GetOwner()); + effectToBeReplaced.CallLuaFunction(owner, "onLose", owner, effectToBeReplaced); + newEffect.CallLuaFunction(owner, "onGain", owner, newEffect); + effects.Remove(effectToBeReplaced.GetStatusEffectId()); + + newEffect.SetStartTime(DateTime.Now); + newEffect.SetEndTime(DateTime.Now.AddSeconds(newEffect.GetDuration())); + uint time = (newEffect.GetFlags() & (uint)StatusEffectFlags.Stance) == 0 ? Utils.UnixTimeStampUTC(newEffect.GetEndTime()) : 0xFFFFFFFF; + int index = Array.IndexOf(owner.charaWork.status, effectToBeReplaced.GetStatusId()); + + //owner.charaWork.status[index] = newEffect.GetStatusId(); + owner.charaWork.statusShownTime[index] = time; + effects[newEffectId] = newEffect; + + SetStatusAtIndex(index, 0); + + //charawork/status + SetStatusAtIndex(index, (ushort) (newEffectId - 200000)); + SetTimeAtIndex(index, time); + + return new BattleAction(owner.actorId, 30328, (uint) HitEffect.StatusEffectType | newEffectId); + } } -} +} \ No newline at end of file diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs index 6533680e..a83d130d 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; @@ -152,11 +149,15 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers public override void Cast(Character target, uint spellId) { // todo: + if(owner.aiContainer.CanChangeState()) + owner.aiContainer.InternalCast(target, spellId); } public override void Ability(Character target, uint abilityId) { // todo: + if (owner.aiContainer.CanChangeState()) + owner.aiContainer.InternalAbility(target, abilityId); } public override void RangedAttack(Character target) @@ -213,13 +214,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) { - 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 +240,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 +276,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 +393,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/controllers/PlayerController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs index 975fd417..1505d453 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/PlayerController.cs @@ -21,6 +21,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers public override void Update(DateTime tick) { + /* if (owner.newMainState != owner.currentMainState) { if (owner.newMainState == SetActorStatePacket.MAIN_STATE_ACTIVE) @@ -32,7 +33,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers owner.Disengage(); } owner.currentMainState = (ushort)owner.newMainState; - } + }*/ } public override void ChangeTarget(Character 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..91896b5e 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/helpers/TargetFind.cs @@ -172,66 +172,78 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai // todo: this is stupid bool withPet = (flags & ValidTarget.Ally) != 0 || masterTarget.allegiance != owner.allegiance; - if (masterTarget != null) + if (masterTarget != null && CanTarget(masterTarget)) targets.Add(masterTarget); 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 > 8) + targets.RemoveRange(8, targets.Count - 8); + + //Curaga starts with lowest health players, so the targets are definitely sorted at least for some abilities + //Other aoe abilities might be sorted by distance? + //Protect is random + targets.Sort(delegate (Character a, Character b) { return a.GetHP().CompareTo(b.GetHP()); }); } /// @@ -311,6 +323,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)) @@ -332,6 +356,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (target == null || !retarget && targets.Contains(target)) return false; + if ((validTarget & ValidTarget.Player) != 0 && target is Player) + return true; + + if ((validTarget & ValidTarget.PartyMember) != 0 && target.currentParty == owner.currentParty) + return true; + // cant target dead if ((validTarget & ValidTarget.Corpse) == 0 && target.IsDead()) return false; @@ -342,6 +372,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if ((validTarget & ValidTarget.Enemy) != 0 && target.allegiance == owner.allegiance) return false; + if (((validTarget & ValidTarget.PartyMember) == 0) && ((validTarget & ValidTarget.Self) == 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; @@ -359,6 +395,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (validTarget == ValidTarget.Self && aoeType == TargetFindAOEType.None && owner != target) return false; + if ((validTarget & ValidTarget.Self) == 0 && target == owner) + return false; + // this is fuckin retarded, think of a better way l8r if (!ignoreAOE) { 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..23f306c8 --- /dev/null +++ b/FFXIVClassic Map Server/actors/chara/ai/state/AbilityState.cs @@ -0,0 +1,136 @@ +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); + } + } + + 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; + + owner.DoBattleCommand(skill, "ability"); + } + + 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..2319db0a 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs @@ -32,9 +32,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state public override bool Update(DateTime tick) { - if ((target == null || owner.target != target || owner.target?.actorId != owner.currentLockedTarget) && owner.isAutoAttackEnabled) - owner.aiContainer.ChangeTarget(target = owner.zone.FindActorInArea(owner.currentLockedTarget)); + owner.aiContainer.ChangeTarget(target = owner.zone.FindActorInArea(owner.currentTarget)); if (target == null || target.IsDead()) { @@ -82,7 +81,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 +98,40 @@ 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); + + + // todo: Change this to use a BattleCommand like the other states + + //List actions = new List(); + BattleActionContainer actions = new BattleActionContainer(); + target.SetMod((uint) Modifier.MinimumHpLock, 0); + + var i = 0; + for (int hitNum = 0; hitNum < 1 /* owner.GetMod((uint) Modifier.HitCount)*/; hitNum++) + { + BattleAction action = new BattleAction(target.actorId, 0x765D, (uint)HitEffect.Hit, 100, (byte)HitDirection.None, (byte) hitNum); + action.commandType = CommandType.AutoAttack; + action.actionType = ActionType.Physical; + action.actionProperty = (ActionProperty) owner.GetMod(Modifier.AttackType); + // evasion, miss, dodge, etc to be handled in script, calling helpers from scripts/weaponskills.lua + // temporary evade/miss/etc function to test hit effects + action.DoAction(owner, target, null, actions); + } + + // todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action + BattleAction[] errors = (BattleAction[])actions.GetList().ToArray().Clone(); + BattleAction error = null;// new BattleAction(0, null, 0, 0); + //owner.DoActions(null, actions.GetList(), ref error); + //owner.OnAttack(this, actions[0], ref errorResult); + owner.DoBattleAction(22104, 0x19001000, actions.GetList()); } 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,12 +147,12 @@ 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() { - if (!owner.isAutoAttackEnabled) + if (!owner.isAutoAttackEnabled || target.allegiance == owner.allegiance) { return false; } 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..14766969 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,36 @@ 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; + } + + //Modify spell based on status effects. Need to do it here because they can modify cast times + List effects = owner.statusEffects.GetStatusEffectsByFlag((uint) (StatusEffectFlags.ActivateOnCastStart)); + + //modify skill based on status effects + foreach (var effect in effects) + lua.LuaEngine.CallLuaStatusEffectFunction(owner, effect, "onMagicCast", owner, effect, spell); + + 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 +99,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 +124,15 @@ 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) - { - 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; - } - - // 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); + owner.DoBattleCommand(spell, "magic"); } public override void TryInterrupt() @@ -127,10 +140,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 +167,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 +183,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..d0bf7121 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,36 @@ 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 +93,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; @@ -88,26 +119,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state { skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget); isCompleted = true; - var targets = skill.targetFind.GetTargets(); - BattleAction[] actions = new BattleAction[targets.Count]; - - var i = 0; - 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); - } - - // 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); - owner.DoBattleAction(skill.id, skill.battleAnimation, actions); - + owner.DoBattleCommand(skill, "weaponskill"); + owner.statusEffects.RemoveStatusEffectsByFlags((uint) StatusEffectFlags.LoseOnAttacking); + lua.LuaEngine.GetInstance().OnSignal("weaponskillUsed"); } public override void TryInterrupt() @@ -115,10 +131,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 +150,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 +160,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..32b96c38 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/utils/BattleUtils.cs @@ -7,79 +7,192 @@ 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.actors.chara.npc; +using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic.Common; namespace FFXIVClassic_Map_Server.actors.chara.ai.utils { static class BattleUtils { + + public static Dictionary SingleHitTypeTextIds = new Dictionary() + { + { HitType.Miss, 30311 }, + { HitType.Evade, 30310 }, + { HitType.Parry, 30308 }, + { HitType.Block, 30306 }, + { HitType.Resist, 30310 }, //Resists seem to use the evade text id + { HitType.Hit, 30301 }, + { HitType.Crit, 30302 } + }; + + public static Dictionary MultiHitTypeTextIds = new Dictionary() + { + { HitType.Miss, 30449 }, //The attack misses. + { HitType.Evade, 0 }, //Evades were removed before multi hit skills got their own messages, so this doesnt exist + { HitType.Parry, 30448 }, //[Target] parries, taking x points of damage. + { HitType.Block, 30447 }, //[Target] blocks, taking x points of damage. + { HitType.Resist, 0 }, //No spells are multi-hit, so this doesn't exist + { HitType.Hit, 30443 }, //[Target] tales x points of damage + { HitType.Crit, 30444 } //Critical! [Target] takes x points of damage. + }; + + 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 } + }; + + //Most of these numbers I'm fairly certain are correct. The repeated numbers at levels 23 and 48 I'm less sure about but they do match some weird spots in the EXP graph + + public static ushort[] BASEEXP = {150, 150, 150, 150, 150, 150, 150, 150, 150, 150, //Level <= 10 + 150, 150, 150, 150, 150, 150, 150, 150, 160, 170, //Level <= 20 + 180, 190, 190, 200, 210, 220, 230, 240, 250, 260, //Level <= 30 + 270, 280, 290, 300, 310, 320, 330, 340, 350, 360, //Level <= 40 + 370, 380, 380, 390, 400, 410, 420, 430, 430, 440}; //Level <= 50 + 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; } - public static ushort CalculateAttackDamage(Character attacker, Character defender, BattleAction action) + private static double CalculateDlvlModifier(short dlvl) { - ushort damage = (ushort)(Program.Random.Next(10) * 10); + //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; - // todo: handle all other crap before protect/stoneskin - // todo: handle crit etc - if (defender.statusEffects.HasStatusEffect(StatusEffectId.Protect) || defender.statusEffects.HasStatusEffect(StatusEffectId.Protect2)) - { - if (action != null) - action.effectId |= (uint)HitEffect.Protect; - } + if (dlvl >= 0) + modifier = (.35 * dlvl) + .225; + else + modifier = (.01 * dlvl) + .25; - if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin)) - { - if (action != null) - action.effectId |= (uint)HitEffect.Stoneskin; - } - return damage; + return modifier.Clamp(0, 1); } - public static ushort GetCriticalHitDamage(Character attacker, Character defender, BattleAction action) + //Damage calculations + //Calculate damage of action + //We could probably just do this when determining the action's hit type + public static void CalculatePhysicalDamageTaken(Character attacker, Character defender, BattleCommand skill, BattleAction action) { - ushort damage = action.amount; + short dlvl = (short)(defender.GetLevel() - attacker.GetLevel()); - // todo: - // - // action.effectId |= (uint)HitEffect.Critical; - // - return damage; + // todo: physical resistances + + //dlvl, Defense, and Vitality all effect how much damage is taken after hittype takes effect + //player attacks cannot do more than 9999 damage. + //VIT is turned into Defense at a 3:2 ratio in calculatestats, so don't need to do that here + double damageTakenPercent = 1 - (defender.GetMod(Modifier.DamageTakenDown) / 100.0); + action.amount = (ushort)(action.amount - CalculateDlvlModifier(dlvl) * (defender.GetMod((uint)Modifier.Defense))).Clamp(0, 9999); + action.amount = (ushort)(action.amount * damageTakenPercent).Clamp(0, 9999); } - public static ushort CalculateSpellDamage(Character attacker, Character defender, BattleAction action) + + public static void CalculateSpellDamageTaken(Character attacker, Character defender, BattleCommand skill, BattleAction action) { - ushort damage = 0; + short dlvl = (short)(defender.GetLevel() - attacker.GetLevel()); - // todo: handle all other crap before shell/stoneskin + // todo: elemental resistances + //Patch 1.19: + //Magic Defense has been abolished and no longer appears in equipment attributes. + //The effect of elemental attributes has been changed to that of reducing damage from element-based attacks. - 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; - } - } + //http://kanican.livejournal.com/55370.html: + //elemental resistance stats are not actually related to resists (except for status effects), instead they impact damage taken - if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin)) - { - if (action != null) - action.effectId |= (uint)HitEffect.Stoneskin; - } - return damage; + + //dlvl, Defense, and Vitality all effect how much damage is taken after hittype takes effect + //player attacks cannot do more than 9999 damage. + double damageTakenPercent = 1 - (defender.GetMod(Modifier.DamageTakenDown) / 100.0); + action.amount = (ushort)(action.amount - CalculateDlvlModifier(dlvl) * (defender.GetMod((uint)Modifier.Defense) + 0.67 * defender.GetMod((uint)Modifier.Vitality))).Clamp(0, 9999); + action.amount = (ushort)(action.amount * damageTakenPercent).Clamp(0, 9999); } - public static void DamageTarget(Character attacker, Character defender, BattleAction action, DamageTakenType type) + + public static void CalculateBlockDamage(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + double percentBlocked; + + //Aegis boon forces a full block + if (defender.statusEffects.HasStatusEffect(StatusEffectId.AegisBoon)) + percentBlocked = 1.0; + else + { + //Is this a case where VIT gives Block? + percentBlocked = defender.GetMod((uint)Modifier.Block) * 0.002;//Every point of Block adds .2% to how much is blocked + percentBlocked += defender.GetMod((uint)Modifier.Vitality) * 0.001;//Every point of vitality adds .1% to how much is blocked + } + + action.amountMitigated = (ushort)(action.amount * percentBlocked); + action.amount = (ushort)(action.amount * (1.0 - percentBlocked)); + } + + //don't know exact crit bonus 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, which breaks the cap) + 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 = 0.75; + + action.amountMitigated = (ushort)(action.amount * (1 - percentParry)); + 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 = 0.5; + + action.amountMitigated = (ushort)(action.amount * (1 - percentResist)); + action.amount = (ushort)(action.amount * percentResist); + } + + //It's weird that stoneskin is handled in C# and all other buffs are in scripts right now + //But it's because stoneskin acts like both a preaction and postaction buff in that it falls off after damage is dealt but impacts how much damage is dealt + public static void HandleStoneskin(Character defender, BattleAction action) + { + var mitigation = Math.Min(action.amount, defender.GetMod(Modifier.Stoneskin)); + + action.amount = (ushort) (action.amount - mitigation).Clamp(0, 9999); + defender.SubtractMod((uint)Modifier.Stoneskin, mitigation); + } + + public static void DamageTarget(Character attacker, Character defender, BattleAction action, BattleActionContainer actionContainer= null) { if (defender != null) { + + defender.DelHP((short)action.amount); + attacker.OnDamageDealt(defender, action, actionContainer); + defender.OnDamageTaken(attacker, action, actionContainer); + // todo: other stuff too if (defender is BattleNpc) { @@ -88,28 +201,391 @@ 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.OnDamageTaken(attacker, action, type); } } - public static int CalculateSpellDamage(Character attacker, Character defender, BattleCommand spell) + public static void HealTarget(Character caster, Character target, BattleAction action, BattleActionContainer actionContainer = null) { - // todo: spell formulas and stuff (stoneskin, mods, stats, etc) - return 69; + if (target != null) + { + target.AddHP(action.amount); + + target.statusEffects.CallLuaFunctionByFlag((uint) StatusEffectFlags.ActivateOnHealed, "onHealed", caster, target, action, actionContainer); + } + } + + + #region Rate Functions + + //How is accuracy actually calculated? + public static double GetHitRate(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + double hitRate = 80.0; + + //Add raw hit rate buffs, subtract raw evade buffs, take into account skill's accuracy modifier. + double hitBuff = attacker.GetMod(Modifier.RawHitRate); + double evadeBuff = defender.GetMod(Modifier.RawEvadeRate); + float modifier = skill != null ? skill.accuracyModifier : 0; + hitRate += (hitBuff + modifier).Clamp(0, 100.0); + hitRate -= evadeBuff; + return hitRate.Clamp(0, 100.0); + } + + //Whats the parry formula? + public static double GetParryRate(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + //Can't parry with shield, can't parry rear attacks + if (defender.GetMod((uint)Modifier.HasShield) != 0 || action.param == (byte) HitDirection.Rear) + return 0; + + double parryRate = 10.0; + + parryRate += defender.GetMod(Modifier.Parry) * 0.1;//.1% rate for every point of Parry + + return parryRate + (defender.GetMod(Modifier.RawParryRate)); + } + + public static double GetCritRate(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + if (action.actionType == ActionType.Status) + return 0.0; + + //using 10.0 for now since gear isn't working + double critRate = 10.0;// 0.16 * attacker.GetMod((uint)Modifier.CritRating);//Crit rating adds .16% per point + + //Add additional crit rate from skill + //Should this be a raw percent or a flat crit raitng? the wording on skills/buffs isn't clear. + critRate += 0.16 * (skill != null ? skill.bonusCritRate : 0); + + return critRate + attacker.GetMod(Modifier.RawCritRate); + } + + //http://kanican.livejournal.com/55370.html + // todo: figure that out + public static double GetResistRate(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + // todo: add elemental stuff + //Can only resist spells? + if (action.commandType != CommandType.Spell && action.actionProperty <= ActionProperty.Projectile) + return 0.0; + + return 15.0 + defender.GetMod(Modifier.RawResistRate); + } + + //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, BattleAction action) + { + //Shields are required to block and can't block from rear. + if (defender.GetMod((uint)Modifier.HasShield) == 0 || action.param == (byte)HitDirection.Rear) + return 0; + + short dlvl = (short)(defender.GetLevel() - attacker.GetLevel()); + double blockRate = (2.5 * dlvl) + 5; // Base block rate + + //Is this one of those thing where DEX gives block rate and this would be taking DEX into account twice? + blockRate += defender.GetMod((uint)Modifier.Dexterity) * 0.1;// .1% for every dex + blockRate += defender.GetMod((uint)Modifier.BlockRate) * 0.2;// .2% for every block rate + + return Math.Min(blockRate, 25.0) + defender.GetMod((uint)Modifier.RawBlockRate); + } + + #endregion + + public static bool TryCrit(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + if ((Program.Random.NextDouble() * 100) <= action.critRate) + { + action.hitType = HitType.Crit; + CalculateCritDamage(attacker, defender, skill, action); + + if(skill != null) + skill.actionCrit = true; + + return true; + } + + return false; + } + + public static bool TryResist(Character attacker, Character defender, BattleCommand skill, BattleAction action) + { + if ((Program.Random.NextDouble() * 100) <= action.resistRate) + { + 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() * 100) <= action.blockRate) + { + action.hitType = 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() * 100) <= action.parryRate) + { + action.hitType = 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() * 100) >= GetHitRate(attacker, defender, skill, action)) + { + action.hitType = (ushort)HitType.Miss; + //On misses, the entire amount is considered mitigated + action.amountMitigated = action.amount; + action.amount = 0; + 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 DoAction(Character caster, Character target, BattleCommand skill, BattleAction action, BattleActionContainer actionContainer = null) + { + switch (action.actionType) + { + case (ActionType.Physical): + FinishActionPhysical(caster, target, skill, action, actionContainer); + break; + case (ActionType.Magic): + FinishActionSpell(caster, target, skill, action, actionContainer); + break; + case (ActionType.Heal): + FinishActionHeal(caster, target, skill, action, actionContainer); + break; + case (ActionType.Status): + FinishActionStatus(caster, target, skill, action, actionContainer); + break; + } + } + + //Determine the hit type, set the hit effect, modify damage based on stoneskin and hit type, hit target + public static void FinishActionPhysical(Character attacker, Character defender, BattleCommand skill, BattleAction action, BattleActionContainer actionContainer = null) + { + //Figure out the hit type and change damage depending on hit type + if (!TryMiss(attacker, defender, skill, action)) + { + //Handle Stoneskin here because it seems like stoneskin mitigates damage done before taking into consideration crit/block/parry damage reductions. + //This is based on the fact that a 0 damage attack due to stoneskin will heal for 0 with Aegis Boon, meaning Aegis Boon didn't mitigate any damage + HandleStoneskin(defender, action); + + //Crits can't be blocked (is this true for Aegis Boon and Divine Veil?) or parried so they are checked first. + if (!TryCrit(attacker, defender, skill, action)) + //Block and parry order don't really matter because if you can block you can't parry and vice versa + if (!TryBlock(attacker, defender, skill, action)) + if(!TryParry(attacker, defender, skill, action)) + //Finally if it's none of these, the attack was a hit + action.hitType = HitType.Hit; + } + + //Actions have different text ids depending on whether they're a part of a multi-hit ws or not. + Dictionary textIds = SingleHitTypeTextIds; + + //If this is the first hit of a multi hit command, add the "You use [command] on [target]" action + //Needs to be done here because certain buff messages appear before it. + if (skill != null && skill.numHits > 1) + { + if (action.hitNum == 1) + actionContainer?.AddAction(new BattleAction(attacker.actorId, 30441, 0)); + + textIds = MultiHitTypeTextIds; + } + + //Set the correct textId + action.worldMasterTextId = textIds[action.hitType]; + + //Set the hit effect + SetHitEffectPhysical(attacker, defender, skill, action, actionContainer); + + //Modify damage based on defender's stats + CalculatePhysicalDamageTaken(attacker, defender, skill, action); + + actionContainer.AddAction(action); + action.enmity = (ushort) (action.enmity * (skill != null ? skill.enmityModifier : 1)); + //Damage the target + DamageTarget(attacker, defender, action, actionContainer); + } + + public static void FinishActionSpell(Character attacker, Character defender, BattleCommand skill, BattleAction action, BattleActionContainer actionContainer = null) + { + //Determine the hit type of the action + if (!TryMiss(attacker, defender, skill, action)) + { + HandleStoneskin(defender, action); + if (!TryCrit(attacker, defender, skill, action)) + if (!TryResist(attacker, defender, skill, action)) + action.hitType = HitType.Hit; + } + + //There are no multi-hit spells + action.worldMasterTextId = SingleHitTypeTextIds[action.hitType]; + + //Set the hit effect + SetHitEffectSpell(attacker, defender, skill, action); + + HandleStoneskin(defender, action); + + CalculateSpellDamageTaken(attacker, defender, skill, action); + + actionContainer.AddAction(action); + + DamageTarget(attacker, defender, action, actionContainer); + } + + public static void FinishActionHeal(Character attacker, Character defender, BattleCommand skill, BattleAction action, BattleActionContainer actionContainer = null) + { + //Set the hit effect + SetHitEffectHeal(attacker, defender, skill, action); + + actionContainer.AddAction(action); + + HealTarget(attacker, defender, action, actionContainer); + } + + public static void FinishActionStatus(Character attacker, Character defender, BattleCommand skill, BattleAction action, BattleActionContainer actionContainer = null) + { + //Set the hit effect + SetHitEffectStatus(attacker, defender, skill, action); + + TryStatus(attacker, defender, skill, action, actionContainer, false); + + actionContainer.AddAction(action); + } + + public static void SetHitEffectPhysical(Character attacker, Character defender, BattleCommand skill, BattleAction action, BattleActionContainer actionContainer) + { + var hitEffect = HitEffect.HitEffectType; + HitType hitType = action.hitType; + + //Don't know what recoil is actually based on, just guessing + //Crit is 2 and 3 together + if (hitType == HitType.Crit) + hitEffect |= HitEffect.CriticalHit; + else + { + //It's not clear what recoil level is based on for physical attacks + double percentDealt = (100.0 * (action.amount / defender.GetMaxHP())); + if (percentDealt > 5.0) + hitEffect |= HitEffect.RecoilLv2; + else if (percentDealt > 10) + hitEffect |= HitEffect.RecoilLv3; + } + + hitEffect |= HitTypeEffects[hitType]; + + //For combos that land, add the combo effect + if (skill != null && skill.isCombo && hitType > HitType.Evade && hitType != HitType.Evade) + hitEffect |= (HitEffect)(skill.comboStep << 15); + + //if attack hit the target, take into account protective status effects + if (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 SetHitEffectSpell(Character attacker, Character defender, BattleCommand skill, BattleAction action, BattleActionContainer actionContainer = null) + { + var hitEffect = HitEffect.MagicEffectType; + HitType hitType = action.hitType; + + //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 (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 + hitEffect |= HitEffect.RecoilLv3; + + hitEffect |= HitTypeEffects[hitType]; + + if (skill != null && skill.isCombo) + hitEffect |= (HitEffect)(skill.comboStep << 15); + + //if attack hit the target, take into account protective status effects + if (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; + + action.hitType = HitType.Hit; } 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) { - + } else { @@ -117,5 +593,234 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils } return scaledCost; } + + + //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 + //this is stupid + public static void TryStatus(Character caster, Character target, BattleCommand skill, BattleAction action, BattleActionContainer battleActions, bool isAdditional = true) + { + double rand = Program.Random.NextDouble(); + + //Statuses only land for non-resisted attacks and attacks that hit + if (skill != null && skill.statusId != 0 && (action.hitType > HitType.Evade && action.hitType != HitType.Resist) && 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.SetMagnitude(skill.statusMagnitude); + effect.SetOwner(target); + if (target.statusEffects.AddStatusEffect(effect, caster)) + { + //If we need an extra action to show the status text + if (isAdditional) + battleActions.AddAction(target.actorId, 30328, skill.statusId | (uint) HitEffect.StatusEffectType); + } + else + action.worldMasterTextId = 32002;//Is this right? + } + else + { + //until all effects are scripted and added to db just doing this + if (target.statusEffects.AddStatusEffect(skill.statusId, skill.statusTier, skill.statusMagnitude, skill.statusDuration, 3000)) + { + //If we need an extra action to show the status text + if (isAdditional) + battleActions.AddAction(target.actorId, 30328, skill.statusId | (uint) HitEffect.StatusEffectType); + } + else + action.worldMasterTextId = 32002;//Is this right? + } + } + } + + //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; + } + + + #region experience helpers + //See 1.19 patch notes for exp info. + public static ushort GetBaseEXP(Player player, BattleNpc mob) + { + //The way EXP seems to work for most enemies is that it gets the lower character's level, gets the base exp for that level, then uses dlvl to modify that exp + //Less than -19 dlvl gives 0 exp and no message is sent. + //This equation doesn't seem to work for certain bosses or NMs. + //Some enemies might give less EXP? Unsure on this. It seems like there might have been a change in base exp amounts after 1.19 + + //Example: + //Level 50 in a party kills a level 45 enemy + //Base exp is 400, as that's the base EXP for level 45 + //That's multiplied by the dlvl modifier for -5, which is 0.5625, which gives 225 + //That's then multiplied by the party modifier, which seems to be 0.667 regardless of party size, which gives 150 + //150 is then modified by bonus experience from food, rested exp, links, and chains + + int dlvl = mob.GetLevel() - player.GetLevel(); + if (dlvl <= -20) + return 0; + + int baseLevel = Math.Min(player.GetLevel(), mob.GetLevel()); + ushort baseEXP = BASEEXP[baseLevel]; + + double dlvlModifier = 1.0; + + //There's 2 functions depending on if the dlvl is positive or negative. + if (dlvl >= 0) + //I'm not sure if this caps out at some point. This is correct up to at least +9 dlvl though. + dlvlModifier += 0.2 * dlvl; + else + //0.1x + 0.0025x^2 + dlvlModifier += 0.1 * dlvl + 0.0025 * (dlvl * dlvl); + + //The party modifier isn't clear yet. It seems like it might just be 0.667 for any number of members in a group, but the 1.19 notes say it's variable + //There also seem to be some cases where it simply doesn't apply but it isn't obvious if that's correct or when it applies if it is correct + double partyModifier = player.currentParty.GetMemberCount() == 1 ? 1.0 : 0.667; + + baseEXP = (ushort) (baseEXP * dlvlModifier * partyModifier); + + return baseEXP; + } + + //Gets the EXP bonus when enemies link + public static byte GetLinkBonus(ushort linkCount) + { + byte bonus = 0; + + switch (linkCount) + { + case (0): + break; + case (1): + bonus = 25; + break; + case (2): + bonus = 50; + break; + case (3): + bonus = 75; + break; + case (4): + default: + bonus = 100; + break; + } + + return bonus; + } + + //Gets EXP chain bonus for Attacker fighting Defender + //Official text on EXP Chains: An EXP Chain occurs when players consecutively defeat enemies of equal or higher level than themselves within a specific amount of time. + //Assuming this means that there is no bonus for enemies below player's level and EXP chains are specific to the person, not party + public static byte GetChainBonus(ushort tier) + { + byte bonus = 0; + + switch (tier) + { + case (0): + break; + case (1): + bonus = 20; + break; + case (2): + bonus = 25; + break; + case (3): + bonus = 30; + break; + case (4): + bonus = 40; + break; + default: + bonus = 50; + break; + } + return bonus; + } + + public static byte GetChainTimeLimit(ushort tier) + { + byte timeLimit = 0; + + switch (tier) + { + case (0): + timeLimit = 100; + break; + case (1): + timeLimit = 80; + break; + case (2): + timeLimit = 60; + break; + case (3): + timeLimit = 20; + break; + default: + timeLimit = 10; + break; + } + + return timeLimit; + } + + //Calculates bonus EXP for Links and Chains + public static void AddBattleBonusEXP(Player attacker, BattleNpc defender, BattleActionContainer actionContainer) + { + ushort baseExp = GetBaseEXP(attacker, defender); + + //Only bother calculating the rest if there's actually exp to be gained. + //0 exp sends no message + if (baseExp > 0) + { + int totalBonus = 0;//GetMod(Modifier.bonusEXP) + + var linkCount = defender.GetMobMod(MobModifier.LinkCount); + totalBonus += GetLinkBonus((byte)Math.Min(linkCount, 255)); + + StatusEffect effect = attacker.statusEffects.GetStatusEffectById((uint)StatusEffectId.EXPChain); + ushort expChainNumber = 0; + uint timeLimit = 100; + if (effect != null) + { + expChainNumber = effect.GetTier(); + timeLimit = (uint)(GetChainTimeLimit(expChainNumber)); + actionContainer?.AddEXPAction(new BattleAction(attacker.actorId, 33919, 0, expChainNumber, (byte)timeLimit)); + } + + totalBonus += GetChainBonus(expChainNumber); + + StatusEffect newChain = Server.GetWorldManager().GetStatusEffect((uint)StatusEffectId.EXPChain); + + newChain.SetDuration(timeLimit); + newChain.SetTier((byte)(Math.Min(expChainNumber + 1, 255))); + attacker.statusEffects.AddStatusEffect(newChain, attacker, true, true); + + actionContainer?.AddEXPActions(attacker.AddExp(baseExp, (byte)attacker.GetClass(), (byte)(totalBonus.Min(255)))); + } + } + + #endregion } -} +} \ No newline at end of file diff --git a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs index 928aedd3..a9a90b35 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 = 3; + var propPacketUtil = new ActorPropertyPacketUtil("npcWork/hate", this); propPacketUtil.AddProperty("npcWork.hateType"); return propPacketUtil.Done()[0]; } @@ -192,13 +191,28 @@ namespace FFXIVClassic_Map_Server.Actors public override bool CanCast(Character target, BattleCommand spell) { // todo: - return false; + if (target == null) + { + // Target does not exist. + return false; + } + if (Utils.Distance(positionX, positionY, positionZ, target.positionX, target.positionY, target.positionZ) > spell.range) + { + // The target is out of range. + return false; + } + if (!IsValidTarget(target, spell.mainTarget) || !spell.IsValidMainTarget(this, target)) + { + // error packet is set in IsValidTarget + return false; + } + return true; } public override bool CanWeaponSkill(Character target, BattleCommand skill) { // todo: - return false; + return true; } public override bool CanUseAbility(Character target, BattleCommand ability) @@ -257,7 +271,7 @@ namespace FFXIVClassic_Map_Server.Actors updateFlags |= ActorUpdateFlags.AllNpc; } - public override void Die(DateTime tick) + public override void Die(DateTime tick, BattleActionContainer actionContainer = null) { if (IsAlive()) { @@ -271,24 +285,25 @@ namespace FFXIVClassic_Map_Server.Actors { //I think this is, or should be odne in DoBattleAction. Packet capture had the message in the same packet as an attack // defeat/defeats - //((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0))); - + actionContainer?.AddEXPAction(new BattleAction(actorId, 30108, 0)); if (lastAttacker.currentParty != null && lastAttacker.currentParty is Party) { foreach (var memberId in ((Party)lastAttacker.currentParty).members) { - var partyMember = zone.FindActorInArea(memberId); + var partyMember = zone.FindActorInArea(memberId); // onDeath(monster, player, killer) lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker); - if (partyMember is Player) - ((Player)partyMember).AddExp(1500, (byte)partyMember.GetClass(), 5); + + // todo: add actual experience calculation and exp bonus values. + if (partyMember is Player player) + BattleUtils.AddBattleBonusEXP(player, this, actionContainer); } } else { // onDeath(monster, player, killer) lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, lastAttacker, lastAttacker); - ((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0))); + //((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0))); } } positionUpdates?.Clear(); @@ -359,18 +374,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 +394,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) @@ -414,6 +429,10 @@ namespace FFXIVClassic_Map_Server.Actors this.bnpcId = id; } + public Int64 GetMobMod(MobModifier mobMod) + { + return GetMobMod((uint)mobMod); + } public Int64 GetMobMod(uint mobModId) { @@ -431,10 +450,11 @@ namespace FFXIVClassic_Map_Server.Actors mobModifiers.Add((MobModifier)mobModId, val); } - public override void OnDamageTaken(Character attacker, BattleAction action, DamageTakenType damageTakenType) + public override void OnDamageTaken(Character attacker, BattleAction action, BattleActionContainer actionContainer = null) { if (GetMobMod((uint)MobModifier.DefendScript) != 0) - lua.LuaEngine.CallLuaBattleFunction(this, "onDamageTaken", this, attacker, action.amount, (uint)damageTakenType); + lua.LuaEngine.CallLuaBattleFunction(this, "onDamageTaken", this, attacker, action.amount); + base.OnDamageTaken(attacker, action, actionContainer); } } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs b/FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs index fe2600e6..1e525cb0 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/MobModifier.cs @@ -33,6 +33,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.npc CallForHelp = 22, // actor with this id outside of target's party with this can attack me FreeForAll = 23, // any actor can attack me Roams = 24, // Do I walk around? - RoamDelay = 25 // What is the delay between roam ticks + RoamDelay = 25, // What is the delay between roam ticks + Linked = 26, // Did I get aggroed via linking? + LinkCount = 27 // How many BattleNPCs got linked with me } } \ No newline at end of file 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..7e0a08c2 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)); } @@ -608,12 +610,12 @@ namespace FFXIVClassic_Map_Server.Actors packet.ReplaceActorID(actorId); var packets = packet.GetSubpackets(); - QueuePackets(packets); } 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 +1707,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) @@ -1771,10 +1775,29 @@ namespace FFXIVClassic_Map_Server.Actors } + + if ((updateFlags & ActorUpdateFlags.Stats) != 0) + { + var propPacketUtil = new ActorPropertyPacketUtil("charaWork/battleParameter", this); + + for (uint i = 0; i < 35; i++) + { + if (GetMod(i) != charaWork.battleTemp.generalParameter[i]) + { + charaWork.battleTemp.generalParameter[i] = (short)GetMod(i); + propPacketUtil.AddProperty($"charaWork.battleTemp.generalParameter[{i}]"); + } + } + + QueuePackets(propPacketUtil.Done()); + + } + + base.PostUpdate(tick, packets); } - public override void Die(DateTime tick) + public override void Die(DateTime tick, BattleActionContainer actionContainer = null) { // todo: death timer aiContainer.InternalDie(tick, 60); @@ -1842,27 +1865,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 +1908,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 +1926,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 +1948,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 +1974,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 +2145,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 +2163,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,57 +2200,57 @@ 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"); } //Handles exp being added, does not handle figuring out exp bonus from buffs or skill/link chains or any of that - public void AddExp(int exp, byte classId, int bonusPercent = 0) - { + //Returns BattleActions that can be sent to display the EXP gained number and level ups + public List AddExp(int exp, byte classId, byte bonusPercent = 0) + { + List actionList = new List(); exp += (int) Math.Ceiling((exp * bonusPercent / 100.0f)); - //You earn [exp](+[bonusPercent]%) experience point(s). - SendGameMessage(this, Server.GetWorldManager().GetActor(), 33934, 0x44, this, 0, 0, 0, 0, 0, 0, 0, 0, 0, exp, "", bonusPercent); + + //33935: You earn [exp] (+[bonusPercent]%) experience points. + actionList.Add(new BattleAction(actorId, 33935, 0, (ushort)exp, bonusPercent)); + bool leveled = false; int diff = MAXEXP[GetLevel() - 1] - charaWork.battleSave.skillPoint[classId - 1]; //While there is enough experience to level up, keep leveling up, unlocking skills and removing experience from exp until we don't have enough to level up while (exp >= diff && GetLevel() < charaWork.battleSave.skillLevelCap[classId]) { - //Level up LevelUp(classId); leveled = true; @@ -2255,9 +2284,12 @@ namespace FFXIVClassic_Map_Server.Actors QueuePackets(expPropertyPacket.Done()); Database.SetExp(this, classId, charaWork.battleSave.skillPoint[classId - 1]); + + return actionList; } - public void LevelUp(byte classId) + //Increaess level of current class and equips new abilities earned at that level + public void LevelUp(byte classId, List actionList = null) { if (charaWork.battleSave.skillLevel[classId - 1] < charaWork.battleSave.skillLevelCap[classId]) { @@ -2265,8 +2297,10 @@ namespace FFXIVClassic_Map_Server.Actors charaWork.battleSave.skillLevel[classId - 1]++; charaWork.parameterSave.state_mainSkillLevel++; - SendGameMessage(this, Server.GetWorldManager().GetActor(), 33909, 0x44, this, 0, 0, 0, 0, 0, 0, 0, 0, 0, (int) GetLevel()); - //If there's an ability that unlocks at this level, equip it. + //33909: You gain level [level] + actionList?.Add(new BattleAction(actorId, 33909, 0, (ushort) charaWork.battleSave.skillLevel[classId - 1])); + + //If there's any abilites that unlocks at this level, equip them. List commandIds = Server.GetWorldManager().GetBattleCommandIdByLevel(classId, GetLevel()); foreach(uint commandId in commandIds) { @@ -2274,6 +2308,9 @@ namespace FFXIVClassic_Map_Server.Actors byte jobId = ConvertClassIdToJobId(classId); if (jobId != classId) EquipAbilityInFirstOpenSlot(jobId, commandId, false); + + //33926: You learn [command]. + actionList?.Add(new BattleAction(actorId, 33926, commandId)); } } } @@ -2307,6 +2344,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 +2366,107 @@ 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); + + //These stats all correlate in a 3:2 fashion + //It seems these stats don't actually increase their respective stats. The magic stats do, however + AddMod((uint)Modifier.Attack, (long)(GetMod(Modifier.Strength) * 0.667)); + AddMod((uint)Modifier.Accuracy, (long)(GetMod(Modifier.Dexterity) * 0.667)); + AddMod((uint)Modifier.Defense, (long)(GetMod(Modifier.Vitality) * 0.667)); + + //These stats correlate in a 4:1 fashion. (Unsure if MND is accurate but it would make sense for it to be) + AddMod((uint)Modifier.MagicAttack, (long)((float)GetMod(Modifier.Intelligence) * 0.25)); + + AddMod((uint)Modifier.MagicAccuracy, (long)((float)GetMod(Modifier.Mind) * 0.25)); + AddMod((uint)Modifier.MagicHeal, (long)((float)GetMod(Modifier.Mind) * 0.25)); + + AddMod((uint)Modifier.MagicEvasion, (long)((float)GetMod(Modifier.Piety) * 0.25)); + AddMod((uint)Modifier.MagicEnfeeblingPotency, (long)((float)GetMod(Modifier.Piety) * 0.25)); + + //VIT correlates to HP in a 1:1 fashion + AddMod((uint)Modifier.Hp, (long)((float)Modifier.Vitality)); + + CalculateTraitMods(); + } + + public bool HasTrait(ushort id) + { + BattleTrait trait = Server.GetWorldManager().GetBattleTrait(id); + + return HasTrait(trait); + } + + public bool HasTrait(BattleTrait trait) + { + return (trait != null) && (trait.job == GetClass()) && (trait.level <= GetLevel()); + } + + public void CalculateTraitMods() + { + var traitIds = Server.GetWorldManager().GetAllBattleTraitIdsForClass((byte) GetClass()); + + foreach(var traitId in traitIds) + { + var trait = Server.GetWorldManager().GetBattleTrait(traitId); + if(HasTrait(trait)) + { + AddMod(trait.modifier, trait.bonus); + } + } + } } } diff --git a/FFXIVClassic Map Server/actors/group/MonsterParty.cs b/FFXIVClassic Map Server/actors/group/MonsterParty.cs index b560fd38..caf2b383 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) @@ -47,7 +48,7 @@ namespace FFXIVClassic_Map_Server.actors.group public override void SendInitWorkValues(Session session) { - SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex); + SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex); groupWork.setTarget("/_init"); SubPacket test = groupWork.buildPacket(session.id, session.id); 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..d4a38be8 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 + ammoVirtualDamagePower) / damageInterval; } } diff --git a/FFXIVClassic Map Server/dataobjects/Session.cs b/FFXIVClassic Map Server/dataobjects/Session.cs index a713efd8..b2baa962 100644 --- a/FFXIVClassic Map Server/dataobjects/Session.cs +++ b/FFXIVClassic Map Server/dataobjects/Session.cs @@ -106,7 +106,7 @@ namespace FFXIVClassic_Map_Server.dataobjects { QueuePacket(RemoveActorPacket.BuildPacket(actorInstanceList[i].actorId)); actorInstanceList.RemoveAt(i); - } + } } //Add new actors or move @@ -123,7 +123,7 @@ namespace FFXIVClassic_Map_Server.dataobjects } else { - QueuePacket(actor.GetSpawnPackets(playerActor, 1)); + QueuePacket(actor.GetSpawnPackets(playerActor, 1)); QueuePacket(actor.GetInitPackets()); QueuePacket(actor.GetSetEventStatusPackets()); 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..9382f4cb 100644 --- a/FFXIVClassic Map Server/lua/LuaEngine.cs +++ b/FFXIVClassic Map Server/lua/LuaEngine.cs @@ -176,8 +176,7 @@ namespace FFXIVClassic_Map_Server.lua public static int CallLuaStatusEffectFunction(Character actor, StatusEffect effect, string functionName, params object[] args) { // todo: this is stupid, load the actual effect name from db table - var name = ((StatusEffectId)effect.GetStatusEffectId()).ToString().ToLower(); - string path = $"./scripts/effects/{name}.lua"; + string path = $"./scripts/effects/{effect.GetName()}.lua"; if (File.Exists(path)) { @@ -247,7 +246,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 +258,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..f93d478f 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,20 @@ 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 and spells but in reverse. RecoilLv1 has a large effect, Lv3 has none. Crit is very large + //For spells they represent resists. Lv0 is a max resist, Lv3 is no resist. Crit is still used for crits. + //Heals used the same effects sometimes but it isn't clear what for, it seems random? Possibly something like a trait proccing or even just a bug + 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 +36,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 +61,29 @@ 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, + MP = 1 << 9, //Causes "MP" text to appear when used with MagicEffectType. | with Heal to make text blue + TP = 1 << 10,//Causes "TP" text to appear when used with MagicEffectType. | with Heal to make text blue //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, + Crit = HitEffect3, + Parry = Hit | HitEffect3, + Block = HitEffect4, + //Knocks you back away from the attacker. KnockbackLv1 = HitEffect4 | HitEffect2 | HitEffect1, KnockbackLv2 = HitEffect4 | HitEffect3, @@ -80,10 +111,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,28 +131,203 @@ 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 + } + + //Type of action + public enum ActionType : ushort + { + None = 0, + Physical = 1, + Magic = 2, + Heal = 3, + Status = 4 + } + + //There's are two columns in gamecommand that are for action property and action element respectively and both have percentages next to them + //the percentages are for what percent that property or element factors into the attack. Astral and Umbral are always 33% because they are both 3 elments combined + //ActionProperty and ActionElement are slightly different. Property defines whta type of attack it is, and 11-13 are used for "sonic, breath, neutral". Neutral is always used for magic + //For Element 11-13 are used for astral, umbral, and healing magic. + //Right now we aren't actually using these but when things like resists get better defined we'll have to + public enum ActionProperty : ushort + { + None = 0, + Slashing = 1, + Piercing = 2, + Blunt = 3, + Projectile = 4, + + Fire = 5, + Ice = 6, + Wind = 7, + Earth = 8, + Lightning = 9, + Water = 10, + + //These I'm not sure about. Check gameCommand.csv + Astral = 11, + Umbral = 12, + Heal = 13 + } + + + /* + public enum ActionProperty : ushort + { + None = 0, + Slashing = 1, + Piercing = 2, + Blunt = 3, + Projectile = 4, + + Fire = 5, + Ice = 6, + Wind = 7, + Earth = 8, + Lightning = 9, + Water = 10, + + Sonic = 11, + Breath = 12, + Neutral = 13, + Astral = 14, + Umbral = 15 + } + + public enum ActionElement : ushort + { + None = 0, + Slashing = 1, + Piercing = 2, + Blunt = 3, + Projectile = 4, + + Fire = 5, + Ice = 6, + Wind = 7, + Earth = 8, + Lightning = 9, + Water = 10, + + //These I'm not sure about. Check gameCommand.csv + Astral = 11, + Umbral = 12, + Heal = 13 + }*/ + + class BattleAction { public uint targetId; public ushort amount; + public ushort amountMitigated; //Amount that got blocked/evaded or resisted + 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 /// - /// this field is not actually part of the packet struct + /// these fields are not actually part of the packet struct /// public uint animation; + public CommandType commandType; //What type of command was used (ie weaponskill, ability, etc) + public ActionProperty actionProperty; //Damage type of the action + public ActionType actionType; //Type of this action (ie physical, magic, heal) + public HitType hitType; - public BattleAction(uint targetId, ushort worldMasterTextId, uint effectId, ushort amount = 0, byte param = 0, byte unknown = 1) + //Rates, I'm not sure if these need to be stored like this but with the way some buffs work maybe they do? + //Makes things like Blindside easy at least. + public double parryRate = 0.0; + public double blockRate = 0.0; + public double resistRate = 0.0; + public double hitRate = 0.0; + public double critRate = 0.0; + + public BattleAction(uint targetId, ushort worldMasterTextId, uint effectId, ushort amount = 0, byte param = 0, byte hitNum = 1) { - this.targetId = targetId; + this.targetId = targetId; this.worldMasterTextId = worldMasterTextId; this.effectId = effectId; this.amount = amount; this.param = param; - this.unknown = unknown; + this.hitNum = hitNum; + this.hitType = HitType.Hit; + this.enmity = amount; + this.commandType = (byte) CommandType.None; + } + + public BattleAction(uint targetId, BattleCommand command, byte param = 0, byte hitNum = 1) + { + this.targetId = targetId; + this.worldMasterTextId = command.worldMasterTextId; + this.param = param; + this.hitNum = hitNum; + this.commandType = command.commandType; + this.actionProperty = command.actionProperty; + this.actionType = command.actionType; + } + + //Order of what (probably) happens when a skill is used: + //Buffs that alter things like recast times or that only happen once per skill usage like Power Surge are activated + //Script calculates damage and handles any special requirements + //Rates are calculated + //Buffs that impact indiviudal hits like Blindside or Blood for Blood are activated + //The final hit type is determined + //Stoneskin takes damage + //Final damage amount is calculated using the hit type and defender's stats + //Buffs that activate or respond to damage like Rampage. Stoneskin gets removed AFTER damage if it falls off. + //Additional effects that are a part of the skill itself or weapon in case of auto attacks take place like status effects + //Certain buffs that alter the whole skill fall off (Resonance, Excruciate) + + public void DoAction(Character caster, Character target, BattleCommand skill, BattleActionContainer battleActions) + { + //First calculate rates for hit/block/etc + CalcRates(caster, target, skill); + + //Next, modify those rates based on preaction buffs + //Still not sure how we shouldh andle these + PreAction(caster, target, skill, battleActions); + + BattleUtils.DoAction(caster, target, skill, this, battleActions); + } + + + //Calculate the chance of hitting/critting/etc + public void CalcRates(Character caster, Character target, BattleCommand skill) + { + hitRate = BattleUtils.GetHitRate(caster, target, skill, this); + critRate = BattleUtils.GetCritRate(caster, target, skill, this); + blockRate = BattleUtils.GetBlockRate(caster, target, skill, this); + parryRate = BattleUtils.GetParryRate(caster, target, skill, this); + resistRate = BattleUtils.GetResistRate(caster, target, skill, this); + } + + //These are buffs that activate before the action hits. Usually they change things like hit or crit rates or damage + public void PreAction(Character caster, Character target, BattleCommand skill, BattleActionContainer battleActions) + { + target.statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnPreactionTarget, "onPreAction", caster, target, skill, this, battleActions); + + caster.statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnPreactionCaster, "onPreAction", caster, target, skill, this, battleActions); + } + + //Try and apply a status effect + public void TryStatus(Character caster, Character target, BattleCommand skill, BattleActionContainer battleActions, bool isAdditional = true) + { + BattleUtils.TryStatus(caster, target, skill, this, battleActions, isAdditional); + } + + public ushort GetHitType() + { + return (ushort)hitType; } } } diff --git a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionContainer.cs b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionContainer.cs new file mode 100644 index 00000000..3f3fbd36 --- /dev/null +++ b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionContainer.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.packets.send.actor.battle +{ + class BattleActionContainer + { + private List actionsList = new List(); + + //EXP messages are always the last mesages in battlea ction packets, so they get appended after all the rest of the actions are done. + private List expActionList = new List(); + + public BattleActionContainer() + { + + } + + public void AddAction(uint targetId, ushort worldMasterTextId, uint effectId, ushort amount = 0, byte param = 0, byte hitNum = 0) + { + AddAction(new BattleAction(targetId, worldMasterTextId, effectId, amount, param, hitNum)); + } + + //Just to make scripting simpler + public void AddMPAction(uint targetId, ushort worldMasterTextId, ushort amount) + { + uint effectId = (uint) (HitEffect.MagicEffectType | HitEffect.MP | HitEffect.Heal); + AddAction(targetId, worldMasterTextId, effectId, amount); + } + + public void AddHPAction(uint targetId, ushort worldMasterTextId, ushort amount) + { + uint effectId = (uint)(HitEffect.MagicEffectType | HitEffect.Heal); + AddAction(targetId, worldMasterTextId, effectId, amount); + } + + public void AddTPAction(uint targetId, ushort worldMasterTextId, ushort amount) + { + uint effectId = (uint)(HitEffect.MagicEffectType | HitEffect.TP); + AddAction(targetId, worldMasterTextId, effectId, amount); + } + + public void AddAction(BattleAction action) + { + if (action != null) + actionsList.Add(action); + } + + public void AddActions(List actions) + { + actionsList.AddRange(actions); + } + + public void AddEXPAction(BattleAction action) + { + expActionList.Add(action); + } + + public void AddEXPActions(List actionList) + { + expActionList.AddRange(actionList); + } + + public void CombineLists() + { + actionsList.AddRange(expActionList); + } + + public List GetList() + { + 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..f5bdde7c 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? @@ -33,7 +33,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle binWriter.Seek(0x20, SeekOrigin.Begin); binWriter.Write((UInt32)1); //Num actions (always 1 for this) binWriter.Write((UInt16)commandId); - binWriter.Write((UInt16)0x810); //? + binWriter.Write((UInt16)0x801); //? binWriter.Write((UInt32)action.targetId); @@ -41,13 +41,12 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle binWriter.Write((UInt16)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..b3d8fca8 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; } @@ -104,7 +104,9 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle binWriter.Seek(0x78, SeekOrigin.Begin); for (int i = 0; i < max; i++) + { binWriter.Write((UInt32)actionList[listOffset + i].effectId); + } binWriter.Seek(0xA0, SeekOrigin.Begin); for (int i = 0; i < max; i++) @@ -112,7 +114,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..cf593d6f 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX18Packet.cs @@ -31,9 +31,9 @@ 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)actionList.Length); //Num actions + binWriter.Write((UInt32)max); //Num actions binWriter.Write((UInt16)commandId); - binWriter.Write((UInt16)0x810); //? + binWriter.Write((UInt16)0x818); //? binWriter.Seek(0x28, SeekOrigin.Begin); for (int i = 0; i < max; i++) @@ -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; } @@ -88,31 +88,31 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle binWriter.Seek(0x20, SeekOrigin.Begin); binWriter.Write((UInt32)max); //Num actions binWriter.Write((UInt16)commandId); - binWriter.Write((UInt16)0x810); //? + binWriter.Write((UInt16)0x818); //? - 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/FFXIVClassic Map Server/packets/send/groups/GroupMembersX16Packet.cs b/FFXIVClassic Map Server/packets/send/groups/GroupMembersX16Packet.cs index 2c1c4855..b4ba775b 100644 --- a/FFXIVClassic Map Server/packets/send/groups/GroupMembersX16Packet.cs +++ b/FFXIVClassic Map Server/packets/send/groups/GroupMembersX16Packet.cs @@ -41,7 +41,7 @@ namespace FFXIVClassic_Map_Server.packets.send.group binWriter.Write(Encoding.ASCII.GetBytes(entry.name), 0, Encoding.ASCII.GetByteCount(entry.name) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(entry.name)); offset++; - } + } } } diff --git a/data/scripts/ability.lua b/data/scripts/ability.lua new file mode 100644 index 00000000..b3d59883 --- /dev/null +++ b/data/scripts/ability.lua @@ -0,0 +1,58 @@ +-- 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.CalcHitType(caster, target, skill); + action.DoAction(caster, target, skill); + action.TryStatus(caster, target, skill, false); + + return action.amount; +end; + +function onAttackAbilityFinish(caster, target, skill, action) + local damage = math.random(50, 150); + action.amount = damage; + action.DoAction(caster, target, skill); + + return action.amount; +end; + +function onHealAbilityFinish(caster, target, skill, action) + local amount = math.random(150, 250); + action.amount = amount; + action.DoAction(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..6cfe600e --- /dev/null +++ b/data/scripts/battleutils.lua @@ -0,0 +1,64 @@ +CommandType = +{ + None = 0, + AutoAttack = 1, + Weaponskill = 2, + Ability = 3, + Spell = 4 +} + +ActionType = +{ + None = 0, + Physical = 1, + Magic = 2, + Heal = 3, + Status = 4 +} + +ActionProperty = +{ + None = 0, + Physical = 1, + Magic = 2, + Heal = 4, + Status = 8, + Ranged = 16 +} + +DamageTakenType = +{ + None, + Attack, + Magic, + Weaponskill, + Ability +} + +HitDirection = +{ + None = 0, + Front = 1, + Right = 2, + Rear = 4, + Left = 8 +} + +HitType = +{ + Miss = 0, + Evade = 1, + Parry = 2, + Block = 3, + Resist = 4, + Hit = 5, + Crit = 6 +} + +TargetFindAOEType = +{ + None = 0, + Circle = 1, + Cone = 2, + Box = 3 +} \ No newline at end of file diff --git a/data/scripts/commands/Ability.lua b/data/scripts/commands/Ability.lua index e1704149..a9429e93 100644 --- a/data/scripts/commands/Ability.lua +++ b/data/scripts/commands/Ability.lua @@ -14,15 +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(); - end \ No newline at end of file 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/ActivateCommand.lua b/data/scripts/commands/ActivateCommand.lua index cec7da25..5afd87dc 100644 --- a/data/scripts/commands/ActivateCommand.lua +++ b/data/scripts/commands/ActivateCommand.lua @@ -10,9 +10,9 @@ Switches between active and passive mode states function onEventStarted(player, command, triggerName) - if (player.newMainState == 0x0000) then + if (player.currentMainState == 0x0000) then player.Engage(0, 0x0002); - elseif (player.newMainState == 0x0002) then + elseif (player.currentMainState == 0x0002) then player.Disengage(0x0000); end player:endEvent(); 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/ChangeJobCommand.lua b/data/scripts/commands/ChangeJobCommand.lua new file mode 100644 index 00000000..4cb38f6a --- /dev/null +++ b/data/scripts/commands/ChangeJobCommand.lua @@ -0,0 +1,6 @@ +function onEventStarted(player, caller, commandRequest, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + + player:SetCurrentJob(17); + + 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..2a92841e 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:GetCurrentClassOrJob(); 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/PointSearchAbility.lua b/data/scripts/commands/PointSearchAbility.lua new file mode 100644 index 00000000..ee05e8c4 --- /dev/null +++ b/data/scripts/commands/PointSearchAbility.lua @@ -0,0 +1,7 @@ + +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/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/aegis_boon.lua b/data/scripts/commands/ability/aegis_boon.lua new file mode 100644 index 00000000..84d41871 --- /dev/null +++ b/data/scripts/commands/ability/aegis_boon.lua @@ -0,0 +1,18 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27164: Swift Aegis Boon + if caster.HasTrait(27164) then + ability.recastTimeMs = ability.recastTimeMs - 15000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/barrage.lua b/data/scripts/commands/ability/barrage.lua new file mode 100644 index 00000000..6dd5eb5e --- /dev/null +++ b/data/scripts/commands/ability/barrage.lua @@ -0,0 +1,22 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + ability.statusMagnitude = 4; + + --27242: Enhanced Barrage: Adds an additional attack to barrage ( 4 -> 5 ) + if caster.HasTrait(27242) then + ability.statusMagnitude = 5; + end + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/berserk.lua b/data/scripts/commands/ability/berserk.lua new file mode 100644 index 00000000..5163866b --- /dev/null +++ b/data/scripts/commands/ability/berserk.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27205: Enhanced Berserk: Increases the effect of Berserk by 20% + if caster.HasTrait(27205) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/blindside.lua b/data/scripts/commands/ability/blindside.lua new file mode 100644 index 00000000..a3182ea8 --- /dev/null +++ b/data/scripts/commands/ability/blindside.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27121: Enhanced Blindside + if caster.HasTrait(27121) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/blissful_mind.lua b/data/scripts/commands/ability/blissful_mind.lua new file mode 100644 index 00000000..b38a8b2b --- /dev/null +++ b/data/scripts/commands/ability/blissful_mind.lua @@ -0,0 +1,43 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27362: Enhanced Blissful Mind + if caster.HasTrait(27362) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --Blissful Mind + --223228: Blissful Mind + --223242: Fully Blissful Mind + local buff = caster.statusEffects.GetStatusEffectById(223228) or caster.statusEffects.GetStatusEffectById(223242); + + --If we have a buff then Blissful Mind removes that buff and restores MP. Otherwise, it adds the Blissful Mind effect + if buff ~= nil then + local amount = buff.GetExtra(); + local remAction = caster.statusEffects.RemoveStatusEffectForBattleAction(buff, 30329); + + caster.AddMP(amount); + + actionContainer.AddMPAction(caster.actorId, 30321, amount); + actionContainer.AddAction(remAction); + else + --Blissful mind takes 25% of CURRENT HP and begins storing MP up to that point, at which point the buff changes to indicate its full + local amount = caster.GetHP() * 0.25; + + caster.DelHP(amount); + skill.statusMagnitude = amount; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + end + +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/blood_for_blood.lua b/data/scripts/commands/ability/blood_for_blood.lua new file mode 100644 index 00000000..e722cd9c --- /dev/null +++ b/data/scripts/commands/ability/blood_for_blood.lua @@ -0,0 +1,24 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27283: Enhanced Blood for Blood: Increases damage dealt to enemies by B4B by 25% + if caster.HasTrait(27283) then + ability.statusTier = 2; + end + + --27284: Swift Blood for Blood: Reduces recast time of B4B by 15 seconds + if caster.HasTrait(27284) then + ability.recastTimeMs = ability.recastTimeMs - 15000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/bloodbath.lua b/data/scripts/commands/ability/bloodbath.lua new file mode 100644 index 00000000..6ae86efa --- /dev/null +++ b/data/scripts/commands/ability/bloodbath.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27202: Swift Bloodbath + if caster.HasTrait(27202) then + ability.recastTimeMs = ability.recastTimeMs - 15000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/chameleon.lua b/data/scripts/commands/ability/chameleon.lua new file mode 100644 index 00000000..92e6c1a5 --- /dev/null +++ b/data/scripts/commands/ability/chameleon.lua @@ -0,0 +1,18 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27245: Swift Chameleon + if caster.HasTrait(27245) then + ability.recastTimeMs = ability.recastTimeMs - 60000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + target.hateContainer.UpdateHate(caster, -840); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/cleric_stance.lua b/data/scripts/commands/ability/cleric_stance.lua new file mode 100644 index 00000000..0cae6d8d --- /dev/null +++ b/data/scripts/commands/ability/cleric_stance.lua @@ -0,0 +1,15 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/collusion.lua b/data/scripts/commands/ability/collusion.lua new file mode 100644 index 00000000..e859eb1c --- /dev/null +++ b/data/scripts/commands/ability/collusion.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --8032701: Fighter's Gauntlets: Reduces Collusion cooldown by 10 seconds + if caster.GetEquipment().GetItemAtSlot(14).itemId == 8032701 then + skill.recastTimeMs = skill.recastTimeMs - 10000; + end + + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/cover.lua b/data/scripts/commands/ability/cover.lua new file mode 100644 index 00000000..5bba25bd --- /dev/null +++ b/data/scripts/commands/ability/cover.lua @@ -0,0 +1,23 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --This is for the "Cover" effect the caster receives. + local coverTier = 1 + --8032701: Gallant Surcoat: Enhances Cover + if caster.GetEquipment().GetItemAtSlot(10).itemId == 8032701 then + coverTier = 2; + end + + actionContainer.AddAction(caster.statusEffects.AddStatusForBattleAction(223063, coverTier)); + + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/dark_seal.lua b/data/scripts/commands/ability/dark_seal.lua new file mode 100644 index 00000000..1e2f2cd2 --- /dev/null +++ b/data/scripts/commands/ability/dark_seal.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27320: Swift Dark Seal + if caster.HasTrait(27320) then + ability.recastTimeMs = ability.recastTimeMs - 30000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/decoy.lua b/data/scripts/commands/ability/decoy.lua new file mode 100644 index 00000000..4e6f4d4b --- /dev/null +++ b/data/scripts/commands/ability/decoy.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27244: Enhanced Decoy: Renders Decoy capable of evading melee attacks + if caster.HasTrait(27244) then + ability.statusId = 223238; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..eed87cb9 --- /dev/null +++ b/data/scripts/commands/ability/default.lua @@ -0,0 +1,13 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, skill) + return 0; +end; + +function onAbilityStart(caster, target, skill) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/divine_veil.lua b/data/scripts/commands/ability/divine_veil.lua new file mode 100644 index 00000000..8e853610 --- /dev/null +++ b/data/scripts/commands/ability/divine_veil.lua @@ -0,0 +1,20 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --8051401: Gallant Cuisses + if caster.GetEquipment().GetItemAtSlot(14).itemId == 8051401 then + ability.statusTier = 2; + end + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..4375f9fb --- /dev/null +++ b/data/scripts/commands/ability/dragonfire_dive.lua @@ -0,0 +1,16 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + action.amount = skill.basePotency; + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/dread_spike.lua b/data/scripts/commands/ability/dread_spike.lua new file mode 100644 index 00000000..c17ac693 --- /dev/null +++ b/data/scripts/commands/ability/dread_spike.lua @@ -0,0 +1,24 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --Need a better way to do this + + for i = 223212,223217 do + local remAction = caster.statusEffects.RemoveStatusEffectForBattleAction(i, 30329) + + if remAction ~= nil then + actionContainer.AddAction(remAction); + skill.statusTier = 2; + break; + end + + end + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/elusive_jump.lua b/data/scripts/commands/ability/elusive_jump.lua new file mode 100644 index 00000000..f1d12012 --- /dev/null +++ b/data/scripts/commands/ability/elusive_jump.lua @@ -0,0 +1,16 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --How to do enmity? + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/enduring_march.lua b/data/scripts/commands/ability/enduring_march.lua new file mode 100644 index 00000000..ba3969d0 --- /dev/null +++ b/data/scripts/commands/ability/enduring_march.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27203: Enhanced Outmaneuver + if caster.HasTrait(27203) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/excruciate.lua b/data/scripts/commands/ability/excruciate.lua new file mode 100644 index 00000000..1432ddef --- /dev/null +++ b/data/scripts/commands/ability/excruciate.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27321: Enhanced Excruciate: Increases critical rate bonus from Excruciate. + if caster.HasTrait(27321) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/featherfoot.lua b/data/scripts/commands/ability/featherfoot.lua new file mode 100644 index 00000000..f4120804 --- /dev/null +++ b/data/scripts/commands/ability/featherfoot.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27123: Enhanced Featherfoot + if caster.HasTrait(27123) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/fists_of_earth.lua b/data/scripts/commands/ability/fists_of_earth.lua new file mode 100644 index 00000000..8416595e --- /dev/null +++ b/data/scripts/commands/ability/fists_of_earth.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27124: Enhanced Fists of Earth + if caster.HasTrait(27125) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/fists_of_fire.lua b/data/scripts/commands/ability/fists_of_fire.lua new file mode 100644 index 00000000..84ac5ce7 --- /dev/null +++ b/data/scripts/commands/ability/fists_of_fire.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27124: Enhanced Fists of Fire + if caster.HasTrait(27124) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/flash.lua b/data/scripts/commands/ability/flash.lua new file mode 100644 index 00000000..ec4f7b05 --- /dev/null +++ b/data/scripts/commands/ability/flash.lua @@ -0,0 +1,27 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27161: Enhanced Flash: Adds Blind effect to flash + if caster.HasTrait(27161) then + ability.statusChance = 1; + end + + --27162: Enhanced Flash II: Expands Flash to affect enemies near target + if caster.HasTrait(27162) then + ability.aoeTarget = TargetFindAOEType.Circle; + end + + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + action.enmity = 400; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/foresight.lua b/data/scripts/commands/ability/foresight.lua new file mode 100644 index 00000000..a54dff7e --- /dev/null +++ b/data/scripts/commands/ability/foresight.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27201: Swift Foresight + if caster.HasTrait(27201) then + ability.recastTimeMs = ability.recastTimeMs - 15000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/hallowed_ground.lua b/data/scripts/commands/ability/hallowed_ground.lua new file mode 100644 index 00000000..7fe937d3 --- /dev/null +++ b/data/scripts/commands/ability/hallowed_ground.lua @@ -0,0 +1,28 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27245: Swift Chameleon + if caster.HasTrait(27245) then + ability.recastTimeMs = ability.recastTimeMs - 60000; + end + return 0; +end; + +--Get all targets with hate on caster and spread 1140 enmity between them. +function onSkillFinish(caster, target, skill, action, actionContainer) + --[[ + local enemies = caster.GetTargetsWithHate() + local enmity = 1140 / enemies.Count + for enemy in enemies do + enemy.hateContainer.updateHate(enmity); + end]] + + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/hawks_eye.lua b/data/scripts/commands/ability/hawks_eye.lua new file mode 100644 index 00000000..e1b2ed10 --- /dev/null +++ b/data/scripts/commands/ability/hawks_eye.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27240: Enhanced Hawks Eye + --Increases accuracy gained by 50%. (Hawks Eye normally gives 12.5% of your accuracy, Traited it gives 18.75%) + if caster.HasTrait(27240) then + ability.statusTier = 2 + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + action.DoAction(caster, target, skill, actionContainer); +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..6d183947 --- /dev/null +++ b/data/scripts/commands/ability/invigorate.lua @@ -0,0 +1,29 @@ +require("global"); +require("Ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27280: Enhanced Invigorate: Increases duration of Invigorate by 15 seconds + if caster.HasTrait(27280) then + ability.statusDuration = ability.statusDuration + 15; + end + + --Drachen Mail: Increases Invigorate TP tick from 100 to 120. + local magnitude = 100; + + --8032704: Drachen Mail + if caster.GetEquipment().GetItemAtSlot(10).itemId == 8032704 then + magnitude = 120; + end + + ability.statusMagnitude = magnitude; + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..85f185b6 --- /dev/null +++ b/data/scripts/commands/ability/jump.lua @@ -0,0 +1,17 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/keen_flurry.lua b/data/scripts/commands/ability/keen_flurry.lua new file mode 100644 index 00000000..f06ec21a --- /dev/null +++ b/data/scripts/commands/ability/keen_flurry.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27285: Enhanced Keen Flurry: Reduces recast time of WS used during KF by 50% + if caster.HasTrait(27285) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/life_surge.lua b/data/scripts/commands/ability/life_surge.lua new file mode 100644 index 00000000..06f165fd --- /dev/null +++ b/data/scripts/commands/ability/life_surge.lua @@ -0,0 +1,53 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27282: Enhanced Life Surge: Increases effect of Life Surge by 20% + if caster.HasTrait(27282) then + ability.statusTier = 2; + end + + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --Need a better way to do this + --223212: Power Surge I + --223213: Power Surge II + --223212: Power Surge III + --No message is sent when PS is removed by Life Surge + caster.statusEffects.RemoveStatusEffect(223212, true); + caster.statusEffects.RemoveStatusEffect(223213, true); + caster.statusEffects.RemoveStatusEffect(223214, true); + + + --Using this ability moves to the next LS buff + local removeId = 0; + --223215: Life Surge I + --223216: Life Surge II + --223217: Life Surge III + if caster.statusEffects.HasStatusEffect(223215) then + removeId = 223215; + skill.statusId = 223216; + skill.statusTier = 2; + elseif caster.statusEffects.HasStatusEffect(223216) then + removeId = 223216; + skill.statusId = 223217; + skill.statusTier = 3; + elseif caster.statusEffects.HasStatusEffect(223217) then + effect = caster.statusEffects.GetStatusEffectById(223217) + effect.RefreshTime(); + skill.statusId = 223217; + end + + if not (removeId == 0) then + --caster.statusEffects.RemoveStatusEffect(removeId, true); + caster.statusEffects.ReplaceEffect(caster.statusEffects.GetStatusEffectById(removeId), skill.statusId, skill.statusTier, skill.statusMagnitude, skill.statusDuration); + end + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..4375f9fb --- /dev/null +++ b/data/scripts/commands/ability/light_shot.lua @@ -0,0 +1,16 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + action.amount = skill.basePotency; + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/necrogenesis.lua b/data/scripts/commands/ability/necrogenesis.lua new file mode 100644 index 00000000..ef6f241e --- /dev/null +++ b/data/scripts/commands/ability/necrogenesis.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27322: Swift Dark Seal + if caster.HasTrait(27322) then + ability.recastTimeMs = ability.recastTimeMs - 30000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/outmaneuver.lua b/data/scripts/commands/ability/outmaneuver.lua new file mode 100644 index 00000000..699d7eba --- /dev/null +++ b/data/scripts/commands/ability/outmaneuver.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27164: Enhanced Outmaneuver + if caster.HasTrait(27164) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/parsimony.lua b/data/scripts/commands/ability/parsimony.lua new file mode 100644 index 00000000..ce1ff01a --- /dev/null +++ b/data/scripts/commands/ability/parsimony.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27323: Enhanced Parsimony: Increases MP gained from Parsimony by 25% + if caster.HasTrait(27323) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/power_surge.lua b/data/scripts/commands/ability/power_surge.lua new file mode 100644 index 00000000..54976f89 --- /dev/null +++ b/data/scripts/commands/ability/power_surge.lua @@ -0,0 +1,24 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27281: Enhanced Power Surge: Increases effect of Power Surge by 50% + if caster.HasTrait(27281) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --Need a better way to do this + actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(223215)); + actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(223216)); + actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(223217)); + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/provoke.lua b/data/scripts/commands/ability/provoke.lua new file mode 100644 index 00000000..64830e2d --- /dev/null +++ b/data/scripts/commands/ability/provoke.lua @@ -0,0 +1,21 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27200: Enhanced Provoke: Adds Attack Down effect to Provoke. + if caster.HasTrait(27200) then + ability.statusChance = 1.0; + end + return 0; +end; + +--http://forum.square-enix.com/ffxiv/threads/47393-Tachi-s-Guide-to-Paladin-%28post-1.22b%29 +function onSkillFinish(caster, target, skill, action, actionContainer) + action.enmity = 750; + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/quelling_strike.lua b/data/scripts/commands/ability/quelling_strike.lua new file mode 100644 index 00000000..35c507a4 --- /dev/null +++ b/data/scripts/commands/ability/quelling_strike.lua @@ -0,0 +1,29 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --QS gives 300 TP by default. + skill.statusMagnitude = 300; + --I'm assuming that with raging strikes, that increases to 500. + --and traited that increases again to 750 (or 450 without RS) + if caster.statusEffects.HasStatusEffect(223221) then + actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(223221)); + skill.statusMagnitude = 500; + end + + --27241: Enhanced Quelling Strike: Increases TP gained from QS by 50% + if caster.HasTrait(27241) then + skill.statusMagnitude = skill.statusMagnitude * 1.5; + end + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/raging_strike.lua b/data/scripts/commands/ability/raging_strike.lua new file mode 100644 index 00000000..5e265f08 --- /dev/null +++ b/data/scripts/commands/ability/raging_strike.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27243: Enhanced Raging Strike: Increases effect of Raging Strike by 50% + if caster.HasTrait(27241) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/rampage.lua b/data/scripts/commands/ability/rampage.lua new file mode 100644 index 00000000..49c9bd60 --- /dev/null +++ b/data/scripts/commands/ability/rampage.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27204: Enhanced Rampage + if caster.HasTrait(27204) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/rampart.lua b/data/scripts/commands/ability/rampart.lua new file mode 100644 index 00000000..604d4929 --- /dev/null +++ b/data/scripts/commands/ability/rampart.lua @@ -0,0 +1,27 @@ +require("global"); +require("ability"); +require("battleutils") + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + + --27163: Enhanced Rampart:Expands rampart to affect party members + if caster.HasTrait(27163) then + ability.aoeType = TargetFindAOEType.Circle; + end + + return 0; +end; + +--http://forum.square-enix.com/ffxiv/threads/47393-Tachi-s-Guide-to-Paladin-%28post-1.22b%29 +--180 enmity per member that has enmity on the current enemy +--Need to figure out enmity system +function onSkillFinish(caster, target, skill, action, actionContainer) + action.enmity = 180; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/sacred_prism.lua b/data/scripts/commands/ability/sacred_prism.lua new file mode 100644 index 00000000..9affbf67 --- /dev/null +++ b/data/scripts/commands/ability/sacred_prism.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27360: Swift Sacred Prism: Reduces recast by 30 seconds + if caster.HasTrait(27360) then + ability.recastTimeMs = ability.recastTimeMs - 30000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..e6c7ddaf --- /dev/null +++ b/data/scripts/commands/ability/second_wind.lua @@ -0,0 +1,39 @@ +require("global"); +require("modifiers"); +--require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + + return 0; +end; + +--http://forum.square-enix.com/ffxiv/threads/51208-2nd-wind-modifier +--The primary modifier for SW is class level. + +--There are three other factors that contribute to SW: +-- PGL's SW trait, which increases potency by 25%. +-- A bonus from INT (2INT=1HP) +-- An additional random integer (580 at level 50. +/- 3%) +function onSkillFinish(caster, target, skill, action, actionContainer) + --Base formula isn't quit known yet + local amount = 100; + --Heals can vary by up to ~3.5% in either direction + amount = math.Clamp(amount * (0.965 + (math.rand() * 7.0)), 0, 9999); + + --PGL gets an INT bonus for Second Wind + if caster.GetClass() == 2 then + amount = amount + caster.GetMod(modifiersGlobal.Intelligence) / 2; + end; + + --27120: Enhanced Second Wind + if caster.HasTrait(27120) then + amount = amount * 1.25; + end; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/sentinel.lua b/data/scripts/commands/ability/sentinel.lua new file mode 100644 index 00000000..4a4b1a7e --- /dev/null +++ b/data/scripts/commands/ability/sentinel.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27160: Enhanced Sentinel + if caster.HasTrait(27160) then + ability.statusTier = 2; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/shroud_of_saints.lua b/data/scripts/commands/ability/shroud_of_saints.lua new file mode 100644 index 00000000..7e5af422 --- /dev/null +++ b/data/scripts/commands/ability/shroud_of_saints.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27361: Swift Shroud of Saints + if caster.HasTrait(27361) then + ability.recastTimeMs = ability.recastTimeMs - 60000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/taunt.lua b/data/scripts/commands/ability/taunt.lua new file mode 100644 index 00000000..a73b5378 --- /dev/null +++ b/data/scripts/commands/ability/taunt.lua @@ -0,0 +1,19 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + --27122: Swift Taunt: Reduces recast time by 15 seconds. + if caster.HasTrait(27121) then + ability.recastTimeMs = ability.recastTimeMs - 15000; + end + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/tempered_will.lua b/data/scripts/commands/ability/tempered_will.lua new file mode 100644 index 00000000..f024acaf --- /dev/null +++ b/data/scripts/commands/ability/tempered_will.lua @@ -0,0 +1,20 @@ +require("global"); +require("ability"); + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --Is this before or after status is gained? + --Will probably need to switch to a flag for this because it might include more than just these 3 effects. + actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(228011)); + actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(228013)); + actionContainer.AddAction(caster.statusEffects.RemoveStatusEffectForBattleAction(228021)); + + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/ability/vengeance.lua b/data/scripts/commands/ability/vengeance.lua new file mode 100644 index 00000000..69136e37 --- /dev/null +++ b/data/scripts/commands/ability/vengeance.lua @@ -0,0 +1,22 @@ +require("global"); +require("ability"); +require("battleutils") + +function onAbilityPrepare(caster, target, ability) + return 0; +end; + +function onAbilityStart(caster, target, ability) + + --8032703: Fighter's Cuirass: Enhances Vengeance + if caster.GetEquipment().GetItemAtSlot(13).itemId == 8032703 then + skill.statusTier = 2; + end + + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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/ba.lua b/data/scripts/commands/gm/ba.lua new file mode 100644 index 00000000..43354441 --- /dev/null +++ b/data/scripts/commands/gm/ba.lua @@ -0,0 +1,29 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "sssss", + description = +[[ +Adds experience to player or . +!giveexp | +!giveexp | +]], +} + +function onTrigger(player, argc, commandId, animationId, textId, effectId, amount) + local sender = "[battleaction] "; + + if player then + cid = tonumber(commandId) or 0; + aid = tonumber(animationId) or 0; + tid = tonumber(textId) or 0; + print(effectId) + eid = tonumber(effectId) or 0; + amt = tonumber(amount) or 0; + + player:DoBattleActionAnimation(cid, aid, tid, eid, amt); + 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/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..d2c0e7fe 100644 --- a/data/scripts/commands/gm/graphic.lua +++ b/data/scripts/commands/gm/graphic.lua @@ -21,9 +21,13 @@ 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; 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/setsize.lua b/data/scripts/commands/gm/setsize.lua new file mode 100644 index 00000000..87a62425 --- /dev/null +++ b/data/scripts/commands/gm/setsize.lua @@ -0,0 +1,24 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "s", + description = +[[ +Changes appearance for equipment with given parameters. +!graphic +]], +} + +function onTrigger(player, argc, size) + local messageID = MESSAGE_TYPE_SYSTEM_ERROR; + local sender = "[setappearance] "; + + s = tonumber(size) or 0; + + if player and player.target then + player.target.appearanceIds[0] = s; + player.target.zone.BroadcastPacketAroundActor(player.target, player.target.CreateAppearancePacket()); + end; + +end; \ No newline at end of file diff --git a/data/scripts/commands/gm/setstate.lua b/data/scripts/commands/gm/setstate.lua new file mode 100644 index 00000000..9c1d8e70 --- /dev/null +++ b/data/scripts/commands/gm/setstate.lua @@ -0,0 +1,27 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "s", + description = +[[ +Changes appearance for equipment with given parameters. +!graphic +]], +} + +function onTrigger(player, argc, state) + local messageID = MESSAGE_TYPE_SYSTEM_ERROR; + local sender = "[setstate] "; + + max = tonumber(state) or 0; + + for s = 0, max do + if player and player.target then + player.target:ChangeState(s); + wait(0.8); + player:SendMessage(0x20, "", "state: "..s); + end; + end + +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..42018e7c 100644 --- a/data/scripts/commands/gm/yolo.lua +++ b/data/scripts/commands/gm/yolo.lua @@ -1,4 +1,5 @@ require("global"); +require("modifiers"); properties = { permissions = 0, @@ -128,7 +129,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 +148,26 @@ 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; + local blocks = tonumber(blockCount) 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, blocks 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.SetMaxHP(10000); + actor.SetHP(10000); + actor.SetMod(modifiersGlobal.HasShield, 1); + end + 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..ca0f28b4 --- /dev/null +++ b/data/scripts/commands/gm/zonecount.lua @@ -0,0 +1,19 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "", + description = +[[ +Get the amount of actors in this zone. +!zonecount +]] + +} + +function onTrigger(player, argc) + + 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..3deb8462 --- /dev/null +++ b/data/scripts/commands/magic/aero.lua @@ -0,0 +1,22 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + action.statusMagnitude = 15; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..d8e01c53 --- /dev/null +++ b/data/scripts/commands/magic/aerora.lua @@ -0,0 +1,33 @@ +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 onSkillFinish(caster, target, skill, action, actionContainer) + --Dispels an effect on each target. + local effects = target.statusEffects.GetStatusEffectsByFlag2(16); --lose on dispel + if effects != nil then + target.statusEffects.RemoveStatusEffect(effects[0]); + end; + + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..8f1a2003 --- /dev/null +++ b/data/scripts/commands/magic/ballad_of_magi.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; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..6fbe1c9b 100644 --- a/data/scripts/commands/magic/blizzara.lua +++ b/data/scripts/commands/magic/blizzara.lua @@ -9,16 +9,13 @@ 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); +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; - return damage; + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..96482678 100644 --- a/data/scripts/commands/magic/blizzard.lua +++ b/data/scripts/commands/magic/blizzard.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); +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; - return damage; + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..1c94a9f8 100644 --- a/data/scripts/commands/magic/burst.lua +++ b/data/scripts/commands/magic/burst.lua @@ -9,16 +9,18 @@ function onMagicStart(caster, target, spell) return 0; end; -function onMagicFinish(caster, target, spell, action) - local damage = math.random(1000, 2500); +--Increased damage with lesser current hp +function onCombo(caster, target, spell) + +end; - -- 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; +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..6713dadc --- /dev/null +++ b/data/scripts/commands/magic/cura.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 onSkillFinish(caster, target, skill, action, actionContainer) + --http://forum.square-enix.com/ffxiv/threads/41900-White-Mage-A-Guide + --2.5 HP per Healing Magic Potency + --0.5 HP per MND + --this is WITH WHM AF chest, don't know formula without AF. AF seems to increase healing by 7-10%? + action.amount = 2.5 * caster.GetMod(modifiersGlobal.MagicHeal) + 0.5 * (caster.GetMod(modifiersGlobal.Mind)); + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..dadd1241 --- /dev/null +++ b/data/scripts/commands/magic/curaga.lua @@ -0,0 +1,19 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +--Idea: add way to sort list of targets by hp here? +function onMagicStart(caster, target, spell) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..cafa30a2 --- /dev/null +++ b/data/scripts/commands/magic/cure.lua @@ -0,0 +1,37 @@ +require("global"); +require("magic"); +require("modifiers"); + +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 +function onSkillFinish(caster, target, skill, action, actionContainer) + + --Non-CNJ + --1.10 per HMP + --0 per MND + local hpPerHMP = 1.10; + local hpPerMND = 0; + + --CNJ + --With AF: + --1.25 HP per Healing Magic Potency + --0.25 HP per MND + --This is WITH AF chest. Without is lower. AF is ~7-10% increase apparently + --I'm guessing without AF hpPerHMP will be 1.1? + if (caster.GetClass() == 23) then + hpPerHMP = 1.25; + hpPerMND = 0.25; + end + + action.amount = hpPerHMP * caster.GetMod(modifiersGlobal.MagicHeal) + hpPerMND * (caster.GetMod(modifiersGlobal.Mind)); + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..6fbe1c9b --- /dev/null +++ b/data/scripts/commands/magic/default.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 onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..c7dc3f48 100644 --- a/data/scripts/commands/magic/fira.lua +++ b/data/scripts/commands/magic/fira.lua @@ -9,16 +9,15 @@ 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 onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..e145c551 100644 --- a/data/scripts/commands/magic/firaga.lua +++ b/data/scripts/commands/magic/firaga.lua @@ -9,16 +9,15 @@ 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 onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..cb88bda7 100644 --- a/data/scripts/commands/magic/fire.lua +++ b/data/scripts/commands/magic/fire.lua @@ -9,19 +9,10 @@ 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); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..6fbe1c9b 100644 --- a/data/scripts/commands/magic/flare.lua +++ b/data/scripts/commands/magic/flare.lua @@ -9,16 +9,13 @@ 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); - - return damage; +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..5d7349bb 100644 --- a/data/scripts/commands/magic/freeze.lua +++ b/data/scripts/commands/magic/freeze.lua @@ -9,16 +9,14 @@ 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); - - return damage; +function onSkillFinish(caster, target, skill, action, actionContainer) + --Freeze generates 0 enmity and removes a flat 720 enmity + spell.enmityModifier = 0; + target.hateContainer.UpdateHate(caster, -720); + + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..6fbe1c9b 100644 --- a/data/scripts/commands/magic/holy.lua +++ b/data/scripts/commands/magic/holy.lua @@ -9,16 +9,13 @@ 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); +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; - return damage; + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..77ada4bc --- /dev/null +++ b/data/scripts/commands/magic/holy_succor.lua @@ -0,0 +1,29 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + action.amount = skill.basePotency; + + --8071401: Gallant Gauntlets: Enhances Holy Succor + if caster.GetEquipment().GetItemAtSlot(13).itemId == 8071401 then + action.amount = action.amount * 1.10; + end + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --When cast on another player you also heal 50% of the amount restored. + if caster != target then + caster.AddHP(action.amount / 2) + --33012: You recover [amount] HP. + actionContainer.AddHPAction(caster.actorId, 33012, (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..8f1a2003 --- /dev/null +++ b/data/scripts/commands/magic/minuet_of_rigor.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; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..8f1a2003 --- /dev/null +++ b/data/scripts/commands/magic/paeon_of_war.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; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..6e24db7b --- /dev/null +++ b/data/scripts/commands/magic/protect.lua @@ -0,0 +1,24 @@ +require("global"); +require("magic"); +require("modifiers"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --Actual amount of def/mdef will be calculated in OnGain + skill.statusMagnitude = caster.GetMod(modifiersGlobal.MagicEnhancePotency); + + --27365: Enhanced Protect: Increases magic defense gained from Protect. + if caster.HasTrait(27365) then + skill.statusTier = 2; + end + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/raise.lua b/data/scripts/commands/magic/raise.lua new file mode 100644 index 00000000..ef5b8ad1 --- /dev/null +++ b/data/scripts/commands/magic/raise.lua @@ -0,0 +1,18 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + --27363: Enhanced Raise: No longer inflicts weakness. + if caster.HasTrait(27363) then + ability.statusTier = 2; + end + return 0; +end; +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/magic/regen.lua b/data/scripts/commands/magic/regen.lua new file mode 100644 index 00000000..5f93a333 --- /dev/null +++ b/data/scripts/commands/magic/regen.lua @@ -0,0 +1,34 @@ +require("global"); +require("magic"); +require("modifiers"); + +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 +function onSkillFinish(caster, target, skill, action, actionContainer) + --For every 1-2-2-1-2 (repeating 3x) then 1-2-1-2-2 (repeating 3x) Enhancing magic potency you have, the amount your Regen cures per tic increases by 1. + --.625 * Enhancing + local slope = 0.625; + local intercept = -110; + + if caster.GetEquipment().GetItemAtSlot(14).itemId == 8051406 then + --I don't know if the numbers in that thread are completely correct because the AF Regen table has 3 1555s in a row. + --If we assume that AF boots multiply both static parts of the regenTick equation by 1.25, we get a decently close match to actual numbers + slope = slope * 1.25; + intercept = intercept * 1.25; + end + + local regenTick = (slope * caster.GetMod(modifiersGlobal.MagicEnhancePotency)) + intercept) + 1; + + + spell.statusMagnitude = regenTick; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..8f1a2003 --- /dev/null +++ b/data/scripts/commands/magic/repose.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; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..8f1a2003 --- /dev/null +++ b/data/scripts/commands/magic/sanguine_rite.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; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..8f1a2003 --- /dev/null +++ b/data/scripts/commands/magic/sleep.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; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..728384d3 --- /dev/null +++ b/data/scripts/commands/magic/stone.lua @@ -0,0 +1,22 @@ +require("global"); +require("magic"); + +function onMagicPrepare(caster, target, spell) + return 0; +end; + +function onMagicStart(caster, target, spell) + return 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + action.statusMagnitude = 50; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..bc535ed2 --- /dev/null +++ b/data/scripts/commands/magic/stonera.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.basePotency = spell.basePotency * 1.5; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..c49b190a --- /dev/null +++ b/data/scripts/commands/magic/stoneskin.lua @@ -0,0 +1,26 @@ +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 +function onSkillFinish(caster, target, skill, action, actionContainer) + + local hpPerPoint = 1.34;--? 1.33? + + --27364: Enhanced Stoneskin: Increases efficacy of Stoneskin + if caster.HasTrait(27364) then + hpPerPoint = 1.96; + end + + spell.statusMagnitude = hpPerPoint * caster.GetMod(modifiersGlobal.MagicEnhancePotency); + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..61b2834e 100644 --- a/data/scripts/commands/magic/thundaga.lua +++ b/data/scripts/commands/magic/thundaga.lua @@ -8,14 +8,18 @@ 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 onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..a828567a 100644 --- a/data/scripts/commands/magic/thundara.lua +++ b/data/scripts/commands/magic/thundara.lua @@ -8,14 +8,20 @@ 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 onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..96482678 100644 --- a/data/scripts/commands/magic/thunder.lua +++ b/data/scripts/commands/magic/thunder.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); +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate damage + action.amount = skill.basePotency; - return damage; + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..0bf98213 --- /dev/null +++ b/data/scripts/commands/weaponskill/aura_pulse.lua @@ -0,0 +1,26 @@ +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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..645d92d0 --- /dev/null +++ b/data/scripts/commands/weaponskill/bloodletter.lua @@ -0,0 +1,26 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +--Changes status to Bloodletter from Bloodletter2. Changes icon of dot and adds additional damage at the end. +function onCombo(caster, target, skill) + skill.statusId = 223127; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..a304d23e --- /dev/null +++ b/data/scripts/commands/weaponskill/brutal_swing.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 damage +function onPositional(caster, target, skill) + skill.basePotency = skill.basePotency * 1.5; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..ed58cdab 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,18 @@ 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; +--Increased crit hit rating +function onCombo(caster, target, skill) + skill.bonusCritRate = 100; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..2271f588 --- /dev/null +++ b/data/scripts/commands/weaponskill/concussive_blow.lua @@ -0,0 +1,31 @@ +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.75; +end; + +function onCombo(caster, target, skill) + skill.basePotency = skill.basePotency * 1.5; +end; + + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..a9da9873 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; @@ -8,19 +9,13 @@ 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, skill.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..733e9e4d --- /dev/null +++ b/data/scripts/commands/weaponskill/demolish.lua @@ -0,0 +1,30 @@ +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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..b58a2b8e --- /dev/null +++ b/data/scripts/commands/weaponskill/disembowel.lua @@ -0,0 +1,26 @@ +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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..e40f0057 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,15 @@ 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; +--Increased accuracy +function onCombo(caster, target, skill) + skill.accuracyModifier = 50; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..115ab485 --- /dev/null +++ b/data/scripts/commands/weaponskill/dragon_kick.lua @@ -0,0 +1,26 @@ +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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..996efacb 100644 --- a/data/scripts/commands/weaponskill/fast_blade.lua +++ b/data/scripts/commands/weaponskill/fast_blade.lua @@ -1,27 +1,22 @@ 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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..620db241 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; @@ -8,19 +9,10 @@ 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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..0cbd4acf 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,17 @@ 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 onCombo(caster, target, skill) + --http://forum.square-enix.com/ffxiv/threads/50479-Gladiator-Paladin-STR-MND-Stat-Caps/page7 + --4.5 is a bonus on top of the 1x of normal flat blade + --This is modified by MND and dlvl and caps at 4.5, dont know the values used though + skill.enmityModifier = 5.5; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..31a84a97 --- /dev/null +++ b/data/scripts/commands/weaponskill/fracture.lua @@ -0,0 +1,21 @@ +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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..7e6e7327 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,12 @@ 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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + caster.AddTP(1000); 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..1045af26 --- /dev/null +++ b/data/scripts/commands/weaponskill/gloom_arrow.lua @@ -0,0 +1,26 @@ +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.75; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..d47129ac --- /dev/null +++ b/data/scripts/commands/weaponskill/godsbane.lua @@ -0,0 +1,45 @@ +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 + local berserk = caster.statusEffects.GetStatusEffectById(223160); + + --if it isn't nil, remove the AP and Defense mods and reset extra to 0, increase potency + if berserk != nil then + local apPerHit = 20; + local defPerHit = 20; + + if berserk.GetTier() == 2 then + apPerHit = 24; + end + + attacker.SubtractMod(modifiersGlobal.Attack, apPerHit * berserk.GetExtra()); + attacker.Add(modifiersGlobal.Defense, defPerHit * berserk.GetExtra()); + + berserk.SetExtra(0); + + --This is about 50% crit. Don't know if that's what it gave on retail but seems kind of reasonable + skill.critRateBonus = 300; + end; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..a63b71ba --- /dev/null +++ b/data/scripts/commands/weaponskill/goring_blade.lua @@ -0,0 +1,33 @@ +require("global"); +require("weaponskill"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + skill.statusMagnitude = 25;--could probalby have a status magnitude value + 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 +--Bleed damage seems like it's 25 with comboed being 38 (25 * 1.5 rounded up) +function onCombo(caster, target, skill) + skill.statusMagnitude = 38; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..a9da9873 --- /dev/null +++ b/data/scripts/commands/weaponskill/haymaker.lua @@ -0,0 +1,21 @@ +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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..620db241 --- /dev/null +++ b/data/scripts/commands/weaponskill/heavy_shot.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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..ce72c59b --- /dev/null +++ b/data/scripts/commands/weaponskill/heavy_swing.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 = 50; +end; + + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..cbdea342 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,18 @@ 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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..d588268b --- /dev/null +++ b/data/scripts/commands/weaponskill/howling_fist.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; + +--Increased accuracy +function onPositional(caster, target, skill) + skill.accuracyModifier = 50; +end; + +--Increased damage +function onCombo(caster, target, skill) + skill.basePotency = skill.basePotency * 1.5; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..1459ebf6 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,20 @@ 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; +--Increased damage +function onPositional(caster, target, skill) + skill.basePotency = skill.basePotency * 1.25 +end; + +--Increased crit hit rating +function onCombo(caster, target, skill) + skill.bonusCritRate = 200; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..e94da64b --- /dev/null +++ b/data/scripts/commands/weaponskill/leaden_arrow.lua @@ -0,0 +1,26 @@ +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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..a8428109 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,18 @@ 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; +--Chance to inflict stun +function onCombo(caster, target, skill) + skill.statusChance = 0.50; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); 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..861e4315 --- /dev/null +++ b/data/scripts/commands/weaponskill/maim.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; + +function onCombo(caster, target, skill) + skill.accuracyModifier = 50; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + skill.Potency = 100; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/overpower.lua b/data/scripts/commands/weaponskill/overpower.lua new file mode 100644 index 00000000..620db241 --- /dev/null +++ b/data/scripts/commands/weaponskill/overpower.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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..e8a48aaf --- /dev/null +++ b/data/scripts/commands/weaponskill/path_of_the_storm.lua @@ -0,0 +1,26 @@ +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; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/phalanx.lua b/data/scripts/commands/weaponskill/phalanx.lua new file mode 100644 index 00000000..620db241 --- /dev/null +++ b/data/scripts/commands/weaponskill/phalanx.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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..620db241 --- /dev/null +++ b/data/scripts/commands/weaponskill/piercing_arrow.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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..31089960 --- /dev/null +++ b/data/scripts/commands/weaponskill/pounce.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; + +function onPositional(caster, target, skill) + skill.statusChance = 0.50; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..996efacb 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,14 @@ 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 onPositional(caster, target, skill) + skill.basePotency = skill.basePotency * 1.25; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..8ff7da1c --- /dev/null +++ b/data/scripts/commands/weaponskill/quick_nock.lua @@ -0,0 +1,26 @@ +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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..d459bdbe --- /dev/null +++ b/data/scripts/commands/weaponskill/rage_of_halone.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; + +--Accuracy increase +function onCombo(caster, target, skill) + --Rage of Halone normally has a -40% hit rate modifier. + --Does the combo negate that, or does it make it even more accurate than if it didnt have the modifier? + skill.accuracyModifier = 0; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..53e84097 --- /dev/null +++ b/data/scripts/commands/weaponskill/rain_of_death.lua @@ -0,0 +1,26 @@ +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.75 +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..ffa4994a --- /dev/null +++ b/data/scripts/commands/weaponskill/ring_of_talons.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 critical hit rate +function onCombo(caster, target, skill) + skill.bonusCritRate = 100; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..d4cd7c07 --- /dev/null +++ b/data/scripts/commands/weaponskill/riot_blade.lua @@ -0,0 +1,27 @@ +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; + skill.statusMagnitude = 100; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..75985bc7 --- /dev/null +++ b/data/scripts/commands/weaponskill/savage_blade.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; + +function onCombo(caster, target, skill) + skill.basePotency = skill.basePotency * 1.25; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..829a81db --- /dev/null +++ b/data/scripts/commands/weaponskill/shadowbind.lua @@ -0,0 +1,26 @@ +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 = 30; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..a9da9873 --- /dev/null +++ b/data/scripts/commands/weaponskill/shield_bash.lua @@ -0,0 +1,21 @@ +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, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +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..2fef254a --- /dev/null +++ b/data/scripts/commands/weaponskill/shoulder_tackle.lua @@ -0,0 +1,26 @@ +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, actionContainer) + --chance to influct stun only when target has no enmity towards you + if not (target.hateContainer.HasHateForTarget(caster)) then + skill.statusChance = 0.50; + end + + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/simian_thrash.lua b/data/scripts/commands/weaponskill/simian_thrash.lua index 0b85c48f..545468c0 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,15 @@ function onSkillStart(caster, target, skill) return 0; 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; +--Increased damage +function onCombo(caster, target, skill) + skill.basePotency = skill.basePotency * 1.5; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..ac7056dd 100644 --- a/data/scripts/commands/weaponskill/skull_sunder.lua +++ b/data/scripts/commands/weaponskill/skull_sunder.lua @@ -9,19 +9,16 @@ 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) + --https://www.bluegartr.com/threads/107403-Stats-and-how-they-work/page17 + skill.enmityModifier = 2.75; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..193a7a70 --- /dev/null +++ b/data/scripts/commands/weaponskill/spirits_within.lua @@ -0,0 +1,29 @@ +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 = 2.5; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --Increased damage with higher hp + --random guess + local potencyModifier = caster:GetHPP() + 25; + + skill.basePotency = skill.basePotency * potencyModifier; + + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..4f1314cd --- /dev/null +++ b/data/scripts/commands/weaponskill/steel_cyclone.lua @@ -0,0 +1,26 @@ +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.bonusCritRate = 200; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/sucker_punch.lua b/data/scripts/commands/weaponskill/sucker_punch.lua new file mode 100644 index 00000000..5f188546 --- /dev/null +++ b/data/scripts/commands/weaponskill/sucker_punch.lua @@ -0,0 +1,44 @@ +require("global"); +require("weaponskill"); +require("battleutils"); +require("hiteffect"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +-- +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, action); + + --additional effect + --Restores MP + --Comboed formula seems to be (0.40 * damage) + 180 + --Uncomboed formula seems to be 0.30 * damage + --These may be wrong. It seems like max mp might influence the slope + + --1.21: Equation used to calculate amount of MP adjusted. + --fug + --This might mean max MP isn't involved and the difference was between patches. need to recheck videos + if action.GetHitType() > HitType.Evade and (action.param == HitDirection.Right or action.param == HitDirection.Left) then + local mpToReturn = 0; + + if skill.isCombo then + mpToReturn = (0.40 * action.amount) + 180; + else + mpToReturn = (0.30 * action.amount); + end + + caster.AddMP(mpToReturn); + --30452: You recover x MP. + actionContainer.AddMPAction(caster.actorId, 30452, mpToReturn); + end +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..ec3d888f 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,15 @@ 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, skill.effectAnimation, 15636); - - if target.hateContainer then - target.hateContainer.UpdateHate(caster, damage); - end; - return damage; +--Increased accuracy from in front +function onPositional(caster, target, skill) + skill.accuracyModifier = 50; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); 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..7669640d 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,18 @@ 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; +--Increased crit rating from behind +function onPositional(caster, target, skill) + skill.bonusCritRate = 200; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); + + --Try to apply status effect + action.TryStatus(caster, target, skill, actionContainer, true); end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/war_drum.lua b/data/scripts/commands/weaponskill/war_drum.lua new file mode 100644 index 00000000..e71feea1 --- /dev/null +++ b/data/scripts/commands/weaponskill/war_drum.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; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --might be wrong + action.enmity = action.enmity + 400; + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..26b7a7e1 --- /dev/null +++ b/data/scripts/commands/weaponskill/whirlwind.lua @@ -0,0 +1,40 @@ +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, remove the AP and Defense mods and reset extra to 0, increase potency + if berserk != nil then + local apPerHit = 20; + local defPerHit = 20; + + if berserk.GetTier() == 2 then + apPerHit = 24; + end + + attacker.SubtractMod(modifiersGlobal.Attack, apPerHit * berserk.GetExtra()); + attacker.Add(modifiersGlobal.Defense, defPerHit * berserk.GetExtra()); + + berserk.SetExtra(0); + skill.basePotency = skill.basePotency * 1.5; + end; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/wide_volley.lua b/data/scripts/commands/weaponskill/wide_volley.lua new file mode 100644 index 00000000..a16f8cd1 --- /dev/null +++ b/data/scripts/commands/weaponskill/wide_volley.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 Accuracy +function onCombo(caster, target, skill) + skill.accuracyModifier = 40; +end; + +function onSkillFinish(caster, target, skill, action, actionContainer) + --calculate ws damage + action.amount = skill.basePotency; + + --DoAction handles rates, buffs, dealing damage + action.DoAction(caster, target, skill, actionContainer); +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..934e48be 100644 --- a/data/scripts/directors/Quest/QuestDirectorMan0g001.lua +++ b/data/scripts/directors/Quest/QuestDirectorMan0g001.lua @@ -10,13 +10,13 @@ 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 function onEventStarted(player, actor, triggerName) man0g0Quest = player:GetQuest("Man0g0"); + player:SetMod(modifiersGlobal.MinimumHpLock, 1); player:SendMessage(0x20, "", "Starting"); startTutorialMode(player); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrBtl001", nil, nil, nil); @@ -30,19 +30,24 @@ 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 openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_TP); waitForSignal("tpOver1000"); + player:SetMod(modifiersGlobal.MinimumTpLock, 1000); closeTutorialWidget(player); openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_WEAPONSKILLS); waitForSignal("weaponskillUsed"); + player:SetMod(modifiersGlobal.MinimumTpLock, 0); 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 +57,41 @@ 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:SetMod(modifiersGlobal.MinimumHpLock, 0); + 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 +107,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/aegis_boon.lua b/data/scripts/effects/aegis_boon.lua new file mode 100644 index 00000000..a710e113 --- /dev/null +++ b/data/scripts/effects/aegis_boon.lua @@ -0,0 +1,21 @@ +require("global") +require("modifiers") +require("hiteffect") +require("utils") + +--Forces a full block (0 damage taken) +function onPreAction(caster, target, effect, skill, action, actionContainer) + --If action hit from the rear and is a weaponskill ation + action.blockRate = 100.0; +end; + +--Heals for the amount of HP blocked, up to a certain point. I don't know what determines the cap but it seems to be 703 at level 50. Unsure if it scales down based on level, dlvl, or if that's an arbitrary cap added. +function onBlock(effect, attacker, defender, action, actionContainer) + --Amount blocked + local absorbAmount = math.Clamp(action.amountMitigated, 0, 703); + + --33008: You recover x HP from Aegis Boon + defender.AddHP(absorbAmount); + actionContainer.AddHPAction(defender.actorId, 33008, absorbAmount); + actionContainer.AddAction(defender.statusEffects.RemoveStatusEffectForBattleAction(effect)); +end; \ No newline at end of file diff --git a/data/scripts/effects/aero.lua b/data/scripts/effects/aero.lua new file mode 100644 index 00000000..d6e012ee --- /dev/null +++ b/data/scripts/effects/aero.lua @@ -0,0 +1,8 @@ +--Doesn't do flat damage. 20 on Lv 50 Truffle Hog, 11 on Coincounter, 7 on nael hard, 19 on 52 fachan +function onGain(target, effect) + owner.AddMod(modifiersGlobal.RegenDown, effect.GetMagnitude()); +end; + +function onLose(target, effect) + owner.AddMod(modifiersGlobal.RegenDown, effect.GetMagnitude()); +end; \ No newline at end of file diff --git a/data/scripts/effects/antagonize.lua b/data/scripts/effects/antagonize.lua new file mode 100644 index 00000000..a88cedf7 --- /dev/null +++ b/data/scripts/effects/antagonize.lua @@ -0,0 +1,13 @@ +require("modifiers") + +--Antagonize's enmity bonus is x1.5 (works for both abilities and damage dealt). x1.65 when AF is worn. +--Is does this mean it's 1.5* or 2.5*? +function onCommandStart(effect, owner, skill, actionContainer) + local enmityModifier = 1.5 + + if effect.GetTier() == 2 then + enmityModifier = 1.65; + end + + skill.enmityModifier = skill.enmityModifier + enmityModifier; +end \ No newline at end of file diff --git a/data/scripts/effects/barrage.lua b/data/scripts/effects/barrage.lua new file mode 100644 index 00000000..3273cc49 --- /dev/null +++ b/data/scripts/effects/barrage.lua @@ -0,0 +1,19 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +--Untraited reduces cooldown by 50% +--Traited reduces cooldown by 100% +function onCommandStart(effect, owner, skill, actionContainer) + --27259: Light Shot + if skill.id == 27259 then + skill.numHits = effect.GetMagnitude(); + end +end; + +function onCommandFinish(effect, owner, skill, actionContainer) + --27259: Light Shot + if skill.id == 27259 then + actionContainer.AddAction(owner.statusEffects.RemoveStatusEffectForBattleAction(effect)); + end +end; \ No newline at end of file diff --git a/data/scripts/effects/berserk2.lua b/data/scripts/effects/berserk2.lua new file mode 100644 index 00000000..fc6b9193 --- /dev/null +++ b/data/scripts/effects/berserk2.lua @@ -0,0 +1,59 @@ +require("modifiers"); +require("battleutils"); + +function onGain(owner, effect) + owner.statusEffects.RemoveStatusEffect(223208); +end + +--Increases attack power and reduces defense with each successful attack +--Does this include weaponskills? +--Is this on every hit or every succesfull skill useage? +function onHit(effect, attacker, defender, action, actionContainer) + --Trait increases effect by 20%. Does this include the reduced defense, + --does this increase the cap or the rate at which you get AP or both? + + if (effect.GetExtra() < 10) then + --This will count how many hits there have been + effect.SetExtra(effect.GetExtra() + 1); + + --If you update these make sure to update them in Whirlwind as well + local apPerHit = 20; + local defPerHit = 20; + + if effect.GetTier() == 2 then + apPerHit = 24; + end + + --Just going to say every hit adds 20 AP up to 200 + --Same for defense + --Traited will be 24 up to 240 + --assuming defense is static + attacker.AddMod(modifiersGlobal.Attack, apPerHit); + attacker.SubtractMod(modifiersGlobal.Defense, defPerHit); + end +end; + +function onDamageTaken(effect, attacker, defender, action, actionContainer) + local apPerHit = 20; + local defPerHit = 20; + + if effect.GetTier() == 2 then + apPerHit = 24; + end + + defender.SubtractMod(modifiersGlobal.Attack, effect.GetExtra() * apPerHit); + defender.SubtractMod(modifiersGlobal.Defense, effect.GetExtra() * defPerHit); + effect.SetExtra(0); +end + +function onLose(owner, effect) + local apPerHit = 20; + local defPerHit = 20; + + if effect.GetTier() == 2 then + apPerHit = 24; + end + + owner.SubtractMod(modifiersGlobal.Attack, effect.GetExtra() * apPerHit); + owner.SubtractMod(modifiersGlobal.Defense, effect.GetExtra() * defPerHit); +end \ No newline at end of file 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/blindside.lua b/data/scripts/effects/blindside.lua new file mode 100644 index 00000000..a88a63fe --- /dev/null +++ b/data/scripts/effects/blindside.lua @@ -0,0 +1,15 @@ +require("modifiers") +require("battleutils") + +--Forces crit of a single WS action from rear. +function onPreAction(caster, target, effect, skill, action, actionContainer) + --If action hit from the rear and is a weaponskill ation + if (action.param == HitDirection.Rear and action.commandType == CommandType.WeaponSkill) then + --Set action's crit rate to 100% + action.critRate = 100.0; + end + + --Remove status and add message + actionsList.AddAction(target.statusEffects.RemoveForBattleAction(effect)); +end; + diff --git a/data/scripts/effects/blissful_mind.lua b/data/scripts/effects/blissful_mind.lua new file mode 100644 index 00000000..1c12cfff --- /dev/null +++ b/data/scripts/effects/blissful_mind.lua @@ -0,0 +1,21 @@ +--The amount of time it takes to fully charge varies depending on how much MP it has to restore, meaning its not just a percent every tick +--Based on a few videos it seems like it heals for 0.5% of max MP every second, traited. This is an early guess but it seems correct +--Untraited is less clear. It could be 0.25%, 0.30%, or 0.40%. Guessing it's 0.30 + +function onTick(owner, effect) + local percentPerSecond = 0.0030; + + if effect.GetTier() == 2 then + percentPerSecond = 0.005; + end + + print(effect.GetExtra()); + + local amount = percentPerSecond * owner.GetMaxMP() + 0.25; + effect.SetExtra(effect.GetExtra() + amount); + if effect.GetExtra() >= effect.GetMagnitude() then + --223242: Fully Blissful Mind + owner.statusEffects.ReplaceEffect(effect, 223242, 1, effect.GetMagnitude(), 0xffffffff); + --owner.statusEffects.ReplaceEffect(effect, true); + end +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/blood_for_blood.lua b/data/scripts/effects/blood_for_blood.lua new file mode 100644 index 00000000..e9b8f0fb --- /dev/null +++ b/data/scripts/effects/blood_for_blood.lua @@ -0,0 +1,20 @@ +require("modifiers") +require("hiteffect") + +--Takes 10% of hp rounded down +--Random guess, but increases damage by 10% (12.5% traited)? +function onPreAction(caster, target, effect, skill, action, actionContainer) + local hpToRemove = math.floor(caster.GetHP() * 0.10); + local modifier = 1.10; + + if (effect.GetTier() == 2) then + modifier = 1.25; + end + + action.amount = action.amount * modifier; + caster.DelHP(hpToRemove); + + --Remove status and add message + actionContainer.AddAction(target.statusEffects.RemoveForBattleAction(effect)); +end; + diff --git a/data/scripts/effects/bloodbath.lua b/data/scripts/effects/bloodbath.lua new file mode 100644 index 00000000..2e6ea6a9 --- /dev/null +++ b/data/scripts/effects/bloodbath.lua @@ -0,0 +1,25 @@ +require("modifiers"); +require("battleutils"); + +--Absorb HP on next WS or ability +function onHit(effect, attacker, defender, action, actionContainer) + + --1.21: Absorb HP amount no longer affected by player VIT rating. + --Bloodbath seems based on both defener and attacker's stats, even after 1.21. + --Miser's Mistriss seems to resist the effect, whereas nael gets absorbed more than 100% + --Garuda resists a small amount + --Unclear what it's based on. + --Possibly magic resist? Slashing resist? + + --For now using 1.0 as baseline since that seems to be the average + if action.commandType == CommandType.Weaponskill or action.commandType == CommandType.Ability then + local absorbModifier = 1.0 + local absorbAmount = action.amount * absorbModifier; + + attacker.AddHP(absorbAmount); + --30332: You absorb hp from target + actionContainer.AddHPAction(defender.actorId, 30332, absorbAmount) + --Bloodbath is lost after absorbing hp + actionContainer.AddAction(defender.statusEffects.RemoveStatusEffectForBattleAction(effect)); + end +end; \ No newline at end of file diff --git a/data/scripts/effects/bloodletter.lua b/data/scripts/effects/bloodletter.lua new file mode 100644 index 00000000..487041de --- /dev/null +++ b/data/scripts/effects/bloodletter.lua @@ -0,0 +1,18 @@ +--This is the comboed version of bloodletter. +--All videos I can find have it dealing 15 damage. +--Damage type is projectile. +--DoT damage is combined and all hits on a single tick. ie a blodletter doing 15 damage and aero doing 11 will combine and tick for 26. + +--Bloodletter is apparently impacted by PIE +--http://forum.square-enix.com/ffxiv/threads/35795-STR-DEX-PIE-ATK-Testing/page2 +--Chance to land is also impacted by PIE +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.RegenDown, 15); +end + +--Additional damage is 570 at level 50 +--https://ffxiv.gamerescape.com/w/index.php?title=Bloodletter&oldid=298020 +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.RegenDown, 15); + owner.DelHP(570); +end diff --git a/data/scripts/effects/bloodletter2.lua b/data/scripts/effects/bloodletter2.lua new file mode 100644 index 00000000..0ed394e0 --- /dev/null +++ b/data/scripts/effects/bloodletter2.lua @@ -0,0 +1,8 @@ +--Bloodletter2 is the uncomboed version of Bloodletter. It doesn't deal any additional damage when it falls off but has the same tick damage +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.RegenDown, 15); +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.RegenDown, 15); +end diff --git a/data/scripts/effects/cleric_stance.lua b/data/scripts/effects/cleric_stance.lua new file mode 100644 index 00000000..061d13e4 --- /dev/null +++ b/data/scripts/effects/cleric_stance.lua @@ -0,0 +1,13 @@ +require("modifiers") +require("battleutils") + +function onPreAction(caster, target, effect, skill, action, actionContainer) + if skill.commandType == CommandType.Spell then + if action.actionType == ActionType.Heal then + action.amount = action.amount * 0.80; + elseif action.actionType == ActionType.Magic then + action.amount = action.amount * 1.20; + end + end +end; + diff --git a/data/scripts/effects/collusion.lua b/data/scripts/effects/collusion.lua new file mode 100644 index 00000000..ae1d34de --- /dev/null +++ b/data/scripts/effects/collusion.lua @@ -0,0 +1,13 @@ +require("global") +require("modifiers") +require("hiteffect") +require("utils") + +function onHit(effect, attacker, defender, action, actionContainer) + local enmity = action.enmity; + action.enmity = 0; + + defender.hateContainer.UpdateHate(effect.GetSource(), enmity); + --Does collusion send a message? + actionContainer.AddAction(attacker.statusEffects.RemoveStatusEffectForBattleAction(effect)); +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/cover.lua b/data/scripts/effects/cover.lua new file mode 100644 index 00000000..590583a6 --- /dev/null +++ b/data/scripts/effects/cover.lua @@ -0,0 +1,9 @@ +require("global") +require("modifiers") +require("hiteffect") +require("utils") + +--Restores 25% of damage taken as MP. Does not send a message +function onDamageTaken(effect, attacker, defender, action, actionContainer) + defender.AddMP(0.25 * action.amount); +end; \ No newline at end of file diff --git a/data/scripts/effects/decoy.lua b/data/scripts/effects/decoy.lua new file mode 100644 index 00000000..284d13d3 --- /dev/null +++ b/data/scripts/effects/decoy.lua @@ -0,0 +1,16 @@ +require("modifiers") +require("battleutils") + +--This is the untraited version of decoy. + +function onPreAction(caster, target, effect, skill, action, actionContainer) + --Evade single ranged or magic attack + --Traited allows for physical attacks + if skill.isRanged or action.actionType == ActionType.Magic then + --Set action's hit rate to 0 + action.hirRate = 0.0; + end + + --Remove status and add message + actionsList.AddAction(target.statusEffects.RemoveForBattleAction(effect)); +end; \ No newline at end of file diff --git a/data/scripts/effects/decoy2.lua b/data/scripts/effects/decoy2.lua new file mode 100644 index 00000000..d6cbf909 --- /dev/null +++ b/data/scripts/effects/decoy2.lua @@ -0,0 +1,16 @@ +require("modifiers") +require("battleutils") + +--This is the traited version of Decoy. It can also evade physical attacks. + +function onPreAction(caster, target, effect, skill, action, actionContainer) + --Evade single ranged or magic attack + --Traited allows for physical attacks + if skill.isRanged or action.actionType == ActionType.Magic or action.actionType == ActionType.Physical then + --Set action's hit rate to 0 + action.hirRate = 0.0; + end + + --Remove status and add message + actionsList.AddAction(target.statusEffects.RemoveForBattleAction(effect)); +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/defense_down.lua b/data/scripts/effects/defense_down.lua new file mode 100644 index 00000000..28de72b9 --- /dev/null +++ b/data/scripts/effects/defense_down.lua @@ -0,0 +1,7 @@ +function onGain(owner, effect) + owner.SubtractMod(modifiersGlobal.Defense, effect.GetMagnitude()); +end + +function onLose(owner, effect) + owner.AddMod(modifiersGlobal.Defense, effect.GetMagnitude()); +end diff --git a/data/scripts/effects/divine_regen.lua b/data/scripts/effects/divine_regen.lua new file mode 100644 index 00000000..0d29706c --- /dev/null +++ b/data/scripts/effects/divine_regen.lua @@ -0,0 +1,16 @@ +--Consistent 85HP/tick normal; 113HP/tick with AF pants +function onGain(owner, effect) + local magnitude = 85 + + --Need a better way to set magnitude when adding effects + if effect.GetTier() == 2 then + magnitude = 113; + end + effect.SetMagnitude(magnitude); + + owner.AddMod(modifiersGlobal.Regen, effect.GetMagnitude()); +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.Regen, effect.GetMagnitude()); +end diff --git a/data/scripts/effects/divine_veil.lua b/data/scripts/effects/divine_veil.lua new file mode 100644 index 00000000..edd08034 --- /dev/null +++ b/data/scripts/effects/divine_veil.lua @@ -0,0 +1,23 @@ +require("modifiers") +require("hiteffect") + +--Increases block rate by 100% +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.RawBlockRate, 100); +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.RawBlockRate, 100); +end + +--Applys Divine Regen to party in range when healed by cure or cura +function onBlock(caster, target, effect, skill, action, actionContainer) + -- cure cura + if (skill.id == 27346 or skill.id == 27347) and (caster != owner) then + --For each party member in range, add divine regen + for chara in owner.GetPartyMembersInRange(8) do + local addAction = chara.statusEffects.AddStatusForBattleAction(223264, 2); + actionContainer.AddAction(addAction); + end + end +end; \ No newline at end of file diff --git a/data/scripts/effects/dread_spike.lua b/data/scripts/effects/dread_spike.lua new file mode 100644 index 00000000..94888302 --- /dev/null +++ b/data/scripts/effects/dread_spike.lua @@ -0,0 +1,23 @@ +require("modifiers") +require("battleutils") + +--Dread spike completely nullifies a physical action and absorbs how much damage it would have done (when it's powered up) +--I'm going to assume it only absorbs half damage without LS/PS up +--When I say it nullifies an attack, it even gets rid of the message. It's as if the attack didn't happen +--Don't know how this works with multi-hit attacks or even how it works with stoneskin or other buffs that respond to damage +-- I dont really know how this should work... +function onDamageTaken(effect, attacker, defender, action, actionContainer) + if action.actionType == ActionType.Physical then + --maybe this works? + local absorbAmount = action.amount; + action.amount = 0; + action.worldMasterTextId = 0; + + attacker.AddHP(absorbAmount); + --30451: You recover [absorbAmount] HP. + actionContainer.AddHPAction(defender.actorId, 30451, absorbAmount) + --Dread Spike is lost after absorbing hp + actionContainer.AddAction(defender.statusEffects.RemoveStatusEffectForBattleAction(effect)); + end +end; + diff --git a/data/scripts/effects/enduring_march.lua b/data/scripts/effects/enduring_march.lua new file mode 100644 index 00000000..32aaa489 --- /dev/null +++ b/data/scripts/effects/enduring_march.lua @@ -0,0 +1,20 @@ +require("modifiers") + +function onGain(target, effect) + --Traited increases speed by 20%. Assuming that means it actually increases speed instead of simply offsetting the negative speed it has by default + local speedModifier = 0.8; + if effect.GetTier() == 2 then + speedModifier = 1.2; + end + + target.SetMod(modifiersGlobal.Speed, target.GetMod(modifiersGlobal.Speed) * speedModifier); +end; + +function onLose(target, effect) + local speedModifier = 0.8; + if effect.GetTier() == 2 then + speedModifier = 1.2; + end + + target.SetMod(modifiersGlobal.Speed, target.GetMod(modifiersGlobal.Speed) / speedModifier); +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..13f635c3 --- /dev/null +++ b/data/scripts/effects/featherfoot.lua @@ -0,0 +1,26 @@ +require("modifiers"); + +--15% in ARR, dont know how it worked in 1.0 +function onGain(target, effect) + target.AddMod(modifiersGlobal.RawEvadeRate, 15); +end; + +function onLose(target, effect) + target.SubtractMod(modifiersGlobal.RawEvadeRate, 15); +end; + +--Returns 25%? of amount dodged as MP +function onEvade(effect, attacker, defender, action, actionContainer) + --25% of amount dodged untraited, 50% traited + local percent = 0.25; + if (effect.GetTier() == 2) then + percent = 0.50; + end + + local mpToReturn = percent * action.amountMitigated; + defender.AddMP(math.ceil(mpToReturn)); + --33010: You recover x MP from Featherfoot + actionContainer.AddMPAction(defender.actorId, 33010, mpToReturn); + --Featherfoot is lost after evading + actionContainer.AddAction(defender.statusEffects.RemoveStatusEffectForBattleAction(effect)); +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..d9ce7955 --- /dev/null +++ b/data/scripts/effects/fists_of_earth.lua @@ -0,0 +1,8 @@ +function onGain(target, effect) + target.statusEffects.RemoveStatusEffect(223209) + target.statusEffects.RemoveStatusEffect(223211) +end; + +--Need to do more research on these. +--From what I've seen, they changed the property of the attack to magic and the element to the respective element +--Unsure if it applies to weaponskills or if it's auto attacks only. \ 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/foresight.lua b/data/scripts/effects/foresight.lua new file mode 100644 index 00000000..2ffcde2f --- /dev/null +++ b/data/scripts/effects/foresight.lua @@ -0,0 +1,15 @@ +require("modifiers") + +function onGain(target, effect) + --Parry is .1% per , Random guess but gonna say it gives 20% worth of parry. + target.AddMod(modifiersGlobal.Parry, 200); +end; + +function onParry(effect, attacker, defender, action, actionContainer) + --Foresight is lost after parrying + actionContainer.AddAction(defender.statusEffects.RemoveStatusEffectForBattleAction(effect)); +end; + +function onLose(target, effect) + target.SubtractMod(modifiersGlobal.Parry, 200); +end; \ No newline at end of file diff --git a/data/scripts/effects/fully_blissful_mind.lua b/data/scripts/effects/fully_blissful_mind.lua new file mode 100644 index 00000000..678c10e5 --- /dev/null +++ b/data/scripts/effects/fully_blissful_mind.lua @@ -0,0 +1,11 @@ +function onGain(owner, effect) + --Using extra because that's what blissful_mind uses + effect.SetExtra(effect.GetMagnitude()); +end + +function onTick(owner, effect) + print("hi") +end + +function onLose(owner, effect) +end diff --git a/data/scripts/effects/goring_blade.lua b/data/scripts/effects/goring_blade.lua new file mode 100644 index 00000000..c1f8d794 --- /dev/null +++ b/data/scripts/effects/goring_blade.lua @@ -0,0 +1,9 @@ +require("modifiers") + +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.RegenDown, effect.GetMagnitude()); +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.RegenDown, effect.GetMagnitude()); +end diff --git a/data/scripts/effects/hawks_eye.lua b/data/scripts/effects/hawks_eye.lua new file mode 100644 index 00000000..99876524 --- /dev/null +++ b/data/scripts/effects/hawks_eye.lua @@ -0,0 +1,22 @@ +require("modifiers"); + +--In one capture, hawks eye seemed to give 18.75% additional accuracy (379 to 450) +--The player in this capture was a Dragoon, so this is untraited. +--Traited Hawk's Eye says it increases Accuracy by 50%. +--This could mean traited hawk's eye gives 28.125% (18.75% * 1.5) or it could mean it gives 68.75% (18.75% + 50%) +function onGain(target, effect) + local accuracyMod = 0.1875; + + if effect.GetTier() == 2 then + accuracyMod = 0.28125; + end + + local amountGained = accuracyMod * target.GetMod(modifiersGlobal.Accuracy); + effect.SetMagnitude(amountGained); + target.AddMod(modifiersGlobal.Accuracy, effect.GetMagnitude()); +end; + +function onLose(target, effect) + + target.SubtractMod(modifiersGlobal.Accuracy, effect.GetMagnitude()); +end; \ No newline at end of file diff --git a/data/scripts/effects/heavy.lua b/data/scripts/effects/heavy.lua new file mode 100644 index 00000000..e49f8813 --- /dev/null +++ b/data/scripts/effects/heavy.lua @@ -0,0 +1,13 @@ +require("modifiers") + +function onGain(target, effect) + local speedModifier = 0.5; + + target.SetMod(modifiersGlobal.Speed, target.GetMod(modifiersGlobal.Speed) * speedModifier); +end; + +function onLose(target, effect) + local speedModifier = 0.5; + + target.SetMod(modifiersGlobal.Speed, target.GetMod(modifiersGlobal.Speed) / speedModifier); +end; \ No newline at end of file diff --git a/data/scripts/effects/hp_boost.lua b/data/scripts/effects/hp_boost.lua new file mode 100644 index 00000000..ca20c3b1 --- /dev/null +++ b/data/scripts/effects/hp_boost.lua @@ -0,0 +1,15 @@ +require("modifiers") + +--Battle Voice grants HP_Boost and it sets max hp to 125% normal amount and heals for the difference between current +--This doesn't seem like the correct way to do this. If max HP changes between gainign and losing wont this break? +function onGain(target, effect) + local newMaxHP = target.GetMaxHP() * 1.25; + local healAmount = newMaxHP - target.GetMaxHP(); + + target.SetMaxHP(newMaxHP); + target.AddHP(healAmount); +end; + +function onLose(target, effect) + target.SetMaxHP(target.GetMaxHP() * 0.75); +end; \ No newline at end of file diff --git a/data/scripts/effects/hundred_fists.lua b/data/scripts/effects/hundred_fists.lua new file mode 100644 index 00000000..3622161b --- /dev/null +++ b/data/scripts/effects/hundred_fists.lua @@ -0,0 +1,13 @@ +require("modifiers") + +--will this break with things like slow? +function onGain(target, effect) + local currDelay = target.GetMod(modifiersGlobal.AttackDelay); + target.SetMod(modifiersGlobal.AttackDelay), 0.66 * currDelay); +end; + +function onLose(target, effect) + local currDelay = target.GetMod(modifiersGlobal.AttackDelay); + target.SetMod(modifiersGlobal.AttackDelay), 1.50 * currDelay); +end; + diff --git a/data/scripts/effects/invigorate.lua b/data/scripts/effects/invigorate.lua new file mode 100644 index 00000000..8502c5f0 --- /dev/null +++ b/data/scripts/effects/invigorate.lua @@ -0,0 +1,8 @@ +--100 TP per tick without AF. 133 TP per tick with AF +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.Regain, effect.GetMagnitude()); +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.Regain, effect.GetMagnitude()); +end diff --git a/data/scripts/effects/keen_flurry.lua b/data/scripts/effects/keen_flurry.lua new file mode 100644 index 00000000..b9ff8042 --- /dev/null +++ b/data/scripts/effects/keen_flurry.lua @@ -0,0 +1,17 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +--Untraited reduces cooldown by 50% +--Traited reduces cooldown by 100% +function onCommandStart(effect, owner, skill, actionContainer) + if skill.commandType == CommandType.Weaponskill then + local reduction = 0.5; + + if effect.GetTier() == 2 then + reduction = 1.0; + end + + skill.recastTimeMs = skill.recastTimeMs - (reduction * skill.recastTimeMs); + end +end; \ No newline at end of file diff --git a/data/scripts/effects/life_surge_I.lua b/data/scripts/effects/life_surge_I.lua new file mode 100644 index 00000000..f99655ea --- /dev/null +++ b/data/scripts/effects/life_surge_I.lua @@ -0,0 +1,21 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +--Heals for 30%? of damage dealt on auto attacks. +--Trait: Increases healing by 20%. Is this the base % or the amount after taking the base percent? +--I'm guessing the way it works is that LSI/II/III have 10/20/30% absorb by default and 30/40/50% traited. +--Seems to match what i can find in videos +function onHit(effect, attacker, defender, action, actionContainer) + if action.commandType == CommandType.AutoAttack then + local healPercent = 0.10; + + if effect.GetTier() == 2 then + healPercent = 0.30; + end + + local amount = math.floor((healPercent * action.amount) + 1); + attacker.AddHP(amount); + actionContainer.AddHPAction(defender.actorId, 30332, amount); + end +end; \ No newline at end of file diff --git a/data/scripts/effects/life_surge_II.lua b/data/scripts/effects/life_surge_II.lua new file mode 100644 index 00000000..22f69cb2 --- /dev/null +++ b/data/scripts/effects/life_surge_II.lua @@ -0,0 +1,17 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +function onHit(effect, attacker, defender, action, actionContainer) + if action.commandType == CommandType.AutoAttack then + local healPercent = 0.20; + + if effect.GetTier() == 2 then + healPercent = 0.40; + end + + local amount = math.floor((healPercent * action.amount) + 1); + attacker.AddHP(amount); + actionContainer.AddHPAction(defender.actorId, 30332, amount); + end +end; \ No newline at end of file diff --git a/data/scripts/effects/life_surge_III.lua b/data/scripts/effects/life_surge_III.lua new file mode 100644 index 00000000..f99655ea --- /dev/null +++ b/data/scripts/effects/life_surge_III.lua @@ -0,0 +1,21 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +--Heals for 30%? of damage dealt on auto attacks. +--Trait: Increases healing by 20%. Is this the base % or the amount after taking the base percent? +--I'm guessing the way it works is that LSI/II/III have 10/20/30% absorb by default and 30/40/50% traited. +--Seems to match what i can find in videos +function onHit(effect, attacker, defender, action, actionContainer) + if action.commandType == CommandType.AutoAttack then + local healPercent = 0.10; + + if effect.GetTier() == 2 then + healPercent = 0.30; + end + + local amount = math.floor((healPercent * action.amount) + 1); + attacker.AddHP(amount); + actionContainer.AddHPAction(defender.actorId, 30332, amount); + end +end; \ No newline at end of file diff --git a/data/scripts/effects/magic_evasion_down.lua b/data/scripts/effects/magic_evasion_down.lua new file mode 100644 index 00000000..d043c8a5 --- /dev/null +++ b/data/scripts/effects/magic_evasion_down.lua @@ -0,0 +1,8 @@ +--Bloodletter2 is the uncomboed version of Bloodletter. It doesn't deal any additional damage when it falls off but has the same tick damage +function onGain(owner, effect) + owner.SubtractMod(modifiersGlobal.MagicEvasion, effect.GetMagnitude()); +end + +function onLose(owner, effect) + owner.AddMod(modifiersGlobal.MagicEvasion, effect.GetMagnitude()); +end diff --git a/data/scripts/effects/mighty_strikes.lua b/data/scripts/effects/mighty_strikes.lua new file mode 100644 index 00000000..4253d792 --- /dev/null +++ b/data/scripts/effects/mighty_strikes.lua @@ -0,0 +1,12 @@ +require("modifiers") +require("battleutils") + +--Forces crit on attacks made with axes +function onPreAction(caster, target, effect, skill, action, actionContainer) + --Assuming "attacks made with axes" means skills specific to MRD/WAR + if (skill.job == 3 or skill.job == 17) then + --Set action's crit rate to 100% + action.critRate = 100.0; + end +end; + 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/outmaneuver2.lua b/data/scripts/effects/outmaneuver2.lua new file mode 100644 index 00000000..6d5fffbe --- /dev/null +++ b/data/scripts/effects/outmaneuver2.lua @@ -0,0 +1,26 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +--Add 30 raw block rate. No idea how much block it actually gives. +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.RawBlockRate, 30); +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.RawBlockRate, 30); +end + +--Gives 200 TP on block. Traited: Gives 10% of the amount blocked back as MP +function onBlock(effect, attacker, defender, action, actionContainer) + --200 TP on block + defender.AddTP(200); + + --If traited, add 10% of damage taken as MP + if(effect.GetTier() == 2) then + local mpToReturn = math.ceil(0.10 * action.amount); + defender.AddMP(math.ceil(mpToReturn)); + --33009: You recover x MP from Outmaneuver + actionContainer.AddMPAction(defender.actorId, 33009, mpToReturn); + end +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/power_surge_I.lua b/data/scripts/effects/power_surge_I.lua new file mode 100644 index 00000000..ba0fe70e --- /dev/null +++ b/data/scripts/effects/power_surge_I.lua @@ -0,0 +1,30 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +--https://www.bluegartr.com/threads/107403-Stats-and-how-they-work/page22 +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.Attack, 115); + owner.SubtractMod(modifiersGlobal.Defense, 158); +end + +function onCommandStart(effect, owner, command, actionContainer) + --if command is a weaponskill or jump + --27266: jump + if command.GetCommandType() == CommandType.Weaponskill or command.id == 27266 then + effect.SetTier(effect.GetTier() + 1); + + --Takes 10 weaponskills/jumps to increase level + if effect.GetTier() > 10 then + local action = owner.statusEffects.ReplaceEffect(effect, 223213, 1, 1, 60); + actionContainer.AddAction(action); + else + effect.RefreshTime(); + end + end +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.Attack, 115); + owner.AddMod(modifiersGlobal.Defense, 158); +end \ No newline at end of file diff --git a/data/scripts/effects/power_surge_II.lua b/data/scripts/effects/power_surge_II.lua new file mode 100644 index 00000000..56e4daf0 --- /dev/null +++ b/data/scripts/effects/power_surge_II.lua @@ -0,0 +1,31 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +--https://www.bluegartr.com/threads/107403-Stats-and-how-they-work/page22 +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.Attack, 230); + owner.SubtractMod(modifiersGlobal.Defense, 158); +end + +function onCommandStart(effect, owner, command, actionContainer) + --if command is a weaponskill or jump + --27266: jump + if command.GetCommandType() == CommandType.Weaponskill or command.id == 27266 then + effect.SetTier(effect.GetTier() + 1); + + --Takes 10 weaponskills/jumps to increase level + if effect.GetTier() > 10 then + local action = owner.statusEffects.ReplaceEffect(effect, 223214, 1, 1, 60); + actionContainer.AddAction(action); + else + effect.RefreshTime(); + end + end +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.Attack, 230); + owner.AddMod(modifiersGlobal.Defense, 158); +end + diff --git a/data/scripts/effects/power_surge_III.lua b/data/scripts/effects/power_surge_III.lua new file mode 100644 index 00000000..8eb171e4 --- /dev/null +++ b/data/scripts/effects/power_surge_III.lua @@ -0,0 +1,24 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +--https://www.bluegartr.com/threads/107403-Stats-and-how-they-work/page22 +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.Attack, 345); + owner.SubtractMod(modifiersGlobal.Defense, 158); +end + +function onCommandStart(effect, owner, command, actionContainer) + --if command is a weaponskill or jump + --27266: jump + if command.GetCommandType() == CommandType.Weaponskill or command.id == 27266 then + --At III just refresh the effect + effect.RefreshTime(); + end +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.Attack, 345); + owner.AddMod(modifiersGlobal.Defense, 158); +end + diff --git a/data/scripts/effects/protect2.lua b/data/scripts/effects/protect2.lua new file mode 100644 index 00000000..21c40a56 --- /dev/null +++ b/data/scripts/effects/protect2.lua @@ -0,0 +1,43 @@ +require("modifiers") + +function onGain(target, effect) + --Magnitude is caster's Enhancing Magic Potency. + --http://forum.square-enix.com/ffxiv/threads/41900-White-Mage-A-Guide + --5-4-5-4-5-4-5-4-5 repeating points of Enhancing for 1 defense + --4.56 * Enhancing Potency + local defenseBuff = 5-- 4.56 * effect.GetMagnitude(); + local magicDefenseBuff = 0; + + target.AddMod(modifiersGlobal.Defense, defenseBuff); + + --27365: Enhanced Protect: Increases magic defense gained from Protect. + --There is no "magic defense" stat, instead it gives stats to each resist stat. + --if effect.GetTier() >= 2 then + --7-6-7 repeating + --6.67 * Enhancing Potency + magicDefenseBuff = 5--6.67 * effect.GetMagnitude(); + for i = modifiersGlobal.ResistFire, modifiersGlobal.ResistWater do + target.AddMod(i, magicDefenseBuff); + end + --end + +end; + +function onLose(target, effect) + local defenseBuff = 4.56 * effect.GetMagnitude(); + local magicDefenseBuff = 0; + + target.SubtractMod(modifiersGlobal.Defense, defenseBuff); + + --27365: Enhanced Protect: Increases magic defense gained from Protect. + --There is no "magic defense" stat, instead it gives stats to each resist stat. + --if effect.GetTier() >= 2 then + --7-6-7 repeating + --6.67 * Enhancing Potency + magicDefenseBuff = 6.67 * effect.GetMagnitude(); + for i = modifiersGlobal.ResistFire, modifiersGlobal.ResistWater do + target.SubtractMod(i, magicDefenseBuff); + end + --end +end; + diff --git a/data/scripts/effects/quelling_strike.lua b/data/scripts/effects/quelling_strike.lua new file mode 100644 index 00000000..9fb7f027 --- /dev/null +++ b/data/scripts/effects/quelling_strike.lua @@ -0,0 +1,17 @@ +require("modifiers") +require("hiteffect") +require("battleutils") + +--Untraited reduces cooldown by 50% +--Traited reduces cooldown by 100% +function onCommandStart(effect, owner, skill, actionContainer) + --Does this apply to auto attacks? + if skill.commandType == CommandType.Weaponskill or skill.commandType == CommandType.Ability or skill.commandType == CommandType.Magic then + if skill.actionType == ActionType.Physical or skill.actionType == ActionType.Magic then + --No idea what the enmity effect is + skill.enmityModifier = skill.enmityModifier * 0.5; + + owner.AddTP(effect.GetMagnitude()); + end + end +end; \ No newline at end of file diff --git a/data/scripts/effects/quick.lua b/data/scripts/effects/quick.lua new file mode 100644 index 00000000..65861376 --- /dev/null +++ b/data/scripts/effects/quick.lua @@ -0,0 +1,13 @@ +require("modifiers") + +function onGain(target, effect) + local speedModifier = 1.25; + + target.SetMod(modifiersGlobal.Speed, target.GetMod(modifiersGlobal.Speed) * speedModifier); +end; + +function onLose(target, effect) + local speedModifier = 1.25; + + target.SetMod(modifiersGlobal.Speed, target.GetMod(modifiersGlobal.Speed) / speedModifier); +end; \ No newline at end of file diff --git a/data/scripts/effects/rampage2.lua b/data/scripts/effects/rampage2.lua new file mode 100644 index 00000000..eb48b564 --- /dev/null +++ b/data/scripts/effects/rampage2.lua @@ -0,0 +1,51 @@ +require("global") +require("modifiers") +require("hiteffect") +require("battleutils") +require("utils") + +parryPerDT = 20; +delayMsPerDT = 100; + +function onGain(owner, effect) + owner.statusEffects.RemoveStatusEffect(223207); +end + +--Increases parry rating and attack speed for each hit. (Need more info) +function onDamageTaken(effect, attacker, defender, action, actionContainer) + + --Assuming 20 parry rating every time you're hit up to 200 + --Delay is more complicated. Most axes are around 4 seconds, so i'm gonna assume it cuts off a full second at max + if (effect.GetExtra() < 10) then + effect.SetExtra(effect.GetExtra() + 1); + + attacker.AddMod(modifiersGlobal.Parry, parryPerDT); + attacker.SubtractMod(modifiersGlobal.Delay, delayMsPerDT); + end +end + +--Heals for 50% of damage dealt on crits with a maximum of 20% of max hp +--Also only heals for as much hp as you're missing at most +function onCrit(effect, attacker, defender, action, actionContainer) + local healAmount = math.Clamp(action.amount * 0.50, 0, defender.GetMaxHP() * 0.20); + healAmount = math.Clamp(healAmount, 0, defender.GetMaxHP() - defender.GetHP()); + defender.AddHP(healAmount); + --33012: You recover [healAmount] HP. + actionContainer.AddHPAction(owner.actorId, 33008, healAmount); +end; + +--"Effect fades over time" +function onTick(owner, effect) + --Enduring march prevents fading of rampage effect + if not owner.statusEffects.HasStatusEffect(223078) and (effect.GetExtra() > 0) then + --Going to assume that every 5 seconds a single hits worth of rampage is lost. + attacker.SubtractMod(modifiersGlobal.Parry, parryPerDT); + attacker.AddMod(modifiersGlobal.Delay, delayMsPerDT); + effect.SetExtra(effect.GetExtra() - 1); + end +end + +function onLose(owner, effect) + attacker.SubtractMod(modifiersGlobal.Parry, effect.GetExtra() * parryPerDT); + attacker.AddMod(modifiersGlobal.Delay, effect.GetExtra() * delayMsPerDT); +end \ No newline at end of file diff --git a/data/scripts/effects/rampart.lua b/data/scripts/effects/rampart.lua new file mode 100644 index 00000000..6602e09a --- /dev/null +++ b/data/scripts/effects/rampart.lua @@ -0,0 +1,15 @@ +require("modifiers") + +--Rampart gives 105 defense at level 50. +--Guessing it scales with level. If I had to guess it's either 2.1 * level or (2 * level) + 5. +--I'm going to guess the latter since it always leaves you with a whole number. I could be completely wrong though +--The party_battle_leve has rampart giving 36? defense. It's from an earlier patch so probably useless +function onGain(target, effect) + effect.SetMagnitude(2 * target.GetLevel() + 5); + + target.AddMod(modifiersGlobal.Defense, effect.GetMagnitude()); +end; + +function onLose(target, effect) + target.SubtractMod(modifiersGlobal.Defense, effect.GetMagnitude()); +end; \ No newline at end of file diff --git a/data/scripts/effects/regen.lua b/data/scripts/effects/regen.lua new file mode 100644 index 00000000..ccb4f606 --- /dev/null +++ b/data/scripts/effects/regen.lua @@ -0,0 +1,8 @@ +--Regen is modified by Enhancing Magic Potency. Formula here: http://forum.square-enix.com/ffxiv/threads/41900-White-Mage-A-Guide +function onGain(owner, effect) + owner.AddMod(modifiersGlobal.Regen, effect.magnitude); +end + +function onLose(owner, effect) + owner.SubtractMod(modifiersGlobal.Regen, effect.magnitude); +end diff --git a/data/scripts/effects/sentinel.lua b/data/scripts/effects/sentinel.lua new file mode 100644 index 00000000..d913a34f --- /dev/null +++ b/data/scripts/effects/sentinel.lua @@ -0,0 +1,29 @@ +require("modifiers") + +function onGain(target, effect) + --Untraited Sentinel is 30% damage taken down, traited is 50% + local amount = 30; + if effect.GetTier() == 2 then + amount = 50; + end + + target.AddMod(modifiersGlobal.DamageTakenDown, amount); +end; + +function onLose(target, effect) + local amount = 30; + if effect.GetTier() == 2 then + amount = 50; + end + + target.SubtractMod(modifiersGlobal.DamageTakenDown, amount); +end; + +--Increases action's enmity by 100 for weaponskills +--http://forum.square-enix.com/ffxiv/threads/47393-Tachi-s-Guide-to-Paladin-%28post-1.22b%29 +--Sentinel only works on weaponskills. It's possible that was a bug because the description says actions +function onHit(effect, attacker, defender, action, actionContainer) + if action.commandType == CommandType.WeaponSkill then + action.enmity = action.enmity + 100; + end +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/effects/stoneskin.lua b/data/scripts/effects/stoneskin.lua new file mode 100644 index 00000000..74712120 --- /dev/null +++ b/data/scripts/effects/stoneskin.lua @@ -0,0 +1,26 @@ +require("global") +require("utils") +require("modifiers") +require("hiteffect") +require("battleutils") + + +--todo: calculate actual mitigation value based on Source's enhancing magic. info: http://forum.square-enix.com/ffxiv/threads/40800-Enhancing-Magic +--This should also probably be calculated when the spell is cast so it doesnt overwrite a stronger stoneskin +function onGain(owner, effect) + --Going to assume its 1.34 * Enhancing Potency untraited, 1.96 * Enhancing Potency traited. + local potencyModifier = 1.34; + if effect.tier == 2 then + potencyModifier = 1.96; + end + local amount = potencyModifier * effect.source.GetMod(modifiersGlobal.MagicEnhancePotency); + + owner.AddMod(modifiersGlobal.Stoneskin, amount); +end + +--Using extra for how much mitigation stoneskin has +function onPostAction(owner, effect, caster, skill, action, actionContainer) + if (owner.GetMod(modifiersGlobal.Stoneskin) <= 0) then + actionContainer.AddAction(owner.statusEffects.RemoveStatusEffectForBattleAction(effect)); + end +end; \ No newline at end of file diff --git a/data/scripts/effects/vengeance.lua b/data/scripts/effects/vengeance.lua new file mode 100644 index 00000000..bbd919bb --- /dev/null +++ b/data/scripts/effects/vengeance.lua @@ -0,0 +1,17 @@ +require("global") +require("modifiers") +require("hiteffect") +require("utils") + +--Unclear what the exact damage is but it seems like it's the total amount of damage the attack would have done before parrying +function onDamageTaken(effect, attacker, defender, action, actionContainer) + local amount = action.amount + action.mitigatedAmount; + + --Only reflects magical attacks if wearing AF chest + if action.actionType == ActionType.Physical or (action.actionType == ActionType.Magic and effect.GetTier() == 2) then + --30350: Counter! You hit target for x points of damage + --There are counter messages for blocks, can Vengeance be blocked/parried? + attacker.DelHP(amount); + actionContainer.AddHitAction(attacker.actorId, 30350, amount); + end; +end; \ No newline at end of file diff --git a/data/scripts/hiteffect.lua b/data/scripts/hiteffect.lua new file mode 100644 index 00000000..1285d363 --- /dev/null +++ b/data/scripts/hiteffect.lua @@ -0,0 +1,105 @@ +HitEffect = +{ + --All HitEffects have the last byte 0x8 + HitEffectType = 134217728, --8 << 24 + --Status effects use 32 <<,24 + StatusEffectType = 536870912,--32 << 24, + --Heal effects use 48 << 24 + MagicEffectType = 805306368,--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 full resist, RecoilLv2 is a partial resist, RecoilLv3 is no resist, CriticalHit is a crit + RecoilLv1 = 0, + RecoilLv2 = 1, + RecoilLv3 = 2, + + --Setting both recoil flags triggers the "Critical!" pop-up text and hit visual effect. + CriticalHit = 3, + + --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 = 4, + HitVisual2 = 8, + HitVisual3 = 16, + HitVisual4 = 32, + + + --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 = 64, + Shell = 128, + ProtectShellSpecial = 192,-- Protect | Shell, + + Heal = 256,-- Required for heal text to be blue along with HealEffectType, not sure if that's all it's used for + MP = 512, + + --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 = 512, + HitEffect2 = 1024, --Plays the standard hit visual effect, but with no sound if used alone. + HitEffect3 = 2048, --Yellow effect, crit? + HitEffect4 = 4096, --Plays the blocking animation + HitEffect5 = 8192, + GustyHitEffect = 3072,--HitEffect3 | HitEffect2, + GreenTintedHitEffect = 4608,-- HitEffect4 | HitEffect1, + + --For specific animations + Miss = 0, + Evade = 512, + Hit = 1536, --HitEffect1 | HitEffect2, + Parry = 3584, --Hit | HitEffect3, + Block = 4096, + Crit = 2048, + + --Knocks you back away from the attacker. + KnockbackLv1 = 5632,-- HitEffect4 | HitEffect2 | HitEffect1, + KnockbackLv2 = 6144,-- HitEffect4 | HitEffect3, + KnockbackLv3 = 6656,-- HitEffect4 | HitEffect3 | HitEffect1, + KnockbackLv4 = 7168,-- HitEffect4 | HitEffect3 | HitEffect2, + KnockbackLv5 = 7680,-- HitEffect4 | HitEffect3 | HitEffect2 | HitEffect1, + + --Knocks you away from the attacker in a counter-clockwise direction. + KnockbackCounterClockwiseLv1 = 8192, + KnockbackCounterClockwiseLv2 = 8704,-- HitEffect5 | HitEffect1, + + --Knocks you away from the attacker in a clockwise direction. + KnockbackClockwiseLv1 = 9216,-- HitEffect5 | HitEffect2, + KnockbackClockwiseLv2 = 9728,-- HitEffect5 | HitEffect2 | HitEffect1, + + --Completely drags target to the attacker, even across large distances. + DrawIn = 10240,-- HitEffect5 | HitEffect3, + + --An additional visual effect that plays on the target based on according buff. + UnknownShieldEffect = 12288,-- HitEffect5 | HitEffect4, + Stoneskin = 12800,-- 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 = 32768, + SkillCombo2 = 65536, + SkillCombo3 = 98304,-- SkillCombo1 | SkillCombo2, + SkillCombo4 = 131072 + + --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..15387d97 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 = { diff --git a/data/scripts/modifiers.lua b/data/scripts/modifiers.lua index 8783e26c..9a2a6a00 100644 --- a/data/scripts/modifiers.lua +++ b/data/scripts/modifiers.lua @@ -1,51 +1,87 @@ -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, +modifiersGlobal = +{ + NAMEPLATE_SHOWN = 0, + TARGETABLE = 1, + NAMEPLATE_SHOWN2 = 2, + --NAMEPLATE_SHOWN2 = 3, - HarvestPotency = 38, - HarvestLimit = 39, - HarvestRate = 40, + Strength = 3, + Vitality = 4, + Dexterity = 5, + Intelligence = 6, + Mind = 7, + Piety = 8, - Raise = 41, - MinimumHpLock = 42, -- hp cannot fall below this value + ResistFire = 9, + ResistIce = 10, + ResistWind = 11, + ResistLightning = 12, + ResistEarth = 13, + ResistWater = 14, + + Accuracy = 15, + Evasion = 16, + Attack = 17, + Defense = 18, --Is there a magic defense stat? 19 maybe? + MagicAttack = 23, + MagicHeal = 24, + MagicEnhancePotency = 25, + MagicEnfeeblingPotency = 26, + + MagicAccuracy = 27, + MagicEvasion = 28, + + CraftProcessing = 30, + CraftMagicProcessing = 31, + CraftProcessControl = 32, + + HarvestPotency = 33, + HarvestLimit = 34, + HarvestRate = 35, + + None = 36, + Hp = 37, + HpPercent = 38, + Mp = 39, + MpPercent = 40, + Tp = 41, + TpPercent = 42, + Regen = 43, + Refresh = 44, + + AttackRange = 45, + Speed = 46, + AttackDelay = 47, + + Raise = 48, + MinimumHpLock = 49, -- hp cannot fall below this value + AttackType = 50, -- slashing, piercing, etc + BlockRate = 51, + Block = 52, + CritRating = 53, + HasShield = 54, -- 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 = 55, -- Amount of hits in an auto attack. Usually 1, 2 for h2h, 3 with spinning heel + + --Flat percent increases to these rates. Probably a better way to do this + RawEvadeRate = 56, + RawParryRate = 57, + RawBlockRate = 58, + RawResistRate = 59, + RawHitRate = 60, + RawCritRate = 61, + + DamageTakenDown = 62, -- Percent damage taken down + StoreTP = 63, --.1% extra tp per point. Lancer trait is 50 StoreTP + PhysicalCritRate = 64, --CritRating but only for physical attacks. Increases chance of critting. + PhysicalCritEvasion = 65, --Opposite of CritRating. Reduces chance of being crit by phyiscal attacks + PhysicalCritAttack = 66, --Increases damage done by Physical Critical hits + PhysicalCritResilience = 67, --Decreases damage taken by Physical Critical hits + Parry = 68, --Increases chance to parry + MagicCritPotency = 69, --Increases + Regain = 70, --TP regen, should be -90 out of combat, Invigorate sets to 100+ depending on traits + RegenDown = 71, --Damage over time effects. Separate from normal Regen because of how they are displayed in game + Stoneskin = 72, --Nullifies damage + MinimumTpLock = 73 } 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..7ed63cac 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) @@ -25,4 +24,5 @@ end function tryAggro(ally, contentGroupCharas) allyGlobal.tryAggro(ally, contentGroupCharas) + end \ No newline at end of file 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/utils.lua b/data/scripts/utils.lua index 2aff7f62..fa5b9770 100644 --- a/data/scripts/utils.lua +++ b/data/scripts/utils.lua @@ -26,4 +26,9 @@ function getDistanceBetweenActors(actor1, actor2) local dz = pos1[2] - pos2[2] return math.sqrt(dx * dx + dy * dy + dz *dz); +end + +function math.Clamp(val, lower, upper) + if lower > upper then lower, upper = upper, lower end -- swap if boundaries supplied the wrong way + return math.max(lower, math.min(upper, val)) end \ No newline at end of file diff --git a/data/scripts/weaponskill.lua b/data/scripts/weaponskill.lua index 84d79cc7..8fb6dabb 100644 --- a/data/scripts/weaponskill.lua +++ b/data/scripts/weaponskill.lua @@ -1,17 +1,42 @@ -- todo: add enums for status effects in global.lua -require("global") - -weaponskill = -{ - -}; - +--require("global") +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) + + +function CalculateDamage(caster, target, skill, action) + --http://forum.square-enix.com/ffxiv/threads/36412-STR-PIE-ATK-Testing/page2 + --DRG numbers + --Against Level 52 Halberdiers: + --0.8 damage/STR. Caps at 350 + --0.67=0.69 damage/PIE. Hard cap at 310 + --0.35-0.37 damage/ATK for both AA and WS. + + --^ Old? + --http://prestigexiv.guildwork.com/forum/threads/4fecdc94205cb248b5000526-dragoon-and-other-dd-dpsbase-damage-study?page=1#4fecdc94205cb248b5000525 + --10/09/2012 http://forum.square-enix.com/ffxiv/threads/55291-DPS-Testing/page4 + -- 1 point prim: 0.8 damage + -- ATK: .1% damage? .38 damage? + + --Possible formula for melee?: + --local strCap = CalculateCapOfWeapon(caster.getweapon)<- not sure how this is calculated yet, just an example + --local secondaryCap = CalculateSecondaryCapOfWeapon(caster.getweapon) + --local cappedStr = math.min(caster.GetMod(modifiersGlobal.Strength), strCap); + --local cappedSec = math.min(caster.GetMod(caster.secondaryStat), secCap); + --local damageBase = skill.basePotency + (0.85 * cappedStr) + (0.65 * cappedSec); + + --The maximum deviation for weaponskills is ~8%. + --local dev = 0.96 + (math.random() * 8); + --damageBase = math.Clamp(damageBase * dev, 1, 9999); + --return damageBase; + return 100; +end + +function HandleHealingSkill(caster, target, skill, action, statId, modifierId, multiplier, baseAmount) potency = potency or 1.0; healAmount = baseAmount; @@ -19,19 +44,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 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..30d820ff 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,15 @@ 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 NOT NULL DEFAULT '1', + `worldMasterTextId` smallint(5) unsigned NOT NULL DEFAULT '0', + `commandType` tinyint(3) unsigned NOT NULL DEFAULT '0', + `actionType` tinyint(3) unsigned NOT NULL DEFAULT '0', + `actionProperty` tinyint(3) unsigned NOT NULL DEFAULT '0', + `isRanged` tinyint(4) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -60,148 +73,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,0,30320,3,3,13,0); +INSERT INTO `server_battle_commands` VALUES (27101,'blindside',2,14,3,1,1,0,0,0,100,1,0,0,0,0,223237,60,1,0,0,60,0,0,14,635,2,3,234889851,0,0,0,0,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27102,'taunt',2,42,4,32,32,0,0,0,100,1,0,0,25,0,223073,5,1,0,0,60,0,0,14,517,2,3,234889733,0,0,0,0,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27108,'shoulder_tackle',15,30,0,32,32,0,0,0,100,1,0,0,15,0,223015,10,0.75,0,0,60,0,0,18,1048,205,3,302830616,0,0,0,0,0,30301,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30301,2,1,3,0); +INSERT INTO `server_battle_commands` VALUES (27111,'concussive_blow',2,10,1,32,32,0,0,0,100,1,4,0,5,0,223007,30,0,0,0,30,0,1500,18,20,3,3,302002196,0,27112,0,2,0,30301,2,1,3,0); +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,0,30301,2,1,3,0); +INSERT INTO `server_battle_commands` VALUES (27113,'aura_pulse',2,38,4,32,32,1,8,1,100,1,0,0,5,0,223003,30,0,0,0,40,0,1500,18,66,203,3,302821442,0,0,0,2,0,30301,2,1,3,0); +INSERT INTO `server_battle_commands` VALUES (27114,'pounce',2,4,4,32,32,0,0,0,100,1,2,0,5,0,223015,10,0,0,0,20,0,1500,18,8,3,3,302002184,0,27115,27117,1,0,30301,2,1,3,0); +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,0,30301,2,1,3,0); +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,0,30301,2,1,3,0); +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,0,30301,2,1,3,0); +INSERT INTO `server_battle_commands` VALUES (27118,'dragon_kick',15,45,0,32,32,0,0,0,100,1,0,0,5,0,223013,10,0,0,0,60,0,2000,18,1041,204,3,302826513,0,0,0,0,0,30301,2,1,3,0); +INSERT INTO `server_battle_commands` VALUES (27119,'haymaker',2,18,4,32,32,0,0,0,100,1,0,2,5,0,223015,10,0.75,0,0,5,0,250,18,23,201,3,302813207,0,0,0,0,0,30301,2,1,3,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27142,'rampart',3,2,3,1,5,0,8,1,100,1,0,0,0,0,223064,60,1,0,0,120,0,0,14,536,2,3,234889752,0,0,0,0,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27144,'outmaneuver',3,34,8,1,1,0,0,0,100,1,0,0,0,0,223236,30,1,0,0,90,0,0,14,512,21,3,234967552,0,0,0,0,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27145,'flash',3,14,3,32,32,0,8,0,100,1,0,0,25,0,223007,10,0,0,0,30,0,0,14,696,2,3,234889912,0,0,0,0,0,30101,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27146,'cover',16,30,0,4,4,0,0,0,100,1,0,0,15,0,223173,15,1,0,0,60,0,0,14,725,2,3,234889941,0,0,0,0,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,3,13,0); +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,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +INSERT INTO `server_battle_commands` VALUES (27154,'riot_blade',3,30,8,32,32,0,0,0,100,1,2,0,5,0,223038,30,0,0,0,80,0,2000,18,75,2,3,301998155,0,27155,0,1,0,30301,2,1,1,1); +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,-40,30301,2,1,1,0); +INSERT INTO `server_battle_commands` VALUES (27156,'shield_bash',3,18,17,32,32,0,0,0,100,1,0,0,5,0,223015,5,0.75,0,0,30,0,250,18,5,26,3,302096389,0,0,0,0,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +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,50,30301,2,1,1,0); +INSERT INTO `server_battle_commands` VALUES (27180,'provoke',4,14,3,32,32,0,0,0,100,1,0,0,25,0,223034,30,0,0,0,30,0,0,14,600,2,3,234889816,0,0,0,0,0,30101,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27183,'berserk',4,22,32,1,1,0,0,0,100,1,0,0,0,0,223207,4294967295,1,0,0,10,0,0,14,682,2,3,234889898,0,0,0,0,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27184,'rampage',4,34,32,1,1,0,0,0,100,1,0,0,0,0,223208,4294967295,1,0,0,10,0,0,14,546,2,3,234889762,0,0,0,0,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +INSERT INTO `server_battle_commands` VALUES (27192,'steel_cyclone',17,45,0,32,32,1,8,0,100,1,0,0,5,0,223015,3,0,0,0,30,0,2000,18,1040,404,3,303645712,0,0,0,3,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +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,30,0,0,0,30,0,1500,18,44,401,3,303632428,0,27197,0,1,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +INSERT INTO `server_battle_commands` VALUES (27198,'fracture',4,18,32,32,32,0,0,0,100,1,0,3,5,0,223013,8,0.75,0,0,40,0,500,18,42,3,3,302002218,0,0,0,0,0,30301,2,1,1,0); +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,0,30301,2,1,1,0); +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,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27221,'quelling_strike',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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27225,'raging_strike',7,14,64,1,1,0,0,0,100,1,0,0,0,0,223221,4294967295,1,0,0,10,0,0,14,632,2,3,234889848,0,0,0,0,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30301,2,1,4,1); +INSERT INTO `server_battle_commands` VALUES (27229,'leaden_arrow',7,10,1,32,32,0,0,0,100,1,0,0,25,0,228021,30,0.75,0,0,30,0,1500,18,1035,4,3,302007307,0,27230,0,2,0,30301,2,1,4,1); +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,-20,30301,2,1,4,1); +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,0,30301,2,1,4,1); +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,5,0.75,0,0,30,0,3000,18,1037,704,3,304874509,0,0,0,3,0,30301,2,1,4,1); +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,0,30301,2,1,4,1); +INSERT INTO `server_battle_commands` VALUES (27234,'gloom_arrow',7,30,1,32,32,0,0,0,100,1,0,0,25,0,223007,30,0,0,0,10,0,1000,18,1039,4,3,302007311,0,27235,0,2,0,30301,2,1,4,1); +INSERT INTO `server_battle_commands` VALUES (27235,'bloodletter',7,46,64,32,32,0,0,0,100,1,0,0,25,0,223241,30,0.75,0,0,80,0,1500,18,53,701,3,304861237,0,0,0,3,0,30301,2,1,4,1); +INSERT INTO `server_battle_commands` VALUES (27236,'shadowbind',7,18,64,32,32,0,0,0,100,1,0,0,25,0,228011,15,0.9,0,0,40,0,250,18,17,4,3,302006289,0,0,0,2,0,30301,2,1,4,1); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,32613,3,0,0,0); +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,0,30301,3,1,0,1); +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,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27261,'power_surge',8,34,128,1,1,0,0,0,100,1,0,0,0,0,223212,60,1,0,0,10,0,0,14,686,2,3,234889902,0,0,0,0,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27262,'life_surge',8,22,128,1,1,0,0,0,100,1,0,0,0,0,223215,180,1,0,0,15,0,250,14,687,2,3,234889903,0,0,0,0,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30301,3,1,2,0); +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,0,30101,3,0,0,0); +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,0,30301,3,2,5,0); +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,0,30301,2,1,2,0); +INSERT INTO `server_battle_commands` VALUES (27270,'leg_sweep',8,30,1,32,32,1,0,0,100,1,0,0,5,0,223015,8,0,0,0,30,0,1000,18,37,1,3,301994021,0,27271,0,2,0,30301,2,1,2,0); +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,0,30301,2,1,2,0); +INSERT INTO `server_battle_commands` VALUES (27272,'disembowel',19,35,0,32,32,0,0,0,100,1,0,0,5,0,223005,15,0.75,0,0,30,0,750,18,1042,2,3,301999122,0,0,0,0,0,30301,2,1,2,0); +INSERT INTO `server_battle_commands` VALUES (27273,'heavy_thrust',8,10,1,32,32,0,0,0,100,1,0,0,5,0,223015,4,0.75,0,0,20,0,1500,18,1031,1,3,301995015,0,0,0,0,0,30301,2,1,2,0); +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,0,30301,2,1,2,0); +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,0,30301,2,1,2,0); +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,0,30301,2,1,2,0); +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,0,30301,2,1,2,0); +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,100,30301,2,1,2,0); +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,50,30301,2,1,2,0); +INSERT INTO `server_battle_commands` VALUES (27300,'dark_seal',22,14,3,1,1,0,0,0,100,1,0,0,0,0,223229,30,1,0,0,90,0,0,14,518,2,3,234889734,0,0,0,0,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27301,'resonance',22,22,3,1,1,0,0,0,100,1,0,0,0,0,223230,30,1,0,0,90,0,0,14,669,2,3,234889885,0,0,0,0,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30101,3,0,0,0); +INSERT INTO `server_battle_commands` VALUES (27306,'sleep',22,42,256,32,32,0,0,0,100,1,0,0,25,0,228001,60,0.9,2,3000,0,75,0,1,651,1,3,16781963,0,0,0,0,0,30328,4,4,12,0); +INSERT INTO `server_battle_commands` VALUES (27307,'sanguine_rite',22,30,3,1,1,0,0,0,100,1,0,0,0,0,223234,20,1,0,0,60,120,0,1,152,1,3,16781464,0,0,0,0,0,30328,4,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27308,'blizzard',22,4,256,32,32,0,0,0,100,1,0,0,25,0,228021,30,0.75,2,3000,10,90,0,1,502,1,3,16781814,0,0,0,0,0,30301,4,2,6,0); +INSERT INTO `server_battle_commands` VALUES (27309,'blizzara',22,26,256,32,32,1,0,0,100,1,0,0,25,0,228011,30,0.75,0,0,40,150,0,1,506,1,3,16781818,0,0,0,0,0,30301,4,2,6,0); +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,0,30301,4,2,5,0); +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,0,30301,4,2,5,0); +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,0,30301,4,2,5,0); +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,0,30301,4,2,9,0); +INSERT INTO `server_battle_commands` VALUES (27314,'thundara',22,18,256,32,32,0,0,0,100,1,0,0,25,0,223015,4,0.75,0,0,30,135,0,1,508,1,3,16781820,0,27315,27316,2,0,30301,4,2,9,0); +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,0,30301,4,2,9,0); +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,0,30301,4,2,9,0); +INSERT INTO `server_battle_commands` VALUES (27317,'sleepga',26,45,0,32,32,1,8,0,100,1,0,0,25,0,228001,60,0.9,2,4000,0,100,0,1,704,1,3,16782016,0,0,0,0,0,30328,4,4,12,0); +INSERT INTO `server_battle_commands` VALUES (27318,'flare',26,40,0,1,32,1,8,1,100,1,0,0,25,0,223262,30,0.75,2,8000,120,200,0,1,706,1,3,16782018,0,0,0,0,0,30301,4,2,5,0); +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,0,30301,4,2,6,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +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,0,30328,3,4,0,0); +INSERT INTO `server_battle_commands` VALUES (27344,'presence_of_mind',27,30,0,1,1,0,0,0,100,1,0,0,0,0,223116,30,1,0,0,300,0,0,14,722,2,3,234889938,0,0,0,0,0,30328,3,4,0,0); +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,0,30320,3,3,13,0); +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,0,30320,4,3,13,0); +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,0,30320,4,3,13,0); +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,0,30320,4,3,13,0); +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,0,30101,4,4,11,0); +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,0,30328,4,4,8,0); +INSERT INTO `server_battle_commands` VALUES (27351,'protect',23,6,3,4,4,1,20,0,100,1,0,0,25,0,223239,300,1,3,3000,30,80,0,1,1085,1,3,16782397,0,0,0,0,0,30328,4,4,11,0); +INSERT INTO `server_battle_commands` VALUES (27352,'repose',23,50,0,32,32,0,0,0,100,1,0,0,25,0,228001,60,0.9,3,3000,0,80,0,1,151,1,3,16781463,0,0,0,0,0,30328,4,4,10,0); +INSERT INTO `server_battle_commands` VALUES (27353,'aero',23,4,3,32,32,0,0,0,100,1,0,0,25,0,223235,20,0.75,3,3000,6,75,0,1,510,1,3,16781822,0,27354,0,1,0,30301,4,2,7,0); +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,0,30301,4,2,7,0); +INSERT INTO `server_battle_commands` VALUES (27355,'stone',23,1,3,32,32,0,0,0,100,1,0,0,25,0,223243,10,0.75,3,2000,6,75,0,1,513,1,3,16781825,0,27356,0,1,0,30301,4,2,8,0); +INSERT INTO `server_battle_commands` VALUES (27356,'stonera',23,22,512,32,32,1,0,0,100,1,0,0,25,0,228021,30,0.75,3,3000,30,150,0,1,514,1,3,16781826,0,0,0,2,0,30301,4,2,8,0); +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,0,30329,4,0,13,0); +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,0,30328,4,4,13,0); +INSERT INTO `server_battle_commands` VALUES (27359,'holy',27,45,0,1,32,1,8,1,100,1,0,0,25,0,228011,10,0.9,0,0,300,100,0,1,708,1,3,16782020,0,0,0,0,0,30301,4,2,11,0); /*!40000 ALTER TABLE `server_battle_commands` ENABLE KEYS */; UNLOCK TABLES; commit; @@ -215,4 +228,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-04-18 14:44:11 diff --git a/sql/server_battle_traits.sql b/sql/server_battle_traits.sql new file mode 100644 index 00000000..2c3d7d9d --- /dev/null +++ b/sql/server_battle_traits.sql @@ -0,0 +1,133 @@ +-- MySQL dump 10.13 Distrib 5.7.11, for Win64 (x86_64) +-- +-- Host: localhost Database: ffxiv_server +-- ------------------------------------------------------ +-- Server version 5.7.11 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `server_battle_traits` +-- + +DROP TABLE IF EXISTS `server_battle_traits`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `server_battle_traits` ( + `id` smallint(6) NOT NULL, + `name` varchar(50) NOT NULL, + `classJob` tinyint(4) NOT NULL, + `lvl` tinyint(4) NOT NULL, + `modifier` int(11) NOT NULL DEFAULT '0', + `bonus` smallint(6) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `server_battle_traits` +-- + +LOCK TABLES `server_battle_traits` WRITE; +/*!40000 ALTER TABLE `server_battle_traits` DISABLE KEYS */; +set autocommit=0; +INSERT INTO `server_battle_traits` VALUES (27240,'enhanced_hawks_eye',7,28,0,0); +INSERT INTO `server_battle_traits` VALUES (27242,'enhanced_barrage',7,44,0,0); +INSERT INTO `server_battle_traits` VALUES (27241,'enhanced_quelling_strike',7,32,0,0); +INSERT INTO `server_battle_traits` VALUES (27243,'enhanced_raging_strike',7,36,0,0); +INSERT INTO `server_battle_traits` VALUES (27244,'enhanced_decoy',7,16,0,0); +INSERT INTO `server_battle_traits` VALUES (27245,'swift_chameleon',7,48,0,0); +INSERT INTO `server_battle_traits` VALUES (27246,'enhanced_physical_crit_accuracy',7,40,65,10); +INSERT INTO `server_battle_traits` VALUES (27247,'enhanced_physical_crit_evasion',7,20,66,10); +INSERT INTO `server_battle_traits` VALUES (27248,'enhanced_physical_evasion',7,12,16,8); +INSERT INTO `server_battle_traits` VALUES (27249,'enhanced_physical_accuracy',7,8,16,8); +INSERT INTO `server_battle_traits` VALUES (27250,'enhanced_physical_accuracy_ii',7,24,16,10); +INSERT INTO `server_battle_traits` VALUES (27120,'enhanced_second_wind',2,20,0,0); +INSERT INTO `server_battle_traits` VALUES (27121,'enhanced_blindside',2,24,0,0); +INSERT INTO `server_battle_traits` VALUES (27122,'swift_taunt',2,48,0,0); +INSERT INTO `server_battle_traits` VALUES (27123,'enhanced_featherfoot',2,28,0,0); +INSERT INTO `server_battle_traits` VALUES (27124,'enhanced_fists_of_fire',2,44,0,0); +INSERT INTO `server_battle_traits` VALUES (27125,'enhanced_fists_of_earth',2,36,0,0); +INSERT INTO `server_battle_traits` VALUES (27126,'enhanced_physical_accuracy',2,16,16,8); +INSERT INTO `server_battle_traits` VALUES (27127,'enhanced_physical_attack',2,8,17,8); +INSERT INTO `server_battle_traits` VALUES (27128,'enhanced_physical_attack_ii',2,40,17,10); +INSERT INTO `server_battle_traits` VALUES (27129,'enhanced_evasion',2,12,16,8); +INSERT INTO `server_battle_traits` VALUES (27130,'enhanced_physical_crit_damage',2,32,67,10); +INSERT INTO `server_battle_traits` VALUES (27160,'enhanced_sentinel',3,36,0,0); +INSERT INTO `server_battle_traits` VALUES (27161,'enhanced_flash',3,28,0,0); +INSERT INTO `server_battle_traits` VALUES (27162,'enhanced_flash_ii',3,48,0,0); +INSERT INTO `server_battle_traits` VALUES (27163,'enhanced_rampart',3,12,0,0); +INSERT INTO `server_battle_traits` VALUES (27164,'swift_aegis_boon',3,20,0,0); +INSERT INTO `server_battle_traits` VALUES (27165,'enhanced_outmaneuver',3,44,0,0); +INSERT INTO `server_battle_traits` VALUES (27167,'enhanced_block_rate',3,16,52,10); +INSERT INTO `server_battle_traits` VALUES (27166,'enhanced_physical_crit_resilience',3,32,68,10); +INSERT INTO `server_battle_traits` VALUES (27168,'enhanced_physical_defense',3,8,18,10); +INSERT INTO `server_battle_traits` VALUES (27169,'enhanced_physical_defense_ii',3,24,18,10); +INSERT INTO `server_battle_traits` VALUES (27170,'enhanced_physical_defense_iii',3,40,18,12); +INSERT INTO `server_battle_traits` VALUES (27200,'enhanced_provoke',4,28,0,0); +INSERT INTO `server_battle_traits` VALUES (27201,'swift_foresight',4,20,0,0); +INSERT INTO `server_battle_traits` VALUES (27202,'swift_bloodbath',4,16,0,0); +INSERT INTO `server_battle_traits` VALUES (27203,'enhanced_enduring_march',4,48,0,0); +INSERT INTO `server_battle_traits` VALUES (27204,'enhanced_rampage',4,44,0,0); +INSERT INTO `server_battle_traits` VALUES (27205,'enhanced_berserk',4,36,0,0); +INSERT INTO `server_battle_traits` VALUES (27206,'enhanced_physical_crit_evasion',4,32,66,10); +INSERT INTO `server_battle_traits` VALUES (27207,'enhanced_parry',4,24,69,8); +INSERT INTO `server_battle_traits` VALUES (27208,'enhanced_physical_defense',4,12,18,8); +INSERT INTO `server_battle_traits` VALUES (27209,'enhanced_physical_defense_ii',4,40,18,10); +INSERT INTO `server_battle_traits` VALUES (27210,'enhanced_physical_attack_power',4,8,17,8); +INSERT INTO `server_battle_traits` VALUES (27280,'enhanced_invigorate',8,28,0,0); +INSERT INTO `server_battle_traits` VALUES (27281,'enhanced_power_surge',8,44,0,0); +INSERT INTO `server_battle_traits` VALUES (27282,'enhanced_life_surge',8,32,0,0); +INSERT INTO `server_battle_traits` VALUES (27283,'enhanced_blood_for_blood',8,48,0,0); +INSERT INTO `server_battle_traits` VALUES (27284,'swift_blood_for_blood',8,16,0,0); +INSERT INTO `server_battle_traits` VALUES (27285,'enhanced_keen_flurry',8,36,0,0); +INSERT INTO `server_battle_traits` VALUES (27286,'store_tp',8,12,64,50); +INSERT INTO `server_battle_traits` VALUES (27287,'enhanced_physical_crit_accuracy',8,24,65,10); +INSERT INTO `server_battle_traits` VALUES (27288,'enhanced_physical_attack_power',8,8,17,8); +INSERT INTO `server_battle_traits` VALUES (27289,'enhanced_physical_attack_power_ii',8,20,17,10); +INSERT INTO `server_battle_traits` VALUES (27290,'enhanced_physical_attack_power_iii',8,40,17,10); +INSERT INTO `server_battle_traits` VALUES (27320,'swift_dark_seal',22,36,0,0); +INSERT INTO `server_battle_traits` VALUES (27321,'enhanced_excruciate',22,48,0,0); +INSERT INTO `server_battle_traits` VALUES (27322,'swift_necrogenesis',22,24,0,0); +INSERT INTO `server_battle_traits` VALUES (27323,'enhanced_parsimony',22,16,0,0); +INSERT INTO `server_battle_traits` VALUES (27324,'enhanced_sanguine_rite',22,44,0,0); +INSERT INTO `server_battle_traits` VALUES (27325,'enhanced_enfeebling_magic',22,12,26,8); +INSERT INTO `server_battle_traits` VALUES (27326,'enhanced_enfeebling_magic_ii',22,28,26,10); +INSERT INTO `server_battle_traits` VALUES (27327,'enhanced_magic_potency',22,8,23,8); +INSERT INTO `server_battle_traits` VALUES (27328,'enhanced_magic_potency_ii',22,28,23,10); +INSERT INTO `server_battle_traits` VALUES (27329,'enhanced_magic_crit_potency',22,40,70,10); +INSERT INTO `server_battle_traits` VALUES (27330,'auto-refresh',22,20,45,3); +INSERT INTO `server_battle_traits` VALUES (27360,'swift_sacred_prism',23,40,0,0); +INSERT INTO `server_battle_traits` VALUES (27361,'swift_shroud_of_saints',23,44,0,0); +INSERT INTO `server_battle_traits` VALUES (27362,'enhanced_blissful_mind',23,32,0,0); +INSERT INTO `server_battle_traits` VALUES (27363,'enhanced_raise',23,48,0,0); +INSERT INTO `server_battle_traits` VALUES (27364,'enhanced_stoneskin',23,36,0,0); +INSERT INTO `server_battle_traits` VALUES (27365,'enhanced_protect',23,24,0,0); +INSERT INTO `server_battle_traits` VALUES (27366,'greater_enhancing_magic',23,12,25,8); +INSERT INTO `server_battle_traits` VALUES (27367,'greater_healing',23,8,24,8); +INSERT INTO `server_battle_traits` VALUES (27368,'greater_healing_ii',23,18,24,10); +INSERT INTO `server_battle_traits` VALUES (27369,'enhanced_magic_accuracy',23,16,27,8); +INSERT INTO `server_battle_traits` VALUES (27370,'auto-refresh',23,20,45,3); +/*!40000 ALTER TABLE `server_battle_traits` ENABLE KEYS */; +UNLOCK TABLES; +commit; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2018-04-18 14:44:11 diff --git a/sql/server_battlenpc_pool_mods.sql b/sql/server_battlenpc_pool_mods.sql index affc751a..b76e11ef 100644 --- a/sql/server_battlenpc_pool_mods.sql +++ b/sql/server_battlenpc_pool_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 */; @@ -40,6 +40,11 @@ LOCK TABLES `server_battlenpc_pool_mods` WRITE; set autocommit=0; INSERT INTO `server_battlenpc_pool_mods` VALUES (2,2,3,1); INSERT INTO `server_battlenpc_pool_mods` VALUES (2,3,3,1); +INSERT INTO `server_battlenpc_pool_mods` VALUES (2,24,0,1); +INSERT INTO `server_battlenpc_pool_mods` VALUES (3,24,0,1); +INSERT INTO `server_battlenpc_pool_mods` VALUES (3,49,1,0); +INSERT INTO `server_battlenpc_pool_mods` VALUES (4,24,0,1); +INSERT INTO `server_battlenpc_pool_mods` VALUES (4,49,1,0); /*!40000 ALTER TABLE `server_battlenpc_pool_mods` ENABLE KEYS */; UNLOCK TABLES; commit; @@ -53,4 +58,4 @@ commit; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2017-09-16 2:43:11 +-- Dump completed on 2018-04-18 14:54:09 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..f706bc7b 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,82 @@ 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',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223002,'haste',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223004,'petrification',264241194,2,0); +INSERT INTO `server_statuseffects` VALUES (223005,'paralysis',42,2,3000); +INSERT INTO `server_statuseffects` VALUES (223006,'silence',4194346,2,0); +INSERT INTO `server_statuseffects` VALUES (223007,'blind',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223008,'mute',4194346,2,0); +INSERT INTO `server_statuseffects` VALUES (223010,'glare',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223011,'poison',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223012,'transfixion',268435498,2,0); +INSERT INTO `server_statuseffects` VALUES (223013,'pacification',8388650,2,0); +INSERT INTO `server_statuseffects` VALUES (223014,'amnesia',16777258,2,0); +INSERT INTO `server_statuseffects` VALUES (223015,'stun',264241194,2,0); +INSERT INTO `server_statuseffects` VALUES (223016,'daze',264241194,2,0); +INSERT INTO `server_statuseffects` VALUES (223029,'hp_boost',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223030,'hp_penalty',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223038,'defense_down',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223058,'aegis_boon',528434,2,0); +INSERT INTO `server_statuseffects` VALUES (223062,'sentinel',1048626,2,0); +INSERT INTO `server_statuseffects` VALUES (223063,'cover',16434,2,0); +INSERT INTO `server_statuseffects` VALUES (223064,'rampart',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223068,'tempered_will',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223075,'featherfoot',131122,2,0); +INSERT INTO `server_statuseffects` VALUES (223078,'enduring_march',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223081,'bloodbath',1048626,2,0); +INSERT INTO `server_statuseffects` VALUES (223083,'foresight',262194,2,0); +INSERT INTO `server_statuseffects` VALUES (223091,'keen_flurry',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223094,'invigorate',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223097,'collusion',1048626,1,0); +INSERT INTO `server_statuseffects` VALUES (223104,'quelling_strike',1058,2,0); +INSERT INTO `server_statuseffects` VALUES (223106,'hawks_eye',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223108,'decoy',4130,2,0); +INSERT INTO `server_statuseffects` VALUES (223127,'bloodletter',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223173,'covered',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223180,'regen',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223205,'combo',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223206,'goring_blade',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223207,'berserk2',1074806818,1,3000); +INSERT INTO `server_statuseffects` VALUES (223208,'rampage2',1075855394,1,5000); +INSERT INTO `server_statuseffects` VALUES (223209,'fists_of_fire',1073742882,2,0); +INSERT INTO `server_statuseffects` VALUES (223210,'fists_of_earth',1073742882,2,0); +INSERT INTO `server_statuseffects` VALUES (223211,'fists_of_wind',1073742882,2,0); +INSERT INTO `server_statuseffects` VALUES (223212,'power_surge_I',1058,2,0); +INSERT INTO `server_statuseffects` VALUES (223213,'power_surge_II',1058,2,0); +INSERT INTO `server_statuseffects` VALUES (223214,'power_surge_III',1058,2,0); +INSERT INTO `server_statuseffects` VALUES (223215,'life_surge_I',1048626,2,0); +INSERT INTO `server_statuseffects` VALUES (223216,'life_surge_II',1048626,2,0); +INSERT INTO `server_statuseffects` VALUES (223217,'life_surge_III',1048626,2,0); +INSERT INTO `server_statuseffects` VALUES (223218,'dread_spike',16418,2,0); +INSERT INTO `server_statuseffects` VALUES (223221,'raging_strike2',1074855970,1,0); +INSERT INTO `server_statuseffects` VALUES (223227,'cleric_stance',8226,1,0); +INSERT INTO `server_statuseffects` VALUES (223228,'blissful_mind',1073741858,1,1000); +INSERT INTO `server_statuseffects` VALUES (223234,'sanguinerite2',0,1,3000); +INSERT INTO `server_statuseffects` VALUES (223236,'outmaneuver2',524338,2,0); +INSERT INTO `server_statuseffects` VALUES (223237,'blindside2',8226,1,0); +INSERT INTO `server_statuseffects` VALUES (223238,'decoy2',4130,2,0); +INSERT INTO `server_statuseffects` VALUES (223239,'protect2',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223240,'sanguinerite3',0,1,3000); +INSERT INTO `server_statuseffects` VALUES (223241,'bloodletter2',42,2,0); +INSERT INTO `server_statuseffects` VALUES (223242,'fully_blissful_mind',1073741858,1,0); +INSERT INTO `server_statuseffects` VALUES (223243,'magic_evasion_down',50,2,0); +INSERT INTO `server_statuseffects` VALUES (223245,'spinning_heel',50,1,0); +INSERT INTO `server_statuseffects` VALUES (223248,'divine_veil',36914,2,0); +INSERT INTO `server_statuseffects` VALUES (223250,'vengeance',16418,1,5000); +INSERT INTO `server_statuseffects` VALUES (223251,'antagonize',1048626,2,0); +INSERT INTO `server_statuseffects` VALUES (223264,'divine_regen',50,2,0); +INSERT INTO `server_statuseffects` VALUES (228021,'heavy',42,2,0); +INSERT INTO `server_statuseffects` VALUES (253003,'evade_proc',34,1,0); +INSERT INTO `server_statuseffects` VALUES (253004,'block_proc',34,1,0); +INSERT INTO `server_statuseffects` VALUES (253005,'parry_proc',34,1,0); +INSERT INTO `server_statuseffects` VALUES (253006,'miss_proc',34,1,0); +INSERT INTO `server_statuseffects` VALUES (253007,'expchain',34,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 +124,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-04-18 14:54:11