922 lines
21 KiB
C#
922 lines
21 KiB
C#
#region References
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
using Server.Factions.AI;
|
|
using Server.Items;
|
|
using Server.Mobiles;
|
|
using Server.Spells;
|
|
using Server.Spells.Fifth;
|
|
using Server.Spells.First;
|
|
using Server.Spells.Fourth;
|
|
using Server.Spells.Second;
|
|
using Server.Spells.Seventh;
|
|
using Server.Spells.Sixth;
|
|
using Server.Spells.Third;
|
|
using Server.Targeting;
|
|
#endregion
|
|
|
|
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 readonly Type m_Spell;
|
|
private readonly TimeSpan m_Hold;
|
|
private readonly int 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 Type Spell { get { return m_Spell; } }
|
|
public TimeSpan Hold { get { return m_Hold; } }
|
|
public int Chance { get { return m_Chance; } }
|
|
}
|
|
|
|
public class SpellCombo
|
|
{
|
|
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)));
|
|
|
|
private readonly int m_Mana;
|
|
private readonly ComboEntry[] m_Entries;
|
|
|
|
public SpellCombo(int mana, params ComboEntry[] entries)
|
|
{
|
|
m_Mana = mana;
|
|
m_Entries = entries;
|
|
}
|
|
|
|
public int Mana { get { return m_Mana; } }
|
|
public ComboEntry[] Entries { get { return m_Entries; } }
|
|
|
|
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 const int ManaReserve = 30;
|
|
private readonly BaseFactionGuard m_Guard;
|
|
private BandageContext m_Bandage;
|
|
private DateTime m_BandageStart;
|
|
private SpellCombo m_Combo;
|
|
private int m_ComboIndex = -1;
|
|
private DateTime m_ReleaseTarget;
|
|
|
|
public FactionGuardAI(BaseFactionGuard guard)
|
|
: base(guard)
|
|
{
|
|
m_Guard = guard;
|
|
}
|
|
|
|
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 IsAllowed(GuardAI flag)
|
|
{
|
|
return ((m_Guard.GuardAI & flag) == flag);
|
|
}
|
|
|
|
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)
|
|
{
|
|
int offset = 0;
|
|
StatMod buff = mob.GetStatMod(String.Format("[Magic] {0} Buff", type));
|
|
StatMod curse = mob.GetStatMod(String.Format("[Magic] {0} Curse", type));
|
|
|
|
if (buff != null)
|
|
offset += buff.Offset;
|
|
if (curse != null)
|
|
offset += curse.Offset;
|
|
|
|
return 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)
|
|
{
|
|
var aggressed = m_Mobile.Aggressed;
|
|
var aggressors = m_Mobile.Aggressors;
|
|
|
|
Mobile active = null;
|
|
double activePrio = 0.0;
|
|
|
|
Mobile comb = m_Mobile.Combatant as Mobile;
|
|
|
|
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 as Mobile;
|
|
|
|
if (comb != null && !comb.Deleted && comb.Alive && !comb.IsDeadBondedPet && CanDispel(comb))
|
|
{
|
|
active = inactive = comb;
|
|
actPrio = inactPrio = m_Mobile.GetDistanceToSqrt(comb);
|
|
}
|
|
|
|
IPooledEnumerable eable = m_Mobile.GetMobilesInRange(12);
|
|
|
|
foreach (Mobile m in eable)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
eable.Free();
|
|
|
|
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((Direction)((int)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 override bool Think()
|
|
{
|
|
if (m_Mobile.Deleted)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Mobile combatant = m_Guard.Combatant as Mobile;
|
|
|
|
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 as Mobile;
|
|
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 as Mobile;
|
|
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)
|
|
{
|
|
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, RecallRuneType.Normal), 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);
|
|
|
|
var 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);
|
|
|
|
var 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;
|
|
}
|
|
}
|
|
}
|