Exemple #1
0
        /// <summary>
        /// Broadcasts Effect packet in range. Parameters can be added,
        /// but you have to watch the types.
        /// </summary>
        /// <param name="delay">Delay in milliseconds</param>
        public static void EffectDelayed(uint effect, uint delay, MabiEntity source, params object[] args)
        {
            var packet = new MabiPacket(Op.EffectDelayed, source.Id);
            packet.PutInt(delay);
            packet.PutInt(effect);
            foreach (var arg in args)
            {
                if (!(arg is bool))
                    packet.Put(arg);
                else
                    packet.PutByte((bool)arg);
            }

            WorldManager.Instance.Broadcast(packet, SendTargets.Range, source);
        }
Exemple #2
0
 public static MabiPacket AcquireStat(MabiCreature creature, string statName, double amount)
 {
     var p = new MabiPacket(Op.AcquireInfo, creature.Id);
     p.PutString("<xml type='{0}' value='{1}' simple='true' onlyLog='false' />", statName, (uint)amount);
     p.PutInt(3000);
     return p;
 }
Exemple #3
0
 public static MabiPacket AcquireAp(MabiCreature creature, uint amount)
 {
     var p = new MabiPacket(Op.AcquireInfo, creature.Id);
     p.PutString("<xml type='ap' value='{0}' simple='true' onlyLog='false' />", amount);
     p.PutInt(3000);
     return p;
 }
Exemple #4
0
 public static MabiPacket AcquireExp(MabiCreature creature, uint amount)
 {
     var p = new MabiPacket(Op.AcquireInfo, creature.Id);
     p.PutString("<xml type='exp' value='{0}'/>", amount);
     p.PutInt(3000);
     return p;
 }
Exemple #5
0
 public static MabiPacket AcquireItem(MabiCreature creature, uint cls, uint amount)
 {
     var p = new MabiPacket(Op.AcquireInfo, creature.Id);
     p.PutString("<xml type='item' classid='{0}' value='{1}'/>", cls, amount);
     p.PutInt(3000);
     return p;
 }
Exemple #6
0
        /// <summary>
        /// Sends item box packet for fixed dyed item to client.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="creature"></param>
        /// <param name="itemId"></param>
        public static void AcquireDyedItem(Client client, MabiCreature creature, ulong itemId)
        {
            var packet = new MabiPacket(Op.AcquireInfo2, creature.Id);
            packet.PutString("<xml type='fixed_color_dyeing' objectid='{0}'/>", itemId);
            packet.PutInt(3000);

            client.Send(packet);
        }
Exemple #7
0
        /// <summary>
        /// Fixed dye complete
        /// </summary>
        /// <param name="client"></param>
        /// <param name="creature"></param>
        /// <param name="skillId"></param>
        /// <param name="part"></param>
        public static void DyeSkillComplete(Client client, MabiCreature creature, SkillConst skillId, uint part)
        {
            var packet = new MabiPacket(Op.SkillComplete, creature.Id);
            packet.PutShort((ushort)skillId);
            packet.PutInt(part);

            client.Send(packet);
        }
Exemple #8
0
        /// <summary>
        /// Sends ConvertGpR to creature's client.
        /// </summary>
        public static void ConvertGpR(MabiCreature creature, bool success, uint amount)
        {
            var packet = new MabiPacket(Op.ConvertGpR, creature.Id);
            packet.PutByte(success);
            packet.PutInt(amount);

            creature.Client.Send(packet);
        }
Exemple #9
0
        /// <summary>
        /// Sends response to accept gift. Response will be negative if gift is null.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="gift"></param>
        public static void AcceptGiftResponse(LoginClient client, Gift gift)
        {
            var packet = new MabiPacket(Op.AcceptGiftR, Id.Login);
            if (gift != null)
            {
                packet.PutByte(true);
                packet.PutByte(gift.IsCharacter);
                packet.PutInt(0); // ?
                packet.PutInt(0); // ?
                packet.PutInt(gift.Type);
                // ?
            }
            else
            {
                packet.PutByte(false);
            }

            client.Send(packet);
        }
Exemple #10
0
        public static MabiPacket AcquireItem(MabiCreature creature, ulong itemId)
        {
            var p = new MabiPacket(Op.AcquireInfo, creature.Id);
            p.PutString("<xml type='item' objectid='{0}'/>", itemId);
            p.PutInt(3000);

            // 001 [................] String : <xml type='stamina' value='17' simple='true' onlyLog='false' />
            // 002 [........00000BB8] Int    : 3000

            return p;
        }
Exemple #11
0
 /// <summary>
 /// Sends info on guild membership status changed. Pass null for guild to remove.
 /// </summary>
 /// <param name="guild"></param>
 /// <param name="creature"></param>
 /// <returns></returns>
 public static MabiPacket GuildMembershipChanged(MabiGuild guild, MabiCreature creature, GuildMemberRank rank = GuildMemberRank.Member)
 {
     var p = new MabiPacket(Op.GuildMembershipChanged, creature.Id);
     if (guild == null)
     {
         p.PutInt(0);
     }
     else
     {
         p.PutInt(1);
         p.PutString(guild.Name);
         p.PutLong(guild.Id);
         p.PutInt((uint)rank); // (5) Member Rank?
         p.PutByte(0);
     }
     return p;
 }
Exemple #12
0
        public void AddMemberPacket(MabiPacket packet, MabiCreature member)
        {
            if (!Members.Contains(member))
                return;

            packet.PutInt(this.GetMemberNumber(member));
            packet.PutLong(member.Id);
            packet.PutString(member.Name);
            packet.PutByte(1); // ?
            packet.PutInt(member.Region);
            MabiVertex loc = member.GetPosition();
            packet.PutInt(loc.X);
            packet.PutInt(loc.Y);
            packet.PutByte(0); // ?
            packet.PutInt((uint)((member.Life * 100) / member.LifeMax));
            packet.PutInt((uint)member.LifeMax);
            packet.PutLong(0); // ?
        }
Exemple #13
0
        /// <summary>
        /// Broadcasts Phoenix Feather above dead creature effect in creature's range.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="creature"></param>
        public static void DeadFeather(MabiCreature creature, DeadMenuOptions options)
        {
            var bits = (uint)options;
            var flags = new List<uint>();

            // Break down options bit by bit, and add them to flags if set.
            for (uint i = 1; bits != 0; ++i, bits >>= 1)
            {
                if ((bits & 1) != 0)
                    flags.Add(i);
            }

            var packet = new MabiPacket(Op.DeadFeather, creature.Id);

            packet.PutShort((ushort)flags.Count);
            foreach (var f in flags)
                packet.PutInt(f);

            packet.PutByte(0);

            WorldManager.Instance.Broadcast(packet, SendTargets.Range, creature);
        }
Exemple #14
0
        public static void OpenNPCShop(WorldClient client, MabiShop shop)
        {
            var packet = new MabiPacket(Op.OpenNPCShop, client.Character.Id);
            packet.PutString("shopname");
            packet.PutByte(0);
            packet.PutByte(0);
            packet.PutInt(0);
            packet.PutByte((byte)shop.Tabs.Count);
            for (var i = 0; i < shop.Tabs.Count; ++i)
            {
                packet.PutString("[{0}]{1}", i, shop.Tabs[i].Name);

                // [160200] ?
                {
                    packet.PutByte(0);
                }

                packet.PutShort((ushort)shop.Tabs[i].Items.Count);
                foreach (var item in shop.Tabs[i].Items)
                    packet.AddItemInfo(item, ItemPacketType.Private);
            }
            client.Send(packet);
        }
Exemple #15
0
        /// <summary>
        /// Sends view equipment to client. Response is negative if items is null.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="targetId"></param>
        /// <param name="items"></param>
        public static void ViewEquipmentResponse(WorldClient client, ulong targetId, IEnumerable<MabiItem> items)
        {
            var packet = new MabiPacket(Op.ViewEquipmentR, client.Character.Id);
            if (items != null)
            {
                packet.PutByte(true);
                packet.PutLong(targetId);
                packet.PutInt((ushort)items.Count());
                foreach (var item in items)
                    packet.AddItemInfo(item, ItemPacketType.Private);
            }
            else
            {
                packet.PutByte(false);
            }

            client.Send(packet);
        }
Exemple #16
0
        public static void UnreadMailCount(Client client, MabiCreature creature, uint count)
        {
            var packet = new MabiPacket(Op.UnreadMailCount, creature.Id);
            packet.PutInt(count);

            client.Send(packet);
        }
Exemple #17
0
        public static void EntitiesAppear(Client client, IEnumerable<MabiEntity> entities)
        {
            var packet = new MabiPacket(Op.EntitiesAppear, Id.Broadcast);

            packet.PutShort((ushort)entities.Count());
            foreach (var entity in entities)
            {
                var data = new MabiPacket(0, 0);
                data.AddPublicEntityInfo(entity);
                var dataBytes = data.Build(false);

                packet.PutShort(entity.DataType);
                packet.PutInt((uint)dataBytes.Length);
                packet.PutBin(dataBytes);
            }

            client.Send(packet);
        }
Exemple #18
0
        // tmp
        public static MabiPacket StatRegenStop(MabiCreature creature, StatUpdateType type, params MabiStatRegen[] stats)
        {
            var packet = new MabiPacket((type & StatUpdateType.Public) != 0 ? Op.StatUpdatePublic : Op.StatUpdatePrivate, creature.Id);

            packet.PutByte((byte)type);
            packet.PutSInt(0); // Stats
            packet.PutInt(0); // Regens

            // Stat mod ids to remove
            packet.PutSInt(stats.Count());
            foreach (var mod in stats)
                packet.PutInt(mod.ModId);

            packet.PutInt(0);

            if (type == StatUpdateType.Public)
            {
                packet.PutInt(0);

                // Stat mod ids to remove
                packet.PutSInt(stats.Count());
                foreach (var mod in stats)
                    packet.PutInt(mod.ModId);

                packet.PutInt(0);
            }

            return packet;
        }
Exemple #19
0
        public static MabiPacket StatUpdate(MabiCreature creature, StatUpdateType type, params Stat[] stats)
        {
            var packet = new MabiPacket((type & StatUpdateType.Public) != 0 ? Op.StatUpdatePublic : Op.StatUpdatePrivate, creature.Id);

            packet.PutByte((byte)type);

            // Stats
            packet.PutSInt(stats.Length);
            foreach (var stat in stats)
            {
                packet.PutInt((uint)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(MabiData.ExpDb.CalculateRemaining(creature.Level, creature.Experience) * 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.GetMod(Stat.LifeMaxMod)); break;
                    case Stat.LifeInjured: packet.PutFloat(creature.LifeInjured); break;
                    case Stat.Mana: packet.PutFloat(creature.Mana); break;
                    case Stat.ManaMax: packet.PutFloat(creature.ManaMaxBaseTotal); break;
                    case Stat.ManaMaxMod: packet.PutFloat(creature.StatMods.GetMod(Stat.ManaMaxMod)); break;
                    case Stat.Stamina: packet.PutFloat(creature.Stamina); break;
                    case Stat.Food: packet.PutFloat(creature.StaminaHunger); break;
                    case Stat.StaminaMax: packet.PutFloat(creature.StaminaMaxBaseTotal); break;
                    case Stat.StaminaMaxMod: packet.PutFloat(creature.StatMods.GetMod(Stat.StaminaMaxMod)); break;

                    case Stat.StrMod: packet.PutFloat(creature.StatMods.GetMod(Stat.StrMod)); break;
                    case Stat.DexMod: packet.PutFloat(creature.StatMods.GetMod(Stat.DexMod)); break;
                    case Stat.IntMod: packet.PutFloat(creature.StatMods.GetMod(Stat.IntMod)); break;
                    case Stat.LuckMod: packet.PutFloat(creature.StatMods.GetMod(Stat.LuckMod)); break;
                    case Stat.WillMod: packet.PutFloat(creature.StatMods.GetMod(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.DefenseBaseMod: packet.PutShort((ushort)creature.DefensePassive); break;
                    case Stat.ProtectBaseMod: packet.PutFloat(creature.ProtectionPassive * 100); break;

                    case Stat.DefenseMod: packet.PutShort((ushort)creature.StatMods.GetMod(Stat.DefenseMod)); break;
                    case Stat.ProtectMod: packet.PutFloat(creature.StatMods.GetMod(Stat.ProtectMod)); break;

                    // Client might crash with a mismatching value,
                    // take a chance and put an int by default.
                    default: packet.PutInt(1); break;
                }
            }

            // (New?) Stat regens
            packet.PutSInt(creature.StatRegens.Count);
            foreach (var mod in creature.StatRegens)
                mod.AddToPacket(packet);

            // Stat mod ids to remove?
            packet.PutInt(0);

            packet.PutInt(0);					 // ?

            if (type == StatUpdateType.Public)
            {
                packet.PutInt(0);  				 // ?

                // Stat mod ids to remove?
                packet.PutInt(0);

                packet.PutInt(0);				 // ?
            }

            return packet;
        }
Exemple #20
0
        public override SkillResults Prepare(MabiCreature creature, MabiSkill skill, MabiPacket packet, uint castTime)
        {
            var rnd = RandomProvider.Get();

            // Check for instrument
            if (creature.RightHand == null || creature.RightHand.Type != ItemType.Instrument)
                return SkillResults.Failure;

            // Spawn chair for Cello
            if (creature.RightHand.DataInfo.Instrument == InstrumentType.Cello)
            {
                var pos = creature.GetPosition();

                // Chair prop
                var prop = new MabiProp((!creature.IsGiant ? CelloChair : GiantCelloChair), creature.Region, pos.X, pos.Y, MabiMath.DirToRad(creature.Direction));
                prop.State = "stand";
                WorldManager.Instance.AddProp(prop);

                // Move char
                Send.AssignChair(creature, prop.Id, 1);

                // Update chair
                prop.ExtraData = string.Format("<xml OWNER='{0}' SITCHAR='{0}'/>", creature.Id);
                Send.PropUpdate(prop);

                creature.Temp.SittingProp = prop;
            }

            // Score scrolls go into the magazine pocket and need a SCORE tag.
            // XXX: Is it possbile to play random with a low durability scroll?
            bool hasScroll = (creature.Magazine != null && creature.Magazine.Tags.Has("SCORE") && creature.Magazine.OptionInfo.Durability >= DurabilityUse);

            // Random score if no usable scroll was found.
            uint rndScore = (!hasScroll ? (uint)rnd.Next(RandomScoreMin, RandomScoreMax + 1) : 0);

            // Quality seems to go from 0 (worst) to 3 (best).
            // Should be generated based on skills + random.
            var quality = (PlayingQuality)rnd.Next((int)PlayingQuality.VeryBad, (int)PlayingQuality.VeryGood + 1);

            // Up quality by chance, based on Musical Knowledge
            var knowledge = creature.Skills.Get(SkillConst.MusicalKnowledge);
            if (knowledge != null && rnd.Next(0, 100) < knowledge.RankInfo.Var2)
                quality++;

            if (quality > PlayingQuality.VeryGood)
                quality = PlayingQuality.VeryGood;

            // Save quality before checking perfect play option,
            // we want proper skill training.
            creature.Temp.PlayingInstrumentQuality = quality;

            if (WorldConf.PerfectPlay)
            {
                quality = PlayingQuality.VeryGood;
                Send.ServerMessage(creature.Client, creature, Localization.Get("skills.perfect_play")); // Regardless of the result, perfect play will let your performance sound perfect.
            }

            // Reduce scroll's durability.
            if (hasScroll)
            {
                creature.Magazine.ReduceDurability(DurabilityUse);
                creature.Client.Send(
                    new MabiPacket(Op.ItemDurabilityUpdate, creature.Id)
                    .PutLong(creature.Magazine.Id)
                    .PutInt(creature.Magazine.OptionInfo.Durability)
                );
            }

            // Music effect
            {
                var effect = hasScroll
                    ? PacketCreator.PlayEffect(creature, creature.RightHand.DataInfo.Instrument, quality, creature.Magazine.Tags["SCORE"])
                    : PacketCreator.PlayEffect(creature, creature.RightHand.DataInfo.Instrument, quality, rndScore);
                WorldManager.Instance.Broadcast(effect, SendTargets.Range, creature);
            }

            // Use skill
            {
                var use = new MabiPacket(Op.SkillUse, creature.Id);
                use.PutShort(skill.Info.Id);
                use.PutLong(0);
                use.PutByte(hasScroll);
                if (!hasScroll)
                    use.PutInt(rndScore);
                else
                    use.PutString(creature.Magazine.Tags["SCORE"]);
                use.PutByte((byte)creature.RightHand.DataInfo.Instrument);
                use.PutByte(1);
                use.PutByte(0);
                creature.Client.Send(use);

                creature.ActiveSkillId = skill.Id;
            }

            // Change motion for Battle Mandolin (no idea if this official, but I like it =P) [exec]
            //if (creature.RightHand.Info.Class == 40367)
            //    WorldManager.Instance.CreatureUseMotion(creature, 88, 2, true);

            return SkillResults.Okay;
        }
Exemple #21
0
 /// <summary>
 /// Playing instrument effect (sound and motion) for creature,
 /// based on the given score id (client:score.xml).
 /// </summary>
 /// <param name="creature"></param>
 /// <param name="instrument"></param>
 /// <param name="quality"></param>
 /// <param name="score"></param>
 /// <returns></returns>
 public static MabiPacket PlayEffect(MabiCreature creature, InstrumentType instrument, PlayingQuality quality, uint score)
 {
     var p = new MabiPacket(Op.Effect, creature.Id);
     p.PutInt(Effect.PlayMusic);
     p.PutByte(false);
     p.PutInt(score);
     p.PutInt(0);
     p.PutShort(0);
     p.PutInt(14113);
     p.PutByte((byte)quality);
     p.PutByte((byte)instrument);
     p.PutByte(0);
     p.PutByte(0);
     p.PutByte(1);
     return p;
 }
Exemple #22
0
        public static void UseItemResponse(Client client, MabiCreature creature, bool success, uint itemClass)
        {
            var packet = new MabiPacket(Op.UseItemR, creature.Id);
            packet.PutByte(success);
            if (success)
                packet.PutInt(itemClass);

            client.Send(packet);
        }
Exemple #23
0
        private CommandResult Command_effect(WorldClient client, MabiCreature creature, string[] args, string msg)
        {
            if (args.Length < 2)
                return CommandResult.WrongParameter;

            var p = new MabiPacket(Op.Effect, creature.Id);

            uint id;
            if (!uint.TryParse(args[1], out id))
                return CommandResult.WrongParameter;

            p.PutInt(id);

            for (int i = 2; i < args.Length; ++i)
            {
                var splitted = args[i].Split(':');

                if (splitted[0] == "me")
                {
                    p.PutLong(creature.Id);
                    continue;
                }

                if (splitted.Length < 2)
                    continue;

                splitted[0] = splitted[0].Trim();
                splitted[1] = splitted[1].Trim();

                switch (splitted[0])
                {
                    case "b":
                        {
                            byte val;
                            if (!byte.TryParse(splitted[1], out val))
                                return CommandResult.WrongParameter;
                            p.PutByte(val);
                            break;
                        }
                    case "i":
                        {
                            uint val;
                            if (!uint.TryParse(splitted[1], out val))
                                return CommandResult.WrongParameter;
                            p.PutInt(val);
                            break;
                        }
                    case "s":
                        {
                            p.PutString(splitted[1]);
                            break;
                        }
                }
            }

            WorldManager.Instance.Broadcast(p, SendTargets.Range, creature);

            return CommandResult.Okay;
        }
Exemple #24
0
        /// <summary>
        /// Broadcasts current conditions of creature.
        /// </summary>
        /// <param name="wm"></param>
        /// <param name="creature"></param>
        public static void StatusEffectUpdate(MabiCreature creature)
        {
            var packet = new MabiPacket(Op.StatusEffectUpdate, creature.Id);
            packet.PutLong((ulong)creature.Conditions.A);
            packet.PutLong((ulong)creature.Conditions.B);
            packet.PutLong((ulong)creature.Conditions.C);
            // [150100] New conditions list
            {
                packet.PutLong((ulong)creature.Conditions.D);
            }
            packet.PutInt(0);

            WorldManager.Instance.Broadcast(packet, SendTargets.Range, creature);
        }
Exemple #25
0
        public void AddPartyPacket(MabiPacket packet)
        {
            packet.PutLong(_id); // Party ID
            packet.PutString(Name);
            packet.PutLong(Leader.Id);
            packet.PutByte(IsOpen);
            packet.PutInt((uint)Finish);
            packet.PutInt((uint)ExpShare);
            packet.PutLong(0); // Quest ID?
            packet.PutInt(MaxSize);
            packet.PutInt(Type);
            packet.PutString(Level);
            packet.PutString(Info);
            packet.PutInt((uint)Members.Count);

            foreach (var member in Members)
                AddMemberPacket(packet, member);

            packet.PutByte(0);
        }
Exemple #26
0
        /// <summary>
        /// Sends enter region permission, which kinda makes the client warp.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="creature"></param>
        /// <param name="permission"></param>
        public static void EnterRegionPermission(WorldClient client, MabiCreature creature, bool permission = true)
        {
            var pos = creature.GetPosition();

            var p = new MabiPacket(Op.EnterRegionPermission, Id.World);
            p.PutLong(creature.Id);
            p.PutByte(permission);
            if (permission)
            {
                p.PutInt(creature.Region);
                p.PutInt(pos.X);
                p.PutInt(pos.Y);
            }

            client.Send(p);
        }
Exemple #27
0
        public static void CutsceneStart(WorldClient client, MabiCutscene cutscene)
        {
            var p = new MabiPacket(Op.CutsceneStart, Id.World);
            p.PutLongs(client.Character.Id, cutscene.Leader.Id);
            p.PutString(cutscene.Name);
            p.PutSInt(cutscene.Actors.Count);
            foreach (var a in cutscene.Actors)
            {
                p.PutString(a.Item1);
                p.PutShort((ushort)a.Item2.Length);
                p.PutBin(a.Item2);
            }
            p.PutInt(1);
            p.PutLong(client.Character.Id);

            client.Send(p);
        }
Exemple #28
0
        /// <summary>
        /// Sends unlock for creature.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="creature"></param>
        /// <param name="lockType"></param>
        public static void CharacterUnlock(WorldClient client, MabiCreature creature, uint lockType = 0xEFFFFFFE)
        {
            var p = new MabiPacket(Op.CharacterUnlock, creature.Id);
            p.PutInt(lockType);

            client.Send(p);
        }
Exemple #29
0
        /// <summary>
        /// Sends revive notice to creature's client.
        /// </summary>
        /// <param name="creature"></param>
        public static void Revived(MabiCreature creature)
        {
            var pos = creature.GetPosition();

            var packet = new MabiPacket(Op.Revived, creature.Id);
            packet.PutInt(1);
            packet.PutInt(creature.Region);
            packet.PutInt(pos.X);
            packet.PutInt(pos.Y);

            creature.Client.Send(packet);
        }
Exemple #30
0
 /// <summary>
 /// Playing instrument effect (sound and motion) for creature,
 /// based on the given MML code.
 /// </summary>
 /// <param name="creature"></param>
 /// <param name="instrument"></param>
 /// <param name="quality"></param>
 /// <param name="compressedMML"></param>
 /// <returns></returns>
 public static MabiPacket PlayEffect(MabiCreature creature, InstrumentType instrument, PlayingQuality quality, string compressedMML)
 {
     var p = new MabiPacket(Op.Effect, creature.Id);
     p.PutInt(Effect.PlayMusic);
     p.PutByte(true); // has scroll
     p.PutString(compressedMML);
     p.PutInt(0);
     p.PutShort(0);
     p.PutInt(14113); // ?
     p.PutByte((byte)quality);
     p.PutByte((byte)instrument);
     p.PutByte(0);
     p.PutByte(0);
     p.PutByte(1); // loops
     return p;
 }