Reorganizes Project (#41)

This commit is contained in:
Kamron Batman 2019-08-02 18:13:40 -07:00 committed by GitHub
parent 08bf44af9a
commit 3614a66aee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3499 changed files with 79 additions and 55 deletions

View file

@ -0,0 +1,35 @@
using System.Collections.Generic;
using Server.Mobiles;
using Server.Targeting;
namespace Server.Targets
{
public class AIControlMobileTarget : Target
{
private List<BaseAI> m_List;
public AIControlMobileTarget(BaseAI ai, OrderType order) : base(-1, false,
order == OrderType.Attack ? TargetFlags.Harmful : TargetFlags.None)
{
m_List = new List<BaseAI>();
Order = order;
AddAI(ai);
}
public OrderType Order{ get; }
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 Mobile m)
for (int i = 0; i < m_List.Count; ++i)
m_List[i].EndPickTarget(from, m, Order);
}
}
}

View file

@ -0,0 +1,150 @@
// Ideas
// When you run on animals the panic
// When if ( distance < 8 && Utility.RandomDouble() * Math.Sqrt( (8 - distance) / 6 ) >= incoming.Skills.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%
double 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))
{
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.DoActionWander();
}
return true;
}
public override bool DoActionCombat()
{
Mobile combatant = m_Mobile.Combatant;
if (combatant?.Deleted != false || 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))
{
m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant);
}
else
{
if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1)
{
if (m_Mobile.Debug)
m_Mobile.DebugSay("I cannot find {0}", combatant.Name);
Action = ActionType.Wander;
return true;
}
if (m_Mobile.Debug)
m_Mobile.DebugSay("I should be closer to {0}", combatant.Name);
}
if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee)
{
double 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()
{
double 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;
}
public override bool DoActionFlee()
{
AcquireFocusMob(m_Mobile.RangePerception * 2, m_Mobile.FightMode, true, false, true);
if (m_Mobile.FocusMob == null)
m_Mobile.FocusMob = m_Mobile.Combatant;
return base.DoActionFlee();
}
}
}

View file

@ -0,0 +1,120 @@
using Server.Items;
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))
{
if (m_Mobile.Debug)
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()
{
if (m_Mobile.Combatant?.Deleted != false || !m_Mobile.Combatant.Alive ||
m_Mobile.Combatant.IsDeadBondedPet)
{
m_Mobile.DebugSay("My combatant is deleted");
Action = ActionType.Guard;
return true;
}
if (Core.TickCount - m_Mobile.LastMoveTime > 1000)
{
if (WalkMobileRange(m_Mobile.Combatant, 1, true, m_Mobile.RangeFight, m_Mobile.Weapon.MaxRange))
{
// Be sure to face the combatant
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 {0}", 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 {0}", m_Mobile.Combatant.Name);
m_Mobile.Combatant = null;
Action = ActionType.Guard;
return true;
}
}
}
}
// When we have no ammo, we flee
Container pack = m_Mobile.Backpack;
if (pack?.FindItemByType<Arrow>() == null)
{
Action = ActionType.Flee;
return true;
}
// At 20% we should check if we must leave
if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100 && m_Mobile.CanFlee)
{
bool bFlee = false;
// if my current hits are more than my opponent, i don't care
if (m_Mobile.Combatant != null && m_Mobile.Hits < m_Mobile.Combatant.Hits)
{
int iDiff = m_Mobile.Combatant.Hits - m_Mobile.Hits;
if (Utility.Random(0, 100) > 10 + iDiff) // 10% to flee + the diff of hits
bFlee = true;
}
else if (m_Mobile.Combatant != null && m_Mobile.Hits >= m_Mobile.Combatant.Hits)
{
if (Utility.Random(0, 100) > 10) // 10% to flee
bFlee = true;
}
if (bFlee) Action = ActionType.Flee;
}
return true;
}
public override bool DoActionGuard()
{
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, 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;
}
}
}

File diff suppressed because it is too large Load diff

View 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?.Deleted != false)
{
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))
{
// Be sure to face the combatant
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;
}
}
}

View file

@ -0,0 +1,157 @@
using Server.Spells;
using Server.Spells.First;
using Server.Spells.Fourth;
using Server.Spells.Second;
using Server.Targeting;
namespace Server.Mobiles
{
public class HealerAI : BaseAI
{
private static NeedDelegate m_Cure = NeedCure;
private static NeedDelegate m_GHeal = NeedGHeal;
private static NeedDelegate m_LHeal = NeedLHeal;
private static NeedDelegate[] m_ACure = { m_Cure };
private static NeedDelegate[] m_AGHeal = { m_GHeal };
private static NeedDelegate[] m_ALHeal = { m_LHeal };
private static NeedDelegate[] m_All = { m_Cure, m_GHeal, m_LHeal };
public HealerAI(BaseCreature m) : base(m)
{
}
public override bool Think()
{
if (m_Mobile.Deleted)
return false;
Target targ = m_Mobile.Target;
if (targ != null)
{
ISpellTarget spellTarg = targ as ISpellTarget;
if (spellTarg?.Spell is CureSpell)
ProcessTarget(targ, m_ACure);
else if (spellTarg?.Spell is GreaterHealSpell)
ProcessTarget(targ, m_AGHeal);
else if (spellTarg?.Spell is HealSpell)
ProcessTarget(targ, m_ALHeal);
else
targ.Cancel(m_Mobile, TargetCancelType.Canceled);
}
else
{
Mobile 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).Cast())
new CureSpell(m_Mobile).Cast();
}
else if (NeedGHeal(toHelp))
{
if (m_Mobile.Debug)
m_Mobile.DebugSay("{0} needs a greater heal", toHelp.Name);
if (!new GreaterHealSpell(m_Mobile).Cast())
new HealSpell(m_Mobile).Cast();
}
else if (NeedLHeal(toHelp))
{
if (m_Mobile.Debug)
m_Mobile.DebugSay("{0} needs a lesser heal", toHelp.Name);
new HealSpell(m_Mobile).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 void ProcessTarget(Target targ, NeedDelegate[] func)
{
Mobile 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;
Map map = m_Mobile.Map;
if (map != null)
{
double prio = 0.0;
Mobile found = null;
foreach (Mobile m in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception))
{
if (!m_Mobile.CanSee(m) || !(m is BaseCreature) || ((BaseCreature)m).Team != m_Mobile.Team)
continue;
for (int i = 0; i < funcs.Length; ++i)
if (funcs[i](m))
{
double val = -m_Mobile.GetDistanceToSqrt(m);
if (found == null || val > prio)
{
prio = val;
found = m;
}
break;
}
}
return found;
}
return null;
}
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 delegate bool NeedDelegate(Mobile m);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,177 @@
//
// This is a first simple AI
//
//
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))
{
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.DoActionWander();
}
return true;
}
public override bool DoActionCombat()
{
Mobile combatant = m_Mobile.Combatant;
if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map || !combatant.Alive ||
combatant.IsDeadBondedPet)
{
m_Mobile.DebugSay("My combatant is gone, so my guard is up");
Action = ActionType.Guard;
return true;
}
if (!m_Mobile.InRange(combatant, 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(combatant, m_Mobile.RangePerception * 3))
{
m_Mobile.Combatant = null;
}
combatant = m_Mobile.Combatant;
if (combatant == null)
{
m_Mobile.DebugSay("My combatant has fled, so I am on guard");
Action = ActionType.Guard;
return true;
}
}
/*if ( !m_Mobile.InLOS( combatant ) )
{
if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) )
{
m_Mobile.Combatant = combatant = m_Mobile.FocusMob;
m_Mobile.FocusMob = null;
}
}*/
if (MoveTo(combatant, true, m_Mobile.RangeFight))
{
m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant);
}
else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
{
if (m_Mobile.Debug)
m_Mobile.DebugSay("My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name);
m_Mobile.Combatant = m_Mobile.FocusMob;
Action = ActionType.Combat;
return true;
}
else if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1)
{
if (m_Mobile.Debug)
m_Mobile.DebugSay("I cannot find {0}, so my guard is up", combatant.Name);
Action = ActionType.Guard;
return true;
}
else
{
if (m_Mobile.Debug)
m_Mobile.DebugSay("I should be closer to {0}", combatant.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?
bool flee = false;
if (m_Mobile.Hits < combatant.Hits)
{
// We are more hurt than them
int diff = combatant.Hits - m_Mobile.Hits;
flee = Utility.Random(0, 100) < 10 + diff; // (10 + diff)% chance to flee
}
else
{
flee = Utility.Random(0, 100) < 10; // 10% chance to flee
}
if (flee)
{
if (m_Mobile.Debug)
m_Mobile.DebugSay("I am going to flee from {0}", combatant.Name);
Action = ActionType.Flee;
}
}
return true;
}
public override bool DoActionGuard()
{
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, 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;
}
public override bool DoActionFlee()
{
if (m_Mobile.Hits > m_Mobile.HitsMax / 2)
{
m_Mobile.DebugSay("I am stronger now, so I will continue fighting");
Action = ActionType.Combat;
}
else
{
m_Mobile.FocusMob = m_Mobile.Combatant;
base.DoActionFlee();
}
return true;
}
}
}

View file

@ -0,0 +1,126 @@
using System;
using Server.Mobiles;
namespace Server
{
public class OppositionGroup
{
private Type[][] m_Types;
public OppositionGroup(Type[][] types)
{
m_Types = types;
}
public static OppositionGroup TerathansAndOphidians{ get; } = new OppositionGroup(new[]
{
new[]
{
typeof(TerathanAvenger),
typeof(TerathanDrone),
typeof(TerathanMatriarch),
typeof(TerathanWarrior)
},
new[]
{
typeof(OphidianArchmage),
typeof(OphidianKnight),
typeof(OphidianMage),
typeof(OphidianMatriarch),
typeof(OphidianWarrior)
}
});
public static OppositionGroup SavagesAndOrcs{ get; } = new OppositionGroup(new[]
{
new[]
{
typeof(Orc),
typeof(OrcBomber),
typeof(OrcBrute),
typeof(OrcCaptain),
typeof(OrcishLord),
typeof(OrcishMage),
typeof(SpawnedOrcishLord)
},
new[]
{
typeof(Savage),
typeof(SavageRider),
typeof(SavageRidgeback),
typeof(SavageShaman)
}
});
public static OppositionGroup FeyAndUndead{ get; } = 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(LichLord),
typeof(Shade),
typeof(Spectre),
typeof(Wraith),
typeof(BoneKnight),
typeof(Ghoul),
typeof(Mummy),
typeof(SkeletalKnight),
typeof(Skeleton),
typeof(Zombie),
typeof(ShadowKnight),
typeof(DarknightCreeper),
typeof(RevenantLion),
typeof(LadyOfTheSnow),
typeof(RottingCorpse),
typeof(SkeletalDragon),
typeof(Lich)
}
});
public bool IsEnemy(object from, object target)
{
int fromGroup = IndexOf(from);
int targGroup = IndexOf(target);
return fromGroup != -1 && targGroup != -1 && fromGroup != targGroup;
}
public int IndexOf(object obj)
{
if (obj == null)
return -1;
Type type = obj.GetType();
for (int i = 0; i < m_Types.Length; ++i)
{
Type[] group = m_Types[i];
bool contains = false;
for (int j = 0; !contains && j < group.Length; ++j)
contains = group[j].IsAssignableFrom(type);
if (contains)
return i;
}
return -1;
}
}
}

View file

@ -0,0 +1,92 @@
/*
* 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()
{
Mobile combatant = m_Mobile.Combatant;
if (combatant?.Deleted != false || 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))
{
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;
}
}
}

View file

@ -0,0 +1,208 @@
using System;
using System.Collections.Generic;
using Server.Factions;
using Server.Mobiles;
namespace Server
{
public class SpeedInfo
{
// Should we use the new method of speeds?
private static bool Enabled = true;
private static Dictionary<Type, SpeedInfo> m_Table;
private static 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)
}),
/* 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(Ronin), typeof(RuneBeetle),
typeof(Changeling),
#region ML named mobs - before Paragon speedboost
typeof(LadyJennifyr), typeof(LadyMarai), typeof(MasterJonath),
typeof(MasterMikael), typeof(MasterTheophilus), typeof(RedDeath),
typeof(SirPatrick), typeof(Miasma), 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(Mephitis), typeof(Neira),
typeof(Rikktor), typeof(Semidar), typeof(EnergyVortex),
typeof(EliteNinja), typeof(Pixie), typeof(SilverSerpent),
typeof(VorpalBunny), 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)
}),
/* Medium */
new SpeedInfo(0.25, 0.5, new[]
{
typeof(AcidElemental), 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(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)
})
};
public SpeedInfo(double activeSpeed, double passiveSpeed, Type[] types)
{
ActiveSpeed = activeSpeed;
PassiveSpeed = passiveSpeed;
Types = types;
}
public double ActiveSpeed{ get; set; }
public double PassiveSpeed{ get; set; }
public Type[] Types{ get; set; }
public static bool Contains(object obj)
{
if (!Enabled)
return false;
if (m_Table == null)
LoadTable();
;
return m_Table.ContainsKey(obj.GetType());
}
public static bool GetSpeeds(object obj, ref double activeSpeed, ref double passiveSpeed)
{
if (!Enabled)
return false;
if (m_Table == null)
LoadTable();
if (!m_Table.TryGetValue(obj.GetType(), out SpeedInfo sp))
return false;
activeSpeed = sp.ActiveSpeed;
passiveSpeed = sp.PassiveSpeed;
return true;
}
private static void LoadTable()
{
m_Table = new Dictionary<Type, SpeedInfo>();
for (int i = 0; i < m_Speeds.Length; ++i)
{
SpeedInfo info = m_Speeds[i];
Type[] types = info.Types;
for (int j = 0; j < types.Length; ++j)
m_Table[types[j]] = info;
}
}
}
}

View file

@ -0,0 +1,180 @@
using Server.Items;
//
// This is a first simple AI
//
//
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()
{
Mobile combatant = m_Mobile.Combatant;
if (combatant?.Deleted != false || combatant.Map != m_Mobile.Map)
{
m_Mobile.DebugSay("My combatant is gone, so my guard is up");
Action = ActionType.Guard;
return true;
}
if (WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
{
m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant);
if (m_toDisarm?.IsChildOf(m_Mobile.Backpack) != false)
m_toDisarm = combatant.FindItemOnLayer(Layer.OneHanded) ?? combatant.FindItemOnLayer(Layer.TwoHanded);
if (!Core.AOS && !m_Mobile.DisarmReady && m_Mobile.Skills.Wrestling.Value >= 80.0 &&
m_Mobile.Skills.ArmsLore.Value >= 80.0 && m_toDisarm != null)
EventSink.InvokeDisarmRequest(new DisarmRequestEventArgs(m_Mobile));
if (m_toDisarm?.IsChildOf(combatant.Backpack) == true &&
Core.TickCount - m_Mobile.NextSkillTime >= 0 && m_toDisarm.LootType != LootType.Blessed &&
m_toDisarm.LootType != LootType.Newbied)
{
m_Mobile.DebugSay("Trying to steal from combatant.");
m_Mobile.UseSkill(SkillName.Stealing);
m_Mobile.Target?.Invoke(m_Mobile, m_toDisarm);
}
else if (m_toDisarm == null && Core.TickCount - m_Mobile.NextSkillTime >= 0)
{
Container cpack = combatant.Backpack;
if (cpack != null)
{
Item steala = cpack.FindItemByType<Bandage>();
if (steala != null)
{
m_Mobile.DebugSay("Trying to steal from combatant.");
m_Mobile.UseSkill(SkillName.Stealing);
m_Mobile.Target?.Invoke(m_Mobile, steala);
}
Item stealb = cpack.FindItemByType<Nightshade>();
if (stealb != null)
{
m_Mobile.DebugSay("Trying to steal from combatant.");
m_Mobile.UseSkill(SkillName.Stealing);
m_Mobile.Target?.Invoke(m_Mobile, stealb);
}
Item stealc = cpack.FindItemByType<BlackPearl>();
if (stealc != null)
{
m_Mobile.DebugSay("Trying to steal from combatant.");
m_Mobile.UseSkill(SkillName.Stealing);
m_Mobile.Target?.Invoke(m_Mobile, stealc);
}
Item steald = cpack.FindItemByType<MandrakeRoot>();
if (steald != null)
{
m_Mobile.DebugSay("Trying to steal from combatant.");
m_Mobile.UseSkill(SkillName.Stealing);
m_Mobile.Target?.Invoke(m_Mobile, steald);
}
else if (steala == null && stealb == null && stealc == null)
{
m_Mobile.DebugSay("I am going to flee from {0}", combatant.Name);
Action = ActionType.Flee;
}
}
}
}
else
{
m_Mobile.DebugSay("I should be closer to {0}", combatant.Name);
}
if (m_Mobile.Hits >= m_Mobile.HitsMax * 20 / 100 || !m_Mobile.CanFlee)
return true;
// We are low on health, should we flee?
bool flee;
if (m_Mobile.Hits < combatant.Hits)
{
// We are more hurt than them
int diff = combatant.Hits - m_Mobile.Hits;
flee = Utility.Random(0, 100) > 10 + diff; // (10 + diff)% chance to flee
}
else
{
flee = Utility.Random(0, 100) > 10; // 10% chance to flee
}
if (flee)
{
m_Mobile.DebugSay("I am going to flee from {0}", combatant.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()
{
if (m_Mobile.Hits > m_Mobile.HitsMax / 2)
{
m_Mobile.DebugSay("I am stronger now, so I will continue fighting");
Action = ActionType.Combat;
}
else
{
m_Mobile.FocusMob = m_Mobile.Combatant;
base.DoActionFlee();
}
return true;
}
}
}

View file

@ -0,0 +1,150 @@
//
// This is a first simple AI
//
//
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);
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()
{
Mobile customer = m_Mobile.FocusMob;
if (m_Mobile.Combatant != null)
{
if (m_Mobile.Debug)
m_Mobile.DebugSay("{0} is attacking me", m_Mobile.Combatant.Name);
m_Mobile.Say(Utility.RandomList(1005305, 501603));
Action = ActionType.Flee;
return true;
}
if (customer?.Deleted != false || 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);
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;
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);
Mobile from = e.Mobile;
if (m_Mobile is BaseVendor vendor && from.InRange(m_Mobile, Core.AOS ? 1 : 4) && !e.Handled)
{
if (e.HasKeyword(0x14D)) // *vendor sell*
{
e.Handled = true;
vendor.VendorSell(from);
vendor.FocusMob = from;
}
else if (e.HasKeyword(0x3C)) // *vendor buy*
{
e.Handled = true;
vendor.VendorBuy(from);
vendor.FocusMob = from;
}
else if (WasNamed(e.Speech))
{
if (e.HasKeyword(0x177)) // *sell*
{
e.Handled = true;
vendor.VendorSell(from);
}
else if (e.HasKeyword(0x171)) // *buy*
{
e.Handled = true;
vendor.VendorBuy(from);
}
vendor.FocusMob = from;
}
}
}
}
}