diff --git a/FFXIVClassic Lobby Server/Database.cs b/FFXIVClassic Lobby Server/Database.cs index deb776b4..a08da78e 100644 --- a/FFXIVClassic Lobby Server/Database.cs +++ b/FFXIVClassic Lobby Server/Database.cs @@ -219,7 +219,7 @@ namespace FFXIVClassic_Lobby_Server { MySqlCommand cmd = new MySqlCommand(); cmd.Connection = conn; - cmd.CommandText = String.Format("INSERT INTO characters_parametersave(characterId, hp, hpMax, mp, mpMax, mainSkill, mainSkillLevel) VALUES(@characterId, 1, 1, 1, 1, @mainSkill, 1);", CharacterCreatorUtils.GetClassNameForId((short)charaInfo.currentClass)); + cmd.CommandText = String.Format("INSERT INTO characters_parametersave(characterId, hp, hpMax, mp, mpMax, mainSkill, mainSkillLevel) VALUES(@characterId, 1900, 1000, 115, 115, @mainSkill, 1);", CharacterCreatorUtils.GetClassNameForId((short)charaInfo.currentClass)); cmd.Prepare(); cmd.Parameters.AddWithValue("@characterId", cid); @@ -231,15 +231,51 @@ namespace FFXIVClassic_Lobby_Server catch (MySqlException e) { Program.Log.Error(e.ToString()); - + conn.Dispose(); + return; + } + //Create Hotbar + try + { + MySqlCommand cmd = new MySqlCommand(); + cmd.Connection = conn; + cmd.CommandText = "SELECT id FROM server_battle_commands WHERE classJob = @classjob AND lvl = 1 ORDER BY id DESC"; + cmd.Prepare(); + + cmd.Parameters.AddWithValue("@classJob", charaInfo.currentClass); + List defaultActions = new List(); + using (var reader = cmd.ExecuteReader()) + { + while(reader.Read()) + { + defaultActions.Add(reader.GetUInt32("id")); + } + } + MySqlCommand cmd2 = new MySqlCommand(); + cmd2.Connection = conn; + cmd2.CommandText = "INSERT INTO characters_hotbar (characterId, classId, hotbarSlot, commandId, recastTime) VALUES (@characterId, @classId, @hotbarSlot, @commandId, 0)"; + cmd2.Prepare(); + cmd2.Parameters.AddWithValue("@characterId", cid); + cmd2.Parameters.AddWithValue("@classId", charaInfo.currentClass); + cmd2.Parameters.Add("@hotbarSlot", MySqlDbType.Int16); + cmd2.Parameters.Add("@commandId", MySqlDbType.Int16); + + for(int i = 0; i < defaultActions.Count; i++) + { + cmd2.Parameters["@hotbarSlot"].Value = i; + cmd2.Parameters["@commandId"].Value = defaultActions[i]; + cmd2.ExecuteNonQuery(); + } + } + catch(MySqlException e) + { + Program.Log.Error(e.ToString()); } finally { conn.Dispose(); } - - } Program.Log.Debug("[SQL] CID={0} state updated to active(2).", cid); diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index 995cdf20..8a0f3188 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -785,6 +785,62 @@ namespace FFXIVClassic_Map_Server } } + //Get class experience + query = @" + SELECT + pug, + gla, + mrd, + arc, + lnc, + + thm, + cnj, + + crp, + bsm, + arm, + gsm, + ltw, + wvr, + alc, + cul, + + min, + btn, + fsh + FROM characters_class_exp WHERE characterId = @charId"; + + cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@charId", player.actorId); + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + player.charaWork.battleSave.skillPoint[Player.CLASSID_PUG - 1] = reader.GetInt32("pug"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_GLA - 1] = reader.GetInt32("gla"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_MRD - 1] = reader.GetInt32("mrd"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_ARC - 1] = reader.GetInt32("arc"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_LNC - 1] = reader.GetInt32("lnc"); + + player.charaWork.battleSave.skillPoint[Player.CLASSID_THM - 1] = reader.GetInt32("thm"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_CNJ - 1] = reader.GetInt32("cnj"); + + player.charaWork.battleSave.skillPoint[Player.CLASSID_CRP - 1] = reader.GetInt32("crp"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_BSM - 1] = reader.GetInt32("bsm"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_ARM - 1] = reader.GetInt32("arm"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_GSM - 1] = reader.GetInt32("gsm"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_LTW - 1] = reader.GetInt32("ltw"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_WVR - 1] = reader.GetInt32("wvr"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_ALC - 1] = reader.GetInt32("alc"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_CUL - 1] = reader.GetInt32("cul"); + + player.charaWork.battleSave.skillPoint[Player.CLASSID_MIN - 1] = reader.GetInt32("min"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_BTN - 1] = reader.GetInt32("btn"); + player.charaWork.battleSave.skillPoint[Player.CLASSID_FSH - 1] = reader.GetInt32("fsh"); + } + } + //Load Saved Parameters query = @" SELECT @@ -1218,7 +1274,7 @@ namespace FFXIVClassic_Map_Server } } - public static void EquipAbility(Player player, ushort hotbarSlot, uint commandId, uint recastTime) + 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) @@ -1245,7 +1301,7 @@ namespace FFXIVClassic_Map_Server cmd = new MySqlCommand(query, conn); cmd.Parameters.AddWithValue("@charId", player.actorId); - cmd.Parameters.AddWithValue("@classId", player.charaWork.parameterSave.state_mainSkill[0]); + cmd.Parameters.AddWithValue("@classId", classId); cmd.Parameters.AddWithValue("@commandId", commandId); cmd.Parameters.AddWithValue("@hotbarSlot", hotbarSlot); cmd.Parameters.AddWithValue("@recastTime", recastTime); @@ -1327,7 +1383,7 @@ namespace FFXIVClassic_Map_Server cmd = new MySqlCommand(query, conn); cmd.Parameters.AddWithValue("@charId", player.actorId); - cmd.Parameters.AddWithValue("@classId", player.charaWork.parameterSave.state_mainSkill[0]); + cmd.Parameters.AddWithValue("@classId", player.GetCurrentClassOrJob()); player.charaWork.commandBorder = 32; @@ -1358,6 +1414,57 @@ namespace FFXIVClassic_Map_Server } } + public static ushort FindFirstCommandSlot(Player player, byte classId) + { + ushort slot = 0; + using (MySqlConnection conn = new MySqlConnection( + String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", + ConfigConstants.DATABASE_HOST, + ConfigConstants.DATABASE_PORT, + ConfigConstants.DATABASE_NAME, + ConfigConstants.DATABASE_USERNAME, + ConfigConstants.DATABASE_PASSWORD))) + { + try + { + conn.Open(); + MySqlCommand cmd; + string query = ""; + + //Drop + List> hotbarList = new List>(); + query = @" + SELECT hotbarSlot + FROM characters_hotbar + WHERE characterId = @charId AND classId = @classId + ORDER BY hotbarSlot + "; + cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@charId", player.actorId); + cmd.Parameters.AddWithValue("@classId", classId); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + if (slot != reader.GetUInt16("hotbarSlot")) + return slot; + + slot++; + } + } + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + return slot; + } public static List GetInventory(Player player, uint slotOffset, uint type) { List items = new List(); @@ -2146,10 +2253,8 @@ namespace FFXIVClassic_Map_Server } } - public static Dictionary LoadGlobalBattleCommandList() + public static void LoadGlobalBattleCommandList(Dictionary battleCommandDict, Dictionary, List> battleCommandIdByLevel) { - var battleCommands = new Dictionary(); - using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) { try @@ -2195,7 +2300,18 @@ namespace FFXIVClassic_Map_Server battleCommand.battleAnimation = reader.GetUInt32("battleAnimation"); battleCommand.validUser = (BattleCommandValidUser)reader.GetByte("validUser"); - battleCommands.Add(id, battleCommand); + battleCommandDict.Add(id, battleCommand); + + Tuple tuple = Tuple.Create(battleCommand.job, battleCommand.level); + if (battleCommandIdByLevel.ContainsKey(tuple)) + { + battleCommandIdByLevel[tuple].Add(id | 0xA0F00000); + } + else + { + List list = new List() { id | 0xA0F00000 }; + battleCommandIdByLevel.Add(tuple, list); + } } } } @@ -2208,7 +2324,72 @@ namespace FFXIVClassic_Map_Server conn.Dispose(); } } - return battleCommands; + } + + public static void SetExp(Player player, byte classId, int exp) + { + using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + try + { + conn.Open(); + + var query = String.Format(@" + UPDATE characters_class_exp + SET + {0} = @exp + WHERE + characterId = @characterId", CharacterUtils.GetClassNameForId(classId)); + MySqlCommand cmd = new MySqlCommand(query, conn); + + cmd.Prepare(); + cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@characterId", player.actorId); + cmd.Parameters.AddWithValue("@exp", exp); + cmd.ExecuteNonQuery(); + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + } + + public static void SetLevel(Player player, byte classId, short level) + { + using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + try + { + conn.Open(); + + var query = String.Format(@" + UPDATE characters_class_levels + SET + {0} = @lvl + WHERE + characterId = @characterId", CharacterUtils.GetClassNameForId(classId)); + MySqlCommand cmd = new MySqlCommand(query, conn); + + cmd.Prepare(); + cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@characterId", player.actorId); + cmd.Parameters.AddWithValue("@lvl", level); + cmd.ExecuteNonQuery(); + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } } } diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 794ab61b..77c61050 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -40,6 +40,7 @@ namespace FFXIVClassic_Map_Server private Dictionary currentPlayerParties = new Dictionary(); //GroupId, Party object private Dictionary statusEffectList = new Dictionary(); private Dictionary battleCommandList = new Dictionary(); + private Dictionary, List> battleCommandIdByLevel = new Dictionary, List>();//Holds battle command ids keyed by class id and level (in that order) private Dictionary battleNpcGenusMods = new Dictionary(); private Dictionary battleNpcPoolMods = new Dictionary(); private Dictionary battleNpcSpawnMods = new Dictionary(); @@ -592,8 +593,15 @@ namespace FFXIVClassic_Map_Server // todo: add to private areas, set up immunity, mob linking, // - load skill/spell/drop lists, set detection icon, load pool/family/group mods + var allegiance = (CharacterTargetingAllegiance)reader.GetByte("allegiance"); + BattleNpc battleNpc = null; - var battleNpc = new BattleNpc(actorId, Server.GetWorldManager().GetActorClass(reader.GetUInt32("actorClassId")), + if (allegiance == CharacterTargetingAllegiance.Player) + battleNpc = new Ally(actorId, Server.GetWorldManager().GetActorClass(reader.GetUInt32("actorClassId")), + reader.GetString("scriptName"), area, reader.GetFloat("positionX"), reader.GetFloat("positionY"), reader.GetFloat("positionZ"), reader.GetFloat("rotation"), + reader.GetUInt16("actorState"), reader.GetUInt32("animationId"), ""); + else + battleNpc = new BattleNpc(actorId, Server.GetWorldManager().GetActorClass(reader.GetUInt32("actorClassId")), reader.GetString("scriptName"), area, reader.GetFloat("positionX"), reader.GetFloat("positionY"), reader.GetFloat("positionZ"), reader.GetFloat("rotation"), reader.GetUInt16("actorState"), reader.GetUInt32("animationId"), ""); @@ -639,7 +647,8 @@ namespace FFXIVClassic_Map_Server battleNpc.dropListId = reader.GetUInt32("dropListId"); battleNpc.spellListId = reader.GetUInt32("spellListId"); battleNpc.skillListId = reader.GetUInt32("skillListId"); - + battleNpc.SetMaxHP(1000); + battleNpc.SetHP(1000); battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId")); battleNpc.CalculateBaseStats(); battleNpc.RecalculateStats(); @@ -1290,7 +1299,9 @@ namespace FFXIVClassic_Map_Server { Program.Tick = DateTime.Now; foreach (Zone zone in zoneList.Values) + { zone.Update(Program.Tick); + } Program.LastTick = Program.Tick; } } @@ -1424,7 +1435,7 @@ namespace FFXIVClassic_Map_Server public void LoadBattleCommands() { - battleCommandList = Database.LoadGlobalBattleCommandList(); + Database.LoadGlobalBattleCommandList(battleCommandList, battleCommandIdByLevel); } public BattleCommand GetBattleCommand(uint id) @@ -1432,5 +1443,11 @@ namespace FFXIVClassic_Map_Server BattleCommand battleCommand; return battleCommandList.TryGetValue((ushort)id, out battleCommand) ? battleCommand.Clone() : null; } + + public List GetBattleCommandIdByLevel(byte classId, short level) + { + List ids; + return battleCommandIdByLevel.TryGetValue(Tuple.Create(classId, level), out ids) ? ids : new List(); + } } } diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index 313b62cc..15b70eb3 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -61,7 +61,7 @@ namespace FFXIVClassic_Map_Server.Actors public List classParams; public List positionUpdates; - public DateTime lastMoveUpdate; + protected DateTime lastUpdateScript; protected DateTime lastUpdate; public Actor target; @@ -438,6 +438,9 @@ namespace FFXIVClassic_Map_Server.Actors updateFlags = ActorUpdateFlags.None; zone.BroadcastPacketsAroundActor(this, packets); + + SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp"); + hpInfo.AddTarget(); } } diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index 7e657d1a..2d245fe3 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -421,6 +421,11 @@ namespace FFXIVClassic_Map_Server.Actors return GetAllActors(); } + public virtual List GetAllies() + { + return GetAllActors(); + } + public void BroadcastPacketsAroundActor(Actor actor, List packets) { foreach (SubPacket packet in packets) @@ -472,7 +477,7 @@ namespace FFXIVClassic_Map_Server.Actors } } - public Npc SpawnActor(uint classId, string uniqueId, float x, float y, float z, float rot = 0, ushort state = 0, uint animId = 0, bool isMob = true) + public Npc SpawnActor(uint classId, string uniqueId, float x, float y, float z, float rot = 0, ushort state = 0, uint animId = 0, bool isMob = false) { lock (mActorList) { @@ -489,13 +494,14 @@ namespace FFXIVClassic_Map_Server.Actors zoneId = actorId; Npc npc; - if (isMob) npc = new BattleNpc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null); else npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null); npc.LoadEventConditions(actorClass.eventConditions); + npc.SetMaxHP(300); + npc.SetHP(300); AddActorToZone(npc); @@ -661,11 +667,14 @@ namespace FFXIVClassic_Map_Server.Actors { lock (mActorList) { - foreach (Actor a in mActorList.Values) + foreach (Actor a in mActorList.Values.ToList()) a.Update(tick); - var deltaTime = (tick - Program.LastTick).TotalMilliseconds; - LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, deltaTime, this); + if ((tick - lastUpdateScript).TotalMilliseconds > 1500) + { + LuaEngine.GetInstance().CallLuaFunctionForReturn(LuaEngine.GetScriptPath(this), "onUpdate", true, this, tick); + lastUpdateScript = tick; + } } } diff --git a/FFXIVClassic Map Server/actors/area/Zone.cs b/FFXIVClassic Map Server/actors/area/Zone.cs index 27cb19ab..1286657f 100644 --- a/FFXIVClassic Map Server/actors/area/Zone.cs +++ b/FFXIVClassic Map Server/actors/area/Zone.cs @@ -152,6 +152,7 @@ namespace FFXIVClassic_Map_Server.actors.area contentAreas.Add(areaName, new List()); PrivateAreaContent contentArea = new PrivateAreaContent(this, classPath, areaName, 1, director, starterPlayer); contentAreas[areaName].Add(contentArea); + return contentArea; } } @@ -168,6 +169,14 @@ namespace FFXIVClassic_Map_Server.actors.area { base.Update(tick); + foreach (var a in privateAreas.Values) + foreach(var b in a.Values) + b.Update(tick); + + foreach (var a in contentAreas.Values) + foreach (var b in a) + b.Update(tick); + // todo: again, this is retarded but debug stuff var diffTime = tick - lastUpdate; diff --git a/FFXIVClassic Map Server/actors/chara/BattleSave.cs b/FFXIVClassic Map Server/actors/chara/BattleSave.cs index bf9a6000..8d2928e4 100644 --- a/FFXIVClassic Map Server/actors/chara/BattleSave.cs +++ b/FFXIVClassic Map Server/actors/chara/BattleSave.cs @@ -5,7 +5,7 @@ public float potencial = 6.6f; public short[] skillLevel = new short[52]; public short[] skillLevelCap = new short[52]; - public short[] skillPoint = new short[52]; + public int[] skillPoint = new int[52]; public short physicalLevel; public int physicalExp; diff --git a/FFXIVClassic Map Server/actors/chara/CharaWork.cs b/FFXIVClassic Map Server/actors/chara/CharaWork.cs index 652178d6..45afc964 100644 --- a/FFXIVClassic Map Server/actors/chara/CharaWork.cs +++ b/FFXIVClassic Map Server/actors/chara/CharaWork.cs @@ -23,7 +23,7 @@ public uint[] command = new uint[64]; //ACTORS public byte[] commandCategory = new byte[64]; public byte commandBorder = 0x20; - public bool[] commandAcquired = new bool[4096]; + public bool[] commandAcquired = new bool[4096]; public bool[] additionalCommandAcquired = new bool[36]; public uint currentContentGroup; diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 618c3b18..6c497bdf 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -45,6 +45,19 @@ namespace FFXIVClassic_Map_Server.Actors public const int CLASSID_THM = 22; public const int CLASSID_CNJ = 23; + public const int CLASSID_CRP = 29; + public const int CLASSID_BSM = 30; + public const int CLASSID_ARM = 31; + public const int CLASSID_GSM = 32; + public const int CLASSID_LTW = 33; + public const int CLASSID_WVR = 34; + public const int CLASSID_ALC = 35; + public const int CLASSID_CUL = 36; + + public const int CLASSID_MIN = 39; + public const int CLASSID_BTN = 40; + public const int CLASSID_FSH = 41; + public const int SIZE = 0; public const int COLORINFO = 1; public const int FACEINFO = 2; @@ -123,7 +136,6 @@ namespace FFXIVClassic_Map_Server.Actors this.statusEffects = new StatusEffectContainer(this); // todo: move this somewhere more appropriate - ResetMoveSpeeds(); // todo: base this on equip and shit SetMod((uint)Modifier.AttackRange, 3); SetMod((uint)Modifier.AttackDelay, (Program.Random.Next(30, 60) * 100)); @@ -264,22 +276,8 @@ namespace FFXIVClassic_Map_Server.Actors public void FollowTarget(Actor target, float stepSize = 1.2f, int maxPath = 25, float radius = 0.0f) { - var player = target as Player; - - if (player != null) - { - if (this.target != player) - { - this.target = target; - } - // todo: move this to own function thing - this.oldMoveState = this.moveState; - this.moveState = 2; - updateFlags |= ActorUpdateFlags.Position | ActorUpdateFlags.Speed; - //this.moveSpeeds = player.moveSpeeds; - - PathTo(player.positionX, player.positionY, player.positionZ, stepSize, maxPath, radius); - } + if (target != null) + PathTo(target.positionX, target.positionY, target.positionZ, stepSize, maxPath, radius); } public Int64 GetMod(uint modifier) @@ -347,7 +345,7 @@ namespace FFXIVClassic_Map_Server.Actors public virtual bool IsValidTarget(Character target, ValidTarget validTarget) { - return true; + return !target.isStatic; } public virtual bool CanAttack() @@ -401,6 +399,7 @@ namespace FFXIVClassic_Map_Server.Actors aiContainer.Engage(zone.FindActorInArea(targid)); } } + return false; } @@ -470,7 +469,7 @@ namespace FFXIVClassic_Map_Server.Actors public bool IsAlive() { - return !aiContainer.IsDead() && GetHP() > 0; + return !aiContainer.IsDead();// && GetHP() > 0; } public short GetHP() @@ -511,7 +510,7 @@ namespace FFXIVClassic_Map_Server.Actors public byte GetHPP() { - return (byte)((charaWork.parameterSave.hp[0] / charaWork.parameterSave.hpMax[0]) * 100); + return (byte)(charaWork.parameterSave.hp[0] == 0 ? 0 : (charaWork.parameterSave.hp[0] / charaWork.parameterSave.hpMax[0]) * 100); } public void SetHP(uint hp) @@ -570,7 +569,11 @@ 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 (tpBase >= 1000) + lua.LuaEngine.GetInstance().OnSignal("tpOver1000"); } public void DelHP(int hp) @@ -598,7 +601,6 @@ namespace FFXIVClassic_Map_Server.Actors { if (GetMod((uint)Modifier.Hp) != 0) { - } // todo: recalculate stats and crap updateFlags |= ActorUpdateFlags.HpTpMp; @@ -637,7 +639,12 @@ namespace FFXIVClassic_Map_Server.Actors // todo: call onAttack/onDamageTaken target.DelHP(action.amount); if (target is BattleNpc) + { ((BattleNpc)target).lastAttacker = this; + ((BattleNpc)target).hateContainer.UpdateHate(this, action.amount); + } + AddTP(115); + target.AddTP(100); } public virtual void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors) @@ -734,7 +741,7 @@ namespace FFXIVClassic_Map_Server.Actors public bool IsMonster() { - return this is BattleNpc && !IsAlly(); + return this is BattleNpc; } public bool IsPet() @@ -746,6 +753,26 @@ namespace FFXIVClassic_Map_Server.Actors { return this is Ally; } + + public bool IsDiscipleOfWar() + { + return currentJob < CLASSID_THM; + } + + public bool IsDiscipleOfMagic() + { + return currentJob >= CLASSID_THM && currentJob < CLASSID_CRP; + } + + public bool IsDiscipleOfHand() + { + return currentJob >= CLASSID_CRP && currentJob < CLASSID_MIN; + } + + public bool IsDiscipleOfLand() + { + return currentJob >= CLASSID_MIN; + } #endregion lua helpers #endregion ai stuff } diff --git a/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs b/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs index 8a01284c..dc04bb46 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/BattleCommand.cs @@ -233,7 +233,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai if (mpCost != 0) return (ushort)Math.Ceiling((cost * mpCost * 0.001)); - return tpCost; + return mpCost != 0 ? (ushort)Math.Ceiling((cost * mpCost * 0.001)) : (ushort)0; } public List GetTargets() diff --git a/FFXIVClassic Map Server/actors/chara/ai/HateContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/HateContainer.cs index d5a0904b..47086f68 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/HateContainer.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/HateContainer.cs @@ -39,15 +39,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai { if (!HasHateForTarget(target)) hateList.Add(target, new HateEntry(target, 1, 0, true)); - else - Program.Log.Error($"{target.actorName} is already on [{owner.actorId}]{owner.actorName}'s hate list!"); } public void UpdateHate(Character target, int damage) { - if (!HasHateForTarget(target)) - AddBaseHate(target); - + AddBaseHate(target); //hateList[target].volatileEnmity += (uint)damage; hateList[target].cumulativeEnmity += (uint)damage; } @@ -55,13 +51,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai public void ClearHate(Character target = null) { if (target != null) - { hateList.Remove(target); - } else - { hateList.Clear(); - } } private void UpdateHate(HateEntry entry) diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/AllyController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/AllyController.cs index b5820871..5f748f72 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/AllyController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/AllyController.cs @@ -34,5 +34,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers } base.DoCombatTick(tick, contentGroupCharas); } + protected override void DoRoamTick(DateTime tick, List contentGroupCharas = null) + { + if (owner.currentContentGroup != null) + { + contentGroupCharas = new List(owner.currentContentGroup.GetMemberCount()); + foreach (var charaId in owner.currentContentGroup.GetMembers()) + { + var chara = owner.zone.FindActorInArea(charaId); + + if (chara != null) + contentGroupCharas.Add(chara); + } + } + base.DoRoamTick(tick, contentGroupCharas); + } } } diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs index 43359fed..19356009 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/BattleNpcController.cs @@ -102,8 +102,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers owner.aiContainer.pathFind.PreparePath(owner.spawnX, owner.spawnY, owner.spawnZ, 1.5f, 10); neutralTime = lastActionTime; owner.hateContainer.ClearHate(); - owner.ResetMoveSpeeds(); - owner.moveState = 1; lua.LuaEngine.CallLuaBattleFunction(owner, "onDisengage", owner, target, Utils.UnixTimeStampUTC(lastUpdate)); } @@ -127,7 +125,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers // todo: } - protected virtual void DoRoamTick(DateTime tick) + protected virtual void DoRoamTick(DateTime tick, List contentGroupCharas = null) { if (owner.hateContainer.GetHateList().Count > 0) { @@ -159,6 +157,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers owner.aiContainer.pathFind.SetPathFlags(PathFindFlags.None); owner.aiContainer.pathFind.PathInRange(owner.spawnX, owner.spawnY, owner.spawnZ, 1.5f, 50.0f); } + lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas); } @@ -194,7 +193,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers protected virtual void DoCombatTick(DateTime tick, List contentGroupCharas = null) { HandleHate(); - // todo: magic/attack/ws cooldowns etc if (TryDeaggro()) { @@ -203,7 +201,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers } Move(); - lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas); + if ((tick - lastCombatTickScript).TotalSeconds > 2) + { + lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas); + lastCombatTickScript = tick; + } } protected virtual void Move() diff --git a/FFXIVClassic Map Server/actors/chara/ai/controllers/Controller.cs b/FFXIVClassic Map Server/actors/chara/ai/controllers/Controller.cs index e91bf4ed..ab194953 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/controllers/Controller.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/controllers/Controller.cs @@ -11,6 +11,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers { protected Character owner; + protected DateTime lastCombatTickScript; protected DateTime lastUpdate; public bool canUpdate = true; protected bool autoAttackEnabled = true; diff --git a/FFXIVClassic Map Server/actors/chara/ai/helpers/PathFind.cs b/FFXIVClassic Map Server/actors/chara/ai/helpers/PathFind.cs index 9af398f0..399bb9b5 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/helpers/PathFind.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/helpers/PathFind.cs @@ -44,7 +44,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai { var pos = new Vector3(owner.positionX, owner.positionY, owner.positionZ); var dest = new Vector3(x, y, z); - var zone = (Zone)owner.GetZone(); + + Zone zone; + if (owner.GetZone() is PrivateArea || owner.GetZone() is PrivateAreaContent) + zone = (Zone)((PrivateArea)owner.GetZone()).GetParentZone(); + else + zone = (Zone)owner.GetZone(); + var sw = new System.Diagnostics.Stopwatch(); sw.Start(); diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs index 0a925e53..79f63178 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/AttackState.cs @@ -38,7 +38,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state if (target == null || target.IsDead()) { - if (owner is BattleNpc) + if (owner.IsMonster() || owner.IsAlly()) target = ((BattleNpc)owner).hateContainer.GetMostHatedTarget(); } else @@ -141,7 +141,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state // todo: shouldnt need to check if owner is dead since all states would be cleared if (owner.IsDead() || target.IsDead()) { - if (owner is BattleNpc) + if (owner.IsMonster() || owner.IsAlly()) ((BattleNpc)owner).hateContainer.ClearHate(target); owner.aiContainer.ChangeTarget(null); diff --git a/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs b/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs index e9b05019..f918c8be 100644 --- a/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs +++ b/FFXIVClassic Map Server/actors/chara/ai/state/WeaponSkillState.cs @@ -106,7 +106,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state var errors = (BattleAction[])actions.Clone(); owner.OnWeaponSkill(this, actions, ref errors); - owner.DoBattleAction(skill.id, 0, actions); + owner.DoBattleAction(skill.id, skill.battleAnimation, actions); } public override void TryInterrupt() diff --git a/FFXIVClassic Map Server/actors/chara/npc/Ally.cs b/FFXIVClassic Map Server/actors/chara/npc/Ally.cs index 5ef2546b..d8924544 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Ally.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Ally.cs @@ -18,6 +18,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.npc { aiContainer = new AIContainer(this, new AllyController(this), new PathFind(this), new TargetFind(this)); this.allegiance = CharacterTargetingAllegiance.Player; + this.isAutoAttackEnabled = true; + this.isMovingToSpawn = false; } } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs index e90bb91f..160f8050 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/BattleNpc.cs @@ -82,7 +82,7 @@ namespace FFXIVClassic_Map_Server.Actors //charaWork.property[2] = 1; //npcWork.hateType = 1; - + this.hateContainer = new HateContainer(this); this.allegiance = CharacterTargetingAllegiance.BattleNpcs; @@ -90,12 +90,7 @@ namespace FFXIVClassic_Map_Server.Actors spawnY = posY; spawnZ = posZ; - // todo: read these from db also - detectionType = DetectionType.Sight; - this.moveState = 2; - ResetMoveSpeeds(); despawnTime = 10; - respawnTime = 30; CalculateBaseStats(); } @@ -245,19 +240,7 @@ namespace FFXIVClassic_Map_Server.Actors { if (respawnTime > 0) { - base.Spawn(tick); - - this.isMovingToSpawn = false; - this.ResetMoveSpeeds(); - this.hateContainer.ClearHate(); - zone.BroadcastPacketsAroundActor(this, GetSpawnPackets(null, 0x01)); - zone.BroadcastPacketsAroundActor(this, GetInitPackets()); - charaWork.parameterSave.hp = charaWork.parameterSave.hpMax; - charaWork.parameterSave.mp = charaWork.parameterSave.mpMax; - RecalculateStats(); - - OnSpawn(); - updateFlags |= ActorUpdateFlags.AllNpc; + ForceRespawn(); } } @@ -266,7 +249,6 @@ namespace FFXIVClassic_Map_Server.Actors base.Spawn(Program.Tick); this.isMovingToSpawn = false; - this.ResetMoveSpeeds(); this.hateContainer.ClearHate(); zone.BroadcastPacketsAroundActor(this, GetSpawnPackets(null, 0x01)); zone.BroadcastPacketsAroundActor(this, GetInitPackets()); @@ -298,7 +280,13 @@ namespace FFXIVClassic_Map_Server.Actors // onDeath(monster, player, killer) lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker); // defeat/defeats - ((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0))); + + if (lastAttacker is Player) + ((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0))); + + if(partyMember is Player) + ((Player)partyMember).AddExp(1500, (byte)partyMember.GetJob(), 5); + } } else @@ -310,9 +298,9 @@ namespace FFXIVClassic_Map_Server.Actors } positionUpdates?.Clear(); aiContainer.InternalDie(tick, despawnTime); - this.ResetMoveSpeeds(); - // todo: reset cooldowns + + lua.LuaEngine.GetInstance().OnSignal("mobkill"); } else { @@ -357,7 +345,6 @@ namespace FFXIVClassic_Map_Server.Actors { this.isMovingToSpawn = false; } - lua.LuaEngine.CallLuaBattleFunction(this, "onRoam", this); } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs index 8feb3f51..31c9541d 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs @@ -80,7 +80,7 @@ namespace FFXIVClassic_Map_Server.Actors charaWork.parameterSave.hpMax[0] = 80; } for (int i = 0; i < 32; i++ ) - charaWork.property[i] = (byte)(((int)actorClass.propertyFlags >> i) & 1); + charaWork.property[i] = (byte)(((int)actorClass.propertyFlags >> i) & 1); npcWork.pushCommand = actorClass.pushCommand; npcWork.pushCommandSub = actorClass.pushCommandSub; diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index fab60a05..f71843b8 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -31,19 +31,6 @@ namespace FFXIVClassic_Map_Server.Actors { class Player : Character { - public const int CLASSID_CRP = 29; - public const int CLASSID_BSM = 30; - public const int CLASSID_ARM = 31; - public const int CLASSID_GSM = 32; - public const int CLASSID_LTW = 33; - public const int CLASSID_WVR = 34; - public const int CLASSID_ALC = 35; - public const int CLASSID_CUL = 36; - - public const int CLASSID_MIN = 39; - public const int CLASSID_BTN = 40; - public const int CLASSID_FSH = 41; - public const int MAXSIZE_INVENTORY_NORMAL = 200; public const int MAXSIZE_INVENTORY_CURRANCY = 320; public const int MAXSIZE_INVENTORY_KEYITEMS = 500; @@ -234,7 +221,7 @@ namespace FFXIVClassic_Map_Server.Actors charaWork.commandBorder = 0x20; - charaWork.parameterTemp.tp = 3000; + charaWork.parameterTemp.tp = 0; Database.LoadPlayerCharacter(this); lastPlayTimeUpdate = Utils.UnixTimeStampUTC(); @@ -387,13 +374,13 @@ namespace FFXIVClassic_Map_Server.Actors propPacketUtil.AddProperty("charaWork.battleTemp.castGauge_speed[0]"); propPacketUtil.AddProperty("charaWork.battleTemp.castGauge_speed[1]"); - + //Battle Save Skillpoint - + propPacketUtil.AddProperty($"charaWork.battleSave.skillPoint[{charaWork.parameterSave.state_mainSkill[0] - 1}]"); + //Commands propPacketUtil.AddProperty("charaWork.commandBorder"); - for (int i = 0; i < charaWork.command.Length; i++) { if (charaWork.command[i] != 0) @@ -407,7 +394,6 @@ namespace FFXIVClassic_Map_Server.Actors } } } - for (int i = 0; i < charaWork.commandCategory.Length; i++) { @@ -421,7 +407,6 @@ namespace FFXIVClassic_Map_Server.Actors if (charaWork.commandAcquired[i] != false) propPacketUtil.AddProperty(String.Format("charaWork.commandAcquired[{0}]", i)); } - for (int i = 0; i < charaWork.additionalCommandAcquired.Length; i++) { @@ -436,13 +421,11 @@ namespace FFXIVClassic_Map_Server.Actors propPacketUtil.AddProperty(String.Format("charaWork.parameterSave.commandSlot_compatibility[{0}]", i)); } - /* - for (int i = 0; i < charaWork.parameterSave.commandSlot_recastTime.Length; i++) - { - if (charaWork.parameterSave.commandSlot_recastTime[i] != 0) - propPacketUtil.AddProperty(String.Format("charaWork.parameterSave.commandSlot_recastTime[{0}]", i)); - } - */ + for (int i = 0; i < charaWork.parameterSave.commandSlot_recastTime.Length; i++) + { + if (charaWork.parameterSave.commandSlot_recastTime[i] != 0) + propPacketUtil.AddProperty(String.Format("charaWork.parameterSave.commandSlot_recastTime[{0}]", i)); + } //System propPacketUtil.AddProperty("charaWork.parameterTemp.forceControl_float_forClientSelf[0]"); @@ -956,11 +939,11 @@ namespace FFXIVClassic_Map_Server.Actors //Calculate hp/mp //Get Potenciel ?????? - + //Set HP/MP/TP PARAMS //Set mainskill and level - + //Set Parameters //Set current EXP @@ -972,7 +955,6 @@ namespace FFXIVClassic_Map_Server.Actors //Check if bonus point available... set //Set rested EXP - charaWork.parameterSave.state_mainSkill[0] = classId; charaWork.parameterSave.state_mainSkillLevel = charaWork.battleSave.skillLevel[classId-1]; playerWork.restBonusExpRate = 0.0f; @@ -988,7 +970,8 @@ namespace FFXIVClassic_Map_Server.Actors propertyBuilder.AddProperty("charaWork.parameterSave.state_mainSkillLevel"); propertyBuilder.NewTarget("playerWork/expBonus"); propertyBuilder.AddProperty("playerWork.restBonusExpRate"); - + propertyBuilder.NewTarget("charaWork/battleStateForSelf"); + propertyBuilder.AddProperty($"charaWork.battleSave.skillPoint[{classId - 1}]"); Database.LoadHotbar(this); var time = Utils.UnixTimeStampUTC(); @@ -1781,9 +1764,13 @@ namespace FFXIVClassic_Map_Server.Actors propPacketUtil.AddProperty($"charaWork.parameterSave.state_mainSkillLevel"); packets.AddRange(propPacketUtil.Done()); + } base.PostUpdate(tick, packets); + SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp"); + hpInfo.AddTarget(); + QueuePacket(hpInfo.BuildPacket(actorId)); } public override void Die(DateTime tick) @@ -1817,19 +1804,20 @@ namespace FFXIVClassic_Map_Server.Actors ActorPropertyPacketUtil compatibiltyUtil = new ActorPropertyPacketUtil("charaWork/commandDetailForSelf", this); foreach (ushort slot in slotsToUpdate) { - propPacketUtil.AddProperty(String.Format("charaWork.command[{0}]", slot)); - propPacketUtil.AddProperty(String.Format("charaWork.commandCategory[{0}]", slot)); + propPacketUtil.AddProperty($"charaWork.command[{slot}]"); + propPacketUtil.AddProperty($"charaWork.commandCategory[{slot}]"); } + propPacketUtil.NewTarget("charaWork/commandDetailForSelf"); //Enable or disable slots based on whether there is an ability in that slot foreach (ushort slot in slotsToUpdate) { charaWork.parameterSave.commandSlot_compatibility[slot - charaWork.commandBorder] = charaWork.command[slot] != 0; - compatibiltyUtil.AddProperty(String.Format("charaWork.parameterSave.commandSlot_compatibility[{0}]", slot - charaWork.commandBorder)); + propPacketUtil.AddProperty($"charaWork.parameterSave.commandSlot_compatibility[{slot - charaWork.commandBorder}]"); } QueuePackets(propPacketUtil.Done()); - QueuePackets(compatibiltyUtil.Done()); + //QueuePackets(compatibiltyUtil.Done()); } //Update recast timers for the passed in hotbar slots @@ -1846,81 +1834,89 @@ namespace FFXIVClassic_Map_Server.Actors QueuePackets(recastPacketUtil.Done()); } - public void EquipAbility(ushort hotbarSlot, ushort commandId) + //Find the first open slot in classId's hotbar and equip an ability there. + public void EquipAbilityInFirstOpenSlot(byte classId, uint commandId, bool printMessage = true) + { + //Find first open slot on class's hotbar slot, then call EquipAbility with that slot. + ushort hotbarSlot = 0; + + //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 + public void EquipAbility(byte classId, uint commandId, ushort hotbarSlot, bool printMessage = true) { var ability = Server.GetWorldManager().GetBattleCommand(commandId); uint trueCommandId = commandId | 0xA0F00000; - ushort trueHotbarSlot = (ushort)(hotbarSlot + charaWork.commandBorder - 1); - ushort endOfHotbar = (ushort)(charaWork.commandBorder + 30); + ushort lowHotbarSlot = (ushort)(hotbarSlot - charaWork.commandBorder); + ushort maxRecastTime = (ushort)ability.recastTimeSeconds; + uint recastEnd = Utils.UnixTimeStampUTC() + maxRecastTime; List slotsToUpdate = new List(); - bool canEquip = true; - - //If the ability is already equipped we need this so we can move its recast timer to the new slot - uint oldRecast = 0; - //Check if the command is already on the hotbar - ushort oldSlot = FindFirstCommandSlotById(trueCommandId); - bool isAlreadyEquipped = oldSlot < endOfHotbar; - - //New ability being added to the hotbar, set truehotbarslot to the first open slot. - if (hotbarSlot == 0) + + Database.EquipAbility(this, classId, (ushort) (hotbarSlot - charaWork.commandBorder), commandId, recastEnd); + //If the class we're equipping for is the current class (need to find out if state_mainSkill is supposed to change when you're a job) + //then equip the ability in charawork.commands and save in databse, otherwise just save in database + if (classId == charaWork.parameterSave.state_mainSkill[0]) { - //If the ability is already equipped, we can't add it to the hotbar again. - if (isAlreadyEquipped) - canEquip = false; - else - trueHotbarSlot = FindFirstCommandSlotById(0); - } - //If the slot we're moving an command to already has an command there, move that command to the new command's old slot. - //Only need to do this if the new command is already equipped, otherwise we just write over the command there - else if (charaWork.command[trueHotbarSlot] != trueCommandId && isAlreadyEquipped) - { - //Move the command to oldslot - charaWork.command[oldSlot] = charaWork.command[trueHotbarSlot]; - //Move recast timers to old slot as well and store the old recast timer - oldRecast = charaWork.parameterSave.commandSlot_recastTime[oldSlot - charaWork.commandBorder]; - charaWork.parameterTemp.maxCommandRecastTime[oldSlot - charaWork.commandBorder] = charaWork.parameterTemp.maxCommandRecastTime[trueHotbarSlot - charaWork.commandBorder]; - charaWork.parameterSave.commandSlot_recastTime[oldSlot - charaWork.commandBorder] = charaWork.parameterSave.commandSlot_recastTime[trueHotbarSlot - charaWork.commandBorder]; - //Save changes - Database.EquipAbility(this, (ushort)(oldSlot - charaWork.commandBorder), charaWork.command[oldSlot], charaWork.parameterSave.commandSlot_recastTime[oldSlot - charaWork.commandBorder]); - slotsToUpdate.Add(oldSlot); + charaWork.command[hotbarSlot] = trueCommandId; + charaWork.commandCategory[hotbarSlot] = 1; + charaWork.parameterTemp.maxCommandRecastTime[lowHotbarSlot] = maxRecastTime; + charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot] = recastEnd; + + slotsToUpdate.Add(hotbarSlot); + UpdateHotbar(slotsToUpdate); } - if (canEquip) - { - charaWork.command[trueHotbarSlot] = trueCommandId; - charaWork.commandCategory[trueHotbarSlot] = 1; - //Set recast time. If the ability was already equipped, then we use the previous recast timer instead of setting a new one - ushort maxRecastTime = (ushort)ability.recastTimeSeconds; - uint recastEnd = isAlreadyEquipped ? oldRecast : Utils.UnixTimeStampUTC() + maxRecastTime; - charaWork.parameterTemp.maxCommandRecastTime[trueHotbarSlot - charaWork.commandBorder] = maxRecastTime; - charaWork.parameterSave.commandSlot_recastTime[trueHotbarSlot - charaWork.commandBorder] = recastEnd; - slotsToUpdate.Add(trueHotbarSlot); + if(printMessage) + SendGameMessage(Server.GetWorldManager().GetActor(), 30603, 0x20, 0, commandId); + } - Database.EquipAbility(this, (ushort) (trueHotbarSlot - charaWork.commandBorder), trueCommandId, recastEnd); + //Doesn't take a classId because the only way to swap abilities is through the ability equip widget oe /eaction, which only apply to current class + //hotbarSlot 1 and 2 are 32-indexed. + public void SwapAbilities(ushort hotbarSlot1, ushort hotbarSlot2) + { + uint lowHotbarSlot1 = (ushort)(hotbarSlot1 - charaWork.commandBorder); + uint lowHotbarSlot2 = (ushort)(hotbarSlot2 - charaWork.commandBorder); + + //Store information about first command + uint commandId = charaWork.command[hotbarSlot1]; + uint recastEnd = charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot1]; + ushort recastMax = charaWork.parameterTemp.maxCommandRecastTime[lowHotbarSlot1]; - //"[Command] set." - if (!isAlreadyEquipped) - SendGameMessage(Server.GetWorldManager().GetActor(), 30603, 0x20, 0, commandId); - } - //Ability is already equipped - else if (isAlreadyEquipped) - { - //"That action is already set to an action slot." - SendGameMessage(Server.GetWorldManager().GetActor(), 30719, 0x20, 0); - } - //Hotbar full - else - { - //"You cannot set any more actions." - SendGameMessage(Server.GetWorldManager().GetActor(), 30720, 0x20, 0); - } + //Move second command's info to first hotbar slot + charaWork.command[hotbarSlot1] = charaWork.command[hotbarSlot2]; + charaWork.parameterTemp.maxCommandRecastTime[lowHotbarSlot1] = charaWork.parameterTemp.maxCommandRecastTime[lowHotbarSlot2]; + charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot1] = charaWork.parameterSave.commandSlot_recastTime[lowHotbarSlot2]; + //Move first command's info to second slot + 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]); + + List slotsToUpdate = new List(); + slotsToUpdate.Add(hotbarSlot1); + slotsToUpdate.Add(hotbarSlot2); UpdateHotbar(slotsToUpdate); } - - public void UnequipAbility(ushort hotbarSlot) + public void UnequipAbility(ushort hotbarSlot, bool printMessage = true) { List slotsToUpdate = new List(); ushort trueHotbarSlot = (ushort)(hotbarSlot + charaWork.commandBorder - 1); @@ -1928,14 +1924,16 @@ namespace FFXIVClassic_Map_Server.Actors Database.UnequipAbility(this, (ushort)(trueHotbarSlot - charaWork.commandBorder)); charaWork.command[trueHotbarSlot] = 0; slotsToUpdate.Add(trueHotbarSlot); - SendGameMessage(Server.GetWorldManager().GetActor(), 30604, 0x20, 0, commandId ^ 0xA0F00000); + + if(printMessage) + SendGameMessage(Server.GetWorldManager().GetActor(), 30604, 0x20, 0, commandId ^ 0xA0F00000); UpdateHotbar(slotsToUpdate); } //Finds the first hotbar slot with a given commandId. //If the returned value is outside the hotbar, it indicates it wasn't found. - private ushort FindFirstCommandSlotById(uint commandId) + public ushort FindFirstCommandSlotById(uint commandId) { if(commandId != 0) commandId |= 0xA0F00000; @@ -2178,6 +2176,8 @@ namespace FFXIVClassic_Map_Server.Actors { ((BattleNpc)target).hateContainer.UpdateHate(this, action.amount); } + + LuaEngine.GetInstance().OnSignal("playerAttack"); } public override void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors) @@ -2188,6 +2188,7 @@ namespace FFXIVClassic_Map_Server.Actors // 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"); } public override void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors) @@ -2199,6 +2200,137 @@ namespace FFXIVClassic_Map_Server.Actors UpdateHotbarTimer(skill.id, skill.recastTimeSeconds); // 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) + { + base.OnAbility(state, actions, ref errors); + + LuaEngine.GetInstance().OnSignal("abilityUse"); + } + + //Handles exp being added, does not handle figuring out exp bonus from buffs or skill/link chains or any of that + public void AddExp(int exp, byte classId, int bonusPercent = 0) + { + exp += (int) Math.Ceiling((exp * bonusPercent / 100.0f)); + //You earn [exp](+[bonusPercent]%) experience point(s). + SendGameMessage(this, Server.GetWorldManager().GetActor(), 33934, 0x44, this, 0, 0, 0, 0, 0, 0, 0, 0, 0, exp, "", bonusPercent); + bool leveled = false; + int diff = MAXEXP[GetLevel() - 1] - charaWork.battleSave.skillPoint[classId - 1]; + //While there is enough experience to level up, keep leveling up, unlocking skills and removing experience from exp until we don't have enough to level up + while (exp >= diff && GetLevel() < charaWork.battleSave.skillLevelCap[classId]) + { + + //Level up + LevelUp(classId); + leveled = true; + //Reduce exp based on how much exp is needed to level + exp -= diff; + diff = MAXEXP[GetLevel() - 1]; + } + + if(leveled) + { + //Set exp to current class to 0 so that exp is added correctly + charaWork.battleSave.skillPoint[classId - 1] = 0; + //send new level + ActorPropertyPacketUtil expPropertyPacket2 = new ActorPropertyPacketUtil("charaWork/exp", this); + ActorPropertyPacketUtil expPropertyPacket3 = new ActorPropertyPacketUtil("charaWork/stateForAll", this); + expPropertyPacket2.AddProperty($"charaWork.battleSave.skillLevel[{classId - 1}]"); + expPropertyPacket2.AddProperty($"charaWork.parameterSave.state_mainSkillLevel"); + QueuePackets(expPropertyPacket2.Done()); + QueuePackets(expPropertyPacket3.Done()); + //play levelup animation (do this outside LevelUp so that it only plays once if multiple levels are earned + //also i dunno how to do this + + Database.SetLevel(this, classId, GetLevel()); + Database.SavePlayerCurrentClass(this); + } + //Cap experience for level 50 + charaWork.battleSave.skillPoint[classId - 1] = Math.Min(charaWork.battleSave.skillPoint[classId - 1] + exp, MAXEXP[GetLevel() - 1]); + + ActorPropertyPacketUtil expPropertyPacket = new ActorPropertyPacketUtil("charaWork/battleStateForSelf", this); + expPropertyPacket.AddProperty($"charaWork.battleSave.skillPoint[{classId - 1}]"); + + QueuePackets(expPropertyPacket.Done()); + Database.SetExp(this, classId, charaWork.battleSave.skillPoint[classId - 1]); + } + + public void LevelUp(byte classId) + { + if (charaWork.battleSave.skillLevel[classId - 1] < charaWork.battleSave.skillLevelCap[classId]) + { + //Increase level + charaWork.battleSave.skillLevel[classId - 1]++; + charaWork.parameterSave.state_mainSkillLevel++; + + SendGameMessage(this, Server.GetWorldManager().GetActor(), 33909, 0x44, this, 0, 0, 0, 0, 0, 0, 0, 0, 0, (int) GetLevel()); + //If there's an ability that unlocks at this level, equip it. + List commandIds = Server.GetWorldManager().GetBattleCommandIdByLevel(classId, GetLevel()); + foreach(uint commandId in commandIds) + { + EquipAbilityInFirstOpenSlot(classId, commandId, false); + byte jobId = ConvertClassIdToJobId(classId); + if (jobId != classId) + EquipAbilityInFirstOpenSlot(jobId, commandId, false); + } + } + } + + public static byte ConvertClassIdToJobId(byte classId) + { + byte jobId = classId; + + switch(classId) + { + case CLASSID_PUG: + case CLASSID_GLA: + case CLASSID_MRD: + jobId += 13; + break; + case CLASSID_ARC: + case CLASSID_LNC: + jobId += 11; + break; + case CLASSID_THM: + case CLASSID_CNJ: + jobId += 4; + break; + } + + return jobId; + } + + public void SetCurrentJob(byte jobId) + { + currentJob = jobId; + BroadcastPacket(SetCurrentJobPacket.BuildPacket(actorId, jobId), true); + Database.LoadHotbar(this); + } + + public byte GetCurrentClassOrJob() + { + if (currentJob != 0) + return (byte) currentJob; + + return charaWork.parameterSave.state_mainSkill[0]; + } + + public void hpstuff(uint hp) + { + SetMaxHP(hp); + SetHP(hp); + mpMaxBase = (ushort)hp; + charaWork.parameterSave.mpMax = (short)hp; + charaWork.parameterSave.mp = (short)hp; + AddTP(0); + //SendCharaExpInfo(); + //ActorPropertyPacketUtil exp = new ActorPropertyPacketUtil("charaWork/exp", this); + SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp"); + hpInfo.AddTarget(); + QueuePacket(hpInfo.BuildPacket(actorId)); + } + } } diff --git a/FFXIVClassic Map Server/actors/director/Director.cs b/FFXIVClassic Map Server/actors/director/Director.cs index 53eee828..04d1740f 100644 --- a/FFXIVClassic Map Server/actors/director/Director.cs +++ b/FFXIVClassic Map Server/actors/director/Director.cs @@ -103,7 +103,7 @@ namespace FFXIVClassic_Map_Server.actors.director List lparams = CallLuaScript("init", args2); - if (lparams.Count >= 1 && lparams[0].value is string) + if (lparams != null && lparams.Count >= 1 && lparams[0].value is string) { classPath = (string)lparams[0].value; className = classPath.Substring(classPath.LastIndexOf("/") + 1); @@ -270,6 +270,7 @@ namespace FFXIVClassic_Map_Server.actors.director { if (directorScript != null) { + directorScript = LuaEngine.LoadScript(String.Format(LuaEngine.FILEPATH_DIRECTORS, directorScriptPath)); if (!directorScript.Globals.Get(funcName).IsNil()) { DynValue result = directorScript.Call(directorScript.Globals[funcName], args); diff --git a/FFXIVClassic Map Server/actors/group/ContentGroup.cs b/FFXIVClassic Map Server/actors/group/ContentGroup.cs index 4370f1c4..bebeb49c 100644 --- a/FFXIVClassic Map Server/actors/group/ContentGroup.cs +++ b/FFXIVClassic Map Server/actors/group/ContentGroup.cs @@ -49,7 +49,7 @@ namespace FFXIVClassic_Map_Server.actors.group { if (actor == null) return; - + members.Add(actor.actorId); if (actor is Character) diff --git a/FFXIVClassic Map Server/actors/quest/Quest.cs b/FFXIVClassic Map Server/actors/quest/Quest.cs index 17a2e292..109f8570 100644 --- a/FFXIVClassic Map Server/actors/quest/Quest.cs +++ b/FFXIVClassic Map Server/actors/quest/Quest.cs @@ -93,7 +93,7 @@ namespace FFXIVClassic_Map_Server.Actors return false; } else - return (questFlags & (1 << bitIndex)) == (1 << bitIndex); + return (questFlags & (1 << bitIndex)) == (1 << bitIndex); } public uint GetPhase() diff --git a/FFXIVClassic Map Server/lua/LuaEngine.cs b/FFXIVClassic Map Server/lua/LuaEngine.cs index e2373967..7d28ab28 100644 --- a/FFXIVClassic Map Server/lua/LuaEngine.cs +++ b/FFXIVClassic Map Server/lua/LuaEngine.cs @@ -224,7 +224,7 @@ namespace FFXIVClassic_Map_Server.lua Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{functionName}] {e.Message}"); } DynValue res = new DynValue(); - + if (!script.Globals.Get(functionName).IsNil()) { res = script.Call(script.Globals.Get(functionName), args); @@ -234,12 +234,32 @@ namespace FFXIVClassic_Map_Server.lua } else { - Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{command.name}] Unable to find script {path}"); + 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 [{functionName}] {e.Message}"); + } + DynValue res = new DynValue(); + DynValue r = script.Globals.Get(functionName); + + if (!script.Globals.Get(functionName).IsNil()) + { + res = script.Call(script.Globals.Get(functionName), args); + if (res != null) + return (int)res.Number; + } } return -1; } - private static string GetScriptPath(Actor target) + public static string GetScriptPath(Actor target) { if (target is Player) { @@ -461,6 +481,7 @@ namespace FFXIVClassic_Map_Server.lua Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine; DynValue value = coroutine.Resume(args2); ResolveResume(player, coroutine, value); + } else { diff --git a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX01Packet.cs b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX01Packet.cs index de771305..006a2740 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX01Packet.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/battle/BattleActionX01Packet.cs @@ -25,6 +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? diff --git a/FFXIVClassic Map Server/utils/CharacterUtils.cs b/FFXIVClassic Map Server/utils/CharacterUtils.cs index 3df2bc6a..b420b5fa 100644 --- a/FFXIVClassic Map Server/utils/CharacterUtils.cs +++ b/FFXIVClassic Map Server/utils/CharacterUtils.cs @@ -96,5 +96,31 @@ namespace FFXIVClassic_Map_Server.utils } } + public static string GetClassNameForId(short id) + { + switch (id) + { + case 2: return "pug"; + case 3: return "gla"; + case 4: return "mrd"; + case 7: return "arc"; + case 8: return "lnc"; + case 22: return "thm"; + case 23: return "cnj"; + case 29: return "crp"; + case 30: return "bsm"; + case 31: return "arm"; + case 32: return "gsm"; + case 33: return "ltw"; + case 34: return "wvr"; + case 35: return "alc"; + case 36: return "cul"; + case 39: return "min"; + case 40: return "btn"; + case 41: return "fsh"; + default: return "undefined"; + } + } + } } diff --git a/data/scripts/ally.lua b/data/scripts/ally.lua index b8c39ff3..c8625288 100644 --- a/data/scripts/ally.lua +++ b/data/scripts/ally.lua @@ -36,21 +36,22 @@ end function allyGlobal.HelpPlayers(ally, contentGroupCharas, pickRandomTarget) if contentGroupCharas then - for _, chara in pairs(contentGroupCharas) do + print("assssss") 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 -- do stuff - if not ally.IsEngaged() then - if chara.IsEngaged() then + if not ally:IsEngaged() then + if chara:IsEngaged() then + print("ass") allyGlobal.EngageTarget(ally, chara.target, nil) + return true end end - elseif chara.IsMonster() and chara.IsEngaged() then - if not ally.IsEngaged() then - allyGlobal.EngageTarget(ally, chara.target, nil) - end + elseif chara:IsMonster() and chara:IsEngaged() then + allyGlobal.EngageTarget(ally, chara, nil) + return true end end end @@ -67,14 +68,15 @@ end function allyGlobal.EngageTarget(ally, target, contentGroupCharas) if contentGroupCharas then - for _, chara in pairs(contentGroupCharas) do + for chara in contentGroupCharas do if chara.IsMonster() then if chara.allegiance ~= ally.allegiance then - ally.Engage(chara) + ally:Engage(chara) end end end elseif target then - ally.Engage(target) + ally:Engage(target) + ally.hateContainer:AddBaseHate(target); end end \ No newline at end of file diff --git a/data/scripts/commands/EquipAbilityCommand.lua b/data/scripts/commands/EquipAbilityCommand.lua index e11e5d27..6b2251d8 100644 --- a/data/scripts/commands/EquipAbilityCommand.lua +++ b/data/scripts/commands/EquipAbilityCommand.lua @@ -10,8 +10,10 @@ function onEventStarted(player, equipAbilityWidget, triggername, slot, commandid local worldManager = GetWorldManager(); local ability = worldManager:GetBattleCommand(commandid); + --Equip if (commandid > 0) then + --[[]] --Can the player equip any more cross class actions if (player.charaWork.parameterTemp.otherClassAbilityCount[0] >= player.charaWork.parameterTemp.otherClassAbilityCount[1]) then --"You cannot set any more actions." @@ -19,30 +21,65 @@ function onEventStarted(player, equipAbilityWidget, triggername, slot, commandid player:endEvent(); return; end - + --Is the player high enough level in that class to equip the ability - if (player.charaWork.battleSave.skillLevel[ability.job] < ability.level) then - - --"You have not yet acquired that action" + if (player.charaWork.battleSave.skillLevel[ability.job - 1] < ability.level) then + --"You have not yet acquired that action." player:SendGameMessage(GetWorldMaster(), 30742, 0x20, 0, 0); player:endEvent(); return; end - --Equip the ability - player:EquipAbility(slot, commandid); + + local oldSlot = player:FindFirstCommandSlotById(commandid); + local isEquipped = oldSlot < player.charaWork.commandBorder + 30; + --If slot is 0, find the first open slot + if (slot == 0) then + --If the ability is already equipped and slot is 0, then it can't be equipped again + --If the slot isn't 0, it's a move or a swap command + if (isEquipped == true) then + --"That action is already set to an action slot." + player:SendGameMessage(GetWorldMaster(), 30719, 0x20, 0); + player:endEvent(); + return; + end + + slot = player:FindFirstCommandSlotById(0) - player.charaWork.commandBorder; + + --If the first open slot is outside the hotbar, then the hotbar is full + if(slot >= 30) then + --"You cannot set any more actions." + player:SendGameMessage(Server.GetWorldManager().GetActor(), 30720, 0x20, 0); + player:endEvent(); + return; + end + else + slot = slot - 1; + end + + if(isEquipped == true) then + player:SwapAbilities(oldSlot, slot + player.charaWork.commandBorder); + else + local tslot = slot + player.charaWork.commandBorder; + player:EquipAbility(player.GetJob(), commandid, tslot, true); + end + --Unequip elseif (commandid == 0) then - commandid = player.charaWork.command[slot + player.charaWork.commandBorder]; - + commandid = player.charaWork.command[slot + player.charaWork.commandBorder - 1]; + 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 - if(worldManager:GetBattleCommand(commandid).job == player.charaWork.parameterSave.state_mainSkill[0]) then - --"Actions of your current class or job cannot be removed." - player:SendGameMessage(GetWorldMaster(), 30745, 0x20, 0, 0); + local classId = player:GetJob(); + local jobId = player:ConvertClassIdToJobId(classId); + + if(ability.job == classId or ability.job == jobId) then + --"Actions of your current class or job cannot be removed." + player:SendGameMessage(GetWorldMaster(), 30745, 0x20, 0, 0); elseif (commandid != 0) then player:UnequipAbility(slot); end end + player:endEvent(); end \ No newline at end of file diff --git a/data/scripts/commands/gm/eaction.lua b/data/scripts/commands/gm/eaction.lua new file mode 100644 index 00000000..597c4952 --- /dev/null +++ b/data/scripts/commands/gm/eaction.lua @@ -0,0 +1,34 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "s", + description = +[[ +Equips in the first open slot without checking if you can. +!eaction +]], +} + +function onTrigger(player, argc, commandid) + local sender = "[givegil] "; + + print(commandid); + if name then + if lastName then + player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil; + else + player = GetWorldManager():GetPCInWorld(name) or nil; + end; + end; + + if player then + classid = player:GetCurrentClassOrJob(); + commandid = tonumber(commandid) or 0; + + local added = player:EquipAbilityInFirstOpenSlot(classid, commandid); + + else + print(sender.."unable to add command, ensure player name is valid."); + end; +end; \ No newline at end of file diff --git a/data/scripts/commands/gm/giveexp.lua b/data/scripts/commands/gm/giveexp.lua new file mode 100644 index 00000000..d27366ca --- /dev/null +++ b/data/scripts/commands/gm/giveexp.lua @@ -0,0 +1,34 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "sss", + description = +[[ +Adds experience to player or . +!giveexp | +!giveexp | +]], +} + +function onTrigger(player, argc, qty, name, lastName) + local sender = "[giveexp] "; + + if name then + if lastName then + player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil; + else + player = GetWorldManager():GetPCInWorld(name) or nil; + end; + end; + + if player then + currency = 1000001; + qty = tonumber(qty) or 1; + location = INVENTORY_CURRENCY; + + player:AddExp(qty, player.charaWork.parameterSave.state_mainSkill[0], 5); + 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/setmaxhp.lua b/data/scripts/commands/gm/setmaxhp.lua new file mode 100644 index 00000000..5088680c --- /dev/null +++ b/data/scripts/commands/gm/setmaxhp.lua @@ -0,0 +1,33 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "sss", + description = +[[ +Sets player or 's maximum hp to and heals them to full. +!setmaxhp | +!setmaxhp +]], +} + +function onTrigger(player, argc, hp, name, lastName) + local sender = "[setmaxhp] "; + + if name then + if lastName then + player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil; + else + player = GetWorldManager():GetPCInWorld(name) or nil; + end; + end; + + if player then + hp = tonumber(hp) or 1; + location = INVENTORY_CURRENCY; + + player:hpstuff(hp); + 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/setmaxmp.lua b/data/scripts/commands/gm/setmaxmp.lua new file mode 100644 index 00000000..5088680c --- /dev/null +++ b/data/scripts/commands/gm/setmaxmp.lua @@ -0,0 +1,33 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "sss", + description = +[[ +Sets player or 's maximum hp to and heals them to full. +!setmaxhp | +!setmaxhp +]], +} + +function onTrigger(player, argc, hp, name, lastName) + local sender = "[setmaxhp] "; + + if name then + if lastName then + player = GetWorldManager():GetPCInWorld(name.." "..lastName) or nil; + else + player = GetWorldManager():GetPCInWorld(name) or nil; + end; + end; + + if player then + hp = tonumber(hp) or 1; + location = INVENTORY_CURRENCY; + + player:hpstuff(hp); + 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/yolo.lua b/data/scripts/commands/gm/yolo.lua new file mode 100644 index 00000000..34ad7d25 --- /dev/null +++ b/data/scripts/commands/gm/yolo.lua @@ -0,0 +1,178 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "ssss", + description = +[[ +yolo +]], +} + +local quests = +{ + [111807] = { level = 25, weight = 4, rewardexp = 1080 }, + [110868] = { level = 50, weight = 4, rewardexp = 4400 }, + [111603] = { level = 22, weight = 5, rewardexp = 1100 }, + [111602] = { level = 22, weight = 5, rewardexp = 1100 }, + [111420] = { level = 45, weight = 5, rewardexp = 4450 }, + [110811] = { level = 18, weight = 6, rewardexp = 780 }, + [110814] = { level = 18, weight = 6, rewardexp = 780 }, + [110707] = { level = 25, weight = 6, rewardexp = 1620 }, + [110682] = { level = 34, weight = 6, rewardexp = 3180 }, + [111202] = { level = 35, weight = 6, rewardexp = 3360 }, + [111222] = { level = 35, weight = 6, rewardexp = 3360 }, + [111302] = { level = 35, weight = 6, rewardexp = 3360 }, + [111223] = { level = 40, weight = 6, rewardexp = 4260 }, + [110819] = { level = 45, weight = 6, rewardexp = 5340 }, + [111224] = { level = 45, weight = 6, rewardexp = 5340 }, + [111225] = { level = 45, weight = 6, rewardexp = 5340 }, + [110867] = { level = 45, weight = 6, rewardexp = 5340 }, + [110869] = { level = 45, weight = 6, rewardexp = 5340 }, + [110708] = { level = 45, weight = 6, rewardexp = 5340 }, + [110627] = { level = 45, weight = 6, rewardexp = 5340 }, + [111434] = { level = 50, weight = 6, rewardexp = 6600 }, + [110850] = { level = 1, weight = 7, rewardexp = 40 }, + [110851] = { level = 1, weight = 7, rewardexp = 40 }, + [110841] = { level = 20, weight = 7, rewardexp = 1120 }, + [110642] = { level = 20, weight = 7, rewardexp = 1120 }, + [110840] = { level = 20, weight = 7, rewardexp = 1120 }, + [110727] = { level = 21, weight = 7, rewardexp = 1401 }, + [111221] = { level = 30, weight = 7, rewardexp = 2661 }, + [111241] = { level = 30, weight = 7, rewardexp = 2661 }, + [110687] = { level = 28, weight = 9, rewardexp = 2970 }, + [110016] = { level = 34, weight = 50, rewardexp = 26500 }, + [110017] = { level = 38, weight = 50, rewardexp = 32500 }, + [110019] = { level = 46, weight = 50, rewardexp = 46000 } +}; + +local expTable = { + 570, -- 1 + 700, + 880, + 1100, + 1500, + 1800, + 2300, + 3200, + 4300, + 5000, -- 10 + 5900, + 6800, + 7700, + 8700, + 9700, + 11000, + 12000, + 13000, + 15000, + 16000, -- 20 + 20000, + 22000, + 23000, + 25000, + 27000, + 29000, + 31000, + 33000, + 35000, + 38000, -- 30 + 45000, + 47000, + 50000, + 53000, + 56000, + 59000, + 62000, + 65000, + 68000, + 71000, -- 40 + 74000, + 78000, + 81000, + 85000, + 89000, + 92000, + 96000, + 100000, + 100000, + 110000 -- 50 +}; + +local commandCost = { + ["raise"] = 150, + ["cure"] = 40, + ["cura"] = 100, + ["curaga"] = 150, +}; +-- stone: (1, 9) (5, 12) (10, ) +-- cure: (1, 5) (5, 6) (10, ) +-- aero: (1, 9) (5, 12) (10, ) +-- protect: (1, 9) (5, 12) (10, ) +--[[ +function onTrigger(player, argc, id, level, weight) + id = tonumber(id) or 111807; + level = tonumber(level) or quests[id].level; + weight = tonumber(weight) or quests[id].weight; + local messageId = MESSAGE_TYPE_SYSTEM_ERROR; + local sender = "yolo"; + + if id == 1 then + return + end + local message = calcSkillPoint(player, level, weight); + if player then + player.SendMessage(messageId, sender, string.format("calculated %s | expected %s", message, quests[id].rewardexp)); + end; + printf("calculated %s | expected %s", message, quests[id].rewardexp); +end; +]] + +function onTrigger(player, argc, skillName, level) + local messageId = MESSAGE_TYPE_SYSTEM_ERROR; + local sender = "yolo"; + + if player then + if false then + local effectId = 223004; + + player.statusEffects.RemoveStatusEffect(effectId); + player.statusEffects.AddStatusEffect(effectId, 1, 0, 5); + return; + end; + + local pos = player:GetPos(); + local x = tonumber(pos[0]); + local y = tonumber(pos[1]); + local z = tonumber(pos[2]); + local rot = tonumber(pos[3]); + local zone = pos[4]; + + printf("%f %f %f", x, y, z); + --local x, y, z = player.GetPos(); + for i = 1, 1 do + + local actor = player.GetZone().SpawnActor(2207303, 'ass', x, y, z, rot, 0, 0, true ); + + --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) + if skillName and level and commandCost[skillName] then + return math.ceil((8000 + (level - 70) * 500) * (commandCost[skillName] * 0.001)); + end; + return 1; +end + +function calcSkillPoint(player, lvl, weight) + weight = weight / 100 + + return math.ceil(expTable[lvl] * weight) +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 new file mode 100644 index 00000000..98ea45f1 --- /dev/null +++ b/data/scripts/commands/weaponskill/chaos_thrust.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/default.lua b/data/scripts/commands/weaponskill/default.lua new file mode 100644 index 00000000..965bcb8d --- /dev/null +++ b/data/scripts/commands/weaponskill/default.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) + local damage = math.random(100, 200); + + -- todo: populate a global script with statuses and modifiers + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + action.effectId = bit32.bxor(0x8000000, skill.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +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 new file mode 100644 index 00000000..98ea45f1 --- /dev/null +++ b/data/scripts/commands/weaponskill/doom_spike.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +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 new file mode 100644 index 00000000..98ea45f1 --- /dev/null +++ b/data/scripts/commands/weaponskill/dread_spike.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/feint.lua b/data/scripts/commands/weaponskill/feint.lua new file mode 100644 index 00000000..98ea45f1 --- /dev/null +++ b/data/scripts/commands/weaponskill/feint.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +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 new file mode 100644 index 00000000..fa1c4b1f --- /dev/null +++ b/data/scripts/commands/weaponskill/flat_blade.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +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 new file mode 100644 index 00000000..98ea45f1 --- /dev/null +++ b/data/scripts/commands/weaponskill/full_thrust.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +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 new file mode 100644 index 00000000..9c72078a --- /dev/null +++ b/data/scripts/commands/weaponskill/heavy_thrust.lua @@ -0,0 +1,27 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) + local damage = math.random(100, 200); + + -- todo: populate a global script with statuses and modifiers + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + + action.effectId = bit32.bxor(0x8000000, skill.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +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 new file mode 100644 index 00000000..98ea45f1 --- /dev/null +++ b/data/scripts/commands/weaponskill/impulse_drive.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +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 new file mode 100644 index 00000000..98ea45f1 --- /dev/null +++ b/data/scripts/commands/weaponskill/leg_sweep.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +end; \ No newline at end of file diff --git a/data/scripts/commands/weaponskill/pummel.lua b/data/scripts/commands/weaponskill/pummel.lua new file mode 100644 index 00000000..98ea45f1 --- /dev/null +++ b/data/scripts/commands/weaponskill/pummel.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +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 new file mode 100644 index 00000000..965bcb8d --- /dev/null +++ b/data/scripts/commands/weaponskill/true_thrust.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +function onSkillStart(caster, target, skill) + return 0; +end; + +function onSkillFinish(caster, target, skill, action) + local damage = math.random(100, 200); + + -- todo: populate a global script with statuses and modifiers + action.worldMasterTextId = 0x765D; + + -- todo: populate a global script with statuses and modifiers + -- magic.HandleAttackMagic(caster, target, spell, action) + -- action.effectId = bit32.bxor(0x8000000, spell.effectAnimation, 15636); + action.effectId = bit32.bxor(0x8000000, skill.effectAnimation, 15636); + + if target.hateContainer then + target.hateContainer.UpdateHate(caster, damage); + end; + return damage; +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 new file mode 100644 index 00000000..98ea45f1 --- /dev/null +++ b/data/scripts/commands/weaponskill/vorpal_thrust.lua @@ -0,0 +1,26 @@ +require("global"); + +function onSkillPrepare(caster, target, skill) + return 0; +end; + +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; +end; \ No newline at end of file diff --git a/data/scripts/content/SimpleContent30010.lua b/data/scripts/content/SimpleContent30010.lua index d78c2935..5894bbf6 100644 --- a/data/scripts/content/SimpleContent30010.lua +++ b/data/scripts/content/SimpleContent30010.lua @@ -1,28 +1,75 @@ +require ("global") +require ("ally") +require ("modifiers") function onCreate(starterPlayer, contentArea, director) + --papalymo = contentArea:SpawnActor(2290005, "papalymo", 365.89, 4.0943, -706.72, -0.718); + --yda = contentArea:SpawnActor(2290006, "yda", 365.266, 4.122, -700.73, 1.5659); + + --mob1 = contentArea:SpawnActor(2201407, "mob1", 374.427, 4.4, -698.711, -1.942); + --mob2 = contentArea:SpawnActor(2201407, "mob2", 375.377, 4.4, -700.247, -1.992); + --mob3 = contentArea:SpawnActor(2201407, "mob3", 375.125, 4.4, -703.591, -1.54); + yda = GetWorldManager().SpawnBattleNpcById(6, contentArea); + papalymo = GetWorldManager().SpawnBattleNpcById(7, contentArea); + yda:ChangeState(2); + mob1 = GetWorldManager().SpawnBattleNpcById(3, contentArea); + mob2 = GetWorldManager().SpawnBattleNpcById(4, contentArea); + mob3 = GetWorldManager().SpawnBattleNpcById(5, contentArea); + starterPlayer.currentParty.members:Add(yda.actorId); + starterPlayer.currentParty.members:Add(papalymo.actorId); + starterPlayer:SetMod(modifiersGlobal.MinimumHpLock, 1); - papalymo = contentArea:SpawnActor(2290005, "papalymo", 365.89, 4.0943, -706.72, -0.718); - yda = contentArea:SpawnActor(2290006, "yda", 365.266, 4.122, -700.73, 1.5659); - yda:ChangeState(2); - - mob1 = contentArea:SpawnActor(2201407, "mob1", 374.427, 4.4, -698.711, -1.942); - mob2 = contentArea:SpawnActor(2201407, "mob2", 375.377, 4.4, -700.247, -1.992); - mob3 = contentArea:SpawnActor(2201407, "mob3", 375.125, 4.4, -703.591, -1.54); openingStoper = contentArea:SpawnActor(1090384, "openingstoper", 356.09, 3.74, -701.62, -1.41); director:AddMember(starterPlayer); director:AddMember(director); - director:AddMember(papalymo); + director:AddMember(papalymo); director:AddMember(yda); director:AddMember(mob1); director:AddMember(mob2); director:AddMember(mob3); - director:StartContentGroup(); + --director:StartContentGroup(); 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() diff --git a/data/scripts/directors/Quest/QuestDirectorMan0g001.lua b/data/scripts/directors/Quest/QuestDirectorMan0g001.lua index 81bf270e..1c766c02 100644 --- a/data/scripts/directors/Quest/QuestDirectorMan0g001.lua +++ b/data/scripts/directors/Quest/QuestDirectorMan0g001.lua @@ -11,71 +11,54 @@ function init() end function onCreateContentArea(players, director, contentArea, contentGroup) - - local worldManager = GetWorldManager(); - - yshtola = GetWorldManager().SpawnBattleNpcById(6, contentArea); - stahlmann = GetWorldManager().SpawnBattleNpcById(7, contentArea); - - mob1 = GetWorldManager().SpawnBattleNpcById(3, contentArea); - mob2 = GetWorldManager().SpawnBattleNpcById(4, contentArea); - mob3 = GetWorldManager().SpawnBattleNpcById(5, contentArea); - - local added = false; - for i = 0, players.Count do - local player = players[i]; - print("asses "..players.Count) - if player.currentParty and not added then - print("shitness") - player.currentParty.members:Add(yshtola.actorId); - print("cunt") - player.currentParty.members:Add(stahlmann.actorId); - print("dickbag") - added = true; - end; - -- dont let player die - player:SetMod(modifiersGlobal.MinimumHpLock, 1); - contentGroup:AddMember(player) - print("shittttt") - break - end; - print("shit") - contentGroup:AddMember(director); - print("shit2"); - contentGroup:AddMember(yshtola); - print("shit3") - contentGroup:AddMember(stahlmann); - print("shit4") - contentGroup:AddMember(mob1); - print("shit5") - contentGroup:AddMember(mob2); - print("shit6") - contentGroup:AddMember(mob3); - print("dicks") + director:StartContentGroup(); end -function onEventStarted(player, actor, triggerName) - +function onEventStarted(player, actor, triggerName) man0g0Quest = player:GetQuest("Man0g0"); startTutorialMode(player); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrBtl001", nil, nil, nil); player:EndEvent(); waitForSignal("playerActive"); - wait(2); --If this isn't here, the scripts bugs out. TODO: Find a better alternative. + wait(1); --If this isn't here, the scripts bugs out. TODO: Find a better alternative. kickEventContinue(player, actor, "noticeEvent", "noticeEvent"); callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrBtl002", nil, nil, nil); player:EndEvent(); - + waitForSignal("playerAttack"); closeTutorialWidget(player); + showTutorialSuccessWidget(player, 9055); --Open TutorialSuccessWidget for attacking enemy wait(3); - - man0g0Quest:NextPhase(5); openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_TP); - wait(5); - - man0g0Quest:NextPhase(6); + waitForSignal("tpOver1000"); closeTutorialWidget(player); - print("ass") + openTutorialWidget(player, CONTROLLER_KEYBOARD, TUTORIAL_WEAPONSKILLS); + + if player:IsDiscipleOfWar() then + waitForSignal("weaponskillUsed"); --Should be wait for weaponskillUsed signal + elseif player:IsDiscipleOfMagic() then + waitForSignal("spellUsed") + elseif player:IsDiscipleOfHand() then + waitForSignal("abilityUsed") + elseif player:IsDiscipleOfLand() then + waitForSignal("abilityUsed") + end + closeTutorialWidget(player); + showTutorialSuccessWidget(player, 9065); --Open TutorialSuccessWidget for weapon skill + + waitForSignal("mobkill"); --Should be wait for mobkill + waitForSignal("mobkill"); + waitForSignal("mobkill"); + worldMaster = GetWorldMaster(); + player:SendDataPacket("attention", worldMaster, "", 51073, 2); + wait(7); + player:ChangeMusic(7); + player:ChangeState(0); + kickEventContinue(player, actor, "noticeEvent", "noticeEvent"); + callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent020_1", nil, nil, nil); + + player:GetZone():ContentFinished(); + player:EndEvent(); + GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1); --[[ IF DoW: OpenWidget (TP) @@ -97,7 +80,6 @@ function onEventStarted(player, actor, triggerName) end function onUpdate(deltaTime, area) - print("fuck") end function onTalkEvent(player, npc) @@ -118,7 +100,5 @@ function onCommand(player, command) end function main(director, contentGroup) - print("shitstain") onCreateContentArea(director:GetPlayerMembers(), director, director:GetZone(), contentGroup); - player:EndEvent(); end; \ No newline at end of file diff --git a/data/scripts/global.lua b/data/scripts/global.lua index b26475fb..770c09ff 100644 --- a/data/scripts/global.lua +++ b/data/scripts/global.lua @@ -116,6 +116,14 @@ DAMAGE_TAKEN_TYPE_MAGIC = 2; DAMAGE_TAKEN_TYPE_WEAPONSKILL = 3; DAMAGE_TAKEN_TYPE_ABILITY = 4; +-- CLASSID +CLASSID_PUG = 2; +CLASSID_GLA = 3; +CLASSID_MRD = 4; +CLASSID_ARC = 7; +CLASSID_LNC = 8; +CLASSID_THM = 22; +CLASSID_CNJ = 23; --UTILS diff --git a/data/scripts/spells/blizzara.lua b/data/scripts/spells/blizzara.lua new file mode 100644 index 00000000..72ccc0bd --- /dev/null +++ b/data/scripts/spells/blizzara.lua @@ -0,0 +1,19 @@ +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 new file mode 100644 index 00000000..1418061a --- /dev/null +++ b/data/scripts/spells/thunder.lua @@ -0,0 +1,18 @@ +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/unique/fst0Battle03/Monster/bloodthirsty_wolf.lua b/data/scripts/unique/fst0Battle03/Monster/bloodthirsty_wolf.lua index d0f3b4cb..a4e24895 100644 --- a/data/scripts/unique/fst0Battle03/Monster/bloodthirsty_wolf.lua +++ b/data/scripts/unique/fst0Battle03/Monster/bloodthirsty_wolf.lua @@ -1,58 +1,22 @@ -require ("global") require ("modifiers") -require ("ally") function onSpawn(mob) + mob:SetMod(modifiersGlobal.Speed, 0) +end -end; +function onDamageTaken(mob, attacker, damage) + if not attacker:IsPlayer() and mob:GetHP() - damage < 0 then + mob:addHP(damage) + end +end -function onDamageTaken(mob, attacker, damage, damageType) - if attacker.IsPlayer() then - local man0g0Quest = attacker:GetQuest("Man0g0"); - if damageType == DAMAGE_TAKEN_TYPE_ATTACK then - if man0g0Quest:GetPhase() == 5 then - closeTutorialWidget(player); - showTutorialSuccessWidget(player, 9055); --Open TutorialSuccessWidget for attacking enemy - man0g0Quest:NextPhase(6); - end; - elseif damageType == DAMAGE_TAKEN_TYPE_WEAPONSKILL or damageType == DAMAGE_TAKEN_TYPE_MAGIC then - if man0g0Quest:GetPhase() == 6 then - closeTutorialWidget(player); - showTutorialSuccessWidget(player, 9065); --Open TutorialSuccessWidget for weapon skill - man0g0Quest:NextPhase(7); - end; - end; - end; -end; +function onCombatTick(mob, target, tick, contentGroupCharas) + if mob:GetSpeed() == 0 then + mob:SetMod(modifiersGlobal.Speed, 8) + end +end -function onDeath(mob, player, lastAttacker) - if player then - local man0g0Quest = player:GetQuest("Man0g0"); - if man0g0Quest and man0g0Quest:GetPhase() >= 7 then - man0g0Quest:NextPhase(man0g0Quest:GetPhase() + 1); - mob:SetTempVar("playerId", player.actorId); - if man0g0Quest:GetPhase() == 10 then - local worldMaster = GetWorldMaster(); - player:SendDataPacket("attention", worldMaster, "", 51073, 1); - kickEventContinue(player, director, "noticeEvent", "noticeEvent"); - callClientFunction(player, "delegateEvent", player, man0g0Quest, "processEvent020_1", nil, nil, nil); - player:ChangeMusic(7); - player:Disengage(0x0000); - mob:SetTempVar("complete", 1); - end; - end; - end; -end; - -function onDespawn(mob) - if zone then - local player = zone.FindActorInArea(mob:GetTempVar("playerId")); - - if player and mob:GetTempVar("complete") == 1 then - local man0g0Quest = player:GetQuest("Man0g0"); - player:GetZone():ContentFinished(); - player:EndEvent(); - GetWorldManager():DoZoneChange(player, 155, "PrivateAreaMasterPast", 1, 15, 175.38, -1.21, -1156.51, -2.1); - end; - end; -end; \ No newline at end of file +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 a262f46b..ca9e38ca 100644 --- a/data/scripts/unique/fst0Battle03/Monster/papalymo.lua +++ b/data/scripts/unique/fst0Battle03/Monster/papalymo.lua @@ -1,11 +1,11 @@ require ("global") - +require ("modifiers") require ("ally") function onSpawn(ally) + ally:SetMaxHP(69420) + ally:SetHP(ally:GetMaxHP()) ally.isAutoAttackEnabled = false; -end; - -function onCombatTick(ally, target, tick, contentGroupCharas) - allyGlobal.onCombatTick(ally, target, tick, contentGroupCharas); -end; \ No newline at end of file + ally.neutral = false + ally:SetMod(modifiersGlobal.Speed, 0) +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Battle03/Monster/yda.lua b/data/scripts/unique/fst0Battle03/Monster/yda.lua index 2abf6f76..c48a4968 100644 --- a/data/scripts/unique/fst0Battle03/Monster/yda.lua +++ b/data/scripts/unique/fst0Battle03/Monster/yda.lua @@ -1,11 +1,10 @@ require ("global") - require ("ally") function onSpawn(ally) - ally.isAutoAttackEnabled = false + ally:SetMaxHP(69420) + ally:SetHP(ally:GetMaxHP()) + ally.isAutoAttackEnabled = false; + ally.neutral = false + ally:SetMod(modifiersGlobal.Speed, 0) end - -function onCombatTick(ally, target, tick, contentGroupCharas) - allyGlobal.onCombatTick(ally, target, tick, contentGroupCharas) -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 a82963ad..0f4d5f7e 100644 --- a/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua +++ b/data/scripts/unique/fst0Battle03/PopulaceStandard/yda.lua @@ -7,9 +7,10 @@ end function onEventStarted(player, npc, triggerName) man0g0Quest = player:GetQuest("Man0g0"); - + print("hi"); if (man0g0Quest ~= nil) then - + + print("hi2"); if (triggerName == "pushDefault") then callClientFunction(player, "delegateEvent", player, man0g0Quest, "processTtrNomal002", nil, nil, nil); elseif (triggerName == "talkDefault") then @@ -22,6 +23,7 @@ function onEventStarted(player, npc, triggerName) man0g0Quest:SaveData(); player:GetDirector("OpeningDirector"):onTalkEvent(player, npc); --Was she talked to after papalymo? + print("hi3"); else if (man0g0Quest:GetQuestFlag(MAN0G0_FLAG_MINITUT_DONE1) == true) then @@ -41,6 +43,7 @@ function onEventStarted(player, npc, triggerName) player:KickEvent(director, "noticeEvent", true); player:SetLoginDirector(director); + print("hi5"); GetWorldManager():DoZoneChangeContent(player, contentArea, 362.4087, 4, -703.8168, 1.5419, 16); return; else diff --git a/data/scripts/unique/fst0Town01/PopulaceStandard/serpent_private_hill.lua b/data/scripts/unique/fst0Town01/PopulaceStandard/serpent_private_hill.lua new file mode 100644 index 00000000..02e67d32 --- /dev/null +++ b/data/scripts/unique/fst0Town01/PopulaceStandard/serpent_private_hill.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultFst = GetStaticActor("DftFst"); + callClientFunction(player, "delegateEvent", player, defaultFst, "defaultTalkWithSerpent_private_hill_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Town01/PopulaceStandard/task_board.lua b/data/scripts/unique/fst0Town01/PopulaceStandard/task_board.lua new file mode 100644 index 00000000..b7448428 --- /dev/null +++ b/data/scripts/unique/fst0Town01/PopulaceStandard/task_board.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultFst = GetStaticActor("DftFst"); + callClientFunction(player, "delegateEvent", player, defaultFst, "defaultTalkWithTask_board_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Town01a/PopulaceStandard/gagaroon.lua b/data/scripts/unique/fst0Town01a/PopulaceStandard/gagaroon.lua new file mode 100644 index 00000000..19143bdf --- /dev/null +++ b/data/scripts/unique/fst0Town01a/PopulaceStandard/gagaroon.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultFst = GetStaticActor("DftFst"); + callClientFunction(player, "delegateEvent", player, defaultFst, "defaultTalkWithGagaroon_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Town01a/PopulaceStandard/louisoix.lua b/data/scripts/unique/fst0Town01a/PopulaceStandard/louisoix.lua new file mode 100644 index 00000000..b0b08cd5 --- /dev/null +++ b/data/scripts/unique/fst0Town01a/PopulaceStandard/louisoix.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultFst = GetStaticActor("DftFst"); + callClientFunction(player, "delegateEvent", player, defaultFst, "defaultTalkWithLouisoix_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_carver.lua b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_carver.lua new file mode 100644 index 00000000..0a24f7e7 --- /dev/null +++ b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_carver.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultFst = GetStaticActor("DftFst"); + callClientFunction(player, "delegateEvent", player, defaultFst, "defaultTalkWithSerpent_private_carver_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_holmes.lua b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_holmes.lua new file mode 100644 index 00000000..fc7c975d --- /dev/null +++ b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_holmes.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultFst = GetStaticActor("DftFst"); + callClientFunction(player, "delegateEvent", player, defaultFst, "defaultTalkWithSerpent_private_holmes_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_kirk.lua b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_kirk.lua new file mode 100644 index 00000000..c7b840aa --- /dev/null +++ b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_kirk.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultFst = GetStaticActor("DftFst"); + callClientFunction(player, "delegateEvent", player, defaultFst, "defaultTalkWithSerpent_private_kirk_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_stone.lua b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_stone.lua new file mode 100644 index 00000000..d6d2ff82 --- /dev/null +++ b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_stone.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultFst = GetStaticActor("DftFst"); + callClientFunction(player, "delegateEvent", player, defaultFst, "defaultTalkWithSerpent_private_stone_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_white.lua b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_white.lua new file mode 100644 index 00000000..d66e5dad --- /dev/null +++ b/data/scripts/unique/fst0Town01a/PopulaceStandard/serpent_private_white.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultFst = GetStaticActor("DftFst"); + callClientFunction(player, "delegateEvent", player, defaultFst, "defaultTalkWithSerpent_private_white_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/data/scripts/unique/sea0Town01a/PopulaceStandard/rubh_hob.lua b/data/scripts/unique/sea0Town01a/PopulaceStandard/rubh_hob.lua new file mode 100644 index 00000000..2b97c724 --- /dev/null +++ b/data/scripts/unique/sea0Town01a/PopulaceStandard/rubh_hob.lua @@ -0,0 +1,7 @@ +require ("global") + +function onEventStarted(player, npc) + defaultSea = GetStaticActor("DftSea"); + callClientFunction(player, "delegateEvent", player, defaultSea, "defaultTalkWithRubh_hob_001", nil, nil, nil); + player:endEvent(); +end \ No newline at end of file diff --git a/sql/server_battlenpc_genus.sql b/sql/server_battlenpc_genus.sql index cb6ad985..8c5793bb 100644 --- a/sql/server_battlenpc_genus.sql +++ b/sql/server_battlenpc_genus.sql @@ -65,71 +65,71 @@ CREATE TABLE `server_battlenpc_genus` ( LOCK TABLES `server_battlenpc_genus` WRITE; /*!40000 ALTER TABLE `server_battlenpc_genus` DISABLE KEYS */; set autocommit=0; -INSERT INTO `server_battlenpc_genus` VALUES (1,'Aldgoat',1,0,1,'Beast',1,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (2,'Antelope',1,0,1,'Beast',1,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (3,'Wolf',1,0,1,'Beast',2,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (4,'Opo-opo',1,0,1,'Beast',1,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (5,'Coeurl',1,0,1,'Beast',15,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (6,'Goobbue',1,0,1,'Beast',4,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (7,'Sheep',1,0,1,'Beast',1,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (8,'Buffalo',1,0,1,'Beast',4,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (9,'Boar',1,0,1,'Beast',2,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (10,'Moon-Mouse?',1,0,1,'Beast',2,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (11,'Mole',1,0,1,'Beast',4,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (12,'Rodent',1,0,1,'Beast',2,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (13,'Cactuar',1,0,2,'Plantoid',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (14,'Funguar',1,0,2,'Plantoid',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (15,'Flying-trap',1,0,2,'Plantoid',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (16,'Morbol',1,0,2,'Plantoid',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (17,'Orobon',1,0,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (18,'Gigantoad',1,0,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (19,'Salamander',1,0,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (20,'Jelly-fish',1,0,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (21,'Slug',1,0,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (22,'Megalo-crab',1,0,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (23,'Amaalja',1,0,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (24,'Ixal',1,0,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (25,'Qiqirn',1,0,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (26,'Goblin',1,0,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (27,'Kobold',1,0,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (28,'Sylph',1,0,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (29,'Person',1,0,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (30,'Drake',1,0,5,'Reptilian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (31,'Basilisk',1,0,5,'Reptilian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (32,'Raptor',1,0,5,'Reptilian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (33,'Ant-ring',1,0,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (34,'Swarm',1,0,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (35,'Diremite',1,0,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (36,'Chigoe',1,0,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (37,'Gnat',1,0,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (38,'Beetle',1,0,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (39,'Yarzon',1,0,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (40,'Apkallu',1,0,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (41,'Vulture',1,0,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (42,'Dodo',1,0,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (43,'Bat',1,0,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (44,'Hippogryph',1,0,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (45,'Puk',1,0,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (46,'Ghost',1,0,8,'Undead',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (47,'The-Damned',1,0,8,'Undead',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (48,'Wight',1,0,8,'Undead',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (49,'Coblyn',1,0,9,'Cursed',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (50,'Spriggan',1,0,9,'Cursed',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (51,'Ahriman',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (52,'Imp',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (53,'Will-O-Wisp',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (54,'Fire-Elemental',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (55,'Water-Elemental',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (56,'Earth-Elemental',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (57,'Lightning-Elemental',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (58,'Ice-Elemental',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (59,'Wind-Elemental',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (60,'Ogre',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (61,'Phurble',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (62,'Plasmoid',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (63,'Flan',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (64,'Bomb',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); -INSERT INTO `server_battlenpc_genus` VALUES (65,'Grenade',1,0,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (1,'Aldgoat',1,8,1,'Beast',1,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (2,'Antelope',1,8,1,'Beast',1,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (3,'Wolf',1,8,1,'Beast',2,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (4,'Opo-opo',1,8,1,'Beast',1,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (5,'Coeurl',1,8,1,'Beast',15,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (6,'Goobbue',1,8,1,'Beast',4,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (7,'Sheep',1,8,1,'Beast',1,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (8,'Buffalo',1,8,1,'Beast',4,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (9,'Boar',1,8,1,'Beast',2,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (10,'Moon-Mouse?',1,8,1,'Beast',2,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (11,'Mole',1,8,1,'Beast',4,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (12,'Rodent',1,8,1,'Beast',2,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (13,'Cactuar',1,8,2,'Plantoid',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (14,'Funguar',1,8,2,'Plantoid',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (15,'Flying-trap',1,8,2,'Plantoid',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (16,'Morbol',1,8,2,'Plantoid',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (17,'Orobon',1,8,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (18,'Gigantoad',1,8,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (19,'Salamander',1,8,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (20,'Jelly-fish',1,8,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (21,'Slug',1,8,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (22,'Megalo-crab',1,8,3,'Aquan',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (23,'Amaalja',1,8,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (24,'Ixal',1,8,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (25,'Qiqirn',1,8,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (26,'Goblin',1,8,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (27,'Kobold',1,8,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (28,'Sylph',1,8,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (29,'Person',1,8,4,'Spoken',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (30,'Drake',1,8,5,'Reptilian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (31,'Basilisk',1,8,5,'Reptilian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (32,'Raptor',1,8,5,'Reptilian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (33,'Ant-ring',1,8,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (34,'Swarm',1,8,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (35,'Diremite',1,8,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (36,'Chigoe',1,8,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (37,'Gnat',1,8,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (38,'Beetle',1,8,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (39,'Yarzon',1,8,6,'Insect',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (40,'Apkallu',1,8,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (41,'Vulture',1,8,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (42,'Dodo',1,8,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (43,'Bat',1,8,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (44,'Hippogryph',1,8,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (45,'Puk',1,8,7,'Avian',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (46,'Ghost',1,8,8,'Undead',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (47,'The-Damned',1,8,8,'Undead',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (48,'Wight',1,8,8,'Undead',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (49,'Coblyn',1,8,9,'Cursed',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (50,'Spriggan',1,8,9,'Cursed',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (51,'Ahriman',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (52,'Imp',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (53,'Will-O-Wisp',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (54,'Fire-Elemental',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (55,'Water-Elemental',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (56,'Earth-Elemental',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (57,'Lightning-Elemental',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (58,'Ice-Elemental',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (59,'Wind-Elemental',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (60,'Ogre',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (61,'Phurble',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (62,'Plasmoid',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (63,'Flan',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (64,'Bomb',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); +INSERT INTO `server_battlenpc_genus` VALUES (65,'Grenade',1,8,10,'Voidsent',0,100,100,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0); /*!40000 ALTER TABLE `server_battlenpc_genus` ENABLE KEYS */; UNLOCK TABLES; commit; @@ -143,4 +143,4 @@ commit; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2017-09-16 2:42:51 +-- Dump completed on 2017-10-11 14:48:52 diff --git a/sql/server_battlenpc_groups.sql b/sql/server_battlenpc_groups.sql index e04c0014..d5bea565 100644 --- a/sql/server_battlenpc_groups.sql +++ b/sql/server_battlenpc_groups.sql @@ -67,4 +67,4 @@ commit; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2017-09-16 2:42:34 +-- Dump completed on 2017-10-11 14:44:48 diff --git a/sql/server_battlenpc_pools.sql b/sql/server_battlenpc_pools.sql index e7bb7073..11082723 100644 --- a/sql/server_battlenpc_pools.sql +++ b/sql/server_battlenpc_pools.sql @@ -46,12 +46,14 @@ CREATE TABLE `server_battlenpc_pools` ( LOCK TABLES `server_battlenpc_pools` WRITE; /*!40000 ALTER TABLE `server_battlenpc_pools` DISABLE KEYS */; +set autocommit=0; INSERT INTO `server_battlenpc_pools` VALUES (1,2104001,'wharf_rat',12,0,1,4200,1,0,0,0,0,0); -INSERT INTO `server_battlenpc_pools` VALUES (2,2205403,'bloodthirsty_wolf',3,0,1,4200,1,0,0,0,0,0); -INSERT INTO `server_battlenpc_pools` VALUES (3,2290001,'yda',29,2,1,4200,1,0,0,0,0,0); -INSERT INTO `server_battlenpc_pools` VALUES (4,2290002,'papalymo',29,22,1,4200,1,0,0,0,0,0); +INSERT INTO `server_battlenpc_pools` VALUES (2,2201407,'bloodthirsty_wolf',3,0,1,4200,1,0,0,0,0,0); +INSERT INTO `server_battlenpc_pools` VALUES (3,2290005,'yda',29,2,1,4200,1,0,0,0,0,0); +INSERT INTO `server_battlenpc_pools` VALUES (4,2290006,'papalymo',29,22,1,4200,1,0,0,0,0,0); /*!40000 ALTER TABLE `server_battlenpc_pools` ENABLE KEYS */; UNLOCK TABLES; +commit; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; @@ -62,4 +64,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2017-09-16 2:40:29 +-- Dump completed on 2017-10-11 14:47:40 diff --git a/sql/server_battlenpc_spawn_locations.sql b/sql/server_battlenpc_spawn_locations.sql index 4e6bbdaa..039614a2 100644 --- a/sql/server_battlenpc_spawn_locations.sql +++ b/sql/server_battlenpc_spawn_locations.sql @@ -43,11 +43,11 @@ LOCK TABLES `server_battlenpc_spawn_locations` WRITE; set autocommit=0; INSERT INTO `server_battlenpc_spawn_locations` VALUES (1,'test',1,25.584,200,-450,-2.514); INSERT INTO `server_battlenpc_spawn_locations` VALUES (2,'test',1,20,200,-444,-3.14); -INSERT INTO `server_battlenpc_spawn_locations` VALUES (3,'bloodthirsty_wolf',2,-3.02,17.35,14.24,-2.81); -INSERT INTO `server_battlenpc_spawn_locations` VALUES (4,'bloodthirsty_wolf',2,0.02,17.35,14.24,-2.81); -INSERT INTO `server_battlenpc_spawn_locations` VALUES (5,'bloodthirsty_wolf',2,-6.02,17.35,14.24,-2.81); -INSERT INTO `server_battlenpc_spawn_locations` VALUES (6,'yshtola',3,-8,16.35,6,0.5); -INSERT INTO `server_battlenpc_spawn_locations` VALUES (7,'papalymo',4,0,16.35,22,3); +INSERT INTO `server_battlenpc_spawn_locations` VALUES (3,'bloodthirsty_wolf',2,374.427,4.4,-698.711,-1.942); +INSERT INTO `server_battlenpc_spawn_locations` VALUES (4,'bloodthirsty_wolf',2,375.377,4.4,-700.247,-1.992); +INSERT INTO `server_battlenpc_spawn_locations` VALUES (5,'bloodthirsty_wolf',2,375.125,4.4,-703.591,-1.54); +INSERT INTO `server_battlenpc_spawn_locations` VALUES (6,'yda',3,365.266,4.122,-700.73,1.5659); +INSERT INTO `server_battlenpc_spawn_locations` VALUES (7,'papalymo',4,365.89,4.0943,-706.72,-0.718); /*!40000 ALTER TABLE `server_battlenpc_spawn_locations` ENABLE KEYS */; UNLOCK TABLES; commit; @@ -61,4 +61,4 @@ commit; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2017-09-16 2:43:52 +-- Dump completed on 2017-10-11 14:47:29