using System; using System.Collections.Generic; using Server.Gumps; using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Spellweaving { public class GiftOfLifeSpell : ArcanistSpell, ISpellTargetingMobile { private static SpellInfo m_Info = new SpellInfo( "Gift of Life", "Illorae", -1 ); private static Dictionary m_Table = new Dictionary(); public GiftOfLifeSpell(Mobile caster, Item scroll = null) : base(caster, scroll, m_Info) { } public override TimeSpan CastDelayBase => TimeSpan.FromSeconds(4.0); public override double RequiredSkill => 38.0; public override int RequiredMana => 70; public double HitsScalar => (Caster.Skills.Spellweaving.Value / 2.4 + FocusLevel) / 100; public static void Initialize() { EventSink.PlayerDeath += delegate(PlayerDeathEventArgs e) { HandleDeath(e.Mobile); }; } public override void OnCast() { Caster.Target = new SpellTargetMobile(this, TargetFlags.Beneficial, 10); } public void Target(Mobile m) { if (m == null) Caster.SendLocalizedMessage(1072077); // You may only cast this spell on yourself or a bonded pet. else if (!Caster.CanSee(m)) Caster.SendLocalizedMessage(500237); // Target can not be seen. else if (m.IsDeadBondedPet || !m.Alive) { // As per Osi: Nothing happens. } else if (m != Caster && !(m is BaseCreature bc && bc.IsBonded && bc.ControlMaster == Caster)) Caster.SendLocalizedMessage(1072077); // You may only cast this spell on yourself or a bonded pet. else if (m_Table.ContainsKey(m)) Caster.SendLocalizedMessage(501775); // This spell is already in effect. else if (CheckBSequence(m)) { if (Caster == m) { Caster.SendLocalizedMessage(1074774); // You weave powerful magic, protecting yourself from death. } else { Caster.SendLocalizedMessage(1074775); // You weave powerful magic, protecting your pet from death. SpellHelper.Turn(Caster, m); } m.PlaySound(0x244); m.FixedParticles(0x3709, 1, 30, 0x26ED, 5, 2, EffectLayer.Waist); m.FixedParticles(0x376A, 1, 30, 0x251E, 5, 3, EffectLayer.Waist); double skill = Caster.Skills.Spellweaving.Value; TimeSpan duration = TimeSpan.FromMinutes((int)(skill / 24) * 2 + FocusLevel); ExpireTimer t = new ExpireTimer(m, duration, this); t.Start(); m_Table[m] = t; BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.GiftOfLife, 1031615, 1075807, duration, m, null, true)); } FinishSequence(); } public static void HandleDeath(Mobile m) { if (m_Table.ContainsKey(m)) Timer.DelayCall(TimeSpan.FromSeconds(Utility.RandomMinMax(2, 4)), HandleDeath_OnCallback, m); } private static void HandleDeath_OnCallback(Mobile m) { if (!m_Table.TryGetValue(m, out ExpireTimer timer)) return; double hitsScalar = timer.Spell.HitsScalar; if (m is BaseCreature pet && pet.IsDeadBondedPet) { Mobile master = pet.GetMaster(); if (master?.NetState != null && Utility.InUpdateRange(pet, master)) { master.CloseGump(); master.SendGump(new PetResurrectGump(master, pet, hitsScalar)); } else { List friends = pet.Friends; for (int i = 0; friends != null && i < friends.Count; i++) { Mobile friend = friends[i]; if (friend.NetState != null && Utility.InUpdateRange(pet, friend)) { friend.CloseGump(); friend.SendGump(new PetResurrectGump(friend, pet)); break; } } } } else { m.CloseGump(); m.SendGump(new ResurrectGump(m, hitsScalar)); } //Per OSI, buff is removed when gump sent, irregardless of online status or acceptance timer.DoExpire(); } public static void OnLogin(LoginEventArgs e) { Mobile m = e.Mobile; if (m == null || m.Alive || m_Table[m] == null) return; HandleDeath_OnCallback(m); } private class ExpireTimer : Timer { private Mobile m_Mobile; public ExpireTimer(Mobile m, TimeSpan delay, GiftOfLifeSpell spell) : base(delay) { m_Mobile = m; Spell = spell; } public GiftOfLifeSpell Spell{ get; } protected override void OnTick() { DoExpire(); } public void DoExpire() { Stop(); m_Mobile.SendLocalizedMessage(1074776); // You are no longer protected with Gift of Life. m_Table.Remove(m_Mobile); BuffInfo.RemoveBuff(m_Mobile, BuffIcon.GiftOfLife); } } } }