Overwrite

Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
Unstable Kitsune
2023-11-28 23:20:26 -05:00
parent 3cd54811de
commit b918192e4e
11608 changed files with 2644205 additions and 47 deletions

File diff suppressed because it is too large Load Diff

View 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();
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

View 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();
}
}
}