- Terrible Hatchlings quest fixed to only reward SE weapons and armor.

- Slayer and opposition systems now account for inheritance.
- Added Satyrs to the fey opposition group.
- Added fey slayer vulnerability to cu sidhes.
- Added reptilian and dragon slayer vulnerabilities to reptalons.
- Various mage AI additions for necromancers. (Spirit speak for healing, pain spike and strangle for damage, automatic mage/necro bias by skill value.)
- Fixed a targeting issue not accounting for FightMode.Evil versus low karma targeting.
- Added melee damage bonus for Talisman killer properties.
- Added general ML loot for ML areas.
- Added ML minor artifact drops for named ML mobs.
- Added named ML mobs for Bedlam.
This commit is contained in:
eos 2012-02-26 05:11:25 +00:00
parent a0bc5e2349
commit 43e5982fbb
22 changed files with 1207 additions and 231 deletions

View file

@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Server.Spells;
using Server.Spells.Fifth;
@ -37,24 +36,28 @@ namespace Server.Mobiles
public virtual bool SmartAI
{
get { return ( m_Mobile is BaseVendor || m_Mobile is BaseEscortable ); }
get { return ( m_Mobile is BaseVendor || m_Mobile is BaseEscortable/* || m_Mobile is Changeling*/ ); } // TODO: Uncomment once added
}
public virtual bool IsNecromancer
{
get { return ( m_Mobile.Skills[ SkillName.Necromancy ].Value > 50 ); }
}
private const double HealChance = 0.10; // 10% chance to heal at gm magery
private const double TeleportChance = 0.05; // 5% chance to teleport at gm magery
private const double DispelChance = 0.75; // 75% chance to dispel at gm magery
public virtual double ScaleByMagery( double v )
public virtual double ScaleBySkill( double v, SkillName skill )
{
return m_Mobile.Skills[ SkillName.Magery ].Value * v * 0.01;
return v * m_Mobile.Skills[ skill ].Value / 100;
}
public override bool DoActionWander()
{
if( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
{
if( m_Mobile.Debug )
m_Mobile.DebugSay( "I am going to attack {0}", m_Mobile.FocusMob.Name );
m_Mobile.DebugSay( "I am going to attack {0}", m_Mobile.FocusMob.Name );
m_Mobile.Combatant = m_Mobile.FocusMob;
Action = ActionType.Combat;
@ -74,7 +77,7 @@ namespace Server.Mobiles
base.DoActionWander();
if( ( Utility.RandomDouble() < .05 ) )
if( Utility.RandomDouble() < 0.05 )
{
Spell spell = CheckCastHealingSpell();
@ -104,7 +107,7 @@ namespace Server.Mobiles
if( !SmartAI )
{
if( ScaleByMagery( HealChance ) < Utility.RandomDouble() )
if( ScaleBySkill( HealChance, SkillName.Magery ) < Utility.RandomDouble() )
return null;
}
else
@ -117,13 +120,22 @@ namespace Server.Mobiles
if( m_Mobile.Hits < ( m_Mobile.HitsMax - 50 ) )
{
spell = new GreaterHealSpell( m_Mobile, null );
if ( UseNecromancy() )
{
m_Mobile.UseSkill( SkillName.SpiritSpeak );
}
else
{
spell = new GreaterHealSpell( m_Mobile, null );
if( spell == null )
spell = new HealSpell( m_Mobile, null );
if( spell == null )
spell = new HealSpell( m_Mobile, null );
}
}
else if( m_Mobile.Hits < ( m_Mobile.HitsMax - 10 ) )
{
spell = new HealSpell( m_Mobile, null );
}
double delay;
@ -175,7 +187,7 @@ namespace Server.Mobiles
public void OnFailedMove()
{
if( !m_Mobile.DisallowAllMoves && ( SmartAI ? Utility.Random( 4 ) == 0 : ScaleByMagery( TeleportChance ) > Utility.RandomDouble() ) )
if( !m_Mobile.DisallowAllMoves && ( SmartAI ? Utility.Random( 4 ) == 0 : ScaleBySkill( TeleportChance, SkillName.Magery ) > Utility.RandomDouble() ) )
{
if( m_Mobile.Target != null )
m_Mobile.Target.Cancel( m_Mobile, TargetCancelType.Canceled );
@ -186,8 +198,7 @@ namespace Server.Mobiles
}
else if( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
{
if( m_Mobile.Debug )
m_Mobile.DebugSay( "My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name );
m_Mobile.DebugSay( "My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name );
m_Mobile.Combatant = m_Mobile.FocusMob;
Action = ActionType.Combat;
@ -209,46 +220,41 @@ namespace Server.Mobiles
OnFailedMove();
}
public virtual bool CanCastNecro()
public virtual bool UseNecromancy()
{
return ( Utility.RandomBool() && ( m_Mobile is BaseCreature ) && ( (BaseCreature)m_Mobile ).IsNecromancer );
}
if ( IsNecromancer )
return ( Utility.Random( m_Mobile.Skills[ SkillName.Necromancy ].BaseFixedPoint ) > Utility.Random( m_Mobile.Skills[ SkillName.Magery ].BaseFixedPoint ) );
public virtual bool CanCastNecroBias( int bias )
{
return ( CanCastNecro() && Utility.Random( bias ) == 0 );
}
public virtual Spell GetRandomDamage()
{
return ( CanCastNecroBias( 2 ) ) ? GetRandomDamageNecroSpell() : GetRandomDamageSpell();
}
public virtual Spell GetRandomDamageNecroSpell()
{
int possibles = 3;
if( myNecro >= 100 )
{
possibles = 5;
}
switch( Utility.Random( possibles ) )
{
default: m_Mobile.DebugSay( "Vengeful Spirit" ); return new VengefulSpiritSpell( m_Mobile, null );
case 0: m_Mobile.DebugSay( "Pain Spike" ); return new PainSpikeSpell( m_Mobile, null );
case 1: m_Mobile.DebugSay( "Poison Strike" ); return new PoisonStrikeSpell( m_Mobile, null );
case 2: m_Mobile.DebugSay( "Strangle" ); return new StrangleSpell( m_Mobile, null );
case 3: m_Mobile.DebugSay( "Wither" ); return new WitherSpell( m_Mobile, null );
}
return false;
}
public virtual Spell GetRandomDamageSpell()
{
int maxCircle = (int)( ( myMagery + 20.0 ) / ( 100.0 / 7.0 ) );
return UseNecromancy() ? GetRandomDamageSpellNecro() : GetRandomDamageSpellMage();
}
public virtual Spell GetRandomDamageSpellNecro()
{
int bound = ( m_Mobile.Skills[ SkillName.Necromancy ].Value >= 100 ) ? 5 : 3;
switch( Utility.Random( bound ) )
{
case 0: m_Mobile.DebugSay( "Pain Spike" ); return new PainSpikeSpell( m_Mobile, null );
case 1: m_Mobile.DebugSay( "Poison Strike" ); return new PoisonStrikeSpell( m_Mobile, null );
case 2: m_Mobile.DebugSay( "Strangle" ); return new StrangleSpell( m_Mobile, null );
case 3: m_Mobile.DebugSay( "Wither" ); return new WitherSpell( m_Mobile, null );
default: m_Mobile.DebugSay( "Vengeful Spirit" ); return new VengefulSpiritSpell( m_Mobile, null );
}
}
public virtual Spell GetRandomDamageSpellMage()
{
int maxCircle = (int)( ( m_Mobile.Skills[ SkillName.Magery ].Value + 20.0 ) / ( 100.0 / 7.0 ) );
if( maxCircle < 1 )
maxCircle = 1;
else if( maxCircle > 8 )
maxCircle = 8;
switch( Utility.Random( maxCircle * 2 ) )
{
@ -268,49 +274,39 @@ namespace Server.Mobiles
}
}
public virtual Spell GetRandomCurse()
public virtual Spell GetRandomCurseSpell()
{
return ( CanCastNecro() ) ? GetRandomNecroCurseSpell() : GetRandomCurseSpell();
return UseNecromancy() ? GetRandomCurseSpellNecro() : GetRandomCurseSpellMage();
}
public virtual Spell GetRandomNecroCurseSpell()
public virtual Spell GetRandomCurseSpellNecro()
{
switch( Utility.Random( 4 ) )
{
default:
case 0: m_Mobile.DebugSay( "Blood Oath" ); return new BloodOathSpell( m_Mobile, null );
case 1: m_Mobile.DebugSay( "Corpse Skin" ); return new CorpseSkinSpell( m_Mobile, null );
case 2: m_Mobile.DebugSay( "Evil Omen" ); return new EvilOmenSpell( m_Mobile, null );
case 3: m_Mobile.DebugSay( "Mind Rot" ); return new MindRotSpell( m_Mobile, null );
default: m_Mobile.DebugSay( "Mind Rot" ); return new MindRotSpell( m_Mobile, null );
}
}
public virtual Spell GetRandomCurseSpell()
public virtual Spell GetRandomCurseSpellMage()
{
if( Utility.Random( 4 ) == 3 )
{
if( myMagery >= 40.0 )
{
return new CurseSpell( m_Mobile, null );
}
}
if( m_Mobile.Skills[ SkillName.Magery ].Value >= 40.0 && Utility.Random( 4 ) == 0 )
return new CurseSpell( m_Mobile, null );
switch( Utility.Random( 3 ) )
{
default:
case 0: return new WeakenSpell( m_Mobile, null );
case 1: return new ClumsySpell( m_Mobile, null );
case 2: return new FeeblemindSpell( m_Mobile, null );
default: return new FeeblemindSpell( m_Mobile, null );
}
}
public virtual Spell GetRandomManaDrainSpell()
{
if( Utility.RandomBool() )
{
if( myMagery >= 80.0 )
return new ManaVampireSpell( m_Mobile, null );
}
if( m_Mobile.Skills[ SkillName.Magery ].Value >= 80.0 && Utility.RandomBool() )
return new ManaVampireSpell( m_Mobile, null );
return new ManaDrainSpell( m_Mobile, null );
}
@ -319,7 +315,7 @@ namespace Server.Mobiles
{
if( !SmartAI )
{
if( ScaleByMagery( DispelChance ) > Utility.RandomDouble() )
if( ScaleBySkill( DispelChance, SkillName.Magery ) > Utility.RandomDouble() )
return new DispelSpell( m_Mobile, null );
return ChooseSpell( toDispel );
@ -340,12 +336,6 @@ namespace Server.Mobiles
return spell;
}
public virtual double myNecro { get { return m_Mobile.Skills[ SkillName.Magery ].Value; } }
public virtual double myMagery { get { return m_Mobile.Skills[ SkillName.Magery ].Value; } }
public virtual double mySpiritSpeak { get { return m_Mobile.Skills[ SkillName.SpiritSpeak ].Value; } }
public virtual Spell ChooseSpell( Mobile c )
{
Spell spell = null;
@ -357,64 +347,74 @@ namespace Server.Mobiles
if( spell != null )
return spell;
if( IsNecromancer )
{
double psDamage = ( ( m_Mobile.Skills[ SkillName.SpiritSpeak ].Value - c.Skills[ SkillName.MagicResist ].Value ) / 10 ) + ( c.Player ? 18 : 30 );
if( psDamage > c.Hits )
return new PainSpikeSpell( m_Mobile, null );
}
switch (Utility.Random(16))
{
case 0:
case 1: // Poison them
{
//m_Mobile.DebugSay( "Attempting to poison" );
if (c.Poisoned)
goto default;
if (!c.Poisoned)
spell = new PoisonSpell(m_Mobile, null);
m_Mobile.DebugSay( "Attempting to poison" );
spell = new PoisonSpell(m_Mobile, null);
break;
}
case 2: // Bless ourselves.
case 2: // Bless ourselves
{
//m_Mobile.DebugSay( "Blessing myself" );
m_Mobile.DebugSay( "Blessing myself" );
spell = new BlessSpell(m_Mobile, null);
break;
}
case 3:
case 4: // Curse them.
case 4: // Curse them
{
//m_Mobile.DebugSay( "Attempting to curse" );
m_Mobile.DebugSay( "Attempting to curse" );
spell = GetRandomCurse();
spell = GetRandomCurseSpell();
break;
}
case 5: // Paralyze them.
case 5: // Paralyze them
{
//m_Mobile.DebugSay( "Attempting to paralyze" );
if (m_Mobile.Skills[ SkillName.Magery ].Value <= 50.0)
goto default;
if (m_Mobile.Skills[SkillName.Magery].Value > 50.0)
spell = new ParalyzeSpell(m_Mobile, null);
m_Mobile.DebugSay( "Attempting to paralyze" );
spell = new ParalyzeSpell(m_Mobile, null);
break;
}
case 6: // Drain mana
{
//m_Mobile.DebugSay( "Attempting to drain mana" );
m_Mobile.DebugSay( "Attempting to drain mana" );
spell = GetRandomManaDrainSpell();
break;
}
case 7:
case 7: // Invis ourselves
{
//m_Mobile.DebugSay( "Attempting to Invis" );
if (Utility.RandomBool())
spell = new InvisibilitySpell(m_Mobile, null);
goto default;
m_Mobile.DebugSay( "Attempting to invis myself" );
spell = new InvisibilitySpell(m_Mobile, null);
break;
}
default: // Damage them.
default: // Damage them
{
//m_Mobile.DebugSay( "Just doing damage" );
m_Mobile.DebugSay( "Just doing damage" );
spell = GetRandomDamage();
spell = GetRandomDamageSpell();
break;
}
}
@ -429,12 +429,12 @@ namespace Server.Mobiles
switch( Utility.Random( 3 ) )
{
default:
case 0: // Poison them
{
if( !c.Poisoned )
spell = new PoisonSpell( m_Mobile, null );
if( c.Poisoned )
goto case 1;
spell = new PoisonSpell( m_Mobile, null );
break;
}
case 1: // Deal some damage
@ -443,9 +443,9 @@ namespace Server.Mobiles
break;
}
case 2: // Set up a combo
default: // Set up a combo
{
if( m_Mobile.Mana < 40 && m_Mobile.Mana > 15 )
if( m_Mobile.Mana > 15 && m_Mobile.Mana < 40 )
{
if( c.Paralyzed && !c.Poisoned )
{
@ -460,7 +460,7 @@ namespace Server.Mobiles
}
else if( m_Mobile.Mana > 60 )
{
if( Utility.Random( 2 ) == 0 && !c.Paralyzed && !c.Frozen && !c.Poisoned )
if( Utility.RandomBool() && !c.Paralyzed && !c.Frozen && !c.Poisoned )
{
m_Combo = 0;
spell = new ParalyzeSpell( m_Mobile, null );
@ -499,15 +499,16 @@ namespace Server.Mobiles
{
if( !c.Poisoned )
spell = new PoisonSpell( m_Mobile, null );
else if ( IsNecromancer )
spell = new StrangleSpell( m_Mobile, null );
++m_Combo; // Move to next spell
}
if( m_Combo == 3 && spell == null )
{
switch( Utility.Random( 3 ) )
switch( Utility.Random( IsNecromancer ? 4 : 3 ) )
{
default:
case 0:
{
if( c.Int < c.Dex )
@ -531,6 +532,12 @@ namespace Server.Mobiles
m_Combo = -1; // Reset combo state
break;
}
default:
{
spell = new PainSpikeSpell( m_Mobile, null );
m_Combo = -1; // Reset combo state
break;
}
}
}
else if( m_Combo == 4 && spell == null )
@ -550,7 +557,7 @@ namespace Server.Mobiles
}
else
{
double del = ScaleByMagery( 3.0 );
double del = ScaleBySkill( 3.0, SkillName.Magery );
double min = 6.0 - ( del * 0.75 );
double max = 6.0 - ( del * 1.25 );
@ -573,8 +580,7 @@ namespace Server.Mobiles
if( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
{
if( m_Mobile.Debug )
m_Mobile.DebugSay( "Something happened to my combatant, so I am going to fight {0}", m_Mobile.FocusMob.Name );
m_Mobile.DebugSay( "Something happened to my combatant, so I am going to fight {0}", m_Mobile.FocusMob.Name );
m_Mobile.Combatant = c = m_Mobile.FocusMob;
m_Mobile.FocusMob = null;
@ -650,8 +656,7 @@ namespace Server.Mobiles
if( flee )
{
if( m_Mobile.Debug )
m_Mobile.DebugSay( "I am going to flee from {0}", c.Name );
m_Mobile.DebugSay( "I am going to flee from {0}", c.Name );
Action = ActionType.Flee;
return true;
@ -784,8 +789,7 @@ namespace Server.Mobiles
}
else if( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
{
if( m_Mobile.Debug )
m_Mobile.DebugSay( "I am scared of {0}", m_Mobile.FocusMob.Name );
m_Mobile.DebugSay( "I am scared of {0}", m_Mobile.FocusMob.Name );
RunFrom( m_Mobile.FocusMob );
m_Mobile.FocusMob = null;

View file

@ -35,7 +35,7 @@ namespace Server
bool contains = false;
for ( int j = 0; !contains && j < group.Length; ++j )
contains = ( type == group[j] );
contains = group[j].IsAssignableFrom( type );
if ( contains )
return i;
@ -107,7 +107,8 @@ namespace Server
typeof( Unicorn ),
typeof( Wisp ),
typeof( Treefellow ),
typeof( MLDryad )
typeof( MLDryad ),
typeof( Satyr )
},
new Type[]
{