494 lines
12 KiB
C#
494 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Server.Items;
|
|
using Server.Mobiles;
|
|
using Server.Network;
|
|
using Server.Factions.AI;
|
|
|
|
namespace Server.Factions
|
|
{
|
|
public abstract class BaseFactionGuard : BaseCreature
|
|
{
|
|
private Faction m_Faction;
|
|
private Town m_Town;
|
|
private Orders m_Orders;
|
|
|
|
public override bool BardImmune => true;
|
|
|
|
[CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )]
|
|
public Faction Faction
|
|
{
|
|
get => m_Faction;
|
|
set{ Unregister(); m_Faction = value; Register(); }
|
|
}
|
|
|
|
public Orders Orders => m_Orders;
|
|
|
|
[CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )]
|
|
public Town Town
|
|
{
|
|
get => m_Town;
|
|
set{ Unregister(); m_Town = value; Register(); }
|
|
}
|
|
|
|
public void Register()
|
|
{
|
|
if ( m_Town != null && m_Faction != null )
|
|
m_Town.RegisterGuard( this );
|
|
}
|
|
|
|
public void Unregister()
|
|
{
|
|
if ( m_Town != null )
|
|
m_Town.UnregisterGuard( this );
|
|
}
|
|
|
|
public abstract GuardAI GuardAI{ get; }
|
|
|
|
protected override BaseAI ForcedAI => new FactionGuardAI( this );
|
|
|
|
public override TimeSpan ReacquireDelay => TimeSpan.FromSeconds( 2.0 );
|
|
|
|
public override bool IsEnemy( Mobile m )
|
|
{
|
|
Faction ourFaction = m_Faction;
|
|
Faction theirFaction = Faction.Find( m );
|
|
|
|
if ( theirFaction == null && m is BaseFactionGuard guard )
|
|
theirFaction = guard.Faction;
|
|
|
|
if ( ourFaction != null && theirFaction != null && ourFaction != theirFaction )
|
|
{
|
|
ReactionType reactionType = Orders.GetReaction( theirFaction ).Type;
|
|
|
|
if ( reactionType == ReactionType.Attack )
|
|
return true;
|
|
|
|
List<AggressorInfo> list = m.Aggressed;
|
|
|
|
for ( int i = 0; i < list.Count; ++i )
|
|
{
|
|
AggressorInfo ai = list[i];
|
|
|
|
if ( ai.Defender is BaseFactionGuard bf && bf.Faction == ourFaction )
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override void OnMovement( Mobile m, Point3D oldLocation )
|
|
{
|
|
if ( m.Player && m.Alive && InRange( m, 10 ) && !InRange( oldLocation, 10 ) && InLOS( m ) && m_Orders.GetReaction( Faction.Find( m ) ).Type == ReactionType.Warn )
|
|
{
|
|
Direction = GetDirectionTo( m );
|
|
|
|
string warning = null;
|
|
|
|
switch ( Utility.Random( 6 ) )
|
|
{
|
|
case 0: warning = "I warn you, {0}, you would do well to leave this area before someone shows you the world of gray."; break;
|
|
case 1: warning = "It would be wise to leave this area, {0}, lest your head become my commanders' trophy."; break;
|
|
case 2: warning = "You are bold, {0}, for one of the meager {1}. Leave now, lest you be taught the taste of dirt."; break;
|
|
case 3: warning = "Your presence here is an insult, {0}. Be gone now, knave."; break;
|
|
case 4: warning = "Dost thou wish to be hung by your toes, {0}? Nay? Then come no closer."; break;
|
|
case 5: warning = "Hey, {0}. Yeah, you. Get out of here before I beat you with a stick."; break;
|
|
}
|
|
|
|
Faction faction = Faction.Find( m );
|
|
|
|
Say( warning, m.Name, faction == null ? "civilians" : faction.Definition.FriendlyName );
|
|
}
|
|
}
|
|
|
|
private const int ListenRange = 12;
|
|
|
|
public override bool HandlesOnSpeech( Mobile from )
|
|
{
|
|
if ( InRange( from, ListenRange ) )
|
|
return true;
|
|
|
|
return base.HandlesOnSpeech( from );
|
|
}
|
|
|
|
private DateTime m_OrdersEnd;
|
|
|
|
private void ChangeReaction( Faction faction, ReactionType type )
|
|
{
|
|
if ( faction == null )
|
|
{
|
|
switch ( type )
|
|
{
|
|
case ReactionType.Ignore: Say( 1005179 ); break; // Civilians will now be ignored.
|
|
case ReactionType.Warn: Say( 1005180 ); break; // Civilians will now be warned of their impending deaths.
|
|
case ReactionType.Attack: return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TextDefinition def = null;
|
|
|
|
switch ( type )
|
|
{
|
|
case ReactionType.Ignore: def = faction.Definition.GuardIgnore; break;
|
|
case ReactionType.Warn: def = faction.Definition.GuardWarn; break;
|
|
case ReactionType.Attack: def = faction.Definition.GuardAttack; break;
|
|
}
|
|
|
|
if ( def != null && def.Number > 0 )
|
|
Say( def.Number );
|
|
else if ( def?.String != null )
|
|
Say( def.String );
|
|
}
|
|
|
|
m_Orders.SetReaction( faction, type );
|
|
}
|
|
|
|
private bool WasNamed( string speech )
|
|
{
|
|
string name = Name;
|
|
|
|
return ( name != null && Insensitive.StartsWith( speech, name ) );
|
|
}
|
|
|
|
public override void OnSpeech( SpeechEventArgs e )
|
|
{
|
|
base.OnSpeech( e );
|
|
|
|
Mobile from = e.Mobile;
|
|
|
|
if ( !e.Handled && InRange( from, ListenRange ) && from.Alive )
|
|
{
|
|
if ( e.HasKeyword( 0xE6 ) && (Insensitive.Equals( e.Speech, "orders" ) || WasNamed( e.Speech )) ) // *orders*
|
|
{
|
|
if ( m_Town == null || !m_Town.IsSheriff( from ) )
|
|
{
|
|
Say( 1042189 ); // I don't work for you!
|
|
}
|
|
else if ( Town.FromRegion( Region ) == m_Town )
|
|
{
|
|
Say( 1042180 ); // Your orders, sire?
|
|
m_OrdersEnd = DateTime.UtcNow + TimeSpan.FromSeconds( 10.0 );
|
|
}
|
|
}
|
|
else if ( DateTime.UtcNow < m_OrdersEnd )
|
|
{
|
|
if ( m_Town != null && m_Town.IsSheriff( from ) && Town.FromRegion( Region ) == m_Town )
|
|
{
|
|
m_OrdersEnd = DateTime.UtcNow + TimeSpan.FromSeconds( 10.0 );
|
|
|
|
bool understood = true;
|
|
ReactionType newType = 0;
|
|
|
|
if ( Insensitive.Contains( e.Speech, "attack" ) )
|
|
newType = ReactionType.Attack;
|
|
else if ( Insensitive.Contains( e.Speech, "warn" ) )
|
|
newType = ReactionType.Warn;
|
|
else if ( Insensitive.Contains( e.Speech, "ignore" ) )
|
|
newType = ReactionType.Ignore;
|
|
else
|
|
understood = false;
|
|
|
|
if ( understood )
|
|
{
|
|
understood = false;
|
|
|
|
if ( Insensitive.Contains( e.Speech, "civil" ) )
|
|
{
|
|
ChangeReaction( null, newType );
|
|
understood = true;
|
|
}
|
|
|
|
List<Faction> factions = Faction.Factions;
|
|
|
|
for ( int i = 0; i < factions.Count; ++i )
|
|
{
|
|
Faction faction = factions[i];
|
|
|
|
if ( faction != m_Faction && Insensitive.Contains( e.Speech, faction.Definition.Keyword ) )
|
|
{
|
|
ChangeReaction( faction, newType );
|
|
understood = true;
|
|
}
|
|
}
|
|
}
|
|
else if ( Insensitive.Contains( e.Speech, "patrol" ) )
|
|
{
|
|
Home = Location;
|
|
RangeHome = 6;
|
|
Combatant = null;
|
|
m_Orders.Movement = MovementType.Patrol;
|
|
Say( 1005146 ); // This spot looks like it needs protection! I shall guard it with my life.
|
|
understood = true;
|
|
}
|
|
else if ( Insensitive.Contains( e.Speech, "follow" ) )
|
|
{
|
|
Home = Location;
|
|
RangeHome = 6;
|
|
Combatant = null;
|
|
m_Orders.Follow = from;
|
|
m_Orders.Movement = MovementType.Follow;
|
|
Say( 1005144 ); // Yes, Sire.
|
|
understood = true;
|
|
}
|
|
|
|
if ( !understood )
|
|
Say( 1042183 ); // I'm sorry, I don't understand your orders...
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void GetProperties( ObjectPropertyList list )
|
|
{
|
|
base.GetProperties( list );
|
|
|
|
if ( m_Faction != null && Map == Faction.Facet )
|
|
list.Add( 1060846, m_Faction.Definition.PropName ); // Guard: ~1_val~
|
|
}
|
|
|
|
public override void OnSingleClick( Mobile from )
|
|
{
|
|
if ( m_Faction != null && Map == Faction.Facet )
|
|
{
|
|
string text = string.Concat( "(Guard, ", m_Faction.Definition.FriendlyName, ")" );
|
|
|
|
int hue = ( Faction.Find( from ) == m_Faction ? 98 : 38 );
|
|
|
|
PrivateOverheadMessage( MessageType.Label, hue, true, text, from.NetState );
|
|
}
|
|
|
|
base.OnSingleClick( from );
|
|
}
|
|
|
|
public virtual void GenerateRandomHair()
|
|
{
|
|
Utility.AssignRandomHair( this );
|
|
Utility.AssignRandomFacialHair( this, HairHue );
|
|
}
|
|
|
|
private static Type[] m_StrongPotions = {
|
|
typeof( GreaterHealPotion ), typeof( GreaterHealPotion ), typeof( GreaterHealPotion ),
|
|
typeof( GreaterCurePotion ), typeof( GreaterCurePotion ), typeof( GreaterCurePotion ),
|
|
typeof( GreaterStrengthPotion ), typeof( GreaterStrengthPotion ),
|
|
typeof( GreaterAgilityPotion ), typeof( GreaterAgilityPotion ),
|
|
typeof( TotalRefreshPotion ), typeof( TotalRefreshPotion ),
|
|
typeof( GreaterExplosionPotion )
|
|
};
|
|
|
|
private static Type[] m_WeakPotions = {
|
|
typeof( HealPotion ), typeof( HealPotion ), typeof( HealPotion ),
|
|
typeof( CurePotion ), typeof( CurePotion ), typeof( CurePotion ),
|
|
typeof( StrengthPotion ), typeof( StrengthPotion ),
|
|
typeof( AgilityPotion ), typeof( AgilityPotion ),
|
|
typeof( RefreshPotion ), typeof( RefreshPotion ),
|
|
typeof( ExplosionPotion )
|
|
};
|
|
|
|
public void PackStrongPotions( int min, int max )
|
|
{
|
|
PackStrongPotions( Utility.RandomMinMax( min, max ) );
|
|
}
|
|
|
|
public void PackStrongPotions( int count )
|
|
{
|
|
for ( int i = 0; i < count; ++i )
|
|
PackStrongPotion();
|
|
}
|
|
|
|
public void PackStrongPotion()
|
|
{
|
|
PackItem( Loot.Construct( m_StrongPotions ) );
|
|
}
|
|
|
|
public void PackWeakPotions( int min, int max )
|
|
{
|
|
PackWeakPotions( Utility.RandomMinMax( min, max ) );
|
|
}
|
|
|
|
public void PackWeakPotions( int count )
|
|
{
|
|
for ( int i = 0; i < count; ++i )
|
|
PackWeakPotion();
|
|
}
|
|
|
|
public void PackWeakPotion()
|
|
{
|
|
PackItem( Loot.Construct( m_WeakPotions ) );
|
|
}
|
|
|
|
public Item Immovable( Item item )
|
|
{
|
|
item.Movable = false;
|
|
return item;
|
|
}
|
|
|
|
public Item Newbied( Item item )
|
|
{
|
|
item.LootType = LootType.Newbied;
|
|
return item;
|
|
}
|
|
|
|
public Item Rehued( Item item, int hue )
|
|
{
|
|
item.Hue = hue;
|
|
return item;
|
|
}
|
|
|
|
public Item Layered( Item item, Layer layer )
|
|
{
|
|
item.Layer = layer;
|
|
return item;
|
|
}
|
|
|
|
public Item Resourced( BaseWeapon weapon, CraftResource resource )
|
|
{
|
|
weapon.Resource = resource;
|
|
return weapon;
|
|
}
|
|
|
|
public Item Resourced( BaseArmor armor, CraftResource resource )
|
|
{
|
|
armor.Resource = resource;
|
|
return armor;
|
|
}
|
|
|
|
public override void OnAfterDelete()
|
|
{
|
|
base.OnAfterDelete();
|
|
Unregister();
|
|
}
|
|
|
|
public override void OnDeath( Container c )
|
|
{
|
|
base.OnDeath( c );
|
|
|
|
c.Delete();
|
|
}
|
|
|
|
public virtual void GenerateBody( bool isFemale, bool randomHair )
|
|
{
|
|
Hue = Utility.RandomSkinHue();
|
|
|
|
if ( isFemale )
|
|
{
|
|
Female = true;
|
|
Body = 401;
|
|
Name = NameList.RandomName( "female" );
|
|
}
|
|
else
|
|
{
|
|
Female = false;
|
|
Body = 400;
|
|
Name = NameList.RandomName( "male" );
|
|
}
|
|
|
|
if ( randomHair )
|
|
GenerateRandomHair();
|
|
}
|
|
|
|
public override bool ClickTitle => false;
|
|
|
|
public BaseFactionGuard( string title ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 )
|
|
{
|
|
m_Orders = new Orders( this );
|
|
Title = title;
|
|
|
|
RangeHome = 6;
|
|
}
|
|
|
|
public BaseFactionGuard( Serial serial ) : base( serial )
|
|
{
|
|
}
|
|
|
|
public override void Serialize( GenericWriter writer )
|
|
{
|
|
base.Serialize( writer );
|
|
|
|
writer.Write( (int) 0 ); // version
|
|
|
|
Faction.WriteReference( writer, m_Faction );
|
|
Town.WriteReference( writer, m_Town );
|
|
|
|
m_Orders.Serialize( writer );
|
|
}
|
|
|
|
public override void Deserialize( GenericReader reader )
|
|
{
|
|
base.Deserialize( reader );
|
|
|
|
int version = reader.ReadInt();
|
|
|
|
m_Faction = Faction.ReadReference( reader );
|
|
m_Town = Town.ReadReference( reader );
|
|
m_Orders = new Orders( this, reader );
|
|
|
|
Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Register ) );
|
|
}
|
|
}
|
|
|
|
public class VirtualMount : IMount
|
|
{
|
|
private VirtualMountItem m_Item;
|
|
|
|
public Mobile Rider
|
|
{
|
|
get => m_Item.Rider;
|
|
set{}
|
|
}
|
|
|
|
public VirtualMount( VirtualMountItem item )
|
|
{
|
|
m_Item = item;
|
|
}
|
|
|
|
public virtual void OnRiderDamaged( int amount, Mobile from, bool willKill )
|
|
{
|
|
}
|
|
}
|
|
|
|
public class VirtualMountItem : Item, IMountItem
|
|
{
|
|
private Mobile m_Rider;
|
|
private VirtualMount m_Mount;
|
|
|
|
public Mobile Rider => m_Rider;
|
|
|
|
public VirtualMountItem( Mobile mob ) : base( 0x3EA0 )
|
|
{
|
|
Layer = Layer.Mount;
|
|
|
|
m_Rider = mob;
|
|
m_Mount = new VirtualMount( this );
|
|
}
|
|
|
|
public IMount Mount => m_Mount;
|
|
|
|
public VirtualMountItem( Serial serial ) : base( serial )
|
|
{
|
|
m_Mount = new VirtualMount( this );
|
|
}
|
|
|
|
public override void Serialize( GenericWriter writer )
|
|
{
|
|
base.Serialize( writer );
|
|
|
|
writer.Write( (int) 0 ); // version
|
|
|
|
writer.Write( (Mobile) m_Rider );
|
|
}
|
|
|
|
public override void Deserialize( GenericReader reader )
|
|
{
|
|
base.Deserialize( reader );
|
|
|
|
int version = reader.ReadInt();
|
|
|
|
m_Rider = reader.ReadMobile();
|
|
|
|
if ( m_Rider == null )
|
|
Delete();
|
|
}
|
|
}
|
|
}
|