/// <summary> /// Broadcasts HittingProp in range of creature. /// </summary> /// <param name="creature"></param> /// <param name="propEntityId"></param> /// <param name="stunTime"></param> public static void HittingProp(Creature creature, long propEntityId, int stunTime) { var pos = creature.GetPosition(); var packet = new Packet(Op.HittingProp, creature.EntityId); packet.PutLong(propEntityId); packet.PutInt(stunTime); packet.PutFloat(pos.X); packet.PutFloat(pos.Y); creature.Region.Broadcast(packet, creature); }
/// <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 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> /// Sends Weather. /// </summary> private static void Weather(Action<Packet> sender, IWeatherProvider provider) { var packet = new Packet(Op.Weather, MabiId.Broadcast); packet.PutByte(0); packet.PutInt(provider.RegionId); if (provider is IWeatherProviderTable) { var table = (IWeatherProviderTable)provider; packet.PutByte(0); packet.PutInt(table.GroupId); packet.PutByte(2); packet.PutByte(1); packet.PutString("table"); packet.PutString(table.Name); packet.PutLong(0); packet.PutByte(0); } else if (provider is IWeatherProviderConstant) { var constant = (IWeatherProviderConstant)provider; // Packet structure is guessed, even though it works, // based on constant_smooth. packet.PutByte(2); packet.PutByte(0); packet.PutByte(1); packet.PutString("constant"); packet.PutFloat(constant.Weather); packet.PutLong(0); packet.PutByte(0); } else if (provider is IWeatherProviderConstantSmooth) { var constantSmooth = (IWeatherProviderConstantSmooth)provider; packet.PutByte(2); packet.PutByte(0); packet.PutByte(1); packet.PutString("constant_smooth"); packet.PutFloat(constantSmooth.Weather); packet.PutLong(DateTime.Now); // Start packet.PutLong(DateTime.MinValue); // End packet.PutFloat(constantSmooth.WeatherBefore); packet.PutFloat(constantSmooth.WeatherBefore); packet.PutLong(constantSmooth.TransitionTime); packet.PutByte(false); // bool? Is table appended? byte? Appended type? packet.PutLong(DateTime.MinValue); // End packet.PutInt(2); // Append a table packet here to go back to that after end packet.PutByte(0); } sender(packet); }
/// <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> /// 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> /// Sends QuestUpdate to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="quest"></param> public static void QuestUpdate(Creature creature, Quest quest) { var progress = quest.GetList(); var packet = new Packet(Op.QuestUpdate, creature.EntityId); packet.PutLong(quest.UniqueId); packet.PutByte(1); packet.PutInt(progress.Count); foreach (var p in progress) { packet.PutInt(p.Count); // [180600, NA187 (25.06.2014)] ? { packet.PutFloat(0); } packet.PutByte(p.Done); packet.PutByte(p.Unlocked); } packet.PutByte(0); packet.PutByte(0); creature.Client.Send(packet); }
/// <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> /// Broadcasts spawn effect (Effect, Spawn) in range of sendFrom. /// </summary> /// <param name="spawnEffect"></param> /// <param name="regionId"></param> /// <param name="x"></param> /// <param name="y"></param> /// <param name="sender"></param> /// <param name="sendFrom">Falls back to sender if null</param> public static void SpawnEffect(SpawnEffect spawnEffect, int regionId, int x, int y, Creature sender, Creature sendFrom = null) { if (sendFrom == null) sendFrom = sender; var packet = new Packet(Op.Effect, sender.EntityId); packet.PutInt(E.Spawn); packet.PutInt(regionId); packet.PutFloat((float)x); packet.PutFloat((float)y); packet.PutByte((byte)spawnEffect); sender.Region.Broadcast(packet, sendFrom); }
/// <summary> /// Broadcasts TurnTo in range of creature. /// </summary> /// <param name="creature"></param> /// <param name="x"></param> /// <param name="y"></param> public static void TurnTo(Creature creature, float x, float y) { var packet = new Packet(Op.TurnTo, creature.EntityId); packet.PutFloat(x); packet.PutFloat(y); creature.Region.Broadcast(packet, creature); }
public static void SpinColorWheelR(Creature creature, float result) { var packet = new Packet(Op.SpinColorWheelR, creature.EntityId); packet.PutFloat(result); creature.Client.Send(packet); }
/// <summary> /// Broadcasts CollectAnimation in creature's range. /// </summary> /// <param name="creature"></param> /// <param name="entityId"></param> /// <param name="collectId"></param> /// <param name="pos"></param> public static void CollectAnimation(Creature creature, long entityId, int collectId, Position pos) { var packet = new Packet(Op.CollectAnimation, creature.EntityId); packet.PutLong(entityId); packet.PutInt(collectId); packet.PutFloat(pos.X); packet.PutFloat(pos.Y); packet.PutFloat(1); creature.Region.Broadcast(packet, creature); }
/// <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 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> /// Sends FishingActionRequired to creature's client. /// </summary> /// <param name="creature"></param> /// <param name="catchSize"></param> /// <param name="time">The time you have to react.</param> /// <param name="fishSpeed">Fish speed for manual catching, 0 = no movement, 3+ = pretty challenging.</param> public static void FishingActionRequired(Creature creature, CatchSize catchSize, int time, float fishSpeed) { var packet = new Packet(Op.FishingActionRequired, creature.EntityId); packet.PutByte((byte)catchSize); packet.PutInt(time); packet.PutFloat(fishSpeed); creature.Client.Send(packet); }
/// <summary> /// Broadcasts PlayDead in range of creature. /// </summary> /// <param name="creature"></param> public static void PlayDead(Creature creature) { var pos = creature.GetPosition(); var packet = new Packet(Op.PlayDead, creature.EntityId); packet.PutByte(true); // ? packet.PutFloat(pos.X); packet.PutFloat(pos.Y); packet.PutInt(5000); creature.Region.Broadcast(packet, creature); }
/// <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 StabilityMeterUpdate to receiver, containing meter information /// of creature. /// </summary> /// <param name="receiver"></param> /// <param name="creature"></param> public static void StabilityMeterUpdate(Creature receiver, Creature creature) { var packet = new Packet(Op.StabilityMeterUpdate, receiver.EntityId); packet.PutLong(creature.EntityId); packet.PutFloat(creature.Stability); packet.PutByte(1); receiver.Client.Send(packet); }
/// <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); }