ModernUO/Scripts/Engines/Virtues/Honor.cs
asayre ce54fe8635 Bug fix for :
skillcaps and things that exceed it
Possible threading issue
Addition of Generic FindItemsByType  - List<T> findItemsByType<T>()
Change to make polymorph not have a timer and instead a toggle skill
KiAttack no longer works with ranged weapons
Poison strike speed and damage changed
Necro spells now affected by FC
Must have atleast 50 weapon skill if no shield equipped to use Evasion
Stalking bonus capped at 10 + skill/10
Stalking bonus now Clears
Bugfix w/Guilds
Crashguard Exception message moved closer to the top of the file.
Character creation of Pallys and necros using advanced skills now gives skill items 
Honor no longer works on Players
Pre-ML area HitArea attacks nolonger require LoS
Serialization fix for BladeOfTheRighteous
RidingSwipe now has paralyzation immunity as with NerveStrike and ParaBlow
AnkhEast renamed to AnkhNorth
LeggingsOfBane no longer have 510 HP
ElvenWashBasinEastAddon Fixed
AnsellaGryen won't be renamed each time the server Restarts
DiseasedCats are properly named
Localization fixes in DefBlacksmithy
BearMasks/DearMAsks and things that extend from them now can be repaired
2006-08-19 23:24:47 +00:00

418 lines
No EOL
10 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 void ApplyPerfectionDamageBonus( ref double factor )
{
if ( m_Perfection > 0 )
factor *= 1.0 + (m_Perfection / 100.0);
}
public void ApplyPerfectionLuckBonus( ref int luck )
{
if ( m_Perfection > 0 )
luck += (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();
}
}
}
}