diff --git a/Scripts/Accounting/Account.cs b/Scripts/Accounting/Account.cs index c001dc032..f32d3650e 100644 --- a/Scripts/Accounting/Account.cs +++ b/Scripts/Accounting/Account.cs @@ -1114,7 +1114,7 @@ namespace Server.Accounting /// public int Limit { - get { return ( Core.SA ? 7 : Core.AOS ? 6 : 5 ); } + get { return ( /* Core.SA ? 7 : */ Core.AOS ? 6 : 5 ); } //TODO: complete SA core support and uncomment } /// diff --git a/Scripts/Engines/Craft/DefAlchemy.cs b/Scripts/Engines/Craft/DefAlchemy.cs index 9fb14bf43..fa8b26f48 100644 --- a/Scripts/Engines/Craft/DefAlchemy.cs +++ b/Scripts/Engines/Craft/DefAlchemy.cs @@ -159,6 +159,21 @@ namespace Server.Engines.Craft index = AddCraft( typeof( SmokeBomb ), 1044537, 1030248, 90.0, 120.0, typeof( Eggs ), 1044477, 1, 1044253 ); AddRes( index, typeof ( Ginseng ), 1044356, 3, 1044364 ); SetNeededExpansion( index, Expansion.SE ); + + // Conflagration Potions + index = AddCraft( typeof(ConflagrationPotion), 1044109, 1072096, 55.0, 105.0, typeof(GraveDust), 1023983, 5, 1044253 ); + AddRes( index, typeof(Bottle), 1044529, 1, 500315 ); + SetNeededExpansion(index, Expansion.SE); + index = AddCraft( typeof(GreaterConflagrationPotion), 1044109, 1072099, 65.0, 115.0, typeof(GraveDust), 1023983, 10, 1044253 ); + AddRes( index, typeof(Bottle), 1044529, 1, 500315 ); + SetNeededExpansion(index, Expansion.SE); + // Confusion Blast Potions + index = AddCraft( typeof(ConfusionBlastPotion), 1044109, 1072106, 55.0, 105.0, typeof(PigIron), 1023978, 5, 1044253 ); + AddRes( index, typeof(Bottle), 1044529, 1, 500315 ); + SetNeededExpansion(index, Expansion.SE); + index = AddCraft( typeof(GreaterConfusionBlastPotion), 1044109, 1072109, 65.0, 115.0, typeof(PigIron), 1023978, 10, 1044253 ); + AddRes( index, typeof(Bottle), 1044529, 1, 500315 ); + SetNeededExpansion(index, Expansion.SE); } } } diff --git a/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs b/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs index a01efef1b..4a9b5e252 100644 --- a/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs +++ b/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs @@ -90,7 +90,18 @@ namespace Server.Items Timer.DelayCall( TimeSpan.Zero, new TimerCallback( UpdateWeight ) ); } - public override int LabelNumber{ get{ return (m_Held > 0 ? 1041620 + (int)m_Type : 1041641); } } + public override int LabelNumber + { + get + { + if ( m_Held == 0 ) + return 1041641; + else if( m_Type >= PotionEffect.Conflagration ) + return 1072658 + (int) m_Type - (int) PotionEffect.Conflagration; + else + return ( 1041620 + (int)m_Type ); + } + } public override void GetProperties( ObjectPropertyList list ) { @@ -297,33 +308,39 @@ namespace Server.Items switch ( m_Type ) { default: - case PotionEffect.Nightsight: return new NightSightPotion(); + case PotionEffect.Nightsight: return new NightSightPotion(); - case PotionEffect.CureLesser: return new LesserCurePotion(); + case PotionEffect.CureLesser: return new LesserCurePotion(); case PotionEffect.Cure: return new CurePotion(); - case PotionEffect.CureGreater: return new GreaterCurePotion(); + case PotionEffect.CureGreater: return new GreaterCurePotion(); case PotionEffect.Agility: return new AgilityPotion(); - case PotionEffect.AgilityGreater: return new GreaterAgilityPotion(); + case PotionEffect.AgilityGreater: return new GreaterAgilityPotion(); case PotionEffect.Strength: return new StrengthPotion(); - case PotionEffect.StrengthGreater: return new GreaterStrengthPotion(); + case PotionEffect.StrengthGreater: return new GreaterStrengthPotion(); - case PotionEffect.PoisonLesser: return new LesserPoisonPotion(); + case PotionEffect.PoisonLesser: return new LesserPoisonPotion(); case PotionEffect.Poison: return new PoisonPotion(); - case PotionEffect.PoisonGreater: return new GreaterPoisonPotion(); - case PotionEffect.PoisonDeadly: return new DeadlyPoisonPotion(); + case PotionEffect.PoisonGreater: return new GreaterPoisonPotion(); + case PotionEffect.PoisonDeadly: return new DeadlyPoisonPotion(); case PotionEffect.Refresh: return new RefreshPotion(); - case PotionEffect.RefreshTotal: return new TotalRefreshPotion(); + case PotionEffect.RefreshTotal: return new TotalRefreshPotion(); - case PotionEffect.HealLesser: return new LesserHealPotion(); + case PotionEffect.HealLesser: return new LesserHealPotion(); case PotionEffect.Heal: return new HealPotion(); - case PotionEffect.HealGreater: return new GreaterHealPotion(); + case PotionEffect.HealGreater: return new GreaterHealPotion(); - case PotionEffect.ExplosionLesser: return new LesserExplosionPotion(); - case PotionEffect.Explosion: return new ExplosionPotion(); - case PotionEffect.ExplosionGreater: return new GreaterExplosionPotion(); + case PotionEffect.ExplosionLesser: return new LesserExplosionPotion(); + case PotionEffect.Explosion: return new ExplosionPotion(); + case PotionEffect.ExplosionGreater: return new GreaterExplosionPotion(); + + case PotionEffect.Conflagration: return new ConflagrationPotion(); + case PotionEffect.ConflagrationGreater: return new GreaterConflagrationPotion(); + + case PotionEffect.ConfusionBlast: return new ConfusionBlastPotion(); + case PotionEffect.ConfusionBlastGreater: return new GreaterConfusionBlastPotion(); } } diff --git a/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs b/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs index 2cf1b5f3c..ada3cb8c8 100644 --- a/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs +++ b/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs @@ -26,7 +26,13 @@ namespace Server.Items HealGreater, ExplosionLesser, Explosion, - ExplosionGreater + ExplosionGreater, + Conflagration, + ConflagrationGreater, + MaskOfDeath, // Mask of Death is not available in OSI but does exist in cliloc files + MaskOfDeathGreater, // included in enumeration for compatability if later enabled by OSI + ConfusionBlast, + ConfusionBlastGreater } public abstract class BasePotion : Item, ICraftable @@ -145,8 +151,12 @@ namespace Server.Items public static int EnhancePotions( Mobile m ) { int EP = AosAttributes.GetValue( m, AosAttribute.EnhancePotions ); - if ( Core.ML && EP > 50 ) - EP = 50; + + int cap = 50 + m.Skills.Alchemy.Fixed / 330 * 10; + + if ( Core.ML && EP > cap && m.AccessLevel <= AccessLevel.Player ) + EP = cap; + return EP; } diff --git a/Scripts/Items/Skill Items/Magical/Potions/Conflagration Potions/BaseConflagrationPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Conflagration Potions/BaseConflagrationPotion.cs new file mode 100644 index 000000000..8b7a511f7 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Conflagration Potions/BaseConflagrationPotion.cs @@ -0,0 +1,347 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Network; +using Server.Targeting; +using Server.Spells; + +namespace Server.Items +{ + public abstract class BaseConflagrationPotion : BasePotion + { + public abstract int MinDamage{ get; } + public abstract int MaxDamage{ get; } + + public override bool RequireFreeHand{ get{ return false; } } + + public BaseConflagrationPotion( PotionEffect effect ) : base( 0xF06, effect ) + { + Hue = 0x489; + } + + public BaseConflagrationPotion( Serial serial ) : base( serial ) + { + } + + public override void Drink( Mobile from ) + { + if ( Core.AOS && (from.Paralyzed || from.Frozen || (from.Spell != null && from.Spell.IsCasting)) ) + { + from.SendLocalizedMessage( 1062725 ); // You can not use that potion while paralyzed. + return; + } + + int delay = GetDelay( from ); + + if ( delay > 0 ) + { + from.SendLocalizedMessage( 1072529, String.Format( "{0}\t{1}", delay, delay > 1 ? "seconds." : "second." ) ); // You cannot use that for another ~1_NUM~ ~2_TIMEUNITS~ + return; + } + + ThrowTarget targ = from.Target as ThrowTarget; + + if ( targ != null && targ.Potion == this ) + return; + + from.RevealingAction(); + + if ( !m_Users.Contains( from ) ) + m_Users.Add( from ); + + from.Target = new ThrowTarget( this ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + private List m_Users = new List(); + + public void Explode_Callback( object state ) + { + object[] states = (object[]) state; + + Explode( (Mobile) states[ 0 ], (Point3D) states[ 1 ], (Map) states[ 2 ] ); + } + + public virtual void Explode( Mobile from, Point3D loc, Map map ) + { + if ( Deleted || map == null ) + return; + + Consume(); + + // Check if any other players are using this potion + for ( int i = 0; i < m_Users.Count; i ++ ) + { + ThrowTarget targ = m_Users[ i ].Target as ThrowTarget; + + if ( targ != null && targ.Potion == this ) + Target.Cancel( from ); + } + + // Effects + Effects.PlaySound( loc, map, 0x20C ); + + for ( int i = -2; i <= 2; i ++ ) + { + for ( int j = -2; j <= 2; j ++ ) + { + Point3D p = new Point3D( loc.X + i, loc.Y + j, loc.Z ); + + if ( map.CanFit( p, 12, true, false ) && from.InLOS( p ) ) + new InternalItem( from, p, map, MinDamage, MaxDamage ); + } + } + } + + #region Delay + private static Hashtable m_Delay = new Hashtable(); + + public static void AddDelay( Mobile m ) + { + Timer timer = m_Delay[ m ] as Timer; + + if ( timer != null ) + timer.Stop(); + + m_Delay[ m ] = Timer.DelayCall( TimeSpan.FromSeconds( 30 ), new TimerStateCallback( EndDelay_Callback ), m ); + } + + public static int GetDelay( Mobile m ) + { + Timer timer = m_Delay[ m ] as Timer; + + if ( timer != null && timer.Next > DateTime.Now ) + return (int) (timer.Next - DateTime.Now).TotalSeconds; + + return 0; + } + + private static void EndDelay_Callback( object obj ) + { + if ( obj is Mobile ) + EndDelay( (Mobile) obj ); + } + + public static void EndDelay( Mobile m ) + { + Timer timer = m_Delay[ m ] as Timer; + + if ( timer != null ) + { + timer.Stop(); + m_Delay.Remove( m ); + } + } + #endregion + + private class ThrowTarget : Target + { + private BaseConflagrationPotion m_Potion; + + public BaseConflagrationPotion Potion + { + get{ return m_Potion; } + } + + public ThrowTarget( BaseConflagrationPotion potion ) : base( 12, true, TargetFlags.None ) + { + m_Potion = potion; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Potion.Deleted || m_Potion.Map == Map.Internal ) + return; + + IPoint3D p = targeted as IPoint3D; + + if ( p == null || from.Map == null ) + return; + + // Add delay + BaseConflagrationPotion.AddDelay( from ); + + SpellHelper.GetSurfaceTop( ref p ); + + from.RevealingAction(); + + IEntity to; + + if ( p is Mobile ) + to = (Mobile)p; + else + to = new Entity( Serial.Zero, new Point3D( p ), from.Map ); + + Effects.SendMovingEffect( from, to, 0xF0D, 7, 0, false, false, m_Potion.Hue, 0 ); + Timer.DelayCall( TimeSpan.FromSeconds( 1.5 ), new TimerStateCallback( m_Potion.Explode_Callback ), new object[] { from, new Point3D( p ), from.Map } ); + } + } + + public class InternalItem : Item + { + private Mobile m_From; + private int m_MinDamage; + private int m_MaxDamage; + private DateTime m_End; + private Timer m_Timer; + + public Mobile From{ get{ return m_From; } } + + public override bool BlocksFit{ get{ return true; } } + + public InternalItem( Mobile from, Point3D loc, Map map, int min, int max ) : base( 0x398C ) + { + Movable = false; + Light = LightType.Circle300; + + MoveToWorld( loc, map ); + + m_From = from; + m_End = DateTime.Now + TimeSpan.FromSeconds( 10 ); + + SetDamage( min, max ); + + m_Timer = new InternalTimer( this, m_End ); + m_Timer.Start(); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Timer != null ) + m_Timer.Stop(); + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public int GetDamage(){ return Utility.RandomMinMax( m_MinDamage, m_MaxDamage ); } + + private void SetDamage( int min, int max ) + { + /* new way to apply alchemy bonus according to Stratics' calculator. + this gives a mean to values 25, 50, 75 and 100. Stratics' calculator is outdated. + Those goals will give 2 to alchemy bonus. It's not really OSI-like but it's an approximation. */ + + m_MinDamage = min; + m_MaxDamage = max; + + if( m_From == null ) + return; + + int alchemySkill = m_From.Skills.Alchemy.Fixed; + int alchemyBonus = alchemySkill / 125 + alchemySkill / 250 ; + + m_MinDamage = Scale( m_From, m_MinDamage + alchemyBonus ); + m_MaxDamage = Scale( m_From, m_MaxDamage + alchemyBonus ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Mobile) m_From ); + writer.Write( (DateTime) m_End ); + writer.Write( (int) m_MinDamage ); + writer.Write( (int) m_MaxDamage ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_From = reader.ReadMobile(); + m_End = reader.ReadDateTime(); + m_MinDamage = reader.ReadInt(); + m_MaxDamage = reader.ReadInt(); + + m_Timer = new InternalTimer( this, m_End ); + m_Timer.Start(); + } + + public override bool OnMoveOver( Mobile m ) + { + if ( Visible && m_From != null && (!Core.AOS || m != m_From) && SpellHelper.ValidIndirectTarget( m_From, m ) && m_From.CanBeHarmful( m, false ) ) + { + m_From.DoHarmful( m ); + + AOS.Damage( m, m_From, GetDamage(), 0, 100, 0, 0, 0 ); + m.PlaySound( 0x208 ); + } + + return true; + } + + private class InternalTimer : Timer + { + private InternalItem m_Item; + private DateTime m_End; + + public InternalTimer( InternalItem item, DateTime end ) : base( TimeSpan.Zero, TimeSpan.FromSeconds( 1.0 ) ) + { + m_Item = item; + m_End = end; + + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + if ( m_Item.Deleted ) + return; + + if ( DateTime.Now > m_End ) + { + m_Item.Delete(); + Stop(); + return; + } + + Mobile from = m_Item.From; + + if ( m_Item.Map == null || from == null ) + return; + + List mobiles = new List(); + + foreach( Mobile mobile in m_Item.GetMobilesInRange( 0 ) ) + mobiles.Add( mobile ); + + for( int i = 0; i < mobiles.Count; i++ ) + { + Mobile m = mobiles[i]; + + if ( (m.Z + 16) > m_Item.Z && (m_Item.Z + 12) > m.Z && (!Core.AOS || m != from) && SpellHelper.ValidIndirectTarget( from, m ) && from.CanBeHarmful( m, false ) ) + { + if ( from != null ) + from.DoHarmful( m ); + + AOS.Damage( m, from, m_Item.GetDamage(), 0, 100, 0, 0, 0 ); + m.PlaySound( 0x208 ); + } + } + } + } + } + } +} diff --git a/Scripts/Items/Skill Items/Magical/Potions/Conflagration Potions/ConflagrationPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Conflagration Potions/ConflagrationPotion.cs new file mode 100644 index 000000000..fc54a969c --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Conflagration Potions/ConflagrationPotion.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ConflagrationPotion : BaseConflagrationPotion + { + public override int MinDamage{ get{ return 2; } } + public override int MaxDamage{ get{ return 4; } } + + public override int LabelNumber{ get{ return 1072095; } } // a Conflagration potion + + [Constructable] + public ConflagrationPotion() : base( PotionEffect.Conflagration ) + { + } + + public ConflagrationPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Skill Items/Magical/Potions/Conflagration Potions/GreaterConflagrationPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Conflagration Potions/GreaterConflagrationPotion.cs new file mode 100644 index 000000000..b034f3e91 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Conflagration Potions/GreaterConflagrationPotion.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GreaterConflagrationPotion : BaseConflagrationPotion + { + public override int MinDamage{ get{ return 4; } } + public override int MaxDamage{ get{ return 8; } } + + public override int LabelNumber{ get{ return 1072098; } } // a Greater Conflagration potion + + [Constructable] + public GreaterConflagrationPotion() : base( PotionEffect.ConflagrationGreater ) + { + } + + public GreaterConflagrationPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Skill Items/Magical/Potions/Confusion Blast Potions/BaseConfusionBlastPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Confusion Blast Potions/BaseConfusionBlastPotion.cs new file mode 100644 index 000000000..c1679562a --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Confusion Blast Potions/BaseConfusionBlastPotion.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Network; +using Server.Targeting; +using Server.Spells; +using Server.Mobiles; +using Server.Misc; + +namespace Server.Items +{ + public abstract class BaseConfusionBlastPotion : BasePotion + { + public abstract int Radius{ get; } + + public override bool RequireFreeHand{ get{ return false; } } + + public BaseConfusionBlastPotion( PotionEffect effect ) : base( 0xF06, effect ) + { + Hue = 0x48D; + } + + public BaseConfusionBlastPotion( Serial serial ) : base( serial ) + { + } + + public override void Drink( Mobile from ) + { + if ( Core.AOS && (from.Paralyzed || from.Frozen || (from.Spell != null && from.Spell.IsCasting)) ) + { + from.SendLocalizedMessage( 1062725 ); // You can not use that potion while paralyzed. + return; + } + + int delay = GetDelay( from ); + + if ( delay > 0 ) + { + from.SendLocalizedMessage( 1072529, String.Format( "{0}\t{1}", delay, delay > 1 ? "seconds." : "second." ) ); // You cannot use that for another ~1_NUM~ ~2_TIMEUNITS~ + return; + } + + ThrowTarget targ = from.Target as ThrowTarget; + + if ( targ != null && targ.Potion == this ) + return; + + from.RevealingAction(); + + if ( !m_Users.Contains( from ) ) + m_Users.Add( from ); + + from.Target = new ThrowTarget( this ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + private List m_Users = new List(); + + public void Explode_Callback( object state ) + { + object[] states = (object[]) state; + + Explode( (Mobile) states[ 0 ], (Point3D) states[ 1 ], (Map) states[ 2 ] ); + } + + public virtual void Explode( Mobile from, Point3D loc, Map map ) + { + if ( Deleted || map == null ) + return; + + Consume(); + + // Check if any other players are using this potion + for ( int i = 0; i < m_Users.Count; i ++ ) + { + ThrowTarget targ = m_Users[ i ].Target as ThrowTarget; + + if ( targ != null && targ.Potion == this ) + Target.Cancel( from ); + } + + // Effects + Effects.PlaySound( loc, map, 0x207 ); + + Geometry.Circle2D( loc, map, Radius, new DoEffect_Callback( BlastEffect ), 270, 90 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 0.3 ), new TimerStateCallback( CircleEffect2 ), new object[] { loc, map } ); + + foreach ( Mobile mobile in map.GetMobilesInRange( loc, Radius ) ) + { + if ( mobile is BaseCreature ) + { + BaseCreature mon = (BaseCreature) mobile; + + mon.Pacify( from, DateTime.Now + TimeSpan.FromSeconds( 5.0 ) ); // TODO check + } + } + } + + #region Effects + public virtual void BlastEffect( Point3D p, Map map ) + { + if ( map.CanFit( p, 12, true, false ) ) + Effects.SendLocationEffect( p, map, 0x376A, 4, 9 ); + } + + public void CircleEffect2( object state ) + { + object[] states = (object[]) state; + + Geometry.Circle2D( (Point3D)states[0], (Map)states[1], Radius, new DoEffect_Callback( BlastEffect ), 90, 270 ); + } + #endregion + + #region Delay + private static Hashtable m_Delay = new Hashtable(); + + public static void AddDelay( Mobile m ) + { + Timer timer = m_Delay[ m ] as Timer; + + if ( timer != null ) + timer.Stop(); + + m_Delay[ m ] = Timer.DelayCall( TimeSpan.FromSeconds( 60 ), new TimerStateCallback( EndDelay_Callback ), m ); + } + + public static int GetDelay( Mobile m ) + { + Timer timer = m_Delay[ m ] as Timer; + + if ( timer != null && timer.Next > DateTime.Now ) + return (int) (timer.Next - DateTime.Now).TotalSeconds; + + return 0; + } + + private static void EndDelay_Callback( object obj ) + { + if ( obj is Mobile ) + EndDelay( (Mobile) obj ); + } + + public static void EndDelay( Mobile m ) + { + Timer timer = m_Delay[ m ] as Timer; + + if ( timer != null ) + { + timer.Stop(); + m_Delay.Remove( m ); + } + } + #endregion + + private class ThrowTarget : Target + { + private BaseConfusionBlastPotion m_Potion; + + public BaseConfusionBlastPotion Potion + { + get{ return m_Potion; } + } + + public ThrowTarget( BaseConfusionBlastPotion potion ) : base( 12, true, TargetFlags.None ) + { + m_Potion = potion; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Potion.Deleted || m_Potion.Map == Map.Internal ) + return; + + IPoint3D p = targeted as IPoint3D; + + if ( p == null || from.Map == null ) + return; + + // Add delay + BaseConfusionBlastPotion.AddDelay( from ); + + SpellHelper.GetSurfaceTop( ref p ); + + from.RevealingAction(); + + IEntity to; + + if ( p is Mobile ) + to = (Mobile)p; + else + to = new Entity( Serial.Zero, new Point3D( p ), from.Map ); + + Effects.SendMovingEffect( from, to, 0xF0D, 7, 0, false, false, m_Potion.Hue, 0 ); + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( m_Potion.Explode_Callback ), new object[] { from, new Point3D( p ), from.Map } ); + } + } + } +} diff --git a/Scripts/Items/Skill Items/Magical/Potions/Confusion Blast Potions/ConfusionBlastPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Confusion Blast Potions/ConfusionBlastPotion.cs new file mode 100644 index 000000000..faa2aacb2 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Confusion Blast Potions/ConfusionBlastPotion.cs @@ -0,0 +1,35 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ConfusionBlastPotion : BaseConfusionBlastPotion + { + public override int Radius{ get{ return 5; } } + + public override int LabelNumber{ get{ return 1072105; } } // a Confusion Blast potion + + [Constructable] + public ConfusionBlastPotion() : base( PotionEffect.ConfusionBlast ) + { + } + + public ConfusionBlastPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Skill Items/Magical/Potions/Confusion Blast Potions/GreaterConfusionBlastPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Confusion Blast Potions/GreaterConfusionBlastPotion.cs new file mode 100644 index 000000000..56d8c2d82 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Confusion Blast Potions/GreaterConfusionBlastPotion.cs @@ -0,0 +1,35 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GreaterConfusionBlastPotion : BaseConfusionBlastPotion + { + public override int Radius{ get{ return 7; } } + + public override int LabelNumber{ get{ return 1072108; } } // a Greater Confusion Blast potion + + [Constructable] + public GreaterConfusionBlastPotion() : base( PotionEffect.ConfusionBlastGreater ) + { + } + + public GreaterConfusionBlastPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Misc/Geometry.cs b/Scripts/Misc/Geometry.cs new file mode 100644 index 000000000..d716b9101 --- /dev/null +++ b/Scripts/Misc/Geometry.cs @@ -0,0 +1,224 @@ +using System; +using Server; + +namespace Server.Misc +{ + public delegate void DoEffect_Callback( Point3D p, Map map ); + + public static class Geometry + { + public static void Swap( ref T a, ref T b ) + { + T temp = a; + a = b; + b = temp; + } + + public static double RadiansToDegrees( double angle ) + { + return angle * (180.0 / Math.PI); + } + + public static double DegreesToRadians( double angle ) + { + return angle * ( Math.PI / 180.0 ); + } + + public class CirclePoint + { + private Point2D point; + private int angle; + private int quadrant; + + public Point2D Point{ get{ return point; } } + public int Angle{ get{ return angle; } } + public int Quadrant{ get{ return quadrant; } } + + public CirclePoint( Point2D point, int angle, int quadrant ) + { + this.point = point; + this.angle = angle; + this.quadrant = quadrant; + } + } + + public static Point2D ArcPoint( Point3D loc, int radius, int angle ) + { + int sideA, sideB; + + if ( angle < 0 ) + angle = 0; + + if ( angle > 90 ) + angle = 90; + + sideA = (int) Math.Round( radius * Math.Sin( DegreesToRadians( angle ) ) ); + sideB = (int) Math.Round( radius * Math.Cos( DegreesToRadians( angle ) ) ); + + return new Point2D( loc.X - sideB, loc.Y - sideA ); + } + + public static void Circle2D( Point3D loc, Map map, int radius, DoEffect_Callback effect ) + { + Circle2D( loc, map, radius, effect, 0, 360 ); + } + + public static void Circle2D( Point3D loc, Map map, int radius, DoEffect_Callback effect, int angleStart, int angleEnd ) + { + if ( angleStart < 0 || angleStart > 360 ) + angleStart = 0; + + if ( angleEnd > 360 || angleEnd < 0 ) + angleEnd = 360; + + if ( angleStart == angleEnd ) + return; + + bool opposite = angleStart > angleEnd; + + int startQuadrant = angleStart / 90; + int endQuadrant = angleEnd / 90; + + Point2D start = ArcPoint( loc, radius, angleStart % 90 ); + Point2D end = ArcPoint( loc, radius, angleEnd % 90 ); + + if ( opposite ) + { + Swap( ref start, ref end ); + Swap( ref startQuadrant, ref endQuadrant ); + } + + CirclePoint startPoint = new CirclePoint( start, angleStart, startQuadrant ); + CirclePoint endPoint = new CirclePoint( end, angleEnd, endQuadrant ); + + int error = -radius; + int x = radius; + int y = 0; + + while (x > y) + { + plot4points( loc, map, x, y, startPoint, endPoint, effect, opposite ); + plot4points( loc, map, y, x, startPoint, endPoint, effect, opposite ); + + error += ( y * 2 ) + 1; + ++y; + + if (error >= 0) + { + --x; + error -= x * 2; + } + } + + plot4points( loc, map, x, y, startPoint, endPoint, effect, opposite ); + } + + public static void plot4points( Point3D loc, Map map, int x, int y, CirclePoint start, CirclePoint end, DoEffect_Callback effect, bool opposite ) + { + Point2D pointA = new Point2D( loc.X - x, loc.Y - y ); + Point2D pointB = new Point2D( loc.X - y, loc.Y - x ); + + int quadrant = 2; + + if ( x == 0 && start.Quadrant == 3 ) + quadrant = 3; + + if ( WithinCircleBounds( quadrant == 3 ? pointB : pointA, quadrant, loc, start, end, opposite ) ) + effect( new Point3D( loc.X + x, loc.Y + y, loc.Z ), map ); + + quadrant = 3; + + if ( y == 0 && start.Quadrant == 0 ) + quadrant = 0; + + if ( x != 0 && WithinCircleBounds( quadrant == 0 ? pointA : pointB, quadrant, loc, start, end, opposite ) ) + effect( new Point3D( loc.X - x, loc.Y + y, loc.Z ), map ); + if ( y != 0 && WithinCircleBounds( pointB, 1, loc, start, end, opposite ) ) + effect( new Point3D( loc.X + x, loc.Y - y, loc.Z ), map ); + if ( x != 0 && y != 0 && WithinCircleBounds( pointA, 0, loc, start, end, opposite ) ) + effect( new Point3D( loc.X - x, loc.Y - y, loc.Z ), map ); + } + + public static bool WithinCircleBounds( Point2D pointLoc, int pointQuadrant, Point3D center, CirclePoint start, CirclePoint end, bool opposite ) + { + if ( start.Angle == 0 && end.Angle == 360 ) + return true; + + int startX = start.Point.X; + int startY = start.Point.Y; + int endX = end.Point.X; + int endY = end.Point.Y; + + int x = pointLoc.X; + int y = pointLoc.Y; + + if ( pointQuadrant < start.Quadrant || pointQuadrant > end.Quadrant ) + return opposite; + + if ( pointQuadrant > start.Quadrant && pointQuadrant < end.Quadrant ) + return !opposite; + + bool withinBounds = true; + + if ( start.Quadrant == end.Quadrant ) + { + if ( startX == endX && ( x > startX || y > startY || y < endY ) ) + withinBounds = false; + else if ( startY == endY && ( y < startY || x < startX || x > endX ) ) + withinBounds = false; + else if ( x < startX || x > endX || y > startY || y < endY ) + withinBounds = false; + } + else if ( pointQuadrant == start.Quadrant && ( x < startX || y > startY ) ) + withinBounds = false; + else if ( pointQuadrant == end.Quadrant && ( x > endX || y < endY ) ) + withinBounds = false; + + return opposite ? !withinBounds : withinBounds; + } + + public static void Line2D( Point3D start, Point3D end, Map map, DoEffect_Callback effect ) + { + bool steep = Math.Abs( end.Y - start.Y ) > Math.Abs( end.X - start.X ); + + int x0 = start.X; + int x1 = end.X; + int y0 = start.Y; + int y1 = end.Y; + + if ( steep ) + { + Swap( ref x0, ref y0 ); + Swap( ref x1, ref y1 ); + } + + if ( x0 > x1 ) + { + Swap( ref x0, ref x1 ); + Swap( ref y0, ref y1 ); + } + + int deltax = x1 - x0; + int deltay = Math.Abs( y1 - y0 ); + int error = deltax / 2; + int ystep = y0 < y1 ? 1 : -1; + int y = y0; + + for ( int x = x0; x <= x1; x++ ) + { + if ( steep ) + effect( new Point3D( y, x, start.Z ), map ); + else + effect( new Point3D( x, y, start.Z ), map ); + + error -= deltay; + + if ( error < 0 ) + { + y += ystep; + error += deltax; + } + } + } + } +} diff --git a/Scripts/Mobiles/AI/Creature/BaseCreature.cs b/Scripts/Mobiles/AI/Creature/BaseCreature.cs index d33827a7c..eb978fc8c 100644 --- a/Scripts/Mobiles/AI/Creature/BaseCreature.cs +++ b/Scripts/Mobiles/AI/Creature/BaseCreature.cs @@ -4502,7 +4502,9 @@ namespace Server.Mobiles Faction.HandleDeath( this, ds.m_Mobile ); } - if( !givenToTKill && Map == Map.Tokuno ) + Region region = ds.m_Mobile.Region; + + if( !givenToTKill && ( Map == Map.Tokuno || region.IsPartOf( "Yomotsu Mines" ) || region.IsPartOf( "Fan Dancer's Dojo" ) )) { givenToTKill = true; TreasuresOfTokuno.HandleKill( this, ds.m_Mobile ); diff --git a/Scripts/Spells/Necromancy/NecromancerSpell.cs b/Scripts/Spells/Necromancy/NecromancerSpell.cs index 394a4b22a..99046805e 100644 --- a/Scripts/Spells/Necromancy/NecromancerSpell.cs +++ b/Scripts/Spells/Necromancy/NecromancerSpell.cs @@ -34,7 +34,7 @@ namespace Server.Spells.Necromancy public override void GetCastSkills( out double min, out double max ) { min = RequiredSkill; - max = RequiredSkill + 40.0; + max = Scroll != null ? min : RequiredSkill + 40.0; } public override bool ConsumeReagents() diff --git a/Scripts/Spells/Spellweaving/ArcaneCircle.cs b/Scripts/Spells/Spellweaving/ArcaneCircle.cs index 689f65f3f..c0695234c 100644 --- a/Scripts/Spells/Spellweaving/ArcaneCircle.cs +++ b/Scripts/Spells/Spellweaving/ArcaneCircle.cs @@ -118,7 +118,7 @@ namespace Server.Spells.Spellweaving //OSI Verified: Even enemies/combatants count foreach( Mobile m in Caster.GetMobilesInRange( 1 ) ) //Range verified as 1 { - if ( m != Caster && Caster.CanBeBeneficial( m, false ) && Math.Abs( Caster.Skills.Spellweaving.Value - m.Skills.Spellweaving.Value ) <= 20 && !(m is Clone) ) + if ( m != Caster && m is PlayerMobile && Caster.CanBeBeneficial( m, false ) && Math.Abs( Caster.Skills.Spellweaving.Value - m.Skills.Spellweaving.Value ) <= 20 && !(m is Clone) ) { weavers.Add( m ); }