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