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,553 @@
using System;
using System.Collections.Generic;
using System.Net;
using Server.Mobiles;
namespace Server.Factions
{
public class Election
{
public const int MaxCandidates = 10;
public const int CandidateRank = 5;
public static readonly TimeSpan PendingPeriod = TimeSpan.FromDays(5.0);
public static readonly TimeSpan CampaignPeriod = TimeSpan.FromDays(1.0);
public static readonly TimeSpan VotingPeriod = TimeSpan.FromDays(3.0);
private Timer m_Timer;
public Election(Faction faction)
{
Faction = faction;
Candidates = new List<Candidate>();
StartTimer();
}
public Election(GenericReader reader)
{
int version = reader.ReadEncodedInt();
switch (version)
{
case 0:
{
Faction = Faction.ReadReference(reader);
LastStateTime = reader.ReadDateTime();
CurrentState = (ElectionState)reader.ReadEncodedInt();
Candidates = new List<Candidate>();
int count = reader.ReadEncodedInt();
for (int i = 0; i < count; ++i)
{
Candidate cd = new Candidate(reader);
if (cd.Mobile != null)
Candidates.Add(cd);
}
break;
}
}
StartTimer();
}
public Faction Faction{ get; }
public List<Candidate> Candidates{ get; }
public ElectionState State
{
get => CurrentState;
set
{
CurrentState = value;
LastStateTime = DateTime.UtcNow;
}
}
public DateTime LastStateTime{ get; private set; }
[CommandProperty(AccessLevel.GameMaster)]
public ElectionState CurrentState{ get; private set; }
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public TimeSpan NextStateTime
{
get
{
TimeSpan period;
switch (CurrentState)
{
default:
case ElectionState.Pending:
period = PendingPeriod;
break;
case ElectionState.Election:
period = VotingPeriod;
break;
case ElectionState.Campaign:
period = CampaignPeriod;
break;
}
TimeSpan until = LastStateTime + period - DateTime.UtcNow;
if (until < TimeSpan.Zero)
until = TimeSpan.Zero;
return until;
}
set
{
TimeSpan period;
switch (CurrentState)
{
default:
case ElectionState.Pending:
period = PendingPeriod;
break;
case ElectionState.Election:
period = VotingPeriod;
break;
case ElectionState.Campaign:
period = CampaignPeriod;
break;
}
LastStateTime = DateTime.UtcNow - period + value;
}
}
public void StartTimer()
{
m_Timer = Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), Slice);
}
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(0); // version
Faction.WriteReference(writer, Faction);
writer.Write(LastStateTime);
writer.WriteEncodedInt((int)CurrentState);
writer.WriteEncodedInt(Candidates.Count);
for (int i = 0; i < Candidates.Count; ++i)
Candidates[i].Serialize(writer);
}
public void AddCandidate(Mobile mob)
{
if (IsCandidate(mob))
return;
Candidates.Add(new Candidate(mob));
mob.SendLocalizedMessage(1010117); // You are now running for office.
}
public void RemoveVoter(Mobile mob)
{
if (CurrentState == ElectionState.Election)
for (int i = 0; i < Candidates.Count; ++i)
{
List<Voter> voters = Candidates[i].Voters;
for (int j = 0; j < voters.Count; ++j)
{
Voter voter = voters[j];
if (voter.From == mob)
voters.RemoveAt(j--);
}
}
}
public void RemoveCandidate(Mobile mob)
{
Candidate cd = FindCandidate(mob);
if (cd == null)
return;
Candidates.Remove(cd);
mob.SendLocalizedMessage(1038031);
if (CurrentState == ElectionState.Election)
{
if (Candidates.Count == 1)
{
Faction.Broadcast(
1038031); // There are no longer any valid candidates in the Faction Commander election.
Candidate winner = Candidates[0];
Mobile winMob = winner.Mobile;
PlayerState pl = PlayerState.Find(winMob);
if (pl == null || pl.Faction != Faction || winMob == Faction.Commander)
{
Faction.Broadcast(1038026); // Faction leadership has not changed.
}
else
{
Faction.Broadcast(1038028); // The faction has a new commander.
Faction.Commander = winMob;
}
Candidates.Clear();
State = ElectionState.Pending;
}
else if (Candidates.Count == 0) // well, I guess this'll never happen
{
Faction.Broadcast(
1038031); // There are no longer any valid candidates in the Faction Commander election.
Candidates.Clear();
State = ElectionState.Pending;
}
}
}
public bool IsCandidate(Mobile mob)
{
return FindCandidate(mob) != null;
}
public bool CanVote(Mobile mob)
{
return CurrentState == ElectionState.Election && !HasVoted(mob);
}
public bool HasVoted(Mobile mob)
{
return FindVoter(mob) != null;
}
public Candidate FindCandidate(Mobile mob)
{
for (int i = 0; i < Candidates.Count; ++i)
if (Candidates[i].Mobile == mob)
return Candidates[i];
return null;
}
public Candidate FindVoter(Mobile mob)
{
for (int i = 0; i < Candidates.Count; ++i)
{
List<Voter> voters = Candidates[i].Voters;
for (int j = 0; j < voters.Count; ++j)
{
Voter voter = voters[j];
if (voter.From == mob)
return Candidates[i];
}
}
return null;
}
public bool CanBeCandidate(Mobile mob)
{
if (IsCandidate(mob))
return false;
if (Candidates.Count >= MaxCandidates)
return false;
if (CurrentState != ElectionState.Campaign)
return false; // sanity..
PlayerState pl = PlayerState.Find(mob);
return pl != null && pl.Faction == Faction && pl.Rank.Rank >= CandidateRank;
}
public void Slice()
{
if (Faction.Election != this)
{
m_Timer?.Stop();
m_Timer = null;
return;
}
switch (CurrentState)
{
case ElectionState.Pending:
{
if (LastStateTime + PendingPeriod > DateTime.UtcNow)
break;
Faction.Broadcast(1038023); // Campaigning for the Faction Commander election has begun.
Candidates.Clear();
State = ElectionState.Campaign;
break;
}
case ElectionState.Campaign:
{
if (LastStateTime + CampaignPeriod > DateTime.UtcNow)
break;
if (Candidates.Count == 0)
{
Faction.Broadcast(1038025); // Nobody ran for office.
State = ElectionState.Pending;
}
else if (Candidates.Count == 1)
{
Faction.Broadcast(1038029); // Only one member ran for office.
Candidate winner = Candidates[0];
Mobile mob = winner.Mobile;
PlayerState pl = PlayerState.Find(mob);
if (pl == null || pl.Faction != Faction || mob == Faction.Commander)
{
Faction.Broadcast(1038026); // Faction leadership has not changed.
}
else
{
Faction.Broadcast(1038028); // The faction has a new commander.
Faction.Commander = mob;
}
Candidates.Clear();
State = ElectionState.Pending;
}
else
{
Faction.Broadcast(1038030);
State = ElectionState.Election;
}
break;
}
case ElectionState.Election:
{
if (LastStateTime + VotingPeriod > DateTime.UtcNow)
break;
Faction.Broadcast(1038024); // The results for the Faction Commander election are in
Candidate winner = null;
for (int i = 0; i < Candidates.Count; ++i)
{
Candidate cd = Candidates[i];
PlayerState pl = PlayerState.Find(cd.Mobile);
if (pl == null || pl.Faction != Faction)
continue;
//cd.CleanMuleVotes();
if (winner == null || cd.Votes > winner.Votes)
winner = cd;
}
if (winner == null)
{
Faction.Broadcast(1038026); // Faction leadership has not changed.
}
else if (winner.Mobile == Faction.Commander)
{
Faction.Broadcast(1038027); // The incumbent won the election.
}
else
{
Faction.Broadcast(1038028); // The faction has a new commander.
Faction.Commander = winner.Mobile;
}
Candidates.Clear();
State = ElectionState.Pending;
break;
}
}
}
}
public class Voter
{
public Voter(Mobile from, Mobile candidate)
{
From = from;
Candidate = candidate;
if (From.NetState != null)
Address = From.NetState.Address;
else
Address = IPAddress.None;
Time = DateTime.UtcNow;
}
public Voter(GenericReader reader, Mobile candidate)
{
Candidate = candidate;
int version = reader.ReadEncodedInt();
switch (version)
{
case 0:
{
From = reader.ReadMobile();
Address = Utility.Intern(reader.ReadIPAddress());
Time = reader.ReadDateTime();
break;
}
}
}
public Mobile From{ get; }
public Mobile Candidate{ get; }
public IPAddress Address{ get; }
public DateTime Time{ get; }
public object[] AcquireFields()
{
TimeSpan gameTime = TimeSpan.Zero;
if (From is PlayerMobile mobile)
gameTime = mobile.GameTime;
int kp = 0;
PlayerState pl = PlayerState.Find(From);
if (pl != null)
kp = pl.KillPoints;
int sk = From.Skills.Total;
int factorSkills = 50 + sk * 100 / 10000;
int factorKillPts = 100 + kp * 2;
int factorGameTime = 50 + (int)(gameTime.Ticks * 100 / TimeSpan.TicksPerDay);
int totalFactor = factorSkills * factorKillPts * Math.Max(factorGameTime, 100) / 10000;
if (totalFactor > 100)
totalFactor = 100;
else if (totalFactor < 0)
totalFactor = 0;
return new object[] { From, Address, Time, totalFactor };
}
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(0);
writer.Write(From);
writer.Write(Address);
writer.Write(Time);
}
}
public class Candidate
{
public Candidate(Mobile mob)
{
Mobile = mob;
Voters = new List<Voter>();
}
public Candidate(GenericReader reader)
{
int version = reader.ReadEncodedInt();
switch (version)
{
case 1:
{
Mobile = reader.ReadMobile();
int count = reader.ReadEncodedInt();
Voters = new List<Voter>(count);
for (int i = 0; i < count; ++i)
{
Voter voter = new Voter(reader, Mobile);
if (voter.From != null)
Voters.Add(voter);
}
break;
}
case 0:
{
Mobile = reader.ReadMobile();
List<Mobile> mobs = reader.ReadStrongMobileList();
Voters = new List<Voter>(mobs.Count);
for (int i = 0; i < mobs.Count; ++i)
Voters.Add(new Voter(mobs[i], Mobile));
break;
}
}
}
public Mobile Mobile{ get; }
public List<Voter> Voters{ get; }
public int Votes => Voters.Count;
public void CleanMuleVotes()
{
for (int i = 0; i < Voters.Count; ++i)
{
Voter voter = Voters[i];
if ((int)voter.AcquireFields()[3] < 90)
Voters.RemoveAt(i--);
}
}
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(1); // version
writer.Write(Mobile);
writer.WriteEncodedInt(Voters.Count);
for (int i = 0; i < Voters.Count; ++i)
Voters[i].Serialize(writer);
}
}
public enum ElectionState
{
Pending,
Campaign,
Election
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,137 @@
using System;
namespace Server.Factions
{
public interface IFactionItem
{
FactionItem FactionItemState{ get; set; }
}
public class FactionItem
{
public static readonly TimeSpan ExpirationPeriod = TimeSpan.FromDays(21.0);
public FactionItem(Item item, Faction faction)
{
Item = item;
Faction = faction;
}
public FactionItem(GenericReader reader, Faction faction)
{
int version = reader.ReadEncodedInt();
switch (version)
{
case 0:
{
Item = reader.ReadItem();
Expiration = reader.ReadDateTime();
break;
}
}
Faction = faction;
}
public Item Item{ get; }
public Faction Faction{ get; }
public DateTime Expiration{ get; private set; }
public bool HasExpired
{
get
{
if (Item?.Deleted != false)
return true;
return Expiration != DateTime.MinValue && DateTime.UtcNow >= Expiration;
}
}
public void StartExpiration()
{
Expiration = DateTime.UtcNow + ExpirationPeriod;
}
public void CheckAttach()
{
if (!HasExpired)
Attach();
else
Detach();
}
public void Attach()
{
if (Item is IFactionItem item)
item.FactionItemState = this;
Faction?.State.FactionItems.Add(this);
}
public void Detach()
{
if (Item is IFactionItem item)
item.FactionItemState = null;
if (Faction?.State.FactionItems.Contains(this) == true)
Faction.State.FactionItems.Remove(this);
}
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(0);
writer.Write(Item);
writer.Write(Expiration);
}
public static int GetMaxWearables(Mobile mob)
{
PlayerState pl = PlayerState.Find(mob);
return pl == null ? 0 : pl.Faction.IsCommander(mob) ? 9 : pl.Rank.MaxWearables;
}
public static FactionItem Find(Item item)
{
if (item is IFactionItem factionItem)
{
FactionItem state = factionItem.FactionItemState;
if (state?.HasExpired == true)
{
state.Detach();
state = null;
}
return state;
}
return null;
}
public static Item Imbue(Item item, Faction faction, bool expire, int hue)
{
if (!(item is IFactionItem))
return item;
FactionItem state = Find(item);
if (state == null)
{
state = new FactionItem(item, faction);
state.Attach();
}
if (expire)
state.StartExpiration();
item.Hue = hue;
return item;
}
}
}

View file

@ -0,0 +1,273 @@
using System;
using System.Collections.Generic;
namespace Server.Factions
{
public class FactionState
{
private const int BroadcastsPerPeriod = 2;
private static readonly TimeSpan BroadcastPeriod = TimeSpan.FromHours(1.0);
private Mobile m_Commander;
private Faction m_Faction;
private DateTime[] m_LastBroadcasts = new DateTime[BroadcastsPerPeriod];
public FactionState(Faction faction)
{
m_Faction = faction;
Tithe = 50;
Members = new List<PlayerState>();
Election = new Election(faction);
FactionItems = new List<FactionItem>();
Traps = new List<BaseFactionTrap>();
}
public FactionState(GenericReader reader)
{
int version = reader.ReadEncodedInt();
switch (version)
{
case 5:
{
LastAtrophy = reader.ReadDateTime();
goto case 4;
}
case 4:
{
int count = reader.ReadEncodedInt();
for (int i = 0; i < count; ++i)
{
DateTime time = reader.ReadDateTime();
if (i < m_LastBroadcasts.Length)
m_LastBroadcasts[i] = time;
}
goto case 3;
}
case 3:
case 2:
case 1:
{
Election = new Election(reader);
goto case 0;
}
case 0:
{
m_Faction = Faction.ReadReference(reader);
m_Commander = reader.ReadMobile();
if (version < 5)
LastAtrophy = DateTime.UtcNow;
if (version < 4)
{
DateTime time = reader.ReadDateTime();
if (m_LastBroadcasts.Length > 0)
m_LastBroadcasts[0] = time;
}
Tithe = reader.ReadEncodedInt();
Silver = reader.ReadEncodedInt();
int memberCount = reader.ReadEncodedInt();
Members = new List<PlayerState>();
for (int i = 0; i < memberCount; ++i)
{
PlayerState pl = new PlayerState(reader, m_Faction, Members);
if (pl.Mobile != null)
Members.Add(pl);
}
m_Faction.State = this;
m_Faction.ZeroRankOffset = Members.Count;
Members.Sort();
for (int i = Members.Count - 1; i >= 0; i--)
{
PlayerState player = Members[i];
if (player.KillPoints <= 0)
m_Faction.ZeroRankOffset = i;
else
player.RankIndex = i;
}
FactionItems = new List<FactionItem>();
if (version >= 2)
{
int factionItemCount = reader.ReadEncodedInt();
for (int i = 0; i < factionItemCount; ++i)
{
FactionItem factionItem = new FactionItem(reader, m_Faction);
Timer.DelayCall(TimeSpan.Zero, factionItem.CheckAttach); // sandbox attachment
}
}
Traps = new List<BaseFactionTrap>();
if (version >= 3)
{
int factionTrapCount = reader.ReadEncodedInt();
for (int i = 0; i < factionTrapCount; ++i)
if (reader.ReadItem() is BaseFactionTrap trap && !trap.CheckDecay())
Traps.Add(trap);
}
break;
}
}
if (version < 1)
Election = new Election(m_Faction);
}
public DateTime LastAtrophy{ get; set; }
public bool FactionMessageReady
{
get
{
for (int i = 0; i < m_LastBroadcasts.Length; ++i)
if (DateTime.UtcNow >= m_LastBroadcasts[i] + BroadcastPeriod)
return true;
return false;
}
}
public bool IsAtrophyReady => DateTime.UtcNow >= LastAtrophy + TimeSpan.FromHours(47.0);
public List<FactionItem> FactionItems{ get; set; }
public List<BaseFactionTrap> Traps{ get; set; }
public Election Election{ get; set; }
public Mobile Commander
{
get => m_Commander;
set
{
m_Commander?.InvalidateProperties();
m_Commander = value;
if (m_Commander != null)
{
m_Commander.SendLocalizedMessage(1042227); // You have been elected Commander of your faction
m_Commander.InvalidateProperties();
PlayerState pl = PlayerState.Find(m_Commander);
if (pl?.Finance != null)
pl.Finance.Finance = null;
if (pl?.Sheriff != null)
pl.Sheriff.Sheriff = null;
}
}
}
public int Tithe{ get; set; }
public int Silver{ get; set; }
public List<PlayerState> Members{ get; set; }
public int CheckAtrophy()
{
if (DateTime.UtcNow < LastAtrophy + TimeSpan.FromHours(47.0))
return 0;
int distrib = 0;
LastAtrophy = DateTime.UtcNow;
List<PlayerState> members = new List<PlayerState>(Members);
for (int i = 0; i < members.Count; ++i)
{
PlayerState ps = members[i];
if (ps.IsActive)
{
ps.IsActive = false;
continue;
}
if (ps.KillPoints > 0)
{
int atrophy = (ps.KillPoints + 9) / 10;
ps.KillPoints -= atrophy;
distrib += atrophy;
}
}
return distrib;
}
public void RegisterBroadcast()
{
for (int i = 0; i < m_LastBroadcasts.Length; ++i)
if (DateTime.UtcNow >= m_LastBroadcasts[i] + BroadcastPeriod)
{
m_LastBroadcasts[i] = DateTime.UtcNow;
break;
}
}
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(5); // version
writer.Write(LastAtrophy);
writer.WriteEncodedInt(m_LastBroadcasts.Length);
for (int i = 0; i < m_LastBroadcasts.Length; ++i)
writer.Write(m_LastBroadcasts[i]);
Election.Serialize(writer);
Faction.WriteReference(writer, m_Faction);
writer.Write(m_Commander);
writer.WriteEncodedInt(Tithe);
writer.WriteEncodedInt(Silver);
writer.WriteEncodedInt(Members.Count);
for (int i = 0; i < Members.Count; ++i)
{
PlayerState pl = Members[i];
pl.Serialize(writer);
}
writer.WriteEncodedInt(FactionItems.Count);
for (int i = 0; i < FactionItems.Count; ++i)
FactionItems[i].Serialize(writer);
writer.WriteEncodedInt(Traps.Count);
for (int i = 0; i < Traps.Count; ++i)
writer.Write(Traps[i]);
}
}
}

View file

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Server.Commands;
namespace Server.Factions
{
public class Generator
{
public static void Initialize()
{
CommandSystem.Register("GenerateFactions", AccessLevel.Administrator, GenerateFactions_OnCommand);
}
public static void GenerateFactions_OnCommand(CommandEventArgs e)
{
new FactionPersistance();
List<Faction> factions = Faction.Factions;
foreach (Faction faction in factions)
Generate(faction);
List<Town> towns = Town.Towns;
foreach (Town town in towns)
Generate(town);
}
public static void Generate(Town town)
{
Map facet = Faction.Facet;
TownDefinition def = town.Definition;
if (!CheckExistance(def.Monolith, facet, typeof(TownMonolith)))
{
TownMonolith mono = new TownMonolith(town);
mono.MoveToWorld(def.Monolith, facet);
mono.Sigil = new Sigil(town);
}
if (!CheckExistance(def.TownStone, facet, typeof(TownStone)))
new TownStone(town).MoveToWorld(def.TownStone, facet);
}
public static void Generate(Faction faction)
{
Map facet = Faction.Facet;
List<Town> towns = Town.Towns;
StrongholdDefinition stronghold = faction.Definition.Stronghold;
if (!CheckExistance(stronghold.JoinStone, facet, typeof(JoinStone)))
new JoinStone(faction).MoveToWorld(stronghold.JoinStone, facet);
if (!CheckExistance(stronghold.FactionStone, facet, typeof(FactionStone)))
new FactionStone(faction).MoveToWorld(stronghold.FactionStone, facet);
for (int i = 0; i < stronghold.Monoliths.Length; ++i)
{
Point3D monolith = stronghold.Monoliths[i];
if (!CheckExistance(monolith, facet, typeof(StrongholdMonolith)))
new StrongholdMonolith(towns[i], faction).MoveToWorld(monolith, facet);
}
}
private static bool CheckExistance(Point3D loc, Map facet, Type type)
{
return facet.GetItemsInRange(loc, 0).Any(type.IsInstanceOfType);
}
}
}

View file

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
namespace Server.Factions
{
public class GuardList
{
public GuardList(GuardDefinition definition)
{
Definition = definition;
Guards = new List<BaseFactionGuard>();
}
public GuardDefinition Definition{ get; }
public List<BaseFactionGuard> Guards{ get; }
public BaseFactionGuard Construct()
{
try
{
return Activator.CreateInstance(Definition.Type) as BaseFactionGuard;
}
catch
{
return null;
}
}
}
}

View file

@ -0,0 +1,154 @@
using System;
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class Keywords
{
public static void Initialize()
{
EventSink.Speech += EventSink_Speech;
}
private static void ShowScore_Sandbox(PlayerState pl)
{
pl?.Mobile.PublicOverheadMessage(MessageType.Regular, pl.Mobile.SpeechHue, true,
pl.KillPoints.ToString("N0")); // NOTE: Added 'N0'
}
private static void EventSink_Speech(SpeechEventArgs e)
{
Mobile from = e.Mobile;
int[] keywords = e.Keywords;
for (int i = 0; i < keywords.Length; ++i)
switch (keywords[i])
{
case 0x00E4: // *i wish to access the city treasury*
{
Town town = Town.FromRegion(from.Region);
if (town == null || !town.IsFinance(from) || !from.Alive)
break;
if (FactionGump.Exists(from))
from.SendLocalizedMessage(1042160); // You already have a faction menu open.
else if (town.Owner != null && from is PlayerMobile mobile)
mobile.SendGump(new FinanceGump(mobile, town.Owner, town));
break;
}
case 0x0ED: // *i am sheriff*
{
Town town = Town.FromRegion(from.Region);
if (town == null || !town.IsSheriff(from) || !from.Alive)
break;
if (FactionGump.Exists(from))
from.SendLocalizedMessage(1042160); // You already have a faction menu open.
else if (town.Owner != null)
from.SendGump(new SheriffGump((PlayerMobile)from, town.Owner, town));
break;
}
case 0x00EF: // *you are fired*
{
Town town = Town.FromRegion(from.Region);
if (town == null)
break;
if (town.IsFinance(from) || town.IsSheriff(from))
town.BeginOrderFiring(from);
break;
}
case 0x00E5: // *i wish to resign as finance minister*
{
PlayerState pl = PlayerState.Find(from);
if (pl?.Finance != null)
{
pl.Finance.Finance = null;
from.SendLocalizedMessage(1005081); // You have been fired as Finance Minister
}
break;
}
case 0x00EE: // *i wish to resign as sheriff*
{
PlayerState pl = PlayerState.Find(from);
if (pl?.Sheriff != null)
{
pl.Sheriff.Sheriff = null;
from.SendLocalizedMessage(1010270); // You have been fired as Sheriff
}
break;
}
case 0x00E9: // *what is my faction term status*
{
PlayerState pl = PlayerState.Find(from);
if (pl?.IsLeaving == true)
{
if (Faction.CheckLeaveTimer(from))
break;
TimeSpan remaining = pl.Leaving + Faction.LeavePeriod - DateTime.UtcNow;
if (remaining.TotalDays >= 1)
from.SendLocalizedMessage(1042743,
remaining.TotalDays
.ToString("N0")); // Your term of service will come to an end in ~1_DAYS~ days.
else if (remaining.TotalHours >= 1)
from.SendLocalizedMessage(1042741,
remaining.TotalHours
.ToString("N0")); // Your term of service will come to an end in ~1_HOURS~ hours.
else
from.SendLocalizedMessage(
1042742); // Your term of service will come to an end in less than one hour.
}
else if (pl != null)
{
from.SendLocalizedMessage(1042233); // You are not in the process of quitting the faction.
}
break;
}
case 0x00EA: // *message faction*
{
Faction faction = Faction.Find(from);
if (faction == null || !faction.IsCommander(from))
break;
if (from.AccessLevel == AccessLevel.Player && !faction.FactionMessageReady)
from.SendLocalizedMessage(
1010264); // The required time has not yet passed since the last message was sent
else
faction.BeginBroadcast(from);
break;
}
case 0x00EC: // *showscore*
{
PlayerState pl = PlayerState.Find(from);
if (pl != null)
Timer.DelayCall(TimeSpan.Zero, ShowScore_Sandbox, pl);
break;
}
case 0x0178: // i honor your leadership
{
Faction.Find(from)?.BeginHonorLeadership(from);
break;
}
}
}
}
}

View file

@ -0,0 +1,92 @@
namespace Server.Factions
{
public enum MerchantTitle
{
None,
Scribe,
Carpenter,
Blacksmith,
Bowyer,
Tialor
}
public class MerchantTitleInfo
{
public MerchantTitleInfo(SkillName skill, double requirement, TextDefinition title, TextDefinition label,
TextDefinition assigned)
{
Skill = skill;
Requirement = requirement;
Title = title;
Label = label;
Assigned = assigned;
}
public SkillName Skill{ get; }
public double Requirement{ get; }
public TextDefinition Title{ get; }
public TextDefinition Label{ get; }
public TextDefinition Assigned{ get; }
}
public class MerchantTitles
{
public static MerchantTitleInfo[] Info{ get; } =
{
new MerchantTitleInfo(SkillName.Inscribe, 90.0, new TextDefinition(1060773, "Scribe"),
new TextDefinition(1011468, "SCRIBE"),
new TextDefinition(1010121, "You now have the faction title of scribe")),
new MerchantTitleInfo(SkillName.Carpentry, 90.0, new TextDefinition(1060774, "Carpenter"),
new TextDefinition(1011469, "CARPENTER"),
new TextDefinition(1010122, "You now have the faction title of carpenter")),
new MerchantTitleInfo(SkillName.Tinkering, 90.0, new TextDefinition(1022984, "Tinker"),
new TextDefinition(1011470, "TINKER"),
new TextDefinition(1010123, "You now have the faction title of tinker")),
new MerchantTitleInfo(SkillName.Blacksmith, 90.0, new TextDefinition(1023016, "Blacksmith"),
new TextDefinition(1011471, "BLACKSMITH"),
new TextDefinition(1010124, "You now have the faction title of blacksmith")),
new MerchantTitleInfo(SkillName.Fletching, 90.0, new TextDefinition(1023022, "Bowyer"),
new TextDefinition(1011472, "BOWYER"),
new TextDefinition(1010125, "You now have the faction title of Bowyer")),
new MerchantTitleInfo(SkillName.Tailoring, 90.0, new TextDefinition(1022982, "Tailor"),
new TextDefinition(1018300, "TAILOR"),
new TextDefinition(1042162, "You now have the faction title of Tailor"))
};
public static MerchantTitleInfo GetInfo(MerchantTitle title)
{
int idx = (int)title - 1;
if (idx >= 0 && idx < Info.Length)
return Info[idx];
return null;
}
public static bool HasMerchantQualifications(Mobile mob)
{
for (int i = 0; i < Info.Length; ++i)
if (IsQualified(mob, Info[i]))
return true;
return false;
}
public static bool IsQualified(Mobile mob, MerchantTitle title)
{
return IsQualified(mob, GetInfo(title));
}
public static bool IsQualified(Mobile mob, MerchantTitleInfo info)
{
if (mob == null || info == null)
return false;
return mob.Skills[info.Skill].Value >= info.Requirement;
}
}
}

View file

@ -0,0 +1,90 @@
using System.Collections.Generic;
namespace Server.Factions
{
public class FactionPersistance : Item
{
public FactionPersistance() : base(1)
{
Movable = false;
if (Instance?.Deleted == true)
Instance = this;
else
base.Delete();
}
public FactionPersistance(Serial serial) : base(serial)
{
Instance = this;
}
public static FactionPersistance Instance{ get; private set; }
public override string DefaultName => "Faction Persistance - Internal";
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
List<Faction> factions = Faction.Factions;
for (int i = 0; i < factions.Count; ++i)
{
writer.WriteEncodedInt((int)PersistedType.Faction);
factions[i].State.Serialize(writer);
}
List<Town> towns = Town.Towns;
for (int i = 0; i < towns.Count; ++i)
{
writer.WriteEncodedInt((int)PersistedType.Town);
towns[i].State.Serialize(writer);
}
writer.WriteEncodedInt((int)PersistedType.Terminator);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
PersistedType type;
while ((type = (PersistedType)reader.ReadEncodedInt()) != PersistedType.Terminator)
switch (type)
{
case PersistedType.Faction:
new FactionState(reader);
break;
case PersistedType.Town:
new TownState(reader);
break;
}
break;
}
}
}
public override void Delete()
{
}
private enum PersistedType
{
Terminator,
Faction,
Town
}
}
}

View file

@ -0,0 +1,303 @@
using System;
using System.Collections.Generic;
using Server.Mobiles;
namespace Server.Factions
{
public class PlayerState : IComparable<PlayerState>
{
private Town m_Finance;
private bool m_InvalidateRank = true;
private int m_KillPoints;
private MerchantTitle m_MerchantTitle;
private RankDefinition m_Rank;
private int m_RankIndex = -1;
private Town m_Sheriff;
public PlayerState(Mobile mob, Faction faction, List<PlayerState> owner)
{
Mobile = mob;
Faction = faction;
Owner = owner;
Attach();
Invalidate();
}
public PlayerState(GenericReader reader, Faction faction, List<PlayerState> owner)
{
Faction = faction;
Owner = owner;
int version = reader.ReadEncodedInt();
switch (version)
{
case 1:
{
IsActive = reader.ReadBool();
LastHonorTime = reader.ReadDateTime();
goto case 0;
}
case 0:
{
Mobile = reader.ReadMobile();
m_KillPoints = reader.ReadEncodedInt();
m_MerchantTitle = (MerchantTitle)reader.ReadEncodedInt();
Leaving = reader.ReadDateTime();
break;
}
}
Attach();
}
public Mobile Mobile{ get; }
public Faction Faction{ get; }
public List<PlayerState> Owner{ get; }
public MerchantTitle MerchantTitle
{
get => m_MerchantTitle;
set
{
m_MerchantTitle = value;
Invalidate();
}
}
public Town Sheriff
{
get => m_Sheriff;
set
{
m_Sheriff = value;
Invalidate();
}
}
public Town Finance
{
get => m_Finance;
set
{
m_Finance = value;
Invalidate();
}
}
public List<SilverGivenEntry> SilverGiven{ get; private set; }
public int KillPoints
{
get => m_KillPoints;
set
{
if (m_KillPoints != value)
{
if (value > m_KillPoints)
{
if (m_KillPoints <= 0)
{
if (value <= 0)
{
m_KillPoints = value;
Invalidate();
return;
}
Owner.Remove(this);
Owner.Insert(Faction.ZeroRankOffset, this);
m_RankIndex = Faction.ZeroRankOffset;
Faction.ZeroRankOffset++;
}
while (m_RankIndex - 1 >= 0)
{
PlayerState p = Owner[m_RankIndex - 1];
if (value > p.KillPoints)
{
Owner[m_RankIndex] = p;
Owner[m_RankIndex - 1] = this;
RankIndex--;
p.RankIndex++;
}
else
{
break;
}
}
}
else
{
if (value <= 0)
{
if (m_KillPoints <= 0)
{
m_KillPoints = value;
Invalidate();
return;
}
while (m_RankIndex + 1 < Faction.ZeroRankOffset)
{
PlayerState p = Owner[m_RankIndex + 1];
Owner[m_RankIndex + 1] = this;
Owner[m_RankIndex] = p;
RankIndex++;
p.RankIndex--;
}
m_RankIndex = -1;
Faction.ZeroRankOffset--;
}
else
{
while (m_RankIndex + 1 < Faction.ZeroRankOffset)
{
PlayerState p = Owner[m_RankIndex + 1];
if (value < p.KillPoints)
{
Owner[m_RankIndex + 1] = this;
Owner[m_RankIndex] = p;
RankIndex++;
p.RankIndex--;
}
else
{
break;
}
}
}
}
m_KillPoints = value;
Invalidate();
}
}
}
public int RankIndex
{
get => m_RankIndex;
set
{
if (m_RankIndex != value)
{
m_RankIndex = value;
m_InvalidateRank = true;
}
}
}
public RankDefinition Rank
{
get
{
if (m_InvalidateRank)
{
RankDefinition[] ranks = Faction.Definition.Ranks;
int percent;
if (Owner.Count == 1)
percent = 1000;
else if (m_RankIndex == -1)
percent = 0;
else
percent = (Faction.ZeroRankOffset - m_RankIndex) * 1000 / Faction.ZeroRankOffset;
for (int i = 0; i < ranks.Length; i++)
{
RankDefinition check = ranks[i];
if (percent >= check.Required)
{
m_Rank = check;
m_InvalidateRank = false;
break;
}
}
Invalidate();
}
return m_Rank;
}
}
public DateTime LastHonorTime{ get; set; }
public DateTime Leaving{ get; set; }
public bool IsLeaving => Leaving > DateTime.MinValue;
public bool IsActive{ get; set; }
public int CompareTo(PlayerState ps)
{
return (ps?.m_KillPoints ?? 0) - m_KillPoints;
}
public bool CanGiveSilverTo(Mobile mob)
{
for (int i = 0; i < SilverGiven?.Count; ++i)
{
SilverGivenEntry sge = SilverGiven[i];
if (sge.IsExpired)
SilverGiven.RemoveAt(i--);
else if (sge.GivenTo == mob)
return false;
}
return true;
}
public void OnGivenSilverTo(Mobile mob)
{
if (SilverGiven == null)
SilverGiven = new List<SilverGivenEntry>();
SilverGiven.Add(new SilverGivenEntry(mob));
}
public void Invalidate()
{
(Mobile as PlayerMobile)?.InvalidateProperties();
}
public void Attach()
{
if (Mobile is PlayerMobile mobile)
mobile.FactionPlayerState = this;
}
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(1); // version
writer.Write(IsActive);
writer.Write(LastHonorTime);
writer.Write(Mobile);
writer.WriteEncodedInt(m_KillPoints);
writer.WriteEncodedInt((int)m_MerchantTitle);
writer.Write(Leaving);
}
public static PlayerState Find(Mobile mob)
{
return mob is PlayerMobile mobile ? mobile.FactionPlayerState : null;
}
}
}

View file

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Factions
{
public class Reflector
{
private static List<Town> m_Towns;
private static List<Faction> m_Factions;
public static List<Town> Towns
{
get
{
if (m_Towns == null)
ProcessTypes();
return m_Towns;
}
}
public static List<Faction> Factions
{
get
{
if (m_Factions == null)
ProcessTypes();
return m_Factions;
}
}
private static object Construct(Type type)
{
try
{
return Activator.CreateInstance(type);
}
catch
{
return null;
}
}
private static void ProcessTypes()
{
m_Factions = new List<Faction>();
m_Towns = new List<Town>();
Assembly[] asms = ScriptCompiler.Assemblies;
for (int i = 0; i < asms.Length; ++i)
{
Assembly asm = asms[i];
TypeCache tc = ScriptCompiler.GetTypeCache(asm);
Type[] types = tc.Types;
for (int j = 0; j < types.Length; ++j)
{
Type type = types[j];
if (type.IsSubclassOf(typeof(Faction)))
{
if (Construct(type) is Faction faction)
Faction.Factions.Add(faction);
}
else if (type.IsSubclassOf(typeof(Town)))
{
if (Construct(type) is Town town)
Town.Towns.Add(town);
}
}
}
}
}
}

View file

@ -0,0 +1,21 @@
using System;
namespace Server.Factions
{
public class SilverGivenEntry
{
public static readonly TimeSpan ExpirePeriod = TimeSpan.FromHours(3.0);
public SilverGivenEntry(Mobile givenTo)
{
GivenTo = givenTo;
TimeOfGift = DateTime.UtcNow;
}
public Mobile GivenTo{ get; }
public DateTime TimeOfGift{ get; }
public bool IsExpired => TimeOfGift + ExpirePeriod < DateTime.UtcNow;
}
}

View file

@ -0,0 +1,40 @@
using Server.Mobiles;
using Server.Regions;
namespace Server.Factions
{
public class StrongholdRegion : BaseRegion
{
public StrongholdRegion(Faction faction) : base(faction.Definition.FriendlyName, Faction.Facet, DefaultPriority,
faction.Definition.Stronghold.Area)
{
Faction = faction;
Register();
}
public Faction Faction{ get; set; }
public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation)
{
if (!base.OnMoveInto(m, d, newLocation, oldLocation))
return false;
if (m.AccessLevel >= AccessLevel.Counselor || Contains(oldLocation))
return true;
if (m is PlayerMobile pm && pm.DuelContext != null)
{
pm.SendMessage("You may not enter this area while participating in a duel or a tournament.");
return false;
}
return Faction.Find(m, true, true) != null;
}
public override bool AllowHousing(Mobile from, Point3D p)
{
return false;
}
}
}

View file

@ -0,0 +1,505 @@
using System;
using System.Collections.Generic;
using Server.Commands;
using Server.Targeting;
namespace Server.Factions
{
[CustomEnum(new[] { "Britain", "Magincia", "Minoc", "Moonglow", "Skara Brae", "Trinsic", "Vesper", "Yew" })]
public abstract class Town : IComparable<Town>
{
public const int SilverCaptureBonus = 10000;
public static readonly TimeSpan TaxChangePeriod = TimeSpan.FromHours(12.0);
public static readonly TimeSpan IncomePeriod = TimeSpan.FromDays(1.0);
private Timer m_IncomeTimer;
private TownState m_State;
public Town()
{
m_State = new TownState(this);
ConstructVendorLists();
ConstructGuardLists();
StartIncomeTimer();
}
public TownDefinition Definition{ get; set; }
public TownState State
{
get => m_State;
set
{
m_State = value;
ConstructGuardLists();
}
}
public int Silver
{
get => m_State.Silver;
set => m_State.Silver = value;
}
public Faction Owner
{
get => m_State.Owner;
set => Capture(value);
}
public Mobile Sheriff
{
get => m_State.Sheriff;
set => m_State.Sheriff = value;
}
public Mobile Finance
{
get => m_State.Finance;
set => m_State.Finance = value;
}
public int Tax
{
get => m_State.Tax;
set => m_State.Tax = value;
}
public DateTime LastTaxChange
{
get => m_State.LastTaxChange;
set => m_State.LastTaxChange = value;
}
public bool TaxChangeReady => m_State.LastTaxChange + TaxChangePeriod < DateTime.UtcNow;
public int FinanceUpkeep
{
get
{
List<VendorList> vendorLists = VendorLists;
int upkeep = 0;
for (int i = 0; i < vendorLists.Count; ++i)
upkeep += vendorLists[i].Vendors.Count * vendorLists[i].Definition.Upkeep;
return upkeep;
}
}
public int SheriffUpkeep
{
get
{
List<GuardList> guardLists = GuardLists;
int upkeep = 0;
for (int i = 0; i < guardLists.Count; ++i)
upkeep += guardLists[i].Guards.Count * guardLists[i].Definition.Upkeep;
return upkeep;
}
}
public int DailyIncome => 10000 * (100 + m_State.Tax) / 100;
public int NetCashFlow => DailyIncome - FinanceUpkeep - SheriffUpkeep;
public TownMonolith Monolith
{
get
{
List<BaseMonolith> monoliths = BaseMonolith.Monoliths;
foreach (BaseMonolith monolith in monoliths)
if (monolith is TownMonolith townMonolith && townMonolith.Town == this)
return townMonolith;
return null;
}
}
public DateTime LastIncome
{
get => m_State.LastIncome;
set => m_State.LastIncome = value;
}
public List<VendorList> VendorLists{ get; set; }
public List<GuardList> GuardLists{ get; set; }
public static List<Town> Towns => Reflector.Towns;
public int CompareTo(Town other)
{
return Definition.Sort - (other?.Definition.Sort ?? 0);
}
public static Town FromRegion(Region reg)
{
if (reg.Map != Faction.Facet)
return null;
List<Town> towns = Towns;
for (int i = 0; i < towns.Count; ++i)
{
Town town = towns[i];
if (reg.IsPartOf(town.Definition.Region))
return town;
}
return null;
}
public void BeginOrderFiring(Mobile from)
{
bool isFinance = IsFinance(from);
bool isSheriff = IsSheriff(from);
string type = null;
// NOTE: Messages not OSI-accurate, intentional
if (isFinance && isSheriff) // GM only
type = "vendor or guard";
else if (isFinance)
type = "vendor";
else if (isSheriff)
type = "guard";
from.SendMessage("Target the {0} you wish to dismiss.", type);
from.BeginTarget(12, false, TargetFlags.None, EndOrderFiring);
}
public void EndOrderFiring(Mobile from, object obj)
{
bool isFinance = IsFinance(from);
bool isSheriff = IsSheriff(from);
string type = null;
if (isFinance && isSheriff) // GM only
type = "vendor or guard";
else if (isFinance)
type = "vendor";
else if (isSheriff)
type = "guard";
if (obj is BaseFactionVendor vendor && vendor.Town == this && isFinance)
vendor.Delete();
else if (obj is BaseFactionGuard guard && guard.Town == this && isSheriff)
guard.Delete();
else
from.SendMessage("That is not a {0}!", type);
}
public void StartIncomeTimer()
{
m_IncomeTimer?.Stop();
m_IncomeTimer = Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), CheckIncome);
}
public void StopIncomeTimer()
{
m_IncomeTimer?.Stop();
m_IncomeTimer = null;
}
public void CheckIncome()
{
if (LastIncome + IncomePeriod > DateTime.UtcNow || Owner == null)
return;
ProcessIncome();
}
public void ProcessIncome()
{
LastIncome = DateTime.UtcNow;
int flow = NetCashFlow;
if (Silver + flow < 0)
{
List<Mobile> toDelete = BuildFinanceList();
while (Silver + flow < 0 && toDelete.Count > 0)
{
int index = Utility.Random(toDelete.Count);
Mobile mob = toDelete[index];
mob.Delete();
toDelete.RemoveAt(index);
flow = NetCashFlow;
}
}
Silver += flow;
}
public List<Mobile> BuildFinanceList()
{
List<Mobile> list = new List<Mobile>();
for (int i = 0; i < VendorLists.Count; ++i)
list.AddRange(VendorLists[i].Vendors);
for (int i = 0; i < GuardLists.Count; ++i)
list.AddRange(GuardLists[i].Guards);
return list;
}
public void ConstructGuardLists()
{
GuardDefinition[] defs = Owner == null ? new GuardDefinition[0] : Owner.Definition.Guards;
GuardLists = new List<GuardList>();
for (int i = 0; i < defs.Length; ++i)
GuardLists.Add(new GuardList(defs[i]));
}
public GuardList FindGuardList(Type type)
{
List<GuardList> guardLists = GuardLists;
for (int i = 0; i < guardLists.Count; ++i)
{
GuardList guardList = guardLists[i];
if (guardList.Definition.Type == type)
return guardList;
}
return null;
}
public void ConstructVendorLists()
{
VendorDefinition[] defs = VendorDefinition.Definitions;
VendorLists = new List<VendorList>();
for (int i = 0; i < defs.Length; ++i)
VendorLists.Add(new VendorList(defs[i]));
}
public VendorList FindVendorList(Type type)
{
List<VendorList> vendorLists = VendorLists;
for (int i = 0; i < vendorLists.Count; ++i)
{
VendorList vendorList = vendorLists[i];
if (vendorList.Definition.Type == type)
return vendorList;
}
return null;
}
public bool RegisterGuard(BaseFactionGuard guard)
{
if (guard == null)
return false;
GuardList guardList = FindGuardList(guard.GetType());
if (guardList == null)
return false;
guardList.Guards.Add(guard);
return true;
}
public bool UnregisterGuard(BaseFactionGuard guard)
{
if (guard == null)
return false;
GuardList guardList = FindGuardList(guard.GetType());
if (guardList == null)
return false;
if (!guardList.Guards.Contains(guard))
return false;
guardList.Guards.Remove(guard);
return true;
}
public bool RegisterVendor(BaseFactionVendor vendor)
{
if (vendor == null)
return false;
VendorList vendorList = FindVendorList(vendor.GetType());
if (vendorList == null)
return false;
vendorList.Vendors.Add(vendor);
return true;
}
public bool UnregisterVendor(BaseFactionVendor vendor)
{
if (vendor == null)
return false;
VendorList vendorList = FindVendorList(vendor.GetType());
if (vendorList == null)
return false;
if (!vendorList.Vendors.Contains(vendor))
return false;
vendorList.Vendors.Remove(vendor);
return true;
}
public static void Initialize()
{
List<Town> towns = Towns;
for (int i = 0; i < towns.Count; ++i)
{
towns[i].Sheriff = towns[i].Sheriff;
towns[i].Finance = towns[i].Finance;
}
CommandSystem.Register("GrantTownSilver", AccessLevel.Administrator, GrantTownSilver_OnCommand);
}
public bool IsSheriff(Mobile mob)
{
return mob?.Deleted == false &&
(mob.AccessLevel >= AccessLevel.GameMaster || mob == Sheriff);
}
public bool IsFinance(Mobile mob)
{
return mob?.Deleted == false &&
(mob.AccessLevel >= AccessLevel.GameMaster || mob == Finance);
}
public void Capture(Faction f)
{
if (m_State.Owner == f)
return;
if (m_State.Owner == null) // going from unowned to owned
{
LastIncome = DateTime.UtcNow;
f.Silver += SilverCaptureBonus;
}
else if (f == null) // going from owned to unowned
{
LastIncome = DateTime.MinValue;
}
else // otherwise changing hands, income timer doesn't change
{
f.Silver += SilverCaptureBonus;
}
m_State.Owner = f;
Sheriff = null;
Finance = null;
TownMonolith monolith = Monolith;
if (monolith != null)
monolith.Faction = f;
List<VendorList> vendorLists = VendorLists;
for (int i = 0; i < vendorLists.Count; ++i)
{
VendorList vendorList = vendorLists[i];
List<BaseFactionVendor> vendors = vendorList.Vendors;
for (int j = vendors.Count - 1; j >= 0; --j)
vendors[j].Delete();
}
List<GuardList> guardLists = GuardLists;
for (int i = 0; i < guardLists.Count; ++i)
{
GuardList guardList = guardLists[i];
List<BaseFactionGuard> guards = guardList.Guards;
for (int j = guards.Count - 1; j >= 0; --j)
guards[j].Delete();
}
ConstructGuardLists();
}
public override string ToString()
{
return Definition.FriendlyName;
}
public static void WriteReference(GenericWriter writer, Town town)
{
int idx = Towns.IndexOf(town);
writer.WriteEncodedInt(idx + 1);
}
public static Town ReadReference(GenericReader reader)
{
int idx = reader.ReadEncodedInt() - 1;
if (idx >= 0 && idx < Towns.Count)
return Towns[idx];
return null;
}
public static Town Parse(string name)
{
List<Town> towns = Towns;
for (int i = 0; i < towns.Count; ++i)
{
Town town = towns[i];
if (Insensitive.Equals(town.Definition.FriendlyName, name))
return town;
}
return null;
}
public static void GrantTownSilver_OnCommand(CommandEventArgs e)
{
Town town = FromRegion(e.Mobile.Region);
if (town == null)
{
e.Mobile.SendMessage("You are not in a faction town.");
}
else if (e.Length == 0)
{
e.Mobile.SendMessage("Format: GrantTownSilver <amount>");
}
else
{
town.Silver += e.GetInt32(0);
e.Mobile.SendMessage("You have granted {0:N0} silver to the town. It now has {1:N0} silver.", e.GetInt32(0),
town.Silver);
}
}
}
}

View file

@ -0,0 +1,135 @@
using System;
namespace Server.Factions
{
public class TownState
{
private Mobile m_Finance;
private Mobile m_Sheriff;
public TownState(Town town)
{
Town = town;
}
public TownState(GenericReader reader)
{
int version = reader.ReadEncodedInt();
switch (version)
{
case 3:
{
LastIncome = reader.ReadDateTime();
goto case 2;
}
case 2:
{
Tax = reader.ReadEncodedInt();
LastTaxChange = reader.ReadDateTime();
goto case 1;
}
case 1:
{
Silver = reader.ReadEncodedInt();
goto case 0;
}
case 0:
{
Town = Town.ReadReference(reader);
Owner = Faction.ReadReference(reader);
m_Sheriff = reader.ReadMobile();
m_Finance = reader.ReadMobile();
Town.State = this;
break;
}
}
}
public Town Town{ get; set; }
public Faction Owner{ get; set; }
public Mobile Sheriff
{
get => m_Sheriff;
set
{
if (m_Sheriff != null)
{
PlayerState pl = PlayerState.Find(m_Sheriff);
if (pl != null)
pl.Sheriff = null;
}
m_Sheriff = value;
if (m_Sheriff != null)
{
PlayerState pl = PlayerState.Find(m_Sheriff);
if (pl != null)
pl.Sheriff = Town;
}
}
}
public Mobile Finance
{
get => m_Finance;
set
{
if (m_Finance != null)
{
PlayerState pl = PlayerState.Find(m_Finance);
if (pl != null)
pl.Finance = null;
}
m_Finance = value;
if (m_Finance != null)
{
PlayerState pl = PlayerState.Find(m_Finance);
if (pl != null)
pl.Finance = Town;
}
}
}
public int Silver{ get; set; }
public int Tax{ get; set; }
public DateTime LastTaxChange{ get; set; }
public DateTime LastIncome{ get; set; }
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(3); // version
writer.Write(LastIncome);
writer.WriteEncodedInt(Tax);
writer.Write(LastTaxChange);
writer.WriteEncodedInt(Silver);
Town.WriteReference(writer, Town);
Faction.WriteReference(writer, Owner);
writer.Write(m_Sheriff);
writer.Write(m_Finance);
}
}
}

View file

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
namespace Server.Factions
{
public class VendorList
{
public VendorList(VendorDefinition definition)
{
Definition = definition;
Vendors = new List<BaseFactionVendor>();
}
public VendorDefinition Definition{ get; }
public List<BaseFactionVendor> Vendors{ get; }
public BaseFactionVendor Construct(Town town, Faction faction)
{
try
{
return Activator.CreateInstance(Definition.Type, town, faction) as BaseFactionVendor;
}
catch
{
return null;
}
}
}
}

View file

@ -0,0 +1,89 @@
namespace Server.Factions
{
public class FactionDefinition
{
public FactionDefinition(int sort, int huePrimary, int hueSecondary, int hueJoin, int hueBroadcast, int warHorseBody,
int warHorseItem, string friendlyName, string keyword, string abbreviation, TextDefinition name,
TextDefinition propName, TextDefinition header, TextDefinition about, TextDefinition cityControl,
TextDefinition sigilControl, TextDefinition signupName, TextDefinition factionStoneName,
TextDefinition ownerLabel, TextDefinition guardIgnore, TextDefinition guardWarn, TextDefinition guardAttack,
StrongholdDefinition stronghold, RankDefinition[] ranks, GuardDefinition[] guards)
{
Sort = sort;
HuePrimary = huePrimary;
HueSecondary = hueSecondary;
HueJoin = hueJoin;
HueBroadcast = hueBroadcast;
WarHorseBody = warHorseBody;
WarHorseItem = warHorseItem;
FriendlyName = friendlyName;
Keyword = keyword;
Abbreviation = abbreviation;
Name = name;
PropName = propName;
Header = header;
About = about;
CityControl = cityControl;
SigilControl = sigilControl;
SignupName = signupName;
FactionStoneName = factionStoneName;
OwnerLabel = ownerLabel;
GuardIgnore = guardIgnore;
GuardWarn = guardWarn;
GuardAttack = guardAttack;
Stronghold = stronghold;
Ranks = ranks;
Guards = guards;
}
public int Sort{ get; }
public int HuePrimary{ get; }
public int HueSecondary{ get; }
public int HueJoin{ get; }
public int HueBroadcast{ get; }
public int WarHorseBody{ get; }
public int WarHorseItem{ get; }
public string FriendlyName{ get; }
public string Keyword{ get; }
public string Abbreviation{ get; }
public TextDefinition Name{ get; }
public TextDefinition PropName{ get; }
public TextDefinition Header{ get; }
public TextDefinition About{ get; }
public TextDefinition CityControl{ get; }
public TextDefinition SigilControl{ get; }
public TextDefinition SignupName{ get; }
public TextDefinition FactionStoneName{ get; }
public TextDefinition OwnerLabel{ get; }
public TextDefinition GuardIgnore{ get; }
public TextDefinition GuardWarn{ get; }
public TextDefinition GuardAttack{ get; }
public StrongholdDefinition Stronghold{ get; }
public RankDefinition[] Ranks{ get; }
public GuardDefinition[] Guards{ get; }
}
}

View file

@ -0,0 +1,48 @@
using System;
using Server.Items;
using Server.Mobiles;
namespace Server.Factions
{
public class FactionItemDefinition
{
private static FactionItemDefinition m_MetalArmor = new FactionItemDefinition(1000, typeof(Blacksmith));
private static FactionItemDefinition m_Weapon = new FactionItemDefinition(1000, typeof(Blacksmith));
private static FactionItemDefinition m_RangedWeapon = new FactionItemDefinition(1000, typeof(Bowyer));
private static FactionItemDefinition m_LeatherArmor = new FactionItemDefinition(750, typeof(Tailor));
private static FactionItemDefinition m_Clothing = new FactionItemDefinition(200, typeof(Tailor));
private static FactionItemDefinition m_Scroll = new FactionItemDefinition(500, typeof(Mage));
public FactionItemDefinition(int silverCost, Type vendorType)
{
SilverCost = silverCost;
VendorType = vendorType;
}
public int SilverCost{ get; }
public Type VendorType{ get; }
public static FactionItemDefinition Identify(Item item)
{
if (item is BaseArmor armor)
{
if (CraftResources.GetType(armor.Resource) == CraftResourceType.Leather)
return m_LeatherArmor;
return m_MetalArmor;
}
if (item is BaseRanged)
return m_RangedWeapon;
if (item is BaseWeapon)
return m_Weapon;
if (item is BaseClothing)
return m_Clothing;
if (item is SpellScroll)
return m_Scroll;
return null;
}
}
}

View file

@ -0,0 +1,35 @@
using System;
namespace Server.Factions
{
public class GuardDefinition
{
public GuardDefinition(Type type, int itemID, int price, int upkeep, int maximum, TextDefinition header,
TextDefinition label)
{
Type = type;
Price = price;
Upkeep = upkeep;
Maximum = maximum;
ItemID = itemID;
Header = header;
Label = label;
}
public Type Type{ get; }
public int Price{ get; }
public int Upkeep{ get; }
public int Maximum{ get; }
public int ItemID{ get; }
public TextDefinition Header{ get; }
public TextDefinition Label{ get; }
}
}

View file

@ -0,0 +1,21 @@
namespace Server.Factions
{
public class RankDefinition
{
public RankDefinition(int rank, int required, int maxWearables, TextDefinition title)
{
Rank = rank;
Required = required;
Title = title;
MaxWearables = maxWearables;
}
public int Rank{ get; }
public int Required{ get; }
public int MaxWearables{ get; }
public TextDefinition Title{ get; }
}
}

View file

@ -0,0 +1,21 @@
namespace Server.Factions
{
public class StrongholdDefinition
{
public StrongholdDefinition(Rectangle2D[] area, Point3D joinStone, Point3D factionStone, Point3D[] monoliths)
{
Area = area;
JoinStone = joinStone;
FactionStone = factionStone;
Monoliths = monoliths;
}
public Rectangle2D[] Area{ get; }
public Point3D JoinStone{ get; }
public Point3D FactionStone{ get; }
public Point3D[] Monoliths{ get; }
}
}

View file

@ -0,0 +1,51 @@
namespace Server.Factions
{
public class TownDefinition
{
public TownDefinition(int sort, int sigilID, string region, string friendlyName, TextDefinition townName,
TextDefinition townStoneHeader, TextDefinition strongholdMonolithName, TextDefinition townMonolithName,
TextDefinition townStoneName, TextDefinition sigilName, TextDefinition corruptedSigilName, Point3D monolith,
Point3D townStone)
{
Sort = sort;
SigilID = sigilID;
Region = region;
FriendlyName = friendlyName;
TownName = townName;
TownStoneHeader = townStoneHeader;
StrongholdMonolithName = strongholdMonolithName;
TownMonolithName = townMonolithName;
TownStoneName = townStoneName;
SigilName = sigilName;
CorruptedSigilName = corruptedSigilName;
Monolith = monolith;
TownStone = townStone;
}
public int Sort{ get; }
public int SigilID{ get; }
public string Region{ get; }
public string FriendlyName{ get; }
public TextDefinition TownName{ get; }
public TextDefinition TownStoneHeader{ get; }
public TextDefinition StrongholdMonolithName{ get; }
public TextDefinition TownMonolithName{ get; }
public TextDefinition TownStoneName{ get; }
public TextDefinition SigilName{ get; }
public TextDefinition CorruptedSigilName{ get; }
public Point3D Monolith{ get; }
public Point3D TownStone{ get; }
}
}

View file

@ -0,0 +1,74 @@
using System;
namespace Server.Factions
{
public class VendorDefinition
{
public VendorDefinition(Type type, int itemID, int price, int upkeep, int maximum, TextDefinition header,
TextDefinition label)
{
Type = type;
Price = price;
Upkeep = upkeep;
Maximum = maximum;
ItemID = itemID;
Header = header;
Label = label;
}
public Type Type{ get; }
public int Price{ get; }
public int Upkeep{ get; }
public int Maximum{ get; }
public int ItemID{ get; }
public TextDefinition Header{ get; }
public TextDefinition Label{ get; }
public static VendorDefinition[] Definitions{ get; } =
{
new VendorDefinition(typeof(FactionBottleVendor), 0xF0E,
5000,
1000,
10,
new TextDefinition(1011549, "POTION BOTTLE VENDOR"),
new TextDefinition(1011544, "Buy Potion Bottle Vendor")
),
new VendorDefinition(typeof(FactionBoardVendor), 0x1BD7,
3000,
500,
10,
new TextDefinition(1011552, "WOOD VENDOR"),
new TextDefinition(1011545, "Buy Wooden Board Vendor")
),
new VendorDefinition(typeof(FactionOreVendor), 0x19B8,
3000,
500,
10,
new TextDefinition(1011553, "IRON ORE VENDOR"),
new TextDefinition(1011546, "Buy Iron Ore Vendor")
),
new VendorDefinition(typeof(FactionReagentVendor), 0xF86,
5000,
1000,
10,
new TextDefinition(1011554, "REAGENT VENDOR"),
new TextDefinition(1011547, "Buy Reagent Vendor")
),
new VendorDefinition(typeof(FactionHorseVendor), 0x20DD,
5000,
1000,
1,
new TextDefinition(1011556, "HORSE BREEDER"),
new TextDefinition(1011555, "Buy Horse Breeder")
)
};
}
}

View file

@ -0,0 +1,127 @@
using System;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class ElectionGump : FactionGump
{
private Election m_Election;
private PlayerMobile m_From;
public ElectionGump(PlayerMobile from, Election election) : base(50, 50)
{
m_From = from;
m_Election = election;
AddPage(0);
AddBackground(0, 0, 420, 180, 5054);
AddBackground(10, 10, 400, 160, 3000);
AddHtmlText(20, 20, 380, 20, election.Faction.Definition.Header, false, false);
// NOTE: Gump not entirely OSI-accurate, intentionally so
switch (election.State)
{
case ElectionState.Pending:
{
TimeSpan toGo = election.LastStateTime + Election.PendingPeriod - DateTime.UtcNow;
int days = (int)(toGo.TotalDays + 0.5);
AddHtmlLocalized(20, 40, 380, 20, 1038034); // A new election campaign is pending
if (days > 0)
{
AddHtmlLocalized(20, 60, 280, 20, 1018062); // Days until next election :
AddLabel(300, 60, 0, days.ToString());
}
else
{
AddHtmlLocalized(20, 60, 280, 20, 1018059); // Election campaigning begins tonight.
}
break;
}
case ElectionState.Campaign:
{
TimeSpan toGo = election.LastStateTime + Election.CampaignPeriod - DateTime.UtcNow;
int days = (int)(toGo.TotalDays + 0.5);
AddHtmlLocalized(20, 40, 380, 20, 1018058); // There is an election campaign in progress.
if (days > 0)
{
AddHtmlLocalized(20, 60, 280, 20, 1038033); // Days to go:
AddLabel(300, 60, 0, days.ToString());
}
else
{
AddHtmlLocalized(20, 60, 280, 20, 1018061); // Campaign in progress. Voting begins tonight.
}
if (m_Election.CanBeCandidate(m_From))
{
AddButton(20, 110, 4005, 4007, 2);
AddHtmlLocalized(55, 110, 350, 20, 1011427); // CAMPAIGN FOR LEADERSHIP
}
else
{
PlayerState pl = PlayerState.Find(m_From);
if (pl == null || pl.Rank.Rank < Election.CandidateRank)
AddHtmlLocalized(20, 100, 380, 20, 1010118); // You must have a higher rank to run for office
}
break;
}
case ElectionState.Election:
{
TimeSpan toGo = election.LastStateTime + Election.VotingPeriod - DateTime.UtcNow;
int days = (int)Math.Ceiling(toGo.TotalDays);
AddHtmlLocalized(20, 40, 380, 20, 1018060); // There is an election vote in progress.
AddHtmlLocalized(20, 60, 280, 20, 1038033);
AddLabel(300, 60, 0, days.ToString());
AddHtmlLocalized(55, 100, 380, 20, 1011428); // VOTE FOR LEADERSHIP
AddButton(20, 100, 4005, 4007, 1);
break;
}
}
AddButton(20, 140, 4005, 4007, 0);
AddHtmlLocalized(55, 140, 350, 20, 1011012); // CANCEL
}
public override void OnResponse(NetState sender, RelayInfo info)
{
switch (info.ButtonID)
{
case 0: // back
{
m_From.SendGump(new FactionStoneGump(m_From, m_Election.Faction));
break;
}
case 1: // vote
{
if (m_Election.State == ElectionState.Election)
m_From.SendGump(new VoteGump(m_From, m_Election));
break;
}
case 2: // campaign
{
if (m_Election.CanBeCandidate(m_From))
m_Election.AddCandidate(m_From);
break;
}
}
}
}
}

View file

@ -0,0 +1,214 @@
using System;
using System.Net;
using Server.Gumps;
using Server.Network;
namespace Server.Factions
{
public class ElectionManagementGump : Gump
{
public const int LabelColor = 0xFFFFFF;
private Candidate m_Candidate;
private Election m_Election;
private int m_Page;
public ElectionManagementGump(Election election, Candidate candidate = null, int page = 0) : base(40, 40)
{
m_Election = election;
m_Candidate = candidate;
m_Page = page;
AddPage(0);
if (candidate != null)
{
AddBackground(0, 0, 448, 354, 9270);
AddAlphaRegion(10, 10, 428, 334);
AddHtml(10, 10, 428, 20, Color(Center("Candidate Management"), LabelColor));
AddHtml(45, 35, 100, 20, Color("Player Name:", LabelColor));
AddHtml(145, 35, 100, 20, Color(candidate.Mobile == null ? "null" : candidate.Mobile.Name, LabelColor));
AddHtml(45, 55, 100, 20, Color("Vote Count:", LabelColor));
AddHtml(145, 55, 100, 20, Color(candidate.Votes.ToString(), LabelColor));
AddButton(12, 73, 4005, 4007, 1);
AddHtml(45, 75, 100, 20, Color("Drop Candidate", LabelColor));
AddImageTiled(13, 99, 422, 242, 9264);
AddImageTiled(14, 100, 420, 240, 9274);
AddAlphaRegion(14, 100, 420, 240);
AddHtml(14, 100, 420, 20, Color(Center("Voters"), LabelColor));
if (page > 0)
AddButton(397, 104, 0x15E3, 0x15E7, 2);
else
AddImage(397, 104, 0x25EA);
if ((page + 1) * 10 < candidate.Voters.Count)
AddButton(414, 104, 0x15E1, 0x15E5, 3);
else
AddImage(414, 104, 0x25E6);
AddHtml(14, 120, 30, 20, Color(Center("DEL"), LabelColor));
AddHtml(47, 120, 150, 20, Color("Name", LabelColor));
AddHtml(195, 120, 100, 20, Color(Center("Address"), LabelColor));
AddHtml(295, 120, 80, 20, Color(Center("Time"), LabelColor));
AddHtml(355, 120, 60, 20, Color(Center("Legit"), LabelColor));
int idx = 0;
for (int i = page * 10; i >= 0 && i < candidate.Voters.Count && i < (page + 1) * 10; ++i, ++idx)
{
Voter voter = candidate.Voters[i];
AddButton(13, 138 + idx * 20, 4002, 4004, 4 + i);
object[] fields = voter.AcquireFields();
int x = 45;
for (int j = 0; j < fields.Length; ++j)
{
object obj = fields[j];
if (obj is Mobile mobile)
{
AddHtml(x + 2, 140 + idx * 20, 150, 20, Color(mobile.Name, LabelColor));
x += 150;
}
else if (obj is IPAddress)
{
AddHtml(x, 140 + idx * 20, 100, 20, Color(Center(obj.ToString()), LabelColor));
x += 100;
}
else if (obj is DateTime time)
{
AddHtml(x, 140 + idx * 20, 80, 20,
Color(Center(FormatTimeSpan(time - election.LastStateTime)), LabelColor));
x += 80;
}
else if (obj is int i1)
{
AddHtml(x, 140 + idx * 20, 60, 20, Color(Center(i1 + "%"), LabelColor));
x += 60;
}
}
}
}
else
{
AddBackground(0, 0, 288, 334, 9270);
AddAlphaRegion(10, 10, 268, 314);
AddHtml(10, 10, 268, 20, Color(Center("Election Management"), LabelColor));
AddHtml(45, 35, 100, 20, Color("Current State:", LabelColor));
AddHtml(145, 35, 100, 20, Color(election.State.ToString(), LabelColor));
AddButton(12, 53, 4005, 4007, 1);
AddHtml(45, 55, 100, 20, Color("Transition Time:", LabelColor));
AddHtml(145, 55, 100, 20, Color(FormatTimeSpan(election.NextStateTime), LabelColor));
AddImageTiled(13, 79, 262, 242, 9264);
AddImageTiled(14, 80, 260, 240, 9274);
AddAlphaRegion(14, 80, 260, 240);
AddHtml(14, 80, 260, 20, Color(Center("Candidates"), LabelColor));
AddHtml(14, 100, 30, 20, Color(Center("-->"), LabelColor));
AddHtml(47, 100, 150, 20, Color("Name", LabelColor));
AddHtml(195, 100, 80, 20, Color(Center("Votes"), LabelColor));
for (int i = 0; i < election.Candidates.Count; ++i)
{
Candidate cd = election.Candidates[i];
Mobile mob = cd.Mobile;
if (mob == null)
continue;
AddButton(13, 118 + i * 20, 4005, 4007, 2 + i);
AddHtml(47, 120 + i * 20, 150, 20, Color(mob.Name, LabelColor));
AddHtml(195, 120 + i * 20, 80, 20, Color(Center(cd.Votes.ToString()), LabelColor));
}
}
}
public string Right(string text)
{
return $"<DIV ALIGN=RIGHT>{text}</DIV>";
}
public string Center(string text)
{
return $"<CENTER>{text}</CENTER>";
}
public string Color(string text, int color)
{
return $"<BASEFONT COLOR=#{color:X6}>{text}</BASEFONT>";
}
public static string FormatTimeSpan(TimeSpan ts)
{
return $"{ts.Days:D2}:{ts.Hours % 24:D2}:{ts.Minutes % 60:D2}:{ts.Seconds % 60:D2}";
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Mobile from = sender.Mobile;
int bid = info.ButtonID;
if (m_Candidate == null)
{
if (bid == 0)
{
}
else if (bid == 1)
{
}
else
{
bid -= 2;
if (bid >= 0 && bid < m_Election.Candidates.Count)
from.SendGump(new ElectionManagementGump(m_Election, m_Election.Candidates[bid]));
}
}
else
{
if (bid == 0)
{
from.SendGump(new ElectionManagementGump(m_Election));
}
else if (bid == 1)
{
m_Election.RemoveCandidate(m_Candidate.Mobile);
from.SendGump(new ElectionManagementGump(m_Election));
}
else if (bid == 2 && m_Page > 0)
{
from.SendGump(new ElectionManagementGump(m_Election, m_Candidate, m_Page - 1));
}
else if (bid == 3 && (m_Page + 1) * 10 < m_Candidate.Voters.Count)
{
from.SendGump(new ElectionManagementGump(m_Election, m_Candidate, m_Page + 1));
}
else
{
bid -= 4;
if (bid >= 0 && bid < m_Candidate.Voters.Count)
{
m_Candidate.Voters.RemoveAt(bid);
from.SendGump(new ElectionManagementGump(m_Election, m_Candidate, m_Page));
}
}
}
}
}
}

View file

@ -0,0 +1,46 @@
using Server.Gumps;
namespace Server.Factions
{
public abstract class FactionGump : Gump
{
public FactionGump(int x, int y) : base(x, y)
{
}
public virtual int ButtonTypes => 10;
public int ToButtonID(int type, int index)
{
return 1 + index * ButtonTypes + type;
}
public bool FromButtonID(int buttonID, out int type, out int index)
{
int offset = buttonID - 1;
if (offset >= 0)
{
type = offset % ButtonTypes;
index = offset / ButtonTypes;
return true;
}
type = index = 0;
return false;
}
public static bool Exists(Mobile mob)
{
return mob.HasGump<FactionGump>();
}
public void AddHtmlText(int x, int y, int width, int height, TextDefinition text, bool back, bool scroll)
{
if (text?.Number > 0)
AddHtmlLocalized(x, y, width, height, text.Number, back, scroll);
else if (text?.String != null)
AddHtml(x, y, width, height, text.String, back, scroll);
}
}
}

View file

@ -0,0 +1,100 @@
using Server.Engines.Craft;
using Server.Gumps;
using Server.Items;
using Server.Network;
namespace Server.Factions
{
public class FactionImbueGump : FactionGump
{
private CraftSystem m_CraftSystem;
private FactionItemDefinition m_Definition;
private Faction m_Faction;
private Item m_Item;
private Mobile m_Mobile;
private object m_Notice;
private BaseTool m_Tool;
public FactionImbueGump(int quality, Item item, Mobile from, CraftSystem craftSystem, BaseTool tool, object notice,
int availableSilver, Faction faction, FactionItemDefinition def) : base(100, 200)
{
m_Item = item;
m_Mobile = from;
m_Faction = faction;
m_CraftSystem = craftSystem;
m_Tool = tool;
m_Notice = notice;
m_Definition = def;
AddPage(0);
AddBackground(0, 0, 320, 270, 5054);
AddBackground(10, 10, 300, 250, 3000);
AddHtmlLocalized(20, 20, 210, 25, 1011569); // Imbue with Faction properties?
AddHtmlLocalized(20, 60, 170, 25, 1018302); // Item quality:
AddHtmlLocalized(175, 60, 100, 25, 1018305 - quality); // Exceptional, Average, Low
AddHtmlLocalized(20, 80, 170, 25, 1011572); // Item Cost :
AddLabel(175, 80, 0x34, def.SilverCost.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 100, 170, 25, 1011573); // Your Silver :
AddLabel(175, 100, 0x34, availableSilver.ToString("N0")); // NOTE: Added 'N0'
AddRadio(20, 140, 210, 211, true, 1);
AddLabel(55, 140, m_Faction.Definition.HuePrimary - 1, "*****");
AddHtmlLocalized(150, 140, 150, 25, 1011570); // Primary Color
AddRadio(20, 160, 210, 211, false, 2);
AddLabel(55, 160, m_Faction.Definition.HueSecondary - 1, "*****");
AddHtmlLocalized(150, 160, 150, 25, 1011571); // Secondary Color
AddHtmlLocalized(55, 200, 200, 25, 1011011); // CONTINUE
AddButton(20, 200, 4005, 4007, 1);
AddHtmlLocalized(55, 230, 200, 25, 1011012); // CANCEL
AddButton(20, 230, 4005, 4007, 0);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (info.ButtonID == 1)
{
Container pack = m_Mobile.Backpack;
if (pack != null && m_Item.IsChildOf(pack))
{
if (pack.ConsumeTotal(typeof(Silver), m_Definition.SilverCost))
{
int hue;
if (m_Item is SpellScroll)
hue = 0;
else if (info.IsSwitched(1))
hue = m_Faction.Definition.HuePrimary;
else
hue = m_Faction.Definition.HueSecondary;
FactionItem.Imbue(m_Item, m_Faction, true, hue);
}
else
{
m_Mobile.SendLocalizedMessage(1042204); // You do not have enough silver.
}
}
}
if (m_Tool?.Deleted == false && m_Tool.UsesRemaining > 0)
m_Mobile.SendGump(new CraftGump(m_Mobile, m_CraftSystem, m_Tool, m_Notice));
else if (m_Notice is string s)
m_Mobile.SendMessage(s);
else if (m_Notice is int i && i > 0)
m_Mobile.SendLocalizedMessage(i);
}
}
}

View file

@ -0,0 +1,357 @@
using System.Collections.Generic;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class FactionStoneGump : FactionGump
{
private Faction m_Faction;
private PlayerMobile m_From;
public FactionStoneGump(PlayerMobile from, Faction faction) : base(20, 30)
{
m_From = from;
m_Faction = faction;
AddPage(0);
AddBackground(0, 0, 550, 440, 5054);
AddBackground(10, 10, 530, 420, 3000);
#region General
AddPage(1);
AddHtmlText(20, 30, 510, 20, faction.Definition.Header, false, false);
AddHtmlLocalized(20, 60, 100, 20, 1011429); // Led By :
AddHtml(125, 60, 200, 20, faction.Commander != null ? faction.Commander.Name : "Nobody");
AddHtmlLocalized(20, 80, 100, 20, 1011457); // Tithe rate :
if (faction.Tithe >= 0 && faction.Tithe <= 100 && faction.Tithe % 10 == 0)
AddHtmlLocalized(125, 80, 350, 20, 1011480 + faction.Tithe / 10);
else
AddHtml(125, 80, 350, 20, faction.Tithe + "%");
AddHtmlLocalized(20, 100, 100, 20, 1011458); // Traps placed :
AddHtml(125, 100, 50, 20, faction.Traps.Count.ToString());
AddHtmlLocalized(55, 225, 200, 20, 1011428); // VOTE FOR LEADERSHIP
AddButton(20, 225, 4005, 4007, ToButtonID(0, 0));
AddHtmlLocalized(55, 150, 100, 20, 1011430); // CITY STATUS
AddButton(20, 150, 4005, 4007, 0, GumpButtonType.Page, 2);
AddHtmlLocalized(55, 175, 100, 20, 1011444); // STATISTICS
AddButton(20, 175, 4005, 4007, 0, GumpButtonType.Page, 4);
bool isMerchantQualified = MerchantTitles.HasMerchantQualifications(from);
PlayerState pl = PlayerState.Find(from);
if (pl != null && pl.MerchantTitle != MerchantTitle.None)
{
AddHtmlLocalized(55, 200, 250, 20, 1011460); // UNDECLARE FACTION MERCHANT
AddButton(20, 200, 4005, 4007, ToButtonID(1, 0));
}
else if (isMerchantQualified)
{
AddHtmlLocalized(55, 200, 250, 20, 1011459); // DECLARE FACTION MERCHANT
AddButton(20, 200, 4005, 4007, 0, GumpButtonType.Page, 5);
}
else
{
AddHtmlLocalized(55, 200, 250, 20, 1011467); // MERCHANT OPTIONS
AddImage(20, 200, 4020);
}
AddHtmlLocalized(55, 250, 300, 20, 1011461); // COMMANDER OPTIONS
if (faction.IsCommander(from))
AddButton(20, 250, 4005, 4007, 0, GumpButtonType.Page, 6);
else
AddImage(20, 250, 4020);
AddHtmlLocalized(55, 275, 300, 20, 1011426); // LEAVE THIS FACTION
AddButton(20, 275, 4005, 4007, ToButtonID(0, 1));
AddHtmlLocalized(55, 300, 200, 20, 1011441); // EXIT
AddButton(20, 300, 4005, 4007, 0);
#endregion
#region City Status
AddPage(2);
AddHtmlLocalized(20, 30, 250, 20, 1011430); // CITY STATUS
List<Town> towns = Town.Towns;
for (int i = 0; i < towns.Count; ++i)
{
Town town = towns[i];
AddHtmlText(40, 55 + i * 30, 150, 20, town.Definition.TownName, false, false);
if (town.Owner == null)
{
AddHtmlLocalized(200, 55 + i * 30, 150, 20, 1011462); // : Neutral
}
else
{
AddHtmlLocalized(200, 55 + i * 30, 150, 20, town.Owner.Definition.OwnerLabel);
BaseMonolith monolith = town.Monolith;
AddImage(20, 60 + i * 30, monolith?.Sigil != null && monolith.Sigil.IsPurifying ? 0x938 : 0x939);
}
}
AddImage(20, 300, 2361);
AddHtmlLocalized(45, 295, 300, 20, 1011491); // sigil may be recaptured
AddImage(20, 320, 2360);
AddHtmlLocalized(45, 315, 300, 20, 1011492); // sigil may not be recaptured
AddHtmlLocalized(55, 350, 100, 20, 1011447); // BACK
AddButton(20, 350, 4005, 4007, 0, GumpButtonType.Page, 1);
#endregion
#region Statistics
AddPage(4);
AddHtmlLocalized(20, 30, 150, 20, 1011444); // STATISTICS
AddHtmlLocalized(20, 100, 100, 20, 1011445); // Name :
AddHtml(120, 100, 150, 20, from.Name);
AddHtmlLocalized(20, 130, 100, 20, 1018064); // score :
AddHtml(120, 130, 100, 20, (pl?.KillPoints ?? 0).ToString());
AddHtmlLocalized(20, 160, 100, 20, 1011446); // Rank :
AddHtml(120, 160, 100, 20, (pl?.Rank.Rank ?? 0).ToString());
AddHtmlLocalized(55, 250, 100, 20, 1011447); // BACK
AddButton(20, 250, 4005, 4007, 0, GumpButtonType.Page, 1);
#endregion
#region Merchant Options
if ((pl == null || pl.MerchantTitle == MerchantTitle.None) && isMerchantQualified)
{
AddPage(5);
AddHtmlLocalized(20, 30, 250, 20, 1011467); // MERCHANT OPTIONS
AddHtmlLocalized(20, 80, 300, 20, 1011473); // Select the title you wish to display
MerchantTitleInfo[] infos = MerchantTitles.Info;
for (int i = 0; i < infos.Length; ++i)
{
MerchantTitleInfo info = infos[i];
if (MerchantTitles.IsQualified(from, info))
AddButton(20, 100 + i * 30, 4005, 4007, ToButtonID(1, i + 1));
else
AddImage(20, 100 + i * 30, 4020);
AddHtmlText(55, 100 + i * 30, 200, 20, info.Label, false, false);
}
AddHtmlLocalized(55, 340, 100, 20, 1011447); // BACK
AddButton(20, 340, 4005, 4007, 0, GumpButtonType.Page, 1);
}
#endregion
#region Commander Options
if (faction.IsCommander(from))
{
#region General
AddPage(6);
AddHtmlLocalized(20, 30, 200, 20, 1011461); // COMMANDER OPTIONS
AddHtmlLocalized(20, 70, 120, 20, 1011457); // Tithe rate :
if (faction.Tithe >= 0 && faction.Tithe <= 100 && faction.Tithe % 10 == 0)
AddHtmlLocalized(140, 70, 250, 20, 1011480 + faction.Tithe / 10);
else
AddHtml(140, 70, 250, 20, faction.Tithe + "%");
AddHtmlLocalized(20, 100, 120, 20, 1011474); // Silver available :
AddHtml(140, 100, 50, 20, faction.Silver.ToString("N0")); // NOTE: Added 'N0' formatting
AddHtmlLocalized(55, 130, 200, 20, 1011478); // CHANGE TITHE RATE
AddButton(20, 130, 4005, 4007, 0, GumpButtonType.Page, 8);
AddHtmlLocalized(55, 160, 200, 20, 1018301); // TRANSFER SILVER
if (faction.Silver >= 10000)
AddButton(20, 160, 4005, 4007, 0, GumpButtonType.Page, 7);
else
AddImage(20, 160, 4020);
AddHtmlLocalized(55, 310, 100, 20, 1011447); // BACK
AddButton(20, 310, 4005, 4007, 0, GumpButtonType.Page, 1);
#endregion
#region Town Finance
if (faction.Silver >= 10000)
{
AddPage(7);
AddHtmlLocalized(20, 30, 250, 20, 1011476); // TOWN FINANCE
AddHtmlLocalized(20, 50, 400, 20, 1011477); // Select a town to transfer 10000 silver to
for (int i = 0; i < towns.Count; ++i)
{
Town town = towns[i];
AddHtmlText(55, 75 + i * 30, 200, 20, town.Definition.TownName, false, false);
if (town.Owner == faction)
AddButton(20, 75 + i * 30, 4005, 4007, ToButtonID(2, i));
else
AddImage(20, 75 + i * 30, 4020);
}
AddHtmlLocalized(55, 310, 100, 20, 1011447); // BACK
AddButton(20, 310, 4005, 4007, 0, GumpButtonType.Page, 1);
}
#endregion
#region Change Tithe Rate
AddPage(8);
AddHtmlLocalized(20, 30, 400, 20, 1011479); // Select the % for the new tithe rate
int y = 55;
for (int i = 0; i <= 10; ++i)
{
if (i == 5)
y += 5;
AddHtmlLocalized(55, y, 300, 20, 1011480 + i);
AddButton(20, y, 4005, 4007, ToButtonID(3, i));
y += 20;
if (i == 5)
y += 5;
}
AddHtmlLocalized(55, 310, 300, 20, 1011447); // BACK
AddButton(20, 310, 4005, 4007, 0, GumpButtonType.Page, 1);
#endregion
}
#endregion
}
public override int ButtonTypes => 4;
public override void OnResponse(NetState sender, RelayInfo info)
{
if (!FromButtonID(info.ButtonID, out int type, out int index))
return;
switch (type)
{
case 0: // general
{
switch (index)
{
case 0: // vote
{
m_From.SendGump(new ElectionGump(m_From, m_Faction.Election));
break;
}
case 1: // leave
{
m_From.SendGump(new LeaveFactionGump(m_From, m_Faction));
break;
}
}
break;
}
case 1: // merchant title
{
if (index >= 0 && index <= MerchantTitles.Info.Length)
{
PlayerState pl = PlayerState.Find(m_From);
MerchantTitle newTitle = (MerchantTitle)index;
MerchantTitleInfo mti = MerchantTitles.GetInfo(newTitle);
if (mti == null)
{
m_From.SendLocalizedMessage(1010120); // Your merchant title has been removed
if (pl != null)
pl.MerchantTitle = newTitle;
}
else if (MerchantTitles.IsQualified(m_From, mti))
{
m_From.SendLocalizedMessage(mti.Assigned);
if (pl != null)
pl.MerchantTitle = newTitle;
}
}
break;
}
case 2: // transfer silver
{
if (!m_Faction.IsCommander(m_From))
return;
List<Town> towns = Town.Towns;
if (index >= 0 && index < towns.Count)
{
Town town = towns[index];
if (town.Owner == m_Faction)
if (m_Faction.Silver >= 10000)
{
m_Faction.Silver -= 10000;
town.Silver += 10000;
// 10k in silver has been received by:
m_From.SendLocalizedMessage(1042726, true, " " + town.Definition.FriendlyName);
}
}
break;
}
case 3: // change tithe
{
if (!m_Faction.IsCommander(m_From))
return;
if (index >= 0 && index <= 10)
m_Faction.Tithe = index * 10;
break;
}
}
}
}
}

View file

@ -0,0 +1,292 @@
using System;
using System.Collections.Generic;
using Server.Gumps;
using Server.Mobiles;
using Server.Multis;
using Server.Network;
namespace Server.Factions
{
public class FinanceGump : FactionGump
{
private static int[] m_PriceOffsets =
{
-30, -25, -20, -15, -10, -5,
+50, +100, +150, +200, +250, +300
};
private Faction m_Faction;
private PlayerMobile m_From;
private Town m_Town;
public FinanceGump(PlayerMobile from, Faction faction, Town town) : base(50, 50)
{
m_From = from;
m_Faction = faction;
m_Town = town;
AddPage(0);
AddBackground(0, 0, 320, 410, 5054);
AddBackground(10, 10, 300, 390, 3000);
#region General
AddPage(1);
AddHtmlLocalized(20, 30, 260, 25, 1011541); // FINANCE MINISTER
AddHtmlLocalized(55, 90, 200, 25, 1011539); // CHANGE PRICES
AddButton(20, 90, 4005, 4007, 0, GumpButtonType.Page, 2);
AddHtmlLocalized(55, 120, 200, 25, 1011540); // BUY SHOPKEEPERS
AddButton(20, 120, 4005, 4007, 0, GumpButtonType.Page, 3);
AddHtmlLocalized(55, 150, 200, 25, 1011495); // VIEW FINANCES
AddButton(20, 150, 4005, 4007, 0, GumpButtonType.Page, 4);
AddHtmlLocalized(55, 360, 200, 25, 1011441); // EXIT
AddButton(20, 360, 4005, 4007, 0);
#endregion
#region Change Prices
AddPage(2);
AddHtmlLocalized(20, 30, 200, 25, 1011539); // CHANGE PRICES
for (int i = 0; i < m_PriceOffsets.Length; ++i)
{
int ofs = m_PriceOffsets[i];
int x = 20 + i / 6 * 150;
int y = 90 + i % 6 * 30;
AddRadio(x, y, 208, 209, town.Tax == ofs, i + 1);
if (ofs < 0)
AddLabel(x + 35, y, 0x26, string.Concat("- ", -ofs, "%"));
else
AddLabel(x + 35, y, 0x12A, string.Concat("+ ", ofs, "%"));
}
AddRadio(20, 270, 208, 209, town.Tax == 0, 0);
AddHtmlLocalized(55, 270, 90, 25, 1011542); // normal
AddHtmlLocalized(55, 330, 200, 25, 1011509); // Set Prices
AddButton(20, 330, 4005, 4007, ToButtonID(0, 0));
AddHtmlLocalized(55, 360, 200, 25, 1011067); // Previous page
AddButton(20, 360, 4005, 4007, 0, GumpButtonType.Page, 1);
#endregion
#region Buy Shopkeepers
AddPage(3);
AddHtmlLocalized(20, 30, 200, 25, 1011540); // BUY SHOPKEEPERS
List<VendorList> vendorLists = town.VendorLists;
for (int i = 0; i < vendorLists.Count; ++i)
{
VendorList list = vendorLists[i];
AddButton(20, 90 + i * 40, 4005, 4007, 0, GumpButtonType.Page, 5 + i);
AddItem(55, 90 + i * 40, list.Definition.ItemID);
AddHtmlText(100, 90 + i * 40, 200, 25, list.Definition.Label, false, false);
}
AddHtmlLocalized(55, 360, 200, 25, 1011067); // Previous page
AddButton(20, 360, 4005, 4007, 0, GumpButtonType.Page, 1);
#endregion
#region View Finances
AddPage(4);
int financeUpkeep = town.FinanceUpkeep;
int sheriffUpkeep = town.SheriffUpkeep;
int dailyIncome = town.DailyIncome;
int netCashFlow = town.NetCashFlow;
AddHtmlLocalized(20, 30, 300, 25, 1011524); // FINANCE STATEMENT
AddHtmlLocalized(20, 80, 300, 25, 1011538); // Current total money for town :
AddLabel(20, 100, 0x44, town.Silver.ToString());
AddHtmlLocalized(20, 130, 300, 25, 1011520); // Finance Minister Upkeep :
AddLabel(20, 150, 0x44, financeUpkeep.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 180, 300, 25, 1011521); // Sheriff Upkeep :
AddLabel(20, 200, 0x44, sheriffUpkeep.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 230, 300, 25, 1011522); // Town Income :
AddLabel(20, 250, 0x44, dailyIncome.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 280, 300, 25, 1011523); // Net Cash flow per day :
AddLabel(20, 300, 0x44, netCashFlow.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(55, 360, 200, 25, 1011067); // Previous page
AddButton(20, 360, 4005, 4007, 0, GumpButtonType.Page, 1);
#endregion
#region Shopkeeper Pages
for (int i = 0; i < vendorLists.Count; ++i)
{
VendorList vendorList = vendorLists[i];
AddPage(5 + i);
AddHtmlText(60, 30, 300, 25, vendorList.Definition.Header, false, false);
AddItem(20, 30, vendorList.Definition.ItemID);
AddHtmlLocalized(20, 90, 200, 25, 1011514); // You have :
AddLabel(230, 90, 0x26, vendorList.Vendors.Count.ToString());
AddHtmlLocalized(20, 120, 200, 25, 1011515); // Maximum :
AddLabel(230, 120, 0x256, vendorList.Definition.Maximum.ToString());
AddHtmlLocalized(20, 150, 200, 25, 1011516); // Cost :
AddLabel(230, 150, 0x44, vendorList.Definition.Price.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 180, 200, 25, 1011517); // Daily Pay :
AddLabel(230, 180, 0x37, vendorList.Definition.Upkeep.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 210, 200, 25, 1011518); // Current Silver :
AddLabel(230, 210, 0x44, town.Silver.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 240, 200, 25, 1011519); // Current Payroll :
AddLabel(230, 240, 0x44, financeUpkeep.ToString("N0")); // NOTE: Added 'N0'
AddHtmlText(55, 300, 200, 25, vendorList.Definition.Label, false, false);
if (town.Silver >= vendorList.Definition.Price)
AddButton(20, 300, 4005, 4007, ToButtonID(1, i));
else
AddImage(20, 300, 4020);
AddHtmlLocalized(55, 360, 200, 25, 1011067); // Previous page
AddButton(20, 360, 4005, 4007, 0, GumpButtonType.Page, 3);
}
#endregion
}
public override int ButtonTypes => 2;
public override void OnResponse(NetState sender, RelayInfo info)
{
if (!m_Town.IsFinance(m_From) || m_Town.Owner != m_Faction)
{
m_From.SendLocalizedMessage(1010339); // You no longer control this city
return;
}
if (!FromButtonID(info.ButtonID, out int type, out int index))
return;
switch (type)
{
case 0: // general
{
switch (index)
{
case 0: // set price
{
int[] switches = info.Switches;
if (switches.Length == 0)
break;
int opt = switches[0];
int newTax = 0;
if (opt >= 1 && opt <= m_PriceOffsets.Length)
newTax = m_PriceOffsets[opt - 1];
if (m_Town.Tax == newTax)
break;
if (m_From.AccessLevel == AccessLevel.Player && !m_Town.TaxChangeReady)
{
TimeSpan remaining = DateTime.UtcNow - (m_Town.LastTaxChange + Town.TaxChangePeriod);
if (remaining.TotalMinutes < 4)
m_From.SendLocalizedMessage(
1042165); // You must wait a short while before changing prices again.
else if (remaining.TotalMinutes < 10)
m_From.SendLocalizedMessage(
1042166); // You must wait several minutes before changing prices again.
else if (remaining.TotalHours < 1)
m_From.SendLocalizedMessage(
1042167); // You must wait up to an hour before changing prices again.
else if (remaining.TotalHours < 4)
m_From.SendLocalizedMessage(
1042168); // You must wait a few hours before changing prices again.
else
m_From.SendLocalizedMessage(
1042169); // You must wait several hours before changing prices again.
}
else
{
m_Town.Tax = newTax;
if (m_From.AccessLevel == AccessLevel.Player)
m_Town.LastTaxChange = DateTime.UtcNow;
}
break;
}
}
break;
}
case 1: // make vendor
{
List<VendorList> vendorLists = m_Town.VendorLists;
if (index >= 0 && index < vendorLists.Count)
{
VendorList vendorList = vendorLists[index];
if (Town.FromRegion(m_From.Region) != m_Town)
{
m_From.SendLocalizedMessage(1010305); // You must be in your controlled city to buy Items
}
else if (vendorList.Vendors.Count >= vendorList.Definition.Maximum)
{
m_From.SendLocalizedMessage(
1010306); // You currently have too many of this enhancement type to place another
}
else if (BaseBoat.FindBoatAt(m_From.Location, m_From.Map) != null)
{
m_From.SendMessage("You cannot place a vendor here");
}
else if (m_Town.Silver >= vendorList.Definition.Price)
{
BaseFactionVendor vendor = vendorList.Construct(m_Town, m_Faction);
if (vendor != null)
{
m_Town.Silver -= vendorList.Definition.Price;
vendor.MoveToWorld(m_From.Location, m_From.Map);
vendor.Home = vendor.Location;
}
}
}
break;
}
}
}
}
}

View file

@ -0,0 +1,91 @@
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class HorseBreederGump : FactionGump
{
private Faction m_Faction;
private PlayerMobile m_From;
public HorseBreederGump(PlayerMobile from, Faction faction) : base(20, 30)
{
m_From = from;
m_Faction = faction;
AddPage(0);
AddBackground(0, 0, 320, 280, 5054);
AddBackground(10, 10, 300, 260, 3000);
AddHtmlText(20, 30, 300, 25, faction.Definition.Header, false, false);
AddHtmlLocalized(20, 60, 300, 25, 1018306); // Purchase a Faction War Horse
AddItem(70, 120, 0x3FFE);
AddItem(150, 120, 0xEF2);
AddLabel(190, 122, 0x3E3, FactionWarHorse.SilverPrice.ToString("N0")); // NOTE: Added 'N0'
AddItem(150, 150, 0xEEF);
AddLabel(190, 152, 0x3E3, FactionWarHorse.GoldPrice.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(55, 210, 200, 25, 1011011); // CONTINUE
AddButton(20, 210, 4005, 4007, 1);
AddHtmlLocalized(55, 240, 200, 25, 1011012); // CANCEL
AddButton(20, 240, 4005, 4007, 0);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (info.ButtonID != 1)
return;
if (Faction.Find(m_From) != m_Faction)
return;
Container pack = m_From.Backpack;
if (pack == null)
return;
FactionWarHorse horse = new FactionWarHorse(m_Faction);
if (m_From.Followers + horse.ControlSlots > m_From.FollowersMax)
{
// TODO: Message?
horse.Delete();
}
else
{
if (pack.GetAmount(typeof(Silver)) < FactionWarHorse.SilverPrice)
{
sender.Mobile.SendLocalizedMessage(1042204); // You do not have enough silver.
horse.Delete();
}
else if (pack.GetAmount(typeof(Gold)) < FactionWarHorse.GoldPrice)
{
sender.Mobile.SendLocalizedMessage(1042205); // You do not have enough gold.
horse.Delete();
}
else if (pack.ConsumeTotal(typeof(Silver), FactionWarHorse.SilverPrice) &&
pack.ConsumeTotal(typeof(Gold), FactionWarHorse.GoldPrice))
{
horse.Controlled = true;
horse.ControlMaster = m_From;
horse.ControlOrder = OrderType.Follow;
horse.ControlTarget = m_From;
horse.MoveToWorld(m_From.Location, m_From.Map);
}
else
{
horse.Delete();
}
}
}
}
}

View file

@ -0,0 +1,50 @@
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class JoinStoneGump : FactionGump
{
private Faction m_Faction;
private PlayerMobile m_From;
public JoinStoneGump(PlayerMobile from, Faction faction) : base(20, 30)
{
m_From = from;
m_Faction = faction;
AddPage(0);
AddBackground(0, 0, 550, 440, 5054);
AddBackground(10, 10, 530, 420, 3000);
AddHtmlText(20, 30, 510, 20, faction.Definition.Header, false, false);
AddHtmlText(20, 130, 510, 100, faction.Definition.About, true, true);
AddHtmlLocalized(20, 60, 100, 20, 1011429); // Led By :
AddHtml(125, 60, 200, 20, faction.Commander != null ? faction.Commander.Name : "Nobody");
AddHtmlLocalized(20, 80, 100, 20, 1011457); // Tithe rate :
if (faction.Tithe >= 0 && faction.Tithe <= 100 && faction.Tithe % 10 == 0)
AddHtmlLocalized(125, 80, 350, 20, 1011480 + faction.Tithe / 10);
else
AddHtml(125, 80, 350, 20, faction.Tithe + "%");
AddButton(20, 400, 4005, 4007, 1);
AddHtmlLocalized(55, 400, 200, 20, 1011425); // JOIN THIS FACTION
AddButton(300, 400, 4005, 4007, 0);
AddHtmlLocalized(335, 400, 200, 20, 1011012); // CANCEL
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (info.ButtonID == 1)
m_Faction.OnJoinAccepted(m_From);
}
}
}

View file

@ -0,0 +1,93 @@
using System;
using Server.Guilds;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class LeaveFactionGump : FactionGump
{
private Faction m_Faction;
private PlayerMobile m_From;
public LeaveFactionGump(PlayerMobile from, Faction faction) : base(20, 30)
{
m_From = from;
m_Faction = faction;
AddBackground(0, 0, 270, 120, 5054);
AddBackground(10, 10, 250, 100, 3000);
if (from.Guild is Guild guild && guild.Leader == from)
AddHtmlLocalized(20, 15, 230, 60, 1018057, true,
true); // Are you sure you want your entire guild to leave this faction?
else
AddHtmlLocalized(20, 15, 230, 60, 1018063, true, true); // Are you sure you want to leave this faction?
AddHtmlLocalized(55, 80, 75, 20, 1011011); // CONTINUE
AddButton(20, 80, 4005, 4007, 1);
AddHtmlLocalized(170, 80, 75, 20, 1011012); // CANCEL
AddButton(135, 80, 4005, 4007, 2);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
switch (info.ButtonID)
{
case 1: // continue
{
if (!(m_From.Guild is Guild guild))
{
PlayerState pl = PlayerState.Find(m_From);
if (pl != null)
{
pl.Leaving = DateTime.UtcNow;
if (Faction.LeavePeriod == TimeSpan.FromDays(3.0))
m_From.SendLocalizedMessage(1005065); // You will be removed from the faction in 3 days
else
m_From.SendMessage("You will be removed from the faction in {0} days.",
Faction.LeavePeriod.TotalDays);
}
}
else if (guild.Leader != m_From)
{
m_From.SendLocalizedMessage(
1005061); // You cannot quit the faction because you are not the guild master
}
else
{
m_From.SendLocalizedMessage(1042285); // Your guild is now quitting the faction.
for (int i = 0; i < guild.Members.Count; ++i)
{
Mobile mob = guild.Members[i];
PlayerState pl = PlayerState.Find(mob);
if (pl != null)
{
pl.Leaving = DateTime.UtcNow;
if (Faction.LeavePeriod == TimeSpan.FromDays(3.0))
mob.SendLocalizedMessage(1005060); // Your guild will quit the faction in 3 days
else
mob.SendMessage("Your guild will quit the faction in {0} days.",
Faction.LeavePeriod.TotalDays);
}
}
}
break;
}
case 2: // cancel
{
m_From.SendLocalizedMessage(500737); // Canceled resignation.
break;
}
}
}
}
}

View file

@ -0,0 +1,188 @@
using System.Collections.Generic;
using Server.Gumps;
using Server.Mobiles;
using Server.Multis;
using Server.Network;
namespace Server.Factions
{
public class SheriffGump : FactionGump
{
private Faction m_Faction;
private PlayerMobile m_From;
private Town m_Town;
public SheriffGump(PlayerMobile from, Faction faction, Town town) : base(50, 50)
{
m_From = from;
m_Faction = faction;
m_Town = town;
AddPage(0);
AddBackground(0, 0, 320, 410, 5054);
AddBackground(10, 10, 300, 390, 3000);
#region General
AddPage(1);
AddHtmlLocalized(20, 30, 260, 25, 1011431); // Sheriff
AddHtmlLocalized(55, 90, 200, 25, 1011494); // HIRE GUARDS
AddButton(20, 90, 4005, 4007, 0, GumpButtonType.Page, 3);
AddHtmlLocalized(55, 120, 200, 25, 1011495); // VIEW FINANCES
AddButton(20, 120, 4005, 4007, 0, GumpButtonType.Page, 2);
AddHtmlLocalized(55, 360, 200, 25, 1011441); // Exit
AddButton(20, 360, 4005, 4007, 0);
#endregion
#region Finances
AddPage(2);
int financeUpkeep = town.FinanceUpkeep;
int sheriffUpkeep = town.SheriffUpkeep;
int dailyIncome = town.DailyIncome;
int netCashFlow = town.NetCashFlow;
AddHtmlLocalized(20, 30, 300, 25, 1011524); // FINANCE STATEMENT
AddHtmlLocalized(20, 80, 300, 25, 1011538); // Current total money for town :
AddLabel(20, 100, 0x44, town.Silver.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 130, 300, 25, 1011520); // Finance Minister Upkeep :
AddLabel(20, 150, 0x44, financeUpkeep.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 180, 300, 25, 1011521); // Sheriff Upkeep :
AddLabel(20, 200, 0x44, sheriffUpkeep.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 230, 300, 25, 1011522); // Town Income :
AddLabel(20, 250, 0x44, dailyIncome.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 280, 300, 25, 1011523); // Net Cash flow per day :
AddLabel(20, 300, 0x44, netCashFlow.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(55, 360, 200, 25, 1011067); // Previous page
AddButton(20, 360, 4005, 4007, 0, GumpButtonType.Page, 1);
#endregion
#region Hire Guards
AddPage(3);
AddHtmlLocalized(20, 30, 300, 25, 1011494); // HIRE GUARDS
List<GuardList> guardLists = town.GuardLists;
for (int i = 0; i < guardLists.Count; ++i)
{
GuardList guardList = guardLists[i];
int y = 90 + i * 60;
AddButton(20, y, 4005, 4007, 0, GumpButtonType.Page, 4 + i);
CenterItem(guardList.Definition.ItemID, 50, y - 20, 70, 60);
AddHtmlText(120, y, 200, 25, guardList.Definition.Header, false, false);
}
AddHtmlLocalized(55, 360, 200, 25, 1011067); // Previous page
AddButton(20, 360, 4005, 4007, 0, GumpButtonType.Page, 1);
#endregion
#region Guard Pages
for (int i = 0; i < guardLists.Count; ++i)
{
GuardList guardList = guardLists[i];
AddPage(4 + i);
AddHtmlText(90, 30, 300, 25, guardList.Definition.Header, false, false);
CenterItem(guardList.Definition.ItemID, 10, 10, 80, 80);
AddHtmlLocalized(20, 90, 200, 25, 1011514); // You have :
AddLabel(230, 90, 0x26, guardList.Guards.Count.ToString());
AddHtmlLocalized(20, 120, 200, 25, 1011515); // Maximum :
AddLabel(230, 120, 0x12A, guardList.Definition.Maximum.ToString());
AddHtmlLocalized(20, 150, 200, 25, 1011516); // Cost :
AddLabel(230, 150, 0x44, guardList.Definition.Price.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 180, 200, 25, 1011517); // Daily Pay :
AddLabel(230, 180, 0x37, guardList.Definition.Upkeep.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 210, 200, 25, 1011518); // Current Silver :
AddLabel(230, 210, 0x44, town.Silver.ToString("N0")); // NOTE: Added 'N0'
AddHtmlLocalized(20, 240, 200, 25, 1011519); // Current Payroll :
AddLabel(230, 240, 0x44, sheriffUpkeep.ToString("N0")); // NOTE: Added 'N0'
AddHtmlText(55, 300, 200, 25, guardList.Definition.Label, false, false);
AddButton(20, 300, 4005, 4007, 1 + i);
AddHtmlLocalized(55, 360, 200, 25, 1011067); // Previous page
AddButton(20, 360, 4005, 4007, 0, GumpButtonType.Page, 3);
}
#endregion
}
private void CenterItem(int itemID, int x, int y, int w, int h)
{
Rectangle2D rc = ItemBounds.Table[itemID];
AddItem(x + (w - rc.Width) / 2 - rc.X, y + (h - rc.Height) / 2 - rc.Y, itemID);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (!m_Town.IsSheriff(m_From) || m_Town.Owner != m_Faction)
{
m_From.SendLocalizedMessage(1010339); // You no longer control this city
return;
}
int index = info.ButtonID - 1;
if (index >= 0 && index < m_Town.GuardLists.Count)
{
GuardList guardList = m_Town.GuardLists[index];
if (Town.FromRegion(m_From.Region) != m_Town)
{
m_From.SendLocalizedMessage(1010305); // You must be in your controlled city to buy Items
}
else if (guardList.Guards.Count >= guardList.Definition.Maximum)
{
m_From.SendLocalizedMessage(
1010306); // You currently have too many of this enhancement type to place another
}
else if (BaseBoat.FindBoatAt(m_From.Location, m_From.Map) != null)
{
m_From.SendMessage("You cannot place a guard here");
}
else if (m_Town.Silver >= guardList.Definition.Price)
{
BaseFactionGuard guard = guardList.Construct();
if (guard != null)
{
guard.Faction = m_Faction;
guard.Town = m_Town;
m_Town.Silver -= guardList.Definition.Price;
guard.MoveToWorld(m_From.Location, m_From.Map);
guard.Home = guard.Location;
}
}
}
}
}
}

View file

@ -0,0 +1,204 @@
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
using Server.Targeting;
namespace Server.Factions
{
public class TownStoneGump : FactionGump
{
private Faction m_Faction;
private PlayerMobile m_From;
private Town m_Town;
public TownStoneGump(PlayerMobile from, Faction faction, Town town) : base(50, 50)
{
m_From = from;
m_Faction = faction;
m_Town = town;
AddPage(0);
AddBackground(0, 0, 320, 250, 5054);
AddBackground(10, 10, 300, 230, 3000);
AddHtmlText(25, 30, 250, 25, town.Definition.TownStoneHeader, false, false);
AddHtmlLocalized(55, 60, 150, 25, 1011557); // Hire Sheriff
AddButton(20, 60, 4005, 4007, 1);
AddHtmlLocalized(55, 90, 150, 25, 1011559); // Hire Finance Minister
AddButton(20, 90, 4005, 4007, 2);
AddHtmlLocalized(55, 120, 150, 25, 1011558); // Fire Sheriff
AddButton(20, 120, 4005, 4007, 3);
AddHtmlLocalized(55, 150, 150, 25, 1011560); // Fire Finance Minister
AddButton(20, 150, 4005, 4007, 4);
AddHtmlLocalized(55, 210, 150, 25, 1011441); // EXIT
AddButton(20, 210, 4005, 4007, 0);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (m_Town.Owner != m_Faction || !m_Faction.IsCommander(m_From))
{
m_From.SendLocalizedMessage(1010339); // You no longer control this city
return;
}
switch (info.ButtonID)
{
case 1: // hire sheriff
{
if (m_Town.Sheriff != null)
{
m_From.SendLocalizedMessage(1010342); // You must fire your Sheriff before you can elect a new one
}
else
{
m_From.SendLocalizedMessage(1010347); // Who shall be your new sheriff
m_From.BeginTarget(12, false, TargetFlags.None, HireSheriff_OnTarget);
}
break;
}
case 2: // hire finance minister
{
if (m_Town.Finance != null)
{
m_From.SendLocalizedMessage(
1010345); // You must fire your finance minister before you can elect a new one
}
else
{
m_From.SendLocalizedMessage(1010348); // Who shall be your new Minister of Finances?
m_From.BeginTarget(12, false, TargetFlags.None, HireFinanceMinister_OnTarget);
}
break;
}
case 3: // fire sheriff
{
if (m_Town.Sheriff == null)
{
m_From.SendLocalizedMessage(1010350); // You need to elect a sheriff before you can fire one
}
else
{
m_From.SendLocalizedMessage(1010349); // You have fired your sheriff
m_Town.Sheriff.SendLocalizedMessage(1010270); // You have been fired as Sheriff
m_Town.Sheriff = null;
}
break;
}
case 4: // fire finance minister
{
if (m_Town.Finance == null)
{
m_From.SendLocalizedMessage(
1010352); // You need to elect a financial minister before you can fire one
}
else
{
m_From.SendLocalizedMessage(1010351); // You have fired your financial Minister
m_Town.Finance.SendLocalizedMessage(1010151); // You have been fired as Finance Minister
m_Town.Finance = null;
}
break;
}
}
}
private void HireSheriff_OnTarget(Mobile from, object obj)
{
if (m_Town.Owner != m_Faction || !m_Faction.IsCommander(from))
{
from.SendLocalizedMessage(1010339); // You no longer control this city
return;
}
if (m_Town.Sheriff != null)
{
from.SendLocalizedMessage(1010342); // You must fire your Sheriff before you can elect a new one
}
else if (obj is Mobile targ)
{
PlayerState pl = PlayerState.Find(targ);
if (pl == null)
{
from.SendLocalizedMessage(1010337); // You must pick someone in a faction
}
else if (pl.Faction != m_Faction)
{
from.SendLocalizedMessage(1010338); // You must pick someone in the correct faction
}
else if (m_Faction.Commander == targ)
{
from.SendLocalizedMessage(1010335); // You cannot elect a commander to a town position
}
else if (pl.Sheriff != null || pl.Finance != null)
{
from.SendLocalizedMessage(1005245); // You must pick someone who does not already hold a city post
}
else
{
m_Town.Sheriff = targ;
targ.SendLocalizedMessage(1010340); // You are now the Sheriff
from.SendLocalizedMessage(1010341); // You have elected a Sheriff
}
}
else
{
from.SendLocalizedMessage(1010334); // You must select a player to hold a city position!
}
}
private void HireFinanceMinister_OnTarget(Mobile from, object obj)
{
if (m_Town.Owner != m_Faction || !m_Faction.IsCommander(from))
{
from.SendLocalizedMessage(1010339); // You no longer control this city
}
else if (m_Town.Finance != null)
{
from.SendLocalizedMessage(1010342); // You must fire your Sheriff before you can elect a new one
}
else if (obj is Mobile targ)
{
PlayerState pl = PlayerState.Find(targ);
if (pl == null)
{
from.SendLocalizedMessage(1010337); // You must pick someone in a faction
}
else if (pl.Faction != m_Faction)
{
from.SendLocalizedMessage(1010338); // You must pick someone in the correct faction
}
else if (m_Faction.Commander == targ)
{
from.SendLocalizedMessage(1010335); // You cannot elect a commander to a town position
}
else if (pl.Sheriff != null || pl.Finance != null)
{
from.SendLocalizedMessage(1005245); // You must pick someone who does not already hold a city post
}
else
{
m_Town.Finance = targ;
targ.SendLocalizedMessage(1010343); // You are now the Financial Minister
from.SendLocalizedMessage(1010344); // You have elected a Financial Minister
}
}
else
{
from.SendLocalizedMessage(1010334); // You must select a player to hold a city position!
}
}
}
}

View file

@ -0,0 +1,66 @@
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class VoteGump : FactionGump
{
private Election m_Election;
private PlayerMobile m_From;
public VoteGump(PlayerMobile from, Election election) : base(50, 50)
{
m_From = from;
m_Election = election;
bool canVote = election.CanVote(from);
AddPage(0);
AddBackground(0, 0, 420, 350, 5054);
AddBackground(10, 10, 400, 330, 3000);
AddHtmlText(20, 20, 380, 20, election.Faction.Definition.Header, false, false);
if (canVote)
AddHtmlLocalized(20, 60, 380, 20, 1011428); // VOTE FOR LEADERSHIP
else
AddHtmlLocalized(20, 60, 380, 20, 1038032); // You have already voted in this election.
for (int i = 0; i < election.Candidates.Count; ++i)
{
Candidate cd = election.Candidates[i];
if (canVote)
AddButton(20, 100 + i * 20, 4005, 4007, i + 1);
AddLabel(55, 100 + i * 20, 0, cd.Mobile.Name);
AddLabel(300, 100 + i * 20, 0, cd.Votes.ToString());
}
AddButton(20, 310, 4005, 4007, 0);
AddHtmlLocalized(55, 310, 100, 20, 1011012); // CANCEL
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (info.ButtonID == 0)
{
m_From.SendGump(new FactionStoneGump(m_From, m_Election.Faction));
}
else
{
if (!m_Election.CanVote(m_From))
return;
int index = info.ButtonID - 1;
if (index >= 0 && index < m_Election.Candidates.Count)
m_Election.Candidates[index].Voters.Add(new Voter(m_From, m_Election.Candidates[index].Mobile));
m_From.SendGump(new VoteGump(m_From, m_Election));
}
}
}
}

View file

@ -0,0 +1,106 @@
namespace Server.Factions
{
public class CouncilOfMages : Faction
{
public CouncilOfMages()
{
Instance = this;
Definition =
new FactionDefinition(
1,
1325, // blue
1310, // bluish white
1325, // join stone : blue
1325, // broadcast : blue
0x77, 0x3EB1, // war horse
"Council of Mages", "council", "CoM",
new TextDefinition(1011535, "COUNCIL OF MAGES"),
new TextDefinition(1060770, "Council of Mages faction"),
new TextDefinition(1011422, "<center>COUNCIL OF MAGES</center>"),
new TextDefinition(1011449,
"The council of Mages have their roots in the city of Moonglow, where " +
"they once convened. They began as a small movement, dedicated to " +
"calling forth the Stranger, who saved the lands once before. A " +
"series of war and murders and misbegotten trials by those loyal to " +
"Lord British has caused the group to take up the banner of war."),
new TextDefinition(1011455, "This city is controlled by the Council of Mages."),
new TextDefinition(1042253, "This sigil has been corrupted by the Council of Mages"),
new TextDefinition(1041044, "The faction signup stone for the Council of Mages"),
new TextDefinition(1041382, "The Faction Stone of the Council of Mages"),
new TextDefinition(1011464, ": Council of Mages"),
new TextDefinition(1005187, "Members of the Council of Mages will now be ignored."),
new TextDefinition(1005188, "Members of the Council of Mages will now be warned to leave."),
new TextDefinition(1005189, "Members of the Council of Mages will now be beaten with a stick."),
// Moonglow
new StrongholdDefinition(
new[]
{
new Rectangle2D(4463, 1487, 15, 35),
new Rectangle2D(4450, 1522, 35, 48)
},
new Point3D(4469, 1486, 0),
new Point3D(4457, 1544, 0),
new[]
{
new Point3D(4464, 1534, 21),
new Point3D(4470, 1536, 21),
new Point3D(4468, 1534, 21),
new Point3D(4470, 1534, 21),
new Point3D(4468, 1536, 21),
new Point3D(4466, 1534, 21),
new Point3D(4466, 1536, 21),
new Point3D(4464, 1536, 21)
}),
// Magincia
/* new StrongholdDefinition(
new Rectangle2D[]
{
new Rectangle2D( 3756, 2232, 4, 23 ),
new Rectangle2D( 3760, 2227, 60, 28 ),
new Rectangle2D( 3782, 2219, 18, 8 ),
new Rectangle2D( 3778, 2255, 35, 17 )
},
new Point3D( 3750, 2241, 20 ),
new Point3D( 3795, 2259, 20 ),
new Point3D[]
{
new Point3D( 3793, 2255, 20 ),
new Point3D( 3793, 2252, 20 ),
new Point3D( 3793, 2249, 20 ),
new Point3D( 3793, 2246, 20 ),
new Point3D( 3797, 2255, 20 ),
new Point3D( 3797, 2252, 20 ),
new Point3D( 3797, 2249, 20 ),
new Point3D( 3797, 2246, 20 )
} ), */
new[]
{
new RankDefinition(10, 991, 8, new TextDefinition(1060789, "Inquisitor of the Council")),
new RankDefinition(9, 950, 7, new TextDefinition(1060788, "Archon of Principle")),
new RankDefinition(8, 900, 6, new TextDefinition(1060787, "Luminary")),
new RankDefinition(7, 800, 6, new TextDefinition(1060787, "Luminary")),
new RankDefinition(6, 700, 5, new TextDefinition(1060786, "Diviner")),
new RankDefinition(5, 600, 5, new TextDefinition(1060786, "Diviner")),
new RankDefinition(4, 500, 5, new TextDefinition(1060786, "Diviner")),
new RankDefinition(3, 400, 4, new TextDefinition(1060785, "Mystic")),
new RankDefinition(2, 200, 4, new TextDefinition(1060785, "Mystic")),
new RankDefinition(1, 0, 4, new TextDefinition(1060785, "Mystic"))
},
new[]
{
new GuardDefinition(typeof(FactionHenchman), 0x1403, 5000, 1000, 10,
new TextDefinition(1011526, "HENCHMAN"), new TextDefinition(1011510, "Hire Henchman")),
new GuardDefinition(typeof(FactionMercenary), 0x0F62, 6000, 2000, 10,
new TextDefinition(1011527, "MERCENARY"), new TextDefinition(1011511, "Hire Mercenary")),
new GuardDefinition(typeof(FactionSorceress), 0x0E89, 7000, 3000, 10,
new TextDefinition(1011507, "SORCERESS"), new TextDefinition(1011501, "Hire Sorceress")),
new GuardDefinition(typeof(FactionWizard), 0x13F8, 8000, 4000, 10,
new TextDefinition(1011508, "ELDER WIZARD"), new TextDefinition(1011502, "Hire Elder Wizard"))
}
);
}
public static Faction Instance{ get; private set; }
}
}

View file

@ -0,0 +1,83 @@
namespace Server.Factions
{
public class Minax : Faction
{
public Minax()
{
Instance = this;
Definition =
new FactionDefinition(
0,
1645, // dark red
1109, // shadow
1645, // join stone : dark red
1645, // broadcast : dark red
0x78, 0x3EAF, // war horse
"Minax", "minax", "Min",
new TextDefinition(1011534, "MINAX"),
new TextDefinition(1060769, "Minax faction"),
new TextDefinition(1011421, "<center>FOLLOWERS OF MINAX</center>"),
new TextDefinition(1011448,
"The followers of Minax have taken control in the old lands, " +
"and intend to hold it for as long as they can. Allying themselves " +
"with orcs, headless, gazers, trolls, and other beasts, they seek " +
"revenge against Lord British, for slights both real and imagined, " +
"though some of the followers wish only to wreak havoc on the " +
"unsuspecting populace."),
new TextDefinition(1011453, "This city is controlled by Minax."),
new TextDefinition(1042252, "This sigil has been corrupted by the Followers of Minax"),
new TextDefinition(1041043, "The faction signup stone for the Followers of Minax"),
new TextDefinition(1041381, "The Faction Stone of Minax"),
new TextDefinition(1011463, ": Minax"),
new TextDefinition(1005190, "Followers of Minax will now be ignored."),
new TextDefinition(1005191, "Followers of Minax will now be told to go away."),
new TextDefinition(1005192, "Followers of Minax will now be hanged by their toes."),
new StrongholdDefinition(
new[]
{
new Rectangle2D(1097, 2570, 70, 50)
},
new Point3D(1172, 2593, 0),
new Point3D(1117, 2587, 18),
new[]
{
new Point3D(1113, 2601, 18),
new Point3D(1113, 2598, 18),
new Point3D(1113, 2595, 18),
new Point3D(1113, 2592, 18),
new Point3D(1116, 2601, 18),
new Point3D(1116, 2598, 18),
new Point3D(1116, 2595, 18),
new Point3D(1116, 2592, 18)
}),
new[]
{
new RankDefinition(10, 991, 8, new TextDefinition(1060784, "Avenger of Mondain")),
new RankDefinition(9, 950, 7, new TextDefinition(1060783, "Dread Knight")),
new RankDefinition(8, 900, 6, new TextDefinition(1060782, "Warlord")),
new RankDefinition(7, 800, 6, new TextDefinition(1060782, "Warlord")),
new RankDefinition(6, 700, 5, new TextDefinition(1060781, "Executioner")),
new RankDefinition(5, 600, 5, new TextDefinition(1060781, "Executioner")),
new RankDefinition(4, 500, 5, new TextDefinition(1060781, "Executioner")),
new RankDefinition(3, 400, 4, new TextDefinition(1060780, "Defiler")),
new RankDefinition(2, 200, 4, new TextDefinition(1060780, "Defiler")),
new RankDefinition(1, 0, 4, new TextDefinition(1060780, "Defiler"))
},
new[]
{
new GuardDefinition(typeof(FactionHenchman), 0x1403, 5000, 1000, 10,
new TextDefinition(1011526, "HENCHMAN"), new TextDefinition(1011510, "Hire Henchman")),
new GuardDefinition(typeof(FactionMercenary), 0x0F62, 6000, 2000, 10,
new TextDefinition(1011527, "MERCENARY"), new TextDefinition(1011511, "Hire Mercenary")),
new GuardDefinition(typeof(FactionBerserker), 0x0F4B, 7000, 3000, 10,
new TextDefinition(1011505, "BERSERKER"), new TextDefinition(1011499, "Hire Berserker")),
new GuardDefinition(typeof(FactionDragoon), 0x1439, 8000, 4000, 10,
new TextDefinition(1011506, "DRAGOON"), new TextDefinition(1011500, "Hire Dragoon"))
}
);
}
public static Faction Instance{ get; private set; }
}
}

View file

@ -0,0 +1,83 @@
namespace Server.Factions
{
public class Shadowlords : Faction
{
public Shadowlords()
{
Instance = this;
Definition =
new FactionDefinition(
3,
1109, // shadow
2211, // green
1109, // join stone : shadow
2211, // broadcast : green
0x79, 0x3EB0, // war horse
"Shadowlords", "shadow", "SL",
new TextDefinition(1011537, "SHADOWLORDS"),
new TextDefinition(1060772, "Shadowlords faction"),
new TextDefinition(1011424, "<center>SHADES OF DARKNESS</center>"),
new TextDefinition(1011451,
"The Shadow Lords are a faction that has sprung up within the ranks of " +
"Minax. Comprised mostly of undead and those who would seek to be " +
"necromancers, they pose a threat to both the sides of good and evil. " +
"Their plans have disrupted the hold Minax has over Felucca, and their " +
"ultimate goal is to destroy all life."),
new TextDefinition(1011456, "This city is controlled by the Shadow Lords."),
new TextDefinition(1042255, "This sigil has been corrupted by the Shadowlords"),
new TextDefinition(1041046, "The faction signup stone for the Shadowlords"),
new TextDefinition(1041384, "The Faction Stone of the Shadowlords"),
new TextDefinition(1011466, ": Shadowlords"),
new TextDefinition(1005184, "Minions of the Shadowlords will now be ignored."),
new TextDefinition(1005185, "Minions of the Shadowlords will now be warned of their impending deaths."),
new TextDefinition(1005186, "Minions of the Shadowlords will now be attacked at will."),
new StrongholdDefinition(
new[]
{
new Rectangle2D(960, 688, 8, 9),
new Rectangle2D(944, 697, 24, 23)
},
new Point3D(969, 768, 0),
new Point3D(947, 713, 0),
new[]
{
new Point3D(953, 713, 20),
new Point3D(953, 709, 20),
new Point3D(953, 705, 20),
new Point3D(953, 701, 20),
new Point3D(957, 713, 20),
new Point3D(957, 709, 20),
new Point3D(957, 705, 20),
new Point3D(957, 701, 20)
}),
new[]
{
new RankDefinition(10, 991, 8, new TextDefinition(1060799, "Purveyor of Darkness")),
new RankDefinition(9, 950, 7, new TextDefinition(1060798, "Agent of Evil")),
new RankDefinition(8, 900, 6, new TextDefinition(1060797, "Bringer of Sorrow")),
new RankDefinition(7, 800, 6, new TextDefinition(1060797, "Bringer of Sorrow")),
new RankDefinition(6, 700, 5, new TextDefinition(1060796, "Keeper of Lies")),
new RankDefinition(5, 600, 5, new TextDefinition(1060796, "Keeper of Lies")),
new RankDefinition(4, 500, 5, new TextDefinition(1060796, "Keeper of Lies")),
new RankDefinition(3, 400, 4, new TextDefinition(1060795, "Servant")),
new RankDefinition(2, 200, 4, new TextDefinition(1060795, "Servant")),
new RankDefinition(1, 0, 4, new TextDefinition(1060795, "Servant"))
},
new[]
{
new GuardDefinition(typeof(FactionHenchman), 0x1403, 5000, 1000, 10,
new TextDefinition(1011526, "HENCHMAN"), new TextDefinition(1011510, "Hire Henchman")),
new GuardDefinition(typeof(FactionMercenary), 0x0F62, 6000, 2000, 10,
new TextDefinition(1011527, "MERCENARY"), new TextDefinition(1011511, "Hire Mercenary")),
new GuardDefinition(typeof(FactionDeathKnight), 0x0F45, 7000, 3000, 10,
new TextDefinition(1011512, "DEATH KNIGHT"), new TextDefinition(1011503, "Hire Death Knight")),
new GuardDefinition(typeof(FactionNecromancer), 0x13F8, 8000, 4000, 10,
new TextDefinition(1011513, "SHADOW MAGE"), new TextDefinition(1011504, "Hire Shadow Mage"))
}
);
}
public static Faction Instance{ get; private set; }
}
}

View file

@ -0,0 +1,88 @@
namespace Server.Factions
{
public class TrueBritannians : Faction
{
public TrueBritannians()
{
Instance = this;
Definition =
new FactionDefinition(
2,
1254, // dark purple
2125, // gold
2214, // join stone : gold
2125, // broadcast : gold
0x76, 0x3EB2, // war horse
"True Britannians", "true", "TB",
new TextDefinition(1011536, "LORD BRITISH"),
new TextDefinition(1060771, "True Britannians faction"),
new TextDefinition(1011423, "<center>TRUE BRITANNIANS</center>"),
new TextDefinition(1011450,
"True Britannians are loyal to the throne of Lord British. They refuse " +
"to give up their homelands to the vile Minax, and detest the Shadowlords " +
"for their evil ways. In addition, the Council of Mages threatens the " +
"existence of their ruler, and as such they have armed themselves, and " +
"prepare for war with all."),
new TextDefinition(1011454, "This city is controlled by Lord British."),
new TextDefinition(1042254, "This sigil has been corrupted by the True Britannians"),
new TextDefinition(1041045, "The faction signup stone for the True Britannians"),
new TextDefinition(1041383, "The Faction Stone of the True Britannians"),
new TextDefinition(1011465, ": True Britannians"),
new TextDefinition(1005181, "Followers of Lord British will now be ignored."),
new TextDefinition(1005182, "Followers of Lord British will now be warned of their impending doom."),
new TextDefinition(1005183, "Followers of Lord British will now be attacked on sight."),
new StrongholdDefinition(
new[]
{
new Rectangle2D(1292, 1556, 25, 25),
new Rectangle2D(1292, 1676, 120, 25),
new Rectangle2D(1388, 1556, 25, 25),
new Rectangle2D(1317, 1563, 71, 18),
new Rectangle2D(1300, 1581, 105, 95),
new Rectangle2D(1405, 1612, 12, 21),
new Rectangle2D(1405, 1633, 11, 5)
},
new Point3D(1419, 1622, 20),
new Point3D(1330, 1621, 50),
new[]
{
new Point3D(1328, 1627, 50),
new Point3D(1328, 1621, 50),
new Point3D(1334, 1627, 50),
new Point3D(1334, 1621, 50),
new Point3D(1340, 1627, 50),
new Point3D(1340, 1621, 50),
new Point3D(1345, 1621, 50),
new Point3D(1345, 1627, 50)
}),
new[]
{
new RankDefinition(10, 991, 8, new TextDefinition(1060794, "Knight of the Codex")),
new RankDefinition(9, 950, 7, new TextDefinition(1060793, "Knight of Virtue")),
new RankDefinition(8, 900, 6, new TextDefinition(1060792, "Crusader")),
new RankDefinition(7, 800, 6, new TextDefinition(1060792, "Crusader")),
new RankDefinition(6, 700, 5, new TextDefinition(1060791, "Sentinel")),
new RankDefinition(5, 600, 5, new TextDefinition(1060791, "Sentinel")),
new RankDefinition(4, 500, 5, new TextDefinition(1060791, "Sentinel")),
new RankDefinition(3, 400, 4, new TextDefinition(1060790, "Defender")),
new RankDefinition(2, 200, 4, new TextDefinition(1060790, "Defender")),
new RankDefinition(1, 0, 4, new TextDefinition(1060790, "Defender"))
},
new[]
{
new GuardDefinition(typeof(FactionHenchman), 0x1403, 5000, 1000, 10,
new TextDefinition(1011526, "HENCHMAN"), new TextDefinition(1011510, "Hire Henchman")),
new GuardDefinition(typeof(FactionMercenary), 0x0F62, 6000, 2000, 10,
new TextDefinition(1011527, "MERCENARY"), new TextDefinition(1011511, "Hire Mercenary")),
new GuardDefinition(typeof(FactionKnight), 0x0F4D, 7000, 3000, 10,
new TextDefinition(1011528, "KNIGHT"), new TextDefinition(1011497, "Hire Knight")),
new GuardDefinition(typeof(FactionPaladin), 0x143F, 8000, 4000, 10,
new TextDefinition(1011529, "PALADIN"), new TextDefinition(1011498, "Hire Paladin"))
}
);
}
public static Faction Instance{ get; private set; }
}
}

View file

@ -0,0 +1,24 @@
namespace Server.Factions
{
public class Britain : Town
{
public Britain()
{
Definition =
new TownDefinition(
0,
0x1869,
"Britain",
"Britain",
new TextDefinition(1011433, "BRITAIN"),
new TextDefinition(1011561, "TOWN STONE FOR BRITAIN"),
new TextDefinition(1041034, "The Faction Sigil Monolith of Britain"),
new TextDefinition(1041404, "The Faction Town Sigil Monolith of Britain"),
new TextDefinition(1041413, "Faction Town Stone of Britain"),
new TextDefinition(1041395, "Faction Town Sigil of Britain"),
new TextDefinition(1041386, "Corrupted Faction Town Sigil of Britain"),
new Point3D(1592, 1680, 10),
new Point3D(1588, 1676, 10));
}
}
}

View file

@ -0,0 +1,24 @@
namespace Server.Factions
{
public class Magincia : Town
{
public Magincia()
{
Definition =
new TownDefinition(
7,
0x1870,
"Magincia",
"Magincia",
new TextDefinition(1011440, "MAGINCIA"),
new TextDefinition(1011568, "TOWN STONE FOR MAGINCIA"),
new TextDefinition(1041041, "The Faction Sigil Monolith of Magincia"),
new TextDefinition(1041411, "The Faction Town Sigil Monolith of Magincia"),
new TextDefinition(1041420, "Faction Town Stone of Magincia"),
new TextDefinition(1041402, "Faction Town Sigil of Magincia"),
new TextDefinition(1041393, "Corrupted Faction Town Sigil of Magincia"),
new Point3D(3714, 2235, 20),
new Point3D(3712, 2230, 20));
}
}
}

View file

@ -0,0 +1,24 @@
namespace Server.Factions
{
public class Minoc : Town
{
public Minoc()
{
Definition =
new TownDefinition(
2,
0x186B,
"Minoc",
"Minoc",
new TextDefinition(1011437, "MINOC"),
new TextDefinition(1011564, "TOWN STONE FOR MINOC"),
new TextDefinition(1041036, "The Faction Sigil Monolith of Minoc"),
new TextDefinition(1041406, "The Faction Town Sigil Monolith Minoc"),
new TextDefinition(1041415, "Faction Town Stone of Minoc"),
new TextDefinition(1041397, "Faction Town Sigil of Minoc"),
new TextDefinition(1041388, "Corrupted Faction Town Sigil of Minoc"),
new Point3D(2471, 439, 15),
new Point3D(2469, 445, 15));
}
}
}

View file

@ -0,0 +1,24 @@
namespace Server.Factions
{
public class Moonglow : Town
{
public Moonglow()
{
Definition =
new TownDefinition(
3,
0x186C,
"Moonglow",
"Moonglow",
new TextDefinition(1011435, "MOONGLOW"),
new TextDefinition(1011563, "TOWN STONE FOR MOONGLOW"),
new TextDefinition(1041037, "The Faction Sigil Monolith of Moonglow"),
new TextDefinition(1041407, "The Faction Town Sigil Monolith of Moonglow"),
new TextDefinition(1041416, "Faction Town Stone of Moonglow"),
new TextDefinition(1041398, "Faction Town Sigil of Moonglow"),
new TextDefinition(1041389, "Corrupted Faction Town Sigil of Moonglow"),
new Point3D(4436, 1083, 0),
new Point3D(4432, 1086, 0));
}
}
}

View file

@ -0,0 +1,24 @@
namespace Server.Factions
{
public class SkaraBrae : Town
{
public SkaraBrae()
{
Definition =
new TownDefinition(
6,
0x186F,
"Skara Brae",
"Skara Brae",
new TextDefinition(1011439, "SKARA BRAE"),
new TextDefinition(1011567, "TOWN STONE FOR SKARA BRAE"),
new TextDefinition(1041040, "The Faction Sigil Monolith of Skara Brae"),
new TextDefinition(1041410, "The Faction Town Sigil Monolith of Skara Brae"),
new TextDefinition(1041419, "Faction Town Stone of Skara Brae"),
new TextDefinition(1041401, "Faction Town Sigil of Skara Brae"),
new TextDefinition(1041392, "Corrupted Faction Town Sigil of Skara Brae"),
new Point3D(576, 2200, 0),
new Point3D(572, 2196, 0));
}
}
}

View file

@ -0,0 +1,24 @@
namespace Server.Factions
{
public class Trinsic : Town
{
public Trinsic()
{
Definition =
new TownDefinition(
1,
0x186A,
"Trinsic",
"Trinsic",
new TextDefinition(1011434, "TRINSIC"),
new TextDefinition(1011562, "TOWN STONE FOR TRINSIC"),
new TextDefinition(1041035, "The Faction Sigil Monolith of Trinsic"),
new TextDefinition(1041405, "The Faction Town Sigil Monolith of Trinsic"),
new TextDefinition(1041414, "Faction Town Stone of Trinsic"),
new TextDefinition(1041396, "Faction Town Sigil of Trinsic"),
new TextDefinition(1041387, "Corrupted Faction Town Sigil of Trinsic"),
new Point3D(1914, 2717, 20),
new Point3D(1909, 2720, 20));
}
}
}

View file

@ -0,0 +1,24 @@
namespace Server.Factions
{
public class Vesper : Town
{
public Vesper()
{
Definition =
new TownDefinition(
5,
0x186E,
"Vesper",
"Vesper",
new TextDefinition(1016413, "VESPER"),
new TextDefinition(1011566, "TOWN STONE FOR VESPER"),
new TextDefinition(1041039, "The Faction Sigil Monolith of Vesper"),
new TextDefinition(1041409, "The Faction Town Sigil Monolith of Vesper"),
new TextDefinition(1041418, "Faction Town Stone of Vesper"),
new TextDefinition(1041400, "Faction Town Sigil of Vesper"),
new TextDefinition(1041391, "Corrupted Faction Town Sigil of Vesper"),
new Point3D(2982, 818, 0),
new Point3D(2985, 821, 0));
}
}
}

View file

@ -0,0 +1,24 @@
namespace Server.Factions
{
public class Yew : Town
{
public Yew()
{
Definition =
new TownDefinition(
4,
0x186D,
"Yew",
"Yew",
new TextDefinition(1011438, "YEW"),
new TextDefinition(1011565, "TOWN STONE FOR YEW"),
new TextDefinition(1041038, "The Faction Sigil Monolith of Yew"),
new TextDefinition(1041408, "The Faction Town Sigil Monolith of Yew"),
new TextDefinition(1041417, "Faction Town Stone of Yew"),
new TextDefinition(1041399, "Faction Town Sigil of Yew"),
new TextDefinition(1041390, "Corrupted Faction Town Sigil of Yew"),
new Point3D(548, 979, 0),
new Point3D(542, 980, 0));
}
}
}

View file

@ -0,0 +1,129 @@
using System.Collections.Generic;
namespace Server.Factions
{
public abstract class BaseMonolith : BaseSystemController
{
private Faction m_Faction;
private Sigil m_Sigil;
private Town m_Town;
public BaseMonolith(Town town = null, Faction faction = null) : base(0x1183)
{
Movable = false;
Town = town;
Faction = faction;
Monoliths.Add(this);
}
public BaseMonolith(Serial serial) : base(serial)
{
Monoliths.Add(this);
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Sigil Sigil
{
get => m_Sigil;
set
{
if (m_Sigil == value)
return;
m_Sigil = value;
if (m_Sigil?.LastMonolith != null && m_Sigil.LastMonolith != this && m_Sigil.LastMonolith.Sigil == m_Sigil)
m_Sigil.LastMonolith.Sigil = null;
if (m_Sigil != null)
m_Sigil.LastMonolith = this;
UpdateSigil();
}
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Town Town
{
get => m_Town;
set
{
m_Town = value;
OnTownChanged();
}
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Faction Faction
{
get => m_Faction;
set
{
m_Faction = value;
Hue = m_Faction?.Definition.HuePrimary ?? 0;
}
}
public static List<BaseMonolith> Monoliths{ get; set; } = new List<BaseMonolith>();
public override void OnLocationChange(Point3D oldLocation)
{
base.OnLocationChange(oldLocation);
UpdateSigil();
}
public override void OnMapChange()
{
base.OnMapChange();
UpdateSigil();
}
public virtual void UpdateSigil()
{
if (m_Sigil?.Deleted != false)
return;
m_Sigil.MoveToWorld(new Point3D(X, Y, Z + 18), Map);
}
public virtual void OnTownChanged()
{
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
Monoliths.Remove(this);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
Town.WriteReference(writer, m_Town);
Faction.WriteReference(writer, m_Faction);
writer.Write(m_Sigil);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
Town = Town.ReadReference(reader);
Faction = Faction.ReadReference(reader);
m_Sigil = reader.ReadItem() as Sigil;
break;
}
}
}
}
}

View file

@ -0,0 +1,64 @@
namespace Server.Factions
{
public abstract class BaseSystemController : Item
{
private int m_LabelNumber;
public BaseSystemController(int itemID) : base(itemID)
{
}
public BaseSystemController(Serial serial) : base(serial)
{
}
public virtual int DefaultLabelNumber => base.LabelNumber;
public new virtual string DefaultName => null;
public override int LabelNumber
{
get
{
if (m_LabelNumber > 0)
return m_LabelNumber;
return DefaultLabelNumber;
}
}
public virtual void AssignName(TextDefinition name)
{
if (name != null && name.Number > 0)
{
m_LabelNumber = name.Number;
Name = null;
}
else if (name?.String != null)
{
m_LabelNumber = 0;
Name = name.String;
}
else
{
m_LabelNumber = 0;
Name = DefaultName;
}
InvalidateProperties();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,99 @@
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class FactionStone : BaseSystemController
{
private Faction m_Faction;
[Constructible]
public FactionStone(Faction faction = null) : base(0xEDC)
{
Movable = false;
Faction = faction;
}
public FactionStone(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Faction Faction
{
get => m_Faction;
set
{
m_Faction = value;
AssignName(m_Faction?.Definition.FactionStoneName);
}
}
public override string DefaultName => "faction stone";
public override void OnDoubleClick(Mobile from)
{
if (m_Faction == null)
return;
if (!from.InRange(GetWorldLocation(), 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
}
else if (FactionGump.Exists(from))
{
from.SendLocalizedMessage(1042160); // You already have a faction menu open.
}
else if (from is PlayerMobile mobile)
{
Faction existingFaction = Faction.Find(mobile);
if (existingFaction == m_Faction || mobile.AccessLevel >= AccessLevel.GameMaster)
{
PlayerState pl = PlayerState.Find(mobile);
if (pl != null && pl.IsLeaving)
mobile.SendLocalizedMessage(
1005051); // You cannot use the faction stone until you have finished quitting your current faction
else
mobile.SendGump(new FactionStoneGump(mobile, m_Faction));
}
else if (existingFaction != null)
{
// TODO: Validate
mobile.SendLocalizedMessage(1005053); // This is not your faction stone!
}
else
{
mobile.SendGump(new JoinStoneGump(mobile, m_Faction));
}
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
Faction.WriteReference(writer, m_Faction);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
Faction = Faction.ReadReference(reader);
break;
}
}
}
}
}

View file

@ -0,0 +1,74 @@
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class JoinStone : BaseSystemController
{
private Faction m_Faction;
[Constructible]
public JoinStone(Faction faction = null) : base(0xEDC)
{
Movable = false;
Faction = faction;
}
public JoinStone(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Faction Faction
{
get => m_Faction;
set
{
m_Faction = value;
Hue = m_Faction?.Definition.HueJoin ?? 0;
AssignName(m_Faction?.Definition.SignupName);
}
}
public override string DefaultName => "faction signup stone";
public override void OnDoubleClick(Mobile from)
{
if (m_Faction == null)
return;
if (!from.InRange(GetWorldLocation(), 2))
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
else if (FactionGump.Exists(from))
from.SendLocalizedMessage(1042160); // You already have a faction menu open.
else if (Faction.Find(from) == null && from is PlayerMobile mobile)
mobile.SendGump(new JoinStoneGump(mobile, m_Faction));
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
Faction.WriteReference(writer, m_Faction);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
Faction = Faction.ReadReference(reader);
break;
}
}
}
}
}

View file

@ -0,0 +1,59 @@
using System;
namespace Server
{
public sealed class BloodRose : PowerFactionItem
{
public BloodRose()
: base(Utility.RandomList(6378, 9035))
{
Hue = 2118;
}
public BloodRose(Serial serial)
: base(serial)
{
}
public override string DefaultName => "blood rose";
public override bool Use(Mobile from)
{
if (from.GetStatMod("blood-rose") == null)
{
from.PlaySound(Utility.Random(0x3A, 3));
if (from.Body.IsHuman && !from.Mounted) from.Animate(34, 5, 1, true, false, 0);
int amount = Utility.Dice(3, 3, 3);
int time = Utility.RandomMinMax(5, 30);
from.FixedParticles(0x373A, 10, 15, 5018, EffectLayer.Waist);
from.PlaySound(0x1EE);
from.AddStatMod(new StatMod(StatType.All, "blood-rose", amount, TimeSpan.FromMinutes(time)));
return true;
}
from.SendLocalizedMessage(
1062927); // You have eaten one of these recently and eating another would provide no benefit.
return false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
}

View file

@ -0,0 +1,67 @@
using System;
namespace Server
{
public sealed class ClarityPotion : PowerFactionItem
{
public ClarityPotion()
: base(3628)
{
Hue = 1154;
}
public ClarityPotion(Serial serial)
: base(serial)
{
}
public override string DefaultName => "clarity potion";
public override bool Use(Mobile from)
{
if (from.BeginAction<ClarityPotion>())
{
int amount = Utility.Dice(3, 3, 3);
int time = Utility.RandomMinMax(5, 30);
from.PlaySound(0x2D6);
if (from.Body.IsHuman) from.Animate(34, 5, 1, true, false, 0);
from.FixedParticles(0x375A, 10, 15, 5011, EffectLayer.Head);
from.PlaySound(0x1EB);
StatMod mod = from.GetStatMod("Concussion");
if (mod != null)
{
from.RemoveStatMod("Concussion");
from.Mana -= mod.Offset;
}
from.PlaySound(0x1EE);
from.AddStatMod(new StatMod(StatType.Int, "clarity-potion", amount, TimeSpan.FromMinutes(time)));
Timer.DelayCall(TimeSpan.FromMinutes(time), delegate { from.EndAction<ClarityPotion>(); });
return true;
}
return false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
}

View file

@ -0,0 +1,51 @@
using Server.Factions;
using Server.Network;
namespace Server
{
public sealed class GemOfEmpowerment : PowerFactionItem
{
public GemOfEmpowerment()
: base(7955)
{
Hue = 1154;
}
public GemOfEmpowerment(Serial serial)
: base(serial)
{
}
public override string DefaultName => "gem of empowerment";
public override bool Use(Mobile from)
{
if (Faction.ClearSkillLoss(from))
{
from.LocalOverheadMessage(MessageType.Regular, 2219, false, "The gem shatters as you invoke its power.");
from.PlaySound(909);
from.FixedEffect(0x373A, 10, 30);
from.PlaySound(0x209);
return true;
}
return false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
}

View file

@ -0,0 +1,187 @@
using System;
using System.IO;
using Server.Factions;
using Server.Mobiles;
using Server.Network;
namespace Server
{
public abstract class PowerFactionItem : Item
{
private static WeightedItem[] _items =
{
new WeightedItem(30, typeof(GemOfEmpowerment)),
new WeightedItem(25, typeof(BloodRose)),
new WeightedItem(20, typeof(ClarityPotion)),
new WeightedItem(15, typeof(UrnOfAscension)),
new WeightedItem(10, typeof(StormsEye))
};
public PowerFactionItem(int itemId)
: base(itemId)
{
}
public PowerFactionItem(Serial serial)
: base(serial)
{
}
public abstract bool Use(Mobile mob);
public static void CheckSpawn(Mobile killer, Mobile victim)
{
if (killer != null && victim != null)
{
PlayerState ps = PlayerState.Find(victim);
if (ps != null)
{
int chance = ps.Rank.Rank;
if (chance > Utility.Random(100))
{
int weight = 0;
foreach (WeightedItem item in _items) weight += item.Weight;
weight = Utility.Random(weight);
foreach (WeightedItem item in _items)
{
if (weight < item.Weight)
{
Item obj = item.Construct();
if (obj != null)
{
killer.AddToBackpack(obj);
killer.SendSound(1470);
killer.LocalOverheadMessage(
MessageType.Regular, 2119, false,
"You notice a strange item on the corpse, and decide to pick it up."
);
try
{
using (StreamWriter op = new StreamWriter("faction-power-items.log", true))
{
op.WriteLine("{0}\t{1}\t{2}\t{3}", DateTime.UtcNow, killer, victim, obj);
}
}
catch
{
// ignored
}
}
break;
}
weight -= item.Weight;
}
}
}
}
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042038); // You must have the object in your backpack to use it.
}
else if (from is PlayerMobile mobile && mobile.DuelContext != null)
{
mobile.SendMessage("You can't use that.");
}
else if (Faction.Find(from) == null)
{
from.LocalOverheadMessage(MessageType.Regular, 2119, false,
"The object vanishes from your hands as you touch it.");
Timer.DelayCall(TimeSpan.FromSeconds(1.0),
delegate
{
from.LocalOverheadMessage(MessageType.Regular, 2118, false,
"You feel a strange tingling sensation throughout your body.");
});
Timer.DelayCall(TimeSpan.FromSeconds(4.0),
delegate { from.LocalOverheadMessage(MessageType.Regular, 2118, false, "Your skin begins to burn."); });
new DestructionTimer(from).Start();
Delete();
//from.SendMessage( "You must be in a faction to use this item." );
}
else if (Use(from))
{
from.RevealingAction();
Consume();
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
private sealed class DestructionTimer : Timer
{
private Mobile _mobile;
private bool _screamed;
public DestructionTimer(Mobile mob)
: base(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(0.1), 10)
{
_mobile = mob;
}
protected override void OnTick()
{
if (_mobile.Alive)
{
if (!_screamed)
{
_screamed = true;
_mobile.PlaySound(_mobile.Female ? 814 : 1088);
_mobile.PublicOverheadMessage(MessageType.Regular, 2118, false, "Aaaaah!");
}
_mobile.Damage(Utility.Dice(2, 6, 0));
}
}
}
private sealed class WeightedItem
{
public WeightedItem(int weight, Type type)
{
Weight = weight;
Type = type;
}
public int Weight{ get; }
public Type Type{ get; }
public Item Construct()
{
return Activator.CreateInstance(Type) as Item;
}
}
}
}

View file

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Server.Factions;
using Server.Spells;
using Server.Targeting;
namespace Server
{
public sealed class StormsEye : PowerFactionItem
{
public StormsEye()
: base(3967)
{
Hue = 1165;
}
public StormsEye(Serial serial)
: base(serial)
{
}
public override string DefaultName => "storms eye";
public override bool Use(Mobile user)
{
if (Movable)
user.BeginTarget(12, true, TargetFlags.None, delegate(Mobile from, object obj)
{
if (Movable && !Deleted)
if (obj is IPoint3D pt)
{
SpellHelper.GetSurfaceTop(ref pt);
Point3D origin = new Point3D(pt);
Map facet = from.Map;
if (facet?.CanFit(pt.X, pt.Y, pt.Z, 16, false, false) != true)
return;
Movable = false;
Effects.SendMovingEffect(
from, new Entity(Serial.Zero, origin, facet),
ItemID & 0x3FFF, 7, 0, false, false, Hue - 1
);
Timer.DelayCall(TimeSpan.FromSeconds(0.5), delegate
{
Delete();
Effects.PlaySound(origin, facet, 530);
Effects.PlaySound(origin, facet, 263);
Effects.SendLocationEffect(
origin, facet,
14284, 96, 1, 0, 2
);
Timer.DelayCall(TimeSpan.FromSeconds(1.0), delegate
{
List<Mobile> targets = facet.GetMobilesInRange(origin, 12).Where(mob =>
from.CanBeHarmful(mob, false) && mob.InLOS(new Point3D(origin, origin.Z + 1)) &&
Faction.Find(mob) != null).ToList();
foreach (Mobile mob in targets)
{
int damage = mob.Hits * 6 / 10;
if (!mob.Player && damage < 10)
damage = 10;
else if (damage > 75)
damage = 75;
Effects.SendMovingEffect(
new Entity(Serial.Zero, new Point3D(origin, origin.Z + 4), facet), mob,
14068, 1, 32, false, false, 1111, 2
);
from.DoHarmful(mob);
SpellHelper.Damage(TimeSpan.FromSeconds(0.50), mob, from, damage / 3.0, 0, 0, 0, 0,
100);
SpellHelper.Damage(TimeSpan.FromSeconds(0.70), mob, from, damage / 3.0, 0, 0, 0, 0,
100);
SpellHelper.Damage(TimeSpan.FromSeconds(1.00), mob, from, damage / 3.0, 0, 0, 0, 0,
100);
Timer.DelayCall(TimeSpan.FromSeconds(0.50), delegate { mob.PlaySound(0x1FB); });
}
});
});
}
});
return false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
}

View file

@ -0,0 +1,69 @@
using Server.Factions;
using Server.Gumps;
using Server.Multis;
using Server.Network;
namespace Server
{
public sealed class UrnOfAscension : PowerFactionItem
{
public UrnOfAscension()
: base(9246)
{
}
public UrnOfAscension(Serial serial)
: base(serial)
{
}
public override string DefaultName => "urn of ascension";
public override bool Use(Mobile from)
{
Faction ourFaction = Faction.Find(from);
bool used = false;
foreach (Mobile mob in from.GetMobilesInRange(8))
if (mob.Player && !mob.Alive && from.InLOS(mob))
{
if (Faction.Find(mob) != ourFaction) continue;
BaseHouse house = BaseHouse.FindHouseAt(mob);
if (house == null || house.IsFriend(from) || house.IsFriend(mob))
{
Faction.ClearSkillLoss(mob);
mob.SendGump(new ResurrectGump(mob, from));
used = true;
}
}
if (used)
{
from.LocalOverheadMessage(MessageType.Regular, 2219, false, "The urn shatters as you invoke its power.");
from.PlaySound(64);
Effects.PlaySound(from.Location, from.Map, 1481);
}
return used;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadEncodedInt();
}
}
}

View file

@ -0,0 +1,456 @@
using System;
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Targeting;
namespace Server.Factions
{
public class Sigil : BaseSystemController
{
public const int OwnershipHue = 0xB;
// ?? time corrupting faction has to return the sigil before corruption time resets ?
public static readonly TimeSpan CorruptionGrace = TimeSpan.FromMinutes(Core.SE ? 30.0 : 15.0);
// Sigil must be held at a stronghold for this amount of time in order to become corrupted
public static readonly TimeSpan CorruptionPeriod = Core.SE ? TimeSpan.FromHours(10.0) : TimeSpan.FromHours(24.0);
// After a sigil has been corrupted it must be returned to the town within this period of time
public static readonly TimeSpan ReturnPeriod = TimeSpan.FromHours(1.0);
// Once it's been returned the corrupting faction owns the town for this period of time
public static readonly TimeSpan PurificationPeriod = TimeSpan.FromDays(3.0);
private Faction m_Corrupted;
private Faction m_Corrupting;
private Town m_Town;
public Sigil(Town town) : base(0x1869)
{
Movable = false;
Town = town;
Sigils.Add(this);
}
public Sigil(Serial serial) : base(serial)
{
Sigils.Add(this);
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public DateTime LastStolen{ get; set; }
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public DateTime GraceStart{ get; set; }
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public DateTime CorruptionStart{ get; set; }
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public DateTime PurificationStart{ get; set; }
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Town Town
{
get => m_Town;
set
{
m_Town = value;
Update();
}
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Faction Corrupted
{
get => m_Corrupted;
set
{
m_Corrupted = value;
Update();
}
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Faction Corrupting
{
get => m_Corrupting;
set
{
m_Corrupting = value;
Update();
}
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public BaseMonolith LastMonolith{ get; set; }
[CommandProperty(AccessLevel.Counselor)]
public bool IsBeingCorrupted => LastMonolith is StrongholdMonolith && LastMonolith.Faction == m_Corrupting &&
m_Corrupting != null;
[CommandProperty(AccessLevel.Counselor)]
public bool IsCorrupted => m_Corrupted != null;
[CommandProperty(AccessLevel.Counselor)]
public bool IsPurifying => PurificationStart != DateTime.MinValue;
[CommandProperty(AccessLevel.Counselor)]
public bool IsCorrupting => m_Corrupting != null && m_Corrupting != m_Corrupted;
[CommandProperty(AccessLevel.GameMaster)]
public TimeSpan TimeUntilCorruption
{
get
{
if (!IsBeingCorrupted)
return TimeSpan.Zero;
TimeSpan ts = CorruptionStart + CorruptionPeriod - DateTime.UtcNow;
if (ts < TimeSpan.Zero)
ts = TimeSpan.Zero;
return ts;
}
}
public static List<Sigil> Sigils{ get; } = new List<Sigil>();
public void Update()
{
ItemID = m_Town?.Definition.SigilID ?? 0x1869;
if (m_Town == null)
AssignName(null);
else if (IsCorrupted || IsPurifying)
AssignName(m_Town.Definition.CorruptedSigilName);
else
AssignName(m_Town.Definition.SigilName);
InvalidateProperties();
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (IsCorrupted)
TextDefinition.AddTo(list, m_Corrupted.Definition.SigilControl);
else
list.Add(1042256); // This sigil is not corrupted.
if (IsCorrupting)
list.Add(1042257); // This sigil is in the process of being corrupted.
else if (IsPurifying)
list.Add(1042258); // This sigil has recently been corrupted, and is undergoing purification.
else
list.Add(1042259); // This sigil is not in the process of being corrupted.
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
if (IsCorrupted)
{
if (m_Corrupted.Definition.SigilControl.Number > 0)
LabelTo(from, m_Corrupted.Definition.SigilControl.Number);
else if (m_Corrupted.Definition.SigilControl.String != null)
LabelTo(from, m_Corrupted.Definition.SigilControl.String);
}
else
{
LabelTo(from, 1042256); // This sigil is not corrupted.
}
if (IsCorrupting)
LabelTo(from, 1042257); // This sigil is in the process of being corrupted.
else if (IsPurifying)
LabelTo(from, 1042258); // This sigil has been recently corrupted, and is undergoing purification.
else
LabelTo(from, 1042259); // This sigil is not in the process of being corrupted.
}
public override bool CheckLift(Mobile from, Item item, ref LRReason reject)
{
from.SendLocalizedMessage(1005225); // You must use the stealing skill to pick up the sigil
return false;
}
private Mobile FindOwner(IEntity parent)
{
if (parent is Item item)
return item.RootParent as Mobile;
if (parent is Mobile mobile)
return mobile;
return null;
}
public override void OnAdded(IEntity parent)
{
base.OnAdded(parent);
Mobile mob = FindOwner(parent);
if (mob != null)
mob.SolidHueOverride = OwnershipHue;
}
public override void OnRemoved(IEntity parent)
{
base.OnRemoved(parent);
Mobile mob = FindOwner(parent);
if (mob != null)
mob.SolidHueOverride = -1;
}
public override void OnDoubleClick(Mobile from)
{
if (IsChildOf(from.Backpack))
{
from.BeginTarget(1, false, TargetFlags.None, Sigil_OnTarget);
from.SendLocalizedMessage(1042251); // Click on a sigil monolith or player
}
}
public static bool ExistsOn(Mobile mob)
{
return mob.Backpack?.FindItemByType<Sigil>() != null;
}
private void BeginCorrupting(Faction faction)
{
m_Corrupting = faction;
CorruptionStart = DateTime.UtcNow;
}
private void ClearCorrupting()
{
m_Corrupting = null;
CorruptionStart = DateTime.MinValue;
}
private void Sigil_OnTarget(Mobile from, object obj)
{
if (Deleted || !IsChildOf(from.Backpack))
return;
#region Give To Mobile
if (obj is Mobile)
{
if (obj is PlayerMobile targ)
{
Faction toFaction = Faction.Find(targ);
Faction fromFaction = Faction.Find(from);
if (toFaction == null)
{
from.SendLocalizedMessage(1005223); // You cannot give the sigil to someone not in a faction
}
else if (fromFaction != toFaction)
{
from.SendLocalizedMessage(1005222); // You cannot give the sigil to someone not in your faction
}
else if (ExistsOn(targ))
{
from.SendLocalizedMessage(1005220); // You cannot give this sigil to someone who already has a sigil
}
else if (!targ.Alive)
{
from.SendLocalizedMessage(1042248); // You cannot give a sigil to a dead person.
}
else if (from.NetState != null && targ.NetState != null)
{
Container pack = targ.Backpack;
pack?.DropItem(this);
}
}
else
{
from.SendLocalizedMessage(1005221); //You cannot give the sigil to them
}
}
#endregion
else if (obj is BaseMonolith)
{
#region Put in Stronghold
if (obj is StrongholdMonolith sm)
{
if (sm.Faction == null || sm.Faction != Faction.Find(from))
{
from.SendLocalizedMessage(1042246); // You can't place that on an enemy monolith
}
else if (sm.Town == null || sm.Town != m_Town)
{
from.SendLocalizedMessage(1042247); // That is not the correct faction monolith
}
else
{
sm.Sigil = this;
Faction newController = sm.Faction;
Faction oldController = m_Corrupting;
if (oldController == null)
{
if (m_Corrupted != newController)
BeginCorrupting(newController);
}
else if (GraceStart > DateTime.MinValue && GraceStart + CorruptionGrace < DateTime.UtcNow)
{
if (m_Corrupted != newController)
BeginCorrupting(newController); // grace time over, reset period
else
ClearCorrupting();
GraceStart = DateTime.MinValue;
}
else if (newController == oldController)
{
GraceStart = DateTime.MinValue; // returned within grace period
}
else if (GraceStart == DateTime.MinValue)
{
GraceStart = DateTime.UtcNow;
}
PurificationStart = DateTime.MinValue;
}
}
#endregion
#region Put in Town
else if (obj is TownMonolith tm)
{
if (tm.Town == null || tm.Town != m_Town)
{
from.SendLocalizedMessage(1042245); // This is not the correct town sigil monolith
}
else if (m_Corrupted == null || m_Corrupted != Faction.Find(from))
{
from.SendLocalizedMessage(
1042244); // Your faction did not corrupt this sigil. Take it to your stronghold.
}
else
{
tm.Sigil = this;
m_Corrupting = null;
PurificationStart = DateTime.UtcNow;
CorruptionStart = DateTime.MinValue;
m_Town.Capture(m_Corrupted);
m_Corrupted = null;
}
}
#endregion
}
else
{
from.SendLocalizedMessage(1005224); // You can't use the sigil on that
}
Update();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
Town.WriteReference(writer, m_Town);
Faction.WriteReference(writer, m_Corrupted);
Faction.WriteReference(writer, m_Corrupting);
writer.Write(LastMonolith);
writer.Write(LastStolen);
writer.Write(GraceStart);
writer.Write(CorruptionStart);
writer.Write(PurificationStart);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
m_Town = Town.ReadReference(reader);
m_Corrupted = Faction.ReadReference(reader);
m_Corrupting = Faction.ReadReference(reader);
LastMonolith = reader.ReadItem() as BaseMonolith;
LastStolen = reader.ReadDateTime();
GraceStart = reader.ReadDateTime();
CorruptionStart = reader.ReadDateTime();
PurificationStart = reader.ReadDateTime();
Update();
if (RootParent is Mobile mob)
mob.SolidHueOverride = OwnershipHue;
break;
}
}
}
public bool ReturnHome()
{
BaseMonolith monolith = LastMonolith;
if (monolith == null && m_Town != null)
monolith = m_Town.Monolith;
if (monolith?.Deleted == false)
monolith.Sigil = this;
return monolith?.Deleted == false;
}
public override void OnParentDeleted(IEntity parent)
{
base.OnParentDeleted(parent);
ReturnHome();
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
Sigils.Remove(this);
}
public override void Delete()
{
if (ReturnHome())
return;
base.Delete();
}
}
}

View file

@ -0,0 +1,51 @@
namespace Server.Factions
{
public class Silver : Item
{
[Constructible]
public Silver() : this(1)
{
}
[Constructible]
public Silver(int amountFrom, int amountTo) : this(Utility.RandomMinMax(amountFrom, amountTo))
{
}
[Constructible]
public Silver(int amount) : base(0xEF0)
{
Stackable = true;
Amount = amount;
}
public Silver(Serial serial) : base(serial)
{
}
public override double DefaultWeight => 0.02;
public override int GetDropSound()
{
if (Amount <= 1)
return 0x2E4;
if (Amount <= 5)
return 0x2E5;
return 0x2E6;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,35 @@
namespace Server.Factions
{
public class StrongholdMonolith : BaseMonolith
{
public StrongholdMonolith(Town town = null, Faction faction = null) : base(town, faction)
{
}
public StrongholdMonolith(Serial serial) : base(serial)
{
}
public override int DefaultLabelNumber => 1041042; // A Faction Sigil Monolith
public override void OnTownChanged()
{
AssignName(Town?.Definition.StrongholdMonolithName);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,34 @@
namespace Server.Factions
{
public class TownMonolith : BaseMonolith
{
public TownMonolith(Town town = null) : base(town)
{
}
public TownMonolith(Serial serial) : base(serial)
{
}
public override int DefaultLabelNumber => 1041403; // A Faction Town Sigil Monolith
public override void OnTownChanged()
{
AssignName(Town?.Definition.TownMonolithName);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,79 @@
using Server.Mobiles;
namespace Server.Factions
{
public class TownStone : BaseSystemController
{
private Town m_Town;
[Constructible]
public TownStone(Town town = null) : base(0xEDE)
{
Movable = false;
Town = town;
}
public TownStone(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Town Town
{
get => m_Town;
set
{
m_Town = value;
AssignName(m_Town?.Definition.TownStoneName);
}
}
public override string DefaultName => "faction town stone";
public override void OnDoubleClick(Mobile from)
{
if (m_Town == null)
return;
Faction faction = Faction.Find(from);
if (faction == null && from.AccessLevel < AccessLevel.GameMaster)
return; // TODO: Message?
if (m_Town.Owner == null || from.AccessLevel < AccessLevel.GameMaster && faction != m_Town.Owner)
from.SendLocalizedMessage(1010332); // Your faction does not control this town
else if (!m_Town.Owner.IsCommander(from))
from.SendLocalizedMessage(1005242); // Only faction Leaders can use townstones
else if (FactionGump.Exists(from))
from.SendLocalizedMessage(1042160); // You already have a faction menu open.
else if (from is PlayerMobile mobile)
mobile.SendGump(new TownStoneGump(mobile, m_Town.Owner, m_Town));
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
Town.WriteReference(writer, m_Town);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
Town = Town.ReadReference(reader);
break;
}
}
}
}
}

View file

@ -0,0 +1,264 @@
using System;
using Server.Items;
using Server.Network;
namespace Server.Factions
{
public enum AllowedPlacing
{
Everywhere,
AnyFactionTown,
ControlledFactionTown,
FactionStronghold
}
public abstract class BaseFactionTrap : BaseTrap
{
private Timer m_Concealing;
public BaseFactionTrap(Faction f, Mobile m, int itemID) : base(itemID)
{
Visible = false;
Faction = f;
TimeOfPlacement = DateTime.UtcNow;
Placer = m;
}
public BaseFactionTrap(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public Faction Faction{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Mobile Placer{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public DateTime TimeOfPlacement{ get; set; }
public virtual int EffectSound => 0;
public virtual int SilverFromDisarm => 100;
public virtual int MessageHue => 0;
public virtual int AttackMessage => 0;
public virtual int DisarmMessage => 0;
public virtual AllowedPlacing AllowedPlacing => AllowedPlacing.Everywhere;
public virtual TimeSpan ConcealPeriod => TimeSpan.FromMinutes(1.0);
public virtual TimeSpan DecayPeriod
{
get
{
if (Core.AOS)
return TimeSpan.FromDays(1.0);
return TimeSpan.MaxValue; // no decay
}
}
public override void OnTrigger(Mobile from)
{
if (!IsEnemy(from))
return;
Conceal();
DoVisibleEffect();
Effects.PlaySound(Location, Map, EffectSound);
DoAttackEffect(from);
int silverToAward = from.Alive ? 20 : 40;
if (silverToAward > 0 && Placer != null && Faction != null)
{
PlayerState victimState = PlayerState.Find(from);
if (victimState?.CanGiveSilverTo(Placer) == true && victimState.KillPoints > 0)
{
int silverGiven = Faction.AwardSilver(Placer, silverToAward);
if (silverGiven > 0)
{
// TODO: Get real message
if (from.Alive)
Placer.SendMessage("You have earned {0} silver pieces because {1} fell for your trap.",
silverGiven, from.Name);
else
Placer.SendLocalizedMessage(1042736,
$"{silverGiven} silver\t{from.Name}"); // You have earned ~1_SILVER_AMOUNT~ pieces for vanquishing ~2_PLAYER_NAME~!
}
victimState.OnGivenSilverTo(Placer);
}
}
from.LocalOverheadMessage(MessageType.Regular, MessageHue, AttackMessage);
}
public abstract void DoVisibleEffect();
public abstract void DoAttackEffect(Mobile m);
public virtual int IsValidLocation()
{
return IsValidLocation(GetWorldLocation(), Map);
}
public virtual int IsValidLocation(Point3D p, Map m)
{
if (m == null)
return 502956; // You cannot place a trap on that.
if (Core.ML)
foreach (Item item in m.GetItemsInRange(p, 0))
if (item is BaseFactionTrap trap && trap.Faction == Faction)
return 1075263; // There is already a trap belonging to your faction at this location.;
switch (AllowedPlacing)
{
case AllowedPlacing.FactionStronghold:
{
StrongholdRegion region = Region.Find(p, m).GetRegion<StrongholdRegion>();
if (region != null && region.Faction == Faction)
return 0;
return 1010355; // This trap can only be placed in your stronghold
}
case AllowedPlacing.AnyFactionTown:
{
Town town = Town.FromRegion(Region.Find(p, m));
if (town != null)
return 0;
return 1010356; // This trap can only be placed in a faction town
}
case AllowedPlacing.ControlledFactionTown:
{
Town town = Town.FromRegion(Region.Find(p, m));
if (town != null && town.Owner == Faction)
return 0;
return 1010357; // This trap can only be placed in a town your faction controls
}
}
return 0;
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
base.OnMovement(m, oldLocation);
if (!CheckDecay() && CheckRange(m.Location, oldLocation, 6))
if (Faction.Find(m) != null &&
(m.Skills.DetectHidden.Value - 80.0) / 20.0 > Utility.RandomDouble())
PrivateOverheadLocalizedMessage(m, 1010154, MessageHue, "", ""); // [Faction Trap]
}
public void PrivateOverheadLocalizedMessage(Mobile to, int number, int hue, string name, string args)
{
NetState ns = to?.NetState;
ns?.Send(new MessageLocalized(Serial, ItemID, MessageType.Regular, hue, 3, number, name, args));
}
public virtual bool CheckDecay()
{
TimeSpan decayPeriod = DecayPeriod;
if (decayPeriod == TimeSpan.MaxValue)
return false;
if (TimeOfPlacement + decayPeriod < DateTime.UtcNow)
{
Timer.DelayCall(TimeSpan.Zero, Delete);
return true;
}
return false;
}
public virtual void BeginConceal()
{
m_Concealing?.Stop();
m_Concealing = Timer.DelayCall(ConcealPeriod, Conceal);
}
public virtual void Conceal()
{
m_Concealing?.Stop();
m_Concealing = null;
if (!Deleted)
Visible = false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
Faction.WriteReference(writer, Faction);
writer.Write(Placer);
writer.Write(TimeOfPlacement);
if (Visible)
BeginConceal();
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
Faction = Faction.ReadReference(reader);
Placer = reader.ReadMobile();
TimeOfPlacement = reader.ReadDateTime();
if (Visible)
BeginConceal();
CheckDecay();
}
public override void OnDelete()
{
if (Faction?.Traps.Contains(this) == true)
Faction.Traps.Remove(this);
base.OnDelete();
}
public virtual bool IsEnemy(Mobile mob)
{
if (mob.Hidden && mob.AccessLevel > AccessLevel.Player)
return false;
if (!mob.Alive || mob.IsDeadBondedPet)
return false;
Faction faction = Faction.Find(mob, true);
if (faction == null && mob is BaseFactionGuard guard)
faction = guard.Faction;
if (faction == null)
return false;
return faction != Faction;
}
}
}

View file

@ -0,0 +1,119 @@
using System;
using Server.Engines.Craft;
using Server.Items;
namespace Server.Factions
{
public abstract class BaseFactionTrapDeed : Item, ICraftable
{
private Faction m_Faction;
public BaseFactionTrapDeed(int itemID= 0x14F0) : base(itemID)
{
Weight = 1.0;
LootType = LootType.Blessed;
}
public BaseFactionTrapDeed(Serial serial) : base(serial)
{
}
public abstract Type TrapType{ get; }
[CommandProperty(AccessLevel.GameMaster)]
public Faction Faction
{
get => m_Faction;
set
{
m_Faction = value;
if (m_Faction != null)
Hue = m_Faction.Definition.HuePrimary;
}
}
#region ICraftable Members
public int OnCraft(int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool,
CraftItem craftItem, int resHue)
{
ItemID = 0x14F0;
Faction = Faction.Find(from);
return 1;
}
#endregion
public virtual BaseFactionTrap Construct(Mobile from)
{
try
{
return Activator.CreateInstance(TrapType, m_Faction, from) as BaseFactionTrap;
}
catch
{
return null;
}
}
public override void OnDoubleClick(Mobile from)
{
Faction faction = Faction.Find(from);
if (faction == null)
{
from.SendLocalizedMessage(1010353, "", 0x23); // Only faction members may place faction traps
}
else if (faction != m_Faction)
{
from.SendLocalizedMessage(1010354, "", 0x23); // You may only place faction traps created by your faction
}
else if (faction.Traps.Count >= faction.MaximumTraps)
{
from.SendLocalizedMessage(1010358, "", 0x23); // Your faction already has the maximum number of traps placed
}
else
{
BaseFactionTrap trap = Construct(from);
if (trap == null)
return;
int message = trap.IsValidLocation(from.Location, from.Map);
if (message > 0)
{
from.SendLocalizedMessage(message, "", 0x23);
trap.Delete();
}
else
{
from.SendLocalizedMessage(1010360); // You arm the trap and carefully hide it from view
trap.MoveToWorld(from.Location, from.Map);
faction.Traps.Add(trap);
Delete();
}
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
Faction.WriteReference(writer, m_Faction);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_Faction = Faction.ReadReference(reader);
}
}
}

View file

@ -0,0 +1,75 @@
using System;
namespace Server.Factions
{
public class FactionExplosionTrap : BaseFactionTrap
{
public FactionExplosionTrap(Faction f = null, Mobile m = null) : base(f, m, 0x11C1)
{
}
public FactionExplosionTrap(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1044599; // faction explosion trap
public override int AttackMessage => 1010543; // You are enveloped in an explosion of fire!
public override int DisarmMessage => 1010539; // You carefully remove the pressure trigger and disable the trap.
public override int EffectSound => 0x307;
public override int MessageHue => 0x78;
public override AllowedPlacing AllowedPlacing => AllowedPlacing.AnyFactionTown;
public override void DoVisibleEffect()
{
Effects.SendLocationEffect(GetWorldLocation(), Map, 0x36BD, 15, 10);
}
public override void DoAttackEffect(Mobile m)
{
m.Damage(Utility.Dice(6, 10, 40), m);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class FactionExplosionTrapDeed : BaseFactionTrapDeed
{
public FactionExplosionTrapDeed() : base(0x36D2)
{
}
public FactionExplosionTrapDeed(Serial serial) : base(serial)
{
}
public override Type TrapType => typeof(FactionExplosionTrap);
public override int LabelNumber => 1044603; // faction explosion trap deed
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,75 @@
using System;
namespace Server.Factions
{
public class FactionGasTrap : BaseFactionTrap
{
public FactionGasTrap(Faction f = null, Mobile m = null) : base(f, m, 0x113C)
{
}
public FactionGasTrap(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1044598; // faction gas trap
public override int AttackMessage => 1010542; // A noxious green cloud of poison gas envelops you!
public override int DisarmMessage => 502376; // The poison leaks harmlessly away due to your deft touch.
public override int EffectSound => 0x230;
public override int MessageHue => 0x44;
public override AllowedPlacing AllowedPlacing => AllowedPlacing.FactionStronghold;
public override void DoVisibleEffect()
{
Effects.SendLocationEffect(Location, Map, 0x3709, 28, 10, 0x1D3, 5);
}
public override void DoAttackEffect(Mobile m)
{
m.ApplyPoison(m, Poison.Lethal);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class FactionGasTrapDeed : BaseFactionTrapDeed
{
public FactionGasTrapDeed() : base(0x11AB)
{
}
public FactionGasTrapDeed(Serial serial) : base(serial)
{
}
public override Type TrapType => typeof(FactionGasTrap);
public override int LabelNumber => 1044602; // faction gas trap deed
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,71 @@
using System;
namespace Server.Factions
{
public class FactionSawTrap : BaseFactionTrap
{
public FactionSawTrap(Faction f = null, Mobile m = null) : base(f, m, 0x11AC)
{
}
public override int LabelNumber => 1041047; // faction saw trap
public override int AttackMessage => 1010544; // The blade cuts deep into your skin!
public override int DisarmMessage => 1010540; // You carefully dismantle the saw mechanism and disable the trap.
public override int EffectSound => 0x218;
public override int MessageHue => 0x5A;
public override AllowedPlacing AllowedPlacing => AllowedPlacing.ControlledFactionTown;
public override void DoVisibleEffect()
{
Effects.SendLocationEffect(Location, Map, 0x11AD, 25, 10);
}
public override void DoAttackEffect(Mobile m)
{
m.Damage(Utility.Dice(6, 10, 40), m);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class FactionSawTrapDeed : BaseFactionTrapDeed
{
public FactionSawTrapDeed() : base(0x1107)
{
}
public FactionSawTrapDeed(Serial serial) : base(serial)
{
}
public override Type TrapType => typeof(FactionSawTrap);
public override int LabelNumber => 1044604; // faction saw trap deed
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,78 @@
using System;
namespace Server.Factions
{
public class FactionSpikeTrap : BaseFactionTrap
{
public FactionSpikeTrap(Faction f = null, Mobile m = null) : base(f, m, 0x11A0)
{
}
public FactionSpikeTrap(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1044601; // faction spike trap
public override int AttackMessage => 1010545; // Large spikes in the ground spring up piercing your skin!
public override int DisarmMessage =>
1010541; // You carefully dismantle the trigger on the spikes and disable the trap.
public override int EffectSound => 0x22E;
public override int MessageHue => 0x5A;
public override AllowedPlacing AllowedPlacing => AllowedPlacing.ControlledFactionTown;
public override void DoVisibleEffect()
{
Effects.SendLocationEffect(Location, Map, 0x11A4, 12, 6);
}
public override void DoAttackEffect(Mobile m)
{
m.Damage(Utility.Dice(6, 10, 40), m);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class FactionSpikeTrapDeed : BaseFactionTrapDeed
{
public FactionSpikeTrapDeed() : base(0x11A5)
{
}
public FactionSpikeTrapDeed(Serial serial) : base(serial)
{
}
public override Type TrapType => typeof(FactionSpikeTrap);
public override int LabelNumber => 1044605; // faction spike trap deed
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,71 @@
namespace Server.Factions
{
public class FactionTrapRemovalKit : Item
{
[Constructible]
public FactionTrapRemovalKit() : base(7867)
{
LootType = LootType.Blessed;
Charges = 25;
}
public FactionTrapRemovalKit(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public int Charges{ get; set; }
public override int LabelNumber => 1041508; // a faction trap removal kit
public void ConsumeCharge(Mobile consumer)
{
--Charges;
if (Charges <= 0)
{
Delete();
consumer?.SendLocalizedMessage(1042531); // You have used all of the parts in your trap removal kit.
}
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
// NOTE: OSI does not list uses remaining; intentional difference
list.Add(1060584, Charges.ToString()); // uses remaining: ~1_val~
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
writer.WriteEncodedInt(Charges);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 1:
{
Charges = reader.ReadEncodedInt();
break;
}
case 0:
{
Charges = 25;
break;
}
}
}
}
}

View file

@ -0,0 +1,107 @@
using Server.Mobiles;
namespace Server.Factions
{
public class FactionWarHorse : BaseMount
{
public const int SilverPrice = 500;
public const int GoldPrice = 3000;
private Faction m_Faction;
public FactionWarHorse(Faction faction = null)
: base("a war horse", 0xE2, 0x3EA0, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4)
{
BaseSoundID = 0xA8;
SetStr(400);
SetDex(125);
SetInt(51, 55);
SetHits(240);
SetMana(0);
SetDamage(5, 8);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 40, 50);
SetResistance(ResistanceType.Fire, 30, 40);
SetResistance(ResistanceType.Cold, 30, 40);
SetResistance(ResistanceType.Poison, 30, 40);
SetResistance(ResistanceType.Energy, 30, 40);
SetSkill(SkillName.MagicResist, 25.1, 30.0);
SetSkill(SkillName.Tactics, 29.3, 44.0);
SetSkill(SkillName.Wrestling, 29.3, 44.0);
Fame = 300;
Karma = 300;
Tamable = true;
ControlSlots = 1;
Faction = faction;
}
public FactionWarHorse(Serial serial) : base(serial)
{
}
public override string CorpseName => "a war horse corpse";
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public Faction Faction
{
get => m_Faction;
set
{
m_Faction = value;
Body = m_Faction?.Definition.WarHorseBody ?? 0xE2;
ItemID = m_Faction?.Definition.WarHorseItem ?? 0x3EA0;
}
}
public override FoodType FavoriteFood => FoodType.FruitsAndVegies | FoodType.GrainsAndHay;
public override void OnDoubleClick(Mobile from)
{
PlayerState pl = PlayerState.Find(from);
if (pl == null)
from.SendLocalizedMessage(1010366); // You cannot mount a faction war horse!
else if (pl.Faction != Faction)
from.SendLocalizedMessage(1010367); // You cannot ride an opposing faction's war horse!
else if (pl.Rank.Rank < 2)
from.SendLocalizedMessage(
1010368); // You must achieve a faction rank of at least two before riding a war horse!
else
base.OnDoubleClick(from);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
Faction.WriteReference(writer, m_Faction);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
Faction = Faction.ReadReference(reader);
break;
}
}
}
}
}

View file

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

View file

@ -0,0 +1,758 @@
using System;
using System.Collections.Generic;
using Server.Factions.AI;
using Server.Items;
using Server.Mobiles;
using Server.Spells;
using Server.Spells.Fifth;
using Server.Spells.First;
using Server.Spells.Fourth;
using Server.Spells.Second;
using Server.Spells.Seventh;
using Server.Spells.Sixth;
using Server.Spells.Third;
using Server.Targeting;
namespace Server.Factions
{
[Flags]
public enum GuardAI
{
Bless = 0x01, // heal, cure, +stats
Curse = 0x02, // poison, -stats
Melee = 0x04, // weapons
Magic = 0x08, // damage spells
Smart = 0x10 // smart weapons/damage spells
}
public class ComboEntry
{
public ComboEntry(Type spell, int chance = 100) : this(spell, chance, TimeSpan.Zero)
{
}
public ComboEntry(Type spell, int chance, TimeSpan hold)
{
Spell = spell;
Chance = chance;
Hold = hold;
}
public Type Spell{ get; }
public TimeSpan Hold{ get; }
public int Chance{ get; }
}
public class SpellCombo
{
public static readonly SpellCombo Simple = new SpellCombo(50,
new ComboEntry(typeof(ParalyzeSpell), 20),
new ComboEntry(typeof(ExplosionSpell), 100, TimeSpan.FromSeconds(2.8)),
new ComboEntry(typeof(PoisonSpell), 30),
new ComboEntry(typeof(EnergyBoltSpell))
);
public static readonly SpellCombo Strong = new SpellCombo(90,
new ComboEntry(typeof(ParalyzeSpell), 20),
new ComboEntry(typeof(ExplosionSpell), 50, TimeSpan.FromSeconds(2.8)),
new ComboEntry(typeof(PoisonSpell), 30),
new ComboEntry(typeof(ExplosionSpell), 100, TimeSpan.FromSeconds(2.8)),
new ComboEntry(typeof(EnergyBoltSpell)),
new ComboEntry(typeof(PoisonSpell), 30),
new ComboEntry(typeof(EnergyBoltSpell))
);
public SpellCombo(int mana, params ComboEntry[] entries)
{
Mana = mana;
Entries = entries;
}
public int Mana{ get; }
public ComboEntry[] Entries{ get; }
public static Spell Process(Mobile mob, Mobile targ, ref SpellCombo combo, ref int index, ref DateTime releaseTime)
{
while (++index < combo.Entries.Length)
{
ComboEntry entry = combo.Entries[index];
if (entry.Spell == typeof(PoisonSpell) && targ.Poisoned)
continue;
if (entry.Chance > Utility.Random(100))
{
releaseTime = DateTime.UtcNow + entry.Hold;
return (Spell)Activator.CreateInstance(entry.Spell, mob, null);
}
}
combo = null;
index = -1;
return null;
}
}
public class FactionGuardAI : BaseAI
{
private const int ManaReserve = 30;
private BandageContext m_Bandage;
private DateTime m_BandageStart;
private SpellCombo m_Combo;
private int m_ComboIndex = -1;
private BaseFactionGuard m_Guard;
private DateTime m_ReleaseTarget;
public FactionGuardAI(BaseFactionGuard guard) : base(guard)
{
m_Guard = guard;
}
public bool IsDamaged => m_Guard.Hits < m_Guard.HitsMax;
public bool IsPoisoned => m_Guard.Poisoned;
public TimeSpan TimeUntilBandage
{
get
{
if (m_Bandage != null && m_Bandage.Timer == null)
m_Bandage = null;
if (m_Bandage == null)
return TimeSpan.MaxValue;
TimeSpan ts = m_BandageStart + m_Bandage.Timer.Delay - DateTime.UtcNow;
if (ts < TimeSpan.FromSeconds(-1.0))
{
m_Bandage = null;
return TimeSpan.MaxValue;
}
if (ts < TimeSpan.Zero)
ts = TimeSpan.Zero;
return ts;
}
}
public bool IsAllowed(GuardAI flag)
{
return (m_Guard.GuardAI & flag) == flag;
}
public bool DequipWeapon()
{
Container pack = m_Guard.Backpack;
if (pack == null)
return false;
if (m_Guard.Weapon is Item weapon && weapon.Parent == m_Guard && !(weapon is Fists))
{
pack.DropItem(weapon);
return true;
}
return false;
}
public bool EquipWeapon()
{
Item weapon = m_Guard.Backpack?.FindItemByType<BaseWeapon>();
return weapon != null && m_Guard.EquipItem(weapon);
}
public bool StartBandage()
{
m_Bandage = null;
if (m_Guard.Backpack?.FindItemByType<Bandage>() == null)
return false;
m_Bandage = BandageContext.BeginHeal(m_Guard, m_Guard);
m_BandageStart = DateTime.UtcNow;
return m_Bandage != null;
}
public bool UseItemByType(Type type)
{
Container pack = m_Guard.Backpack;
Item item = pack?.FindItemByType(type);
if (item == null)
return false;
bool requip = DequipWeapon();
item.OnDoubleClick(m_Guard);
if (requip)
EquipWeapon();
return true;
}
public int GetStatMod(Mobile mob, StatType type)
{
StatMod mod = mob.GetStatMod($"[Magic] {type} Offset");
if (mod == null)
return 0;
return mod.Offset;
}
public Spell RandomOffenseSpell()
{
int maxCircle = (int)((m_Guard.Skills.Magery.Value + 20.0) / (100.0 / 7.0));
if (maxCircle < 1)
maxCircle = 1;
switch (Utility.Random(maxCircle * 2))
{
case 0:
case 1: return new MagicArrowSpell(m_Guard);
case 2:
case 3: return new HarmSpell(m_Guard);
case 4:
case 5: return new FireballSpell(m_Guard);
case 6:
case 7: return new LightningSpell(m_Guard);
case 8: return new MindBlastSpell(m_Guard);
case 9: return new ParalyzeSpell(m_Guard);
case 10: return new EnergyBoltSpell(m_Guard);
case 11: return new ExplosionSpell(m_Guard);
default: return new FlameStrikeSpell(m_Guard);
}
}
public Mobile FindDispelTarget(bool activeOnly)
{
if (m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel(m_Mobile) || m_Mobile.AutoDispel)
return null;
if (activeOnly)
{
List<AggressorInfo> aggressed = m_Mobile.Aggressed;
List<AggressorInfo> aggressors = m_Mobile.Aggressors;
Mobile active = null;
double activePrio = 0.0;
Mobile comb = m_Mobile.Combatant;
if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && m_Mobile.InRange(comb, 12) &&
CanDispel(comb))
{
active = comb;
activePrio = m_Mobile.GetDistanceToSqrt(comb);
if (activePrio <= 2)
return active;
}
for (int i = 0; i < aggressed.Count; ++i)
{
AggressorInfo info = aggressed[i];
Mobile m = info.Defender;
if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, 12) && CanDispel(m))
{
double prio = m_Mobile.GetDistanceToSqrt(m);
if (active == null || prio < activePrio)
{
active = m;
activePrio = prio;
if (activePrio <= 2)
return active;
}
}
}
for (int i = 0; i < aggressors.Count; ++i)
{
AggressorInfo info = aggressors[i];
Mobile m = info.Attacker;
if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, 12) && CanDispel(m))
{
double prio = m_Mobile.GetDistanceToSqrt(m);
if (active == null || prio < activePrio)
{
active = m;
activePrio = prio;
if (activePrio <= 2)
return active;
}
}
}
return active;
}
Map map = m_Mobile.Map;
if (map != null)
{
Mobile active = null, inactive = null;
double actPrio = 0.0, inactPrio = 0.0;
Mobile comb = m_Mobile.Combatant;
if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && CanDispel(comb))
{
active = inactive = comb;
actPrio = inactPrio = m_Mobile.GetDistanceToSqrt(comb);
}
foreach (Mobile m in m_Mobile.GetMobilesInRange(12))
if (m != m_Mobile && CanDispel(m))
{
double prio = m_Mobile.GetDistanceToSqrt(m);
if (inactive == null || prio < inactPrio)
{
inactive = m;
inactPrio = prio;
}
if ((m_Mobile.Combatant == m || m.Combatant == m_Mobile) && (active == null || prio < actPrio))
{
active = m;
actPrio = prio;
}
}
return active ?? inactive;
}
return null;
}
public bool CanDispel(Mobile m)
{
return m is BaseCreature creature && creature.Summoned && m_Mobile.CanBeHarmful(creature, false) &&
!creature.IsAnimatedDead;
}
public void RunTo(Mobile m)
{
/*if ( m.Paralyzed || m.Frozen )
{
if ( m_Mobile.InRange( m, 1 ) )
RunFrom( m );
else if ( !m_Mobile.InRange( m, m_Mobile.RangeFight > 2 ? m_Mobile.RangeFight : 2 ) && !MoveTo( m, true, 1 ) )
OnFailedMove();
}
else
{*/
if (!m_Mobile.InRange(m, m_Mobile.RangeFight))
{
if (!MoveTo(m, true, 1))
OnFailedMove();
}
else if (m_Mobile.InRange(m, m_Mobile.RangeFight - 1))
{
RunFrom(m);
}
/*}*/
}
public void RunFrom(Mobile m)
{
Run((m_Mobile.GetDirectionTo(m) - 4) & Direction.Mask);
}
public void OnFailedMove()
{
/*if ( !m_Mobile.DisallowAllMoves && 20 > Utility.Random( 100 ) && IsAllowed( GuardAI.Magic ) )
{
if ( m_Mobile.Target != null )
m_Mobile.Target.Cancel( m_Mobile, TargetCancelType.Canceled );
new TeleportSpell( m_Mobile, null ).Cast();
m_Mobile.DebugSay( "I am stuck, I'm going to try teleporting away" );
}
else*/
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
{
if (m_Mobile.Debug)
m_Mobile.DebugSay("My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name);
m_Mobile.Combatant = m_Mobile.FocusMob;
Action = ActionType.Combat;
}
else
{
m_Mobile.DebugSay("I am stuck");
}
}
public void Run(Direction d)
{
if (m_Mobile.Spell != null && m_Mobile.Spell.IsCasting || m_Mobile.Paralyzed || m_Mobile.Frozen ||
m_Mobile.DisallowAllMoves)
return;
m_Mobile.Direction = d | Direction.Running;
if (!DoMove(m_Mobile.Direction, true))
OnFailedMove();
}
public override bool Think()
{
if (m_Mobile.Deleted)
return false;
Mobile combatant = m_Guard.Combatant;
if (combatant?.Deleted != false || !combatant.Alive || combatant.IsDeadBondedPet ||
!m_Mobile.CanSee(combatant) || !m_Mobile.CanBeHarmful(combatant, false) || combatant.Map != m_Mobile.Map)
{
// Our combatant is deleted, dead, hidden, or we cannot hurt them
// Try to find another combatant
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
{
m_Mobile.Combatant = combatant = m_Mobile.FocusMob;
m_Mobile.FocusMob = null;
}
else
{
m_Mobile.Combatant = combatant = null;
}
}
if (combatant != null && (!m_Mobile.InLOS(combatant) || !m_Mobile.InRange(combatant, 12)))
{
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
{
m_Mobile.Combatant = combatant = m_Mobile.FocusMob;
m_Mobile.FocusMob = null;
}
else if (!m_Mobile.InRange(combatant, 36))
{
m_Mobile.Combatant = combatant = null;
}
}
Mobile dispelTarget = FindDispelTarget(true);
if (m_Guard.Target != null && m_ReleaseTarget == DateTime.MinValue)
m_ReleaseTarget = DateTime.UtcNow + TimeSpan.FromSeconds(10.0);
if (m_Guard.Target != null && DateTime.UtcNow > m_ReleaseTarget)
{
Target targ = m_Guard.Target;
Mobile toHarm = dispelTarget ?? combatant;
if ((targ.Flags & TargetFlags.Harmful) != 0 && toHarm != null)
{
if (m_Guard.Map == toHarm.Map && (targ.Range < 0 || m_Guard.InRange(toHarm, targ.Range)) &&
m_Guard.CanSee(toHarm) && m_Guard.InLOS(toHarm))
targ.Invoke(m_Guard, toHarm);
else if ((targ as ISpellTarget)?.Spell is DispelSpell)
targ.Cancel(m_Guard, TargetCancelType.Canceled);
}
else if ((targ.Flags & TargetFlags.Beneficial) != 0)
{
targ.Invoke(m_Guard, m_Guard);
}
else
{
targ.Cancel(m_Guard, TargetCancelType.Canceled);
}
m_ReleaseTarget = DateTime.MinValue;
}
if (dispelTarget != null)
{
if (Action != ActionType.Combat)
Action = ActionType.Combat;
m_Guard.Warmode = true;
RunFrom(dispelTarget);
}
else if (combatant != null)
{
if (Action != ActionType.Combat)
Action = ActionType.Combat;
m_Guard.Warmode = true;
RunTo(combatant);
}
else if (m_Guard.Orders.Movement != MovementType.Stand)
{
Mobile toFollow = null;
if (m_Guard.Town != null && m_Guard.Orders.Movement == MovementType.Follow)
{
toFollow = m_Guard.Orders.Follow ?? m_Guard.Town.Sheriff;
}
if (toFollow != null && toFollow.Map == m_Guard.Map &&
toFollow.InRange(m_Guard, m_Guard.RangePerception * 3) &&
Town.FromRegion(toFollow.Region) == m_Guard.Town)
{
if (Action != ActionType.Combat)
Action = ActionType.Combat;
if (m_Mobile.CurrentSpeed != m_Mobile.ActiveSpeed)
m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed;
m_Guard.Warmode = true;
RunTo(toFollow);
}
else
{
if (Action != ActionType.Wander)
Action = ActionType.Wander;
if (m_Mobile.CurrentSpeed != m_Mobile.PassiveSpeed)
m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed;
m_Guard.Warmode = false;
WalkRandomInHome(2, 2, 1);
}
}
else
{
if (Action != ActionType.Wander)
Action = ActionType.Wander;
m_Guard.Warmode = false;
}
if ((IsDamaged || IsPoisoned) && m_Guard.Skills.Healing.Base > 20.0)
{
TimeSpan ts = TimeUntilBandage;
if (ts == TimeSpan.MaxValue)
StartBandage();
}
Spell spell = m_Mobile.Spell as Spell;
if (spell == null && Core.TickCount - m_Mobile.NextSpellTime >= 0)
{
DateTime toRelease = DateTime.MinValue;
if (IsPoisoned)
{
Poison p = m_Guard.Poison;
TimeSpan ts = TimeUntilBandage;
if (p != Poison.Lesser || ts == TimeSpan.MaxValue || TimeUntilBandage < TimeSpan.FromSeconds(1.5) ||
m_Guard.HitsMax - m_Guard.Hits > Utility.Random(250))
{
if (IsAllowed(GuardAI.Bless))
spell = new CureSpell(m_Guard);
else
UseItemByType(typeof(BaseCurePotion));
}
}
else if (IsDamaged && m_Guard.HitsMax - m_Guard.Hits > Utility.Random(200))
{
if (IsAllowed(GuardAI.Magic) && m_Guard.Hits * 100 / Math.Max(m_Guard.HitsMax, 1) < 10 &&
m_Guard.Home != Point3D.Zero && !Utility.InRange(m_Guard.Location, m_Guard.Home, 15) &&
m_Guard.Mana >= 11)
{
spell = new RecallSpell(m_Guard,
new RunebookEntry(m_Guard.Home, m_Guard.Map, "Guard's Home"));
}
else if (IsAllowed(GuardAI.Bless))
{
if (m_Guard.Mana >= 11 && m_Guard.Hits + 30 < m_Guard.HitsMax)
spell = new GreaterHealSpell(m_Guard);
else if (m_Guard.Hits + 10 < m_Guard.HitsMax &&
(m_Guard.Mana < 11 || m_Guard.NextCombatTime - Core.TickCount > 2000))
spell = new HealSpell(m_Guard);
}
else if (m_Guard.CanBeginAction<BaseHealPotion>())
{
UseItemByType(typeof(BaseHealPotion));
}
}
else if (dispelTarget != null &&
(IsAllowed(GuardAI.Magic) || IsAllowed(GuardAI.Bless) || IsAllowed(GuardAI.Curse)))
{
if (!dispelTarget.Paralyzed && m_Guard.Mana > ManaReserve + 20 && 40 > Utility.Random(100))
spell = new ParalyzeSpell(m_Guard);
else
spell = new DispelSpell(m_Guard);
}
if (combatant != null)
{
if (m_Combo != null)
{
if (spell == null)
{
spell = SpellCombo.Process(m_Guard, combatant, ref m_Combo, ref m_ComboIndex, ref toRelease);
}
else
{
m_Combo = null;
m_ComboIndex = -1;
}
}
else if (20 > Utility.Random(100) && IsAllowed(GuardAI.Magic))
{
if (80 > Utility.Random(100))
{
m_Combo = IsAllowed(GuardAI.Smart) ? SpellCombo.Simple : SpellCombo.Strong;
m_ComboIndex = -1;
if (m_Guard.Mana >= ManaReserve + m_Combo.Mana)
{
spell = SpellCombo.Process(m_Guard, combatant, ref m_Combo, ref m_ComboIndex, ref toRelease);
}
else
{
m_Combo = null;
if (m_Guard.Mana >= ManaReserve + 40)
spell = RandomOffenseSpell();
}
}
else if (m_Guard.Mana >= ManaReserve + 40)
{
spell = RandomOffenseSpell();
}
}
if (spell == null && 2 > Utility.Random(100) && m_Guard.Mana >= ManaReserve + 10)
{
int strMod = GetStatMod(m_Guard, StatType.Str);
int dexMod = GetStatMod(m_Guard, StatType.Dex);
int intMod = GetStatMod(m_Guard, StatType.Int);
List<Type> types = new List<Type>();
if (strMod <= 0)
types.Add(typeof(StrengthSpell));
if (dexMod <= 0 && IsAllowed(GuardAI.Melee))
types.Add(typeof(AgilitySpell));
if (intMod <= 0 && IsAllowed(GuardAI.Magic))
types.Add(typeof(CunningSpell));
if (IsAllowed(GuardAI.Bless))
{
if (types.Count > 1)
spell = new BlessSpell(m_Guard);
else if (types.Count == 1)
spell = Activator.CreateInstance(types[0], m_Guard, null) as Spell;
}
else if (types.Count > 0)
{
if (types[0] == typeof(StrengthSpell))
UseItemByType(typeof(BaseStrengthPotion));
else if (types[0] == typeof(AgilitySpell))
UseItemByType(typeof(BaseAgilityPotion));
}
}
if (spell == null && 2 > Utility.Random(100) && m_Guard.Mana >= ManaReserve + 10 &&
IsAllowed(GuardAI.Curse))
{
if (!combatant.Poisoned && 40 > Utility.Random(100))
{
spell = new PoisonSpell(m_Guard);
}
else
{
int strMod = GetStatMod(combatant, StatType.Str);
int dexMod = GetStatMod(combatant, StatType.Dex);
int intMod = GetStatMod(combatant, StatType.Int);
List<Type> types = new List<Type>();
if (strMod >= 0)
types.Add(typeof(WeakenSpell));
if (dexMod >= 0 && IsAllowed(GuardAI.Melee))
types.Add(typeof(ClumsySpell));
if (intMod >= 0 && IsAllowed(GuardAI.Magic))
types.Add(typeof(FeeblemindSpell));
if (types.Count > 1)
spell = new CurseSpell(m_Guard);
else if (types.Count == 1)
spell = (Spell)Activator.CreateInstance(types[0], m_Guard, null);
}
}
}
if (spell != null && m_Guard.HitsMax - m_Guard.Hits + 10 > Utility.Random(100))
{
Type type = null;
if (spell is GreaterHealSpell)
type = typeof(BaseHealPotion);
else if (spell is CureSpell)
type = typeof(BaseCurePotion);
else if (spell is StrengthSpell)
type = typeof(BaseStrengthPotion);
else if (spell is AgilitySpell)
type = typeof(BaseAgilityPotion);
if (type == typeof(BaseHealPotion) && !m_Guard.CanBeginAction(type))
type = null;
if (type != null && m_Guard.Target == null && UseItemByType(type))
{
if (spell is GreaterHealSpell)
{
if (m_Guard.Hits + 30 > m_Guard.HitsMax && m_Guard.Hits + 10 < m_Guard.HitsMax)
spell = new HealSpell(m_Guard);
}
else
{
spell = null;
}
}
}
else if (spell == null && m_Guard.Stam < m_Guard.StamMax / 3 && IsAllowed(GuardAI.Melee))
{
UseItemByType(typeof(BaseRefreshPotion));
}
if (spell?.Cast() != true)
EquipWeapon();
}
else if (spell?.State == SpellState.Sequencing)
{
EquipWeapon();
}
return true;
}
}
}

View file

@ -0,0 +1,141 @@
using System.Collections.Generic;
namespace Server.Factions.AI
{
public enum ReactionType
{
Ignore,
Warn,
Attack
}
public enum MovementType
{
Stand,
Patrol,
Follow
}
public class Reaction
{
public Reaction(Faction faction, ReactionType type)
{
Faction = faction;
Type = type;
}
public Reaction(GenericReader reader)
{
int version = reader.ReadEncodedInt();
switch (version)
{
case 0:
{
Faction = Faction.ReadReference(reader);
Type = (ReactionType)reader.ReadEncodedInt();
break;
}
}
}
public Faction Faction{ get; }
public ReactionType Type{ get; set; }
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(0); // version
Faction.WriteReference(writer, Faction);
writer.WriteEncodedInt((int)Type);
}
}
public class Orders
{
private List<Reaction> m_Reactions;
public Orders(BaseFactionGuard guard)
{
Guard = guard;
m_Reactions = new List<Reaction>();
Movement = MovementType.Patrol;
}
public Orders(BaseFactionGuard guard, GenericReader reader)
{
Guard = guard;
int version = reader.ReadEncodedInt();
switch (version)
{
case 1:
{
Follow = reader.ReadMobile();
goto case 0;
}
case 0:
{
int count = reader.ReadEncodedInt();
m_Reactions = new List<Reaction>(count);
for (int i = 0; i < count; ++i)
m_Reactions.Add(new Reaction(reader));
Movement = (MovementType)reader.ReadEncodedInt();
break;
}
}
}
public BaseFactionGuard Guard{ get; }
public MovementType Movement{ get; set; }
public Mobile Follow{ get; set; }
public Reaction GetReaction(Faction faction)
{
Reaction reaction;
for (int i = 0; i < m_Reactions.Count; ++i)
{
reaction = m_Reactions[i];
if (reaction.Faction == faction)
return reaction;
}
reaction = new Reaction(faction,
faction == null || faction == Guard.Faction ? ReactionType.Ignore : ReactionType.Attack);
m_Reactions.Add(reaction);
return reaction;
}
public void SetReaction(Faction faction, ReactionType type)
{
Reaction reaction = GetReaction(faction);
reaction.Type = type;
}
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(1); // version
writer.Write(Follow);
writer.WriteEncodedInt(m_Reactions.Count);
for (int i = 0; i < m_Reactions.Count; ++i)
m_Reactions[i].Serialize(writer);
writer.WriteEncodedInt((int)Movement);
}
}
}

View file

@ -0,0 +1,72 @@
using Server.Items;
namespace Server.Factions
{
public class FactionBerserker : BaseFactionGuard
{
[Constructible]
public FactionBerserker() : base("the berserker")
{
GenerateBody(false, false);
SetStr(126, 150);
SetDex(61, 85);
SetInt(81, 95);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 30, 50);
SetResistance(ResistanceType.Fire, 30, 50);
SetResistance(ResistanceType.Cold, 30, 50);
SetResistance(ResistanceType.Energy, 30, 50);
SetResistance(ResistanceType.Poison, 30, 50);
VirtualArmor = 24;
SetSkill(SkillName.Swords, 100.0, 110.0);
SetSkill(SkillName.Wrestling, 100.0, 110.0);
SetSkill(SkillName.Tactics, 100.0, 110.0);
SetSkill(SkillName.MagicResist, 100.0, 110.0);
SetSkill(SkillName.Healing, 100.0, 110.0);
SetSkill(SkillName.Anatomy, 100.0, 110.0);
SetSkill(SkillName.Magery, 100.0, 110.0);
SetSkill(SkillName.EvalInt, 100.0, 110.0);
SetSkill(SkillName.Meditation, 100.0, 110.0);
AddItem(Immovable(Rehued(new BodySash(), 1645)));
AddItem(Immovable(Rehued(new Kilt(), 1645)));
AddItem(Immovable(Rehued(new Sandals(), 1645)));
AddItem(Newbied(new DoubleAxe()));
HairItemID = 0x2047; // Afro
HairHue = 0x29;
FacialHairItemID = 0x204B; // Medium Short Beard
FacialHairHue = 0x29;
PackItem(new Bandage(Utility.RandomMinMax(30, 40)));
PackStrongPotions(6, 12);
}
public FactionBerserker(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Melee | GuardAI.Curse | GuardAI.Bless;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,68 @@
using Server.Items;
namespace Server.Factions
{
public class FactionDeathKnight : BaseFactionGuard
{
[Constructible]
public FactionDeathKnight() : base("the death knight")
{
GenerateBody(false, false);
Hue = 1;
SetStr(126, 150);
SetDex(61, 85);
SetInt(81, 95);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 30, 50);
SetResistance(ResistanceType.Fire, 30, 50);
SetResistance(ResistanceType.Cold, 30, 50);
SetResistance(ResistanceType.Energy, 30, 50);
SetResistance(ResistanceType.Poison, 30, 50);
VirtualArmor = 24;
SetSkill(SkillName.Swords, 100.0, 110.0);
SetSkill(SkillName.Wrestling, 100.0, 110.0);
SetSkill(SkillName.Tactics, 100.0, 110.0);
SetSkill(SkillName.MagicResist, 100.0, 110.0);
SetSkill(SkillName.Healing, 100.0, 110.0);
SetSkill(SkillName.Anatomy, 100.0, 110.0);
SetSkill(SkillName.Magery, 100.0, 110.0);
SetSkill(SkillName.EvalInt, 100.0, 110.0);
SetSkill(SkillName.Meditation, 100.0, 110.0);
Item shroud = new Item(0x204E);
shroud.Layer = Layer.OuterTorso;
AddItem(Immovable(Rehued(shroud, 1109)));
AddItem(Newbied(Rehued(new ExecutionersAxe(), 2211)));
PackItem(new Bandage(Utility.RandomMinMax(30, 40)));
PackStrongPotions(6, 12);
}
public FactionDeathKnight(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Melee | GuardAI.Curse | GuardAI.Bless;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,72 @@
using Server.Items;
namespace Server.Factions
{
public class FactionDragoon : BaseFactionGuard
{
[Constructible]
public FactionDragoon() : base("the dragoon")
{
GenerateBody(false, false);
SetStr(151, 175);
SetDex(61, 85);
SetInt(151, 175);
SetResistance(ResistanceType.Physical, 40, 60);
SetResistance(ResistanceType.Fire, 40, 60);
SetResistance(ResistanceType.Cold, 40, 60);
SetResistance(ResistanceType.Energy, 40, 60);
SetResistance(ResistanceType.Poison, 40, 60);
VirtualArmor = 32;
SetSkill(SkillName.Macing, 110.0, 120.0);
SetSkill(SkillName.Wrestling, 110.0, 120.0);
SetSkill(SkillName.Tactics, 110.0, 120.0);
SetSkill(SkillName.MagicResist, 110.0, 120.0);
SetSkill(SkillName.Healing, 110.0, 120.0);
SetSkill(SkillName.Anatomy, 110.0, 120.0);
SetSkill(SkillName.Magery, 110.0, 120.0);
SetSkill(SkillName.EvalInt, 110.0, 120.0);
SetSkill(SkillName.Meditation, 110.0, 120.0);
AddItem(Immovable(Rehued(new Cloak(), 1645)));
AddItem(Immovable(Rehued(new PlateChest(), 1645)));
AddItem(Immovable(Rehued(new PlateLegs(), 1109)));
AddItem(Immovable(Rehued(new PlateArms(), 1109)));
AddItem(Immovable(Rehued(new PlateGloves(), 1109)));
AddItem(Immovable(Rehued(new PlateGorget(), 1109)));
AddItem(Immovable(Rehued(new PlateHelm(), 1109)));
AddItem(Newbied(new WarHammer()));
AddItem(Immovable(Rehued(new VirtualMountItem(this), 1109)));
PackItem(new Bandage(Utility.RandomMinMax(30, 40)));
PackStrongPotions(6, 12);
}
public FactionDragoon(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Magic | GuardAI.Melee | GuardAI.Smart | GuardAI.Bless | GuardAI.Curse;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,65 @@
using Server.Items;
namespace Server.Factions
{
public class FactionHenchman : BaseFactionGuard
{
[Constructible]
public FactionHenchman() : base("the henchman")
{
GenerateBody(false, true);
SetStr(91, 115);
SetDex(61, 85);
SetInt(81, 95);
SetDamage(10, 14);
SetResistance(ResistanceType.Physical, 10, 30);
SetResistance(ResistanceType.Fire, 10, 30);
SetResistance(ResistanceType.Cold, 10, 30);
SetResistance(ResistanceType.Energy, 10, 30);
SetResistance(ResistanceType.Poison, 10, 30);
VirtualArmor = 8;
SetSkill(SkillName.Fencing, 80.0, 90.0);
SetSkill(SkillName.Wrestling, 80.0, 90.0);
SetSkill(SkillName.Tactics, 80.0, 90.0);
SetSkill(SkillName.MagicResist, 80.0, 90.0);
SetSkill(SkillName.Healing, 80.0, 90.0);
SetSkill(SkillName.Anatomy, 80.0, 90.0);
AddItem(new StuddedChest());
AddItem(new StuddedLegs());
AddItem(new StuddedArms());
AddItem(new StuddedGloves());
AddItem(new StuddedGorget());
AddItem(new Boots());
AddItem(Newbied(new Spear()));
PackItem(new Bandage(Utility.RandomMinMax(10, 20)));
PackWeakPotions(1, 4);
}
public FactionHenchman(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Melee;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,73 @@
using Server.Items;
namespace Server.Factions
{
public class FactionKnight : BaseFactionGuard
{
[Constructible]
public FactionKnight() : base("the knight")
{
GenerateBody(false, false);
SetStr(126, 150);
SetDex(61, 85);
SetInt(81, 95);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 30, 50);
SetResistance(ResistanceType.Fire, 30, 50);
SetResistance(ResistanceType.Cold, 30, 50);
SetResistance(ResistanceType.Energy, 30, 50);
SetResistance(ResistanceType.Poison, 30, 50);
VirtualArmor = 24;
SetSkill(SkillName.Swords, 100.0, 110.0);
SetSkill(SkillName.Wrestling, 100.0, 110.0);
SetSkill(SkillName.Tactics, 100.0, 110.0);
SetSkill(SkillName.MagicResist, 100.0, 110.0);
SetSkill(SkillName.Healing, 100.0, 110.0);
SetSkill(SkillName.Anatomy, 100.0, 110.0);
SetSkill(SkillName.Magery, 100.0, 110.0);
SetSkill(SkillName.EvalInt, 100.0, 110.0);
SetSkill(SkillName.Meditation, 100.0, 110.0);
AddItem(Immovable(Rehued(new ChainChest(), 2125)));
AddItem(Immovable(Rehued(new ChainLegs(), 2125)));
AddItem(Immovable(Rehued(new ChainCoif(), 2125)));
AddItem(Immovable(Rehued(new PlateArms(), 2125)));
AddItem(Immovable(Rehued(new PlateGloves(), 2125)));
AddItem(Immovable(Rehued(new BodySash(), 1254)));
AddItem(Immovable(Rehued(new Kilt(), 1254)));
AddItem(Immovable(Rehued(new Sandals(), 1254)));
AddItem(Newbied(new Bardiche()));
PackItem(new Bandage(Utility.RandomMinMax(30, 40)));
PackStrongPotions(6, 12);
}
public FactionKnight(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Magic | GuardAI.Melee | GuardAI.Smart | GuardAI.Curse | GuardAI.Bless;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,63 @@
using Server.Items;
namespace Server.Factions
{
public class FactionMercenary : BaseFactionGuard
{
[Constructible]
public FactionMercenary() : base("the mercenary")
{
GenerateBody(false, true);
SetStr(116, 125);
SetDex(61, 85);
SetInt(81, 95);
SetResistance(ResistanceType.Physical, 20, 40);
SetResistance(ResistanceType.Fire, 20, 40);
SetResistance(ResistanceType.Cold, 20, 40);
SetResistance(ResistanceType.Energy, 20, 40);
SetResistance(ResistanceType.Poison, 20, 40);
VirtualArmor = 16;
SetSkill(SkillName.Fencing, 90.0, 100.0);
SetSkill(SkillName.Wrestling, 90.0, 100.0);
SetSkill(SkillName.Tactics, 90.0, 100.0);
SetSkill(SkillName.MagicResist, 90.0, 100.0);
SetSkill(SkillName.Healing, 90.0, 100.0);
SetSkill(SkillName.Anatomy, 90.0, 100.0);
AddItem(new ChainChest());
AddItem(new ChainLegs());
AddItem(new RingmailArms());
AddItem(new RingmailGloves());
AddItem(new ChainCoif());
AddItem(new Boots());
AddItem(Newbied(new ShortSpear()));
PackItem(new Bandage(Utility.RandomMinMax(20, 30)));
PackStrongPotions(3, 8);
}
public FactionMercenary(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Melee | GuardAI.Smart;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,66 @@
using Server.Items;
namespace Server.Factions
{
public class FactionNecromancer : BaseFactionGuard
{
[Constructible]
public FactionNecromancer() : base("the necromancer")
{
GenerateBody(false, false);
Hue = 1;
SetStr(151, 175);
SetDex(61, 85);
SetInt(151, 175);
SetResistance(ResistanceType.Physical, 40, 60);
SetResistance(ResistanceType.Fire, 40, 60);
SetResistance(ResistanceType.Cold, 40, 60);
SetResistance(ResistanceType.Energy, 40, 60);
SetResistance(ResistanceType.Poison, 40, 60);
VirtualArmor = 32;
SetSkill(SkillName.Macing, 110.0, 120.0);
SetSkill(SkillName.Wrestling, 110.0, 120.0);
SetSkill(SkillName.Tactics, 110.0, 120.0);
SetSkill(SkillName.MagicResist, 110.0, 120.0);
SetSkill(SkillName.Healing, 110.0, 120.0);
SetSkill(SkillName.Anatomy, 110.0, 120.0);
SetSkill(SkillName.Magery, 110.0, 120.0);
SetSkill(SkillName.EvalInt, 110.0, 120.0);
SetSkill(SkillName.Meditation, 110.0, 120.0);
Item shroud = new Item(0x204E);
shroud.Layer = Layer.OuterTorso;
AddItem(Immovable(Rehued(shroud, 1109)));
AddItem(Newbied(Rehued(new GnarledStaff(), 2211)));
PackItem(new Bandage(Utility.RandomMinMax(30, 40)));
PackStrongPotions(6, 12);
}
public FactionNecromancer(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Magic | GuardAI.Smart | GuardAI.Bless | GuardAI.Curse;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,73 @@
using Server.Items;
namespace Server.Factions
{
public class FactionPaladin : BaseFactionGuard
{
[Constructible]
public FactionPaladin() : base("the paladin")
{
GenerateBody(false, false);
SetStr(151, 175);
SetDex(61, 85);
SetInt(81, 95);
SetResistance(ResistanceType.Physical, 40, 60);
SetResistance(ResistanceType.Fire, 40, 60);
SetResistance(ResistanceType.Cold, 40, 60);
SetResistance(ResistanceType.Energy, 40, 60);
SetResistance(ResistanceType.Poison, 40, 60);
VirtualArmor = 32;
SetSkill(SkillName.Swords, 110.0, 120.0);
SetSkill(SkillName.Wrestling, 110.0, 120.0);
SetSkill(SkillName.Tactics, 110.0, 120.0);
SetSkill(SkillName.MagicResist, 110.0, 120.0);
SetSkill(SkillName.Healing, 110.0, 120.0);
SetSkill(SkillName.Anatomy, 110.0, 120.0);
SetSkill(SkillName.Magery, 110.0, 120.0);
SetSkill(SkillName.EvalInt, 110.0, 120.0);
SetSkill(SkillName.Meditation, 110.0, 120.0);
AddItem(Immovable(Rehued(new PlateChest(), 2125)));
AddItem(Immovable(Rehued(new PlateLegs(), 2125)));
AddItem(Immovable(Rehued(new PlateHelm(), 2125)));
AddItem(Immovable(Rehued(new PlateGorget(), 2125)));
AddItem(Immovable(Rehued(new PlateArms(), 2125)));
AddItem(Immovable(Rehued(new PlateGloves(), 2125)));
AddItem(Immovable(Rehued(new BodySash(), 1254)));
AddItem(Immovable(Rehued(new Cloak(), 1254)));
AddItem(Newbied(new Halberd()));
AddItem(Immovable(Rehued(new VirtualMountItem(this), 1254)));
PackItem(new Bandage(Utility.RandomMinMax(30, 40)));
PackStrongPotions(6, 12);
}
public FactionPaladin(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Magic | GuardAI.Melee | GuardAI.Smart | GuardAI.Curse | GuardAI.Bless;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,70 @@
using Server.Items;
namespace Server.Factions
{
public class FactionSorceress : BaseFactionGuard
{
[Constructible]
public FactionSorceress() : base("the sorceress")
{
GenerateBody(true, false);
SetStr(126, 150);
SetDex(61, 85);
SetInt(126, 150);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 30, 50);
SetResistance(ResistanceType.Fire, 30, 50);
SetResistance(ResistanceType.Cold, 30, 50);
SetResistance(ResistanceType.Energy, 30, 50);
SetResistance(ResistanceType.Poison, 30, 50);
VirtualArmor = 24;
SetSkill(SkillName.Macing, 100.0, 110.0);
SetSkill(SkillName.Wrestling, 100.0, 110.0);
SetSkill(SkillName.Tactics, 100.0, 110.0);
SetSkill(SkillName.MagicResist, 100.0, 110.0);
SetSkill(SkillName.Healing, 100.0, 110.0);
SetSkill(SkillName.Anatomy, 100.0, 110.0);
SetSkill(SkillName.Magery, 100.0, 110.0);
SetSkill(SkillName.EvalInt, 100.0, 110.0);
SetSkill(SkillName.Meditation, 100.0, 110.0);
AddItem(Immovable(Rehued(new WizardsHat(), 1325)));
AddItem(Immovable(Rehued(new Sandals(), 1325)));
AddItem(Immovable(Rehued(new LeatherGorget(), 1325)));
AddItem(Immovable(Rehued(new LeatherGloves(), 1325)));
AddItem(Immovable(Rehued(new LeatherLegs(), 1325)));
AddItem(Immovable(Rehued(new Skirt(), 1325)));
AddItem(Immovable(Rehued(new FemaleLeatherChest(), 1325)));
AddItem(Newbied(Rehued(new QuarterStaff(), 1310)));
PackItem(new Bandage(Utility.RandomMinMax(30, 40)));
PackStrongPotions(6, 12);
}
public FactionSorceress(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Magic | GuardAI.Bless | GuardAI.Curse;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,67 @@
using Server.Items;
namespace Server.Factions
{
public class FactionWizard : BaseFactionGuard
{
[Constructible]
public FactionWizard() : base("the wizard")
{
GenerateBody(false, false);
SetStr(151, 175);
SetDex(61, 85);
SetInt(151, 175);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 40, 60);
SetResistance(ResistanceType.Fire, 40, 60);
SetResistance(ResistanceType.Cold, 40, 60);
SetResistance(ResistanceType.Energy, 40, 60);
SetResistance(ResistanceType.Poison, 40, 60);
VirtualArmor = 32;
SetSkill(SkillName.Macing, 110.0, 120.0);
SetSkill(SkillName.Wrestling, 110.0, 120.0);
SetSkill(SkillName.Tactics, 110.0, 120.0);
SetSkill(SkillName.MagicResist, 110.0, 120.0);
SetSkill(SkillName.Healing, 110.0, 120.0);
SetSkill(SkillName.Anatomy, 110.0, 120.0);
SetSkill(SkillName.Magery, 110.0, 120.0);
SetSkill(SkillName.EvalInt, 110.0, 120.0);
SetSkill(SkillName.Meditation, 110.0, 120.0);
AddItem(Immovable(Rehued(new WizardsHat(), 1325)));
AddItem(Immovable(Rehued(new Sandals(), 1325)));
AddItem(Immovable(Rehued(new Robe(), 1310)));
AddItem(Immovable(Rehued(new LeatherGloves(), 1325)));
AddItem(Newbied(Rehued(new GnarledStaff(), 1310)));
PackItem(new Bandage(Utility.RandomMinMax(30, 40)));
PackStrongPotions(6, 12);
}
public FactionWizard(Serial serial) : base(serial)
{
}
public override GuardAI GuardAI => GuardAI.Magic | GuardAI.Smart | GuardAI.Bless | GuardAI.Curse;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,121 @@
using System.Collections.Generic;
using Server.Mobiles;
namespace Server.Factions
{
public abstract class BaseFactionVendor : BaseVendor
{
private Faction m_Faction;
private Town m_Town;
public BaseFactionVendor(Town town, Faction faction, string title) : base(title)
{
Frozen = true;
CantWalk = true;
Female = false;
BodyValue = 400;
Name = NameList.RandomName("male");
RangeHome = 0;
m_Town = town;
m_Faction = faction;
Register();
}
public BaseFactionVendor(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Town Town
{
get => m_Town;
set
{
Unregister();
m_Town = value;
Register();
}
}
[CommandProperty(AccessLevel.Counselor, AccessLevel.Administrator)]
public Faction Faction
{
get => m_Faction;
set
{
Unregister();
m_Faction = value;
Register();
}
}
protected override List<SBInfo> SBInfos{ get; } = new List<SBInfo>();
public void Register()
{
if (m_Town != null && m_Faction != null)
m_Town.RegisterVendor(this);
}
public override bool OnMoveOver(Mobile m)
{
if (Core.ML)
return true;
return base.OnMoveOver(m);
}
public void Unregister()
{
m_Town?.UnregisterVendor(this);
}
public override void InitSBInfo()
{
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
Unregister();
}
public override bool CheckVendorAccess(Mobile from)
{
return true;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
Town.WriteReference(writer, m_Town);
Faction.WriteReference(writer, m_Faction);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
m_Town = Town.ReadReference(reader);
m_Faction = Faction.ReadReference(reader);
Register();
break;
}
}
Frozen = true;
}
}
}

View file

@ -0,0 +1,66 @@
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
namespace Server.Factions
{
public class FactionBoardVendor : BaseFactionVendor
{
public FactionBoardVendor(Town town, Faction faction) :
base(town, faction, "the LumberMan") // NOTE: title inconsistant, as OSI
{
SetSkill(SkillName.Carpentry, 85.0, 100.0);
SetSkill(SkillName.Lumberjacking, 60.0, 83.0);
}
public FactionBoardVendor(Serial serial) : base(serial)
{
}
public override void InitSBInfo()
{
SBInfos.Add(new SBFactionBoard());
}
public override void InitOutfit()
{
base.InitOutfit();
AddItem(new HalfApron());
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class SBFactionBoard : SBInfo
{
public override IShopSellInfo SellInfo{ get; } = new InternalSellInfo();
public override List<GenericBuyInfo> BuyInfo{ get; } = new InternalBuyInfo();
public class InternalBuyInfo : List<GenericBuyInfo>
{
public InternalBuyInfo()
{
for (int i = 0; i < 5; ++i)
Add(new GenericBuyInfo(typeof(Board), 3, 20, 0x1BD7, 0));
}
}
public class InternalSellInfo : GenericSellInfo
{
}
}
}

View file

@ -0,0 +1,67 @@
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
namespace Server.Factions
{
public class FactionBottleVendor : BaseFactionVendor
{
public FactionBottleVendor(Town town, Faction faction) : base(town, faction, "the Bottle Seller")
{
SetSkill(SkillName.Alchemy, 85.0, 100.0);
SetSkill(SkillName.TasteID, 65.0, 88.0);
}
public FactionBottleVendor(Serial serial) : base(serial)
{
}
public override VendorShoeType ShoeType => Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals;
public override void InitSBInfo()
{
SBInfos.Add(new SBFactionBottle());
}
public override void InitOutfit()
{
base.InitOutfit();
AddItem(new Robe(Utility.RandomPinkHue()));
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class SBFactionBottle : SBInfo
{
public override IShopSellInfo SellInfo{ get; } = new InternalSellInfo();
public override List<GenericBuyInfo> BuyInfo{ get; } = new InternalBuyInfo();
public class InternalBuyInfo : List<GenericBuyInfo>
{
public InternalBuyInfo()
{
for (int i = 0; i < 5; ++i)
Add(new GenericBuyInfo(typeof(Bottle), 5, 20, 0xF0E, 0));
}
}
public class InternalSellInfo : GenericSellInfo
{
}
}
}

View file

@ -0,0 +1,78 @@
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
using Server.Network;
namespace Server.Factions
{
public class FactionHorseVendor : BaseFactionVendor
{
public FactionHorseVendor(Town town, Faction faction) : base(town, faction, "the Horse Breeder")
{
SetSkill(SkillName.AnimalLore, 64.0, 100.0);
SetSkill(SkillName.AnimalTaming, 90.0, 100.0);
SetSkill(SkillName.Veterinary, 65.0, 88.0);
}
public FactionHorseVendor(Serial serial) : base(serial)
{
}
public override VendorShoeType ShoeType => Female ? VendorShoeType.ThighBoots : VendorShoeType.Boots;
public override void InitSBInfo()
{
}
public override int GetShoeHue()
{
return 0;
}
public override void InitOutfit()
{
base.InitOutfit();
AddItem(Utility.RandomBool() ? new QuarterStaff() : (Item)new ShepherdsCrook());
}
public override void VendorBuy(Mobile from)
{
if (Faction == null || Faction.Find(from, true) != Faction)
PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1042201,
from.NetState); // You are not in my faction, I cannot sell you a horse!
else if (FactionGump.Exists(from))
from.SendLocalizedMessage(1042160); // You already have a faction menu open.
else if (from is PlayerMobile mobile)
mobile.SendGump(new HorseBreederGump(mobile, Faction));
}
public override void VendorSell(Mobile from)
{
}
public override bool OnBuyItems(Mobile buyer, List<BuyItemResponse> list)
{
return false;
}
public override bool OnSellItems(Mobile seller, List<SellItemResponse> list)
{
return false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,68 @@
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
namespace Server.Factions
{
public class FactionOreVendor : BaseFactionVendor
{
public FactionOreVendor(Town town, Faction faction) : base(town, faction, "the Ore Man")
{
// NOTE: Skills verified
SetSkill(SkillName.Carpentry, 85.0, 100.0);
SetSkill(SkillName.Lumberjacking, 60.0, 83.0);
}
public FactionOreVendor(Serial serial) : base(serial)
{
}
public override void InitSBInfo()
{
SBInfos.Add(new SBFactionOre());
}
public override void InitOutfit()
{
base.InitOutfit();
AddItem(new HalfApron());
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class SBFactionOre : SBInfo
{
private static readonly object[] m_FixedSizeArgs = { true };
public override IShopSellInfo SellInfo{ get; } = new InternalSellInfo();
public override List<GenericBuyInfo> BuyInfo{ get; } = new InternalBuyInfo();
public class InternalBuyInfo : List<GenericBuyInfo>
{
public InternalBuyInfo()
{
for (int i = 0; i < 5; ++i)
Add(new GenericBuyInfo(typeof(IronOre), 16, 20, 0x19B8, 0, m_FixedSizeArgs));
}
}
public class InternalSellInfo : GenericSellInfo
{
}
}
}

View file

@ -0,0 +1,81 @@
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
namespace Server.Factions
{
public class FactionReagentVendor : BaseFactionVendor
{
public FactionReagentVendor(Town town, Faction faction) : base(town, faction, "the Reagent Man")
{
SetSkill(SkillName.EvalInt, 65.0, 88.0);
SetSkill(SkillName.Inscribe, 60.0, 83.0);
SetSkill(SkillName.Magery, 64.0, 100.0);
SetSkill(SkillName.Meditation, 60.0, 83.0);
SetSkill(SkillName.MagicResist, 65.0, 88.0);
SetSkill(SkillName.Wrestling, 36.0, 68.0);
}
public FactionReagentVendor(Serial serial) : base(serial)
{
}
public override VendorShoeType ShoeType => Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals;
public override void InitSBInfo()
{
SBInfos.Add(new SBFactionReagent());
}
public override void InitOutfit()
{
base.InitOutfit();
AddItem(new Robe(Utility.RandomBlueHue()));
AddItem(new GnarledStaff());
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
public class SBFactionReagent : SBInfo
{
public override IShopSellInfo SellInfo{ get; } = new InternalSellInfo();
public override List<GenericBuyInfo> BuyInfo{ get; } = new InternalBuyInfo();
public class InternalBuyInfo : List<GenericBuyInfo>
{
public InternalBuyInfo()
{
for (int i = 0; i < 2; ++i)
{
Add(new GenericBuyInfo(typeof(BlackPearl), 5, 20, 0xF7A, 0));
Add(new GenericBuyInfo(typeof(Bloodmoss), 5, 20, 0xF7B, 0));
Add(new GenericBuyInfo(typeof(MandrakeRoot), 3, 20, 0xF86, 0));
Add(new GenericBuyInfo(typeof(Garlic), 3, 20, 0xF84, 0));
Add(new GenericBuyInfo(typeof(Ginseng), 3, 20, 0xF85, 0));
Add(new GenericBuyInfo(typeof(Nightshade), 3, 20, 0xF88, 0));
Add(new GenericBuyInfo(typeof(SpidersSilk), 3, 20, 0xF8D, 0));
Add(new GenericBuyInfo(typeof(SulfurousAsh), 3, 20, 0xF8C, 0));
}
}
}
public class InternalSellInfo : GenericSellInfo
{
}
}
}