From cd7c2becf7a531f5edfd4f22fe826732234bbf6d Mon Sep 17 00:00:00 2001 From: daedalus Date: Mon, 14 Dec 2009 19:01:59 +0000 Subject: [PATCH] - Hit Life Leech now works in PvP (http://www.uodemise.com/discussion/showthread.php?t=121298) - Summoned Daemon now has the proper bodyvalue (AoS+) and effect (http://www.uodemise.com/discussion/showthread.php?t=127404) - Two new house signs are added (http://www.uodemise.com/discussion/showthread.php?t=125409) - Changes to Satyrs and Dryads: --> Dryads: ----> they do area peace ----> can partially undress males ----> have chance to drop peculiar or fragrant (not yet implemented) seeds --> Satyrs: ----> their karma is neutral ----> can peace combatants ----> can provoke nearby creatures to the combatant ----> can undress females ----> can discord combatants --> Both: can drop spellweaving scrolls (http://www.uodemise.com/discussion/showthread.php?t=125444) - Added Auto Arrow Recovery feature (SE+) (http://www.uodemise.com/discussion/showthread.php?t=114807) --- Scripts/Gumps/HouseGumpAOS.cs | 31 ++- Scripts/Items/Weapons/BaseWeapon.cs | 7 + Scripts/Items/Weapons/Ranged/BaseRanged.cs | 27 ++ Scripts/Misc/Loot.cs | 10 + Scripts/Misc/LootPack.cs | 11 + Scripts/Mobiles/AI/Creature/BaseCreature.cs | 11 + .../Mobiles/AI/Creature/OppositionGroup.cs | 3 +- .../Monsters/ML/Humanoid/Magic/MLDryad.cs | 156 +++++++++--- .../Monsters/ML/Humanoid/Magic/Satyr.cs | 240 ++++++++++++++---- .../Monsters/Summons/SummonedDaemon.cs | 2 +- Scripts/Mobiles/PlayerMobile.cs | 86 ++++++- Scripts/Spells/Base/Spell.cs | 9 + Scripts/Spells/Base/SpellHelper.cs | 5 + Scripts/Spells/Eighth/SummonDaemon.cs | 11 +- 14 files changed, 511 insertions(+), 98 deletions(-) diff --git a/Scripts/Gumps/HouseGumpAOS.cs b/Scripts/Gumps/HouseGumpAOS.cs index e66fd4700..efaf8383b 100644 --- a/Scripts/Gumps/HouseGumpAOS.cs +++ b/Scripts/Gumps/HouseGumpAOS.cs @@ -190,6 +190,8 @@ namespace Server.Gumps 660, 666, 672, 898, 970, 974, 982 }; + + private static List _HouseSigns = new List(); public HouseGumpAOS( HouseGumpPageAOS page, Mobile from, BaseHouse house ) : base( 50, 40 ) { @@ -486,20 +488,37 @@ namespace Server.Gumps case HouseGumpPageAOS.ChangeSign: { int index = 0; + + if ( _HouseSigns.Count == 0 ) + { + // Add standard signs + for ( int i = 0; i < 54; ++i ) + { + _HouseSigns.Add( 2980 + ( i * 2 ) ); + } + + // Add library and beekeeper signs ( ML ) + _HouseSigns.Add( 2966 ); + _HouseSigns.Add( 3140 ); + } + + int signsPerPage = Core.ML ? 24 : 18; + int totalSigns = Core.ML ? 56 : 54; + int pages = (int) Math.Ceiling( (double) totalSigns / signsPerPage ); - for ( int i = 0; i < 3; ++i ) + for ( int i = 0; i < pages; ++i ) { AddPage( i + 1 ); - AddButton( 10, 360, 4005, 4007, 0, GumpButtonType.Page, ((i + 1) % 3) + 1 ); + AddButton( 10, 360, 4005, 4007, 0, GumpButtonType.Page, ((i + 1) % pages ) + 1 ); - for ( int j = 0; j < 18; ++j ) + for ( int j = 0; j < signsPerPage && totalSigns - ( signsPerPage * i ) - j > 0; ++j ) { int x = 30 + ((j % 6) * 60); int y = 130 + ((j / 6) * 60); AddButton( x, y, 4005, 4007, GetButtonID( 9, index ), GumpButtonType.Reply, 0 ); - AddItem( x + 20, y, 2980 + (index++ * 2) ); + AddItem( x + 20, y, _HouseSigns[index++] ); } } @@ -1305,9 +1324,9 @@ namespace Server.Gumps } case 9: { - if ( isOwner && m_House.Public && index >= 0 && index < 54 ) + if ( isOwner && m_House.Public && index >= 0 && index < _HouseSigns.Count ) { - m_House.ChangeSignType( 2980 + (index * 2) ); + m_House.ChangeSignType( _HouseSigns[index] ); from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Customize, from, m_House ) ); } diff --git a/Scripts/Items/Weapons/BaseWeapon.cs b/Scripts/Items/Weapons/BaseWeapon.cs index 0d9e25712..4fda0b7da 100644 --- a/Scripts/Items/Weapons/BaseWeapon.cs +++ b/Scripts/Items/Weapons/BaseWeapon.cs @@ -1011,6 +1011,13 @@ namespace Server.Items canSwing = ( sp == null || !sp.IsCasting || !sp.BlocksMovement ); } + + if ( canSwing ) + { + PlayerMobile p = attacker as PlayerMobile; + + canSwing = ( p == null || p.PeacedUntil <= DateTime.Now ); + } } if ( canSwing && attacker.HarmfulCheck( defender ) ) diff --git a/Scripts/Items/Weapons/Ranged/BaseRanged.cs b/Scripts/Items/Weapons/Ranged/BaseRanged.cs index 3be8e01db..55d9524ca 100644 --- a/Scripts/Items/Weapons/Ranged/BaseRanged.cs +++ b/Scripts/Items/Weapons/Ranged/BaseRanged.cs @@ -21,6 +21,7 @@ namespace Server.Items public override SkillName AccuracySkill{ get{ return SkillName.Archery; } } + private Timer m_RecoveryTimer; // so we don't start too many timers private bool m_Balanced; private int m_Velocity; @@ -120,7 +121,33 @@ namespace Server.Items public override void OnMiss( Mobile attacker, Mobile defender ) { if ( attacker.Player && 0.4 >= Utility.RandomDouble() ) + { + if ( Core.SE ) + { + PlayerMobile p = attacker as PlayerMobile; + + if ( p != null ) + { + Type ammo = AmmoType; + + if ( p.RecoverableAmmo.ContainsKey( ammo ) ) + p.RecoverableAmmo[ ammo ]++; + else + p.RecoverableAmmo.Add( ammo, 1 ); + + if ( !p.Warmode ) + { + if ( m_RecoveryTimer == null ) + m_RecoveryTimer = Timer.DelayCall( TimeSpan.FromSeconds( 10 ), new TimerCallback( p.RecoverAmmo ) ); + + if ( !m_RecoveryTimer.Running ) + m_RecoveryTimer.Start(); + } + } + } + else Ammo.MoveToWorld( new Point3D( defender.X + Utility.RandomMinMax( -1, 1 ), defender.Y + Utility.RandomMinMax( -1, 1 ), defender.Z ), defender.Map ); + } base.OnMiss( attacker, defender ); } diff --git a/Scripts/Misc/Loot.cs b/Scripts/Misc/Loot.cs index f58ab356f..945aa3c52 100644 --- a/Scripts/Misc/Loot.cs +++ b/Scripts/Misc/Loot.cs @@ -224,10 +224,19 @@ namespace Server private static Type[] m_PaladinScrollTypes = new Type[0]; + private static Type[] m_ArcaneScrollTypes = new Type[] + { + typeof( ArcaneCircleScroll ), typeof ( GiftOfRenewalScroll ), typeof( ImmolatingWeaponScroll ), typeof( AttuneWeaponScroll ), + typeof( ThunderstormScroll ), typeof( NatureFuryScroll ), typeof( ReaperFormScroll ), typeof( WildfireScroll ), + typeof( EssenceOfWindScroll ), typeof( DryadAllureScroll ), typeof( EtherealVoyageScroll ), typeof( WordOfDeathScroll ), + typeof( GiftOfLifeScroll ), typeof( ArcaneEmpowermentScroll ) + }; + public static Type[] RegularScrollTypes{ get{ return m_RegularScrollTypes; } } public static Type[] NecromancyScrollTypes{ get{ return m_NecromancyScrollTypes; } } public static Type[] SENecromancyScrollTypes{ get{ return m_SENecromancyScrollTypes; } } public static Type[] PaladinScrollTypes{ get{ return m_PaladinScrollTypes; } } + public static Type[] ArcaneScrollTypes{ get{ return m_ArcaneScrollTypes; } } private static Type[] m_GrimmochJournalTypes = new Type[] { @@ -589,6 +598,7 @@ namespace Server case SpellbookType.Regular: types = m_RegularScrollTypes; break; case SpellbookType.Necromancer: types = (Core.SE ? m_SENecromancyScrollTypes : m_NecromancyScrollTypes ); break; case SpellbookType.Paladin: types = m_PaladinScrollTypes; break; + case SpellbookType.Arcanist: types = m_ArcaneScrollTypes; break; } return Construct( types, Utility.RandomMinMax( minIndex, maxIndex ) ) as SpellScroll; diff --git a/Scripts/Misc/LootPack.cs b/Scripts/Misc/LootPack.cs index 33c04227a..169b66c9c 100644 --- a/Scripts/Misc/LootPack.cs +++ b/Scripts/Misc/LootPack.cs @@ -239,6 +239,17 @@ namespace Server }; #endregion + #region ML definitions + public static readonly LootPack MlRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "4d50+450" ), + new LootPackEntry( false, AosMagicItemsRichType1, 100.00, 1, 3, 0, 75 ), + new LootPackEntry( false, AosMagicItemsRichType1, 80.00, 1, 3, 0, 75 ), + new LootPackEntry( false, AosMagicItemsRichType1, 60.00, 1, 5, 0, 100 ), + new LootPackEntry( false, Instruments, 1.00, 1 ) + } ); + #endregion + #region SE definitions public static readonly LootPack SePoor = new LootPack( new LootPackEntry[] { diff --git a/Scripts/Mobiles/AI/Creature/BaseCreature.cs b/Scripts/Mobiles/AI/Creature/BaseCreature.cs index 6f9cb7ecb..11abb6d57 100644 --- a/Scripts/Mobiles/AI/Creature/BaseCreature.cs +++ b/Scripts/Mobiles/AI/Creature/BaseCreature.cs @@ -1222,6 +1222,9 @@ namespace Server.Mobiles if ( m_ReceivedHonorContext != null ) m_ReceivedHonorContext.OnTargetDamaged( from, amount ); + if ( willKill && from is PlayerMobile ) + Timer.DelayCall( TimeSpan.FromSeconds( 10 ), new TimerCallback( ((PlayerMobile) from).RecoverAmmo ) ); + base.OnDamage( amount, from, willKill ); } @@ -3507,6 +3510,14 @@ namespace Server.Mobiles PackItem( Loot.RandomPotion() ); } + public void PackArcanceScroll( double chance ) + { + if ( !Core.ML || chance <= Utility.RandomDouble() ) + return; + + PackItem( Loot.Construct( Loot.ArcaneScrollTypes ) ); + } + public void PackNecroScroll( int index ) { if ( !Core.AOS || 0.05 <= Utility.RandomDouble() ) diff --git a/Scripts/Mobiles/AI/Creature/OppositionGroup.cs b/Scripts/Mobiles/AI/Creature/OppositionGroup.cs index 4729350d9..dd095eb60 100644 --- a/Scripts/Mobiles/AI/Creature/OppositionGroup.cs +++ b/Scripts/Mobiles/AI/Creature/OppositionGroup.cs @@ -106,7 +106,8 @@ namespace Server typeof( Silvani ), typeof( Unicorn ), typeof( Wisp ), - typeof( Treefellow ) + typeof( Treefellow ), + typeof( MLDryad ) }, new Type[] { diff --git a/Scripts/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs b/Scripts/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs index bf4a156d1..e68284621 100644 --- a/Scripts/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs +++ b/Scripts/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs @@ -1,86 +1,160 @@ using System; -using System.Collections; -using Server.Items; -using Server.Targeting; -using Server.Misc; +using Server.Engines.Plants; namespace Server.Mobiles { - [CorpseName("a dryad corpse")] + [CorpseName( "a dryad's corpse" )] public class MLDryad : BaseCreature { public override bool InitialInnocent { get { return true; } } - - /* VULNERABLE TO FEY SLAYER! - * These creatures should be able to cast Area Peace as - * well as cause Players' armor to drop into their backpack*/ - public override WeaponAbility GetWeaponAbility() + public override OppositionGroup OppositionGroup { - return WeaponAbility.Disrobe; + get { return OppositionGroup.FeyAndUndead; } } [Constructable] - public MLDryad() - : base(AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.2, 0.4) // NEED TO CHECK + public MLDryad() : base( AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4 ) { Name = "a dryad"; Body = 266; BaseSoundID = 0x57B; - SetStr(132, 149); - SetDex(152, 168); - SetInt(251, 280); + SetStr( 132, 149 ); + SetDex( 152, 168 ); + SetInt( 251, 280 ); - SetHits(304, 321); + SetHits( 304, 321 ); - SetDamage(11, 20); + SetDamage( 11, 20 ); - SetDamageType(ResistanceType.Physical, 100); + SetDamageType( ResistanceType.Physical, 100 ); - SetResistance(ResistanceType.Physical, 40, 50); - SetResistance(ResistanceType.Fire, 15, 25); - SetResistance(ResistanceType.Cold, 40, 45); - SetResistance(ResistanceType.Poison, 30, 40); - SetResistance(ResistanceType.Energy, 25, 35); + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 40, 45 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 25, 35 ); - SetSkill(SkillName.Meditation, 80.9, 89.9); - SetSkill(SkillName.EvalInt, 70.3, 78.7); - SetSkill(SkillName.Magery, 70.7, 75.7); - SetSkill(SkillName.Anatomy, 0); - SetSkill(SkillName.MagicResist, 101.7, 117.1); - SetSkill(SkillName.Tactics, 71.7, 79.8); - SetSkill(SkillName.Wrestling, 72.5, 79.5); + SetSkill( SkillName.Meditation, 80.0, 90.0 ); + SetSkill( SkillName.EvalInt, 70.0, 80.0 ); + SetSkill( SkillName.Magery, 70.0, 80.0 ); + SetSkill( SkillName.Anatomy, 0 ); + SetSkill( SkillName.MagicResist, 100.0, 120.0 ); + SetSkill( SkillName.Tactics, 70.0, 80.0 ); + SetSkill( SkillName.Wrestling, 70.0, 80.0 ); Fame = 5000; Karma = 5000; VirtualArmor = 28; // Don't know what it should be - if ( Core.ML && Utility.RandomDouble() < .33 ) - PackItem( Engines.Plants.Seed.RandomPeculiarSeed(1) ); + if ( Core.ML && Utility.RandomDouble() < .60 ) + PackItem( Seed.RandomPeculiarSeed( 1 ) ); + + PackArcanceScroll( 0.05 ); } public override void GenerateLoot() { - AddLoot(LootPack.Rich); // Need to verify + AddLoot( LootPack.MlRich ); } public override int Meat { get { return 1; } } - - public MLDryad(Serial serial) : base(serial) + + public override void OnThink() + { + base.OnThink(); + + AreaPeace(); + AreaUndress(); + } + + #region Area Peace + private DateTime m_NextPeace; + + public void AreaPeace() + { + if ( Combatant == null || Deleted || !Alive || m_NextPeace > DateTime.Now || 0.1 < Utility.RandomDouble() ) + return; + + TimeSpan duration = TimeSpan.FromSeconds( Utility.RandomMinMax( 20, 80 ) ); + + foreach ( Mobile m in GetMobilesInRange( RangePerception ) ) + { + PlayerMobile p = m as PlayerMobile; + + if ( IsValidTarget( p ) ) + { + p.PeacedUntil = DateTime.Now + duration; + p.SendLocalizedMessage( 1072065 ); // You gaze upon the dryad's beauty, and forget to continue battling! + p.FixedParticles( 0x376A, 1, 20, 0x7F5, EffectLayer.Waist ); + p.Combatant = null; + } + } + + m_NextPeace = DateTime.Now + TimeSpan.FromSeconds( 10 ); + PlaySound( 0x1D3 ); + } + + public bool IsValidTarget( PlayerMobile m ) + { + if ( m != null && m.PeacedUntil < DateTime.Now && !m.Hidden && m.AccessLevel == AccessLevel.Player && CanBeHarmful( m ) ) + return true; + + return false; + } + #endregion + + #region Undress + private DateTime m_NextUndress; + + public void AreaUndress() + { + if ( Combatant == null || Deleted || !Alive || m_NextUndress > DateTime.Now || 0.005 < Utility.RandomDouble() ) + return; + + foreach ( Mobile m in GetMobilesInRange( RangePerception ) ) + { + if ( m != null && m.Player && !m.Female && !m.Hidden && m.AccessLevel == AccessLevel.Player && CanBeHarmful( m ) ) + { + UndressItem( m, Layer.OuterTorso ); + UndressItem( m, Layer.InnerTorso ); + UndressItem( m, Layer.MiddleTorso ); + UndressItem( m, Layer.Pants ); + UndressItem( m, Layer.Shirt ); + + m.SendLocalizedMessage( 1072197 ); // The dryad's beauty makes your blood race. Your clothing is too confining. + } + } + + m_NextUndress = DateTime.Now + TimeSpan.FromMinutes( 1 ); + } + + public void UndressItem( Mobile m, Layer layer ) + { + Item item = m.FindItemOnLayer( layer ); + + if ( item != null && item.Movable ) + m.PlaceInBackpack( item ); + } + #endregion + + public MLDryad( Serial serial ) : base( serial ) { } - public override void Serialize(GenericWriter writer) + public override void Serialize( GenericWriter writer ) { - base.Serialize(writer); - writer.Write((int)0); + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version } - public override void Deserialize(GenericReader reader) + public override void Deserialize( GenericReader reader ) { - base.Deserialize(reader); + base.Deserialize( reader ); + int version = reader.ReadInt(); } } diff --git a/Scripts/Mobiles/Monsters/ML/Humanoid/Magic/Satyr.cs b/Scripts/Mobiles/Monsters/ML/Humanoid/Magic/Satyr.cs index ca7140ad3..33a73d2bd 100644 --- a/Scripts/Mobiles/Monsters/ML/Humanoid/Magic/Satyr.cs +++ b/Scripts/Mobiles/Monsters/ML/Humanoid/Magic/Satyr.cs @@ -1,83 +1,233 @@ using System; -using System.Collections; -using Server.Items; -using Server.Targeting; -using Server.Misc; +using System.Collections.Generic; namespace Server.Mobiles { - [CorpseName("a satyr corpse")] + [CorpseName( "a satyr's corpse" )] public class Satyr : BaseCreature { - /* These creatures should be able to cast Area Peace as - * well as cause Players' armor to drop into their backpack - */ - - public override WeaponAbility GetWeaponAbility() - { - return WeaponAbility.Disrobe; - } - [Constructable] - public Satyr() - : base(AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4) // NEED TO CHECK + public Satyr() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) { Name = "a satyr"; Body = 271; - BaseSoundID = 0x586; + BaseSoundID = 0x586; - SetStr(177, 195); - SetDex(251, 269); - SetInt(153, 170); + SetStr( 177, 195 ); + SetDex( 251, 269 ); + SetInt( 153, 170 ); - SetHits(353, 399); + SetHits( 350, 400 ); - SetDamage(13, 24); + SetDamage( 13, 24 ); - SetDamageType(ResistanceType.Physical, 100); + SetDamageType( ResistanceType.Physical, 100 ); - SetResistance(ResistanceType.Physical, 55, 60); - SetResistance(ResistanceType.Fire, 26, 35); - SetResistance(ResistanceType.Cold, 30, 40); - SetResistance(ResistanceType.Poison, 30, 40); - SetResistance(ResistanceType.Energy, 30, 40); + SetResistance( ResistanceType.Physical, 55, 60 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); - SetSkill(SkillName.Poisoning, 0); - SetSkill(SkillName.Meditation, 0); - SetSkill(SkillName.EvalInt, 0); - SetSkill(SkillName.Magery, 0); - SetSkill(SkillName.Anatomy, 0); - SetSkill(SkillName.MagicResist, 55.3, 64.3); - SetSkill(SkillName.Tactics, 80.1, 99.3); - SetSkill(SkillName.Wrestling, 80.6, 100.0); + SetSkill( SkillName.MagicResist, 55.0, 65.0 ); + SetSkill( SkillName.Tactics, 80.0, 100.0 ); + SetSkill( SkillName.Wrestling, 80.0, 100.0 ); Fame = 5000; - Karma = -5000; + Karma = 0; VirtualArmor = 28; // Don't know what it should be - } + PackArcanceScroll( 0.05 ); + } public override void GenerateLoot() { - AddLoot(LootPack.Rich); // Need to verify + AddLoot( LootPack.MlRich ); } + public override void OnThink() + { + base.OnThink(); + + Peace( Combatant ); + Undress( Combatant ); + Suppress( Combatant ); + Provoke( Combatant ); + } + + #region Peace + private DateTime m_NextPeace; + + public void Peace( Mobile target ) + { + if ( target == null || Deleted || !Alive || m_NextPeace > DateTime.Now || 0.1 < Utility.RandomDouble() ) + return; + + PlayerMobile p = target as PlayerMobile; + + if ( p != null && p.PeacedUntil < DateTime.Now && !p.Hidden && CanBeHarmful( p ) ) + { + p.PeacedUntil = DateTime.Now + TimeSpan.FromMinutes( 1 ); + p.SendLocalizedMessage( 500616 ); // You hear lovely music, and forget to continue battling! + p.FixedParticles( 0x376A, 1, 32, 0x15BD, EffectLayer.Waist ); + p.Combatant = null; + + PlaySound( 0x58D ); + } + + m_NextPeace = DateTime.Now + TimeSpan.FromSeconds( 10 ); + } + #endregion + + #region Suppress + private static Dictionary m_Suppressed = new Dictionary(); + private DateTime m_NextSuppress; + + public void Suppress( Mobile target ) + { + if ( target == null || m_Suppressed.ContainsKey( target ) || Deleted || !Alive || m_NextSuppress > DateTime.Now || 0.1 < Utility.RandomDouble() ) + return; + + TimeSpan delay = TimeSpan.FromSeconds( Utility.RandomMinMax( 20, 80 ) ); + + if ( !target.Hidden && CanBeHarmful( target ) ) + { + target.SendLocalizedMessage( 1072061 ); // You hear jarring music, suppressing your strength. + + for ( int i = 0; i < target.Skills.Length; i++ ) + { + Skill s = target.Skills[ i ]; + + target.AddSkillMod( new TimedSkillMod( s.SkillName, true, s.Base * -0.28, delay ) ); + } + + int count = (int) Math.Round( delay.TotalSeconds / 1.25 ); + Timer timer = new AnimateTimer( target, count ); + m_Suppressed.Add( target, timer ); + timer.Start(); + + PlaySound( 0x58C ); + } + + m_NextSuppress = DateTime.Now + TimeSpan.FromSeconds( 10 ); + } + + public static void SuppressRemove( Mobile target ) + { + if ( target != null && m_Suppressed.ContainsKey( target ) ) + { + Timer timer = m_Suppressed[ target ]; + + if ( timer != null || timer.Running ) + timer.Stop(); + + m_Suppressed.Remove( target ); + } + } + + private class AnimateTimer : Timer + { + private Mobile m_Owner; + private int m_Count; + + public AnimateTimer( Mobile owner, int count ) : base( TimeSpan.Zero, TimeSpan.FromSeconds( 1.25 ) ) + { + m_Owner = owner; + m_Count = count; + } + + protected override void OnTick() + { + if ( m_Owner.Deleted || !m_Owner.Alive || m_Count-- < 0 ) + { + SuppressRemove( m_Owner ); + } + else + m_Owner.FixedParticles( 0x376A, 1, 32, 0x15BD, EffectLayer.Waist ); + } + } + #endregion + + #region Undress + private DateTime m_NextUndress; + + public void Undress( Mobile target ) + { + if ( target == null || Deleted || !Alive || m_NextUndress > DateTime.Now || 0.005 < Utility.RandomDouble() ) + return; + + if ( target.Player && target.Female && !target.Hidden && CanBeHarmful( target ) ) + { + UndressItem( target, Layer.OuterTorso ); + UndressItem( target, Layer.InnerTorso ); + UndressItem( target, Layer.MiddleTorso ); + UndressItem( target, Layer.Pants ); + UndressItem( target, Layer.Shirt ); + + target.SendLocalizedMessage( 1072196 ); // The satyr's music makes your blood race. Your clothing is too confining. + } + + m_NextUndress = DateTime.Now + TimeSpan.FromMinutes( 1 ); + } + + public void UndressItem( Mobile m, Layer layer ) + { + Item item = m.FindItemOnLayer( layer ); + + if ( item != null && item.Movable ) + m.PlaceInBackpack( item ); + } + #endregion + + #region Provoke + private DateTime m_NextProvoke; + + public void Provoke( Mobile target ) + { + if ( target == null || Deleted || !Alive || m_NextProvoke > DateTime.Now || 0.05 < Utility.RandomDouble() ) + return; + + foreach ( Mobile m in GetMobilesInRange( RangePerception ) ) + { + if ( m is BaseCreature ) + { + BaseCreature c = (BaseCreature) m; + + if ( c == this || c == target || c.Unprovokable || c.IsParagon || c.BardProvoked || c.AccessLevel != AccessLevel.Player || !c.CanBeHarmful( target ) ) + continue; + + c.Provoke( this, target, true ); + + if ( target.Player ) + target.SendLocalizedMessage( 1072062 ); // You hear angry music, and start to fight. + + PlaySound( 0x58A ); + break; + } + } + + m_NextProvoke = DateTime.Now + TimeSpan.FromSeconds( 10 ); + } + #endregion + public override int Meat { get { return 1; } } - public Satyr(Serial serial) : base(serial) + public Satyr( Serial serial ) : base( serial ) { } - public override void Serialize(GenericWriter writer) + public override void Serialize( GenericWriter writer ) { - base.Serialize(writer); - writer.Write((int)0); + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version } - public override void Deserialize(GenericReader reader) + public override void Deserialize( GenericReader reader ) { - base.Deserialize(reader); + base.Deserialize( reader ); + int version = reader.ReadInt(); } } diff --git a/Scripts/Mobiles/Monsters/Summons/SummonedDaemon.cs b/Scripts/Mobiles/Monsters/Summons/SummonedDaemon.cs index d2c145bf3..e5c03f50a 100644 --- a/Scripts/Mobiles/Monsters/Summons/SummonedDaemon.cs +++ b/Scripts/Mobiles/Monsters/Summons/SummonedDaemon.cs @@ -14,7 +14,7 @@ namespace Server.Mobiles public SummonedDaemon () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) { Name = NameList.RandomName( "daemon" ); - Body = 9; + Body = Core.AOS ? 10 : 9; BaseSoundID = 357; SetStr( 200 ); diff --git a/Scripts/Mobiles/PlayerMobile.cs b/Scripts/Mobiles/PlayerMobile.cs index 52306ddff..40d85c11e 100644 --- a/Scripts/Mobiles/PlayerMobile.cs +++ b/Scripts/Mobiles/PlayerMobile.cs @@ -313,6 +313,62 @@ namespace Server.Mobiles } #endregion + #region Auto Arrow Recovery + private Dictionary m_RecoverableAmmo = new Dictionary(); + + public Dictionary RecoverableAmmo + { + get { return m_RecoverableAmmo; } + } + + public void RecoverAmmo() + { + if ( Core.SE && Alive ) + { + foreach ( KeyValuePair kvp in m_RecoverableAmmo ) + { + if ( kvp.Value > 0 ) + { + Item ammo = null; + + try + { + ammo = Activator.CreateInstance( kvp.Key ) as Item; + } + catch + { + } + + if ( ammo != null ) + { + string name = ammo.Name; + ammo.Amount = kvp.Value; + + if ( name == null ) + { + if ( ammo is Arrow ) + name = "arrow"; + else if ( ammo is Bolt ) + name = "bolt"; + } + + if ( name != null && ammo.Amount > 1 ) + name = String.Format( "{0}s", name ); + + if ( name == null ) + name = String.Format( "#{0}", ammo.LabelNumber ); + + PlaceInBackpack( ammo ); + SendLocalizedMessage( 1073504, String.Format( "{0}\t{1}", ammo.Amount, name ) ); // You recover ~1_NUM~ ~2_AMMO~. + } + } + } + + m_RecoverableAmmo.Clear(); + } + } + #endregion + private DateTime m_AnkhNextUse; [CommandProperty( AccessLevel.GameMaster )] @@ -322,6 +378,15 @@ namespace Server.Mobiles set{ m_AnkhNextUse = value; } } + private DateTime m_PeacedUntil; + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime PeacedUntil + { + get { return m_PeacedUntil; } + set { m_PeacedUntil = value; } + } + #region Scroll of Alacrity private DateTime m_AcceleratedStart; @@ -342,7 +407,6 @@ namespace Server.Mobiles } #endregion - public static Direction GetDirection4( Point3D from, Point3D to ) { int dx = from.X - to.X; @@ -1833,6 +1897,9 @@ namespace Server.Mobiles if ( m_SentHonorContext != null ) m_SentHonorContext.OnSourceDamaged( from, amount ); + if ( willKill && from is PlayerMobile ) + Timer.DelayCall( TimeSpan.FromSeconds( 10 ), new TimerCallback( ((PlayerMobile) from).RecoverAmmo ) ); + base.OnDamage( amount, from, willKill ); } @@ -1862,6 +1929,12 @@ namespace Server.Mobiles } } + public override void OnWarmodeChanged() + { + if ( !Warmode ) + Timer.DelayCall( TimeSpan.FromSeconds( 10 ), new TimerCallback( RecoverAmmo ) ); + } + private Mobile m_InsuranceAward; private int m_InsuranceCost; private int m_InsuranceBonus; @@ -1890,6 +1963,8 @@ namespace Server.Mobiles if ( m_SentHonorContext != null ) m_SentHonorContext.OnSourceKilled(); + RecoverAmmo(); + return base.OnBeforeDeath(); } @@ -2417,6 +2492,12 @@ namespace Server.Mobiles switch ( version ) { + case 28: + { + m_PeacedUntil = reader.ReadDateTime(); + + goto case 27; + } case 27: { m_AnkhNextUse = reader.ReadDateTime(); @@ -2710,8 +2791,9 @@ namespace Server.Mobiles base.Serialize( writer ); - writer.Write( (int) 27 ); // version + writer.Write( (int) 28 ); // version + writer.Write( (DateTime) m_PeacedUntil ); writer.Write( (DateTime) m_AnkhNextUse ); writer.Write( m_AutoStabled, true ); diff --git a/Scripts/Spells/Base/Spell.cs b/Scripts/Spells/Base/Spell.cs index ad4f6e298..42007bffc 100644 --- a/Scripts/Spells/Base/Spell.cs +++ b/Scripts/Spells/Base/Spell.cs @@ -498,6 +498,10 @@ namespace Server.Spells { m_Caster.SendLocalizedMessage( 502644 ); // You have not yet recovered from casting a spell. } + else if ( m_Caster is PlayerMobile && ( (PlayerMobile) m_Caster ).PeacedUntil > DateTime.Now ) + { + m_Caster.SendLocalizedMessage( 1072060 ); // You cannot cast a spell while calmed. + } else if ( m_Caster.Mana >= ScaleMana( GetMana() ) ) { if ( m_Caster.Spell == null && m_Caster.CheckSpellCast( this ) && CheckCast() && m_Caster.Region.OnBeginSpellCast( m_Caster, this ) ) @@ -727,6 +731,11 @@ namespace Server.Spells m_Caster.SendLocalizedMessage( 502646 ); // You cannot cast a spell while frozen. DoFizzle(); } + else if ( m_Caster is PlayerMobile && ((PlayerMobile) m_Caster).PeacedUntil > DateTime.Now ) + { + m_Caster.SendLocalizedMessage( 1072060 ); // You cannot cast a spell while calmed. + DoFizzle(); + } else if ( CheckFizzle() ) { m_Caster.Mana -= mana; diff --git a/Scripts/Spells/Base/SpellHelper.cs b/Scripts/Spells/Base/SpellHelper.cs index 50c8168c2..c78eff561 100644 --- a/Scripts/Spells/Base/SpellHelper.cs +++ b/Scripts/Spells/Base/SpellHelper.cs @@ -987,6 +987,11 @@ namespace Server.Spells //from.SendMessage(String.Format("You Leeched {0} Mana", manaLeech)); } } + if ( context != null && context.Type == typeof( VampiricEmbraceSpell ) ) + { + from.Hits += AOS.Scale( damageGiven, 20 ); + from.PlaySound( 0x44D ); + } } public static void Heal( int amount, Mobile target, Mobile from ) diff --git a/Scripts/Spells/Eighth/SummonDaemon.cs b/Scripts/Spells/Eighth/SummonDaemon.cs index 558bbcfb2..ee5621162 100644 --- a/Scripts/Spells/Eighth/SummonDaemon.cs +++ b/Scripts/Spells/Eighth/SummonDaemon.cs @@ -1,4 +1,6 @@ using System; +using Server.Misc; +using Server.Items; using Server.Mobiles; using Server.Network; using Server.Targeting; @@ -7,6 +9,8 @@ namespace Server.Spells.Eighth { public class SummonDaemonSpell : MagerySpell { + private BaseCreature m_Daemon = new SummonedDaemon(); + private static SpellInfo m_Info = new SpellInfo( "Summon Daemon", "Kal Vas Xen Corp", 269, @@ -41,11 +45,14 @@ namespace Server.Spells.Eighth public override void OnCast() { if ( CheckSequence() ) - { + { TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 ); if ( Core.AOS ) - SpellHelper.Summon( new SummonedDaemon(), Caster, 0x216, duration, false, false ); + { + SpellHelper.Summon( m_Daemon, Caster, 0x216, duration, false, false ); + m_Daemon.FixedParticles(0x3728, 8, 20, 5042, EffectLayer.Head ); + } else SpellHelper.Summon( new Daemon(), Caster, 0x216, duration, false, false ); }