ModernUO/Scripts/Engines/Factions/Core/Election.cs

536 lines
12 KiB
C#

using System;
using System.Net;
using Server.Mobiles;
using System.Collections.Generic;
namespace Server.Factions
{
public class Election
{
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 );
public const int MaxCandidates = 10;
public const int CandidateRank = 5;
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;
}
}
private Timer m_Timer;
public void StartTimer()
{
m_Timer = Timer.DelayCall( TimeSpan.FromMinutes( 1.0 ), TimeSpan.FromMinutes( 1.0 ), Slice );
}
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 void Serialize( GenericWriter writer )
{
writer.WriteEncodedInt( (int) 0 ); // version
Faction.WriteReference( writer, Faction );
writer.Write( (DateTime) 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 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 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 void Serialize( GenericWriter writer )
{
writer.WriteEncodedInt( (int) 0 );
writer.Write( (Mobile) From );
writer.Write( (IPAddress) Address );
writer.Write( (DateTime) Time );
}
}
public class Candidate
{
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 = (Voter)Voters[i];
if ( (int)voter.AcquireFields()[3] < 90 )
Voters.RemoveAt( i-- );
}
}
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 void Serialize( GenericWriter writer )
{
writer.WriteEncodedInt( (int) 1 ); // version
writer.Write( (Mobile) Mobile );
writer.WriteEncodedInt( (int) Voters.Count );
for ( int i = 0; i < Voters.Count; ++i )
((Voter)Voters[i]).Serialize( writer );
}
}
public enum ElectionState
{
Pending,
Campaign,
Election
}
}