Files
abysmal-isle/Scripts/Services/City Loyalty System/Election.cs
Unstable Kitsune b918192e4e Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
2023-11-28 23:20:26 -05:00

659 lines
21 KiB
C#

using System;
using Server;
using Server.Mobiles;
using Server.Items;
using System.Collections.Generic;
using System.Globalization;
using Server.Accounting;
using System.Linq;
using Server.Gumps;
namespace Server.Engines.CityLoyalty
{
[PropertyObject]
public class CityElection
{
public static readonly int VotePeriod = 7;
public static readonly int NominationDeadline = 24;
[CommandProperty(AccessLevel.GameMaster)]
public CityLoyaltySystem City { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public bool ElectionEnded { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public bool Ongoing { get { return CanNominate() || CanVote(); } }
[CommandProperty(AccessLevel.GameMaster)]
public DateTime AutoPickGovernor { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public string Time1
{
get { return StartTimes.Length > 0 ? StartTimes[0].ToShortDateString() : "Empty"; }
}
[CommandProperty(AccessLevel.GameMaster)]
public string Time2
{
get { return StartTimes.Length > 1 ? StartTimes[1].ToShortDateString() : "Empty"; }
}
[CommandProperty(AccessLevel.GameMaster)]
public string Time3
{
get { return StartTimes.Length > 2 ? StartTimes[2].ToShortDateString() : "Empty"; }
}
[CommandProperty(AccessLevel.GameMaster)]
public string Time4
{
get { return StartTimes.Length > 3 ? StartTimes[3].ToShortDateString() : "Empty"; }
}
public List<BallotEntry> Candidates { get; set; }
public DateTime[] StartTimes { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public bool ForceStart
{
get { return false; }
set
{
if (value)
{
if (Ongoing)
EndElection();
StartTimes = new DateTime[1];
StartTimes[0] = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
}
}
}
public override string ToString()
{
return "...";
}
public CityElection(CityLoyaltySystem city)
{
City = city;
ElectionEnded = true;
Candidates = new List<BallotEntry>();
GetDefaultStartTimes();
}
public bool TryNominate(PlayerMobile pm)
{
CityLoyaltyEntry pentry = City.GetPlayerEntry<CityLoyaltyEntry>(pm);
if (pm.Young)
pm.SendMessage("Young players cannot be nominated for the ballot!");
else if (!City.IsCitizen(pm) || pentry == null)
pm.SendLocalizedMessage(1153890); // You must be a citizen of this City to nominate yourself for the ballot!
else if (City.GetLoyaltyRating(pm) < LoyaltyRating.Adored)
pm.SendLocalizedMessage(1153891); // You must at least be adored within the City to nominate yourself for the ballot.
else
{
Account a = pm.Account as Account;
for (int i = 0; i < a.Length; i++)
{
Mobile m = a[i];
if(!(m is PlayerMobile))
continue;
BallotEntry ballot = Candidates.FirstOrDefault(entry => entry.Player == m);
if (ballot != null && m is PlayerMobile)
{
pm.SendLocalizedMessage(ballot.Endorsements.Count > 0 ? 1153917 : 1153889); // A character from this account is currently endorsed for Candidacy and cannot be nominated. // A character from this account has already been nominated to run for office.
return false; // A character from this account has already been nominated to run for office.
}
ballot = Candidates.FirstOrDefault(entry => entry.Endorsements.Contains(m));
if (ballot != null && m is PlayerMobile)
{
pm.SendLocalizedMessage(1153892); // A character from this account has already endorsed a nominee!
return false;
}
}
Candidates.Add(new BallotEntry(pm, pentry.Love, pentry.Hate));
pm.PrivateOverheadMessage(Network.MessageType.Regular, 0x3B2, 1153905, pm.NetState); // *You etch your name into the stone*
pm.SendLocalizedMessage(1154087); // You have 24 hours to get your nomination endorsed. If you do not get an endorsement within that period you will need to re-nominate yourself.
return true;
}
return false;
}
public bool TryEndorse(PlayerMobile pm, PlayerMobile nominee)
{
if (pm.Young)
pm.SendMessage("Young players cannot endorose an nominee!");
else if (!City.IsCitizen(pm))
pm.SendLocalizedMessage(1153893); // You must be a citizen of this City to endorse a nominee for the ballot!
else
{
Account a = pm.Account as Account;
for (int i = 0; i < a.Length; i++)
{
Mobile m = a[i];
if(!(m is PlayerMobile))
continue;
BallotEntry ballot = Candidates.FirstOrDefault(entry => entry.Endorsements.Contains(m as PlayerMobile));
if (m is PlayerMobile && ballot != null)
{
pm.SendLocalizedMessage(1153892); // A character from this account has already endorsed a nominee!
return false;
}
BallotEntry ballot2 = Candidates.FirstOrDefault(entry => entry.Player == m);
if (m is PlayerMobile && ballot2 != null)
{
pm.SendLocalizedMessage(1153912); // A character from this account is currently nominated for candidacy and cannot offer an endorsement.
return false;
}
}
BallotEntry pentry = Candidates.FirstOrDefault(e => e.Player == nominee);
if (pentry != null)
{
//<CENTER>Are you sure you wish to endorse this nominee? All endorsements are final and cannot be changed!</CENTER>
pm.SendGump(new ConfirmCallbackGump(pm, null, 1154091, pentry, null, (m, o) =>
{
BallotEntry e = o as BallotEntry;
e.Endorsements.Add(m as PlayerMobile);
m.PrivateOverheadMessage(Network.MessageType.Regular, 0x3B2, 1153913, m.NetState); // *You etch your endorsement for the nominee into the stone*
}));
return true;
}
}
return false;
}
public bool TryVote(PlayerMobile voter, PlayerMobile candidate)
{
if (!CanVote())
voter.SendLocalizedMessage(1153919); // The stone is not currently accepting votes.
else if (voter.Young)
voter.SendMessage("Young players cannot vote in a city election!");
else if (!City.IsCitizen(voter))
voter.SendLocalizedMessage(1153894); // You must be a citizen of this City to vote!
else if (City.GetLoyaltyRating(voter) < LoyaltyRating.Respected)
voter.SendLocalizedMessage(1154579); // You must be at least respected within the city to vote.
else
{
Account a = voter.Account as Account;
for (int i = 0; i < a.Length; i++)
{
Mobile m = a[i];
if (m is PlayerMobile && Candidates.FirstOrDefault(e => e.Votes.Contains(m)) != null)
{
voter.SendLocalizedMessage(1153922); // This account has already cast a vote in this election. You may only vote once.
return false;
}
}
BallotEntry pentry = Candidates.FirstOrDefault(e => e.Player == candidate);
if (pentry != null)
{
//<CENTER>Are you sure you wish to cast your vote for this candidate? All votes are final and cannot be changed!</CENTER>
voter.SendGump(new ConfirmCallbackGump(voter, null, 1153921, pentry, null, (m, o) =>
{
BallotEntry e = o as BallotEntry;
e.Votes.Add(voter);
m.PrivateOverheadMessage(Network.MessageType.Regular, 0x3B2, 1153923, voter.NetState); // *You etch your vote into the stone*
}));
}
}
return false;
}
public void TryWithdraw(PlayerMobile pm)
{
BallotEntry entry = Candidates.FirstOrDefault(e => e.Player == pm);
if (entry != null)
{
//Are you sure you wish to withdrawal?
pm.SendGump(new ConfirmCallbackGump(pm, null, 1153918, entry, null, (m, o) =>
{
BallotEntry e = (BallotEntry)o;
if (Candidates.Contains(e))
{
Candidates.Remove(e);
m.PrivateOverheadMessage(Network.MessageType.Regular, 0x3B2, 1153911, m.NetState); // *You smudge your name off the stone*
}
}));
}
else
pm.SendLocalizedMessage(1153924); // You are not currently on any ballot to withdraw from.
}
public double GetStanding(BallotEntry entry)
{
if (Candidates.Contains(entry))
{
if (entry.Votes.Count <= 0)
return 0.0;
return (entry.Votes.Count / GetTotalVotes()) * 100;
}
return 0.0;
}
public int GetTotalVotes()
{
int votes = 0;
Candidates.ForEach(c =>
{
votes += c.Votes.Count;
});
return votes;
}
public void OnTick()
{
foreach(DateTime dt in StartTimes)
{
if (dt.Year == DateTime.Now.Year && DateTime.Now.Month == dt.Month && DateTime.Now.Day > 14 && !ElectionEnded)
{
EndElection();
}
else if (dt.Year == DateTime.Now.Year && DateTime.Now.Month == dt.Month && DateTime.Now.Day == dt.Day && ElectionEnded)
{
StartNewElection();
}
}
if (City.Governor == null && AutoPickGovernor != DateTime.MinValue && DateTime.Now > AutoPickGovernor)
{
if (City.GovernorElect != null)
{
if (Candidates.Count > 0)
{
BallotEntry entry = Candidates.FirstOrDefault(c => c.Player == City.GovernorElect);
if (entry != null)
Candidates.Remove(entry);
}
City.GovernorElect = null;
}
if (Candidates.Count > 0)
{
Candidates.Sort();
City.GovernorElect = Candidates[0].Player;
}
AutoPickGovernor = DateTime.MinValue;
}
if (CanNominate())
{
Candidates.ForEach(entry =>
{
if (entry.TimeOfNomination + TimeSpan.FromHours(NominationDeadline) < DateTime.Now && entry.Endorsements.Count == 0)
Candidates.Remove(entry);
});
}
}
public void StartNewElection()
{
ElectionEnded = false;
Candidates.Clear();
Candidates.TrimExcess();
AutoPickGovernor = DateTime.MinValue;
}
private static int[] _Periods = { 3, 6, 9, 12 };
public void GetDefaultStartTimes()
{
StartTimes = new DateTime[4];
for(int i = 0; i < _Periods.Length; i++)
{
DateTime dt = new DateTime(DateTime.Now.Year, _Periods[i], 1);
if (dt < DateTime.Now)
dt = new DateTime(dt.Year + 1, dt.Month, dt.Day);
StartTimes[i] = dt;
}
}
public static DateTime[] ValidateStartTimes(Mobile m, int[] times)
{
if (times != null && times.Length > 0)
{
DateTime[] starttimes = new DateTime[times.Length];
for (int i = 0; i < times.Length; i++)
{
int month = times[i];
if (month <= 0 || month > 12)
{
m.SendMessage("Invalid month for #{0}. It must be between 0 and 12.", i + 1);
return null;
}
DateTime dt = new DateTime(DateTime.Now.Year, month, 1);
if (dt < DateTime.Now)
dt = new DateTime(dt.Year + 1, dt.Month, dt.Day);
starttimes[i] = dt;
}
if (CheckConflict(starttimes))
{
m.SendMessage("times are too close to another start time. Please try again.");
return null;
}
return starttimes;
}
return null;
}
public static bool CheckConflict(DateTime[] times)
{
if (times == null || times.Length == 0)
return false;
for(int i = 0; i < times.Length; i++)
{
DateTime t = times[i];
for (int j = 0; j < times.Length; j++)
{
if (i == j)
continue;
if ((times[j] > t && times[j] - t < TimeSpan.FromDays(30)) || times[j] < t && t - times[j] < TimeSpan.FromDays(30))
return true;
}
}
return false;
}
public DateTime NextElection()
{
if (CanNominate() || CanVote())
return DateTime.MinValue;
DateTime closest = DateTime.MinValue;
foreach (DateTime dt in StartTimes)
{
if (DateTime.Now < dt)
{
if (closest == DateTime.MinValue || dt - DateTime.Now < closest - DateTime.Now)
{
closest = dt;
}
}
}
return closest;
}
public bool CanNominate()
{
DateTime until = DateTime.MinValue;
return CanNominate(out until);
}
public bool CanNominate(out DateTime until)
{
until = DateTime.Now;
foreach (DateTime dt in StartTimes)
{
if (dt.Year == DateTime.Now.Year && DateTime.Now.Month == dt.Month && DateTime.Now <= dt.AddDays(VotePeriod))
{
until = dt.AddDays(VotePeriod);
return true;
}
}
return false;
}
public bool CanVote()
{
DateTime until = DateTime.MinValue;
return CanVote(out until);
}
public bool CanVote(out DateTime until)
{
until = DateTime.Now;
foreach (DateTime dt in StartTimes)
{
if (dt.Year == DateTime.Now.Year && DateTime.Now.Month == dt.Month && DateTime.Now > dt.AddDays(VotePeriod) && DateTime.Now <= dt.AddDays(VotePeriod * 2))
{
until = dt.AddDays(VotePeriod * 2);
return true;
}
}
return false;
}
public void EndElection()
{
ElectionEnded = true;
if (Candidates.Count > 0)
{
Candidates.Sort();
City.GovernorElect = Candidates[0].Player;
AutoPickGovernor = DateTime.Now + TimeSpan.FromDays(7);
}
if (City.GovernorElect == null)
{
City.PendingGovernor = true;
AutoPickGovernor = DateTime.Now + TimeSpan.FromDays(Utility.RandomMinMax(2, 4));
if (City.Stone != null)
City.Stone.InvalidateProperties();
}
for(int i = 0; i < StartTimes.Length; i++)
{
DateTime dt = StartTimes[i];
if (dt == DateTime.MinValue)
continue;
if (dt < DateTime.Now)
dt = new DateTime(dt.Year + 1, dt.Month, dt.Day);
}
}
public void OnRejectOffice(PlayerMobile pm)
{
BallotEntry entry = Candidates.FirstOrDefault(c => c.Player == pm);
if (entry != null)
Candidates.Remove(entry);
AutoPickGovernor = DateTime.Now + TimeSpan.FromDays(Utility.RandomMinMax(2, 4));
}
public void Serialize(GenericWriter writer)
{
writer.Write(0);
writer.Write(ElectionEnded);
writer.Write(AutoPickGovernor);
writer.Write(StartTimes.Length);
foreach (DateTime dt in StartTimes)
writer.Write(dt);
writer.Write(Candidates.Count);
Candidates.ForEach(entry => entry.Serialize(writer));
}
public CityElection(CityLoyaltySystem city, GenericReader reader)
{
City = city;
Candidates = new List<BallotEntry>();
int version = reader.ReadInt();
ElectionEnded = reader.ReadBool();
AutoPickGovernor = reader.ReadDateTime();
int c = reader.ReadInt();
StartTimes = new DateTime[c];
for (int i = 0; i < c; i++)
{
DateTime time = reader.ReadDateTime();
if (time < DateTime.Now && ElectionEnded)
time = new DateTime(time.Year + 1, time.Month, time.Day);
StartTimes[i] = time;
}
c = reader.ReadInt();
for (int i = 0; i < c; i++)
{
BallotEntry entry = new BallotEntry(reader);
if (entry.Player != null)
Candidates.Add(entry);
}
}
}
public class BallotEntry : IComparable<BallotEntry>
{
public PlayerMobile Player { get; set; }
public DateTime TimeOfNomination { get; set; }
public int Love { get; set; }
public int Hate { get; set; }
public List<PlayerMobile> Endorsements { get; set; }
public List<PlayerMobile> Votes { get; set; }
public BallotEntry(PlayerMobile pm, int love, int hate)
{
Player = pm;
TimeOfNomination = DateTime.Now;
Love = love;
Hate = hate;
Endorsements = new List<PlayerMobile>();
Votes = new List<PlayerMobile>();
}
public int CompareTo(BallotEntry entry)
{
if (Player == null)
return -1;
if (Votes.Count > entry.Votes.Count)
return 1;
else if (Votes.Count < entry.Votes.Count)
return -1;
else
{
if (Love > entry.Love || Hate < entry.Hate)
return 1;
else if (Love == entry.Love && Hate == entry.Hate && Utility.RandomBool())
return 1;
else
return -1;
}
}
public void Serialize(GenericWriter writer)
{
writer.Write(0);
writer.Write(Player);
writer.Write(TimeOfNomination);
writer.Write(Love);
writer.Write(Hate);
writer.Write(Endorsements.Count);
Endorsements.ForEach(p => writer.Write(p));
writer.Write(Votes.Count);
Votes.ForEach(p => writer.Write(p));
}
public BallotEntry(GenericReader reader)
{
Endorsements = new List<PlayerMobile>();
Votes = new List<PlayerMobile>();
int version = reader.ReadInt();
Player = reader.ReadMobile() as PlayerMobile;
TimeOfNomination = reader.ReadDateTime();
Love = reader.ReadInt();
Hate = reader.ReadInt();
int c = reader.ReadInt();
for (int i = 0; i < c; i++)
{
PlayerMobile p = reader.ReadMobile() as PlayerMobile;
if (p != null)
Endorsements.Add(p);
}
c = reader.ReadInt();
for (int i = 0; i < c; i++)
{
PlayerMobile p = reader.ReadMobile() as PlayerMobile;
if (p != null)
Votes.Add(p);
}
}
}
}