- 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)
This commit is contained in:
daedalus 2009-12-14 19:01:59 +00:00
parent 37043076e0
commit cd7c2becf7
14 changed files with 511 additions and 98 deletions

View file

@ -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() )

View file

@ -106,7 +106,8 @@ namespace Server
typeof( Silvani ),
typeof( Unicorn ),
typeof( Wisp ),
typeof( Treefellow )
typeof( Treefellow ),
typeof( MLDryad )
},
new Type[]
{

View file

@ -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();
}
}

View file

@ -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<Mobile, Timer> m_Suppressed = new Dictionary<Mobile, Timer>();
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();
}
}

View file

@ -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 );

View file

@ -313,6 +313,62 @@ namespace Server.Mobiles
}
#endregion
#region Auto Arrow Recovery
private Dictionary<Type, int> m_RecoverableAmmo = new Dictionary<Type,int>();
public Dictionary<Type, int> RecoverableAmmo
{
get { return m_RecoverableAmmo; }
}
public void RecoverAmmo()
{
if ( Core.SE && Alive )
{
foreach ( KeyValuePair<Type, int> 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 );