760 lines
21 KiB
C#
760 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
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 => m_Spell;
|
|
public TimeSpan Hold => m_Hold;
|
|
public int Chance => 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 => m_Mana;
|
|
public ComboEntry[] Entries => 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 => ( m_Guard.Hits < m_Guard.HitsMax );
|
|
|
|
public bool IsPoisoned => 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;
|
|
|
|
if ( m_Guard.Weapon is Item weapon && weapon.Parent == m_Guard && !(weapon is Fists) )
|
|
{
|
|
pack.DropItem( weapon );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool EquipWeapon()
|
|
{
|
|
Container pack = m_Guard.Backpack;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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($"[Magic] {type} Offset");
|
|
|
|
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 creature && creature.Summoned && m_Mobile.CanBeHarmful( creature, false ) && !creature.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();
|
|
}
|
|
|
|
Spell spell = m_Mobile.Spell as Spell;
|
|
|
|
if ( spell == null && Core.TickCount - m_Mobile.NextSpellTime >= 0 )
|
|
{
|
|
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 = Activator.CreateInstance( types[0], new object[]{ m_Guard, null } ) as Spell;
|
|
}
|
|
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 ( spell?.State == SpellState.Sequencing )
|
|
{
|
|
EquipWeapon();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|