ModernUO/Projects/Scripts/Engines/Factions/Mobiles/Guards/GuardAI.cs
Kamron Batman 179cb50557 Updates Packets & Randomizer (#43)
* Fixes a bug with the main loop. Removes unnecessary optimizations for RDRand.

* WIP - Rewriting packets.

* Cleanup and updates README

* Converts more packets

* Fixes header

* Converts Effects packets.

* Forgot the send command

* Adds the start of containers packets.

* Adds message packets with caching

* Extends the send methods

* Starts to add Acquire methods

* Converted the packets

* Cleanup

* Cleanup

* Adds more packets

* Adds more packets

* Fixes clearing arrays from pool. Adds Mobile Incoming

* Converts more packets.

* Moves more packets

* Moves more packets

* Test compression

* Merges

* Migrating to an idiomatic syntax that also supports compression, and proxying.

* Optimize the stack alloc. Changes attributes

* Converted container packets

* Visual Studio doesn't auto save files. I am still getting used to subpar IDEs.

* Adds static packet caching. Will profile later. Converts more packets

* Fixes packets and changes how compression is configured

* WIP - Adds basics for Gumps. Not finished though.

* Fix file

* Fixes formatting

* Changed SpanWriter to be more idiomatic.

* Fixes Span vs RawSpan and missing stackallocs

* Converts over some more gumps

* WIP - Deletes 32bit support.

* Drops RDRand32 support.

* Fixes gump compilation

* Converting gump components

* Removes old huffman compression function

* Revert signature for backwards compatibility

* WIP - Converting more gump components

* Creates ArraySet for the strings. Updates AppendTo to reference that.

* Converts the maining gump components

* Cleans up gump components

* Cleans up directives

* Cleans up ArraySet and moves it. Adds null-coalescing-assignment

* Cleanup, Target Packets, and C# 8 changes.

* Removed OPL Packet

* World packets

* Fixing packet uses

* Cleans up code. Fixes packet uses in various places.

* More cleanup for packets

* Code cleanup

* Converts more packet uses and cleans up more code

* More code cleanup

* Finishes fixing the packets in Item

* Updates secure trade packets

* Updates core and gets it to compile.

* Moved packets to scripts. Fixed account handler use of packets

* Code cleanup

* Updates chat packets

* Code cleanuo

* Adds party packets, but need to implement them.

* Party packets WIP

* Finishes party packets

* Rearrange movement namespaces and classes

* Finishes plant packets

* Code Formatting

* Fixes moving effects

* Code cleanup, eliminates equipinfo

* Adds more packets. Fixes bugs with various packets.

* Finishes mahjon packets

* Fixes mahjong packet

* Code cleanup

* Finishes mahjong packets

* Adds Map packets

* Add multifacet maps and charts

* Cleans up some packets with UTF8

* Optimizes packets

* Cleans up more packets. Moves the MessageHelper

* Removes assistant support. Removes extended protocol. Incorporates MapUO packets as normal packets.

* Updates protocol extensions packet receiver

* Fixes a few bugs. Fixes a few more packets.

* Code cleanup and fixing more packets

* Fixes packet effects

* Cleaned up more code

* Buff Icon cleanup

* Removed unused constructors

* Code Cleanup. Adds BoatHS Packets

* Moves house files. Updates house foundation packets.

* Deployment cleanup

* Fixes:

* Code cleanup

* More code cleanup

* More code cleanup

* Cleaned up BaseHouse

* Enforces styling

* Converts foreach to linq where possible.

* Dont need that

* Goals/Readme updates

* Removes 32bit support at the highest level. Turns on HRT by default.

* Code cleanup. Fixes extended features packet.

* Fixes various bugs

* Code cleanup

* Code cleanup

* Code cleanup. Fixes gump X/Y assignment.

* Code cleanup using |= operator

* More code cleanup

* Cleanup

* Fixes spacing issues. Thanks Visual Studio. You suck.

* Compiler error

* Fixes NPE from RunUO 2.7

* Renames ScriptCompiler to AssemblyHandler. Fixes packets. Updates README

* Fixes more packets. Stupid trailing nulls.

* Fixes various bugs.

* Fixes for gumps

* Fixes more gump stuff. Going to split it out later since it is getting insane

* Recoded the gump writing

* WIP

* *Added output path of scripts project to dev branch
*Activated debugging in code
2019-11-11 12:40:16 +01:00

751 lines
22 KiB
C#

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;
namespace Server.Factions
{
[Flags]
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
{
public ComboEntry(Type spell, int chance = 100) : this(spell, chance, TimeSpan.Zero)
{
}
public ComboEntry(Type spell, int chance, TimeSpan hold)
{
Spell = spell;
Chance = chance;
Hold = hold;
}
public Type Spell{ get; }
public TimeSpan Hold{ get; }
public int Chance{ get; }
}
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))
);
public SpellCombo(int mana, params ComboEntry[] entries)
{
Mana = mana;
Entries = entries;
}
public int Mana{ get; }
public ComboEntry[] Entries{ get; }
public static Spell Process(Mobile mob, Mobile targ, ref SpellCombo combo, ref int index, ref DateTime releaseTime)
{
while (++index < combo.Entries.Length)
{
ComboEntry entry = combo.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, mob, null);
}
}
combo = null;
index = -1;
return null;
}
}
public class FactionGuardAI : BaseAI
{
private const int ManaReserve = 30;
private BandageContext m_Bandage;
private DateTime m_BandageStart;
private SpellCombo m_Combo;
private int m_ComboIndex = -1;
private BaseFactionGuard m_Guard;
private DateTime m_ReleaseTarget;
public FactionGuardAI(BaseFactionGuard guard) : base(guard) => m_Guard = guard;
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 IsAllowed(GuardAI flag) => (m_Guard.GuardAI & flag) == flag;
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()
{
Item weapon = m_Guard.Backpack?.FindItemByType<BaseWeapon>();
return weapon != null && m_Guard.EquipItem(weapon);
}
public bool StartBandage()
{
m_Bandage = null;
if (m_Guard.Backpack?.FindItemByType<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;
return Utility.Random(maxCircle * 2) switch
{
0 => (Spell)new MagicArrowSpell(m_Guard),
1 => new MagicArrowSpell(m_Guard),
2 => new HarmSpell(m_Guard),
3 => new HarmSpell(m_Guard),
4 => new FireballSpell(m_Guard),
5 => new FireballSpell(m_Guard),
6 => new LightningSpell(m_Guard),
7 => new LightningSpell(m_Guard),
8 => new MindBlastSpell(m_Guard),
9 => new ParalyzeSpell(m_Guard),
10 => new EnergyBoltSpell(m_Guard),
11 => new ExplosionSpell(m_Guard),
_ => new FlameStrikeSpell(m_Guard)
};
}
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?.Deleted == false && 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;
}
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?.Deleted == false && 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 (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 ?? inactive;
}
return null;
}
public bool CanDispel(Mobile m) =>
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?.IsCasting == true || 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;
if (combatant?.Deleted != false || !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 ?? combatant;
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 as ISpellTarget)?.Spell is DispelSpell)
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 ?? 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);
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,
new RunebookEntry(m_Guard.Home, m_Guard.Map, "Guard's Home"));
}
else if (IsAllowed(GuardAI.Bless))
{
if (m_Guard.Mana >= 11 && m_Guard.Hits + 30 < m_Guard.HitsMax)
spell = new GreaterHealSpell(m_Guard);
else if (m_Guard.Hits + 10 < m_Guard.HitsMax &&
(m_Guard.Mana < 11 || m_Guard.NextCombatTime - Core.TickCount > 2000))
spell = new HealSpell(m_Guard);
}
else if (m_Guard.CanBeginAction<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);
else
spell = new DispelSpell(m_Guard);
}
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);
else if (types.Count == 1)
spell = Activator.CreateInstance(types[0], 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);
}
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);
else if (types.Count == 1)
spell = (Spell)Activator.CreateInstance(types[0], 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);
}
else
{
spell = null;
}
}
}
else if (spell == null && m_Guard.Stam < m_Guard.StamMax / 3 && IsAllowed(GuardAI.Melee))
{
UseItemByType(typeof(BaseRefreshPotion));
}
if (spell?.Cast() != true)
EquipWeapon();
}
else if (spell?.State == SpellState.Sequencing)
{
EquipWeapon();
}
return true;
}
}
}