/// <summary> /// Sends SkillInfo to creature's client. /// </summary> /// <param name="client"></param> /// <param name="creature"></param> /// <param name="skill"></param> public static void SkillInfo(Creature creature, Skill skill) { var packet = new Packet(Op.SkillInfo, creature.EntityId); packet.PutBin(skill.Info); creature.Client.Send(packet); }
/// <summary> /// Broadcasts CreatureBodyUpdate in range of creature. /// </summary> /// <param name="creature"></param> public static void CreatureBodyUpdate(Creature creature) { var packet = new Packet(Op.CreatureBodyUpdate, creature.EntityId); packet.PutBin(creature.Body); creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends NpcTalk to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="xml"></param> public static void NpcTalk(Creature creature, string xml) { var packet = new Packet(Op.NpcTalk, creature.EntityId); packet.PutString(xml); packet.PutBin(); creature.Client.Send(packet); }
/// <summary> /// Broadcasts EquipmentChanged in creature's range. /// </summary> /// <param name="creature"></param> /// <param name="item"></param> public static void EquipmentChanged(Creature creature, Item item) { var packet = new Packet(Op.EquipmentChanged, creature.EntityId); packet.PutBin(item.Info); packet.PutByte(1); creature.Region.Broadcast(packet, creature); }
/// <summary> /// Sends SkillRankUp to creature's client. /// </summary> /// <param name="client"></param> /// <param name="creature"></param> /// <param name="skill"></param> public static void SkillRankUp(Creature creature, Skill skill) { var packet = new Packet(Op.SkillRankUp, creature.EntityId); packet.PutByte(1); packet.PutBin(skill.Info); packet.PutFloat(0); creature.Client.Send(packet); }
/// <summary> /// Sends DyePickColorR to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="success"></param> public static void DyePickColorR(Creature creature, bool success) { var packet = new Packet(Op.DyePickColorR, creature.EntityId); packet.PutByte(success); if (success) packet.PutBin(creature.Temp.RegularDyePickers); creature.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) { 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 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 CombatActionPack in range of pack's attacker. /// </summary> /// <param name="pack"></param> public static void CombatAction(CombatActionPack pack) { var attackerPos = pack.Attacker.GetPosition(); var packet = new Packet(Op.CombatActionPack, MabiId.Broadcast); packet.PutInt(pack.Id); packet.PutInt(pack.PrevId); packet.PutByte(pack.Hit); packet.PutByte((byte)pack.Type); packet.PutByte(pack.Flags); if ((pack.Flags & 1) == 1) { packet.PutInt(pack.BlockedByShieldPosX); packet.PutInt(pack.BlockedByShieldPosY); packet.PutLong(pack.ShieldCasterId); } // Actions packet.PutInt(pack.Actions.Count); Packet actionPacket = null; foreach (var action in pack.Actions) { var pos = action.Creature.GetPosition(); if (actionPacket == null) actionPacket = new Packet(Op.CombatAction, action.Creature.EntityId); else actionPacket.Clear(Op.CombatAction, action.Creature.EntityId); actionPacket.PutInt(pack.Id); actionPacket.PutLong(action.Creature.EntityId); actionPacket.PutByte((byte)action.Flags); actionPacket.PutShort(action.Stun); actionPacket.PutUShort((ushort)action.SkillId); actionPacket.PutUShort((ushort)action.SecondarySkillId); // Official name for CombatAction is CombatScene, and both Actions are Combatant. // Official client distinguish between Attacker and Defender simply by checking Flags. tachiorz // AttackerAction //if ((action.Flags & CombatActionFlags.Attacker) != 0) if (action.Category == CombatActionCategory.Attack) { var aAction = action as AttackerAction; actionPacket.PutLong(aAction.TargetId); actionPacket.PutUInt((uint)aAction.Options); actionPacket.PutByte(aAction.UsedWeaponSet); actionPacket.PutByte(aAction.WeaponParameterType); // !aAction.Has(AttackerOptions.KnockBackHit2) ? 2 : 1)); // ? actionPacket.PutInt(pos.X); actionPacket.PutInt(pos.Y); if ((aAction.Options & AttackerOptions.Dashed) != 0) { actionPacket.PutFloat(0); // DashedPosX actionPacket.PutFloat(0); // DashedPosY actionPacket.PutUInt(0); // DashDelay } if ((aAction.Options & AttackerOptions.PhaseAttack) != 0) actionPacket.PutByte(aAction.Phase); if ((aAction.Options & AttackerOptions.UseEffect) != 0) actionPacket.PutLong(aAction.PropId); } // TargetAction //if ((action.Flags & CombatActionFlags.TakeHit) != 0) else if (action.Category == CombatActionCategory.Target && !action.Is(CombatActionType.None)) { var tAction = action as TargetAction; // Target used Defense or Counter if ((action.Flags & CombatActionType.Attacker) != 0) { actionPacket.PutLong(tAction.Attacker.EntityId); actionPacket.PutInt(0); // attacker Options actionPacket.PutByte(tAction.UsedWeaponSet); actionPacket.PutByte(tAction.WeaponParameterType); actionPacket.PutInt(pos.X); actionPacket.PutInt(pos.Y); } actionPacket.PutUInt((uint)tAction.Options); actionPacket.PutFloat(tAction.Damage); actionPacket.PutFloat(tAction.Wound); actionPacket.PutInt((int)tAction.ManaDamage); actionPacket.PutFloat(attackerPos.X - pos.X); actionPacket.PutFloat(attackerPos.Y - pos.Y); if (tAction.IsKnockBack) { actionPacket.PutFloat(pos.X); actionPacket.PutFloat(pos.Y); // [190200, NA203 (22.04.2015)] { actionPacket.PutInt(0); } } if ((tAction.Options & TargetOptions.MultiHit) != 0) { actionPacket.PutUInt(0); // MultiHitDamageCount actionPacket.PutUInt(0); // MultiHitDamageShowTime } actionPacket.PutByte((byte)tAction.EffectFlags); actionPacket.PutInt(tAction.Delay); actionPacket.PutLong(tAction.Attacker.EntityId); } packet.PutBin(actionPacket); } pack.Attacker.Region.Broadcast(packet, pack.Attacker); }
/// <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 CombatActionPack in range of pack's attacker. /// </summary> /// <param name="pack"></param> public static void CombatAction(CombatActionPack pack) { var attackerPos = pack.Attacker.GetPosition(); var packet = new Packet(Op.CombatActionPack, MabiId.Broadcast); packet.PutInt(pack.Id); packet.PutInt(pack.PrevId); packet.PutByte(pack.Hit); packet.PutByte(pack.MaxHits); packet.PutByte(0); // Actions packet.PutInt(pack.Actions.Count); Packet actionPacket = null; foreach (var action in pack.Actions) { var pos = action.Creature.GetPosition(); if (actionPacket == null) actionPacket = new Packet(Op.CombatAction, action.Creature.EntityId); else actionPacket.Clear(Op.CombatAction, action.Creature.EntityId); actionPacket.PutInt(pack.Id); actionPacket.PutLong(action.Creature.EntityId); actionPacket.PutByte((byte)action.Type); actionPacket.PutShort(action.Stun); actionPacket.PutUShort((ushort)action.SkillId); actionPacket.PutShort(0); // AttackerAction if (action.Category == CombatActionCategory.Attack) { var aAction = action as AttackerAction; actionPacket.PutLong(aAction.TargetId); actionPacket.PutUInt((uint)aAction.Options); actionPacket.PutByte(0); actionPacket.PutByte((byte)(!aAction.Has(AttackerOptions.KnockBackHit2) ? 2 : 1)); // ? actionPacket.PutInt(pos.X); actionPacket.PutInt(pos.Y); if (aAction.PropId != 0) actionPacket.PutLong(aAction.PropId); } // TargetAction else if (action.Category == CombatActionCategory.Target && !action.Is(CombatActionType.None)) { var tAction = action as TargetAction; // Target used Defense or Counter if (tAction.Is(CombatActionType.Defended) || tAction.Is(CombatActionType.CounteredHit) || tAction.Is(CombatActionType.CounteredHit2)) { actionPacket.PutLong(tAction.Attacker.EntityId); actionPacket.PutInt(0); actionPacket.PutByte(0); actionPacket.PutByte(1); actionPacket.PutInt(pos.X); actionPacket.PutInt(pos.Y); } actionPacket.PutUInt((uint)tAction.Options); actionPacket.PutFloat(tAction.Damage); actionPacket.PutFloat(0); // Related to mana damage? actionPacket.PutInt((int)tAction.ManaDamage); actionPacket.PutFloat(attackerPos.X - pos.X); actionPacket.PutFloat(attackerPos.Y - pos.Y); if (tAction.IsKnockBack) { actionPacket.PutFloat(pos.X); actionPacket.PutFloat(pos.Y); // [190200, NA203 (22.04.2015)] { actionPacket.PutInt(0); } } actionPacket.PutByte(0); // PDef? Seen as 0x20 in a normal attack (G18) actionPacket.PutInt(tAction.Delay); actionPacket.PutLong(tAction.Attacker.EntityId); } packet.PutBin(actionPacket); } pack.Attacker.Region.Broadcast(packet, pack.Attacker); }
/// <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 SkillTrainingUp to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="skill"></param> /// <param name="exp">Exp gained</param> public static void SkillTrainingUp(Creature creature, Skill skill, float exp, string bonus = "") { var packet = new Packet(Op.SkillTrainingUp, creature.EntityId); packet.PutBin(skill.Info); packet.PutFloat(exp); packet.PutByte(1); packet.PutString(bonus); // (Specialized Skill Bonus: x2) creature.Client.Send(packet); }
/// <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); }