From 33de7434c5da9d416daef9eda9c8eeff7fc29b1d Mon Sep 17 00:00:00 2001 From: xavier Date: Fri, 31 Dec 2010 20:28:58 +0000 Subject: [PATCH] Ninja Darts and Shuriken should use combat checks, never 100% chance of hit. Should trigger Evil Omen poison level increase. Missed shots should not consume poison chg, but empty ammo clears weapon poison. Code overhaul. --- .../Harvest Tools/BaseHarvestTool.cs | 4 +- Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs | 288 ++----------- .../Items/Skill Items/Ninjitsu/FukiyaDarts.cs | 2 +- .../Skill Items/Ninjitsu/LeatherNinjaBelt.cs | 304 ++------------ .../Skill Items/Ninjitsu/NinjaWeapons.cs | 385 ++++++++++++++++++ .../Items/Skill Items/Ninjitsu/Shuriken.cs | 2 +- Scripts/Mobiles/BaseCreature.cs | 20 +- Scripts/Mobiles/PlayerMobile.cs | 4 +- Scripts/Spells/Chivalry/NobleSacrifice.cs | 2 +- Scripts/Spells/Chivalry/RemoveCurse.cs | 2 +- Scripts/Spells/Necromancy/EvilOmen.cs | 78 ++-- 11 files changed, 520 insertions(+), 571 deletions(-) create mode 100644 Scripts/Items/Skill Items/Ninjitsu/NinjaWeapons.cs diff --git a/Scripts/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs b/Scripts/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs index 2a5c96570..770b96c24 100644 --- a/Scripts/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs +++ b/Scripts/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs @@ -1,7 +1,5 @@ using System; -using System.Collections; using System.Collections.Generic; -using Server; using Server.Mobiles; using Server.Network; using Server.Engines.Craft; @@ -10,7 +8,7 @@ using Server.ContextMenus; namespace Server.Items { - interface IUsesRemaining + public interface IUsesRemaining { int UsesRemaining{ get; set; } bool ShowUsesRemaining{ get; set; } diff --git a/Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs b/Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs index ca3ee8355..2250dd902 100644 --- a/Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs +++ b/Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs @@ -9,10 +9,22 @@ using Server.Mobiles; namespace Server.Items { [FlipableAttribute( 0x27AA, 0x27F5 )] - public class Fukiya : Item, IUsesRemaining + public class Fukiya : Item, INinjaWeapon { - private int m_UsesRemaining; + public virtual int WrongAmmoMessage { get { return 1063329; } } //You can only load fukiya darts + public virtual int NoFreeHandMessage { get { return 1063327; } } //You must have a free hand to use a fukiya. + public virtual int EmptyWeaponMessage { get { return 1063325; } } //You have no fukiya darts! + public virtual int RecentlyUsedMessage { get { return 1063326; } } //You are already using that fukiya. + public virtual int FullWeaponMessage { get { return 1063330; } } //You can only load fukiya darts + public virtual int WeaponMinRange { get { return 0; } } + public virtual int WeaponMaxRange { get { return 6; } } + + public virtual int WeaponDamage { get { return Utility.RandomMinMax(4, 6); } } + + public Type AmmoType{ get { return typeof(FukiyaDarts); } } + + private int m_UsesRemaining; private Poison m_Poison; private int m_PoisonCharges; @@ -20,7 +32,7 @@ namespace Server.Items public int UsesRemaining { get { return m_UsesRemaining; } - set { m_UsesRemaining = value; InvalidateProperties(); } + set { m_UsesRemaining = value; InvalidateProperties(); } } [CommandProperty( AccessLevel.GameMaster )] @@ -50,6 +62,17 @@ namespace Server.Items { } + public void AttackAnimation(Mobile from, Mobile to) + { + if (from.Body.IsHuman && !from.Mounted) + { + from.Animate(33, 2, 1, true, true, 0); + } + + from.PlaySound(0x223); + from.MovingEffect(to, 0x2804, 5, 0, false, false); + } + public override void GetProperties( ObjectPropertyList list ) { base.GetProperties( list ); @@ -62,224 +85,7 @@ namespace Server.Items public override void OnDoubleClick( Mobile from ) { - if ( !IsChildOf( from ) ) - return; - - if ( m_UsesRemaining < 1 ) - { - // You have no fukiya darts! - from.SendLocalizedMessage( 1063325 ); - } - else if (((PlayerMobile)from).NinjaWepCooldown) - { - // You are already using that fukiya. - from.SendLocalizedMessage( 1063326 ); - } - else if ( !BasePotion.HasFreeHand( from ) ) - { - // You must have a free hand to use a fukiya. - from.SendLocalizedMessage( 1063327 ); - } - else - { - from.BeginTarget( 5, false, TargetFlags.Harmful, new TargetCallback( OnTarget ) ); - } - } - - public void Shoot( Mobile from, Mobile target ) - { - if ( from == target ) - return; - - if ( m_UsesRemaining < 1 ) - { - // You have no fukiya darts! - from.SendLocalizedMessage( 1063325 ); - } - else if (((PlayerMobile)from).NinjaWepCooldown) - { - // You are already using that fukiya. - from.SendLocalizedMessage( 1063326 ); - } - else if ( !BasePotion.HasFreeHand( from ) ) - { - // You must have a free hand to use a fukiya. - from.SendLocalizedMessage( 1063327 ); - } - else if ( from.CanBeHarmful( target ) ) - { - ((PlayerMobile)from).NinjaWepCooldown = true; - - from.Direction = from.GetDirectionTo( target ); - - from.RevealingAction(); - - if ( from.Body.IsHuman && !from.Mounted ) - from.Animate( 33, 2, 1, true, true, 0 ); - - from.PlaySound( 0x223 ); - from.MovingEffect( target, 0x2804, 5, 0, false, false ); - - if ( from.CheckSkill( SkillName.Ninjitsu, -10.0, 50.0 ) ) - Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( OnDartHit ), new object[]{ from, target } ); - else - ConsumeUse(); - - Timer.DelayCall( TimeSpan.FromSeconds( 2.5 ), new TimerStateCallback( ResetUsing ), from ); - } - } - - private void OnDartHit( object state ) - { - object[] states = (object[])state; - Mobile from = (Mobile)states[0]; - Mobile target = (Mobile)states[1]; - - if ( !from.CanBeHarmful( target ) ) - return; - - from.DoHarmful( target ); - - AOS.Damage( target, from, Utility.RandomMinMax( 4, 6 ), 100, 0, 0, 0, 0 ); - - if ( m_Poison != null && m_PoisonCharges > 0 ) - target.ApplyPoison( from, m_Poison ); - - ConsumeUse(); - } - - public void ConsumeUse() - { - if ( m_UsesRemaining < 1 ) - return; - - --UsesRemaining; - - if ( m_PoisonCharges > 0 ) - { - --PoisonCharges; - - if ( m_PoisonCharges == 0 ) - Poison = null; - } - } - - public void ResetUsing(object state) - { - PlayerMobile from = (PlayerMobile)state; - from.NinjaWepCooldown = false; - } - - private const int MaxUses = 10; - - public void Unload( Mobile from ) - { - if ( m_UsesRemaining < 1 ) - return; - - FukiyaDarts darts = new FukiyaDarts( m_UsesRemaining ); - - darts.Poison = m_Poison; - darts.PoisonCharges = m_PoisonCharges; - - from.AddToBackpack( darts ); - - UsesRemaining = 0; - PoisonCharges = 0; - Poison = null; - } - - public void Reload( Mobile from, FukiyaDarts darts ) - { - int need = ( MaxUses - m_UsesRemaining ); - - if ( need <= 0 ) - { - // You cannot add anymore fukiya darts - from.SendLocalizedMessage( 1063330 ); - } - else if ( darts.UsesRemaining > 0 ) - { - bool canload = false; - bool poison = false; - - if ( need > darts.UsesRemaining ) - need = darts.UsesRemaining; - - if( darts.Poison != null && darts.PoisonCharges > 0 ) - { - poison = true; - - if( m_Poison == null || ( m_Poison.Level < darts.Poison.Level )) - { - Unload( from ); - canload = true; - } - else if( m_Poison != null && ( m_Poison.Level == darts.Poison.Level )) - { - canload = true; - } - } - else if( darts.Poison == null || darts.PoisonCharges <= 0 ) - { - if( m_Poison == null || m_PoisonCharges <= 0 ) - { - canload = true; - } - } - - if( !canload ) - { - from.SendLocalizedMessage( 1070767 ); // Loaded projectile is stronger, unload it first - } - else - { - if( poison ) - { - if ( need > darts.PoisonCharges ) - { - need = darts.PoisonCharges; - } - - if ( m_Poison == null || m_PoisonCharges <= 0 ) - { - PoisonCharges = need; - } - else - { - PoisonCharges += need; - } - - Poison = darts.Poison; - - darts.PoisonCharges -= need; - - if ( darts.PoisonCharges <= 0 ) - { - darts.Poison = null; - } - } - - UsesRemaining += need; - darts.UsesRemaining -= need; - } - - if ( darts.UsesRemaining <= 0 ) - darts.Delete(); - } - } - - public void OnTarget( Mobile from, object obj ) - { - if ( Deleted || !IsChildOf( from ) ) - return; - - if ( obj is Mobile ) - Shoot( from, (Mobile) obj ); - else if ( obj is FukiyaDarts ) - Reload( from, (FukiyaDarts) obj ); - else - from.SendLocalizedMessage( 1063329 ); // You can only load fukiya darts + NinjaWeapon.AttemptShoot((PlayerMobile)from, this); } public override void GetContextMenuEntries( Mobile from, List list ) @@ -288,42 +94,8 @@ namespace Server.Items if ( IsChildOf( from ) ) { - list.Add( new LoadEntry( this ) ); - list.Add( new UnloadEntry( this ) ); - } - } - - private class LoadEntry : ContextMenuEntry - { - private Fukiya m_Fukiya; - - public LoadEntry( Fukiya fukiya ) : base( 6224, 0 ) - { - m_Fukiya = fukiya; - } - - public override void OnClick() - { - if ( !m_Fukiya.Deleted && m_Fukiya.IsChildOf( Owner.From ) ) - Owner.From.BeginTarget( 5, false, TargetFlags.Harmful, new TargetCallback( m_Fukiya.OnTarget ) ); - } - } - - private class UnloadEntry : ContextMenuEntry - { - private Fukiya m_Fukiya; - - public UnloadEntry( Fukiya fukiya ) : base( 6225, 0 ) - { - m_Fukiya = fukiya; - - Enabled = ( fukiya.UsesRemaining > 0 ); - } - - public override void OnClick() - { - if ( !m_Fukiya.Deleted && m_Fukiya.IsChildOf( Owner.From ) ) - m_Fukiya.Unload( Owner.From ); + list.Add(new NinjaWeapon.LoadEntry(this)); + list.Add(new NinjaWeapon.UnloadEntry(this)); } } @@ -338,7 +110,7 @@ namespace Server.Items Poison.Serialize( m_Poison, writer ); writer.Write( (int) m_PoisonCharges ); } - + public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); diff --git a/Scripts/Items/Skill Items/Ninjitsu/FukiyaDarts.cs b/Scripts/Items/Skill Items/Ninjitsu/FukiyaDarts.cs index e43797a97..30ab1395f 100644 --- a/Scripts/Items/Skill Items/Ninjitsu/FukiyaDarts.cs +++ b/Scripts/Items/Skill Items/Ninjitsu/FukiyaDarts.cs @@ -4,7 +4,7 @@ using Server.Engines.Craft; namespace Server.Items { - public class FukiyaDarts : Item, IUsesRemaining, ICraftable + public class FukiyaDarts : Item, ICraftable, INinjaAmmo { private int m_UsesRemaining; diff --git a/Scripts/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs b/Scripts/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs index 6e01b35ee..71e56766f 100644 --- a/Scripts/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs +++ b/Scripts/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs @@ -9,12 +9,24 @@ using Server.Mobiles; namespace Server.Items { [FlipableAttribute( 0x2790, 0x27DB )] - public class LeatherNinjaBelt : BaseWaist, IUsesRemaining, IDyable + public class LeatherNinjaBelt : BaseWaist, IDyable, INinjaWeapon { public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } - private int m_UsesRemaining; + public virtual int WrongAmmoMessage { get { return 1063301; } } //You can only place shuriken in a ninja belt. + public virtual int NoFreeHandMessage { get { return 1063299; } } //You must have a free hand to throw shuriken. + public virtual int EmptyWeaponMessage { get { return 1063297; } } //You have no shuriken in your ninja belt! + public virtual int RecentlyUsedMessage { get { return 1063298; } } //You cannot throw another shuriken yet. + public virtual int FullWeaponMessage { get { return 1063302; } } //You cannot add any more shuriken. + public virtual int WeaponMinRange { get { return 2; } } + public virtual int WeaponMaxRange { get { return 10; } } + + public virtual int WeaponDamage { get { return Utility.RandomMinMax(3, 5); } } + + public virtual Type AmmoType { get { return typeof(Shuriken); } } + + private int m_UsesRemaining; private Poison m_Poison; private int m_PoisonCharges; @@ -43,6 +55,7 @@ namespace Server.Items [Constructable] public LeatherNinjaBelt() : base( 0x2790 ) + { Weight = 1.0; Layer = Layer.Waist; @@ -52,6 +65,17 @@ namespace Server.Items { } + public void AttackAnimation(Mobile from, Mobile to) + { + if (from.Body.IsHuman) + { + from.Animate(from.Mounted ? 26 : 9, 7, 1, true, false, 0); + } + + from.PlaySound(0x23A); + from.MovingEffect(to, 0x27AC, 1, 0, false, false); + } + public override void GetProperties( ObjectPropertyList list ) { base.GetProperties( list ); @@ -64,237 +88,17 @@ namespace Server.Items public override bool OnEquip( Mobile from ) { - if ( !base.OnEquip( from ) ) - return false; - - from.SendLocalizedMessage( 1070785 ); // Double click this item each time you wish to throw a shuriken. - return true; + if (base.OnEquip(from)) + { + from.SendLocalizedMessage(1070785); // Double click this item each time you wish to throw a shuriken. + return true; + } + return false; } - public override void OnDoubleClick( Mobile from ) + public override void OnDoubleClick(Mobile from) { - if ( !IsChildOf( from ) ) - return; - - if ( m_UsesRemaining < 1 ) - { - // You have no shuriken in your ninja belt! - from.SendLocalizedMessage( 1063297 ); - } - else if (((PlayerMobile)from).NinjaWepCooldown) - { - // You cannot throw another shuriken yet. - from.SendLocalizedMessage( 1063298 ); - } - else if ( !BasePotion.HasFreeHand( from ) ) - { - // You must have a free hand to throw shuriken. - from.SendLocalizedMessage( 1063299 ); - } - else - { - from.BeginTarget( 10, false, TargetFlags.Harmful, new TargetCallback( OnTarget ) ); - } - } - - public void Shoot( Mobile from, Mobile target ) - { - if ( from == target ) - return; - - if ( m_UsesRemaining < 1 ) - { - // You have no shuriken in your ninja belt! - from.SendLocalizedMessage( 1063297 ); - } - else if (((PlayerMobile)from).NinjaWepCooldown) - { - // You cannot throw another shuriken yet. - from.SendLocalizedMessage( 1063298 ); - } - else if ( !BasePotion.HasFreeHand( from ) ) - { - // You must have a free hand to throw shuriken. - from.SendLocalizedMessage( 1063299 ); - } - else if ( from.InRange( target, 2 ) ) - { - from.SendLocalizedMessage( 1063303 ); // Your target is too close! - } - else if ( from.CanBeHarmful( target ) ) - { - ((PlayerMobile)from).NinjaWepCooldown = true; - - from.Direction = from.GetDirectionTo( target ); - - from.RevealingAction(); - - if ( from.Body.IsHuman ) - from.Animate( from.Mounted ? 26 : 9, 7, 1, true, false, 0 ); - - from.PlaySound( 0x23A ); - from.MovingEffect( target, 0x27AC, 1, 0, false, false ); - - if ( from.CheckSkill( SkillName.Ninjitsu, -10.0, 65.0 ) ) - Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( OnShurikenHit ), new object[]{ from, target } ); - else - ConsumeUse(); - - Timer.DelayCall(TimeSpan.FromSeconds(2.5), new TimerStateCallback( ResetUsing ), from ); - } - } - - private void OnShurikenHit( object state ) - { - object[] states = (object[])state; - Mobile from = (Mobile)states[0]; - Mobile target = (Mobile)states[1]; - - if ( !from.CanBeHarmful( target ) ) - return; - - from.DoHarmful( target ); - - AOS.Damage( target, from, Utility.RandomMinMax( 3, 5 ), 100, 0, 0, 0, 0 ); - - if ( m_Poison != null && m_PoisonCharges > 0 ) - target.ApplyPoison( from, m_Poison ); - - ConsumeUse(); - } - - public void ConsumeUse() - { - if ( m_UsesRemaining < 1 ) - return; - - --UsesRemaining; - - if ( m_PoisonCharges > 0 ) - { - --PoisonCharges; - - if ( m_PoisonCharges == 0 ) - Poison = null; - } - } - - public void ResetUsing(object state) - { - PlayerMobile from = (PlayerMobile)state; - from.NinjaWepCooldown = false; - } - - private const int MaxUses = 10; - - public void Unload( Mobile from ) - { - if ( m_UsesRemaining < 1 ) - return; - - Shuriken shuriken = new Shuriken( m_UsesRemaining ); - - shuriken.Poison = m_Poison; - shuriken.PoisonCharges = m_PoisonCharges; - - from.AddToBackpack( shuriken ); - - UsesRemaining = 0; - PoisonCharges = 0; - Poison = null; - } - - public void Reload( Mobile from, Shuriken shuriken ) - { - int need = ( MaxUses - m_UsesRemaining ); - - if ( need <= 0 ) - { - // You cannot add any more shuriken. - from.SendLocalizedMessage( 1063302 ); - } - else if ( shuriken.UsesRemaining > 0 ) - { - bool canload = false; - bool poison = false; - - if ( need > shuriken.UsesRemaining ) - need = shuriken.UsesRemaining; - - if( shuriken.Poison != null && shuriken.PoisonCharges > 0 ) - { - poison = true; - - if( m_Poison == null || ( m_Poison.Level < shuriken.Poison.Level )) - { - Unload( from ); - canload = true; - } - else if( m_Poison != null && ( m_Poison.Level == shuriken.Poison.Level )) - { - canload = true; - } - } - else if( shuriken.Poison == null || shuriken.PoisonCharges <= 0 ) - { - if( m_Poison == null || m_PoisonCharges <= 0 ) - { - canload = true; - } - } - - if( !canload ) - { - from.SendLocalizedMessage( 1070767 ); // Loaded projectile is stronger, unload it first - } - else - { - if( poison ) - { - if ( need > shuriken.PoisonCharges ) - { - need = shuriken.PoisonCharges; - } - - if ( m_Poison == null || m_PoisonCharges <= 0 ) - { - PoisonCharges = need; - } - else - { - PoisonCharges += need; - } - - Poison = shuriken.Poison; - - shuriken.PoisonCharges -= need; - - if ( shuriken.PoisonCharges <= 0 ) - { - shuriken.Poison = null; - } - } - - UsesRemaining += need; - shuriken.UsesRemaining -= need; - } - - if ( shuriken.UsesRemaining <= 0 ) - shuriken.Delete(); - } - } - - public void OnTarget( Mobile from, object obj ) - { - if ( Deleted || !IsChildOf( from ) ) - return; - - if ( obj is Mobile ) - Shoot( from, (Mobile) obj ); - else if ( obj is Shuriken ) - Reload( from, (Shuriken) obj ); - else - from.SendLocalizedMessage( 1063301 ); // You can only place shuriken in a ninja belt. + NinjaWeapon.AttemptShoot((PlayerMobile)from, this); } public override void GetContextMenuEntries( Mobile from, List list ) @@ -303,42 +107,8 @@ namespace Server.Items if ( IsChildOf( from ) ) { - list.Add( new LoadEntry( this ) ); - list.Add( new UnloadEntry( this ) ); - } - } - - private class LoadEntry : ContextMenuEntry - { - private LeatherNinjaBelt m_Belt; - - public LoadEntry( LeatherNinjaBelt belt ) : base( 6222, 0 ) - { - m_Belt = belt; - } - - public override void OnClick() - { - if ( !m_Belt.Deleted && m_Belt.IsChildOf( Owner.From ) ) - Owner.From.BeginTarget( 10, false, TargetFlags.Harmful, new TargetCallback( m_Belt.OnTarget ) ); - } - } - - private class UnloadEntry : ContextMenuEntry - { - private LeatherNinjaBelt m_Belt; - - public UnloadEntry( LeatherNinjaBelt belt ) : base( 6223, 0 ) - { - m_Belt = belt; - - Enabled = ( belt.UsesRemaining > 0 ); - } - - public override void OnClick() - { - if ( !m_Belt.Deleted && m_Belt.IsChildOf( Owner.From ) ) - m_Belt.Unload( Owner.From ); + list.Add( new NinjaWeapon.LoadEntry( this ) ); + list.Add( new NinjaWeapon.UnloadEntry( this ) ); } } @@ -353,7 +123,7 @@ namespace Server.Items Poison.Serialize( m_Poison, writer ); writer.Write( (int) m_PoisonCharges ); } - + public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); diff --git a/Scripts/Items/Skill Items/Ninjitsu/NinjaWeapons.cs b/Scripts/Items/Skill Items/Ninjitsu/NinjaWeapons.cs new file mode 100644 index 000000000..65425a72b --- /dev/null +++ b/Scripts/Items/Skill Items/Ninjitsu/NinjaWeapons.cs @@ -0,0 +1,385 @@ +using System; +using Server.ContextMenus; +using Server.Mobiles; +using Server.Spells.Necromancy; +using Server.Spells.Ninjitsu; +using Server.Targeting; + +/* + * There really was no prettier way to do this, other than the one + * suggestion to make a rigged baseninjaweapon class that bypasses its + * own serialization, due to the way these weapons were originaly coded. + */ + +namespace Server.Items +{ + public interface INinjaAmmo : IUsesRemaining + { + int PoisonCharges { get; set; } + Poison Poison { get; set; } + } + + public interface INinjaWeapon : IUsesRemaining + { + int NoFreeHandMessage { get; } + int EmptyWeaponMessage { get; } + int RecentlyUsedMessage { get; } + int FullWeaponMessage { get; } + int WrongAmmoMessage { get; } + Type AmmoType { get; } + int PoisonCharges { get; set; } + Poison Poison { get; set; } + int WeaponDamage { get; } + int WeaponMinRange { get; } + int WeaponMaxRange { get; } + + void AttackAnimation(Mobile from, Mobile to); + } + + public class NinjaWeapon + { + private const int MaxUses = 10; + + public static void AttemptShoot(PlayerMobile from, INinjaWeapon weapon) + { + if (CanUseWeapon(from, weapon)) + { + from.BeginTarget(weapon.WeaponMaxRange, false, TargetFlags.Harmful, new TargetStateCallback(OnTarget), weapon); + } + } + + private static void Shoot(PlayerMobile from, Mobile target, INinjaWeapon weapon) + { + if (from != target && CanUseWeapon(from, weapon) && from.CanBeHarmful(target)) + { + if (weapon.WeaponMinRange == 0 || !from.InRange(target, weapon.WeaponMinRange)) + { + from.NinjaWepCooldown = true; + + from.Direction = from.GetDirectionTo(target); + + from.RevealingAction(); + + weapon.AttackAnimation(from, target); + + ConsumeUse(weapon); + + if (CombatCheck(from, target)) + { + Timer.DelayCall(TimeSpan.FromSeconds(1.0), new TimerStateCallback(OnHit), new object[] { from, target, weapon }); + } + + Timer.DelayCall(TimeSpan.FromSeconds(2.5), new TimerStateCallback(ResetUsing), from); + } + else + { + from.SendLocalizedMessage(1063303); // Your target is too close! + } + } + } + + private static void ResetUsing(PlayerMobile from) + { + from.NinjaWepCooldown = false; + } + + private static void Unload(Mobile from, INinjaWeapon weapon) + { + if (weapon.UsesRemaining > 0) + { + INinjaAmmo ammo = Activator.CreateInstance(weapon.AmmoType, new object[] { weapon.UsesRemaining }) as INinjaAmmo; + + ammo.Poison = weapon.Poison; + ammo.PoisonCharges = weapon.PoisonCharges; + + from.AddToBackpack((Item)ammo); + + weapon.UsesRemaining = 0; + weapon.PoisonCharges = 0; + weapon.Poison = null; + } + } + + private static void Reload(PlayerMobile from, INinjaWeapon weapon, INinjaAmmo ammo) + { + if (weapon.UsesRemaining < MaxUses) + { + int need = Math.Min((MaxUses - weapon.UsesRemaining), ammo.UsesRemaining); + + if (need > 0) + { + if (weapon.Poison != null && (ammo.Poison == null || weapon.Poison.Level > ammo.Poison.Level)) + { + from.SendLocalizedMessage(1070767); // Loaded projectile is stronger, unload it first + } + else + { + if (weapon.UsesRemaining > 0) + { + if ((weapon.Poison == null && ammo.Poison != null) + || ((weapon.Poison != null && ammo.Poison != null) && weapon.Poison.Level != ammo.Poison.Level)) + { + Unload(from, weapon); + need = Math.Min(MaxUses, ammo.UsesRemaining); + } + } + int poisonneeded = Math.Min((MaxUses - weapon.PoisonCharges), ammo.PoisonCharges); + + weapon.UsesRemaining += need; + weapon.PoisonCharges += poisonneeded; + + if (weapon.PoisonCharges > 0) + { + weapon.Poison = ammo.Poison; + } + + ammo.PoisonCharges -= poisonneeded; + ammo.UsesRemaining -= need; + + if (ammo.UsesRemaining < 1) + { + ((Item)ammo).Delete(); + } + else if (ammo.PoisonCharges < 1) + { + ammo.Poison = null; + } + } + } // "else" here would mean they targeted "ammo" with 0 uses. undefined behavior. + } + else + { + from.SendLocalizedMessage(weapon.FullWeaponMessage); + } + } + + private static void ConsumeUse(INinjaWeapon weapon) + { + if (weapon.UsesRemaining > 0) + { + weapon.UsesRemaining--; + + if (weapon.UsesRemaining < 1) + { + weapon.PoisonCharges = 0; + weapon.Poison = null; + } + } + } + + private static bool CanUseWeapon(PlayerMobile from, INinjaWeapon weapon) + { + if (WeaponIsValid(weapon, from)) + { + if (weapon.UsesRemaining > 0) + { + if (!from.NinjaWepCooldown) + { + if (BasePotion.HasFreeHand(from)) + { + return true; + } + else + { + from.SendLocalizedMessage(weapon.NoFreeHandMessage); + } + } + else + { + from.SendLocalizedMessage(weapon.RecentlyUsedMessage); + } + } + else + { + from.SendLocalizedMessage(weapon.EmptyWeaponMessage); + } + } + return false; + } + + private static bool CombatCheck(Mobile attacker, Mobile defender) /* mod'd from baseweapon */ + { + BaseWeapon defWeapon = defender.Weapon as BaseWeapon; + + Skill atkSkill = defender.Skills.Ninjitsu; + Skill defSkill = defender.Skills[defWeapon.Skill]; + + double atSkillValue = attacker.Skills.Ninjitsu.Value; + double defSkillValue = defWeapon.GetDefendSkillValue(attacker, defender); + + double attackValue = AosAttributes.GetValue(attacker, AosAttribute.AttackChance); + + if (defSkillValue <= -20.0) + { + defSkillValue = -19.9; + } + + if (Spells.Chivalry.DivineFurySpell.UnderEffect(attacker)) + { + attackValue += 10; + } + + if (AnimalForm.UnderTransformation(attacker, typeof(GreyWolf)) || AnimalForm.UnderTransformation(attacker, typeof(BakeKitsune))) + { + attackValue += 20; + } + + if (HitLower.IsUnderAttackEffect(attacker)) + { + attackValue -= 25; + } + + if (attackValue > 45) + { + attackValue = 45; + } + + attackValue = (atSkillValue + 20.0) * (100 + attackValue); + + double defenseValue = AosAttributes.GetValue(defender, AosAttribute.DefendChance); + + if (Spells.Chivalry.DivineFurySpell.UnderEffect(defender)) + { + defenseValue -= 20; + } + + if (HitLower.IsUnderDefenseEffect(defender)) + { + defenseValue -= 25; + } + + int refBonus = 0; + + if (Block.GetBonus(defender, ref refBonus)) + { + defenseValue += refBonus; + } + + if (SkillHandlers.Discordance.GetEffect(attacker, ref refBonus)) + { + defenseValue -= refBonus; + } + + if (defenseValue > 45) + { + defenseValue = 45; + } + + defenseValue = (defSkillValue + 20.0) * (100 + defenseValue); + + double chance = attackValue / (defenseValue * 2.0); + + if (chance < 0.02) + { + chance = 0.02; + } + + return attacker.CheckSkill(atkSkill.SkillName, chance); + } + + private static void OnHit(object[] states) + { + Mobile from = states[0] as Mobile; + Mobile target = states[1] as Mobile; + INinjaWeapon weapon = states[2] as INinjaWeapon; + + if (from.CanBeHarmful(target)) + { + from.DoHarmful(target); + + AOS.Damage(target, from, weapon.WeaponDamage, 100, 0, 0, 0, 0); + + if (weapon.Poison != null && weapon.PoisonCharges > 0) + { + if (EvilOmenSpell.TryEndEffect(target)) + { + target.ApplyPoison(from, Poison.GetPoison(weapon.Poison.Level + 1)); + } + else + { + target.ApplyPoison(from, weapon.Poison); + } + + weapon.PoisonCharges--; + + if (weapon.PoisonCharges < 1) + { + weapon.Poison = null; + } + } + } + } + + private static void OnTarget(Mobile from, object targeted, INinjaWeapon weapon) + { + PlayerMobile player = from as PlayerMobile; + + if (WeaponIsValid(weapon, from)) + { + if (targeted is Mobile) + { + Shoot(player, (Mobile)targeted, weapon); + } + else if (targeted.GetType() == weapon.AmmoType) + { + Reload(player, weapon, (INinjaAmmo)targeted); + } + else + { + player.SendLocalizedMessage(weapon.WrongAmmoMessage); + } + } + } + + private static bool WeaponIsValid(INinjaWeapon weapon, Mobile from) + { + Item item = weapon as Item; + + if (!item.Deleted && item.RootParent == from) + { + return true; + } + return false; + } + + public class LoadEntry : ContextMenuEntry + { + private INinjaWeapon weapon; + + public LoadEntry(INinjaWeapon wep) + : base(6222, 0) + { + weapon = wep; + } + + public override void OnClick() + { + if (WeaponIsValid(weapon, Owner.From)) + { + Owner.From.BeginTarget(10, false, TargetFlags.Harmful, new TargetStateCallback(OnTarget), weapon); + } + } + } + + public class UnloadEntry : ContextMenuEntry + { + private INinjaWeapon weapon; + + public UnloadEntry(INinjaWeapon belt) + : base(6223, 0) + { + weapon = belt; + + Enabled = (weapon.UsesRemaining > 0); + } + + public override void OnClick() + { + if (WeaponIsValid(weapon, Owner.From)) + { + Unload(Owner.From, weapon); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Ninjitsu/Shuriken.cs b/Scripts/Items/Skill Items/Ninjitsu/Shuriken.cs index 420a55249..3045ccd2e 100644 --- a/Scripts/Items/Skill Items/Ninjitsu/Shuriken.cs +++ b/Scripts/Items/Skill Items/Ninjitsu/Shuriken.cs @@ -5,7 +5,7 @@ using Server.Engines.Craft; namespace Server.Items { [Flipable( 0x27AC, 0x27F7 )] - public class Shuriken : Item, IUsesRemaining, ICraftable + public class Shuriken : Item, ICraftable, INinjaAmmo { private int m_UsesRemaining; diff --git a/Scripts/Mobiles/BaseCreature.cs b/Scripts/Mobiles/BaseCreature.cs index b5a26e710..5ec50f79f 100644 --- a/Scripts/Mobiles/BaseCreature.cs +++ b/Scripts/Mobiles/BaseCreature.cs @@ -633,13 +633,16 @@ namespace Server.Mobiles return damage; } + #endregion #region Spill Acid + public void SpillAcid( int Amount ) { SpillAcid( null, Amount ); } + public void SpillAcid( Mobile target, int Amount ) { if ( (target != null && target.Map == null) || this.Map == null ) @@ -676,10 +679,12 @@ namespace Server.Mobiles Solen Style, override me for other mobiles/items: kappa+acidslime, grizzles+whatever, etc. */ + public virtual Item NewHarmfulItem() { return new PoolOfAcid( TimeSpan.FromSeconds(10), 30, 30 ); } + #endregion #region Flee!!! @@ -714,6 +719,7 @@ namespace Server.Mobiles { m_EndFlee = DateTime.Now + maxDuration; } + #endregion public BaseAI AIObject{ get{ return m_AI; } } @@ -766,6 +772,7 @@ namespace Server.Mobiles return ( m_iTeam == c.m_iTeam && ( (m_bSummoned || m_bControlled) == (c.m_bSummoned || c.m_bControlled) )/* && c.Combatant != this */); } + #endregion #region Allegiance @@ -803,6 +810,7 @@ namespace Server.Mobiles return ( ethic == EthicAllegiance ? Allegiance.Ally : Allegiance.Enemy ); } + #endregion public virtual bool IsEnemy( Mobile m ) @@ -983,7 +991,7 @@ namespace Server.Mobiles if ( Core.AOS && !this.Summoned && this.Controlled && 0.2 > Utility.RandomDouble() ) amount = (int)(amount * BonusPetDamageScalar); - if ( Spells.Necromancy.EvilOmenSpell.CheckEffect( this ) ) + if ( Spells.Necromancy.EvilOmenSpell.TryEndEffect( this ) ) amount = (int)(amount * 1.25); Mobile oath = Spells.Necromancy.BloodOathSpell.GetBloodOath( from ); @@ -1032,7 +1040,7 @@ namespace Server.Mobiles if ( !Alive || IsDeadPet ) return ApplyPoisonResult.Immune; - if ( Spells.Necromancy.EvilOmenSpell.CheckEffect( this ) ) + if ( Spells.Necromancy.EvilOmenSpell.TryEndEffect( this ) ) poison = PoisonImpl.IncreaseLevel( poison ); ApplyPoisonResult result = base.ApplyPoison( from, poison ); @@ -1318,6 +1326,7 @@ namespace Server.Mobiles public virtual void AlterMeleeDamageTo( Mobile to, ref int damage ) { } + #endregion public virtual void CheckReflect( Mobile caster, ref bool reflect ) @@ -2111,6 +2120,7 @@ namespace Server.Mobiles public virtual bool CanAngerOnTame{ get{ return false; } } #region OnAction[...] + public virtual void OnActionWander() { } @@ -2134,6 +2144,7 @@ namespace Server.Mobiles public virtual void OnActionBackoff() { } + #endregion public override bool OnDragDrop( Mobile from, Item dropped ) @@ -2765,6 +2776,7 @@ namespace Server.Mobiles * -Could add a FightMode.Prefered * */ + public virtual double GetFightModeRanking( Mobile m, FightMode acqType, bool bPlayerOnly ) { if ( ( bPlayerOnly && m.Player ) || !bPlayerOnly ) @@ -3060,6 +3072,7 @@ namespace Server.Mobiles return false; } + #endregion public override void AggressiveAction( Mobile aggressor, bool criminal ) @@ -3635,6 +3648,7 @@ namespace Server.Mobiles } #region Pack & Loot + public void PackPotion() { PackItem( Loot.RandomPotion() ); @@ -4079,6 +4093,7 @@ namespace Server.Mobiles if ( !item.Stackable || !pack.TryDropItem( this, item, false ) ) // try stack pack.DropItem( item ); // failed, drop it anyway } + #endregion public override void OnDoubleClick( Mobile from ) @@ -4922,6 +4937,7 @@ namespace Server.Mobiles m_NextHealTime = DateTime.Now + TimeSpan.FromSeconds(MinHealDelay + (Utility.RandomDouble() * MaxHealDelay)); } + #endregion public virtual void OnThink() diff --git a/Scripts/Mobiles/PlayerMobile.cs b/Scripts/Mobiles/PlayerMobile.cs index e6040aed5..efd8f6a0a 100644 --- a/Scripts/Mobiles/PlayerMobile.cs +++ b/Scripts/Mobiles/PlayerMobile.cs @@ -2542,7 +2542,7 @@ namespace Server.Mobiles public override void Damage( int amount, Mobile from ) { - if ( Spells.Necromancy.EvilOmenSpell.CheckEffect( this ) ) + if ( Spells.Necromancy.EvilOmenSpell.TryEndEffect( this ) ) amount = (int)(amount * 1.25); Mobile oath = Spells.Necromancy.BloodOathSpell.GetBloodOath( from ); @@ -2580,7 +2580,7 @@ namespace Server.Mobiles if ( !Alive ) return ApplyPoisonResult.Immune; - if ( Spells.Necromancy.EvilOmenSpell.CheckEffect( this ) ) + if ( Spells.Necromancy.EvilOmenSpell.TryEndEffect( this ) ) poison = PoisonImpl.IncreaseLevel( poison ); ApplyPoisonResult result = base.ApplyPoison( from, poison ); diff --git a/Scripts/Spells/Chivalry/NobleSacrifice.cs b/Scripts/Spells/Chivalry/NobleSacrifice.cs index 6bd8c35d1..169b585e5 100644 --- a/Scripts/Spells/Chivalry/NobleSacrifice.cs +++ b/Scripts/Spells/Chivalry/NobleSacrifice.cs @@ -140,7 +140,7 @@ namespace Server.Spells.Chivalry sendEffect = true; } - if ( EvilOmenSpell.CheckEffect( m ) ) + if ( EvilOmenSpell.TryEndEffect( m ) ) sendEffect = true; if ( StrangleSpell.RemoveCurse( m ) ) diff --git a/Scripts/Spells/Chivalry/RemoveCurse.cs b/Scripts/Spells/Chivalry/RemoveCurse.cs index 7024d3bb5..c07ea15ed 100644 --- a/Scripts/Spells/Chivalry/RemoveCurse.cs +++ b/Scripts/Spells/Chivalry/RemoveCurse.cs @@ -81,7 +81,7 @@ namespace Server.Spells.Chivalry m.Paralyzed = false; - EvilOmenSpell.CheckEffect( m ); + EvilOmenSpell.TryEndEffect( m ); StrangleSpell.RemoveCurse( m ); CorpseSkinSpell.RemoveCurse( m ); CurseSpell.RemoveEffect( m ); diff --git a/Scripts/Spells/Necromancy/EvilOmen.cs b/Scripts/Spells/Necromancy/EvilOmen.cs index cd1acfe91..c57f46c0b 100644 --- a/Scripts/Spells/Necromancy/EvilOmen.cs +++ b/Scripts/Spells/Necromancy/EvilOmen.cs @@ -1,8 +1,6 @@ using System; using System.Collections; using Server.Mobiles; -using Server.Network; -using Server.Items; using Server.Targeting; namespace Server.Spells.Necromancy @@ -17,58 +15,59 @@ namespace Server.Spells.Necromancy Reagent.NoxCrystal ); - public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds( 0.75 ); } } + public override TimeSpan CastDelayBase { get { return TimeSpan.FromSeconds(0.75); } } - public override double RequiredSkill{ get{ return 20.0; } } - public override int RequiredMana{ get{ return 11; } } + public override double RequiredSkill { get { return 20.0; } } + public override int RequiredMana { get { return 11; } } - public EvilOmenSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + public EvilOmenSpell(Mobile caster, Item scroll) + : base(caster, scroll, m_Info) { } public override void OnCast() { - Caster.Target = new InternalTarget( this ); + Caster.Target = new InternalTarget(this); } - public void Target( Mobile m ) + public void Target(Mobile m) { - if ( !(m is BaseCreature || m is PlayerMobile) ) + if (!(m is BaseCreature || m is PlayerMobile)) { - Caster.SendLocalizedMessage( 1060508 ); // You can't curse that. + Caster.SendLocalizedMessage(1060508); // You can't curse that. } - else if ( CheckHSequence( m ) ) + else if (CheckHSequence(m)) { - SpellHelper.Turn( Caster, m ); + SpellHelper.Turn(Caster, m); /* Curses the target so that the next harmful event that affects them is magnified. * Damage to the target's hit points is increased 25%, * the poison level of the attack will be 1 higher * and the Resist Magic skill of the target will be fixed on 50. - * + * * The effect lasts for one harmful event only. */ - if ( m.Spell != null ) + if (m.Spell != null) m.Spell.OnCasterHurt(); - - m.PlaySound( 0xFC ); - m.FixedParticles( 0x3728, 1, 13, 9912, 1150, 7, EffectLayer.Head ); - m.FixedParticles( 0x3779, 1, 15, 9502, 67, 7, EffectLayer.Head ); - if ( !m_Table.Contains( m ) ) + m.PlaySound(0xFC); + m.FixedParticles(0x3728, 1, 13, 9912, 1150, 7, EffectLayer.Head); + m.FixedParticles(0x3779, 1, 15, 9502, 67, 7, EffectLayer.Head); + + if (!m_Table.Contains(m)) { - SkillMod mod = new DefaultSkillMod( SkillName.MagicResist, false, 50.0 ); + SkillMod mod = new DefaultSkillMod(SkillName.MagicResist, false, 50.0); - if ( m.Skills[SkillName.MagicResist].Base > 50.0 ) - m.AddSkillMod( mod ); + if (m.Skills[SkillName.MagicResist].Base > 50.0) + m.AddSkillMod(mod); m_Table[m] = mod; } - TimeSpan duration = TimeSpan.FromSeconds( (Caster.Skills[SkillName.SpiritSpeak].Value / 12) + 1.0 ); + TimeSpan duration = TimeSpan.FromSeconds((Caster.Skills[SkillName.SpiritSpeak].Value / 12) + 1.0); - Timer.DelayCall( duration, new TimerStateCallback( EffectExpire_Callback ), m ); + Timer.DelayCall(duration, new TimerStateCallback(EffectExpire_Callback), m); } FinishSequence(); @@ -76,19 +75,27 @@ namespace Server.Spells.Necromancy private static Hashtable m_Table = new Hashtable(); - private static void EffectExpire_Callback( object state ) + private static void EffectExpire_Callback(object state) { - CheckEffect( (Mobile)state ); + TryEndEffect((Mobile)state); } - public static bool CheckEffect( Mobile m ) + /* + * The naming here was confusing. Its a 1-off effect spell. + * So, we dont actually "checkeffect"; we endeffect with bool + * return to determine external behaviors. + * + * -refactored. + */ + + public static bool TryEndEffect(Mobile m) { SkillMod mod = (SkillMod)m_Table[m]; - if ( mod == null ) + if (mod == null) return false; - m_Table.Remove( m ); + m_Table.Remove(m); mod.Remove(); return true; @@ -98,20 +105,21 @@ namespace Server.Spells.Necromancy { private EvilOmenSpell m_Owner; - public InternalTarget( EvilOmenSpell owner ) : base( Core.ML ? 10 : 12, false, TargetFlags.Harmful ) + public InternalTarget(EvilOmenSpell owner) + : base(Core.ML ? 10 : 12, false, TargetFlags.Harmful) { m_Owner = owner; } - protected override void OnTarget( Mobile from, object o ) + protected override void OnTarget(Mobile from, object o) { - if ( o is Mobile ) - m_Owner.Target( (Mobile) o ); + if (o is Mobile) + m_Owner.Target((Mobile)o); else - from.SendLocalizedMessage( 1060508 ); // You can't curse that. + from.SendLocalizedMessage(1060508); // You can't curse that. } - protected override void OnTargetFinish( Mobile from ) + protected override void OnTargetFinish(Mobile from) { m_Owner.FinishSequence(); }