using System; using System.Net; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using Server; using Server.Gumps; using Server.Network; using Server.Prompts; namespace Server.Misc { public class ShardPoller : Item { private string m_Title; private ShardPollOption[] m_Options; private IPAddress[] m_Addresses; private TimeSpan m_Duration; private DateTime m_StartTime; private bool m_Active; public ShardPollOption[] Options { get{ return m_Options; } set{ m_Options = value; } } public IPAddress[] Addresses { get{ return m_Addresses; } set{ m_Addresses = value; } } [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] public string Title { get{ return m_Title; } set{ m_Title = ShardPollPrompt.UrlToHref( value ); } } [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] public TimeSpan Duration { get{ return m_Duration; } set{ m_Duration = value; } } [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] public DateTime StartTime { get{ return m_StartTime; } set{ m_StartTime = value; } } [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] public TimeSpan TimeRemaining { get { if ( m_StartTime == DateTime.MinValue || !m_Active ) return TimeSpan.Zero; try { TimeSpan ts = (m_StartTime + m_Duration) - DateTime.Now; if ( ts < TimeSpan.Zero ) return TimeSpan.Zero; return ts; } catch { return TimeSpan.Zero; } } } [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] public bool Active { get{ return m_Active; } set { if ( m_Active == value ) return; m_Active = value; if ( m_Active ) { m_StartTime = DateTime.Now; m_ActivePollers.Add( this ); } else { m_ActivePollers.Remove( this ); } } } public bool HasAlreadyVoted( NetState ns ) { for ( int i = 0; i < m_Options.Length; ++i ) { if ( m_Options[i].HasAlreadyVoted( ns ) ) return true; } return false; } public void AddVote( NetState ns, ShardPollOption option ) { option.AddVote( ns ); } public void RemoveOption( ShardPollOption option ) { int index = Array.IndexOf( m_Options, option ); if ( index < 0 ) return; ShardPollOption[] old = m_Options; m_Options = new ShardPollOption[old.Length - 1]; for ( int i = 0; i < index; ++i ) m_Options[i] = old[i]; for ( int i = index; i < m_Options.Length; ++i ) m_Options[i] = old[i + 1]; } public void AddOption( ShardPollOption option ) { ShardPollOption[] old = m_Options; m_Options = new ShardPollOption[old.Length + 1]; for ( int i = 0; i < old.Length; ++i ) m_Options[i] = old[i]; m_Options[old.Length] = option; } public override string DefaultName { get { return "shard poller"; } } [Constructable( AccessLevel.Administrator )] public ShardPoller() : base( 0x1047 ) { m_Duration = TimeSpan.FromHours( 24.0 ); m_Options = new ShardPollOption[0]; m_Addresses = new IPAddress[0]; Movable = false; } public static void Initialize() { EventSink.Login += new LoginEventHandler( EventSink_Login ); } private static List m_ActivePollers = new List(); private static void EventSink_Login( LoginEventArgs e ) { if ( m_ActivePollers.Count == 0 ) return; Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( EventSink_Login_Callback ), e.Mobile ); } private static void EventSink_Login_Callback( object state ) { Mobile from = (Mobile)state; NetState ns = from.NetState; if ( ns == null ) return; ShardPollGump spg = null; for ( int i = 0; i < m_ActivePollers.Count; ++i ) { ShardPoller poller = m_ActivePollers[i]; if ( poller.Deleted || !poller.Active ) continue; if ( poller.TimeRemaining > TimeSpan.Zero ) { if ( poller.HasAlreadyVoted( ns ) ) continue; if ( spg == null ) { spg = new ShardPollGump( from, poller, false, null ); from.SendGump( spg ); } else { spg.QueuePoll( poller ); } } else { poller.Active = false; } } } public void SendQueuedPoll_Callback( object state ) { object[] states = (object[])state; Mobile from = (Mobile)states[0]; Queue queue = (Queue)states[1]; from.SendGump( new ShardPollGump( from, this, false, queue ) ); } public override void OnDoubleClick( Mobile from ) { if ( from.AccessLevel >= AccessLevel.Administrator ) from.SendGump( new ShardPollGump( from, this, true, null ) ); } public ShardPoller( Serial serial ) : base( serial ) { } public override void Serialize( GenericWriter writer ) { base.Serialize( writer ); writer.Write( (int) 0 ); // version writer.Write( m_Title ); writer.Write( m_Duration ); writer.Write( m_StartTime ); writer.Write( m_Active ); writer.Write( m_Options.Length ); for ( int i = 0; i < m_Options.Length; ++i ) m_Options[i].Serialize( writer ); } public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); int version = reader.ReadInt(); switch ( version ) { case 0: { m_Title = reader.ReadString(); m_Duration = reader.ReadTimeSpan(); m_StartTime = reader.ReadDateTime(); m_Active = reader.ReadBool(); m_Options = new ShardPollOption[reader.ReadInt()]; for ( int i = 0; i < m_Options.Length; ++i ) m_Options[i] = new ShardPollOption( reader ); if ( m_Active ) m_ActivePollers.Add( this ); break; } } } public override void OnDelete() { base.OnDelete(); Active = false; } } public class ShardPollOption { private string m_Title; private int m_LineBreaks; private IPAddress[] m_Voters; public string Title{ get{ return m_Title; } set{ m_Title = value; m_LineBreaks = GetBreaks( m_Title ); } } public int LineBreaks{ get{ return m_LineBreaks; } } public int Votes{ get{ return m_Voters.Length; } } public IPAddress[] Voters{ get{ return m_Voters; } set{ m_Voters = value; } } public ShardPollOption( string title ) { m_Title = title; m_LineBreaks = GetBreaks( m_Title ); m_Voters = new IPAddress[0]; } public bool HasAlreadyVoted( NetState ns ) { if ( ns == null ) return false; IPAddress ipAddress = ns.Address; for ( int i = 0; i < m_Voters.Length; ++i ) { if ( Utility.IPMatchClassC( m_Voters[i], ipAddress ) ) return true; } return false; } public void AddVote( NetState ns ) { if ( ns == null ) return; IPAddress[] old = m_Voters; m_Voters = new IPAddress[old.Length + 1]; for ( int i = 0; i < old.Length; ++i ) m_Voters[i] = old[i]; m_Voters[old.Length] = ns.Address; } public int ComputeHeight() { int height = m_LineBreaks * 18; if ( height > 30 ) return height; return 30; } public int GetBreaks( string title ) { if ( title == null ) return 1; int count = 0; int index = -1; do { ++count; index = title.IndexOf( "
", index + 1 ); } while ( index >= 0 ); return count; } public ShardPollOption( GenericReader reader ) { int version = reader.ReadInt(); switch ( version ) { case 0: { m_Title = reader.ReadString(); m_LineBreaks = GetBreaks( m_Title ); m_Voters = new IPAddress[reader.ReadInt()]; for ( int i = 0; i < m_Voters.Length; ++i ) m_Voters[i] = Utility.Intern( reader.ReadIPAddress() ); break; } } } public void Serialize( GenericWriter writer ) { writer.Write( (int) 0 ); // version writer.Write( m_Title ); writer.Write( m_Voters.Length ); for ( int i = 0; i < m_Voters.Length; ++i ) writer.Write( m_Voters[i] ); } } public class ShardPollGump : Gump { private Mobile m_From; private ShardPoller m_Poller; private bool m_Editing; private Queue m_Polls; public bool Editing{ get{ return m_Editing; } } public void QueuePoll( ShardPoller poller ) { if ( m_Polls == null ) m_Polls = new Queue( 4 ); m_Polls.Enqueue( poller ); } public string Center( string text ) { return String.Format( "
{0}
", text ); } public string Color( string text, int color ) { return String.Format( "{1}", color, text ); } private const int LabelColor32 = 0xFFFFFF; public ShardPollGump( Mobile from, ShardPoller poller, bool editing, Queue polls ) : base( 50, 50 ) { m_From = from; m_Poller = poller; m_Editing = editing; m_Polls = polls; Closable = false; AddPage( 0 ); int totalVotes = 0; int totalOptionHeight = 0; for ( int i = 0; i < poller.Options.Length; ++i ) { totalVotes += poller.Options[i].Votes; totalOptionHeight += poller.Options[i].ComputeHeight() + 5; } bool isViewingResults = editing && poller.Active; bool isCompleted = totalVotes > 0 && !poller.Active; if ( editing && !isViewingResults ) totalOptionHeight += 35; int height = 115 + totalOptionHeight; AddBackground( 1, 1, 398, height - 2, 3600 ); AddAlphaRegion( 16, 15, 369, height - 31 ); AddItem( 308, 30, 0x1E5E ); string title; if ( editing ) title = ( isCompleted ? "Poll Completed" : "Poll Editor" ); else title = "Shard Poll"; AddHtml( 22, 22, 294, 20, Color( Center( title ), LabelColor32 ), false, false ); if ( editing ) { AddHtml( 22, 22, 294, 20, Color( String.Format( "{0} total", totalVotes ), LabelColor32 ), false, false ); AddButton( 287, 23, 0x2622, 0x2623, 2, GumpButtonType.Reply, 0 ); } AddHtml( 22, 50, 294, 40, Color( poller.Title, 0x99CC66 ), false, false ); AddImageTiled( 32, 88, 264, 1, 9107 ); AddImageTiled( 42, 90, 264, 1, 9157 ); int y = 100; for ( int i = 0; i < poller.Options.Length; ++i ) { ShardPollOption option = poller.Options[i]; string text = option.Title; if ( editing && totalVotes > 0 ) { double perc = option.Votes / (double)totalVotes; text = String.Format( "[{1}: {2}%] {0}", text, option.Votes, (int)(perc*100) ); } int optHeight = option.ComputeHeight(); y += optHeight/2; if ( isViewingResults ) AddImage( 24, y - 15, 0x25FE ); else AddRadio( 24, y - 15, 0x25F9, 0x25FC, false, 1 + i ); AddHtml( 60, y - (9 * option.LineBreaks), 250, 18 * option.LineBreaks, Color( text, LabelColor32 ), false, false ); y += optHeight/2; y += 5; } if ( editing && !isViewingResults ) { AddRadio( 24, y + 15 - 15, 0x25F9, 0x25FC, false, 1 + poller.Options.Length ); AddHtml( 60, y + 15 - 9, 250, 18, Color( "Create new option.", 0x99CC66 ), false, false ); } AddButton( 314, height - 73, 247, 248, 1, GumpButtonType.Reply, 0 ); AddButton( 314, height - 47, 242, 241, 0, GumpButtonType.Reply, 0 ); } public override void OnResponse( NetState sender, RelayInfo info ) { if ( m_Polls != null && m_Polls.Count > 0 ) { ShardPoller poller = m_Polls.Dequeue(); if ( poller != null ) Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( poller.SendQueuedPoll_Callback ), new object[]{ m_From, m_Polls } ); } if ( info.ButtonID == 1 ) { int[] switches = info.Switches; if ( switches.Length == 0 ) return; int switched = switches[0] - 1; ShardPollOption opt = null; if ( switched >= 0 && switched < m_Poller.Options.Length ) opt = m_Poller.Options[switched]; if ( opt == null && !m_Editing ) return; if ( m_Editing ) { if ( !m_Poller.Active ) { m_From.SendMessage( "Enter a title for the option. Escape to cancel.{0}", opt == null ? "" : " Use \"DEL\" to delete." ); m_From.Prompt = new ShardPollPrompt( m_Poller, opt ); } else { m_From.SendMessage( "You may not edit an active poll. Deactivate it first." ); m_From.SendGump( new ShardPollGump( m_From, m_Poller, m_Editing, m_Polls ) ); } } else { if ( !m_Poller.Active ) m_From.SendMessage( "The poll has been deactivated." ); else if ( m_Poller.HasAlreadyVoted( sender ) ) m_From.SendMessage( "You have already voted on this poll." ); else m_Poller.AddVote( sender, opt ); } } else if ( info.ButtonID == 2 && m_Editing ) { m_From.SendGump( new ShardPollGump( m_From, m_Poller, m_Editing, m_Polls ) ); m_From.SendGump( new PropertiesGump( m_From, m_Poller ) ); } } } public class ShardPollPrompt : Prompt { private ShardPoller m_Poller; private ShardPollOption m_Option; public ShardPollPrompt( ShardPoller poller, ShardPollOption opt ) { m_Poller = poller; m_Option = opt; } public override void OnCancel( Mobile from ) { from.SendGump( new ShardPollGump( from, m_Poller, true, null ) ); } private static Regex m_UrlRegex = new Regex( @"\[url(?:=(.*?))?\](.*?)\[/url\]", RegexOptions.IgnoreCase | RegexOptions.Compiled ); private static string UrlRegex_Match( Match m ) { if ( m.Groups[1].Success ) { if ( m.Groups[2].Success ) return String.Format( "{1}", m.Groups[1].Value, m.Groups[2].Value ); } else if ( m.Groups[2].Success ) { return String.Format( "{0}", m.Groups[2].Value ); } return m.Value; } public static string UrlToHref( string text ) { if ( text == null ) return null; return m_UrlRegex.Replace( text, new MatchEvaluator( UrlRegex_Match ) ); } public override void OnResponse( Mobile from, string text ) { if ( m_Poller.Active ) { from.SendMessage( "You may not edit an active poll. Deactivate it first." ); } else if ( text == "DEL" ) { if ( m_Option != null ) m_Poller.RemoveOption( m_Option ); } else { text = UrlToHref( text ); if ( m_Option == null ) m_Poller.AddOption( new ShardPollOption( text ) ); else m_Option.Title = text; } from.SendGump( new ShardPollGump( from, m_Poller, true, null ) ); } } }