Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
54
Scripts/Mobiles/AI/AIControlMobileTarget.cs
Normal file
54
Scripts/Mobiles/AI/AIControlMobileTarget.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
#region References
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server.Gumps;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Targeting;
|
||||
#endregion
|
||||
|
||||
namespace Server.Targets
|
||||
{
|
||||
public class AIControlMobileTarget : Target
|
||||
{
|
||||
private readonly List<BaseAI> m_List;
|
||||
private readonly OrderType m_Order;
|
||||
private readonly BaseCreature m_Mobile;
|
||||
|
||||
public AIControlMobileTarget(BaseAI ai, OrderType order)
|
||||
: base(-1, false, (order == OrderType.Attack ? TargetFlags.Harmful : TargetFlags.None))
|
||||
{
|
||||
m_List = new List<BaseAI>();
|
||||
m_Order = order;
|
||||
|
||||
AddAI(ai);
|
||||
m_Mobile = ai.m_Mobile;
|
||||
}
|
||||
|
||||
public OrderType Order { get { return m_Order; } }
|
||||
|
||||
public void AddAI(BaseAI ai)
|
||||
{
|
||||
if (!m_List.Contains(ai))
|
||||
m_List.Add(ai);
|
||||
}
|
||||
|
||||
protected override void OnTarget(Mobile from, object o)
|
||||
{
|
||||
if (o is IDamageable)
|
||||
{
|
||||
var dam = o as IDamageable;
|
||||
|
||||
for (var i = 0; i < m_List.Count; ++i)
|
||||
m_List[i].EndPickTarget(from, dam, m_Order);
|
||||
}
|
||||
else if (o is MoonglowDonationBox && m_Order == OrderType.Transfer && from is PlayerMobile)
|
||||
{
|
||||
var pm = (PlayerMobile)from;
|
||||
var box = (MoonglowDonationBox)o;
|
||||
|
||||
pm.SendGump(new ConfirmTransferPetGump(box, from.Location, m_Mobile));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
136
Scripts/Mobiles/AI/AnimalAI.cs
Normal file
136
Scripts/Mobiles/AI/AnimalAI.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
|
||||
|
||||
// Ideas
|
||||
// When you run on animals the panic
|
||||
// When if ( distance < 8 && Utility.RandomDouble() * Math.Sqrt( (8 - distance) / 6 ) >= incoming.Skills[SkillName.AnimalTaming].Value )
|
||||
// More your close, the more it can panic
|
||||
/*
|
||||
* AnimalHunterAI, AnimalHidingAI, AnimalDomesticAI...
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class AnimalAI : BaseAI
|
||||
{
|
||||
public AnimalAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
// Old:
|
||||
#if false
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, true, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay( "There is something near, I go away" );
|
||||
Action = ActionType.Backoff;
|
||||
}
|
||||
else if ( m_Mobile.IsHurt() || m_Mobile.Combatant != null )
|
||||
{
|
||||
m_Mobile.DebugSay( "I am hurt or being attacked, I flee" );
|
||||
Action = ActionType.Flee;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionWander();
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
// New, only flee @ 10%
|
||||
var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax;
|
||||
|
||||
if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // Less than 10% health
|
||||
{
|
||||
m_Mobile.DebugSay("I am low on health!");
|
||||
Action = ActionType.Flee;
|
||||
}
|
||||
else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionWander();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
var combatant = m_Mobile.Combatant;
|
||||
|
||||
if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map)
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant is gone..");
|
||||
|
||||
Action = ActionType.Wander;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
|
||||
{
|
||||
if (!DirectionLocked)
|
||||
m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1)
|
||||
{
|
||||
m_Mobile.DebugSay("I cannot find {0}", combatant.Name);
|
||||
|
||||
Action = ActionType.Wander;
|
||||
|
||||
return true;
|
||||
}
|
||||
m_Mobile.DebugSay("I should be closer to {0}", combatant.Name);
|
||||
}
|
||||
|
||||
if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee)
|
||||
{
|
||||
var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax;
|
||||
|
||||
if (hitPercent < 0.1)
|
||||
{
|
||||
m_Mobile.DebugSay("I am low on health!");
|
||||
Action = ActionType.Flee;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionBackoff()
|
||||
{
|
||||
var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax;
|
||||
|
||||
if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // Less than 10% health
|
||||
{
|
||||
Action = ActionType.Flee;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception * 2, FightMode.Closest, true, false, true))
|
||||
{
|
||||
if (WalkMobileRange(m_Mobile.FocusMob, 1, false, m_Mobile.RangePerception, m_Mobile.RangePerception * 2))
|
||||
{
|
||||
m_Mobile.DebugSay("Well, here I am safe");
|
||||
Action = ActionType.Wander;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I have lost my focus, lets relax");
|
||||
Action = ActionType.Wander;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Scripts/Mobiles/AI/ArcherAI.cs
Normal file
121
Scripts/Mobiles/AI/ArcherAI.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class ArcherAI : BaseAI
|
||||
{
|
||||
public ArcherAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
m_Mobile.DebugSay("I have no combatant");
|
||||
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I have detected {0} and I will attack", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.DoActionWander();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
var c = m_Mobile.Combatant;
|
||||
|
||||
if (c == null || c.Deleted || !c.Alive || (c is Mobile && ((Mobile)c).IsDeadBondedPet))
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant is deleted");
|
||||
Action = ActionType.Guard;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Core.TickCount - m_Mobile.LastMoveTime > 1000)
|
||||
{
|
||||
if (WalkMobileRange(c, 1, true, m_Mobile.RangeFight, m_Mobile.Weapon.MaxRange))
|
||||
{
|
||||
if (!DirectionLocked)
|
||||
m_Mobile.Direction = m_Mobile.GetDirectionTo(c.Location);
|
||||
}
|
||||
else if (c != null)
|
||||
{
|
||||
m_Mobile.DebugSay("I am still not in range of {0}", c.Name);
|
||||
|
||||
if ((int)m_Mobile.GetDistanceToSqrt(c) > m_Mobile.RangePerception + 1)
|
||||
{
|
||||
m_Mobile.DebugSay("I have lost {0}", c.Name);
|
||||
|
||||
Action = ActionType.Guard;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee)
|
||||
{
|
||||
if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100)
|
||||
{
|
||||
// We are low on health, should we flee?
|
||||
if (Utility.Random(100) <= Math.Max(10, 10 + c.Hits - m_Mobile.Hits))
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to flee from {0}", c.Name);
|
||||
Action = ActionType.Flee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionGuard()
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionGuard();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionFlee()
|
||||
{
|
||||
var c = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if ( m_Mobile.Hits > (m_Mobile.HitsMax / 2))
|
||||
{
|
||||
// If I have a target, go back and fight them
|
||||
if (c != null && m_Mobile.GetDistanceToSqrt(c) <= m_Mobile.RangePerception * 2)
|
||||
{
|
||||
m_Mobile.DebugSay("I am stronger now, reengaging {0}", c.Name);
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I am stronger now, my guard is up");
|
||||
Action = ActionType.Guard;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionFlee();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
3127
Scripts/Mobiles/AI/BaseAI.cs
Normal file
3127
Scripts/Mobiles/AI/BaseAI.cs
Normal file
File diff suppressed because it is too large
Load Diff
82
Scripts/Mobiles/AI/BerserkAI.cs
Normal file
82
Scripts/Mobiles/AI/BerserkAI.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class BerserkAI : BaseAI
|
||||
{
|
||||
public BerserkAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
m_Mobile.DebugSay("I have No Combatant");
|
||||
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, FightMode.Closest, false, true, true))
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("I have detected " + m_Mobile.FocusMob.Name + " and I will attack");
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionWander();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
if (m_Mobile.Combatant == null || m_Mobile.Combatant.Deleted)
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant is deleted");
|
||||
Action = ActionType.Guard;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WalkMobileRange(m_Mobile.Combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
|
||||
{
|
||||
if (!DirectionLocked)
|
||||
m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant.Location);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Mobile.Combatant != null)
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("I am still not in range of " + m_Mobile.Combatant.Name);
|
||||
|
||||
if ((int)m_Mobile.GetDistanceToSqrt(m_Mobile.Combatant) > m_Mobile.RangePerception + 1)
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("I have lost " + m_Mobile.Combatant.Name);
|
||||
|
||||
Action = ActionType.Guard;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionGuard()
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, true, true))
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionGuard();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
177
Scripts/Mobiles/AI/HealerAI.cs
Normal file
177
Scripts/Mobiles/AI/HealerAI.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
#region References
|
||||
using Server.Spells.First;
|
||||
using Server.Spells.Fourth;
|
||||
using Server.Spells.Second;
|
||||
using Server.Targeting;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class HealerAI : BaseAI
|
||||
{
|
||||
private static readonly NeedDelegate m_Cure = NeedCure;
|
||||
private static readonly NeedDelegate m_GHeal = NeedGHeal;
|
||||
private static readonly NeedDelegate m_LHeal = NeedLHeal;
|
||||
private static readonly NeedDelegate[] m_ACure = {m_Cure};
|
||||
private static readonly NeedDelegate[] m_AGHeal = {m_GHeal};
|
||||
private static readonly NeedDelegate[] m_ALHeal = {m_LHeal};
|
||||
private static readonly NeedDelegate[] m_All = {m_Cure, m_GHeal, m_LHeal};
|
||||
|
||||
public HealerAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
private delegate bool NeedDelegate(Mobile m);
|
||||
|
||||
public override bool Think()
|
||||
{
|
||||
if (m_Mobile.Deleted)
|
||||
return false;
|
||||
|
||||
var targ = m_Mobile.Target;
|
||||
|
||||
if (targ != null)
|
||||
{
|
||||
if (targ is CureSpell.InternalTarget)
|
||||
{
|
||||
ProcessTarget(targ, m_ACure);
|
||||
}
|
||||
else if (targ is GreaterHealSpell.InternalTarget)
|
||||
{
|
||||
ProcessTarget(targ, m_AGHeal);
|
||||
}
|
||||
else if (targ is HealSpell.InternalTarget)
|
||||
{
|
||||
ProcessTarget(targ, m_ALHeal);
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.Cancel(m_Mobile, TargetCancelType.Canceled);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var toHelp = Find(m_All);
|
||||
|
||||
if (toHelp != null)
|
||||
{
|
||||
if (NeedCure(toHelp))
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("{0} needs a cure", toHelp.Name);
|
||||
|
||||
if (!(new CureSpell(m_Mobile, null)).Cast())
|
||||
new CureSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
else if (NeedGHeal(toHelp))
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("{0} needs a greater heal", toHelp.Name);
|
||||
|
||||
if (!(new GreaterHealSpell(m_Mobile, null)).Cast())
|
||||
new HealSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
else if (NeedLHeal(toHelp))
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("{0} needs a lesser heal", toHelp.Name);
|
||||
|
||||
new HealSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, FightMode.Weakest, false, true, false))
|
||||
{
|
||||
WalkMobileRange(m_Mobile.FocusMob, 1, false, 4, 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
WalkRandomInHome(3, 2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool NeedCure(Mobile m)
|
||||
{
|
||||
return m.Poisoned;
|
||||
}
|
||||
|
||||
private static bool NeedGHeal(Mobile m)
|
||||
{
|
||||
return m.Hits < m.HitsMax - 40;
|
||||
}
|
||||
|
||||
private static bool NeedLHeal(Mobile m)
|
||||
{
|
||||
return m.Hits < m.HitsMax - 10;
|
||||
}
|
||||
|
||||
private void ProcessTarget(Target targ, NeedDelegate[] func)
|
||||
{
|
||||
var toHelp = Find(func);
|
||||
|
||||
if (toHelp != null)
|
||||
{
|
||||
if (targ.Range != -1 && !m_Mobile.InRange(toHelp, targ.Range))
|
||||
{
|
||||
DoMove(m_Mobile.GetDirectionTo(toHelp) | Direction.Running);
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.Invoke(m_Mobile, toHelp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.Cancel(m_Mobile, TargetCancelType.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
private Mobile Find(params NeedDelegate[] funcs)
|
||||
{
|
||||
if (m_Mobile.Deleted)
|
||||
return null;
|
||||
|
||||
var map = m_Mobile.Map;
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
var prio = 0.0;
|
||||
Mobile found = null;
|
||||
IPooledEnumerable eable = m_Mobile.GetMobilesInRange(m_Mobile.RangePerception);
|
||||
|
||||
foreach (Mobile m in eable)
|
||||
{
|
||||
if (!m_Mobile.CanSee(m) || !(m is BaseCreature) || ((BaseCreature)m).Team != m_Mobile.Team)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < funcs.Length; ++i)
|
||||
{
|
||||
if (funcs[i](m))
|
||||
{
|
||||
var val = -m_Mobile.GetDistanceToSqrt(m);
|
||||
|
||||
if (found == null || val > prio)
|
||||
{
|
||||
prio = val;
|
||||
found = m;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
1606
Scripts/Mobiles/AI/Magical AI/MageAI.cs
Normal file
1606
Scripts/Mobiles/AI/Magical AI/MageAI.cs
Normal file
File diff suppressed because it is too large
Load Diff
172
Scripts/Mobiles/AI/Magical AI/MysticAI.cs
Normal file
172
Scripts/Mobiles/AI/Magical AI/MysticAI.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
#region References
|
||||
using Server.Spells;
|
||||
using Server.Spells.Mysticism;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class MysticAI : MageAI
|
||||
{
|
||||
public override SkillName CastSkill { get { return SkillName.Mysticism; } }
|
||||
|
||||
public override bool UsesMagery
|
||||
{
|
||||
get { return m_Mobile.Skills[SkillName.Magery].Base >= 20.0 && !m_Mobile.Controlled; }
|
||||
}
|
||||
|
||||
public MysticAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override Spell GetRandomDamageSpell()
|
||||
{
|
||||
if (UsesMagery && 0.5 > Utility.RandomDouble())
|
||||
{
|
||||
return base.GetRandomDamageSpell();
|
||||
}
|
||||
|
||||
var mana = m_Mobile.Mana;
|
||||
var select = 1;
|
||||
|
||||
if (mana >= 50)
|
||||
select = 5;
|
||||
else if (mana >= 20)
|
||||
select = 3;
|
||||
else if (mana >= 9)
|
||||
select = 2;
|
||||
|
||||
switch (Utility.Random(select))
|
||||
{
|
||||
case 0:
|
||||
return new NetherBoltSpell(m_Mobile, null);
|
||||
case 1:
|
||||
return new EagleStrikeSpell(m_Mobile, null);
|
||||
case 2:
|
||||
return new BombardSpell(m_Mobile, null);
|
||||
case 3:
|
||||
return new HailStormSpell(m_Mobile, null);
|
||||
case 4:
|
||||
return new NetherCycloneSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomCurseSpell()
|
||||
{
|
||||
if (UsesMagery && 0.5 > Utility.RandomDouble())
|
||||
{
|
||||
return base.GetRandomCurseSpell();
|
||||
}
|
||||
|
||||
var mana = m_Mobile.Mana;
|
||||
var select = 1;
|
||||
|
||||
if (mana >= 40)
|
||||
select = 4;
|
||||
else if (mana >= 14)
|
||||
select = 3;
|
||||
else if (mana >= 8)
|
||||
select = 2;
|
||||
|
||||
switch (Utility.Random(select))
|
||||
{
|
||||
case 0:
|
||||
return new PurgeMagicSpell(m_Mobile, null);
|
||||
case 1:
|
||||
return new SleepSpell(m_Mobile, null);
|
||||
case 2:
|
||||
return new MassSleepSpell(m_Mobile, null);
|
||||
case 3:
|
||||
return new SpellPlagueSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetHealSpell()
|
||||
{
|
||||
if (UsesMagery && 0.5 > Utility.RandomDouble())
|
||||
{
|
||||
return base.GetHealSpell();
|
||||
}
|
||||
|
||||
if (m_Mobile.Mana >= 20)
|
||||
return new CleansingWindsSpell(m_Mobile, null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetCureSpell()
|
||||
{
|
||||
if (UsesMagery)
|
||||
{
|
||||
return base.GetCureSpell();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomBuffSpell()
|
||||
{
|
||||
if (UsesMagery)
|
||||
{
|
||||
return base.GetRandomBuffSpell();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell RandomCombatSpell()
|
||||
{
|
||||
var spell = CheckCastHealingSpell();
|
||||
|
||||
if (spell != null)
|
||||
return spell;
|
||||
|
||||
switch (Utility.Random(6))
|
||||
{
|
||||
case 0: // Curse
|
||||
{
|
||||
m_Mobile.DebugSay("Cursing Thou!");
|
||||
spell = GetRandomCurseSpell();
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5: // damage
|
||||
{
|
||||
m_Mobile.DebugSay("Just doing damage");
|
||||
spell = GetRandomDamageSpell();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return spell;
|
||||
}
|
||||
|
||||
protected override bool ProcessTarget()
|
||||
{
|
||||
var t = m_Mobile.Target;
|
||||
|
||||
if (t == null)
|
||||
return false;
|
||||
|
||||
if (t is HailStormSpell.InternalTarget || t is NetherCycloneSpell.InternalTarget)
|
||||
{
|
||||
if (m_Mobile.Combatant != null && m_Mobile.InRange(m_Mobile.Combatant.Location, 8))
|
||||
{
|
||||
t.Invoke(m_Mobile, m_Mobile.Combatant);
|
||||
}
|
||||
else
|
||||
t.Invoke(m_Mobile, m_Mobile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.ProcessTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Scripts/Mobiles/AI/Magical AI/NecroAI.cs
Normal file
107
Scripts/Mobiles/AI/Magical AI/NecroAI.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
#region References
|
||||
using Server.Spells;
|
||||
using Server.Spells.Necromancy;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class NecroAI : MageAI
|
||||
{
|
||||
public override SkillName CastSkill { get { return SkillName.Necromancy; } }
|
||||
public override bool UsesMagery { get { return false; } }
|
||||
|
||||
public NecroAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override Spell GetRandomDamageSpell()
|
||||
{
|
||||
var mana = m_Mobile.Mana;
|
||||
var select = 1;
|
||||
|
||||
if (mana >= 29)
|
||||
select = 4;
|
||||
else if (mana >= 23)
|
||||
select = 3;
|
||||
else if (mana >= 17)
|
||||
select = 2;
|
||||
|
||||
switch (Utility.Random(select))
|
||||
{
|
||||
case 0:
|
||||
return new PainSpikeSpell(m_Mobile, null);
|
||||
case 1:
|
||||
return new PoisonStrikeSpell(m_Mobile, null);
|
||||
case 2:
|
||||
return new WitherSpell(m_Mobile, null);
|
||||
case 3:
|
||||
return new StrangleSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomSummonSpell()
|
||||
{
|
||||
if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.Mana >= 23)
|
||||
{
|
||||
return new AnimateDeadSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomCurseSpell()
|
||||
{
|
||||
var mana = m_Mobile.Mana;
|
||||
var select = 1;
|
||||
|
||||
if (mana >= 17)
|
||||
select = 5;
|
||||
else if (mana >= 13)
|
||||
select = 4;
|
||||
else if (mana >= 11)
|
||||
select = 3;
|
||||
|
||||
switch (Utility.Random(select))
|
||||
{
|
||||
case 0:
|
||||
return new CurseWeaponSpell(m_Mobile, null);
|
||||
case 1:
|
||||
Spell spell;
|
||||
|
||||
if (NecroMageAI.CheckCastCorpseSkin(m_Mobile))
|
||||
spell = new CorpseSkinSpell(m_Mobile, null);
|
||||
else
|
||||
spell = new CurseWeaponSpell(m_Mobile, null);
|
||||
|
||||
return spell;
|
||||
case 2:
|
||||
return new EvilOmenSpell(m_Mobile, null);
|
||||
case 3:
|
||||
return new BloodOathSpell(m_Mobile, null);
|
||||
case 4:
|
||||
return new MindRotSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetCureSpell()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomBuffSpell()
|
||||
{
|
||||
return new CurseWeaponSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
public override Spell GetHealSpell()
|
||||
{
|
||||
m_Mobile.UseSkill(SkillName.SpiritSpeak);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
190
Scripts/Mobiles/AI/Magical AI/NecromageAI.cs
Normal file
190
Scripts/Mobiles/AI/Magical AI/NecromageAI.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
#region References
|
||||
using Server.Spells;
|
||||
using Server.Spells.First;
|
||||
using Server.Spells.Fourth;
|
||||
using Server.Spells.Necromancy;
|
||||
using Server.Spells.Second;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class NecroMageAI : MageAI
|
||||
{
|
||||
/*private Mobile m_Animated;
|
||||
|
||||
public Mobile Animated
|
||||
{
|
||||
get { return m_Animated; }
|
||||
set { m_Animated = value; }
|
||||
}*/
|
||||
|
||||
public override SkillName CastSkill { get { return SkillName.Magery; } }
|
||||
|
||||
public NecroMageAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override Spell GetRandomDamageSpell()
|
||||
{
|
||||
if (0.5 > Utility.RandomDouble())
|
||||
{
|
||||
return base.GetRandomDamageSpell();
|
||||
}
|
||||
|
||||
var mana = m_Mobile.Mana;
|
||||
var select = 1;
|
||||
|
||||
if (mana >= 29)
|
||||
select = 4;
|
||||
else if (mana >= 23)
|
||||
select = 3;
|
||||
else if (mana >= 17)
|
||||
select = 2;
|
||||
|
||||
switch (Utility.Random(select))
|
||||
{
|
||||
case 0:
|
||||
return new PainSpikeSpell(m_Mobile, null);
|
||||
case 1:
|
||||
return new PoisonStrikeSpell(m_Mobile, null);
|
||||
case 2:
|
||||
return new WitherSpell(m_Mobile, null);
|
||||
case 3:
|
||||
return new StrangleSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomCurseSpell()
|
||||
{
|
||||
if (0.5 > Utility.RandomDouble())
|
||||
{
|
||||
return base.GetRandomCurseSpell();
|
||||
}
|
||||
|
||||
var mana = m_Mobile.Mana;
|
||||
var select = 1;
|
||||
|
||||
if (mana >= 17)
|
||||
select = 4;
|
||||
else if (mana >= 13)
|
||||
select = 3;
|
||||
else if (mana >= 11)
|
||||
select = 2;
|
||||
|
||||
switch (Utility.Random(select))
|
||||
{
|
||||
case 0:
|
||||
Spell spell;
|
||||
|
||||
if (CheckCastCorpseSkin(m_Mobile))
|
||||
spell = new CorpseSkinSpell(m_Mobile, null);
|
||||
else
|
||||
spell = new EvilOmenSpell(m_Mobile, null);
|
||||
|
||||
return spell;
|
||||
case 1:
|
||||
return new EvilOmenSpell(m_Mobile, null);
|
||||
case 2:
|
||||
return new BloodOathSpell(m_Mobile, null);
|
||||
case 3:
|
||||
return new MindRotSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomBuffSpell()
|
||||
{
|
||||
if (0.5 > Utility.RandomDouble())
|
||||
{
|
||||
return base.GetRandomBuffSpell();
|
||||
}
|
||||
|
||||
if (!SmartAI && Utility.RandomBool())
|
||||
{
|
||||
return new CurseWeaponSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return GetRandomSummonSpell();
|
||||
}
|
||||
|
||||
public override Spell GetRandomSummonSpell()
|
||||
{
|
||||
if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.Mana >= 23)
|
||||
{
|
||||
return new AnimateDeadSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override Spell CheckCastHealingSpell()
|
||||
{
|
||||
if (m_Mobile.Summoned || m_Mobile.Hits >= m_Mobile.HitsMax)
|
||||
return null;
|
||||
|
||||
if (0.1 > Utility.RandomDouble())
|
||||
m_Mobile.UseSkill(SkillName.SpiritSpeak);
|
||||
else
|
||||
return base.CheckCastHealingSpell();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override bool DoActionGuard()
|
||||
{
|
||||
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.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Mobile.Poisoned)
|
||||
{
|
||||
new CureSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
else if (!m_Mobile.Summoned)
|
||||
{
|
||||
if (ScaleBySkill(HealChance, SkillName.Necromancy) > Utility.RandomDouble() &&
|
||||
m_Mobile.Hits < m_Mobile.HitsMax - 30)
|
||||
{
|
||||
m_Mobile.UseSkill(SkillName.SpiritSpeak);
|
||||
}
|
||||
else if (ScaleBySkill(HealChance, SkillName.Magery) > Utility.RandomDouble())
|
||||
{
|
||||
if (m_Mobile.Hits < (m_Mobile.HitsMax - 50))
|
||||
{
|
||||
if (!new GreaterHealSpell(m_Mobile, null).Cast())
|
||||
new HealSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
else if (m_Mobile.Hits < (m_Mobile.HitsMax - 10))
|
||||
{
|
||||
new HealSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionGuard();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionGuard();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CheckCastCorpseSkin(BaseCreature bc)
|
||||
{
|
||||
return bc.ColdDamage != 100 && bc.PhysicalDamage != 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
202
Scripts/Mobiles/AI/Magical AI/NinjaAI.cs
Normal file
202
Scripts/Mobiles/AI/Magical AI/NinjaAI.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server.Items;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Ninjitsu;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class NinjaAI : MeleeAI
|
||||
{
|
||||
private DateTime m_NextCastTime;
|
||||
private DateTime m_NextRanged;
|
||||
|
||||
public NinjaAI(BaseCreature bc)
|
||||
: base(bc)
|
||||
{
|
||||
m_NextCastTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
private void TryPerformHide()
|
||||
{
|
||||
if (!m_Mobile.Alive || m_Mobile.Deleted)
|
||||
return;
|
||||
|
||||
if (!m_Mobile.Hidden && Core.TickCount - m_Mobile.NextSkillTime >= 0)
|
||||
{
|
||||
var chance = 0.05;
|
||||
|
||||
if (m_Mobile.Hits < 20)
|
||||
chance = 0.10;
|
||||
|
||||
if (m_Mobile.Poisoned)
|
||||
chance = 0.01;
|
||||
|
||||
if (Utility.RandomDouble() < chance)
|
||||
HideSelf();
|
||||
}
|
||||
}
|
||||
|
||||
private void HideSelf()
|
||||
{
|
||||
Effects.SendLocationParticles(
|
||||
EffectItem.Create(m_Mobile.Location, m_Mobile.Map, EffectItem.DefaultDuration),
|
||||
0x3728,
|
||||
10,
|
||||
10,
|
||||
2023);
|
||||
|
||||
m_Mobile.PlaySound(0x22F);
|
||||
m_Mobile.Hidden = true;
|
||||
|
||||
m_Mobile.UseSkill(SkillName.Stealth);
|
||||
}
|
||||
|
||||
public virtual SpecialMove GetHiddenSpecialMove()
|
||||
{
|
||||
var skill = (int)m_Mobile.Skills[SkillName.Ninjitsu].Value;
|
||||
|
||||
if (skill < 40)
|
||||
return null;
|
||||
|
||||
if (skill >= 60)
|
||||
{
|
||||
//return .5 > Utility.RandomDouble() ? new SupriseAttack() : new Backstab();
|
||||
return .5 > Utility.RandomDouble() ? SpellRegistry.GetSpecialMove(504) : SpellRegistry.GetSpecialMove(505);
|
||||
}
|
||||
|
||||
return SpellRegistry.GetSpecialMove(505); //new Backstab();
|
||||
}
|
||||
|
||||
public virtual SpecialMove GetSpecialMove()
|
||||
{
|
||||
var skill = (int)m_Mobile.Skills[SkillName.Ninjitsu].Value;
|
||||
|
||||
if (skill < 40)
|
||||
return null;
|
||||
|
||||
var avail = 1;
|
||||
|
||||
if (skill >= 85)
|
||||
avail = 3;
|
||||
else if (skill >= 80)
|
||||
avail = 2;
|
||||
|
||||
switch (Utility.Random(avail))
|
||||
{
|
||||
case 0:
|
||||
return SpellRegistry.GetSpecialMove(500); //new FocusAttack();
|
||||
case 1:
|
||||
return SpellRegistry.GetSpecialMove(503); //new KiAttack();
|
||||
case 2:
|
||||
return SpellRegistry.GetSpecialMove(501); //new DeathStrike();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DoRangedAttack()
|
||||
{
|
||||
var c = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if (c == null)
|
||||
return;
|
||||
|
||||
var list = new List<INinjaWeapon>();
|
||||
var d = (int)m_Mobile.GetDistanceToSqrt(c.Location);
|
||||
|
||||
foreach (var item in m_Mobile.Items)
|
||||
if (item is INinjaWeapon && ((INinjaWeapon)item).UsesRemaining > 0 && d >= ((INinjaWeapon)item).WeaponMinRange &&
|
||||
d <= ((INinjaWeapon)item).WeaponMaxRange)
|
||||
list.Add(item as INinjaWeapon);
|
||||
|
||||
if (m_Mobile.Backpack != null)
|
||||
{
|
||||
foreach (var item in m_Mobile.Backpack.Items)
|
||||
if (item is INinjaWeapon && ((INinjaWeapon)item).UsesRemaining > 0 && d >= ((INinjaWeapon)item).WeaponMinRange &&
|
||||
d <= ((INinjaWeapon)item).WeaponMaxRange)
|
||||
list.Add(item as INinjaWeapon);
|
||||
}
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
var toUse = list[Utility.Random(list.Count)];
|
||||
|
||||
if (toUse != null)
|
||||
NinjaWeapon.Shoot(m_Mobile, c, toUse);
|
||||
}
|
||||
|
||||
ColUtility.Free(list);
|
||||
|
||||
m_NextRanged = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(30, 120));
|
||||
}
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
base.DoActionWander();
|
||||
|
||||
if (m_Mobile.Combatant == null)
|
||||
TryPerformHide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
base.DoActionCombat();
|
||||
|
||||
if (m_Mobile.Combatant == null)
|
||||
return true;
|
||||
|
||||
var special = SpecialMove.GetCurrentMove(m_Mobile);
|
||||
|
||||
if (special == null && m_NextCastTime < DateTime.UtcNow && 0.05 > Utility.RandomDouble())
|
||||
{
|
||||
if (0.05 > Utility.RandomDouble())
|
||||
{
|
||||
new MirrorImage(m_Mobile, null).Cast();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Mobile.Hidden)
|
||||
special = GetHiddenSpecialMove();
|
||||
else
|
||||
special = GetSpecialMove();
|
||||
|
||||
if (special != null)
|
||||
{
|
||||
SpecialMove.SetCurrentMove(m_Mobile, special);
|
||||
m_NextCastTime = DateTime.UtcNow + GetCastDelay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_NextRanged < DateTime.UtcNow && 0.08 > Utility.RandomDouble())
|
||||
DoRangedAttack();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionFlee()
|
||||
{
|
||||
base.DoActionFlee();
|
||||
TryPerformHide();
|
||||
return true;
|
||||
}
|
||||
|
||||
public TimeSpan GetCastDelay()
|
||||
{
|
||||
var skill = (int)m_Mobile.Skills[SkillName.Ninjitsu].Value;
|
||||
|
||||
if (skill >= 85)
|
||||
return TimeSpan.FromSeconds(15);
|
||||
if (skill > 40)
|
||||
return TimeSpan.FromSeconds(30);
|
||||
|
||||
return TimeSpan.FromSeconds(45);
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Scripts/Mobiles/AI/Magical AI/PaladinAI.cs
Normal file
85
Scripts/Mobiles/AI/Magical AI/PaladinAI.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
#region References
|
||||
using Server.Spells;
|
||||
using Server.Spells.Chivalry;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class PaladinAI : MageAI
|
||||
{
|
||||
public override SkillName CastSkill { get { return SkillName.Chivalry; } }
|
||||
public override bool UsesMagery { get { return false; } }
|
||||
public override double HealChance { get { return .1; } }
|
||||
|
||||
public PaladinAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override Spell GetRandomDamageSpell()
|
||||
{
|
||||
if (m_Mobile.Mana > 10 && 0.1 > Utility.RandomDouble())
|
||||
return new HolyLightSpell(m_Mobile, null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomCurseSpell()
|
||||
{
|
||||
if (m_Mobile.Mana > 10)
|
||||
return new DispelEvilSpell(m_Mobile, null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomBuffSpell()
|
||||
{
|
||||
var mana = m_Mobile.Mana;
|
||||
var select = 1;
|
||||
|
||||
if (mana >= 15)
|
||||
select = 3;
|
||||
|
||||
if (mana >= 20 && !EnemyOfOneSpell.UnderEffect(m_Mobile))
|
||||
select = 4;
|
||||
|
||||
switch (Utility.Random(select))
|
||||
{
|
||||
case 0:
|
||||
return new RemoveCurseSpell(m_Mobile, null);
|
||||
case 1:
|
||||
return new DivineFurySpell(m_Mobile, null);
|
||||
case 2:
|
||||
return new ConsecrateWeaponSpell(m_Mobile, null);
|
||||
case 3:
|
||||
return new EnemyOfOneSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return new ConsecrateWeaponSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
public override Spell GetHealSpell()
|
||||
{
|
||||
if (m_Mobile.Mana > 10)
|
||||
return new CloseWoundsSpell(m_Mobile, null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetCureSpell()
|
||||
{
|
||||
if (m_Mobile.Mana > 10)
|
||||
return new CleanseByFireSpell(m_Mobile, null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override bool ProcessTarget()
|
||||
{
|
||||
if (m_Mobile.Target == null)
|
||||
return false;
|
||||
|
||||
m_Mobile.Target.Invoke(m_Mobile, m_Mobile);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
129
Scripts/Mobiles/AI/Magical AI/SamuraiAI.cs
Normal file
129
Scripts/Mobiles/AI/Magical AI/SamuraiAI.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server.Spells;
|
||||
using Server.Spells.Bushido;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class SamuraiAI : MeleeAI
|
||||
{
|
||||
private DateTime m_NextCastTime;
|
||||
private DateTime m_NextSpecial;
|
||||
|
||||
public SamuraiAI(BaseCreature bc)
|
||||
: base(bc)
|
||||
{
|
||||
m_NextCastTime = DateTime.UtcNow;
|
||||
m_NextSpecial = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public virtual SpecialMove GetSpecialMove()
|
||||
{
|
||||
var skill = (int)m_Mobile.Skills[SkillName.Bushido].Value;
|
||||
|
||||
if (skill <= 50)
|
||||
return null;
|
||||
|
||||
if (m_Mobile.Combatant != null && m_Mobile.Combatant.Hits <= 10 && skill >= 25)
|
||||
return SpellRegistry.GetSpecialMove(400); //new HonerableExecution();
|
||||
if (skill >= 70 && CheckForMomentumStrike() && 0.5 > Utility.RandomDouble())
|
||||
return SpellRegistry.GetSpecialMove(405); //new MomentumStrike();
|
||||
return SpellRegistry.GetSpecialMove(404); // new LightningStrike();
|
||||
}
|
||||
|
||||
private bool CheckForMomentumStrike()
|
||||
{
|
||||
var count = 0;
|
||||
IPooledEnumerable eable = m_Mobile.GetMobilesInRange(1);
|
||||
|
||||
foreach (Mobile m in eable)
|
||||
{
|
||||
if (m.CanBeHarmful(m_Mobile) && m != m_Mobile)
|
||||
count++;
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
return count > 1;
|
||||
}
|
||||
|
||||
public virtual Spell GetRandomSpell()
|
||||
{
|
||||
// 25 - Confidence
|
||||
// 40 - Counter Attack
|
||||
// 60 - Evasion
|
||||
var skill = (int)m_Mobile.Skills[SkillName.Bushido].Value;
|
||||
|
||||
if (skill < 25)
|
||||
return null;
|
||||
|
||||
var avail = 1;
|
||||
|
||||
if (skill >= 60)
|
||||
avail = 3;
|
||||
else if (skill >= 40)
|
||||
avail = 2;
|
||||
|
||||
switch (Utility.Random(avail))
|
||||
{
|
||||
case 0:
|
||||
return new Confidence(m_Mobile, null);
|
||||
case 1:
|
||||
return new CounterAttack(m_Mobile, null);
|
||||
case 2:
|
||||
return new Evasion(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
base.DoActionCombat();
|
||||
|
||||
var c = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if (c != null)
|
||||
{
|
||||
var move = SpecialMove.GetCurrentMove(m_Mobile);
|
||||
|
||||
if (move == null && m_NextSpecial < DateTime.UtcNow && 0.05 > Utility.RandomDouble())
|
||||
{
|
||||
move = GetSpecialMove();
|
||||
|
||||
if (move != null)
|
||||
{
|
||||
SpecialMove.SetCurrentMove(m_Mobile, move);
|
||||
m_NextSpecial = DateTime.UtcNow + GetCastDelay();
|
||||
}
|
||||
}
|
||||
else if (m_Mobile.Spell == null && m_NextCastTime < DateTime.UtcNow && 0.05 > Utility.RandomDouble())
|
||||
{
|
||||
var spell = GetRandomSpell();
|
||||
|
||||
if (spell != null)
|
||||
{
|
||||
spell.Cast();
|
||||
m_NextCastTime = DateTime.UtcNow + GetCastDelay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public TimeSpan GetCastDelay()
|
||||
{
|
||||
var skill = (int)m_Mobile.Skills[SkillName.Bushido].Value;
|
||||
|
||||
if (skill >= 60)
|
||||
return TimeSpan.FromSeconds(15);
|
||||
if (skill > 25)
|
||||
return TimeSpan.FromSeconds(30);
|
||||
|
||||
return TimeSpan.FromSeconds(45);
|
||||
}
|
||||
}
|
||||
}
|
||||
820
Scripts/Mobiles/AI/Magical AI/SpellbinderAI.cs
Normal file
820
Scripts/Mobiles/AI/Magical AI/SpellbinderAI.cs
Normal file
@@ -0,0 +1,820 @@
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server.Spells;
|
||||
using Server.Spells.Fifth;
|
||||
using Server.Spells.First;
|
||||
using Server.Spells.Fourth;
|
||||
using Server.Spells.Necromancy;
|
||||
using Server.Spells.Second;
|
||||
using Server.Spells.Seventh;
|
||||
using Server.Spells.Sixth;
|
||||
using Server.Spells.Third;
|
||||
using Server.Targeting;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class SpellbinderAI : BaseAI
|
||||
{
|
||||
private static readonly int[] m_Offsets =
|
||||
{
|
||||
-1, -1, -1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 0, 1, 1, -2, -2, -2, -1, -2, 0, -2, 1, -2, 2, -1, -2, -1, 2, 0, -2, 0,
|
||||
2, 1, -2, 1, 2, 2, -2, 2, -1, 2, 0, 2, 1, 2, 2
|
||||
};
|
||||
|
||||
private const double HealChance = 0.05; // 5% chance to heal at gm necromancy, uses spirit speak healing
|
||||
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
|
||||
private DateTime m_NextCastTime;
|
||||
private DateTime m_NextHealTime = DateTime.UtcNow;
|
||||
|
||||
public SpellbinderAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override bool Think()
|
||||
{
|
||||
if (m_Mobile.Deleted)
|
||||
return false;
|
||||
|
||||
var targ = m_Mobile.Target;
|
||||
|
||||
if (targ != null)
|
||||
{
|
||||
ProcessTarget(targ);
|
||||
|
||||
return true;
|
||||
}
|
||||
return base.Think();
|
||||
}
|
||||
|
||||
public virtual double ScaleByNecromancy(double v)
|
||||
{
|
||||
return m_Mobile.Skills[SkillName.Necromancy].Value * v * 0.01;
|
||||
}
|
||||
|
||||
public virtual double ScaleByMagery(double v)
|
||||
{
|
||||
return m_Mobile.Skills[SkillName.Magery].Value * v * 0.01;
|
||||
}
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to attack {0}", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
m_NextCastTime = DateTime.UtcNow;
|
||||
}
|
||||
else if (m_Mobile.Mana < m_Mobile.ManaMax)
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to meditate");
|
||||
|
||||
m_Mobile.UseSkill(SkillName.Meditation);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I am wandering");
|
||||
|
||||
m_Mobile.Warmode = false;
|
||||
|
||||
base.DoActionWander();
|
||||
|
||||
if (m_Mobile.Poisoned)
|
||||
{
|
||||
new CureSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
else if (!m_Mobile.Summoned)
|
||||
{
|
||||
if (m_Mobile.Hits < (m_Mobile.HitsMax - 50))
|
||||
{
|
||||
if (!new GreaterHealSpell(m_Mobile, null).Cast())
|
||||
new HealSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
else if (m_Mobile.Hits < (m_Mobile.HitsMax - 10))
|
||||
{
|
||||
new HealSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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(IDamageable m)
|
||||
{
|
||||
Run((Direction)((int)m_Mobile.GetDirectionTo(m) - 4) & Direction.Mask);
|
||||
}
|
||||
|
||||
public void OnFailedMove()
|
||||
{
|
||||
if (!m_Mobile.DisallowAllMoves && ScaleByMagery(TeleportChance) > Utility.RandomDouble())
|
||||
{
|
||||
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))
|
||||
{
|
||||
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.CanMove || m_Mobile.Paralyzed ||
|
||||
m_Mobile.Frozen || m_Mobile.DisallowAllMoves)
|
||||
return;
|
||||
|
||||
m_Mobile.Direction = d | Direction.Running;
|
||||
|
||||
if (!DoMove(m_Mobile.Direction, true))
|
||||
OnFailedMove();
|
||||
}
|
||||
|
||||
public virtual Spell GetRandomCurseSpell()
|
||||
{
|
||||
var necro = (int)((m_Mobile.Skills[SkillName.Necromancy].Value + 50.0) / (100.0 / 7.0));
|
||||
var mage = (int)((m_Mobile.Skills[SkillName.Magery].Value + 50.0) / (100.0 / 7.0));
|
||||
|
||||
if (mage < 1)
|
||||
mage = 1;
|
||||
|
||||
if (necro < 1)
|
||||
necro = 1;
|
||||
|
||||
if (m_Mobile.Skills[SkillName.Necromancy].Value > 30 && Utility.Random(necro) > Utility.Random(mage))
|
||||
{
|
||||
switch (Utility.Random(necro - 5))
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
return new CorpseSkinSpell(m_Mobile, null);
|
||||
case 2:
|
||||
case 3:
|
||||
return new MindRotSpell(m_Mobile, null);
|
||||
default:
|
||||
return new MindRotSpell(m_Mobile, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (Utility.RandomBool() && mage > 3)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Spell GetRandomManaDrainSpell()
|
||||
{
|
||||
if (Utility.RandomBool())
|
||||
{
|
||||
if (m_Mobile.Skills[SkillName.Magery].Value >= 80.0)
|
||||
return new ManaVampireSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return new ManaDrainSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
public virtual Spell DoDispel(Mobile toDispel)
|
||||
{
|
||||
if (ScaleByMagery(DispelChance) > Utility.RandomDouble())
|
||||
return new DispelSpell(m_Mobile, null);
|
||||
|
||||
return ChooseSpell(toDispel);
|
||||
}
|
||||
|
||||
public virtual Spell ChooseSpell(IDamageable c)
|
||||
{
|
||||
var spell = CheckCastHealingSpell();
|
||||
|
||||
if (spell != null)
|
||||
return spell;
|
||||
|
||||
if (!(c is Mobile))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var mob = c as Mobile;
|
||||
var damage = ((m_Mobile.Skills[SkillName.SpiritSpeak].Value - mob.Skills[SkillName.MagicResist].Value) / 10) +
|
||||
(mob.Player ? 18 : 30);
|
||||
|
||||
if (damage > c.Hits)
|
||||
spell = new ManaDrainSpell(m_Mobile, null);
|
||||
|
||||
switch (Utility.Random(16))
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2: // Poison them
|
||||
{
|
||||
m_Mobile.DebugSay("Attempting to BloodOath");
|
||||
|
||||
if (!mob.Poisoned)
|
||||
spell = new BloodOathSpell(m_Mobile, null);
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: // Bless ourselves.
|
||||
{
|
||||
m_Mobile.DebugSay("Blessing myself");
|
||||
|
||||
spell = new BlessSpell(m_Mobile, null);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
case 5:
|
||||
case 6: // Curse them.
|
||||
{
|
||||
m_Mobile.DebugSay("Attempting to curse");
|
||||
|
||||
spell = GetRandomCurseSpell();
|
||||
break;
|
||||
}
|
||||
case 7: // Paralyze them.
|
||||
{
|
||||
m_Mobile.DebugSay("Attempting to paralyze");
|
||||
|
||||
if (m_Mobile.Skills[SkillName.Magery].Value > 50.0)
|
||||
spell = new ParalyzeSpell(m_Mobile, null);
|
||||
|
||||
break;
|
||||
}
|
||||
case 8: // Drain mana
|
||||
{
|
||||
m_Mobile.DebugSay("Attempting to drain mana");
|
||||
|
||||
spell = GetRandomManaDrainSpell();
|
||||
break;
|
||||
}
|
||||
case 9: // Blood oath them
|
||||
{
|
||||
m_Mobile.DebugSay("Attempting to blood oath");
|
||||
|
||||
if (m_Mobile.Skills[SkillName.Necromancy].Value > 30 && BloodOathSpell.GetBloodOath(mob) != m_Mobile)
|
||||
spell = new BloodOathSpell(m_Mobile, null);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return spell;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
var c = m_Mobile.Combatant;
|
||||
m_Mobile.Warmode = true;
|
||||
|
||||
if (c == null || c.Deleted || !c.Alive || (c is Mobile && ((Mobile)c).IsDeadBondedPet) || !m_Mobile.CanSee(c) ||
|
||||
!m_Mobile.CanBeHarmful(c, false) || c.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.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;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("Something happened to my combatant, and nothing is around. I am on guard.");
|
||||
Action = ActionType.Guard;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_Mobile.InLOS(c))
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.Combatant = c = m_Mobile.FocusMob;
|
||||
m_Mobile.FocusMob = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_Mobile.StunReady && m_Mobile.Skills[SkillName.Wrestling].Value >= 80.0 &&
|
||||
m_Mobile.Skills[SkillName.Anatomy].Value >= 80.0)
|
||||
EventSink.InvokeStunRequest(new StunRequestEventArgs(m_Mobile));
|
||||
|
||||
if (!m_Mobile.InRange(c, m_Mobile.RangePerception))
|
||||
{
|
||||
// They are somewhat far away, can we find something else?
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
m_Mobile.FocusMob = null;
|
||||
}
|
||||
else if (!m_Mobile.InRange(c, m_Mobile.RangePerception * 3))
|
||||
{
|
||||
m_Mobile.Combatant = null;
|
||||
}
|
||||
|
||||
c = m_Mobile.Combatant;
|
||||
|
||||
if (c == null)
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant has fled, so I am on guard");
|
||||
Action = ActionType.Guard;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee)
|
||||
{
|
||||
if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100)
|
||||
{
|
||||
// We are low on health, should we flee?
|
||||
if (Utility.Random(100) <= Math.Max(10, 10 + c.Hits - m_Mobile.Hits))
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to flee from {0}", c.Name);
|
||||
Action = ActionType.Flee;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Mobile.Spell == null && DateTime.UtcNow > m_NextCastTime && m_Mobile.InRange(c, 12))
|
||||
{
|
||||
// We are ready to cast a spell
|
||||
Spell spell = null;
|
||||
var toDispel = FindDispelTarget(true);
|
||||
|
||||
if (m_Mobile.Poisoned) // Top cast priority is cure
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to cure myself");
|
||||
|
||||
spell = new CureSpell(m_Mobile, null);
|
||||
}
|
||||
else if (toDispel != null) // Something dispellable is attacking us
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to dispel {0}", toDispel);
|
||||
|
||||
spell = DoDispel(toDispel);
|
||||
}
|
||||
else if (c is Mobile && (((Mobile)c).Spell is HealSpell || ((Mobile)c).Spell is GreaterHealSpell) &&
|
||||
!((Mobile)c).Poisoned) // They have a heal spell out
|
||||
{
|
||||
spell = new BloodOathSpell(m_Mobile, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
spell = ChooseSpell(c);
|
||||
}
|
||||
|
||||
// Now we have a spell picked
|
||||
// Move first before casting
|
||||
|
||||
if (toDispel != null)
|
||||
{
|
||||
if (m_Mobile.InRange(toDispel, 10))
|
||||
RunFrom(toDispel);
|
||||
else if (!m_Mobile.InRange(toDispel, 12))
|
||||
RunTo(toDispel);
|
||||
}
|
||||
else if (c is Mobile)
|
||||
{
|
||||
RunTo((Mobile)c);
|
||||
}
|
||||
|
||||
if (spell != null)
|
||||
spell.Cast();
|
||||
|
||||
TimeSpan delay;
|
||||
|
||||
if (spell is DispelSpell)
|
||||
delay = TimeSpan.FromSeconds(m_Mobile.ActiveSpeed);
|
||||
else
|
||||
delay = GetDelay();
|
||||
|
||||
m_NextCastTime = DateTime.UtcNow + delay;
|
||||
}
|
||||
else if (c is Mobile && (m_Mobile.Spell == null || !m_Mobile.Spell.IsCasting))
|
||||
{
|
||||
RunTo((Mobile)c);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionGuard()
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to attack {0}", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Mobile.Poisoned)
|
||||
{
|
||||
new CureSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
else if (!m_Mobile.Summoned && ((ScaleByMagery(HealChance) > Utility.RandomDouble())))
|
||||
{
|
||||
if (m_Mobile.Hits < (m_Mobile.HitsMax - 50))
|
||||
{
|
||||
if (!new GreaterHealSpell(m_Mobile, null).Cast())
|
||||
new HealSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
else if (m_Mobile.Hits < (m_Mobile.HitsMax - 10))
|
||||
{
|
||||
new HealSpell(m_Mobile, null).Cast();
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionGuard();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionGuard();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionFlee()
|
||||
{
|
||||
var c = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if ((m_Mobile.Mana > 20 || m_Mobile.Mana == m_Mobile.ManaMax) && m_Mobile.Hits > (m_Mobile.HitsMax / 2))
|
||||
{
|
||||
// If I have a target, go back and fight them
|
||||
if (c != null && m_Mobile.GetDistanceToSqrt(c) <= m_Mobile.RangePerception * 2)
|
||||
{
|
||||
m_Mobile.DebugSay("I am stronger now, reengaging {0}", c.Name);
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I am stronger now, my guard is up");
|
||||
Action = ActionType.Guard;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionFlee();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
var activePrio = 0.0;
|
||||
|
||||
var 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 (var i = 0; i < aggressed.Count; ++i)
|
||||
{
|
||||
var info = aggressed[i];
|
||||
var m = info.Defender;
|
||||
|
||||
if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, 12) && CanDispel(m))
|
||||
{
|
||||
var prio = m_Mobile.GetDistanceToSqrt(m);
|
||||
|
||||
if (active == null || prio < activePrio)
|
||||
{
|
||||
active = m;
|
||||
activePrio = prio;
|
||||
|
||||
if (activePrio <= 2)
|
||||
return active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < aggressors.Count; ++i)
|
||||
{
|
||||
var info = aggressors[i];
|
||||
var m = info.Attacker;
|
||||
|
||||
if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, 12) && CanDispel(m))
|
||||
{
|
||||
var prio = m_Mobile.GetDistanceToSqrt(m);
|
||||
|
||||
if (active == null || prio < activePrio)
|
||||
{
|
||||
active = m;
|
||||
activePrio = prio;
|
||||
|
||||
if (activePrio <= 2)
|
||||
return active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
var map = m_Mobile.Map;
|
||||
|
||||
if (map != null)
|
||||
{
|
||||
Mobile active = null, inactive = null;
|
||||
double actPrio = 0.0, inactPrio = 0.0;
|
||||
|
||||
var 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))
|
||||
{
|
||||
var 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);
|
||||
}
|
||||
|
||||
private Spell CheckCastHealingSpell()
|
||||
{
|
||||
// If I'm poisoned, always attempt to cure.
|
||||
if (m_Mobile.Poisoned)
|
||||
return new CureSpell(m_Mobile, null);
|
||||
|
||||
// Summoned creatures never heal themselves.
|
||||
if (m_Mobile.Summoned)
|
||||
return null;
|
||||
|
||||
if (m_Mobile.Controlled)
|
||||
{
|
||||
if (DateTime.UtcNow < m_NextHealTime)
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ScaleByMagery(HealChance) < Utility.RandomDouble())
|
||||
return null;
|
||||
|
||||
Spell spell = null;
|
||||
|
||||
if (m_Mobile.Hits < (m_Mobile.HitsMax - 50))
|
||||
{
|
||||
spell = new GreaterHealSpell(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;
|
||||
|
||||
if (m_Mobile.Int >= 500)
|
||||
delay = Utility.RandomMinMax(7, 10);
|
||||
else
|
||||
delay = Math.Sqrt(600 - m_Mobile.Int);
|
||||
|
||||
m_Mobile.UseSkill(SkillName.SpiritSpeak);
|
||||
|
||||
m_NextHealTime = DateTime.UtcNow + TimeSpan.FromSeconds(delay);
|
||||
|
||||
return spell;
|
||||
}
|
||||
|
||||
private TimeSpan GetDelay()
|
||||
{
|
||||
var del = ScaleByMagery(3.0);
|
||||
var min = 6.0 - (del * 0.75);
|
||||
var max = 6.0 - (del * 1.25);
|
||||
|
||||
return TimeSpan.FromSeconds(min + ((max - min) * Utility.RandomDouble()));
|
||||
}
|
||||
|
||||
private void ProcessTarget(Target targ)
|
||||
{
|
||||
var isDispel = (targ is DispelSpell.InternalTarget);
|
||||
var isParalyze = (targ is ParalyzeSpell.InternalTarget);
|
||||
var isTeleport = (targ is TeleportSpell.InternalTarget);
|
||||
var teleportAway = false;
|
||||
|
||||
Mobile toTarget;
|
||||
|
||||
if (isDispel)
|
||||
{
|
||||
toTarget = FindDispelTarget(false);
|
||||
|
||||
if (toTarget != null && m_Mobile.InRange(toTarget, 10))
|
||||
RunFrom(toTarget);
|
||||
}
|
||||
else if (isParalyze || isTeleport)
|
||||
{
|
||||
toTarget = FindDispelTarget(true);
|
||||
|
||||
if (toTarget == null)
|
||||
{
|
||||
toTarget = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if (toTarget != null)
|
||||
RunTo(toTarget);
|
||||
}
|
||||
else if (m_Mobile.InRange(toTarget, 10))
|
||||
{
|
||||
RunFrom(toTarget);
|
||||
teleportAway = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
teleportAway = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
toTarget = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if (toTarget != null)
|
||||
RunTo(toTarget);
|
||||
}
|
||||
|
||||
if ((targ.Flags & TargetFlags.Harmful) != 0 && toTarget != null)
|
||||
{
|
||||
if ((targ.Range == -1 || m_Mobile.InRange(toTarget, targ.Range)) && m_Mobile.CanSee(toTarget) &&
|
||||
m_Mobile.InLOS(toTarget))
|
||||
{
|
||||
targ.Invoke(m_Mobile, toTarget);
|
||||
}
|
||||
else if (isDispel)
|
||||
{
|
||||
targ.Cancel(m_Mobile, TargetCancelType.Canceled);
|
||||
}
|
||||
}
|
||||
else if ((targ.Flags & TargetFlags.Beneficial) != 0)
|
||||
{
|
||||
targ.Invoke(m_Mobile, m_Mobile);
|
||||
}
|
||||
else if (isTeleport && toTarget != null)
|
||||
{
|
||||
var map = m_Mobile.Map;
|
||||
|
||||
if (map == null)
|
||||
{
|
||||
targ.Cancel(m_Mobile, TargetCancelType.Canceled);
|
||||
return;
|
||||
}
|
||||
|
||||
int px, py;
|
||||
|
||||
if (teleportAway)
|
||||
{
|
||||
var rx = m_Mobile.X - toTarget.X;
|
||||
var ry = m_Mobile.Y - toTarget.Y;
|
||||
|
||||
var d = m_Mobile.GetDistanceToSqrt(toTarget);
|
||||
|
||||
px = toTarget.X + (int)(rx * (10 / d));
|
||||
py = toTarget.Y + (int)(ry * (10 / d));
|
||||
}
|
||||
else
|
||||
{
|
||||
px = toTarget.X;
|
||||
py = toTarget.Y;
|
||||
}
|
||||
|
||||
for (var i = 0; i < m_Offsets.Length; i += 2)
|
||||
{
|
||||
int x = m_Offsets[i], y = m_Offsets[i + 1];
|
||||
|
||||
var p = new Point3D(px + x, py + y, 0);
|
||||
|
||||
var lt = new LandTarget(p, map);
|
||||
|
||||
if ((targ.Range == -1 || m_Mobile.InRange(p, targ.Range)) && m_Mobile.InLOS(lt) &&
|
||||
map.CanSpawnMobile(px + x, py + y, lt.Z) && !SpellHelper.CheckMulti(p, map))
|
||||
{
|
||||
targ.Invoke(m_Mobile, lt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var teleRange = targ.Range;
|
||||
|
||||
if (teleRange < 0)
|
||||
teleRange = 12;
|
||||
|
||||
for (var i = 0; i < 10; ++i)
|
||||
{
|
||||
var randomPoint = new Point3D(
|
||||
m_Mobile.X - teleRange + Utility.Random(teleRange * 2 + 1),
|
||||
m_Mobile.Y - teleRange + Utility.Random(teleRange * 2 + 1),
|
||||
0);
|
||||
|
||||
var lt = new LandTarget(randomPoint, map);
|
||||
|
||||
if (m_Mobile.InLOS(lt) && map.CanSpawnMobile(lt.X, lt.Y, lt.Z) && !SpellHelper.CheckMulti(randomPoint, map))
|
||||
{
|
||||
targ.Invoke(m_Mobile, new LandTarget(randomPoint, map));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
targ.Cancel(m_Mobile, TargetCancelType.Canceled);
|
||||
}
|
||||
else
|
||||
{
|
||||
targ.Cancel(m_Mobile, TargetCancelType.Canceled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
126
Scripts/Mobiles/AI/Magical AI/SpellweavingAI.cs
Normal file
126
Scripts/Mobiles/AI/Magical AI/SpellweavingAI.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
#region References
|
||||
using Server.Items;
|
||||
using Server.Spells;
|
||||
using Server.Spells.Spellweaving;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class SpellweavingAI : MageAI
|
||||
{
|
||||
public override SkillName CastSkill { get { return SkillName.Spellweaving; } }
|
||||
|
||||
public override bool UsesMagery
|
||||
{
|
||||
get { return m_Mobile.Skills[SkillName.Magery].Base >= 20.0 && !m_Mobile.Controlled; }
|
||||
}
|
||||
|
||||
public SpellweavingAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override Spell GetRandomBuffSpell()
|
||||
{
|
||||
if (UsesMagery && 0.5 > Utility.RandomDouble())
|
||||
{
|
||||
return base.GetRandomBuffSpell();
|
||||
}
|
||||
|
||||
var mana = m_Mobile.Mana;
|
||||
var wep = m_Mobile.Weapon as BaseWeapon;
|
||||
|
||||
if (mana >= 50 && !ArcaneEmpowermentSpell.IsUnderEffects(m_Mobile) && 0.5 >= Utility.RandomDouble())
|
||||
return new ArcaneEmpowermentSpell(m_Mobile, null);
|
||||
if (mana >= 32 && wep != null && !ImmolatingWeaponSpell.IsImmolating(m_Mobile, wep))
|
||||
return new ImmolatingWeaponSpell(m_Mobile, null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomCurseSpell()
|
||||
{
|
||||
if (UsesMagery)
|
||||
{
|
||||
return base.GetRandomCurseSpell();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetRandomDamageSpell()
|
||||
{
|
||||
if (UsesMagery && 0.5 > Utility.RandomDouble())
|
||||
{
|
||||
return base.GetRandomDamageSpell();
|
||||
}
|
||||
|
||||
var mana = m_Mobile.Mana;
|
||||
var select = 1;
|
||||
|
||||
if (mana >= 50)
|
||||
select = 4;
|
||||
else if (mana >= 40)
|
||||
select = 3;
|
||||
else if (mana >= 30)
|
||||
select = 2;
|
||||
|
||||
switch (Utility.Random(select))
|
||||
{
|
||||
case 0:
|
||||
return new ThunderstormSpell(m_Mobile, null);
|
||||
case 1:
|
||||
return new EssenceOfWindSpell(m_Mobile, null);
|
||||
case 2:
|
||||
return new WildfireSpell(m_Mobile, null);
|
||||
case 3:
|
||||
return new WordOfDeathSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetHealSpell()
|
||||
{
|
||||
if (UsesMagery && 0.5 > Utility.RandomDouble())
|
||||
{
|
||||
return base.GetHealSpell();
|
||||
}
|
||||
|
||||
if (m_Mobile.Mana >= 24)
|
||||
{
|
||||
return new GiftOfRenewalSpell(m_Mobile, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Spell GetCureSpell()
|
||||
{
|
||||
if (UsesMagery)
|
||||
{
|
||||
return base.GetCureSpell();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override bool ProcessTarget()
|
||||
{
|
||||
var t = m_Mobile.Target;
|
||||
|
||||
if (t is WildfireSpell.InternalTarget)
|
||||
{
|
||||
if (m_Mobile.Combatant != null && m_Mobile.InRange(m_Mobile.Combatant.Location, 8))
|
||||
{
|
||||
t.Invoke(m_Mobile, m_Mobile.Combatant);
|
||||
}
|
||||
else
|
||||
t.Invoke(m_Mobile, m_Mobile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.ProcessTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
155
Scripts/Mobiles/AI/MeleeAI.cs
Normal file
155
Scripts/Mobiles/AI/MeleeAI.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
#region References
|
||||
using System;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class MeleeAI : BaseAI
|
||||
{
|
||||
public MeleeAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
m_Mobile.DebugSay("I have no combatant");
|
||||
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionWander();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
var c = m_Mobile.Combatant;
|
||||
|
||||
if (c == null || c.Deleted || c.Map != m_Mobile.Map || !c.Alive || (c is Mobile && ((Mobile)c).IsDeadBondedPet))
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant is gone, so my guard is up");
|
||||
|
||||
Action = ActionType.Guard;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_Mobile.InRange(c, m_Mobile.RangePerception))
|
||||
{
|
||||
// They are somewhat far away, can we find something else?
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
m_Mobile.FocusMob = null;
|
||||
}
|
||||
else if (!m_Mobile.InRange(c, m_Mobile.RangePerception * 3))
|
||||
{
|
||||
m_Mobile.Combatant = null;
|
||||
}
|
||||
|
||||
c = m_Mobile.Combatant;
|
||||
|
||||
if (c == null)
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant has fled, so I am on guard");
|
||||
Action = ActionType.Guard;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (MoveTo(c, true, m_Mobile.RangeFight))
|
||||
{
|
||||
if (!DirectionLocked)
|
||||
m_Mobile.Direction = m_Mobile.GetDirectionTo(c);
|
||||
}
|
||||
else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
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;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (m_Mobile.GetDistanceToSqrt(c) > m_Mobile.RangePerception + 1)
|
||||
{
|
||||
m_Mobile.DebugSay("I cannot find {0}, so my guard is up", c.Name);
|
||||
|
||||
Action = ActionType.Guard;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I should be closer to {0}", c.Name);
|
||||
}
|
||||
|
||||
if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee)
|
||||
{
|
||||
if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100)
|
||||
{
|
||||
// We are low on health, should we flee?
|
||||
if (Utility.Random(100) <= Math.Max(10, 10 + c.Hits - m_Mobile.Hits))
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to flee from {0}", c.Name);
|
||||
Action = ActionType.Flee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionGuard()
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionGuard();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionFlee()
|
||||
{
|
||||
var c = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if (m_Mobile.Hits > (m_Mobile.HitsMax / 2))
|
||||
{
|
||||
// If I have a target, go back and fight them
|
||||
if (c != null && m_Mobile.GetDistanceToSqrt(c) <= m_Mobile.RangePerception * 2)
|
||||
{
|
||||
m_Mobile.DebugSay("I am stronger now, reengaging {0}", c.Name);
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I am stronger now, my guard is up");
|
||||
Action = ActionType.Guard;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionFlee();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Scripts/Mobiles/AI/OppositionGroup.cs
Normal file
92
Scripts/Mobiles/AI/OppositionGroup.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public class OppositionGroup
|
||||
{
|
||||
private static readonly OppositionGroup m_TerathansAndOphidians = new OppositionGroup(
|
||||
new[]
|
||||
{
|
||||
new[] {typeof(TerathanAvenger), typeof(TerathanDrone), typeof(TerathanMatriarch), typeof(TerathanWarrior)},
|
||||
new[]
|
||||
{
|
||||
typeof(OphidianArchmage), typeof(OphidianKnight), typeof(OphidianMage), typeof(OphidianMatriarch),
|
||||
typeof(OphidianWarrior)
|
||||
}
|
||||
});
|
||||
|
||||
private static readonly OppositionGroup m_SavagesAndOrcs = new OppositionGroup(
|
||||
new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
typeof(Orc), typeof(OrcBomber), typeof(OrcBrute), typeof(OrcCaptain), typeof(OrcChopper), typeof(OrcishLord),
|
||||
typeof(OrcishMage), typeof(OrcScout), typeof(SpawnedOrcishLord)
|
||||
},
|
||||
new[] {typeof(Savage), typeof(SavageRider), typeof(SavageRidgeback), typeof(SavageShaman)}
|
||||
});
|
||||
|
||||
private static readonly OppositionGroup m_FeyAndUndead = new OppositionGroup(
|
||||
new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
typeof(Centaur), typeof(EtherealWarrior), typeof(Kirin), typeof(LordOaks), typeof(Pixie), typeof(Silvani),
|
||||
typeof(Unicorn), typeof(Wisp), typeof(Treefellow), typeof(MLDryad), typeof(Satyr)
|
||||
},
|
||||
new[]
|
||||
{
|
||||
typeof(AncientLich), typeof(Bogle), typeof(BoneKnight), typeof(BoneMagi), typeof(DarknightCreeper), typeof(Ghoul),
|
||||
typeof(LadyOfTheSnow), typeof(Lich), typeof(LichLord), typeof(Mummy), typeof(RevenantLion), typeof(RottingCorpse),
|
||||
typeof(Shade), typeof(ShadowKnight), typeof(SkeletalDragon), typeof(SkeletalDrake), typeof(SkeletalKnight),
|
||||
typeof(SkeletalMage), typeof(Skeleton), typeof(Spectre), typeof(Wraith), typeof(Zombie)
|
||||
}
|
||||
});
|
||||
|
||||
private readonly Type[][] m_Types;
|
||||
|
||||
public OppositionGroup(Type[][] types)
|
||||
{
|
||||
m_Types = types;
|
||||
}
|
||||
|
||||
public static OppositionGroup TerathansAndOphidians { get { return m_TerathansAndOphidians; } }
|
||||
public static OppositionGroup SavagesAndOrcs { get { return m_SavagesAndOrcs; } }
|
||||
public static OppositionGroup FeyAndUndead { get { return m_FeyAndUndead; } }
|
||||
|
||||
public bool IsEnemy(object from, object target)
|
||||
{
|
||||
var fromGroup = IndexOf(from);
|
||||
var targGroup = IndexOf(target);
|
||||
|
||||
return (fromGroup != -1 && targGroup != -1 && fromGroup != targGroup);
|
||||
}
|
||||
|
||||
public int IndexOf(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return -1;
|
||||
|
||||
var type = obj.GetType();
|
||||
|
||||
for (var i = 0; i < m_Types.Length; ++i)
|
||||
{
|
||||
var group = m_Types[i];
|
||||
|
||||
var contains = false;
|
||||
|
||||
for (var j = 0; !contains && j < group.Length; ++j)
|
||||
contains = group[j].IsAssignableFrom(type);
|
||||
|
||||
if (contains)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
330
Scripts/Mobiles/AI/OrcScoutAI.cs
Normal file
330
Scripts/Mobiles/AI/OrcScoutAI.cs
Normal file
@@ -0,0 +1,330 @@
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server.Items;
|
||||
using Server.Spells;
|
||||
using Server.Targeting;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class OrcScoutAI : BaseAI
|
||||
{
|
||||
private static readonly double teleportChance = 0.04;
|
||||
private static readonly int[] m_Offsets = {0, 0, -1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, -1, 0, 1, 1, 1};
|
||||
|
||||
public OrcScoutAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
m_Mobile.DebugSay("I have no combatant");
|
||||
|
||||
PerformHide();
|
||||
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Mobile.Combatant != null)
|
||||
{
|
||||
Action = ActionType.Combat;
|
||||
return true;
|
||||
}
|
||||
|
||||
base.DoActionWander();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
var c = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if (c == null || c.Deleted || c.Map != m_Mobile.Map || !c.Alive || c.IsDeadBondedPet)
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant is gone, so my guard is up");
|
||||
|
||||
Action = ActionType.Guard;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Utility.RandomDouble() < teleportChance)
|
||||
{
|
||||
TryToTeleport();
|
||||
}
|
||||
|
||||
if (!m_Mobile.InRange(c, m_Mobile.RangePerception))
|
||||
{
|
||||
// They are somewhat far away, can we find something else?
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
m_Mobile.FocusMob = null;
|
||||
}
|
||||
else if (!m_Mobile.InRange(c, m_Mobile.RangePerception * 3))
|
||||
{
|
||||
m_Mobile.Combatant = null;
|
||||
}
|
||||
|
||||
c = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if (c == null)
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant has fled, so I am on guard");
|
||||
Action = ActionType.Guard;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (MoveTo(c, true, m_Mobile.RangeFight))
|
||||
{
|
||||
if (!DirectionLocked)
|
||||
m_Mobile.Direction = m_Mobile.GetDirectionTo(c);
|
||||
}
|
||||
else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
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;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (m_Mobile.GetDistanceToSqrt(c) > m_Mobile.RangePerception + 1)
|
||||
{
|
||||
m_Mobile.DebugSay("I cannot find {0}, so my guard is up", c.Name);
|
||||
|
||||
Action = ActionType.Guard;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I should be closer to {0}", c.Name);
|
||||
}
|
||||
|
||||
if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee)
|
||||
{
|
||||
/* // When we have no ammo, we flee
|
||||
Container pack = m_Mobile.Backpack;
|
||||
|
||||
if (pack == null || pack.FindItemByType(typeof(Arrow)) == null)
|
||||
{
|
||||
Action = ActionType.Flee;
|
||||
if (Utility.RandomDouble() < teleportChance + 0.1)
|
||||
{
|
||||
TryToTeleport();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100)
|
||||
{
|
||||
// We are low on health, should we flee?
|
||||
if (Utility.Random(100) <= Math.Max(10, 10 + c.Hits - m_Mobile.Hits))
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to flee from {0}", c.Name);
|
||||
Action = ActionType.Flee;
|
||||
if (Utility.RandomDouble() < teleportChance + 0.1)
|
||||
{
|
||||
TryToTeleport();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionGuard()
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionGuard();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionFlee()
|
||||
{
|
||||
var c = m_Mobile.Combatant as Mobile;
|
||||
|
||||
// Container pack = m_Mobile.Backpack;
|
||||
// bool hasAmmo = !(pack == null || pack.FindItemByType(typeof(Arrow)) == null);
|
||||
// They can shoot even with no ammo!
|
||||
|
||||
if ( /*hasAmmo && */m_Mobile.Hits > m_Mobile.HitsMax / 2)
|
||||
{
|
||||
// If I have a target, go back and fight them
|
||||
if (c != null && m_Mobile.GetDistanceToSqrt(c) <= m_Mobile.RangePerception * 2)
|
||||
{
|
||||
m_Mobile.DebugSay("I am stronger now, reengaging {0}", c.Name);
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I am stronger now, my guard is up");
|
||||
Action = ActionType.Guard;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PerformHide();
|
||||
|
||||
base.DoActionFlee();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Mobile FindNearestAggressor()
|
||||
{
|
||||
Mobile nearest = null;
|
||||
|
||||
var dist = 9999.0;
|
||||
|
||||
IPooledEnumerable eable = m_Mobile.GetMobilesInRange(m_Mobile.RangePerception);
|
||||
|
||||
foreach (Mobile m in eable)
|
||||
{
|
||||
if (m.Player && !m.Hidden && m.IsPlayer() && m.Combatant == m_Mobile)
|
||||
{
|
||||
if (dist > m.GetDistanceToSqrt(m_Mobile))
|
||||
{
|
||||
nearest = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
return nearest;
|
||||
}
|
||||
|
||||
private void TryToTeleport()
|
||||
{
|
||||
var m = FindNearestAggressor();
|
||||
|
||||
if (m == null || m.Map == null || m_Mobile.Map == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Mobile.GetDistanceToSqrt(m) > m_Mobile.RangePerception + 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var px = m_Mobile.X;
|
||||
var py = m_Mobile.Y;
|
||||
|
||||
var dx = m_Mobile.X - m.X;
|
||||
var dy = m_Mobile.Y - m.Y;
|
||||
|
||||
// get vector's length
|
||||
|
||||
var l = Math.Sqrt((dx * dx + dy * dy));
|
||||
|
||||
if (l == 0)
|
||||
{
|
||||
var rand = Utility.Random(8) + 1;
|
||||
rand *= 2;
|
||||
dx = m_Offsets[rand];
|
||||
dy = m_Offsets[rand + 1];
|
||||
l = Math.Sqrt((dx * dx + dy * dy));
|
||||
}
|
||||
|
||||
// normalize vector
|
||||
var dpx = (dx) / l;
|
||||
var dpy = (dy) / l;
|
||||
// move
|
||||
px += (int)(dpx * (4 + Utility.Random(3)));
|
||||
py += (int)(dpy * (4 + Utility.Random(3)));
|
||||
|
||||
for (var i = 0; i < m_Offsets.Length; i += 2)
|
||||
{
|
||||
int x = m_Offsets[i], y = m_Offsets[i + 1];
|
||||
|
||||
var p = new Point3D(px + x, py + y, 0);
|
||||
|
||||
var lt = new LandTarget(p, m_Mobile.Map);
|
||||
|
||||
if (m_Mobile.InLOS(lt) && m_Mobile.Map.CanSpawnMobile(px + x, py + y, lt.Z) &&
|
||||
!SpellHelper.CheckMulti(p, m_Mobile.Map))
|
||||
{
|
||||
m_Mobile.FixedParticles(0x376A, 9, 32, 0x13AF, EffectLayer.Waist);
|
||||
m_Mobile.PlaySound(0x1FE);
|
||||
|
||||
m_Mobile.Location = new Point3D(lt.X, lt.Y, lt.Z);
|
||||
m_Mobile.ProcessDelta();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HideSelf()
|
||||
{
|
||||
if (Core.TickCount >= m_Mobile.NextSkillTime)
|
||||
{
|
||||
Effects.SendLocationParticles(
|
||||
EffectItem.Create(m_Mobile.Location, m_Mobile.Map, EffectItem.DefaultDuration),
|
||||
0x3728,
|
||||
10,
|
||||
10,
|
||||
2023);
|
||||
|
||||
m_Mobile.PlaySound(0x22F);
|
||||
m_Mobile.Hidden = true;
|
||||
|
||||
m_Mobile.UseSkill(SkillName.Stealth);
|
||||
}
|
||||
}
|
||||
|
||||
private void PerformHide()
|
||||
{
|
||||
if (!m_Mobile.Alive || m_Mobile.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_Mobile.Hidden)
|
||||
{
|
||||
var chance = 0.05;
|
||||
|
||||
if (m_Mobile.Hits < 20)
|
||||
{
|
||||
chance = 0.1;
|
||||
}
|
||||
|
||||
if (m_Mobile.Poisoned)
|
||||
{
|
||||
chance = 0.01;
|
||||
}
|
||||
|
||||
if (Utility.RandomDouble() < chance)
|
||||
{
|
||||
HideSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
Scripts/Mobiles/AI/PredatorAI.cs
Normal file
94
Scripts/Mobiles/AI/PredatorAI.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
|
||||
/*
|
||||
* PredatorAI, its an animal that can attack
|
||||
* Dont flee but dont attack if not hurt or attacked
|
||||
*
|
||||
*/
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class PredatorAI : BaseAI
|
||||
{
|
||||
public PredatorAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
if (m_Mobile.Combatant != null)
|
||||
{
|
||||
m_Mobile.DebugSay("I am hurt or being attacked, I kill him");
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, true, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("There is something near, I go away");
|
||||
Action = ActionType.Backoff;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionWander();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
var combatant = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map)
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant is gone, so my guard is up");
|
||||
Action = ActionType.Wander;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
|
||||
{
|
||||
if (!DirectionLocked)
|
||||
m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1)
|
||||
{
|
||||
m_Mobile.DebugSay("I cannot find {0}", combatant.Name);
|
||||
|
||||
Action = ActionType.Wander;
|
||||
return true;
|
||||
}
|
||||
|
||||
m_Mobile.DebugSay("I should be closer to {0}", combatant.Name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionBackoff()
|
||||
{
|
||||
if (m_Mobile.IsHurt() || m_Mobile.Combatant != null)
|
||||
{
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception * 2, FightMode.Closest, true, false, true))
|
||||
{
|
||||
if (WalkMobileRange(m_Mobile.FocusMob, 1, false, m_Mobile.RangePerception, m_Mobile.RangePerception * 2))
|
||||
{
|
||||
m_Mobile.DebugSay("Well, here I am safe");
|
||||
Action = ActionType.Wander;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I have lost my focus, lets relax");
|
||||
Action = ActionType.Wander;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
255
Scripts/Mobiles/AI/SpeedInfo.cs
Normal file
255
Scripts/Mobiles/AI/SpeedInfo.cs
Normal file
@@ -0,0 +1,255 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
using Server.Factions;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public class SpeedInfo
|
||||
{
|
||||
public static readonly double MinDelay = 0.1;
|
||||
public static readonly double MaxDelay = 0.5;
|
||||
public static readonly double MinDelayWild = 0.4;
|
||||
public static readonly double MaxDelayWild = 0.8;
|
||||
|
||||
public static bool GetSpeedsNew(BaseCreature bc, ref double activeSpeed, ref double passiveSpeed)
|
||||
{
|
||||
var maxDex = GetMaxMovementDex(bc);
|
||||
var dex = Math.Min(maxDex, Math.Max(25, bc.Dex));
|
||||
|
||||
var min = bc.IsMonster || InActivePVPCombat(bc) ? MinDelayWild : MinDelay;
|
||||
var max = bc.IsMonster || InActivePVPCombat(bc) ? MaxDelayWild : MaxDelay;
|
||||
|
||||
if (bc.IsParagon)
|
||||
{
|
||||
min /= 2;
|
||||
max = min + .4;
|
||||
}
|
||||
|
||||
activeSpeed = max - ((max - min) * ((double)dex / maxDex));
|
||||
|
||||
if (activeSpeed < min)
|
||||
{
|
||||
activeSpeed = min;
|
||||
}
|
||||
|
||||
passiveSpeed = activeSpeed * 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int GetMaxMovementDex(BaseCreature bc)
|
||||
{
|
||||
return bc.IsMonster ? 150 : 190;
|
||||
}
|
||||
|
||||
public static bool InActivePVPCombat(BaseCreature bc)
|
||||
{
|
||||
return bc.Combatant != null && bc.ControlOrder != OrderType.Follow && bc.Combatant is PlayerMobile;
|
||||
}
|
||||
|
||||
public static double TransformMoveDelay(BaseCreature bc, double delay)
|
||||
{
|
||||
var adjusted = bc.IsMonster ? MaxDelayWild : MaxDelay;
|
||||
|
||||
if (!bc.IsDeadPet && (bc.ReduceSpeedWithDamage || bc.IsSubdued))
|
||||
{
|
||||
var offset = (double)bc.Stam / (double)bc.StamMax;
|
||||
|
||||
if (offset < 1.0)
|
||||
{
|
||||
delay = delay + ((adjusted - delay) * (1.0 - offset));
|
||||
}
|
||||
}
|
||||
|
||||
if (delay > adjusted)
|
||||
{
|
||||
delay = adjusted;
|
||||
}
|
||||
|
||||
return delay;
|
||||
}
|
||||
|
||||
public static bool UseNewSpeeds { get { return true; } }
|
||||
public double ActiveSpeed { get; set; }
|
||||
public double PassiveSpeed { get; set; }
|
||||
|
||||
public Type[] Types { get; set; }
|
||||
|
||||
public SpeedInfo(double activeSpeed, double passiveSpeed, Type[] types)
|
||||
{
|
||||
ActiveSpeed = activeSpeed;
|
||||
PassiveSpeed = passiveSpeed;
|
||||
Types = types;
|
||||
}
|
||||
|
||||
/*public static bool Contains(object obj)
|
||||
{
|
||||
if (UseNewSpeeds)
|
||||
return false;
|
||||
|
||||
if (m_Table == null)
|
||||
LoadTable();
|
||||
|
||||
var sp = (SpeedInfo)m_Table[obj.GetType()];
|
||||
|
||||
return (sp != null);
|
||||
}*/
|
||||
|
||||
public static bool GetSpeeds(BaseCreature bc, ref double activeSpeed, ref double passiveSpeed)
|
||||
{
|
||||
if (UseNewSpeeds)
|
||||
{
|
||||
GetSpeedsNew(bc, ref activeSpeed, ref passiveSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Table == null)
|
||||
LoadTable();
|
||||
|
||||
var sp = (SpeedInfo)m_Table[bc.GetType()];
|
||||
|
||||
if (sp == null)
|
||||
return false;
|
||||
|
||||
activeSpeed = sp.ActiveSpeed;
|
||||
passiveSpeed = sp.PassiveSpeed;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void LoadTable()
|
||||
{
|
||||
m_Table = new Hashtable();
|
||||
|
||||
for (var i = 0; i < m_Speeds.Length; ++i)
|
||||
{
|
||||
var info = m_Speeds[i];
|
||||
var types = info.Types;
|
||||
|
||||
for (var j = 0; j < types.Length; ++j)
|
||||
m_Table[types[j]] = info;
|
||||
}
|
||||
}
|
||||
|
||||
private static Hashtable m_Table;
|
||||
|
||||
private static readonly SpeedInfo[] m_Speeds =
|
||||
{
|
||||
// Slow
|
||||
new SpeedInfo(
|
||||
0.3,
|
||||
0.6,
|
||||
new[]
|
||||
{
|
||||
typeof(AntLion), typeof(ArcticOgreLord), typeof(BogThing), typeof(Bogle), typeof(BoneKnight),
|
||||
typeof(EarthElemental), typeof(Ettin), typeof(FrostOoze), typeof(FrostTroll), typeof(GazerLarva), typeof(Ghoul),
|
||||
typeof(Golem), typeof(HeadlessOne), typeof(Jwilson), typeof(Mummy), typeof(Ogre), typeof(OgreLord),
|
||||
typeof(PlagueBeast), typeof(Quagmire), typeof(Rat), typeof(RottingCorpse), typeof(Sewerrat), typeof(Skeleton),
|
||||
typeof(Slime), typeof(Zombie), typeof(Walrus), typeof(RestlessSoul), typeof(CrystalElemental),
|
||||
typeof(DarknightCreeper), typeof(MoundOfMaggots), typeof(Juggernaut), typeof(Yamandon), typeof(Serado),
|
||||
typeof(RuddyBoura), typeof(LowlandBoura), typeof(HighPlainsBoura), typeof(Relanord), typeof(Ortanord),
|
||||
typeof(Korpre), typeof(Anzuanord), typeof(Anlorzen), typeof(UndeadGuardian), typeof(PutridUndeadGuardian),
|
||||
typeof(CorgulTheSoulBinder), typeof(GooeyMaggots), typeof(Fezzik), typeof(Ronin)
|
||||
}),
|
||||
// Fast
|
||||
new SpeedInfo(
|
||||
0.2,
|
||||
0.4,
|
||||
new[]
|
||||
{
|
||||
typeof(LordOaks), typeof(Silvani), typeof(AirElemental), typeof(AncientWyrm), typeof(Balron), typeof(BladeSpirits),
|
||||
typeof(DreadSpider), typeof(Efreet), typeof(EtherealWarrior), typeof(Lich), typeof(Nightmare),
|
||||
typeof(OphidianArchmage), typeof(OphidianMage), typeof(OphidianWarrior), typeof(OphidianMatriarch),
|
||||
typeof(OphidianKnight), typeof(PoisonElemental), typeof(Revenant), typeof(SandVortex), typeof(SavageRider),
|
||||
typeof(SavageShaman), typeof(SnowElemental), typeof(WhiteWyrm), typeof(Wisp), typeof(DemonKnight),
|
||||
typeof(GiantBlackWidow), typeof(SummonedAirElemental), typeof(LesserHiryu), typeof(Hiryu), typeof(LadyOfTheSnow),
|
||||
typeof(RaiJu), typeof(RuneBeetle), typeof(Changeling), typeof(SentinelSpider), typeof(Anlorvaglem), typeof(Medusa),
|
||||
typeof(PrimevalLich), typeof(StygianDragon), typeof(CoralSnake), typeof(DarkWisp), typeof(DreamWraith),
|
||||
typeof(FireAnt), typeof(KepetchAmbusher), typeof(LavaElemental), typeof(MaddeningHorror), typeof(Wight),
|
||||
typeof(WolfSpider), typeof(UndeadGargoyle), typeof(SlasherOfVeils), typeof(SavagePackWolf), typeof(DemonicJailor),
|
||||
typeof(SilverSerpent),
|
||||
|
||||
#region ML named mobs - before Paragon speedboost
|
||||
typeof(LadyJennifyr), typeof(LadyMarai), typeof(MasterJonath), typeof(MasterMikael), typeof(MasterTheophilus),
|
||||
typeof(RedDeath), typeof(SirPatrick), typeof(Rend), typeof(Grobu), typeof(Gnaw), typeof(Guile), typeof(Irk),
|
||||
typeof(Spite), typeof(LadyLissith), typeof(LadySabrix), typeof(Malefic), typeof(Silk), typeof(Virulent)
|
||||
// TODO: Where to put Lurg, Putrefier, Swoop and Pyre? They seem slower.
|
||||
#endregion
|
||||
}),
|
||||
// Very Fast
|
||||
new SpeedInfo(
|
||||
0.175,
|
||||
0.350,
|
||||
new[]
|
||||
{
|
||||
typeof(Barracoon), typeof(Neira), typeof(Rikktor), typeof(EnergyVortex), typeof(EliteNinja), typeof(Pixie),
|
||||
typeof(FleshRenderer), typeof(KhaldunRevenant), typeof(FactionDragoon), typeof(FactionKnight),
|
||||
typeof(FactionPaladin), typeof(FactionHenchman), typeof(FactionMercenary), typeof(FactionNecromancer),
|
||||
typeof(FactionSorceress), typeof(FactionWizard), typeof(FactionBerserker), typeof(FactionPaladin),
|
||||
typeof(Leviathan), typeof(FireBeetle), typeof(FanDancer), typeof(FactionDeathKnight), typeof(ClockworkExodus),
|
||||
typeof(Navrey), typeof(Raptor), typeof(TrapdoorSpider)
|
||||
}),
|
||||
// Extremely Fast
|
||||
new SpeedInfo(0.08, 0.20, new[] {typeof(Miasma), typeof(Semidar), typeof(Mephitis)}),
|
||||
// Medium
|
||||
new SpeedInfo(
|
||||
0.25,
|
||||
0.5,
|
||||
new[]
|
||||
{
|
||||
typeof(ToxicElemental), typeof(AgapiteElemental), typeof(Alligator), typeof(AncientLich), typeof(Betrayer),
|
||||
typeof(Bird), typeof(BlackBear), typeof(BlackSolenInfiltratorQueen), typeof(BlackSolenInfiltratorWarrior),
|
||||
typeof(BlackSolenQueen), typeof(BlackSolenWarrior), typeof(BlackSolenWorker), typeof(BloodElemental), typeof(Boar),
|
||||
typeof(Bogling), typeof(BoneMagi), typeof(Brigand), typeof(BronzeElemental), typeof(BrownBear), typeof(Bull),
|
||||
typeof(BullFrog), typeof(Cat), typeof(Centaur), typeof(ChaosDaemon), typeof(Chicken), typeof(GolemController),
|
||||
typeof(CopperElemental), typeof(CopperElemental), typeof(Cougar), typeof(Cow), typeof(Cyclops), typeof(Daemon),
|
||||
typeof(DeepSeaSerpent), typeof(DesertOstard), typeof(DireWolf), typeof(Dog), typeof(Dolphin), typeof(Dragon),
|
||||
typeof(Drake), typeof(DullCopperElemental), typeof(Eagle), typeof(ElderGazer), typeof(EvilMage),
|
||||
typeof(EvilMageLord), typeof(Executioner), typeof(Savage), typeof(FireElemental), typeof(FireGargoyle),
|
||||
typeof(FireSteed), typeof(ForestOstard), typeof(FrenziedOstard), typeof(FrostSpider), typeof(Gargoyle),
|
||||
typeof(Gazer), typeof(IceSerpent), typeof(GiantRat), typeof(GiantSerpent), typeof(GiantSpider), typeof(GiantToad),
|
||||
typeof(Goat), typeof(GoldenElemental), typeof(Gorilla), typeof(GreatHart), typeof(GreyWolf), typeof(GrizzlyBear),
|
||||
typeof(Guardian), typeof(Harpy), typeof(Harrower), typeof(HellHound), typeof(Hind), typeof(HordeMinion),
|
||||
typeof(Horse), typeof(IceElemental), typeof(IceFiend), typeof(IceSnake), typeof(Imp), typeof(JackRabbit),
|
||||
typeof(Kirin), typeof(Kraken), typeof(PredatorHellCat), typeof(LavaLizard), typeof(LavaSerpent), typeof(LavaSnake),
|
||||
typeof(Lizardman), typeof(Llama), typeof(Mongbat), typeof(StrongMongbat), typeof(MountainGoat), typeof(Orc),
|
||||
typeof(OrcBomber), typeof(OrcBrute), typeof(OrcCaptain), typeof(OrcishLord), typeof(OrcishMage), typeof(PackHorse),
|
||||
typeof(PackLlama), typeof(Panther), typeof(Pig), typeof(PlagueSpawn), typeof(PolarBear), typeof(Rabbit),
|
||||
typeof(Ratman), typeof(RatmanArcher), typeof(RatmanMage), typeof(RedSolenInfiltratorQueen),
|
||||
typeof(RedSolenInfiltratorWarrior), typeof(RedSolenQueen), typeof(RedSolenWarrior), typeof(RedSolenWorker),
|
||||
typeof(RidableLlama), typeof(Ridgeback), typeof(Scorpion), typeof(SeaSerpent), typeof(SerpentineDragon),
|
||||
typeof(Shade), typeof(ShadowIronElemental), typeof(ShadowWisp), typeof(ShadowWyrm), typeof(Sheep),
|
||||
typeof(SilverSteed), typeof(SkeletalDragon), typeof(SkeletalMage), typeof(SkeletalMount), typeof(HellCat),
|
||||
typeof(Snake), typeof(SnowLeopard), typeof(SpectralArmour), typeof(Spectre), typeof(StoneGargoyle),
|
||||
typeof(StoneHarpy), typeof(SwampDragon), typeof(ScaledSwampDragon), typeof(SwampTentacle), typeof(TerathanAvenger),
|
||||
typeof(TerathanDrone), typeof(TerathanMatriarch), typeof(TerathanWarrior), typeof(TimberWolf), typeof(Titan),
|
||||
typeof(Troll), typeof(Unicorn), typeof(ValoriteElemental), typeof(VeriteElemental), typeof(CoMWarHorse),
|
||||
typeof(MinaxWarHorse), typeof(SLWarHorse), typeof(TBWarHorse), typeof(WaterElemental), typeof(WhippingVine),
|
||||
typeof(WhiteWolf), typeof(Wraith), typeof(Wyvern), typeof(KhaldunZealot), typeof(KhaldunSummoner),
|
||||
typeof(SavageRidgeback), typeof(LichLord), typeof(SkeletalKnight), typeof(SummonedDaemon),
|
||||
typeof(SummonedEarthElemental), typeof(SummonedWaterElemental), typeof(SummonedFireElemental), typeof(MeerWarrior),
|
||||
typeof(MeerEternal), typeof(MeerMage), typeof(MeerCaptain), typeof(JukaLord), typeof(JukaMage),
|
||||
typeof(JukaWarrior), typeof(AbysmalHorror), typeof(BoneDemon), typeof(Devourer), typeof(FleshGolem),
|
||||
typeof(Gibberling), typeof(GoreFiend), typeof(Impaler), typeof(PatchworkSkeleton), typeof(Ravager),
|
||||
typeof(ShadowKnight), typeof(SkitteringHopper), typeof(Treefellow), typeof(VampireBat), typeof(WailingBanshee),
|
||||
typeof(WandererOfTheVoid), typeof(Cursed), typeof(GrimmochDrummel), typeof(LysanderGathenwale), typeof(MorgBergen),
|
||||
typeof(ShadowFiend), typeof(SpectralArmour), typeof(TavaraSewel), typeof(ArcaneDaemon), typeof(Doppleganger),
|
||||
typeof(EnslavedGargoyle), typeof(ExodusMinion), typeof(ExodusOverseer), typeof(GargoyleDestroyer),
|
||||
typeof(GargoyleEnforcer), typeof(Moloch), typeof(BakeKitsune), typeof(DeathwatchBeetleHatchling), typeof(Kappa),
|
||||
typeof(KazeKemono), typeof(DeathwatchBeetle), typeof(TsukiWolf), typeof(YomotsuElder), typeof(YomotsuPriest),
|
||||
typeof(YomotsuWarrior), typeof(RevenantLion), typeof(Oni), typeof(Gaman), typeof(Crane), typeof(Beetle),
|
||||
typeof(ColdDrake), typeof(Vasanord), typeof(Ballem), typeof(Betballem), typeof(Anlorlem), typeof(BloodWorm),
|
||||
typeof(GreenGoblin), typeof(EnslavedGreenGoblin), typeof(GreenGoblinAlchemist), typeof(GreenGoblinScout),
|
||||
typeof(Kepetch), typeof(ClanRC), typeof(EnslavedGoblinKeeper), typeof(EnslavedGoblinMage),
|
||||
typeof(EnslavedGoblinScout), typeof(EnslavedGrayGoblin), typeof(GrayGoblin), typeof(GrayGoblinKeeper),
|
||||
typeof(GrayGoblinMage), typeof(Gremlin), typeof(SkeletalDrake), typeof(Slith), typeof(StoneSlith),
|
||||
typeof(ToxicSlith), typeof(WyvernRenowned), typeof(GrayGoblinMageRenowned), typeof(FireDaemon), typeof(AcidSlug)
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
195
Scripts/Mobiles/AI/ThiefAI.cs
Normal file
195
Scripts/Mobiles/AI/ThiefAI.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server.Items;
|
||||
#endregion
|
||||
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class ThiefAI : BaseAI
|
||||
{
|
||||
private Item m_toDisarm;
|
||||
|
||||
public ThiefAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
m_Mobile.DebugSay("I have no combatant");
|
||||
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionWander();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionCombat()
|
||||
{
|
||||
var c = m_Mobile.Combatant as Mobile;
|
||||
|
||||
if (c == null || c.Deleted || c.Map != m_Mobile.Map)
|
||||
{
|
||||
m_Mobile.DebugSay("My combatant is gone, so my guard is up");
|
||||
|
||||
Action = ActionType.Guard;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WalkMobileRange(c, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
|
||||
{
|
||||
if (!DirectionLocked)
|
||||
m_Mobile.Direction = m_Mobile.GetDirectionTo(c);
|
||||
|
||||
if (m_toDisarm == null)
|
||||
{
|
||||
m_toDisarm = c.FindItemOnLayer(Layer.OneHanded);
|
||||
}
|
||||
|
||||
if (m_toDisarm == null)
|
||||
{
|
||||
m_toDisarm = c.FindItemOnLayer(Layer.TwoHanded);
|
||||
}
|
||||
|
||||
if (m_toDisarm != null && m_toDisarm.IsChildOf(m_Mobile.Backpack))
|
||||
{
|
||||
m_toDisarm = c.FindItemOnLayer(Layer.OneHanded);
|
||||
|
||||
if (m_toDisarm == null)
|
||||
{
|
||||
m_toDisarm = c.FindItemOnLayer(Layer.TwoHanded);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Core.AOS && !m_Mobile.DisarmReady && m_Mobile.Skills[SkillName.Wrestling].Value >= 80.0 &&
|
||||
m_Mobile.Skills[SkillName.ArmsLore].Value >= 80.0 && m_toDisarm != null)
|
||||
{
|
||||
EventSink.InvokeDisarmRequest(new DisarmRequestEventArgs(m_Mobile));
|
||||
}
|
||||
|
||||
if (m_toDisarm != null && m_toDisarm.IsChildOf(c.Backpack) && m_Mobile.NextSkillTime <= Core.TickCount &&
|
||||
(m_toDisarm.LootType != LootType.Blessed && m_toDisarm.LootType != LootType.Newbied))
|
||||
{
|
||||
m_Mobile.DebugSay("Trying to steal from combatant.");
|
||||
m_Mobile.UseSkill(SkillName.Stealing);
|
||||
|
||||
if (m_Mobile.Target != null)
|
||||
{
|
||||
m_Mobile.Target.Invoke(m_Mobile, m_toDisarm);
|
||||
}
|
||||
}
|
||||
else if (m_toDisarm == null && m_Mobile.NextSkillTime <= Core.TickCount)
|
||||
{
|
||||
var cpack = c.Backpack;
|
||||
|
||||
if (cpack != null)
|
||||
{
|
||||
var steala = cpack.FindItemByType(typeof(Bandage));
|
||||
|
||||
if (steala != null)
|
||||
{
|
||||
m_Mobile.DebugSay("Trying to steal from combatant.");
|
||||
m_Mobile.UseSkill(SkillName.Stealing);
|
||||
|
||||
if (m_Mobile.Target != null)
|
||||
{
|
||||
m_Mobile.Target.Invoke(m_Mobile, steala);
|
||||
}
|
||||
}
|
||||
|
||||
var stealb = cpack.FindItemByType(typeof(Nightshade));
|
||||
|
||||
if (stealb != null)
|
||||
{
|
||||
m_Mobile.DebugSay("Trying to steal from combatant.");
|
||||
m_Mobile.UseSkill(SkillName.Stealing);
|
||||
|
||||
if (m_Mobile.Target != null)
|
||||
{
|
||||
m_Mobile.Target.Invoke(m_Mobile, stealb);
|
||||
}
|
||||
}
|
||||
|
||||
var stealc = cpack.FindItemByType(typeof(BlackPearl));
|
||||
|
||||
if (stealc != null)
|
||||
{
|
||||
m_Mobile.DebugSay("Trying to steal from combatant.");
|
||||
m_Mobile.UseSkill(SkillName.Stealing);
|
||||
|
||||
if (m_Mobile.Target != null)
|
||||
{
|
||||
m_Mobile.Target.Invoke(m_Mobile, stealc);
|
||||
}
|
||||
}
|
||||
|
||||
var steald = cpack.FindItemByType(typeof(MandrakeRoot));
|
||||
|
||||
if (steald != null)
|
||||
{
|
||||
m_Mobile.DebugSay("Trying to steal from combatant.");
|
||||
m_Mobile.UseSkill(SkillName.Stealing);
|
||||
|
||||
if (m_Mobile.Target != null)
|
||||
{
|
||||
m_Mobile.Target.Invoke(m_Mobile, steald);
|
||||
}
|
||||
}
|
||||
else if (steala == null && stealb == null && stealc == null && steald == null)
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to flee from {0}", c.Name);
|
||||
|
||||
Action = ActionType.Flee;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.DebugSay("I should be closer to {0}", c.Name);
|
||||
}
|
||||
|
||||
if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee)
|
||||
{
|
||||
if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100)
|
||||
{
|
||||
// We are low on health, should we flee?
|
||||
if (Utility.Random(100) <= Math.Max(10, 10 + c.Hits - m_Mobile.Hits))
|
||||
{
|
||||
m_Mobile.DebugSay("I am going to flee from {0}", c.Name);
|
||||
Action = ActionType.Flee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionGuard()
|
||||
{
|
||||
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
|
||||
{
|
||||
m_Mobile.DebugSay("I have detected {0}, attacking", m_Mobile.FocusMob.Name);
|
||||
|
||||
m_Mobile.Combatant = m_Mobile.FocusMob;
|
||||
Action = ActionType.Combat;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DoActionGuard();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
158
Scripts/Mobiles/AI/VendorAI.cs
Normal file
158
Scripts/Mobiles/AI/VendorAI.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
namespace Server.Mobiles
|
||||
{
|
||||
public class VendorAI : BaseAI
|
||||
{
|
||||
public VendorAI(BaseCreature m)
|
||||
: base(m)
|
||||
{ }
|
||||
|
||||
public override bool DoActionWander()
|
||||
{
|
||||
m_Mobile.DebugSay("I'm fine");
|
||||
|
||||
if (m_Mobile.Combatant != null)
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("{0} is attacking me", m_Mobile.Combatant.Name);
|
||||
|
||||
if (m_Mobile.CanCallGuards)
|
||||
m_Mobile.Say(Utility.RandomList(1005305, 501603));
|
||||
|
||||
Action = ActionType.Flee;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Mobile.FocusMob != null)
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("{0} has talked to me", m_Mobile.FocusMob.Name);
|
||||
|
||||
Action = ActionType.Interact;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Mobile.Warmode = false;
|
||||
|
||||
base.DoActionWander();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionInteract()
|
||||
{
|
||||
var customer = m_Mobile.FocusMob as Mobile;
|
||||
|
||||
if (m_Mobile.Combatant != null)
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("{0} is attacking me", m_Mobile.Combatant.Name);
|
||||
|
||||
if (m_Mobile.CanCallGuards)
|
||||
m_Mobile.Say(Utility.RandomList(1005305, 501603));
|
||||
|
||||
Action = ActionType.Flee;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (customer == null || customer.Deleted || customer.Map != m_Mobile.Map)
|
||||
{
|
||||
m_Mobile.DebugSay("My customer have disapeared");
|
||||
m_Mobile.FocusMob = null;
|
||||
|
||||
Action = ActionType.Wander;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (customer.InRange(m_Mobile, m_Mobile.RangeFight))
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("I am with {0}", customer.Name);
|
||||
|
||||
if (!DirectionLocked)
|
||||
m_Mobile.Direction = m_Mobile.GetDirectionTo(customer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Mobile.Debug)
|
||||
m_Mobile.DebugSay("{0} is gone", customer.Name);
|
||||
|
||||
m_Mobile.FocusMob = null;
|
||||
|
||||
Action = ActionType.Wander;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool DoActionGuard()
|
||||
{
|
||||
m_Mobile.FocusMob = m_Mobile.Combatant as Mobile;
|
||||
return base.DoActionGuard();
|
||||
}
|
||||
|
||||
public override bool HandlesOnSpeech(Mobile from)
|
||||
{
|
||||
if (from.InRange(m_Mobile, 4))
|
||||
return true;
|
||||
|
||||
return base.HandlesOnSpeech(from);
|
||||
}
|
||||
|
||||
// Temporary
|
||||
public override void OnSpeech(SpeechEventArgs e)
|
||||
{
|
||||
base.OnSpeech(e);
|
||||
|
||||
var from = e.Mobile;
|
||||
|
||||
if (m_Mobile is BaseVendor && from.InRange(m_Mobile, Core.AOS ? 1 : 4) && !e.Handled)
|
||||
{
|
||||
if (e.HasKeyword(0x14D)) // *vendor sell*
|
||||
{
|
||||
e.Handled = true;
|
||||
|
||||
((BaseVendor)m_Mobile).VendorSell(from);
|
||||
m_Mobile.FocusMob = from;
|
||||
}
|
||||
else if (e.HasKeyword(0x3C)) // *vendor buy*
|
||||
{
|
||||
e.Handled = true;
|
||||
|
||||
((BaseVendor)m_Mobile).VendorBuy(from);
|
||||
m_Mobile.FocusMob = from;
|
||||
}
|
||||
else if (WasNamed(e.Speech))
|
||||
{
|
||||
if (e.HasKeyword(0x177)) // *sell*
|
||||
{
|
||||
e.Handled = true;
|
||||
|
||||
((BaseVendor)m_Mobile).VendorSell(from);
|
||||
}
|
||||
else if (e.HasKeyword(0x171)) // *buy*
|
||||
{
|
||||
e.Handled = true;
|
||||
|
||||
((BaseVendor)m_Mobile).VendorBuy(from);
|
||||
}
|
||||
|
||||
m_Mobile.FocusMob = from;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override double TransformMoveDelay(double delay)
|
||||
{
|
||||
if (m_Mobile is BaseVendor)
|
||||
{
|
||||
return ((BaseVendor)m_Mobile).GetMoveDelay;
|
||||
}
|
||||
|
||||
return base.TransformMoveDelay(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user