Logging of staff crafting items, Fixes for the OSI client changes The potion stacking stuff BaseWeapon fix, so that damage increase things don't multiply themselves. admin gump fixes for Firewall Various display tweaks for weight SoS no longer spawn in Tokuno Hiryu hues happified to OSI standards staff now see alliance chat correctly Play barkeeper text increased per OSI Various other OSI skill requirement changes Various unimplemented features of the SE moves EventSink for when the client version is received from client. Client validation moved out of the core. Client version validation now has options to ignore/kick/warn/annoy. Automagically tries to detect current client. Reverted the delayeddamagecontext back to OSI standards. Can no longer use bags of sending in jail. MagerySpell implemented. Now only Magery spells have a circle, and various other properties of 'em. TransformationSpellHelper now implemented. Things moved around for this. Spells now need a Timespan for the cast delay, instead of arbitrarily based on Circle. Circle doesn't make sense for non-Magery spells! Alot of the spellweaving spells added to OSI standards.
416 lines
No EOL
9.9 KiB
C#
416 lines
No EOL
9.9 KiB
C#
using System;
|
|
using Server;
|
|
using Server.Mobiles;
|
|
using Server.Targeting;
|
|
|
|
namespace Server
|
|
{
|
|
public class HonorVirtue
|
|
{
|
|
private static readonly TimeSpan LossDelay = TimeSpan.FromDays( 7.0 );
|
|
private static readonly int LossAmount = 500;
|
|
|
|
private static readonly TimeSpan UseDelay = TimeSpan.FromMinutes( 5.0 );
|
|
|
|
public static void Initialize()
|
|
{
|
|
VirtueGump.Register( 107, new OnVirtueUsed( OnVirtueUsed ) );
|
|
}
|
|
|
|
public static void CheckAtrophy( Mobile from )
|
|
{
|
|
PlayerMobile pm = from as PlayerMobile;
|
|
|
|
if ( pm == null )
|
|
return;
|
|
|
|
try
|
|
{
|
|
if ( (pm.LastHonorLoss + LossDelay) < DateTime.Now )
|
|
{
|
|
if ( VirtueHelper.Atrophy( from, VirtueName.Honor, LossAmount ) )
|
|
from.SendLocalizedMessage( 1063227 ); // You have lost some Honor.
|
|
|
|
pm.LastHonorLoss = DateTime.Now;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
private static void OnVirtueUsed( Mobile from )
|
|
{
|
|
if ( from.Alive )
|
|
{
|
|
from.SendLocalizedMessage( 1063160 ); // Target what you wish to honor.
|
|
from.Target = new InternalTarget();
|
|
}
|
|
}
|
|
|
|
private class InternalTarget : Target
|
|
{
|
|
public InternalTarget() : base( 12, false, TargetFlags.None )
|
|
{
|
|
CheckLOS = true;
|
|
}
|
|
|
|
protected override void OnTarget( Mobile from, object targeted )
|
|
{
|
|
PlayerMobile pm = from as PlayerMobile;
|
|
if ( pm == null )
|
|
return;
|
|
|
|
if ( targeted == pm )
|
|
EmbraceHonor( pm );
|
|
else if ( targeted is Mobile )
|
|
Honor( pm, (Mobile) targeted );
|
|
}
|
|
|
|
protected override void OnTargetOutOfRange( Mobile from, object targeted )
|
|
{
|
|
from.SendLocalizedMessage( 1063232 ); // You are too far away to honor your opponent
|
|
}
|
|
}
|
|
|
|
private static int GetHonorDuration( PlayerMobile from )
|
|
{
|
|
switch ( VirtueHelper.GetLevel( from, VirtueName.Honor ) )
|
|
{
|
|
case VirtueLevel.Seeker: return 30;
|
|
case VirtueLevel.Follower: return 90;
|
|
case VirtueLevel.Knight: return 300;
|
|
|
|
default: return 0 ;
|
|
}
|
|
}
|
|
|
|
private static void EmbraceHonor( PlayerMobile pm )
|
|
{
|
|
if ( pm.HonorActive )
|
|
{
|
|
pm.SendLocalizedMessage( 1063230 ); // You must wait awhile before you can embrace honor again.
|
|
return;
|
|
}
|
|
|
|
int duration = GetHonorDuration( pm );
|
|
if ( duration == 0 )
|
|
{
|
|
pm.SendLocalizedMessage( 1063234 ); // You do not have enough honor to do that
|
|
return;
|
|
}
|
|
|
|
TimeSpan waitTime = DateTime.Now - pm.LastHonorUse;
|
|
if ( waitTime < UseDelay )
|
|
{
|
|
TimeSpan remainingTime = UseDelay - waitTime;
|
|
int remainingMinutes = (int) Math.Ceiling( remainingTime.TotalMinutes );
|
|
|
|
pm.SendLocalizedMessage( 1063240, remainingMinutes.ToString() ); // You must wait ~1_HONOR_WAIT~ minutes before embracing honor again
|
|
return;
|
|
}
|
|
|
|
int usedPoints = pm.Virtues.Honor / 20;
|
|
VirtueHelper.Atrophy( pm, VirtueName.Honor, usedPoints );
|
|
|
|
pm.HonorActive = true;
|
|
pm.SendLocalizedMessage( 1063235 ); // You embrace your honor
|
|
|
|
Timer.DelayCall( TimeSpan.FromSeconds( duration ),
|
|
delegate() {
|
|
pm.HonorActive = false;
|
|
pm.LastHonorUse = DateTime.Now;
|
|
pm.SendLocalizedMessage( 1063236 ); // You no longer embrace your honor
|
|
} );
|
|
}
|
|
|
|
private static void Honor( PlayerMobile source, Mobile target )
|
|
{
|
|
IHonorTarget honorTarget = target as IHonorTarget;
|
|
|
|
if ( honorTarget == null || !source.CanBeHarmful( target, true ) )
|
|
return;
|
|
|
|
if ( honorTarget.ReceivedHonorContext != null )
|
|
{
|
|
if ( honorTarget.ReceivedHonorContext.Source == source )
|
|
return;
|
|
|
|
if ( honorTarget.ReceivedHonorContext.CheckDistance() )
|
|
{
|
|
source.SendLocalizedMessage( 1063233 ); // Somebody else is honoring this opponent
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( target.Hits < target.HitsMax )
|
|
{
|
|
source.SendLocalizedMessage( 1063166 ); // You cannot honor this monster because it is too damaged.
|
|
return;
|
|
}
|
|
|
|
if( Core.ML && target is PlayerMobile )
|
|
{
|
|
source.SendLocalizedMessage( 1075614 ); // You cannot honor other players.
|
|
return;
|
|
}
|
|
|
|
if ( source.SentHonorContext != null )
|
|
source.SentHonorContext.Cancel();
|
|
|
|
new HonorContext( source, target );
|
|
|
|
source.Direction = source.GetDirectionTo( target );
|
|
|
|
if ( !source.Mounted )
|
|
source.Animate( 32, 5, 1, true, true, 0 );
|
|
|
|
// OSI apparently removed this message... it's nice though
|
|
source.Say( 1063231 ); // I honor you
|
|
}
|
|
}
|
|
|
|
public interface IHonorTarget
|
|
{
|
|
HonorContext ReceivedHonorContext{ get; set; }
|
|
}
|
|
|
|
public class HonorContext
|
|
{
|
|
private PlayerMobile m_Source;
|
|
private Mobile m_Target;
|
|
|
|
private int m_HonorDamageTwentieths;
|
|
private int m_TotalDamage;
|
|
|
|
private int m_Perfection;
|
|
|
|
private enum FirstHit
|
|
{
|
|
NotDelivered,
|
|
Delivered,
|
|
Granted
|
|
}
|
|
|
|
private FirstHit m_FirstHit;
|
|
private bool m_Poisoned;
|
|
private Point3D m_InitialLocation;
|
|
private Map m_InitialMap;
|
|
|
|
private InternalTimer m_Timer;
|
|
|
|
public PlayerMobile Source{ get{ return m_Source; } }
|
|
public Mobile Target{ get{ return m_Target; } }
|
|
|
|
public HonorContext( PlayerMobile source, Mobile target )
|
|
{
|
|
m_Source = source;
|
|
m_Target = target;
|
|
|
|
m_FirstHit = FirstHit.NotDelivered;
|
|
m_Poisoned = false;
|
|
|
|
m_InitialLocation = source.Location;
|
|
m_InitialMap = source.Map;
|
|
|
|
source.SentHonorContext = this;
|
|
((IHonorTarget)target).ReceivedHonorContext = this;
|
|
|
|
m_Timer = new InternalTimer( this );
|
|
m_Timer.Start();
|
|
}
|
|
|
|
public void OnSourceDamaged( Mobile from, int amount )
|
|
{
|
|
if ( from != m_Target )
|
|
return;
|
|
|
|
if ( m_FirstHit == FirstHit.NotDelivered )
|
|
m_FirstHit = FirstHit.Granted;
|
|
}
|
|
|
|
public void OnTargetPoisoned()
|
|
{
|
|
m_Poisoned = true; // Set this flag for OnTargetDamaged which will be called next
|
|
}
|
|
|
|
public void OnTargetDamaged( Mobile from, int amount )
|
|
{
|
|
if ( m_FirstHit == FirstHit.NotDelivered )
|
|
m_FirstHit = FirstHit.Delivered;
|
|
|
|
if ( m_Poisoned )
|
|
{
|
|
m_TotalDamage += amount * 2;
|
|
m_Poisoned = false; // Reset the flag
|
|
|
|
return;
|
|
}
|
|
|
|
m_TotalDamage += amount;
|
|
|
|
if ( from == m_Source )
|
|
{
|
|
if ( m_Target.CanSee( m_Source ) && m_Target.InLOS( m_Source ) && ( m_Source.InRange( m_Target, 1 )
|
|
|| ( m_Source.Location == m_InitialLocation && m_Source.Map == m_InitialMap ) ) )
|
|
{
|
|
m_HonorDamageTwentieths += amount * 20;
|
|
}
|
|
else
|
|
{
|
|
m_HonorDamageTwentieths += amount * 2;
|
|
}
|
|
}
|
|
else if ( from is BaseCreature && ((BaseCreature)from).GetMaster() == from )
|
|
{
|
|
m_HonorDamageTwentieths += amount;
|
|
}
|
|
}
|
|
|
|
public void OnTargetHit( Mobile from )
|
|
{
|
|
if ( from != m_Source || m_Perfection == 100 )
|
|
return;
|
|
|
|
int bushido = (int) from.Skills.Bushido.Value;
|
|
if ( bushido < 50 )
|
|
return;
|
|
|
|
m_Perfection += bushido / 10;
|
|
|
|
if ( m_Perfection >= 100 )
|
|
{
|
|
m_Perfection = 100;
|
|
m_Source.SendLocalizedMessage( 1063254 ); // You have Achieved Perfection in inflicting damage to this opponent!
|
|
}
|
|
else
|
|
{
|
|
m_Source.SendLocalizedMessage( 1063255 ); // You gain in Perfection as you precisely strike your opponent.
|
|
}
|
|
}
|
|
|
|
public void OnTargetMissed( Mobile from )
|
|
{
|
|
if ( from != m_Source || m_Perfection == 0 )
|
|
return;
|
|
|
|
m_Perfection -= 25;
|
|
|
|
if ( m_Perfection <= 0 )
|
|
{
|
|
m_Perfection = 0;
|
|
m_Source.SendLocalizedMessage( 1063256 ); // You have lost all Perfection in fighting this opponent.
|
|
}
|
|
else
|
|
{
|
|
m_Source.SendLocalizedMessage( 1063257 ); // You have lost some Perfection in fighting this opponent.
|
|
}
|
|
}
|
|
|
|
public void OnSourceBeneficialAction( Mobile to )
|
|
{
|
|
if ( to != m_Target )
|
|
return;
|
|
|
|
if ( m_Perfection >= 0 )
|
|
{
|
|
m_Perfection = 0;
|
|
m_Source.SendLocalizedMessage( 1063256 ); // You have lost all Perfection in fighting this opponent.
|
|
}
|
|
}
|
|
|
|
public void OnSourceKilled()
|
|
{
|
|
Cancel();
|
|
}
|
|
|
|
public void OnTargetKilled()
|
|
{
|
|
Cancel();
|
|
|
|
int targetFame = m_Target.Fame;
|
|
|
|
if ( m_Perfection > 0 )
|
|
{
|
|
int restore = Math.Min( m_Perfection * ( targetFame + 5000 ) / 25000, 10 );
|
|
|
|
m_Source.Hits += restore;
|
|
m_Source.Stam += restore;
|
|
m_Source.Mana += restore;
|
|
}
|
|
|
|
if ( m_Source.Virtues.Honor > targetFame )
|
|
return;
|
|
|
|
double dGain = ( targetFame * m_HonorDamageTwentieths ) / (double)( 20 * m_TotalDamage );
|
|
dGain = dGain * dGain / 2000000.0;
|
|
|
|
if ( m_FirstHit == FirstHit.Granted )
|
|
dGain *= 1.1; // Is this correct?
|
|
|
|
int gain = Math.Min( (int)dGain, 200 );
|
|
|
|
if ( gain <= 0 )
|
|
return;
|
|
|
|
if ( VirtueHelper.IsHighestPath( m_Source, VirtueName.Honor ) )
|
|
{
|
|
m_Source.SendLocalizedMessage( 1063228 ); // You cannot gain more Honor.
|
|
return;
|
|
}
|
|
|
|
bool gainedPath = false;
|
|
if ( VirtueHelper.Award( m_Source, VirtueName.Honor, (int) gain, ref gainedPath ) )
|
|
{
|
|
if ( gainedPath )
|
|
m_Source.SendLocalizedMessage( 1063226 ); // You have gained a path in Honor!
|
|
else
|
|
m_Source.SendLocalizedMessage( 1063225 ); // You have gained in Honor.
|
|
}
|
|
}
|
|
|
|
public int PerfectionDamageBonus
|
|
{
|
|
get { return m_Perfection; }
|
|
}
|
|
|
|
public int PerfectionLuckBonus
|
|
{
|
|
get{ return (m_Perfection * m_Perfection) / 10; }
|
|
}
|
|
|
|
public bool CheckDistance()
|
|
{
|
|
if ( m_Source.Map == Map.Internal || m_Source.Map != m_Target.Map || !m_Source.InRange( m_Target, 24 ) )
|
|
{
|
|
Cancel();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Cancel()
|
|
{
|
|
m_Source.SentHonorContext = null;
|
|
((IHonorTarget)m_Target).ReceivedHonorContext = null;
|
|
|
|
m_Timer.Stop();
|
|
}
|
|
|
|
private class InternalTimer : Timer
|
|
{
|
|
private HonorContext m_Context;
|
|
|
|
public InternalTimer( HonorContext context ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) )
|
|
{
|
|
m_Context = context;
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
m_Context.CheckDistance();
|
|
}
|
|
}
|
|
}
|
|
} |