using System; using System.Buffers; using Server.Mobiles; using Server.Network; namespace Server { public class BuffInfo { private TimerExecutionToken _timerToken; public BuffInfo(BuffIcon iconID, int titleCliloc) : this(iconID, titleCliloc, titleCliloc + 1) { } public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc) { ID = iconID; TitleCliloc = titleCliloc; SecondaryCliloc = secondaryCliloc; } public BuffInfo(BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m) : this(iconID, titleCliloc, titleCliloc + 1, length, m) { } // Only the timed one needs to Mobile to know when to automagically remove it. public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m) : this(iconID, titleCliloc, secondaryCliloc) { TimeLength = length; TimeStart = Core.TickCount; Timer.StartTimer(length, () => RemoveBuff(m, this), out _timerToken); } public BuffInfo(BuffIcon iconID, int titleCliloc, TextDefinition args) : this(iconID, titleCliloc, titleCliloc + 1, args) { } public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args) : this(iconID, titleCliloc, secondaryCliloc) => Args = args; public BuffInfo(BuffIcon iconID, int titleCliloc, bool retainThroughDeath) : this(iconID, titleCliloc, titleCliloc + 1, retainThroughDeath) { } public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, bool retainThroughDeath) : this(iconID, titleCliloc, secondaryCliloc) => RetainThroughDeath = retainThroughDeath; public BuffInfo(BuffIcon iconID, int titleCliloc, TextDefinition args, bool retainThroughDeath) : this(iconID, titleCliloc, titleCliloc + 1, args, retainThroughDeath) { } public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args, bool retainThroughDeath) : this(iconID, titleCliloc, secondaryCliloc, args) => RetainThroughDeath = retainThroughDeath; public BuffInfo(BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m, TextDefinition args) : this(iconID, titleCliloc, titleCliloc + 1, length, m, args) { } public BuffInfo( BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m, TextDefinition args ) : this(iconID, titleCliloc, secondaryCliloc, length, m) => Args = args; public BuffInfo( BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m, TextDefinition args, bool retainThroughDeath ) : this(iconID, titleCliloc, titleCliloc + 1, length, m, args, retainThroughDeath) { } public BuffInfo( BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m, TextDefinition args, bool retainThroughDeath ) : this(iconID, titleCliloc, secondaryCliloc, length, m) { Args = args; RetainThroughDeath = retainThroughDeath; } public static bool Enabled { get; private set; } public BuffIcon ID { get; } public int TitleCliloc { get; } public int SecondaryCliloc { get; } public TimeSpan TimeLength { get; } public long TimeStart { get; } public TimerExecutionToken TimerToken => _timerToken; public bool RetainThroughDeath { get; } public TextDefinition Args { get; } public static void Configure() { Enabled = ServerConfiguration.GetOrUpdateSetting("buffIcons.enable", Core.ML); } public static void Initialize() { if (Enabled) { EventSink.ClientVersionReceived += ResendBuffsOnClientVersionReceived; } } public static void ResendBuffsOnClientVersionReceived(NetState ns, ClientVersion cv) { if (ns.Mobile is PlayerMobile pm) { Timer.StartTimer(pm.ResendBuffs); } } public static void AddBuff(Mobile m, BuffInfo b) { (m as PlayerMobile)?.AddBuff(b); } public static void RemoveBuff(Mobile m, BuffInfo b) { (m as PlayerMobile)?.RemoveBuff(b); } public static void RemoveBuff(Mobile m, BuffIcon b) { (m as PlayerMobile)?.RemoveBuff(b); } public void SendAddBuffPacket(NetState ns, Serial m) => SendAddBuffPacket( ns, m, ID, TitleCliloc, SecondaryCliloc, Args, TimeStart == 0 ? 0 : Math.Max(TimeStart + (long)TimeLength.TotalMilliseconds - Core.TickCount, 0) ); public static void SendAddBuffPacket( NetState ns, Serial mob, BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args, long ticks ) { if (ns == null) { return; } var hasArgs = args != null; var length = hasArgs ? args.ToString().Length * 2 + 52 : 46; var writer = new SpanWriter(stackalloc byte[length]); writer.Write((byte)0xDF); // Packet ID writer.Write((ushort)length); writer.Write(mob); writer.Write((short)iconID); writer.Write((short)0x1); // command (0 = remove, 1 = add, 2 = data) writer.Write(0); writer.Write((short)iconID); writer.Write((short)0x1); // command (0 = remove, 1 = add, 2 = data) writer.Write(0); writer.Write((short)(ticks / 1000)); writer.Clear(3); writer.Write(titleCliloc); writer.Write(secondaryCliloc); if (hasArgs) { writer.Write(0); writer.Write((short)0x1); writer.Write((ushort)0); writer.WriteLE('\t'); writer.WriteLittleUniNull(args); writer.Write((short)0x1); writer.Write((ushort)0); } else { writer.Clear(10); } ns.Send(writer.Span); } public void SendRemoveBuffPacket(NetState ns, Serial mob) => SendRemoveBuffPacket(ns, mob, ID); public static void SendRemoveBuffPacket(NetState ns, Serial mob, BuffIcon iconID) { if (ns == null) { return; } var writer = new SpanWriter(stackalloc byte[15]); writer.Write((byte)0xDF); // Packet ID writer.Write((ushort)15); writer.Write(mob); writer.Write((short)iconID); writer.Write((short)0x0); // command (0 = remove, 1 = add, 2 = data) writer.Write(0); ns.Send(writer.Span); } } public enum BuffIcon : short { DismountPrevention = 0x3E9, NoRearm = 0x3EA, // Currently, no 0x3EB or 0x3EC NightSight = 0x3ED, // * DeathStrike, EvilOmen, UnknownStandingSwirl, // Which is healing throttle & Stamina throttle? UnknownKneelingSword, DivineFury, // * EnemyOfOne, // * HidingAndOrStealth, // * ActiveMeditation, // * BloodOathCaster, // * BloodOathCurse, // * CorpseSkin, // * Mindrot, // * PainSpike, // * Strangle, GiftOfRenewal, // * AttuneWeapon, // * Thunderstorm, // * EssenceOfWind, // * EtherealVoyage, // * GiftOfLife, // * ArcaneEmpowerment, // * MortalStrike, ReactiveArmor, // * Protection, // * ArchProtection, MagicReflection, // * Incognito, // * Disguised, AnimalForm, Polymorph, Invisibility, // * Paralyze, // * Poison, Bleed, Clumsy, // * FeebleMind, // * Weaken, // * Curse, // * MassCurse, Agility, // * Cunning, // * Strength, // * Bless, // * Sleep, StoneForm, SpellPlague, SpellTrigger, NetherBolt, Fly } }