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,41 @@
using System;
using Server.Mobiles;
namespace Server
{
public class CompassionVirtue
{
private const int LossAmount = 500;
private static TimeSpan LossDelay = TimeSpan.FromDays(7.0);
public static void Initialize()
{
VirtueGump.Register(105, 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)
{
if (!(from is PlayerMobile pm))
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
{
// ignored
}
}
}
}

View file

@ -0,0 +1,409 @@
using System;
using Server.Gumps;
using Server.Mobiles;
using Server.Regions;
using Server.Targeting;
namespace Server
{
public class HonorVirtue
{
private static readonly TimeSpan UseDelay = TimeSpan.FromMinutes(5.0);
public static void Initialize()
{
VirtueGump.Register(107, OnVirtueUsed);
}
private static void OnVirtueUsed(Mobile from)
{
if (from.Alive)
{
from.SendLocalizedMessage(1063160); // Target what you wish to honor.
from.Target = new InternalTarget();
}
}
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 = source.Region.GetRegion<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;
}
if (target.Body.IsHuman && (!(target is BaseCreature cret) || !cret.AlwaysAttackable && !cret.AlwaysMurderer))
{
if (reg?.IsDisabled() != true)
{
//Allow honor on blue if Out of guardzone
}
else if ((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;
}
source.SentHonorContext?.Cancel();
new HonorContext(source, target);
source.Direction = source.GetDirectionTo(target);
if (!source.Mounted)
source.Animate(32, 5, 1, true, true, 0);
}
private class InternalTarget : Target
{
public InternalTarget() : base(12, false, TargetFlags.None)
{
CheckLOS = true;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (!(from is PlayerMobile pm))
return;
if (targeted == pm)
EmbraceHonor(pm);
else if (targeted is Mobile mobile)
Honor(pm, mobile);
}
protected override void OnTargetOutOfRange(Mobile from, object targeted)
{
from.SendLocalizedMessage(1063232); // You are too far away to honor your opponent
}
}
}
public interface IHonorTarget
{
HonorContext ReceivedHonorContext{ get; set; }
}
public class HonorContext
{
private FirstHit m_FirstHit;
private double m_HonorDamage;
private Point3D m_InitialLocation;
private Map m_InitialMap;
private bool m_Poisoned;
private InternalTimer m_Timer;
private int m_TotalDamage;
public HonorContext(PlayerMobile source, Mobile target)
{
Source = source;
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 PlayerMobile Source{ get; }
public Mobile Target{ get; }
public int PerfectionDamageBonus{ get; private set; }
public int PerfectionLuckBonus => PerfectionDamageBonus * PerfectionDamageBonus / 10;
public void OnSourceDamaged(Mobile from, int amount)
{
if (from != 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 == Source)
{
if (Target.CanSee(Source) && Target.InLOS(Source) && (Source.InRange(Target, 1)
|| Source.Location == m_InitialLocation &&
Source.Map == m_InitialMap))
m_HonorDamage += amount;
else
m_HonorDamage += amount * 0.8;
}
else if (from is BaseCreature creature && creature.GetMaster() == Source)
{
m_HonorDamage += amount * 0.8;
}
}
public void OnTargetHit(Mobile from)
{
if (from != Source || PerfectionDamageBonus == 100)
return;
int bushido = (int)from.Skills.Bushido.Value;
if (bushido < 50)
return;
PerfectionDamageBonus += bushido / 10;
if (PerfectionDamageBonus >= 100)
{
PerfectionDamageBonus = 100;
Source.SendLocalizedMessage(1063254); // You have Achieved Perfection in inflicting damage to this opponent!
}
else
{
Source.SendLocalizedMessage(1063255); // You gain in Perfection as you precisely strike your opponent.
}
}
public void OnTargetMissed(Mobile from)
{
if (from != Source || PerfectionDamageBonus == 0)
return;
PerfectionDamageBonus -= 25;
if (PerfectionDamageBonus <= 0)
{
PerfectionDamageBonus = 0;
Source.SendLocalizedMessage(1063256); // You have lost all Perfection in fighting this opponent.
}
else
{
Source.SendLocalizedMessage(1063257); // You have lost some Perfection in fighting this opponent.
}
}
public void OnSourceBeneficialAction(Mobile to)
{
if (to != Target)
return;
if (PerfectionDamageBonus >= 0)
{
PerfectionDamageBonus = 0;
Source.SendLocalizedMessage(1063256); // You have lost all Perfection in fighting this opponent.
}
}
public void OnSourceKilled()
{
}
public void OnTargetKilled()
{
Cancel();
int targetFame = Target.Fame;
if (PerfectionDamageBonus > 0)
{
int restore = Math.Min(PerfectionDamageBonus * (targetFame + 5000) / 25000, 10);
Source.Hits += restore;
Source.Stam += restore;
Source.Mana += restore;
}
if (Source.Virtues.Honor > targetFame)
return;
double dGain =
targetFame / 100.0 * (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(Source, VirtueName.Honor))
{
Source.SendLocalizedMessage(1063228); // You cannot gain more Honor.
return;
}
bool gainedPath = false;
if (VirtueHelper.Award(Source, VirtueName.Honor, gain, ref gainedPath))
{
if (gainedPath)
Source.SendLocalizedMessage(1063226); // You have gained a path in Honor!
else
Source.SendLocalizedMessage(1063225); // You have gained in Honor.
}
}
public bool CheckDistance()
{
return true;
}
public void Cancel()
{
Source.SentHonorContext = null;
((IHonorTarget)Target).ReceivedHonorContext = null;
m_Timer.Stop();
}
private enum FirstHit
{
NotDelivered,
Delivered,
Granted
}
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,237 @@
using System;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
using Server.Targeting;
namespace Server
{
public class JusticeVirtue
{
private const int LossAmount = 950;
private static TimeSpan LossDelay = TimeSpan.FromDays(7.0);
public static void Initialize()
{
VirtueGump.Register(109, 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;
if (!(from is PlayerMobile protector))
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<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, 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<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<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<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 = $"{protector.Name}\t{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 = $"{protector.Name}\t{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<JusticeVirtue>())
Timer.DelayCall(TimeSpan.FromMinutes(15.0), protector.EndAction<JusticeVirtue>);
}
public static void CheckAtrophy(Mobile from)
{
if (!(from is PlayerMobile pm))
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
{
// ignored
}
}
}
public class AcceptProtectorGump : Gump
{
private PlayerMobile m_Protectee;
private PlayerMobile m_Protector;
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); // 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); // Yes, I would like their protection.
AddRadio(30, 145, 9727, 9730, false, 0);
AddHtmlLocalized(65, 148, 300, 25, 1049445, 0x7FFF); // No thanks, I can take care of myself.
AddButton(160, 175, 247, 248, 2);
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(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,189 @@
using System;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
using Server.Targeting;
namespace Server
{
public class SacrificeVirtue
{
private const int LossAmount = 500;
private static TimeSpan GainDelay = TimeSpan.FromDays(1.0);
private static TimeSpan LossDelay = TimeSpan.FromDays(7.0);
public static void Initialize()
{
VirtueGump.Register(110, 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)
{
if (!(from is PlayerMobile pm))
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
{
// ignored
}
}
public static void Resurrect(Mobile from)
{
if (from.Alive)
return;
if (!(from is PlayerMobile pm))
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<ResurrectGump>();
from.SendGump(new ResurrectGump(from, true));
}
}
public static void Sacrifice(Mobile from, object targeted)
{
if (!from.CheckAlive())
return;
if (!(from is PlayerMobile pm))
return;
if (!(targeted is Mobile targ))
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), 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 creature && (creature.Controlled || creature.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,142 @@
using System;
using Server.Engines.CannedEvil;
using Server.Mobiles;
using Server.Targeting;
namespace Server
{
public class ValorVirtue
{
private const int LossAmount = 250;
private static TimeSpan LossDelay = TimeSpan.FromDays(7.0);
public static void Initialize()
{
VirtueGump.Register(112, 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)
{
if (!(from is PlayerMobile pm))
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
{
// ignored
}
}
public static void Valor(Mobile from, object targ)
{
if (!(targ is IdolOfTheChampion idol) || idol.Deleted || idol.Spawn?.Deleted != false)
{
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,181 @@
using System.Collections.Generic;
using Server.Gumps;
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>();
private static int[] m_Table = {
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 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);
AddButton(186, 269, 2071, 2071, 2);
}
}
public static void Initialize()
{
EventSink.VirtueGumpRequest += EventSink_VirtueGumpRequest;
EventSink.VirtueItemRequest += EventSink_VirtueItemRequest;
EventSink.VirtueMacroRequest += 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<VirtueGump>();
if (e.Beholder.Kills >= 5)
{
e.Beholder.SendLocalizedMessage(1049609); // Murderers cannot invoke this virtue.
return;
}
if (m_Callbacks.TryGetValue(e.GumpID, out OnVirtueUsed callback))
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<VirtueGump>();
beholder.SendGump(new VirtueGump(beholder, beheld));
}
}
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 + vl];
}
public override void OnResponse(NetState state, RelayInfo info)
{
if (info.ButtonID == 1 && m_Beholder == m_Beheld)
m_Beholder.SendGump(new VirtueStatusGump(m_Beholder));
}
private class InternalEntry : GumpImage
{
private static byte[] m_Class = StringToBuffer(" class=VirtueGumpItem");
public InternalEntry(int x, int y, int gumpID, int hue) : base(x, y, gumpID, hue)
{
}
public override string Compile(NetState ns)
{
return $"{{ gumppic {X} {Y} {GumpID} hue={Hue} class=VirtueGumpItem }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
base.AppendTo(ns, disp);
disp.AppendLayout(m_Class);
}
}
}
}

View file

@ -0,0 +1,163 @@
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 (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,102 @@
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, string webPage = null) : 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);
AddHtmlLocalized(75, 95, 220, 140, description);
AddHtmlLocalized(70, 224, 229, 60, valueDesc);
AddButton(65, 277, 1209, 1209, 1);
AddButton(280, 43, 4014, 4014, 2);
AddHtmlLocalized(83, 275, 400, 40, webPage == null ? 1052055 : 1052052); // 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,107 @@
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");
AddHtmlLocalized(80, 100, 100, 40, 1051000); // Humility
AddHtmlLocalized(80, 129, 100, 40, 1051001); // Sacrifice
AddHtmlLocalized(80, 159, 100, 40, 1051002); // Compassion
AddHtmlLocalized(80, 189, 100, 40, 1051003); // Spirituality
AddHtmlLocalized(200, 100, 200, 40, 1051004); // Valor
AddHtmlLocalized(200, 129, 200, 40, 1051005); // Honor
AddHtmlLocalized(200, 159, 200, 40, 1051006); // Justice
AddHtmlLocalized(200, 189, 200, 40, 1051007); // Honesty
AddHtmlLocalized(75, 224, 220, 60, 1052062); // Click on a blue gem to view your status in that virtue.
AddButton(60, 100, 1210, 1210, 1);
AddButton(60, 129, 1210, 1210, 2);
AddButton(60, 159, 1210, 1210, 3);
AddButton(60, 189, 1210, 1210, 4);
AddButton(180, 100, 1210, 1210, 5);
AddButton(180, 129, 1210, 1210, 6);
AddButton(180, 159, 1210, 1210, 7);
AddButton(180, 189, 1210, 1210, 8);
AddButton(280, 43, 4014, 4014, 9);
}
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;
}
}
}
}
}