Convert movement, actions (lift/use), combat, and spells to use Core.TickCount and avoid DateTime caveats (performance, system time dependency, etc) Refactor DateTime.Now to DateTime.UtcNow Add LOS check for Iron Maiden addon
779 lines
No EOL
21 KiB
C#
779 lines
No EOL
21 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Server;
|
|
using Server.Items;
|
|
using Server.Mobiles;
|
|
using Server.Targeting;
|
|
using Server.Factions.AI;
|
|
using Server.Spells;
|
|
using Server.Spells.First;
|
|
using Server.Spells.Second;
|
|
using Server.Spells.Third;
|
|
using Server.Spells.Fourth;
|
|
using Server.Spells.Fifth;
|
|
using Server.Spells.Sixth;
|
|
using Server.Spells.Seventh;
|
|
|
|
namespace Server.Factions
|
|
{
|
|
public enum GuardAI
|
|
{
|
|
Bless = 0x01, // heal, cure, +stats
|
|
Curse = 0x02, // poison, -stats
|
|
Melee = 0x04, // weapons
|
|
Magic = 0x08, // damage spells
|
|
Smart = 0x10 // smart weapons/damage spells
|
|
}
|
|
|
|
public class ComboEntry
|
|
{
|
|
private Type m_Spell;
|
|
private TimeSpan m_Hold;
|
|
private int m_Chance;
|
|
|
|
public Type Spell{ get{ return m_Spell; } }
|
|
public TimeSpan Hold{ get{ return m_Hold; } }
|
|
public int Chance{ get{ return m_Chance; } }
|
|
|
|
public ComboEntry( Type spell ) : this( spell, 100, TimeSpan.Zero )
|
|
{
|
|
}
|
|
|
|
public ComboEntry( Type spell, int chance ) : this( spell, chance, TimeSpan.Zero )
|
|
{
|
|
}
|
|
|
|
public ComboEntry( Type spell, int chance, TimeSpan hold )
|
|
{
|
|
m_Spell = spell;
|
|
m_Chance = chance;
|
|
m_Hold = hold;
|
|
}
|
|
}
|
|
|
|
public class SpellCombo
|
|
{
|
|
private int m_Mana;
|
|
private ComboEntry[] m_Entries;
|
|
|
|
public int Mana{ get{ return m_Mana; } }
|
|
public ComboEntry[] Entries{ get{ return m_Entries; } }
|
|
|
|
public SpellCombo( int mana, params ComboEntry[] entries )
|
|
{
|
|
m_Mana = mana;
|
|
m_Entries = entries;
|
|
}
|
|
|
|
public static readonly SpellCombo Simple = new SpellCombo( 50,
|
|
new ComboEntry( typeof( ParalyzeSpell ), 20 ),
|
|
new ComboEntry( typeof( ExplosionSpell ), 100, TimeSpan.FromSeconds( 2.8 ) ),
|
|
new ComboEntry( typeof( PoisonSpell ), 30 ),
|
|
new ComboEntry( typeof( EnergyBoltSpell ) )
|
|
);
|
|
|
|
public static readonly SpellCombo Strong = new SpellCombo( 90,
|
|
new ComboEntry( typeof( ParalyzeSpell ), 20 ),
|
|
new ComboEntry( typeof( ExplosionSpell ), 50, TimeSpan.FromSeconds( 2.8 ) ),
|
|
new ComboEntry( typeof( PoisonSpell ), 30 ),
|
|
new ComboEntry( typeof( ExplosionSpell ), 100, TimeSpan.FromSeconds( 2.8 ) ),
|
|
new ComboEntry( typeof( EnergyBoltSpell ) ),
|
|
new ComboEntry( typeof( PoisonSpell ), 30 ),
|
|
new ComboEntry( typeof( EnergyBoltSpell ) )
|
|
);
|
|
|
|
public static Spell Process( Mobile mob, Mobile targ, ref SpellCombo combo, ref int index, ref DateTime releaseTime )
|
|
{
|
|
while ( ++index < combo.m_Entries.Length )
|
|
{
|
|
ComboEntry entry = combo.m_Entries[index];
|
|
|
|
if ( entry.Spell == typeof( PoisonSpell ) && targ.Poisoned )
|
|
continue;
|
|
|
|
if ( entry.Chance > Utility.Random( 100 ) )
|
|
{
|
|
releaseTime = DateTime.UtcNow + entry.Hold;
|
|
return (Spell) Activator.CreateInstance( entry.Spell, new object[]{ mob, null } );
|
|
}
|
|
}
|
|
|
|
combo = null;
|
|
index = -1;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public class FactionGuardAI : BaseAI
|
|
{
|
|
private BaseFactionGuard m_Guard;
|
|
|
|
private BandageContext m_Bandage;
|
|
private DateTime m_BandageStart;
|
|
|
|
private SpellCombo m_Combo;
|
|
private int m_ComboIndex = -1;
|
|
private DateTime m_ReleaseTarget;
|
|
|
|
private const int ManaReserve = 30;
|
|
|
|
public bool IsAllowed( GuardAI flag )
|
|
{
|
|
return ( ( m_Guard.GuardAI & flag ) == flag );
|
|
}
|
|
|
|
public bool IsDamaged
|
|
{
|
|
get{ return ( m_Guard.Hits < m_Guard.HitsMax ); }
|
|
}
|
|
|
|
public bool IsPoisoned
|
|
{
|
|
get{ return m_Guard.Poisoned; }
|
|
}
|
|
|
|
public TimeSpan TimeUntilBandage
|
|
{
|
|
get
|
|
{
|
|
if ( m_Bandage != null && m_Bandage.Timer == null )
|
|
m_Bandage = null;
|
|
|
|
if ( m_Bandage == null )
|
|
return TimeSpan.MaxValue;
|
|
|
|
TimeSpan ts = ( m_BandageStart + m_Bandage.Timer.Delay ) - DateTime.UtcNow;
|
|
|
|
if ( ts < TimeSpan.FromSeconds( -1.0 ) )
|
|
{
|
|
m_Bandage = null;
|
|
return TimeSpan.MaxValue;
|
|
}
|
|
|
|
if ( ts < TimeSpan.Zero )
|
|
ts = TimeSpan.Zero;
|
|
|
|
return ts;
|
|
}
|
|
}
|
|
|
|
public bool DequipWeapon()
|
|
{
|
|
Container pack = m_Guard.Backpack;
|
|
|
|
if ( pack == null )
|
|
return false;
|
|
|
|
Item weapon = m_Guard.Weapon as Item;
|
|
|
|
if ( weapon != null && weapon.Parent == m_Guard && !(weapon is Fists) )
|
|
{
|
|
pack.DropItem( weapon );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool EquipWeapon()
|
|
{
|
|
Container pack = m_Guard.Backpack;
|
|
|
|
if ( pack == null )
|
|
return false;
|
|
|
|
Item weapon = pack.FindItemByType( typeof( BaseWeapon ) );
|
|
|
|
if ( weapon == null )
|
|
return false;
|
|
|
|
return m_Guard.EquipItem( weapon );
|
|
}
|
|
|
|
public bool StartBandage()
|
|
{
|
|
m_Bandage = null;
|
|
|
|
Container pack = m_Guard.Backpack;
|
|
|
|
if ( pack == null )
|
|
return false;
|
|
|
|
Item bandage = pack.FindItemByType( typeof( Bandage ) );
|
|
|
|
if ( bandage == null )
|
|
return false;
|
|
|
|
m_Bandage = BandageContext.BeginHeal( m_Guard, m_Guard );
|
|
m_BandageStart = DateTime.UtcNow;
|
|
return ( m_Bandage != null );
|
|
}
|
|
|
|
public bool UseItemByType( Type type )
|
|
{
|
|
Container pack = m_Guard.Backpack;
|
|
|
|
if ( pack == null )
|
|
return false;
|
|
|
|
Item item = pack.FindItemByType( type );
|
|
|
|
if ( item == null )
|
|
return false;
|
|
|
|
bool requip = DequipWeapon();
|
|
|
|
item.OnDoubleClick( m_Guard );
|
|
|
|
if ( requip )
|
|
EquipWeapon();
|
|
|
|
return true;
|
|
}
|
|
|
|
public int GetStatMod( Mobile mob, StatType type )
|
|
{
|
|
StatMod mod = mob.GetStatMod( String.Format( "[Magic] {0} Offset", type ) );
|
|
|
|
if ( mod == null )
|
|
return 0;
|
|
|
|
return mod.Offset;
|
|
}
|
|
|
|
public Spell RandomOffenseSpell()
|
|
{
|
|
int maxCircle = (int)((m_Guard.Skills.Magery.Value + 20.0) / (100.0 / 7.0));
|
|
|
|
if ( maxCircle < 1 )
|
|
maxCircle = 1;
|
|
|
|
switch ( Utility.Random( maxCircle*2 ) )
|
|
{
|
|
case 0: case 1: return new MagicArrowSpell( m_Guard, null );
|
|
case 2: case 3: return new HarmSpell( m_Guard, null );
|
|
case 4: case 5: return new FireballSpell( m_Guard, null );
|
|
case 6: case 7: return new LightningSpell( m_Guard, null );
|
|
case 8: return new MindBlastSpell( m_Guard, null );
|
|
case 9: return new ParalyzeSpell( m_Guard, null );
|
|
case 10: return new EnergyBoltSpell( m_Guard, null );
|
|
case 11: return new ExplosionSpell( m_Guard, null );
|
|
default: return new FlameStrikeSpell( m_Guard, null );
|
|
}
|
|
}
|
|
|
|
public Mobile FindDispelTarget( bool activeOnly )
|
|
{
|
|
if ( m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel( m_Mobile ) || m_Mobile.AutoDispel )
|
|
return null;
|
|
|
|
if ( activeOnly )
|
|
{
|
|
List<AggressorInfo> aggressed = m_Mobile.Aggressed;
|
|
List<AggressorInfo> aggressors = m_Mobile.Aggressors;
|
|
|
|
Mobile active = null;
|
|
double activePrio = 0.0;
|
|
|
|
Mobile comb = m_Mobile.Combatant;
|
|
|
|
if ( comb != null && !comb.Deleted && comb.Alive && !comb.IsDeadBondedPet && m_Mobile.InRange( comb, 12 ) && CanDispel( comb ) )
|
|
{
|
|
active = comb;
|
|
activePrio = m_Mobile.GetDistanceToSqrt( comb );
|
|
|
|
if ( activePrio <= 2 )
|
|
return active;
|
|
}
|
|
|
|
for ( int i = 0; i < aggressed.Count; ++i )
|
|
{
|
|
AggressorInfo info = aggressed[i];
|
|
Mobile m = info.Defender;
|
|
|
|
if ( m != comb && m.Combatant == m_Mobile && m_Mobile.InRange( m, 12 ) && CanDispel( m ) )
|
|
{
|
|
double prio = m_Mobile.GetDistanceToSqrt( m );
|
|
|
|
if ( active == null || prio < activePrio )
|
|
{
|
|
active = m;
|
|
activePrio = prio;
|
|
|
|
if ( activePrio <= 2 )
|
|
return active;
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < aggressors.Count; ++i )
|
|
{
|
|
AggressorInfo info = aggressors[i];
|
|
Mobile m = info.Attacker;
|
|
|
|
if ( m != comb && m.Combatant == m_Mobile && m_Mobile.InRange( m, 12 ) && CanDispel( m ) )
|
|
{
|
|
double prio = m_Mobile.GetDistanceToSqrt( m );
|
|
|
|
if ( active == null || prio < activePrio )
|
|
{
|
|
active = m;
|
|
activePrio = prio;
|
|
|
|
if ( activePrio <= 2 )
|
|
return active;
|
|
}
|
|
}
|
|
}
|
|
|
|
return active;
|
|
}
|
|
else
|
|
{
|
|
Map map = m_Mobile.Map;
|
|
|
|
if ( map != null )
|
|
{
|
|
Mobile active = null, inactive = null;
|
|
double actPrio = 0.0, inactPrio = 0.0;
|
|
|
|
Mobile comb = m_Mobile.Combatant;
|
|
|
|
if ( comb != null && !comb.Deleted && comb.Alive && !comb.IsDeadBondedPet && CanDispel( comb ) )
|
|
{
|
|
active = inactive = comb;
|
|
actPrio = inactPrio = m_Mobile.GetDistanceToSqrt( comb );
|
|
}
|
|
|
|
foreach ( Mobile m in m_Mobile.GetMobilesInRange( 12 ) )
|
|
{
|
|
if ( m != m_Mobile && CanDispel( m ) )
|
|
{
|
|
double prio = m_Mobile.GetDistanceToSqrt( m );
|
|
|
|
if ( !activeOnly && (inactive == null || prio < inactPrio) )
|
|
{
|
|
inactive = m;
|
|
inactPrio = prio;
|
|
}
|
|
|
|
if ( (m_Mobile.Combatant == m || m.Combatant == m_Mobile) && (active == null || prio < actPrio) )
|
|
{
|
|
active = m;
|
|
actPrio = prio;
|
|
}
|
|
}
|
|
}
|
|
|
|
return active != null ? active : inactive;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public bool CanDispel( Mobile m )
|
|
{
|
|
return ( m is BaseCreature && ((BaseCreature)m).Summoned && m_Mobile.CanBeHarmful( m, false ) && !((BaseCreature)m).IsAnimatedDead );
|
|
}
|
|
|
|
public void RunTo( Mobile m )
|
|
{
|
|
/*if ( m.Paralyzed || m.Frozen )
|
|
{
|
|
if ( m_Mobile.InRange( m, 1 ) )
|
|
RunFrom( m );
|
|
else if ( !m_Mobile.InRange( m, m_Mobile.RangeFight > 2 ? m_Mobile.RangeFight : 2 ) && !MoveTo( m, true, 1 ) )
|
|
OnFailedMove();
|
|
}
|
|
else
|
|
{*/
|
|
if ( !m_Mobile.InRange( m, m_Mobile.RangeFight ) )
|
|
{
|
|
if ( !MoveTo( m, true, 1 ) )
|
|
OnFailedMove();
|
|
}
|
|
else if ( m_Mobile.InRange( m, m_Mobile.RangeFight - 1 ) )
|
|
{
|
|
RunFrom( m );
|
|
}
|
|
/*}*/
|
|
}
|
|
|
|
public void RunFrom( Mobile m )
|
|
{
|
|
Run( (m_Mobile.GetDirectionTo( m ) - 4) & Direction.Mask );
|
|
}
|
|
|
|
public void OnFailedMove()
|
|
{
|
|
/*if ( !m_Mobile.DisallowAllMoves && 20 > Utility.Random( 100 ) && IsAllowed( GuardAI.Magic ) )
|
|
{
|
|
if ( m_Mobile.Target != null )
|
|
m_Mobile.Target.Cancel( m_Mobile, TargetCancelType.Canceled );
|
|
|
|
new TeleportSpell( m_Mobile, null ).Cast();
|
|
|
|
m_Mobile.DebugSay( "I am stuck, I'm going to try teleporting away" );
|
|
}
|
|
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.Combatant = m_Mobile.FocusMob;
|
|
Action = ActionType.Combat;
|
|
}
|
|
else
|
|
{
|
|
m_Mobile.DebugSay( "I am stuck" );
|
|
}
|
|
}
|
|
|
|
public void Run( Direction d )
|
|
{
|
|
if ( (m_Mobile.Spell != null && m_Mobile.Spell.IsCasting) || m_Mobile.Paralyzed || m_Mobile.Frozen || m_Mobile.DisallowAllMoves )
|
|
return;
|
|
|
|
m_Mobile.Direction = d | Direction.Running;
|
|
|
|
if ( !DoMove( m_Mobile.Direction, true ) )
|
|
OnFailedMove();
|
|
}
|
|
|
|
public FactionGuardAI( BaseFactionGuard guard ) : base( guard )
|
|
{
|
|
m_Guard = guard;
|
|
}
|
|
|
|
public override bool Think()
|
|
{
|
|
if ( m_Mobile.Deleted )
|
|
return false;
|
|
|
|
Mobile combatant = m_Guard.Combatant;
|
|
|
|
if ( combatant == null || combatant.Deleted || !combatant.Alive || combatant.IsDeadBondedPet || !m_Mobile.CanSee( combatant ) || !m_Mobile.CanBeHarmful( combatant, false ) || combatant.Map != m_Mobile.Map )
|
|
{
|
|
// Our combatant is deleted, dead, hidden, or we cannot hurt them
|
|
// Try to find another combatant
|
|
|
|
if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
|
|
{
|
|
m_Mobile.Combatant = combatant = m_Mobile.FocusMob;
|
|
m_Mobile.FocusMob = null;
|
|
}
|
|
else
|
|
{
|
|
m_Mobile.Combatant = combatant = null;
|
|
}
|
|
}
|
|
|
|
if ( combatant != null && (!m_Mobile.InLOS( combatant ) || !m_Mobile.InRange( combatant, 12 )) )
|
|
{
|
|
if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
|
|
{
|
|
m_Mobile.Combatant = combatant = m_Mobile.FocusMob;
|
|
m_Mobile.FocusMob = null;
|
|
}
|
|
else if ( !m_Mobile.InRange( combatant, 36 ) )
|
|
{
|
|
m_Mobile.Combatant = combatant = null;
|
|
}
|
|
}
|
|
|
|
Mobile dispelTarget = FindDispelTarget( true );
|
|
|
|
if ( m_Guard.Target != null && m_ReleaseTarget == DateTime.MinValue )
|
|
m_ReleaseTarget = DateTime.UtcNow + TimeSpan.FromSeconds( 10.0 );
|
|
|
|
if ( m_Guard.Target != null && DateTime.UtcNow > m_ReleaseTarget )
|
|
{
|
|
Target targ = m_Guard.Target;
|
|
|
|
Mobile toHarm = ( dispelTarget == null ? combatant : dispelTarget );
|
|
|
|
if ( (targ.Flags & TargetFlags.Harmful) != 0 && toHarm != null )
|
|
{
|
|
if ( m_Guard.Map == toHarm.Map && ( targ.Range < 0 || m_Guard.InRange( toHarm, targ.Range ) ) && m_Guard.CanSee( toHarm ) && m_Guard.InLOS( toHarm ) )
|
|
targ.Invoke( m_Guard, toHarm );
|
|
else if ( targ is DispelSpell.InternalTarget )
|
|
targ.Cancel( m_Guard, TargetCancelType.Canceled );
|
|
}
|
|
else if ( (targ.Flags & TargetFlags.Beneficial) != 0 )
|
|
{
|
|
targ.Invoke( m_Guard, m_Guard );
|
|
}
|
|
else
|
|
{
|
|
targ.Cancel( m_Guard, TargetCancelType.Canceled );
|
|
}
|
|
|
|
m_ReleaseTarget = DateTime.MinValue;
|
|
}
|
|
|
|
if ( dispelTarget != null )
|
|
{
|
|
if ( Action != ActionType.Combat )
|
|
Action = ActionType.Combat;
|
|
|
|
m_Guard.Warmode = true;
|
|
|
|
RunFrom( dispelTarget );
|
|
}
|
|
else if ( combatant != null )
|
|
{
|
|
if ( Action != ActionType.Combat )
|
|
Action = ActionType.Combat;
|
|
|
|
m_Guard.Warmode = true;
|
|
|
|
RunTo( combatant );
|
|
}
|
|
else if ( m_Guard.Orders.Movement != MovementType.Stand )
|
|
{
|
|
Mobile toFollow = null;
|
|
|
|
if ( m_Guard.Town != null && m_Guard.Orders.Movement == MovementType.Follow )
|
|
{
|
|
toFollow = m_Guard.Orders.Follow;
|
|
|
|
if ( toFollow == null )
|
|
toFollow = m_Guard.Town.Sheriff;
|
|
}
|
|
|
|
if ( toFollow != null && toFollow.Map == m_Guard.Map && toFollow.InRange( m_Guard, m_Guard.RangePerception * 3 ) && Town.FromRegion( toFollow.Region ) == m_Guard.Town )
|
|
{
|
|
if ( Action != ActionType.Combat )
|
|
Action = ActionType.Combat;
|
|
|
|
if ( m_Mobile.CurrentSpeed != m_Mobile.ActiveSpeed )
|
|
m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed;
|
|
|
|
m_Guard.Warmode = true;
|
|
|
|
RunTo( toFollow );
|
|
}
|
|
else
|
|
{
|
|
if ( Action != ActionType.Wander )
|
|
Action = ActionType.Wander;
|
|
|
|
if ( m_Mobile.CurrentSpeed != m_Mobile.PassiveSpeed )
|
|
m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed;
|
|
|
|
m_Guard.Warmode = false;
|
|
|
|
WalkRandomInHome( 2, 2, 1 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( Action != ActionType.Wander )
|
|
Action = ActionType.Wander;
|
|
|
|
m_Guard.Warmode = false;
|
|
}
|
|
|
|
if ( (IsDamaged || IsPoisoned) && m_Guard.Skills.Healing.Base > 20.0 )
|
|
{
|
|
TimeSpan ts = TimeUntilBandage;
|
|
|
|
if ( ts == TimeSpan.MaxValue )
|
|
StartBandage();
|
|
}
|
|
|
|
if ( m_Mobile.Spell == null && Core.TickCount - m_Mobile.NextSpellTime >= 0 )
|
|
{
|
|
Spell spell = null;
|
|
|
|
DateTime toRelease = DateTime.MinValue;
|
|
|
|
if ( IsPoisoned )
|
|
{
|
|
Poison p = m_Guard.Poison;
|
|
|
|
TimeSpan ts = TimeUntilBandage;
|
|
|
|
if ( p != Poison.Lesser || ts == TimeSpan.MaxValue || TimeUntilBandage < TimeSpan.FromSeconds( 1.5 ) || (m_Guard.HitsMax - m_Guard.Hits) > Utility.Random( 250 ) )
|
|
{
|
|
if ( IsAllowed( GuardAI.Bless ) )
|
|
spell = new CureSpell( m_Guard, null );
|
|
else
|
|
UseItemByType( typeof( BaseCurePotion ) );
|
|
}
|
|
}
|
|
else if ( IsDamaged && (m_Guard.HitsMax - m_Guard.Hits) > Utility.Random( 200 ) )
|
|
{
|
|
if( IsAllowed( GuardAI.Magic ) && ((m_Guard.Hits * 100) / Math.Max( m_Guard.HitsMax, 1 )) < 10 && m_Guard.Home != Point3D.Zero && !Utility.InRange( m_Guard.Location, m_Guard.Home, 15 ) && m_Guard.Mana >= 11 )
|
|
{
|
|
spell = new RecallSpell( m_Guard, null, new RunebookEntry( m_Guard.Home, m_Guard.Map, "Guard's Home", null ), null );
|
|
}
|
|
else if ( IsAllowed( GuardAI.Bless ) )
|
|
{
|
|
if ( m_Guard.Mana >= 11 && (m_Guard.Hits + 30) < m_Guard.HitsMax )
|
|
spell = new GreaterHealSpell( m_Guard, null );
|
|
else if ( (m_Guard.Hits + 10) < m_Guard.HitsMax && (m_Guard.Mana < 11 || (m_Guard.NextCombatTime - Core.TickCount) > 2000) )
|
|
spell = new HealSpell( m_Guard, null );
|
|
}
|
|
else if ( m_Guard.CanBeginAction( typeof( BaseHealPotion ) ) )
|
|
{
|
|
UseItemByType( typeof( BaseHealPotion ) );
|
|
}
|
|
}
|
|
else if ( dispelTarget != null && (IsAllowed( GuardAI.Magic ) || IsAllowed( GuardAI.Bless ) || IsAllowed( GuardAI.Curse )) )
|
|
{
|
|
if ( !dispelTarget.Paralyzed && m_Guard.Mana > (ManaReserve + 20) && 40 > Utility.Random( 100 ) )
|
|
spell = new ParalyzeSpell( m_Guard, null );
|
|
else
|
|
spell = new DispelSpell( m_Guard, null );
|
|
}
|
|
|
|
if ( combatant != null )
|
|
{
|
|
if ( m_Combo != null )
|
|
{
|
|
if ( spell == null )
|
|
{
|
|
spell = SpellCombo.Process( m_Guard, combatant, ref m_Combo, ref m_ComboIndex, ref toRelease );
|
|
}
|
|
else
|
|
{
|
|
m_Combo = null;
|
|
m_ComboIndex = -1;
|
|
}
|
|
}
|
|
else if ( 20 > Utility.Random( 100 ) && IsAllowed( GuardAI.Magic ) )
|
|
{
|
|
if ( 80 > Utility.Random( 100 ) )
|
|
{
|
|
m_Combo = ( IsAllowed( GuardAI.Smart ) ? SpellCombo.Simple : SpellCombo.Strong );
|
|
m_ComboIndex = -1;
|
|
|
|
if ( m_Guard.Mana >= (ManaReserve + m_Combo.Mana) )
|
|
spell = SpellCombo.Process( m_Guard, combatant, ref m_Combo, ref m_ComboIndex, ref toRelease );
|
|
else
|
|
{
|
|
m_Combo = null;
|
|
|
|
if ( m_Guard.Mana >= (ManaReserve + 40) )
|
|
spell = RandomOffenseSpell();
|
|
}
|
|
}
|
|
else if ( m_Guard.Mana >= (ManaReserve + 40) )
|
|
{
|
|
spell = RandomOffenseSpell();
|
|
}
|
|
}
|
|
|
|
if ( spell == null && 2 > Utility.Random( 100 ) && m_Guard.Mana >= (ManaReserve + 10) )
|
|
{
|
|
int strMod = GetStatMod( m_Guard, StatType.Str );
|
|
int dexMod = GetStatMod( m_Guard, StatType.Dex );
|
|
int intMod = GetStatMod( m_Guard, StatType.Int );
|
|
|
|
List<Type> types = new List<Type>();
|
|
|
|
if ( strMod <= 0 )
|
|
types.Add( typeof( StrengthSpell ) );
|
|
|
|
if ( dexMod <= 0 && IsAllowed( GuardAI.Melee ) )
|
|
types.Add( typeof( AgilitySpell ) );
|
|
|
|
if ( intMod <= 0 && IsAllowed( GuardAI.Magic ) )
|
|
types.Add( typeof( CunningSpell ) );
|
|
|
|
if ( IsAllowed( GuardAI.Bless ) )
|
|
{
|
|
if ( types.Count > 1 )
|
|
spell = new BlessSpell( m_Guard, null );
|
|
else if ( types.Count == 1 )
|
|
spell = (Spell) Activator.CreateInstance( types[0], new object[]{ m_Guard, null } );
|
|
}
|
|
else if ( types.Count > 0 )
|
|
{
|
|
if ( types[0] == typeof( StrengthSpell ) )
|
|
UseItemByType( typeof( BaseStrengthPotion ) );
|
|
else if ( types[0] == typeof( AgilitySpell ) )
|
|
UseItemByType( typeof( BaseAgilityPotion ) );
|
|
}
|
|
}
|
|
|
|
if ( spell == null && 2 > Utility.Random( 100 ) && m_Guard.Mana >= (ManaReserve + 10) && IsAllowed( GuardAI.Curse ) )
|
|
{
|
|
if ( !combatant.Poisoned && 40 > Utility.Random( 100 ) )
|
|
{
|
|
spell = new PoisonSpell( m_Guard, null );
|
|
}
|
|
else
|
|
{
|
|
int strMod = GetStatMod( combatant, StatType.Str );
|
|
int dexMod = GetStatMod( combatant, StatType.Dex );
|
|
int intMod = GetStatMod( combatant, StatType.Int );
|
|
|
|
List<Type> types = new List<Type>();
|
|
|
|
if ( strMod >= 0 )
|
|
types.Add( typeof( WeakenSpell ) );
|
|
|
|
if ( dexMod >= 0 && IsAllowed( GuardAI.Melee ) )
|
|
types.Add( typeof( ClumsySpell ) );
|
|
|
|
if ( intMod >= 0 && IsAllowed( GuardAI.Magic ) )
|
|
types.Add( typeof( FeeblemindSpell ) );
|
|
|
|
if ( types.Count > 1 )
|
|
spell = new CurseSpell( m_Guard, null );
|
|
else if ( types.Count == 1 )
|
|
spell = (Spell) Activator.CreateInstance( types[0], new object[]{ m_Guard, null } );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( spell != null && (m_Guard.HitsMax - m_Guard.Hits + 10) > Utility.Random( 100 ) )
|
|
{
|
|
Type type = null;
|
|
|
|
if ( spell is GreaterHealSpell )
|
|
type = typeof( BaseHealPotion );
|
|
else if ( spell is CureSpell )
|
|
type = typeof( BaseCurePotion );
|
|
else if ( spell is StrengthSpell )
|
|
type = typeof( BaseStrengthPotion );
|
|
else if ( spell is AgilitySpell )
|
|
type = typeof( BaseAgilityPotion );
|
|
|
|
if ( type == typeof( BaseHealPotion ) && !m_Guard.CanBeginAction( type ) )
|
|
type = null;
|
|
|
|
if ( type != null && m_Guard.Target == null && UseItemByType( type ) )
|
|
{
|
|
if ( spell is GreaterHealSpell )
|
|
{
|
|
if ( (m_Guard.Hits + 30) > m_Guard.HitsMax && (m_Guard.Hits + 10) < m_Guard.HitsMax )
|
|
spell = new HealSpell( m_Guard, null );
|
|
}
|
|
else
|
|
{
|
|
spell = null;
|
|
}
|
|
}
|
|
}
|
|
else if ( spell == null && m_Guard.Stam < (m_Guard.StamMax / 3) && IsAllowed( GuardAI.Melee ) )
|
|
{
|
|
UseItemByType( typeof( BaseRefreshPotion ) );
|
|
}
|
|
|
|
if ( spell == null || !spell.Cast() )
|
|
EquipWeapon();
|
|
}
|
|
else if ( m_Mobile.Spell is Spell && ((Spell)m_Mobile.Spell).State == SpellState.Sequencing )
|
|
{
|
|
EquipWeapon();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
} |