#W# Source and Scripts added. Added Scripts/Settings.cs to expose some basic tweak able settings.

This commit is contained in:
WarrentyExpired 2026-08-06 11:06:05 -04:00
parent b51c58f514
commit 3045c83799
3512 changed files with 627673 additions and 0 deletions

View file

@ -0,0 +1,47 @@
using System;
using System.Collections;
using Server;
using Server.Items;
using Server.Gumps;
using Server.Mobiles;
using Server.Targeting;
namespace Server
{
public class CompassionVirtue
{
private static TimeSpan LossDelay = TimeSpan.FromDays( 7.0 );
private const int LossAmount = 500;
public static void Initialize()
{
VirtueGump.Register( 105, new OnVirtueUsed( OnVirtueUsed ) );
}
public static void OnVirtueUsed( Mobile from )
{
from.SendLocalizedMessage( 1053001 ); // This virtue is not activated through the virtue menu.
}
public static void CheckAtrophy( Mobile from )
{
PlayerMobile pm = from as PlayerMobile;
if ( pm == null )
return;
try
{
if ( (pm.LastCompassionLoss + LossDelay) < DateTime.UtcNow )
{
VirtueHelper.Atrophy( from, VirtueName.Compassion, LossAmount );
//OSI has no cliloc message for losing compassion. Weird.
pm.LastCompassionLoss = DateTime.UtcNow;
}
}
catch
{
}
}
}
}

View file

@ -0,0 +1,433 @@
using System;
using Server;
using Server.Mobiles;
using Server.Gumps;
using Server.Targeting;
using Server.Regions;
namespace Server
{
public class HonorVirtue
{
private static readonly TimeSpan UseDelay = TimeSpan.FromMinutes( 5.0 );
public static void Initialize()
{
VirtueGump.Register( 107, new OnVirtueUsed( OnVirtueUsed ) );
}
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;
}
if ( GetHonorDuration( pm ) == 0 )
{
pm.SendLocalizedMessage( 1063234 ); // You do not have enough honor to do that
return;
}
TimeSpan waitTime = DateTime.UtcNow - 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;
}
pm.SendGump( new HonorSelf( pm ) );
}
public static void ActivateEmbrace( PlayerMobile pm )
{
int duration = GetHonorDuration( pm );
int usedPoints;
if ( pm.Virtues.Honor < 4399)
usedPoints = 400;
else if ( pm.Virtues.Honor < 10599 )
usedPoints = 600;
else
usedPoints = 1000;
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.UtcNow;
pm.SendLocalizedMessage( 1063236 ); // You no longer embrace your honor
} );
}
private static void Honor( PlayerMobile source, Mobile target )
{
IHonorTarget honorTarget = target as IHonorTarget;
GuardedRegion reg = (GuardedRegion) source.Region.GetRegion( typeof( GuardedRegion ) );
Map map = source.Map;
if ( honorTarget == null )
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;
}
BaseCreature cret = target as BaseCreature;
if ( target.Body.IsHuman && (cret == null || (!cret.AlwaysAttackable && !cret.AlwaysMurderer)) )
{
if( reg == null || reg.IsDisabled() )
{
//Allow honor on blue if Out of guardzone
}
else if ( map != null && (map.Rules & MapRules.HarmfulRestrictions) == 0 )
{
//Allow honor on blue if in Fel
}
else
{
source.SendLocalizedMessage( 1001018 ); // You cannot perform negative acts
return; //cannot honor in trammel town on blue
}
}
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 );
}
}
public interface IHonorTarget
{
HonorContext ReceivedHonorContext{ get; set; }
}
public class HonorContext
{
private PlayerMobile m_Source;
private Mobile m_Target;
private double m_HonorDamage;
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();
source.m_hontime = (DateTime.UtcNow + TimeSpan.FromMinutes( 40 ));
Timer.DelayCall( TimeSpan.FromMinutes( 40 ),
delegate() {
if (source.m_hontime < DateTime.UtcNow && source.SentHonorContext != null)
{
Cancel();
}
} );
}
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_HonorDamage += amount * 0.8;
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_HonorDamage += amount;
}
else
{
m_HonorDamage += amount * 0.8;
}
}
else if ( from is BaseCreature && ((BaseCreature)from).GetMaster() == m_Source )
{
m_HonorDamage += amount * 0.8;
}
}
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()
{
return;
}
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 / 100 ) * (m_HonorDamage / m_TotalDamage ); //Initial honor gain is 100th of the monsters honor
if ( m_HonorDamage == m_TotalDamage && m_FirstHit == FirstHit.Granted)
dGain = dGain * 1.5; //honor gain is increased alot more if the combat was fully honorable
else
dGain = dGain * 0.9;
int gain = Math.Min( (int)dGain, 200 );
if ( gain < 1 )
gain=1; //Minimum gain of 1 honor when the honor is under the monsters fame
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()
{
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();
}
}
}
}

View file

@ -0,0 +1,250 @@
using System;
using System.Collections;
using Server;
using Server.Items;
using Server.Gumps;
using Server.Mobiles;
using Server.Targeting;
namespace Server
{
public class JusticeVirtue
{
private static TimeSpan LossDelay = TimeSpan.FromDays( 7.0 );
private const int LossAmount = 950;
public static void Initialize()
{
VirtueGump.Register( 109, new OnVirtueUsed( OnVirtueUsed ) );
}
public static bool CheckMapRegion( Mobile first, Mobile second )
{
Map map = first.Map;
if ( second.Map != map )
return false;
return GetMapRegion( map, first.Location ) == GetMapRegion( map, second.Location );
}
public static int GetMapRegion( Map map, Point3D loc )
{
if ( map == null || map.MapID >= 2 )
return 0;
if ( loc.X < 5120 )
return 0;
if ( loc.Y < 2304 )
return 1;
return 2;
}
public static void OnVirtueUsed( Mobile from )
{
if ( !from.CheckAlive() )
return;
PlayerMobile protector = from as PlayerMobile;
if ( protector == null )
return;
if ( !VirtueHelper.IsSeeker( protector, VirtueName.Justice ) )
{
protector.SendLocalizedMessage( 1049610 ); // You must reach the first path in this virtue to invoke it.
}
else if ( !protector.CanBeginAction( typeof( JusticeVirtue ) ) )
{
protector.SendLocalizedMessage( 1049370 ); // You must wait a while before offering your protection again.
}
else if ( protector.JusticeProtectors.Count > 0 )
{
protector.SendLocalizedMessage( 1049542 ); // You cannot protect someone while being protected.
}
else if ( protector.Map != Map.Felucca )
{
protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here.
}
else
{
protector.BeginTarget( 14, false, TargetFlags.None, new TargetCallback( OnVirtueTargeted ) );
protector.SendLocalizedMessage( 1049366 ); // Choose the player you wish to protect.
}
}
public static void OnVirtueTargeted( Mobile from, object obj )
{
PlayerMobile protector = from as PlayerMobile;
PlayerMobile pm = obj as PlayerMobile;
if ( protector == null )
return;
if ( !VirtueHelper.IsSeeker( protector, VirtueName.Justice ) )
protector.SendLocalizedMessage( 1049610 ); // You must reach the first path in this virtue to invoke it.
else if ( !protector.CanBeginAction( typeof( JusticeVirtue ) ) )
protector.SendLocalizedMessage( 1049370 ); // You must wait a while before offering your protection again.
else if ( protector.JusticeProtectors.Count > 0 )
protector.SendLocalizedMessage( 1049542 ); // You cannot protect someone while being protected.
else if ( protector.Map != Map.Felucca )
protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here.
else if ( pm == null )
protector.SendLocalizedMessage( 1049678 ); // Only players can be protected.
else if ( pm.Map != Map.Felucca )
protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here.
else if ( pm == protector || pm.Criminal || pm.Kills >= 5 )
protector.SendLocalizedMessage( 1049436 ); // That player cannot be protected.
else if ( pm.JusticeProtectors.Count > 0 )
protector.SendLocalizedMessage( 1049369 ); // You cannot protect that player right now.
else if ( pm.HasGump( typeof( AcceptProtectorGump ) ) )
protector.SendLocalizedMessage( 1049369 ); // You cannot protect that player right now.
else
pm.SendGump( new AcceptProtectorGump( protector, pm ) );
}
public static void OnVirtueAccepted( PlayerMobile protector, PlayerMobile protectee )
{
if ( !VirtueHelper.IsSeeker( protector, VirtueName.Justice ) )
{
protector.SendLocalizedMessage( 1049610 ); // You must reach the first path in this virtue to invoke it.
}
else if ( !protector.CanBeginAction( typeof( JusticeVirtue ) ) )
{
protector.SendLocalizedMessage( 1049370 ); // You must wait a while before offering your protection again.
}
else if ( protector.JusticeProtectors.Count > 0 )
{
protector.SendLocalizedMessage( 1049542 ); // You cannot protect someone while being protected.
}
else if ( protector.Map != Map.Felucca )
{
protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here.
}
else if ( protectee.Map != Map.Felucca )
{
protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here.
}
else if ( protectee == protector || protectee.Criminal || protectee.Kills >= 5 )
{
protector.SendLocalizedMessage( 1049436 ); // That player cannot be protected.
}
else if ( protectee.JusticeProtectors.Count > 0 )
{
protector.SendLocalizedMessage( 1049369 ); // You cannot protect that player right now.
}
else
{
protectee.JusticeProtectors.Add( protector );
string args = String.Format( "{0}\t{1}", protector.Name, protectee.Name );
protectee.SendLocalizedMessage( 1049451, args ); // You are now being protected by ~1_NAME~.
protector.SendLocalizedMessage( 1049452, args ); // You are now protecting ~2_NAME~.
}
}
public static void OnVirtueRejected( PlayerMobile protector, PlayerMobile protectee )
{
string args = String.Format( "{0}\t{1}", protector.Name, protectee.Name );
protectee.SendLocalizedMessage( 1049453, args ); // You have declined protection from ~1_NAME~.
protector.SendLocalizedMessage( 1049454, args ); // ~2_NAME~ has declined your protection.
if ( protector.BeginAction( typeof( JusticeVirtue ) ) )
Timer.DelayCall( TimeSpan.FromMinutes( 15.0 ), new TimerStateCallback( RejectDelay_Callback ), protector );
}
public static void RejectDelay_Callback( object state )
{
Mobile m = state as Mobile;
if ( m != null )
m.EndAction( typeof( JusticeVirtue ) );
}
public static void CheckAtrophy( Mobile from )
{
PlayerMobile pm = from as PlayerMobile;
if ( pm == null )
return;
try
{
if ( (pm.LastJusticeLoss + LossDelay) < DateTime.UtcNow )
{
if ( VirtueHelper.Atrophy( from, VirtueName.Justice, LossAmount ) )
from.SendLocalizedMessage( 1049373 ); // You have lost some Justice.
pm.LastJusticeLoss = DateTime.UtcNow;
}
}
catch
{
}
}
}
public class AcceptProtectorGump : Gump
{
private PlayerMobile m_Protector;
private PlayerMobile m_Protectee;
public AcceptProtectorGump( PlayerMobile protector, PlayerMobile protectee ) : base( 150, 50 )
{
m_Protector = protector;
m_Protectee = protectee;
Closable = false;
AddPage( 0 );
AddBackground( 0, 0, 396, 218, 3600 );
AddImageTiled( 15, 15, 365, 190, 2624 );
AddAlphaRegion( 15, 15, 365, 190 );
AddHtmlLocalized( 30, 20, 360, 25, 1049365, 0x7FFF, false, false ); // Another player is offering you their <a href="?ForceTopic88">protection</a>:
AddLabel( 90, 55, 1153, protector.Name );
AddImage( 50, 45, 9005 );
AddImageTiled( 80, 80, 200, 1, 9107 );
AddImageTiled( 95, 82, 200, 1, 9157 );
AddRadio( 30, 110, 9727, 9730, true, 1 );
AddHtmlLocalized( 65, 115, 300, 25, 1049444, 0x7FFF, false, false ); // Yes, I would like their protection.
AddRadio( 30, 145, 9727, 9730, false, 0 );
AddHtmlLocalized( 65, 148, 300, 25, 1049445, 0x7FFF, false, false ); // No thanks, I can take care of myself.
AddButton( 160, 175, 247, 248, 2, GumpButtonType.Reply, 0 );
AddImage( 215, 0, 50581 );
AddImageTiled( 15, 14, 365, 1, 9107 );
AddImageTiled( 380, 14, 1, 190, 9105 );
AddImageTiled( 15, 205, 365, 1, 9107 );
AddImageTiled( 15, 14, 1, 190, 9105 );
AddImageTiled( 0, 0, 395, 1, 9157 );
AddImageTiled( 394, 0, 1, 217, 9155 );
AddImageTiled( 0, 216, 395, 1, 9157 );
AddImageTiled( 0, 0, 1, 217, 9155 );
}
public override void OnResponse( Server.Network.NetState sender, RelayInfo info )
{
if ( info.ButtonID == 2 )
{
bool okay = info.IsSwitched( 1 );
if ( okay )
JusticeVirtue.OnVirtueAccepted( m_Protector, m_Protectee );
else
JusticeVirtue.OnVirtueRejected( m_Protector, m_Protectee );
}
}
}
}

View file

@ -0,0 +1,194 @@
using System;
using Server;
using Server.Gumps;
using Server.Mobiles;
using Server.Targeting;
using Server.Network;
namespace Server
{
public class SacrificeVirtue
{
private static TimeSpan GainDelay = TimeSpan.FromDays( 1.0 );
private static TimeSpan LossDelay = TimeSpan.FromDays( 7.0 );
private const int LossAmount = 500;
public static void Initialize()
{
VirtueGump.Register( 110, new OnVirtueUsed( OnVirtueUsed ) );
}
public static void OnVirtueUsed( Mobile from )
{
if ( !from.Hidden )
{
if ( from.Alive )
from.Target = new InternalTarget();
else
Resurrect( from );
}
else
from.SendLocalizedMessage( 1052015 ); // You cannot do that while hidden.
}
public static void CheckAtrophy( Mobile from )
{
PlayerMobile pm = from as PlayerMobile;
if ( pm == null )
return;
try
{
if ( (pm.LastSacrificeLoss + LossDelay) < DateTime.UtcNow )
{
if ( VirtueHelper.Atrophy( from, VirtueName.Sacrifice, LossAmount ) )
from.SendLocalizedMessage( 1052041 ); // You have lost some Sacrifice.
VirtueLevel level = VirtueHelper.GetLevel( from, VirtueName.Sacrifice );
pm.AvailableResurrects = (int)level;
pm.LastSacrificeLoss = DateTime.UtcNow;
}
}
catch
{
}
}
public static void Resurrect( Mobile from )
{
if ( from.Alive )
return;
PlayerMobile pm = from as PlayerMobile;
if ( pm == null )
return;
if ( from.Criminal )
{
from.SendLocalizedMessage( 1052007 ); // You cannot use this ability while flagged as a criminal.
}
else if ( !VirtueHelper.IsSeeker( from, VirtueName.Sacrifice ) )
{
from.SendLocalizedMessage( 1052004 ); // You cannot use this ability.
}
else if ( pm.AvailableResurrects <= 0 )
{
from.SendLocalizedMessage( 1052005 ); // You do not have any resurrections left.
}
else
{
/*
* We need to wait for them to accept the gump or they can just use
* Sacrifice and cancel to have items in their backpack for free.
*/
from.CloseGump( typeof( ResurrectGump ) );
from.SendGump( new ResurrectGump( from, true ) );
}
}
public static void Sacrifice( Mobile from, object targeted )
{
if ( !from.CheckAlive() )
return;
PlayerMobile pm = from as PlayerMobile;
if ( pm == null )
return;
Mobile targ = targeted as Mobile;
if ( targ == null )
return;
if ( !ValidateCreature( targ ) )
{
from.SendLocalizedMessage( 1052014 ); // You cannot sacrifice your fame for that creature.
}
else if ( ((targ.Hits * 100) / Math.Max( targ.HitsMax, 1 )) < 90 )
{
from.SendLocalizedMessage( 1052013 ); // You cannot sacrifice for this monster because it is too damaged.
}
else if ( from.Hidden )
{
from.SendLocalizedMessage( 1052015 ); // You cannot do that while hidden.
}
else if ( VirtueHelper.IsHighestPath( from, VirtueName.Sacrifice ) )
{
from.SendLocalizedMessage( 1052068 ); // You have already attained the highest path in this virtue.
}
else if ( from.Fame < 2500 )
{
from.SendLocalizedMessage( 1052017 ); // You do not have enough fame to sacrifice.
}
else if ( DateTime.UtcNow < (pm.LastSacrificeGain + GainDelay) )
{
from.SendLocalizedMessage( 1052016 ); // You must wait approximately one day before sacrificing again.
}
else
{
int toGain;
if( from.Fame < 5000 )
toGain = 500;
else if( from.Fame < 10000 )
toGain = 1000;
else
toGain = 2000;
from.Fame = 0;
// I have seen the error of my ways!
targ.PublicOverheadMessage( MessageType.Regular, 0x3B2, 1052009 );
from.SendLocalizedMessage( 1052010 ); // You have set the creature free.
Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerCallback( targ.Delete ) );
pm.LastSacrificeGain = DateTime.UtcNow;
bool gainedPath = false;
if ( VirtueHelper.Award( from, VirtueName.Sacrifice, toGain, ref gainedPath ) )
{
if ( gainedPath )
{
from.SendLocalizedMessage( 1052008 ); // You have gained a path in Sacrifice!
if ( pm.AvailableResurrects < 3 )
++pm.AvailableResurrects;
}
else
{
from.SendLocalizedMessage( 1054160 ); // You have gained in sacrifice.
}
}
from.SendLocalizedMessage( 1052016 ); // You must wait approximately one day before sacrificing again.
}
}
public static bool ValidateCreature( Mobile m )
{
if ( m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned) )
return false;
return ( m is Lich || m is Succubus || m is Daemon || m is EvilMage || m is EnslavedGargoyle || m is GargoyleEnforcer );
}
private class InternalTarget : Target
{
public InternalTarget() : base( 8, false, TargetFlags.None )
{
}
protected override void OnTarget( Mobile from, object targeted )
{
Sacrifice( from, targeted );
}
}
}
}

View file

@ -0,0 +1,138 @@
using System;
using System.Collections;
using Server;
using Server.Items;
using Server.Gumps;
using Server.Mobiles;
using Server.Targeting;
using Server.Engines.CannedEvil;
namespace Server
{
public class ValorVirtue
{
private static TimeSpan LossDelay = TimeSpan.FromDays( 7.0 );
private const int LossAmount = 250;
public static void Initialize()
{
VirtueGump.Register( 112, new OnVirtueUsed( OnVirtueUsed ) );
}
public static void OnVirtueUsed( Mobile from )
{
if( from.Alive )
{
from.SendLocalizedMessage( 1054034 ); // Target the Champion Idol of the Champion you wish to challenge!.
from.Target = new InternalTarget();
}
}
public static void CheckAtrophy( Mobile from )
{
PlayerMobile pm = from as PlayerMobile;
if( pm == null )
return;
try
{
if( (pm.LastValorLoss + LossDelay) < DateTime.UtcNow )
{
if( VirtueHelper.Atrophy( from, VirtueName.Valor, LossAmount ) )
from.SendLocalizedMessage( 1054040 ); // You have lost some Valor.
pm.LastValorLoss = DateTime.UtcNow;
}
}
catch
{
}
}
public static void Valor( Mobile from, object targ )
{
IdolOfTheChampion idol = targ as IdolOfTheChampion;
if( idol == null || idol.Deleted || idol.Spawn == null || idol.Spawn.Deleted )
from.SendLocalizedMessage( 1054035 ); // You must target a Champion Idol to challenge the Champion's spawn!
else if( from.Hidden )
from.SendLocalizedMessage( 1052015 ); // You cannot do that while hidden.
else if( idol.Spawn.HasBeenAdvanced )
from.SendLocalizedMessage( 1054038 ); // The Champion of this region has already been challenged!
else
{
VirtueLevel vl = VirtueHelper.GetLevel( from, VirtueName.Valor );
if( idol.Spawn.Active )
{
if( idol.Spawn.Champion != null ) //TODO: Message?
return;
int needed, consumed;
switch( idol.Spawn.GetSubLevel() )
{
case 0:
{
needed = consumed = 2500;
break;
}
case 1:
{
needed = consumed = 5000;
break;
}
case 2:
{
needed = 10000;
consumed = 7500;
break;
}
default:
{
needed = 20000;
consumed = 10000;
break;
}
}
if( from.Virtues.GetValue( (int)VirtueName.Valor ) >= needed )
{
VirtueHelper.Atrophy( from, VirtueName.Valor, consumed );
from.SendLocalizedMessage( 1054037 ); // Your challenge is heard by the Champion of this region! Beware its wrath!
idol.Spawn.HasBeenAdvanced = true;
idol.Spawn.AdvanceLevel();
}
else
from.SendLocalizedMessage( 1054039 ); // The Champion of this region ignores your challenge. You must further prove your valor.
}
else
{
if( vl == VirtueLevel.Knight )
{
VirtueHelper.Atrophy( from, VirtueName.Valor, 11000 );
from.SendLocalizedMessage( 1054037 ); // Your challenge is heard by the Champion of this region! Beware its wrath!
idol.Spawn.EndRestart();
idol.Spawn.HasBeenAdvanced = true;
}
else
{
from.SendLocalizedMessage( 1054036 ); // You must be a Knight of Valor to summon the champion's spawn in this manner!
}
}
}
}
private class InternalTarget : Target
{
public InternalTarget() : base( 14, false, TargetFlags.None )
{
}
protected override void OnTarget( Mobile from, object targeted )
{
Valor( from, targeted );
}
}
}
}

View file

@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using Server;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
namespace Server
{
public delegate void OnVirtueUsed( Mobile from );
public class VirtueGump : Gump
{
private static Dictionary<int, OnVirtueUsed> m_Callbacks = new Dictionary<int, OnVirtueUsed>();
public static void Initialize()
{
EventSink.VirtueGumpRequest += new VirtueGumpRequestEventHandler( EventSink_VirtueGumpRequest );
EventSink.VirtueItemRequest += new VirtueItemRequestEventHandler( EventSink_VirtueItemRequest );
EventSink.VirtueMacroRequest += new VirtueMacroRequestEventHandler( EventSink_VirtueMacroRequest );
}
public static void Register( int gumpID, OnVirtueUsed callback )
{
m_Callbacks[gumpID] = callback;
}
private static void EventSink_VirtueItemRequest( VirtueItemRequestEventArgs e )
{
if ( e.Beholder != e.Beheld )
return;
e.Beholder.CloseGump( typeof( VirtueGump ) );
if ( e.Beholder.Kills >= 5 )
{
e.Beholder.SendLocalizedMessage( 1049609 ); // Murderers cannot invoke this virtue.
return;
}
OnVirtueUsed callback = null;
m_Callbacks.TryGetValue(e.GumpID, out callback);
if ( callback != null )
callback( e.Beholder );
else
e.Beholder.SendLocalizedMessage( 1052066 ); // That virtue is not active yet.
}
private static void EventSink_VirtueMacroRequest( VirtueMacroRequestEventArgs e )
{
int virtueID = 0;
switch ( e.VirtueID )
{
case 0: // Honor
virtueID = 107; break;
case 1: // Sacrifice
virtueID = 110; break;
case 2: // Valor;
virtueID = 112; break;
}
EventSink_VirtueItemRequest( new VirtueItemRequestEventArgs( e.Mobile, e.Mobile, virtueID ) );
}
private static void EventSink_VirtueGumpRequest( VirtueGumpRequestEventArgs e )
{
Mobile beholder = e.Beholder;
Mobile beheld = e.Beheld;
if ( beholder == beheld && beholder.Kills >= 5 )
{
beholder.SendLocalizedMessage( 1049609 ); // Murderers cannot invoke this virtue.
}
else if ( beholder.Map == beheld.Map && beholder.InRange( beheld, 12 ) )
{
beholder.CloseGump( typeof( VirtueGump ) );
beholder.SendGump( new VirtueGump( beholder, beheld ) );
}
}
private Mobile m_Beholder, m_Beheld;
public VirtueGump( Mobile beholder, Mobile beheld ) : base( 0, 0 )
{
m_Beholder = beholder;
m_Beheld = beheld;
Serial = beheld.Serial;
AddPage( 0 );
AddImage( 30, 40, 104 );
AddPage( 1 );
Add( new InternalEntry( 61, 71, 108, GetHueFor( 0 ) ) ); // Humility
Add( new InternalEntry( 123, 46, 112, GetHueFor( 4 ) ) ); // Valor
Add( new InternalEntry( 187, 70, 107, GetHueFor( 5 ) ) ); // Honor
Add( new InternalEntry( 35, 135, 110, GetHueFor( 1 ) ) ); // Sacrifice
Add( new InternalEntry( 211, 133, 105, GetHueFor( 2 ) ) ); // Compassion
Add( new InternalEntry( 61, 195, 111, GetHueFor( 3 ) ) ); // Spiritulaity
Add( new InternalEntry( 186, 195, 109, GetHueFor( 6 ) ) ); // Justice
Add( new InternalEntry( 121, 221, 106, GetHueFor( 7 ) ) ); // Honesty
if ( m_Beholder == m_Beheld )
{
AddButton( 57, 269, 2027, 2027, 1, GumpButtonType.Reply, 0 );
AddButton( 186, 269, 2071, 2071, 2, GumpButtonType.Reply, 0 );
}
}
private static int[] m_Table = new int[24]
{
0x0481, 0x0963, 0x0965,
0x060A, 0x060F, 0x002A,
0x08A4, 0x08A7, 0x0034,
0x0965, 0x08FD, 0x0480,
0x00EA, 0x0845, 0x0020,
0x0011, 0x0269, 0x013D,
0x08A1, 0x08A3, 0x0042,
0x0543, 0x0547, 0x0061
};
private int GetHueFor( int index )
{
if ( m_Beheld.Virtues.GetValue( index ) == 0 )
return 2402;
int value = m_Beheld.Virtues.GetValue( index );
if ( value < 4000 )
return 2402;
if( value >= 30000 )
value = 20000; //Sanity
int vl;
if( value < 10000 )
vl = 0;
else if( value >= 20000 && index == 5)
vl = 2;
else if( value >= 21000 && index != 1)
vl = 2;
else if( value >= 22000 && index == 1)
vl = 2;
else
vl = 1;
return m_Table[(index * 3) + (int) vl];
}
private class InternalEntry : GumpImage
{
public InternalEntry( int x, int y, int gumpID, int hue ) : base( x, y, gumpID, hue )
{
}
public override string Compile()
{
return String.Format( "{{ gumppic {0} {1} {2} hue={3} class=VirtueGumpItem }}", X, Y, GumpID, Hue );
}
private static byte[] m_Class = Gump.StringToBuffer( " class=VirtueGumpItem" );
public override void AppendTo( IGumpWriter disp )
{
base.AppendTo( disp );
disp.AppendLayout( m_Class );
}
}
public override void OnResponse( NetState state, RelayInfo info )
{
if ( info.ButtonID == 1 && m_Beholder == m_Beheld )
m_Beholder.SendGump( new VirtueStatusGump( m_Beholder ) );
}
}
}

View file

@ -0,0 +1,162 @@
using System;
using Server.Mobiles;
namespace Server
{
public enum VirtueLevel
{
None,
Seeker,
Follower,
Knight
}
public enum VirtueName
{
Humility,
Sacrifice,
Compassion,
Spirituality,
Valor,
Honor,
Justice,
Honesty
}
public class VirtueHelper
{
public static bool HasAny( Mobile from, VirtueName virtue )
{
return ( from.Virtues.GetValue( (int)virtue ) > 0 );
}
public static bool IsHighestPath( Mobile from, VirtueName virtue )
{
return ( from.Virtues.GetValue( (int)virtue ) >= GetMaxAmount( virtue ) );
}
public static VirtueLevel GetLevel( Mobile from, VirtueName virtue )
{
int v = from.Virtues.GetValue( (int)virtue );
int vl;
int vmax = GetMaxAmount( virtue );
if ( v < 4000 )
vl = 0;
else if ( v >= vmax)
vl = 3;
else
vl = ( v + 9999 ) / 10000;
return (VirtueLevel)vl;
}
public static int GetMaxAmount( VirtueName virtue )
{
if( virtue == VirtueName.Honor )
return 20000;
if( virtue == VirtueName.Sacrifice )
return 22000;
return 21000;
}
public static bool Award( Mobile from, VirtueName virtue, int amount, ref bool gainedPath )
{
int current = from.Virtues.GetValue( (int)virtue );
int maxAmount = GetMaxAmount( virtue );
if ( current >= maxAmount )
return false;
if( (current + amount) >= maxAmount )
amount = maxAmount - current;
VirtueLevel oldLevel = GetLevel( from, virtue );
from.Virtues.SetValue( (int)virtue, current + amount );
gainedPath = ( GetLevel( from, virtue ) != oldLevel );
return true;
}
public static bool Atrophy( Mobile from, VirtueName virtue )
{
return Atrophy( from, virtue, 1 );
}
public static bool Atrophy( Mobile from, VirtueName virtue, int amount )
{
int current = from.Virtues.GetValue( (int)virtue );
if( (current - amount) >= 0 )
from.Virtues.SetValue( (int)virtue, current - amount );
else
from.Virtues.SetValue( (int)virtue, 0 );
return ( current > 0 );
}
public static bool IsSeeker( Mobile from, VirtueName virtue )
{
return ( GetLevel( from, virtue ) >= VirtueLevel.Seeker );
}
public static bool IsFollower( Mobile from, VirtueName virtue )
{
return ( GetLevel( from, virtue ) >= VirtueLevel.Follower );
}
public static bool IsKnight( Mobile from, VirtueName virtue )
{
return ( GetLevel( from, virtue ) >= VirtueLevel.Knight );
}
public static void AwardVirtue( PlayerMobile pm, VirtueName virtue, int amount )
{
if ( virtue == VirtueName.Compassion )
{
if ( pm.CompassionGains > 0 && DateTime.UtcNow > pm.NextCompassionDay )
{
pm.NextCompassionDay = DateTime.MinValue;
pm.CompassionGains = 0;
}
if ( pm.CompassionGains >= 5 )
{
pm.SendLocalizedMessage( 1053004 ); // You must wait about a day before you can gain in compassion again.
return;
}
}
bool gainedPath = false;
string virtueName = Enum.GetName( typeof( VirtueName ), virtue );
if ( VirtueHelper.Award( pm, virtue, amount, ref gainedPath ) )
{
// TODO: Localize?
if ( gainedPath )
pm.SendMessage( "You have gained a path in {0}!", virtueName );
else
pm.SendMessage( "You have gained in {0}.", virtueName );
if ( virtue == VirtueName.Compassion )
{
pm.NextCompassionDay = DateTime.UtcNow + TimeSpan.FromDays( 1.0 );
++pm.CompassionGains;
if ( pm.CompassionGains >= 5 )
pm.SendLocalizedMessage( 1053004 ); // You must wait about a day before you can gain in compassion again.
}
}
else
{
// TODO: Localize?
pm.SendMessage( "You have achieved the highest path of {0} and can no longer gain any further.", virtueName );
}
}
}
}

View file

@ -0,0 +1,110 @@
using System;
using Server;
using Server.Gumps;
using Server.Network;
namespace Server
{
public class VirtueInfoGump : Gump
{
private Mobile m_Beholder;
private int m_Desc;
private string m_Page;
private VirtueName m_Virtue;
public VirtueInfoGump( Mobile beholder, VirtueName virtue, int description ) : this( beholder, virtue, description, null )
{
}
public VirtueInfoGump( Mobile beholder, VirtueName virtue, int description, string webPage ) : base( 0, 0 )
{
m_Beholder = beholder;
m_Virtue = virtue;
m_Desc = description;
m_Page = webPage;
int value = beholder.Virtues.GetValue( (int)virtue );
AddPage( 0 );
AddImage( 30, 40, 2080 );
AddImage( 47, 77, 2081 );
AddImage( 47, 147, 2081 );
AddImage( 47, 217, 2081 );
AddImage( 47, 267, 2083 );
AddImage( 70, 213, 2091 );
AddPage( 1 );
int maxValue = VirtueHelper.GetMaxAmount( m_Virtue );
int valueDesc;
int dots;
if( value < 4000 )
dots = value / 400;
else if( value < 10000 )
dots = (value - 4000) / 600;
else if( value < maxValue )
dots = (value - 10000) / ((maxValue-10000)/10);
else
dots = 10;
for ( int i = 0; i < 10; ++i )
AddImage( 95 + (i * 17), 50, i < dots ? 2362 : 2360 );
if( value < 1 )
valueDesc = 1052044; // You have not started on the path of this Virtue.
else if( value < 400 )
valueDesc = 1052045; // You have barely begun your journey through the path of this Virtue.
else if( value < 2000 )
valueDesc = 1052046; // You have progressed in this Virtue, but still have much to do.
else if( value < 3600 )
valueDesc = 1052047; // Your journey through the path of this Virtue is going well.
else if( value < 4000 )
valueDesc = 1052048; // You feel very close to achieving your next path in this Virtue.
else if( dots < 1 )
valueDesc = 1052049; // You have achieved a path in this Virtue.
else if( dots < 9 )
valueDesc = 1052047; // Your journey through the path of this Virtue is going well.
else if( dots < 10 )
valueDesc = 1052048; // You feel very close to achieving your next path in this Virtue.
else
valueDesc = 1052050; // You have achieved the highest path in this Virtue.
AddHtmlLocalized( 157, 73, 200, 40, 1051000 + (int)virtue, false, false );
AddHtmlLocalized( 75, 95, 220, 140, description, false, false );
AddHtmlLocalized( 70, 224, 229, 60, valueDesc, false, false );
AddButton( 65, 277, 1209, 1209, 1, GumpButtonType.Reply, 0 );
AddButton( 280, 43, 4014, 4014, 2, GumpButtonType.Reply, 0 );
AddHtmlLocalized( 83, 275, 400, 40, (webPage == null) ? 1052055 : 1052052, false, false ); // This virtue is not yet defined. OR -click to learn more (opens webpage)
}
public override void OnResponse( NetState state, RelayInfo info )
{
switch ( info.ButtonID )
{
case 1:
{
m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, m_Virtue, m_Desc, m_Page ) );
if( m_Page != null )
state.Send( new LaunchBrowser( m_Page ) ); //No message about web browser starting on OSI
break;
}
case 2:
{
m_Beholder.SendGump( new VirtueStatusGump( m_Beholder ) );
break;
}
}
}
}
}

View file

@ -0,0 +1,104 @@
using System;
using Server;
using Server.Gumps;
using Server.Network;
namespace Server
{
public class VirtueStatusGump : Gump
{
private Mobile m_Beholder;
public VirtueStatusGump( Mobile beholder ) : base( 0, 0 )
{
m_Beholder = beholder;
AddPage( 0 );
AddImage( 30, 40, 2080 );
AddImage( 47, 77, 2081 );
AddImage( 47, 147, 2081 );
AddImage( 47, 217, 2081 );
AddImage( 47, 267, 2083 );
AddImage( 70, 213, 2091 );
AddPage( 1 );
AddHtml( 140, 73, 200, 20, "The Virtues", false, false );
AddHtmlLocalized( 80, 100, 100, 40, 1051000, false, false ); // Humility
AddHtmlLocalized( 80, 129, 100, 40, 1051001, false, false ); // Sacrifice
AddHtmlLocalized( 80, 159, 100, 40, 1051002, false, false ); // Compassion
AddHtmlLocalized( 80, 189, 100, 40, 1051003, false, false ); // Spirituality
AddHtmlLocalized( 200, 100, 200, 40, 1051004, false, false ); // Valor
AddHtmlLocalized( 200, 129, 200, 40, 1051005, false, false ); // Honor
AddHtmlLocalized( 200, 159, 200, 40, 1051006, false, false ); // Justice
AddHtmlLocalized( 200, 189, 200, 40, 1051007, false, false ); // Honesty
AddHtmlLocalized( 75, 224, 220, 60, 1052062, false, false ); // Click on a blue gem to view your status in that virtue.
AddButton( 60, 100, 1210, 1210, 1, GumpButtonType.Reply, 0 );
AddButton( 60, 129, 1210, 1210, 2, GumpButtonType.Reply, 0 );
AddButton( 60, 159, 1210, 1210, 3, GumpButtonType.Reply, 0 );
AddButton( 60, 189, 1210, 1210, 4, GumpButtonType.Reply, 0 );
AddButton( 180, 100, 1210, 1210, 5, GumpButtonType.Reply, 0 );
AddButton( 180, 129, 1210, 1210, 6, GumpButtonType.Reply, 0 );
AddButton( 180, 159, 1210, 1210, 7, GumpButtonType.Reply, 0 );
AddButton( 180, 189, 1210, 1210, 8, GumpButtonType.Reply, 0 );
AddButton( 280, 43, 4014, 4014, 9, GumpButtonType.Reply, 0 );
}
public override void OnResponse( NetState state, RelayInfo info )
{
switch ( info.ButtonID )
{
case 1:
{
m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Humility, 1052051 ) );
break;
}
case 2:
{
m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Sacrifice, 1052053, @"http://update.uo.com/design_389.html" ) );
break;
}
case 3:
{
m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Compassion, 1053000, @"http://update.uo.com/design_412.html" ) );
break;
}
case 4:
{
m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Spirituality, 1052056 ) );
break;
}
case 5:
{
m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Valor, 1054033, @"http://update.uo.com/design_427.html" ) );
break;
}
case 6:
{
m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Honor, 1052058, @"http://guide.uo.com/virtues_2.html" ) );
break;
}
case 7:
{
m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Justice, 1052059, @"http://update.uo.com/design_413.html" ) );
break;
}
case 8:
{
m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Honesty, 1052060 ) );
break;
}
case 9:
{
m_Beholder.SendGump( new VirtueGump( m_Beholder, m_Beholder ) );
break;
}
}
}
}
}