/// <summary> /// Sends BlacksmithingMiniGame to creature's client, which starts /// the Blacksmithing mini-game. /// </summary> /// <remarks> /// The position of the dots is relative to the upper left of the /// field. They land exactly on those spots after "wavering" for a /// moment. This wavering is randomized on the client side and /// doesn't affect anything. /// /// The time bar is always the same, but the time it takes to fill /// up changes based on the "time displacement". The lower the value, /// the longer it takes to fill up. Using values that are too high /// or too low mess up the calculations and cause confusing results. /// The official range seems to be between ~0.81 and ~0.98. /// </remarks> /// <param name="creature"></param> /// <param name="prop"></param> /// <param name="item"></param> /// <param name="dots"></param> /// <param name="deviation"></param> public static void BlacksmithingMiniGame(Creature creature, Prop prop, Item item, List<BlacksmithDot> dots, int deviation) { if (dots == null || dots.Count != 5) throw new ArgumentException("5 dots required."); var packet = new Packet(Op.BlacksmithingMiniGame, creature.EntityId); // Untested if this is actually the deviation/cursor size, // but Tailoring does something very similar. Just like with // Tailoring, wrong values cause failed games. packet.PutShort((short)deviation); foreach (var dot in dots) { packet.PutShort((short)dot.X); packet.PutShort((short)dot.Y); packet.PutFloat(dot.TimeDisplacement); packet.PutShort((short)dot.Deviation); } packet.PutLong(prop.EntityId); packet.PutInt(0); packet.PutLong(item.EntityId); packet.PutInt(0); creature.Client.Send(packet); }
/// <summary> /// Sends EntitiesDisappear to client, unless entities is empty. /// </summary> /// <param name="client"></param> /// <param name="entities"></param> public static void EntitiesDisappear(ChannelClient client, IEnumerable<Entity> entities) { var count = (short)entities.Count(); if (count < 1) return; var packet = new Packet(Op.EntitiesDisappear, MabiId.Broadcast); packet.PutShort(count); foreach (var entity in entities) { packet.PutShort((short)entity.DataType); packet.PutLong(entity.EntityId); } client.Send(packet); }
/// <summary> /// Sends EntrustmentChanceUpdate to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="chance"></param> /// <param name="unkShort"></param> public static void EntrustmentChanceUpdate(Creature creature, float chance, SkillRank skillRank) { var packet = new Packet(Op.EntrustmentChanceUpdate, creature.EntityId); packet.PutFloat(chance); packet.PutShort((short)skillRank); creature.Client.Send(packet); }
/// <summary> /// Broadcasts RankUp in range of creature. /// </summary> /// <remarks> /// The second parameter is the rank, but doesn't seem to be necessary. /// </remarks> /// <param name="creature"></param> /// <param name="skillId">Excluded if 0</param> public static void RankUp(Creature creature, SkillId skillId = 0) { var packet = new Packet(Op.RankUp, creature.EntityId); if (skillId > 0) packet.PutUShort((ushort)skillId); packet.PutShort(1); // Rank creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends EntitiesAppear to client, unless entities is empty. /// </summary> /// <param name="client"></param> /// <param name="entities"></param> public static void EntitiesAppear(ChannelClient client, IEnumerable<Entity> entities) { var count = (short)entities.Count(); if (count < 1) return; var packet = new Packet(Op.EntitiesAppear, MabiId.Broadcast); packet.PutShort(count); foreach (var entity in entities) { var data = Packet.Empty().AddPublicEntityInfo(entity).Build(false); packet.PutShort((short)entity.DataType); packet.PutInt(data.Length); packet.PutBin(data); } client.Send(packet); }
/// <summary> /// Sends ChannelInfoRequestR to client. /// </summary> /// <param name="client"></param> /// <param name="info">Negative response if null.</param> /// <param name="characterId"></param> public static void ChannelInfoRequestR(LoginClient client, ChannelInfo info, long characterId) { var packet = new Packet(Op.ChannelInfoRequestR, MabiId.Channel); packet.PutByte(info != null); if (info != null) { packet.PutString(info.ServerName); packet.PutString(info.Name); packet.PutShort(6); // Channel "Id"? (seems to be equal to channel nr) packet.PutString(info.Host); packet.PutString(info.Host); packet.PutShort((short)info.Port); packet.PutShort((short)(info.Port + 2)); packet.PutInt(1); packet.PutLong(characterId); } client.Send(packet); }
/// <summary> /// Sends EntitiesAppear to client, unless entities is empty. /// </summary> /// <param name="client"></param> /// <param name="entities"></param> public static void EntitiesAppear(ChannelClient client, IEnumerable<Entity> entities) { // Count() is much faster then creating a list, speed being // important in this method. var count = (short)entities.Count(); if (count < 1) return; var packet = new Packet(Op.EntitiesAppear, MabiId.Broadcast); packet.PutShort(count); foreach (var entity in entities) { var data = Packet.Empty().AddPublicEntityInfo(entity).Build(); packet.PutShort((short)entity.DataType); packet.PutInt(data.Length); packet.PutBin(data); } client.Send(packet); }
/// <summary> /// Broadcasts Effect in range of creature. /// </summary> /// <remarks> /// Parameters have to be casted to the proper type, use carefully! /// </remarks> /// <param name="creature"></param> /// <param name="parameters"></param> public static void Effect(Creature creature, int effectId, params object[] parameters) { var packet = new Packet(Op.Effect, creature.EntityId); packet.PutInt(effectId); foreach (var p in parameters) { if (p is byte) packet.PutByte((byte)p); else if (p is bool) packet.PutByte((bool)p); else if (p is short) packet.PutShort((short)p); else if (p is int) packet.PutInt((int)p); else if (p is long) packet.PutLong((long)p); else if (p is float) packet.PutFloat((float)p); else if (p is string) packet.PutString((string)p); else throw new Exception("Unsupported effect parameter: " + p.GetType()); } creature.Region.Broadcast(packet, creature); }
/// <summary> /// Broadcasts Effect in range of creature. /// </summary> /// <param name="creature"></param> /// <param name="instrument"></param> /// <param name="quality"></param> /// <param name="compressedMML"></param> /// <param name="rndScore"></param> public static void PlayEffect(Creature creature, InstrumentType instrument, PlayingQuality quality, string compressedMML, int rndScore) { var packet = new Packet(Op.Effect, creature.EntityId); packet.PutInt(E.PlayMusic); packet.PutByte(compressedMML != null); // has scroll if (compressedMML != null) packet.PutString(compressedMML); else packet.PutInt(rndScore); packet.PutInt(0); packet.PutShort(0); packet.PutInt(14113); // ? packet.PutByte((byte)quality); packet.PutByte((byte)instrument); packet.PutByte(0); packet.PutByte(0); packet.PutByte(1); // loops // Singing? //packet.PutByte(0); //packet.PutByte(1); creature.Region.Broadcast(packet, creature); }
/// <summary> /// Broadcasts LevelUp in range of creature. /// </summary> /// <param name="creature"></param> public static void LevelUp(Creature creature) { var packet = new Packet(Op.LevelUp, creature.EntityId); packet.PutShort(creature.Level); creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends StatUpdatePublic to creature's in range, /// or StatUpdatePrivate to creature's client. /// </summary> /// <remarks> /// In private mode this packet has simply 4 lists. /// - A list of stats and their (new) values. /// - A list of (new) regens. /// - A list of regens to remove (by id). /// - A list of regens to update, with new change and max values. /// (The last one is speculation.) /// Since it's private, it's only sent to the creature's client, /// and they get every stat and regen. /// /// In public mode the same information is sent, but limited to stats /// like life, that's required for displaying life bars for others. /// It also has 3 more lists, that seem to do almost the same as the /// last 3 of private, regens, removing, and updating. /// - Some regens are sent in the first list, some in the second. /// (Like life vs injuries when using Rest.) /// - Regens that are to be removed are sent in both lists. /// - Updates are only sent in the first list. /// More research is required, to find out what the second lists /// actually do. /// </remarks> public static void StatUpdate(Creature creature, StatUpdateType type, ICollection<Stat> stats, ICollection<StatRegen> regens, ICollection<StatRegen> regensRemove, ICollection<StatRegen> regensUpdate) { var packet = new Packet(type == StatUpdateType.Public ? Op.StatUpdatePublic : Op.StatUpdatePrivate, creature.EntityId); packet.PutByte((byte)type); // Stats if (stats == null) packet.PutInt(0); else { packet.PutInt(stats.Count); foreach (var stat in stats) { packet.PutInt((int)stat); switch (stat) { case Stat.Height: packet.PutFloat(creature.Height); break; case Stat.Weight: packet.PutFloat(creature.Weight); break; case Stat.Upper: packet.PutFloat(creature.Upper); break; case Stat.Lower: packet.PutFloat(creature.Lower); break; case Stat.CombatPower: packet.PutFloat(creature.CombatPower); break; case Stat.Level: packet.PutShort(creature.Level); break; case Stat.AbilityPoints: packet.PutShort(creature.AbilityPoints); break; case Stat.Experience: packet.PutLong(AuraData.ExpDb.CalculateRemaining(creature.Level, creature.Exp) * 1000); break; case Stat.Life: packet.PutFloat(creature.Life); break; case Stat.LifeMax: packet.PutFloat(creature.LifeMaxBaseTotal); break; case Stat.LifeMaxMod: packet.PutFloat(creature.StatMods.Get(Stat.LifeMaxMod)); break; case Stat.LifeInjured: packet.PutFloat(creature.LifeInjured); break; case Stat.LifeMaxFoodMod: packet.PutFloat(creature.LifeFoodMod); break; case Stat.Mana: packet.PutFloat(creature.Mana); break; case Stat.ManaMax: packet.PutFloat(creature.ManaMaxBaseTotal); break; case Stat.ManaMaxMod: packet.PutFloat(creature.StatMods.Get(Stat.ManaMaxMod)); break; case Stat.ManaMaxFoodMod: packet.PutFloat(creature.ManaFoodMod); break; case Stat.Stamina: packet.PutFloat(creature.Stamina); break; case Stat.Hunger: packet.PutFloat(creature.StaminaHunger); break; case Stat.StaminaMax: packet.PutFloat(creature.StaminaMaxBaseTotal); break; case Stat.StaminaMaxMod: packet.PutFloat(creature.StatMods.Get(Stat.StaminaMaxMod)); break; case Stat.StaminaMaxFoodMod: packet.PutFloat(creature.StaminaFoodMod); break; case Stat.StrMod: packet.PutFloat(creature.StatMods.Get(Stat.StrMod)); break; case Stat.DexMod: packet.PutFloat(creature.StatMods.Get(Stat.DexMod)); break; case Stat.IntMod: packet.PutFloat(creature.StatMods.Get(Stat.IntMod)); break; case Stat.LuckMod: packet.PutFloat(creature.StatMods.Get(Stat.LuckMod)); break; case Stat.WillMod: packet.PutFloat(creature.StatMods.Get(Stat.WillMod)); break; case Stat.Str: packet.PutFloat(creature.StrBaseTotal); break; case Stat.Int: packet.PutFloat(creature.IntBaseTotal); break; case Stat.Dex: packet.PutFloat(creature.DexBaseTotal); break; case Stat.Will: packet.PutFloat(creature.WillBaseTotal); break; case Stat.Luck: packet.PutFloat(creature.LuckBaseTotal); break; case Stat.StrFoodMod: packet.PutFloat(creature.StrFoodMod); break; case Stat.DexFoodMod: packet.PutFloat(creature.DexFoodMod); break; case Stat.IntFoodMod: packet.PutFloat(creature.IntFoodMod); break; case Stat.LuckFoodMod: packet.PutFloat(creature.LuckFoodMod); break; case Stat.WillFoodMod: packet.PutFloat(creature.WillFoodMod); break; case Stat.DefenseBase: packet.PutShort((short)creature.DefenseBase); break; case Stat.ProtectionBase: packet.PutFloat(creature.ProtectionBase); break; case Stat.DefenseBaseMod: packet.PutShort((short)(creature.DefenseBaseModClient); break; //Since client already updates Defense, remove the STR defense from showing if combat renewal is off. case Stat.ProtectionBaseMod: packet.PutFloat(creature.ProtectionBaseMod); break; case Stat.DefenseMod: packet.PutShort((short)creature.DefenseMod); break; case Stat.ProtectionMod: packet.PutFloat(creature.ProtectionMod); break; case Stat.BalanceBase: packet.PutShort((short)(creature.BalanceBase)); break; case Stat.BalanceBaseMod: packet.PutShort((short)(creature.BalanceBaseMod)); break; case Stat.RightBalanceMod: packet.PutShort((short)creature.RightBalanceMod); break; case Stat.LeftBalanceMod: packet.PutShort((short)creature.LeftBalanceMod); break; case Stat.CriticalBase: packet.PutFloat(creature.CriticalBase); break; case Stat.CriticalBaseMod: packet.PutFloat(creature.CriticalBaseMod); break; case Stat.RightCriticalMod: packet.PutFloat(creature.RightCriticalMod); break; case Stat.LeftCriticalMod: packet.PutFloat(creature.LeftCriticalMod); break; case Stat.AttackMinBase: packet.PutShort((short)creature.AttackMinBase); break; case Stat.AttackMaxBase: packet.PutShort((short)creature.AttackMaxBase); break; case Stat.AttackMinBaseMod: packet.PutShort((short)creature.AttackMinBaseMod); break; case Stat.AttackMaxBaseMod: packet.PutShort((short)creature.AttackMaxBaseMod); break; case Stat.AttackMinMod: packet.PutShort((short)creature.AttackMinMod); break; case Stat.AttackMaxMod: packet.PutShort((short)creature.AttackMaxMod); break; case Stat.RightAttackMinMod: packet.PutShort((short)creature.RightAttackMinMod); break; case Stat.RightAttackMaxMod: packet.PutShort((short)creature.RightAttackMaxMod); break; case Stat.LeftAttackMinMod: packet.PutShort((short)creature.LeftAttackMinMod); break; case Stat.LeftAttackMaxMod: packet.PutShort((short)creature.LeftAttackMaxMod); break; case Stat.InjuryMinBaseMod: packet.PutShort((short)creature.InjuryMinBaseMod); break; case Stat.InjuryMaxBaseMod: packet.PutShort((short)creature.InjuryMaxBaseMod); break; case Stat.Age: packet.PutShort((short)creature.Age); break; // Client might crash with a mismatching value, // take a chance and put an int by default. default: Log.Warning("StatUpdate: Unknown stat '{0}'.", stat); packet.PutInt(0); break; } } } // Regens if (regens == null) packet.PutInt(0); else { packet.PutInt(regens.Count); foreach (var regen in regens) packet.AddRegen(regen); } // Regens to Remove if (regensRemove == null) packet.PutInt(0); else { packet.PutInt(regensRemove.Count); foreach (var regen in regensRemove) packet.PutInt(regen.Id); } // ? // Maybe update of change and max? if (regensUpdate == null) packet.PutInt(0); else { packet.PutInt(regensUpdate.Count); foreach (var regen in regensUpdate) { packet.PutInt(regen.Id); packet.PutFloat(regen.Change); packet.PutFloat(regen.Max); } } if (type == StatUpdateType.Public) { // Another list of regens...? packet.PutInt(0); if (regensRemove == null) packet.PutInt(0); else { // Regens to Remove (again...?) packet.PutInt(regensRemove.Count); foreach (var regen in regensRemove) packet.PutInt(regen.Id); } // Update? packet.PutInt(0); } if (type == StatUpdateType.Private) creature.Client.Send(packet); else if (creature.Region != Region.Limbo) creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends CombatSetAimR to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="targetEntityId">Set 0 for negative response.</param> /// <param name="skillId"></param> /// <param name="unkByte"></param> public static void CombatSetAimR(Creature creature, long targetEntityId, SkillId skillId, byte unkByte) { var packet = new Packet(Op.CombatSetAimR, creature.EntityId); packet.PutByte(targetEntityId != 0); if (targetEntityId != 0) { packet.PutLong(targetEntityId); packet.PutShort((short)skillId); packet.PutByte(unkByte); } creature.Client.Send(packet); }
/// <summary> /// Broadcasts VisualChat to creatures in range. /// </summary> /// <param name="creature"></param> /// <param name="url"></param> /// <param name="width"></param> /// <param name="height"></param> public static void VisualChat(Creature creature, string url, short width, short height) { var packet = new Packet(Op.VisualChat, creature.EntityId); packet.PutString(creature.Name); packet.PutString(url); packet.PutShort(width); packet.PutShort(height); packet.PutByte(0); creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends AgeUpEffect to creature's client. /// </summary> /// <remarks> /// Notice + Light effect. /// Effect is only played for ages 1~25. /// </remarks> /// <param name="creature"></param> /// <param name="age"></param> public static void AgeUpEffect(Creature creature, short age) { var packet = new Packet(Op.AgeUpEffect, creature.EntityId); packet.PutShort(age); creature.Client.Send(packet); }
/// <summary> /// Broadcasts DeadFeather in range of creature. /// </summary> /// <param name="creature"></param> public static void DeadFeather(Creature creature) { var bits = (int)creature.DeadMenu.Options; var flags = new List<int>(); flags.Add(0); // Break down options bit by bit, and add them to flags if set. for (var i = 1; bits != 0; ++i, bits >>= 1) { if ((bits & 1) != 0) flags.Add(i); } var packet = new Packet(Op.DeadFeather, creature.EntityId); packet.PutShort((short)flags.Count); foreach (var flag in flags) packet.PutInt(flag); packet.PutByte(0); creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends SkillComplete to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="skillId"></param> /// <param name="entityId"></param> /// <param name="unkInt"></param> /// <param name="unkShort"></param> public static void SkillCompleteUnk(Creature creature, SkillId skillId, long entityId, int unkInt, short unkShort) { var packet = new Packet(Op.SkillCompleteUnk, creature.EntityId); packet.PutUShort((ushort)skillId); packet.PutLong(entityId); packet.PutInt(unkInt); packet.PutShort(unkShort); creature.Client.Send(packet); }
/// <summary> /// Sends ProductionSuccessRequestR to creature's client, informing it /// about the success rate it requested. /// </summary> /// <remarks> /// This version of the packet is used for Tailoring and Blacksmithing. /// </remarks> /// <param name="creature"></param> /// <param name="skillId">Skill the rate is used for.</param> /// <param name="successRate"> /// Bonus success rate, added to the value calculated by the client, /// or the total success rate to use, if totalSuccess is true. /// </param> /// <param name="totalSuccess"> /// If true, the client will display the given successRate, if it's false, /// it will calculate the default rate itself and add successRate as bonus. /// </param> public static void ProductionSuccessRequestR(Creature creature, SkillId skillId, float successRate, bool totalSuccess, float unkFloat) { var gp = new Packet(Op.ProductionSuccessRequestR, creature.EntityId); gp.PutByte(1); gp.PutUShort((ushort)skillId); gp.PutShort(6); gp.PutFloat(successRate); gp.PutByte(0); gp.PutByte(totalSuccess); gp.PutFloat(unkFloat); creature.Client.Send(gp); }
/// <summary> /// Broadcasts CombatSetAimR in creature's range, putting it in /// "aim mode". /// </summary> /// <param name="creature"></param> /// <param name="targetEntityId">Set 0 for negative response.</param> /// <param name="skillId"></param> /// <param name="flag"></param> public static void CombatSetAimR(Creature creature, long targetEntityId, SkillId skillId, byte flag) { var packet = new Packet(Op.CombatSetAimR, creature.EntityId); packet.PutByte(targetEntityId != 0); if (targetEntityId != 0) { packet.PutLong(targetEntityId); packet.PutShort((short)skillId); packet.PutByte(flag); } creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends TailoringMiniGame to creature's client to start tailoring minigame. /// </summary> /// <remarks> /// The offsets specify the distance of the "stitch points" from the /// center of the 200x200px minigame field. X is 1:1 the distance /// from the center for each point, while Y gets added up. /// The point closest to the center is Y/2 px away, the second Y/2+Y, /// and the third Y/2+Y*2. /// /// Deviation is an array of 6 values, one for each point, that specify /// the amount of pixels your clicks can deviate from the actual /// position you clicked. For example, if you click on 60x180 for the /// first point, and the first byte in deviation is 3, the actual /// position sent to the server is between 57x177 and 63x183, /// randomized by the client. /// If the deviation values are too big, the minigame glitches and is /// likely to fail, the biggest value seen in logs was 4. /// </remarks> /// <param name="creature"></param> /// <param name="item">Item that is to be finished.</param> /// <param name="xOffset">Offset of stitch points on the x-axis.</param> /// <param name="yOffset">Offset of stitch points on the y-axis.</param> /// <param name="deviation">Randomization for the 6 stitch points.</param> public static void TailoringMiniGame(Creature creature, Item item, int xOffset, int yOffset, byte[] deviation, byte deviation2) { if (deviation == null || deviation.Length != 6) throw new ArgumentException("rng needs exactly 6 values."); var packet = new Packet(Op.TailoringMiniGame, creature.EntityId); packet.PutShort((short)xOffset); packet.PutShort((short)yOffset); packet.PutBin(deviation); // Modifies cursor size, glitches the minigame if smaller than // deviation? Seems to be a general deviation that applies to // all points? Setting all deviations, incl this, to 0 gives // the most spot-on results. packet.PutByte(deviation2); packet.PutLong(0); packet.PutInt(0); packet.PutLong(item.EntityId); packet.PutInt(0); creature.Client.Send(packet); }
/// <summary> /// Broadcasts UseMotion and CancelMotion (if cancel is true) in creature's region. /// </summary> /// <param name="creature"></param> /// <param name="category"></param> /// <param name="type"></param> /// <param name="loop"></param> /// <param name="cancel"></param> public static void UseMotion(Creature creature, int category, int type, bool loop = false, bool cancel = false) { if (cancel) CancelMotion(creature); // Do motion var packet = new Packet(Op.UseMotion, creature.EntityId); packet.PutInt(category); packet.PutInt(type); packet.PutByte(loop); packet.PutShort(0); // XXX: Why is it region and not range again...? Maybe so you see // the motion when coming into range? ... does that work? creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends xInfoRequestR to client. /// </summary> /// <param name="client"></param> /// <param name="op"></param> /// <param name="character"></param> /// <param name="items"></param> public static void CharacterInfoRequestR(LoginClient client, int op, Character character, List<Item> items) { var packet = new Packet(op, MabiId.Login); packet.PutByte(character != null); if (character != null) { packet.PutString(character.Server); packet.PutLong(character.EntityId); packet.PutByte(1); packet.PutString(character.Name); packet.PutString(""); packet.PutString(""); packet.PutInt(character.Race); packet.PutByte(character.SkinColor); packet.PutShort(character.EyeType); packet.PutByte(character.EyeColor); packet.PutByte(character.MouthType); packet.PutUInt((uint)character.State); packet.PutFloat(character.Height); packet.PutFloat(character.Weight); packet.PutFloat(character.Upper); packet.PutFloat(character.Lower); packet.PutInt(0); packet.PutInt(0); packet.PutInt(0); packet.PutByte(0); packet.PutInt(0); packet.PutByte(0); packet.PutInt((int)character.Color1); packet.PutInt((int)character.Color2); packet.PutInt((int)character.Color3); packet.PutFloat(0.0f); packet.PutString(""); packet.PutFloat(49.0f); packet.PutFloat(49.0f); packet.PutFloat(0.0f); packet.PutFloat(49.0f); // [180800, NA196 (14.10.2014)] ? { packet.PutShort(0); } packet.PutInt(0); packet.PutInt(0); packet.PutShort(0); packet.PutLong(0); packet.PutString(""); packet.PutByte(0); packet.PutInt(items.Count); foreach (var item in items) { packet.PutLong(item.Id); packet.PutBin(item.Info); } packet.PutInt(0); // PetRemainingTime packet.PutLong(0); // PetLastTime packet.PutLong(0); // PetExpireTime } client.Send(packet); }
/// <summary> /// Sends OpenNpcShop to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="shop"></param> public static void OpenNpcShop(Creature creature, IList<NpcShopTab> tabs) { var packet = new Packet(Op.OpenNpcShop, creature.EntityId); packet.PutString("shopname"); // e.g. TirchonaillShop_Dilys packet.PutByte(0); // 1 in remote shops? packet.PutByte(0); packet.PutInt(0); packet.PutByte((byte)tabs.Count); foreach (var tab in tabs) { packet.PutString("[{0}]{1}", tab.Order, tab.Title); // [160200] ? { packet.PutByte(0); } var items = tab.GetItems(); packet.PutShort((short)items.Count); foreach (var item in items) packet.AddItemInfo(item, ItemPacketType.Private); } creature.Client.Send(packet); }
/// <summary> /// Sends SpecialUnitInfoRequestR to creature's client. /// </summary> /// <param name="creature"></param> public static void SpecialUnitInfoRequestR(Creature creature) { var packet = new Packet(Op.SpecialUnitInfoRequestR, 0); packet.PutInt(0); // count { //002 [............0001] Short : 1 //003 [........00000005] Int : 5 //004 [............0001] Short : 1 } packet.PutInt(0); // count { //093 [............0001] Short : 1 //094 [................] String : g20_mk_kanna //095 [........00000001] Int : 1 //096 [..............01] Byte : 1 //097 [............0003] Short : 3 //098 [............0003] Short : 3 //099 [............0002] Short : 2 //100 [............0001] Short : 1 //101 [............0001] Short : 1 //102 [............0001] Short : 1 //103 [............0001] Short : 1 //104 [................] String : I-I'm sorry. I would like some counseling before I begin my mission. Would that be okay?|I'm sorry, captain. I really need some counseling. I'm so exhausted. //105 [........00000005] Int : 5 //106 [............0001] Short : 1 //107 [........0000001E] Int : 30 //108 [................] String : Hee hee, all clear, sir! //109 [................] String : I will report on the mission! //110 [............0002] Short : 2 //111 [........00000064] Int : 100 //112 [................] String : Hee hee, all clear, sir! No wait, not all clear! I need your help! //113 [................] String : I will report on the mission! //114 [............0003] Short : 3 //115 [........00000046] Int : 70 //116 [................] String : Ahaha, sorry. I kind of broke something... So I'm helping them out in return. Ahaha.|Ahh, what do I do? I was practicing my sword strokes at the inn, and split the bed in two! //117 [................] String : I will report on the mission! //118 [............0004] Short : 4 //119 [........00000064] Int : 100 //120 [................] String : Ahaha, I left for my mission without bringing my wallet... So I've been sleeping rough the whole time.|Gahh... Captain, have you seen my sword? Where is it? Where did I leave it? //121 [................] String : I will report on the mission! //122 [............0005] Short : 5 //123 [........00000064] Int : 100 //124 [................] String : Hee hee hee, that's it for missions! Don't worry. I'm completely focused on my duties so I can see you that much sooner!|Heh, what do you know? Doing things your way makes everything go much smoother, captain. //125 [................] String : I will report on the mission! } packet.PutInt(0); // count { //292 [........0009EB75] Int : 650101 //293 [............0001] Short : 1 //294 [............0000] Short : 0 //295 [..............01] Byte : 1 //296 [..............00] Byte : 0 //297 [............0000] Short : 0 //298 [............0000] Short : 0 //299 [000000000020F580] Long : 2160000 //300 [................] String : Sharp teeth from mutant animals were found near Ciar Dungeon. //301 [................] String : All dungeons around Tir Chonaill were checked, but no pollutant was found. //302 [............0001] Short : 1 //303 [............0000] Short : 0 //304 [............0000] Short : 0 //305 [............0001] Short : 1 //306 [............0000] Short : 0 //307 [............0000] Short : 0 //308 [............0000] Short : 0 //309 [................] String : Check Foul Plume //310 [................] String : Mutant animals appear around Ciar Dungeon time to time. Investigate to see if there are any remaining traces of mutant animals. //311 [................] String : * [Reward] Basic Baltane Seal x1\n* Command EXP 5\n* Training Points 3\n //312 [........00000000] Int : 0 } packet.PutLong(604800000); packet.PutLong(2160000); packet.PutShort(2); packet.PutShort(30); packet.PutInt(1); packet.PutShort(1); packet.PutShort(1); creature.Client.Send(packet); }
/// <summary> /// Sends SkillUse to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="skillId"></param> /// <param name="part"></param> /// <param name="x"></param> /// <param name="y"></param> public static void SkillUseDye(Creature creature, SkillId skillId, int part, short x, short y) { var packet = new Packet(Op.SkillUse, creature.EntityId); packet.PutUShort((ushort)skillId); packet.PutInt(part); packet.PutShort(x); packet.PutShort(y); creature.Client.Send(packet); }
/// <summary> /// Sends PlayCutscene to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="cutscene"></param> public static void PlayCutscene(Creature creature, Cutscene cutscene) { var packet = new Packet(Op.PlayCutscene, MabiId.Channel); packet.PutLong(creature.EntityId); packet.PutLong(cutscene.Leader.EntityId); packet.PutString(cutscene.Name); packet.PutInt(cutscene.Actors.Count); foreach (var actor in cutscene.Actors) { var subPacket = Packet.Empty(); subPacket.AddCreatureInfo(actor.Value, CreaturePacketType.Public); var bArr = subPacket.Build(); packet.PutString(actor.Key); packet.PutShort((short)bArr.Length); packet.PutBin(bArr); } packet.PutInt(1); // count? packet.PutLong(creature.EntityId); // TODO: Send to whole party? creature.Client.Send(packet); }
/// <summary> /// Broadcasts Effect in range of creature, with the given packet id. /// </summary> /// <remarks> /// Parameters have to be casted to the proper type, use carefully! /// </remarks> /// <param name="id"></param> /// <param name="entity"></param> /// <param name="delay">Delay in milliseconds</param> /// <param name="effectId"></param> /// <param name="parameters"></param> public static void EffectDelayed(long id, Entity entity, int delay, int effectId, params object[] parameters) { var packet = new Packet(Op.EffectDelayed, id); packet.PutInt(delay); packet.PutInt(effectId); foreach (var p in parameters) { if (p is byte) packet.PutByte((byte)p); else if (p is bool) packet.PutByte((bool)p); else if (p is short) packet.PutShort((short)p); else if (p is ushort) packet.PutUShort((ushort)p); else if (p is int) packet.PutInt((int)p); else if (p is uint) packet.PutUInt((uint)p); else if (p is long) packet.PutLong((long)p); else if (p is ulong) packet.PutULong((ulong)p); else if (p is float) packet.PutFloat((float)p); else if (p is string) packet.PutString((string)p); else throw new Exception("Unsupported effect parameter: " + p.GetType()); } entity.Region.Broadcast(packet, entity); }
/// <summary> /// Sends OpenNpcShop to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="shop"></param> public static void OpenNpcShop(Creature creature, NpcShop shop) { var packet = new Packet(Op.OpenNpcShop, creature.EntityId); packet.PutString("shopname"); packet.PutByte(0); packet.PutByte(0); packet.PutInt(0); packet.PutByte((byte)shop.Tabs.Count); foreach (var tab in shop.Tabs) { packet.PutString("[{0}]{1}", tab.Order, tab.Title); // [160200] ? { packet.PutByte(0); } packet.PutShort((short)tab.Items.Count); foreach (var item in tab.Items) packet.AddItemInfo(item, ItemPacketType.Private); } creature.Client.Send(packet); }
/// <summary> /// Broadcasts DeadFeather in range of creature. /// </summary> /// <param name="creature"></param> public static void DeadFeather(Creature creature) { var packet = new Packet(Op.DeadFeather, creature.EntityId); packet.PutShort(1); packet.PutInt(0); packet.PutByte(0); creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends ItemExpUpdate to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="item"></param> public static void ItemExpUpdate(Creature creature, Item item) { var packet = new Packet(Op.ItemExpUpdate, creature.EntityId); packet.PutLong(item.EntityId); packet.PutShort(item.OptionInfo.Experience); packet.PutByte(item.OptionInfo.EP); creature.Client.Send(packet); }
/// <summary> /// Sends SwitchChannelR to creature's client /// </summary> /// <param name="creature"></param> /// <param name="channel">Negative answer if null.</param> public static void SwitchChannelR(Creature creature, ChannelInfo channel) { var packet = new Packet(Op.SwitchChannelR, MabiId.Channel); packet.PutByte(channel != null); if (channel != null) { packet.PutString(channel.ServerName); packet.PutString(channel.Name); packet.PutShort(1); // Channel id packet.PutString(channel.Host); packet.PutString(channel.Host); packet.PutUShort((ushort)channel.Port); packet.PutUShort((ushort)(channel.Port + 4)); } creature.Client.Send(packet); }