/// <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); }