diff --git a/Scripts/Items/Special/House Raffle/HouseRaffleManagementGump.cs b/Scripts/Items/Special/House Raffle/HouseRaffleManagementGump.cs
new file mode 100644
index 000000000..14e85080c
--- /dev/null
+++ b/Scripts/Items/Special/House Raffle/HouseRaffleManagementGump.cs
@@ -0,0 +1,138 @@
+using System;
+using Server;
+using Server.Accounting;
+using Server.Gumps;
+using Server.Items;
+using Server.Mobiles;
+using Server.Network;
+
+namespace Server.Gumps
+{
+ public class HouseRaffleManagementGump : Gump
+ {
+ public string Right( string text )
+ {
+ return String.Format( "
{0}
", text );
+ }
+
+ public string Center( string text )
+ {
+ return String.Format( "{0}", text );
+ }
+
+ public string Color( string text, int color )
+ {
+ return String.Format( "{1}", color, text );
+ }
+
+ public const int LabelColor = 0xFFFFFF;
+
+ private HouseRaffleStone m_Stone;
+ private int m_Page;
+
+ public override void OnResponse( NetState sender, RelayInfo info )
+ {
+ Mobile from = sender.Mobile;
+ int buttonId = info.ButtonID;
+
+ if ( buttonId == 2 && m_Page > 0 )
+ {
+ from.SendGump( new HouseRaffleManagementGump( m_Stone, m_Page - 1 ) );
+ }
+ else if ( buttonId == 3 && (m_Page + 1) * 10 < m_Stone.Entries.Count )
+ {
+ from.SendGump( new HouseRaffleManagementGump( m_Stone, m_Page + 1 ) );
+ }
+ else
+ {
+ buttonId -= 4;
+
+ if ( buttonId >= 0 && buttonId < m_Stone.Entries.Count )
+ {
+ m_Stone.Entries.RemoveAt( buttonId );
+ from.SendGump( new HouseRaffleManagementGump( m_Stone, m_Page ) );
+ }
+ }
+ }
+
+ public HouseRaffleManagementGump( HouseRaffleStone stone )
+ : this( stone, 0 )
+ {
+ }
+
+ public HouseRaffleManagementGump( HouseRaffleStone stone, int page ) : base( 40, 40 )
+ {
+ m_Stone = stone;
+ m_Page = page;
+
+ AddPage( 0 );
+
+ AddBackground( 0, 0, 618, 354, 9270 );
+ AddAlphaRegion( 10, 10, 598, 334 );
+
+ AddHtml( 10, 10, 598, 20, Color( Center( "Raffle Management" ), LabelColor ), false, false );
+
+ AddHtml( 45, 35, 100, 20, Color( "Location:", LabelColor ), false, false );
+ AddHtml( 145, 35, 250, 20, Color( m_Stone.FormatLocation(), LabelColor ), false, false );
+
+ AddHtml( 45, 55, 100, 20, Color( "Ticket Price:", LabelColor ), false, false );
+ AddHtml( 145, 55, 250, 20, Color( m_Stone.FormatPrice(), LabelColor ), false, false );
+
+ AddHtml( 45, 75, 100, 20, Color( "Total Entries:", LabelColor ), false, false );
+ AddHtml( 145, 75, 250, 20, Color( m_Stone.Entries.Count.ToString(), LabelColor ), false, false );
+
+ AddImageTiled( 13, 99, 592, 242, 9264 );
+ AddImageTiled( 14, 100, 590, 240, 9274 );
+ AddAlphaRegion( 14, 100, 590, 240 );
+
+ AddHtml( 14, 100, 590, 20, Color( Center( "Entries" ), LabelColor ), false, false );
+
+ if ( page > 0 )
+ AddButton( 567, 104, 0x15E3, 0x15E7, 2, GumpButtonType.Reply, 0 );
+ else
+ AddImage( 567, 104, 0x25EA );
+
+ if ( (page + 1) * 10 < m_Stone.Entries.Count )
+ AddButton( 584, 104, 0x15E1, 0x15E5, 3, GumpButtonType.Reply, 0 );
+ else
+ AddImage( 584, 104, 0x25E6 );
+
+ AddHtml( 14, 120, 30, 20, Color( Center( "DEL" ), LabelColor ), false, false );
+ AddHtml( 47, 120, 250, 20, Color( "Name", LabelColor ), false, false );
+ AddHtml( 295, 120, 100, 20, Color( Center( "Address" ), LabelColor ), false, false );
+ AddHtml( 395, 120, 150, 20, Color( Center( "Date" ), LabelColor ), false, false );
+ AddHtml( 545, 120, 60, 20, Color( Center( "Num" ), LabelColor ), false, false );
+
+ int idx = 0;
+
+ for ( int i = page * 10; i >= 0 && i < m_Stone.Entries.Count && i < (page + 1) * 10; ++i, ++idx )
+ {
+ RaffleEntry entry = m_Stone.Entries[i];
+
+ AddButton( 13, 138 + (idx * 20), 4002, 4004, 4 + i, GumpButtonType.Reply, 0 );
+
+ int x = 45;
+
+ string name;
+ Account acc = entry.From.Account as Account;
+
+ if ( acc != null )
+ name = String.Format( "{0} ({1})", entry.From.Name, acc );
+ else
+ name = entry.From.Name;
+
+ AddHtml( x + 2, 140 + (idx * 20), 250, 20, Color( name, LabelColor ), false, false );
+ x += 250;
+
+ AddHtml( x, 140 + (idx * 20), 100, 20, Color( Center( entry.Address.ToString() ), LabelColor ), false, false );
+ x += 100;
+
+ AddHtml( x, 140 + (idx * 20), 150, 20, Color( Center( entry.Date.ToString() ), LabelColor ), false, false );
+ x += 150;
+
+ AddHtml( x, 140 + (idx * 20), 60, 20, Color( Center( "1" ), LabelColor ), false, false );
+ x += 60;
+ }
+ }
+ }
+}
diff --git a/Scripts/Items/Special/House Raffle/HouseRaffleRegion.cs b/Scripts/Items/Special/House Raffle/HouseRaffleRegion.cs
new file mode 100644
index 000000000..355887405
--- /dev/null
+++ b/Scripts/Items/Special/House Raffle/HouseRaffleRegion.cs
@@ -0,0 +1,69 @@
+using System;
+using Server;
+using Server.Accounting;
+using Server.Items;
+using Server.Spells.Sixth;
+using Server.Targeting;
+
+namespace Server.Regions
+{
+ public class HouseRaffleRegion : BaseRegion
+ {
+ private HouseRaffleStone m_Stone;
+
+ public HouseRaffleRegion( HouseRaffleStone stone )
+ : base( null, stone.PlotFacet, Region.DefaultPriority, stone.PlotBounds )
+ {
+ m_Stone = stone;
+ }
+
+ public bool CheckAccount( Mobile mobCheck, Mobile accCheck )
+ {
+ if ( accCheck != null )
+ {
+ Account a = accCheck.Account as Account;
+
+ if ( a != null )
+ {
+ for ( int i = 0; i < a.Length; ++i )
+ {
+ if ( a[i] == mobCheck )
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public override bool AllowHousing( Mobile from, Point3D p )
+ {
+ if ( m_Stone == null || m_Stone.Winner == null )
+ return false;
+
+ return ( from == m_Stone.Winner || CheckAccount( from, m_Stone.Winner ) );
+ }
+
+ public override bool OnTarget( Mobile m, Target t, object o )
+ {
+ if ( m.Spell != null && m.Spell is MarkSpell && m.AccessLevel == AccessLevel.Player )
+ {
+ m.SendLocalizedMessage( 501800 ); // You cannot mark an object at that location.
+ return false;
+ }
+
+ return base.OnTarget( m, t, o );
+ }
+
+ public override bool OnBeginSpellCast( Mobile m, ISpell s )
+ {
+ if ( s is MarkSpell && m.AccessLevel == AccessLevel.Player )
+ {
+ m.SendLocalizedMessage( 501800 ); // You cannot mark an object at that location.
+ return false;
+ }
+
+ return base.OnBeginSpellCast( m, s );
+ }
+ }
+}
diff --git a/Scripts/Items/Special/House Raffle/HouseRaffleStone.cs b/Scripts/Items/Special/House Raffle/HouseRaffleStone.cs
new file mode 100644
index 000000000..b83788484
--- /dev/null
+++ b/Scripts/Items/Special/House Raffle/HouseRaffleStone.cs
@@ -0,0 +1,577 @@
+using System;
+using System.Net;
+using System.Collections.Generic;
+using Server;
+using Server.Accounting;
+using Server.ContextMenus;
+using Server.Gumps;
+using Server.Mobiles;
+using Server.Network;
+using Server.Regions;
+
+namespace Server.Items
+{
+ public class RaffleEntry
+ {
+ private Mobile m_From;
+ private IPAddress m_Address;
+ private DateTime m_Date;
+
+ public Mobile From
+ {
+ get { return m_From; }
+ }
+
+ public IPAddress Address
+ {
+ get { return m_Address; }
+ }
+
+ public DateTime Date
+ {
+ get { return m_Date; }
+ }
+
+ public RaffleEntry( Mobile from )
+ {
+ m_From = from;
+
+ if ( m_From.NetState != null )
+ m_Address = m_From.NetState.Address;
+ else
+ m_Address = IPAddress.None;
+
+ m_Date = DateTime.Now;
+ }
+
+ public void Serialize( GenericWriter writer )
+ {
+ writer.Write( m_From );
+ writer.Write( m_Address );
+ writer.Write( m_Date );
+ }
+
+ public RaffleEntry( GenericReader reader, int version )
+ {
+ switch ( version )
+ {
+ case 0:
+ {
+ m_From = reader.ReadMobile();
+ m_Address = Utility.Intern( reader.ReadIPAddress() );
+ m_Date = reader.ReadDateTime();
+
+ break;
+ }
+ }
+ }
+ }
+
+ public enum HouseRaffleState
+ {
+ Inactive,
+ Active,
+ Completed
+ }
+
+ [FlipableAttribute( 0xEDD, 0xEDE )]
+ public class HouseRaffleStone : Item
+ {
+ private const int EntryLimitPerIP = 4;
+ private const int DefaultTicketPrice = 5000;
+ private const int MessageHue = 1153;
+
+ private HouseRaffleRegion m_Region;
+ private Rectangle2D m_Bounds;
+ private Map m_Facet;
+
+ private Mobile m_Winner;
+
+ private bool m_Active;
+ private DateTime m_Started;
+ private TimeSpan m_Duration;
+ private int m_TicketPrice;
+
+ private List m_Entries;
+
+ [CommandProperty( AccessLevel.GameMaster )]
+ public HouseRaffleState CurrentState
+ {
+ get
+ {
+ if ( !m_Active && m_Winner != null )
+ return HouseRaffleState.Completed;
+ else if ( m_Active )
+ return HouseRaffleState.Active;
+ else
+ return HouseRaffleState.Inactive;
+ }
+ }
+
+ public bool Active
+ {
+ get { return m_Active; }
+ set
+ {
+ if ( m_Active != value )
+ {
+ if ( value )
+ {
+ m_Entries.Clear();
+ m_Winner = null;
+ m_Started = DateTime.Now;
+ }
+
+ m_Active = value;
+ InvalidateProperties();
+ }
+ }
+ }
+
+ [CommandProperty( AccessLevel.GameMaster, AccessLevel.Seer )]
+ public Rectangle2D PlotBounds
+ {
+ get { return m_Bounds; }
+ set
+ {
+ m_Bounds = value;
+
+ InvalidateRegion();
+ InvalidateProperties();
+ }
+ }
+
+ [CommandProperty( AccessLevel.GameMaster, AccessLevel.Seer )]
+ public Map PlotFacet
+ {
+ get { return m_Facet; }
+ set
+ {
+ m_Facet = value;
+
+ InvalidateRegion();
+ InvalidateProperties();
+ }
+ }
+
+ [CommandProperty( AccessLevel.GameMaster, AccessLevel.Seer )]
+ public Mobile Winner
+ {
+ get { return m_Winner; }
+ set { m_Winner = value; InvalidateProperties(); }
+ }
+
+ [CommandProperty( AccessLevel.GameMaster, AccessLevel.Seer )]
+ public DateTime Started
+ {
+ get { return m_Started; }
+ set { m_Started = value; InvalidateProperties(); }
+ }
+
+ [CommandProperty( AccessLevel.GameMaster, AccessLevel.Seer )]
+ public TimeSpan Duration
+ {
+ get { return m_Duration; }
+ set { m_Duration = value; InvalidateProperties(); }
+ }
+
+ [CommandProperty( AccessLevel.GameMaster, AccessLevel.Seer )]
+ public int TicketPrice
+ {
+ get { return m_TicketPrice; }
+ set
+ {
+ m_TicketPrice = Math.Max( 0, value );
+ InvalidateProperties();
+ }
+ }
+
+ public List Entries
+ {
+ get { return m_Entries; }
+ }
+
+ public override string DefaultName
+ {
+ get { return "a house raffle stone"; }
+ }
+
+ public override bool DisplayWeight
+ {
+ get { return false; }
+ }
+
+ private static List m_AllStones = new List();
+
+ public static void CheckEnd_OnTick()
+ {
+ for ( int i = 0; i < m_AllStones.Count; i++ )
+ m_AllStones[i].CheckEnd();
+ }
+
+ public static void Initialize()
+ {
+ Timer.DelayCall( TimeSpan.FromMinutes( 1.0 ), TimeSpan.FromMinutes( 1.0 ), new TimerCallback( CheckEnd_OnTick ) );
+ }
+
+ [Constructable]
+ public HouseRaffleStone()
+ : base( 0xEDD )
+ {
+ m_Region = null;
+ m_Bounds = new Rectangle2D();
+ m_Facet = null;
+
+ m_Winner = null;
+
+ m_Active = false;
+ m_Started = DateTime.MinValue;
+ m_Duration = TimeSpan.Zero;
+ m_TicketPrice = DefaultTicketPrice;
+
+ m_Entries = new List();
+
+ Movable = false;
+
+ m_AllStones.Add( this );
+ }
+
+ public HouseRaffleStone( Serial serial )
+ : base( serial )
+ {
+ }
+
+ public bool ValidLocation()
+ {
+ return ( m_Bounds.Start != Point2D.Zero && m_Bounds.End != Point2D.Zero && m_Facet != null && m_Facet != Map.Internal );
+ }
+
+ private void InvalidateRegion()
+ {
+ if ( m_Region != null )
+ {
+ m_Region.Unregister();
+ m_Region = null;
+ }
+
+ if ( ValidLocation() )
+ {
+ m_Region = new HouseRaffleRegion( this );
+ m_Region.Register();
+ }
+ }
+
+ private bool HasEntered( Mobile from )
+ {
+ Account acc = from.Account as Account;
+
+ if ( acc == null )
+ return false;
+
+ foreach ( RaffleEntry entry in m_Entries )
+ {
+ if ( entry.From != null )
+ {
+ Account entryAcc = entry.From.Account as Account;
+
+ if ( entryAcc == acc )
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsAtIPLimit( Mobile from )
+ {
+ if ( from.NetState == null )
+ return false;
+
+ IPAddress address = from.NetState.Address;
+ int tickets = 0;
+
+ foreach ( RaffleEntry entry in m_Entries )
+ {
+ if ( Utility.IPMatchClassC( entry.Address, address ) )
+ {
+ if ( ++tickets >= EntryLimitPerIP )
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public string FormatLocation()
+ {
+ if ( !ValidLocation() )
+ return "no location set";
+
+ int xLong = 0, yLat = 0;
+ int xMins = 0, yMins = 0;
+ bool xEast = false, ySouth = false;
+
+ Point3D loc = new Point3D( m_Bounds.X + m_Bounds.Width / 2, m_Bounds.Y + m_Bounds.Height / 2, 0 );
+ Map map = m_Facet;
+
+ if ( Sextant.Format( loc, map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) )
+ return String.Format( "{0}°{1}'{2},{3}°{4}'{5} ({6})", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W", map );
+ else
+ return String.Format( "{0},{1} ({2})", loc.X, loc.Y, map );
+ }
+
+ public string FormatPrice()
+ {
+ if ( m_TicketPrice == 0 )
+ return "FREE";
+ else
+ return String.Format( "{0} gold", m_TicketPrice );
+ }
+
+ public override void GetProperties( ObjectPropertyList list )
+ {
+ base.GetProperties( list );
+
+ if ( ValidLocation() )
+ list.Add( FormatLocation() );
+
+ if ( m_Active )
+ {
+ list.Add( 1060658, "ticket price\t{0}", FormatPrice() ); // ~1_val~: ~2_val~
+ list.Add( 1060659, "ends\t{0}", m_Started + m_Duration ); // ~1_val~: ~2_val~
+ }
+ else if ( m_Winner != null )
+ {
+ list.Add( 1060658, "won by\t{0}", m_Winner.Name ); // ~1_val~: ~2_val~
+ }
+ }
+
+ public override void GetContextMenuEntries( Mobile from, List list )
+ {
+ base.GetContextMenuEntries( from, list );
+
+ if ( from.AccessLevel >= AccessLevel.Seer )
+ {
+ list.Add( new EditEntry( from, this ) );
+
+ if ( !m_Active && m_Winner == null )
+ list.Add( new ActivateEntry( from, this ) );
+ else
+ list.Add( new ManagementEntry( from, this ) );
+ }
+ }
+
+ public override void OnDoubleClick( Mobile from )
+ {
+ if ( !m_Active || !from.CheckAlive() )
+ return;
+
+ if ( !from.InRange( GetWorldLocation(), 2 ) )
+ {
+ from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that.
+ return;
+ }
+
+ if ( HasEntered( from ) )
+ {
+ from.SendMessage( MessageHue, "You have already entered this plot's raffle." );
+ }
+ else if ( IsAtIPLimit( from ) )
+ {
+ from.SendMessage( MessageHue, "You may not enter this plot's raffle." );
+ }
+ else
+ {
+ from.SendGump( new WarningGump( 1150470, 0x7F00, String.Format( "You are about to purchase a raffle ticket for the house plot located at {0}. The ticket price is {1}. Tickets are non-refundable and you can only purchase one ticket per account. Do you wish to continue?", FormatLocation(), FormatPrice() ), 0xFFFFFF, 420, 280, new WarningGumpCallback( Purchase_Callback ), null ) ); // CONFIRM TICKET PURCHASE
+ }
+ }
+
+ public void Purchase_Callback( Mobile from, bool okay, object state )
+ {
+ if ( Deleted || !from.CheckAlive() || HasEntered( from ) || IsAtIPLimit( from ) )
+ return;
+
+ Account acc = from.Account as Account;
+
+ if ( acc == null )
+ return;
+
+ if ( okay )
+ {
+ Container bank = from.FindBankNoCreate();
+
+ if ( m_TicketPrice == 0 || ( from.Backpack != null && from.Backpack.ConsumeTotal( typeof( Gold ), m_TicketPrice ) ) || ( bank != null && bank.ConsumeTotal( typeof( Gold ), m_TicketPrice ) ) )
+ {
+ m_Entries.Add( new RaffleEntry( from ) );
+
+ from.SendMessage( MessageHue, "You have successfully entered the plot's raffle." );
+ }
+ else
+ {
+ from.SendMessage( MessageHue, "You do not have the {0} required to enter the raffle.", FormatPrice() );
+ }
+ }
+ else
+ {
+ from.SendMessage( MessageHue, "You have chosen not to enter the raffle." );
+ }
+ }
+
+ public void CheckEnd()
+ {
+ if ( !m_Active || m_Region == null || m_Entries.Count == 0 || m_Started + m_Duration > DateTime.Now )
+ return;
+
+ m_Active = false;
+
+ int winner = Utility.Random( m_Entries.Count );
+
+ m_Winner = m_Entries[winner].From;
+ m_Winner.SendMessage( MessageHue, "Congratulations, {0}! You have won the raffle for the plot located at {1}. The plot is now open for house placement to you.", m_Entries[winner].From.Name, FormatLocation() );
+
+ InvalidateProperties();
+ }
+
+ public override void OnDelete()
+ {
+ if ( m_Region != null )
+ {
+ m_Region.Unregister();
+ m_Region = null;
+ }
+
+ m_AllStones.Remove( this );
+
+ base.OnDelete();
+ }
+
+ public override void Serialize( GenericWriter writer )
+ {
+ base.Serialize( writer );
+
+ writer.Write( (int) 0 ); // version
+
+ writer.Write( m_Active );
+
+ writer.Write( m_Bounds );
+ writer.Write( m_Facet );
+
+ writer.Write( m_Winner );
+
+ writer.Write( m_TicketPrice );
+ writer.Write( m_Started );
+ writer.Write( m_Duration );
+
+ writer.Write( m_Entries.Count );
+
+ foreach ( RaffleEntry entry in m_Entries )
+ entry.Serialize( writer );
+ }
+
+ public override void Deserialize( GenericReader reader )
+ {
+ base.Deserialize( reader );
+
+ int version = reader.ReadInt();
+
+ switch ( version )
+ {
+ case 0:
+ {
+ m_Active = reader.ReadBool();
+
+ m_Bounds = reader.ReadRect2D();
+ m_Facet = reader.ReadMap();
+
+ m_Winner = reader.ReadMobile();
+
+ m_TicketPrice = reader.ReadInt();
+ m_Started = reader.ReadDateTime();
+ m_Duration = reader.ReadTimeSpan();
+
+ int entryCount = reader.ReadInt();
+ m_Entries = new List( entryCount );
+
+ for ( int i = 0; i < entryCount; i++ )
+ {
+ RaffleEntry entry = new RaffleEntry( reader, version );
+
+ if ( entry.From == null )
+ continue; // Character was deleted
+
+ m_Entries.Add( entry );
+ }
+
+ InvalidateRegion();
+
+ m_AllStones.Add( this );
+
+ break;
+ }
+ }
+ }
+
+ private class RaffleContextMenuEntry : ContextMenuEntry
+ {
+ protected Mobile m_From;
+ protected HouseRaffleStone m_Stone;
+
+ public RaffleContextMenuEntry( Mobile from, HouseRaffleStone stone, int label )
+ : base( label )
+ {
+ m_From = from;
+ m_Stone = stone;
+ }
+ }
+
+ private class EditEntry : RaffleContextMenuEntry
+ {
+ public EditEntry( Mobile from, HouseRaffleStone stone )
+ : base( from, stone, 5101 ) // Edit
+ {
+ }
+
+ public override void OnClick()
+ {
+ if ( m_Stone.Deleted || m_From.AccessLevel < AccessLevel.Seer )
+ return;
+
+ m_From.SendGump( new PropertiesGump( m_From, m_Stone ) );
+ }
+ }
+
+ private class ActivateEntry : RaffleContextMenuEntry
+ {
+ public ActivateEntry( Mobile from, HouseRaffleStone stone )
+ : base( from, stone, 5113 ) // Start
+ {
+ if ( !stone.ValidLocation() )
+ Flags |= Network.CMEFlags.Disabled;
+ }
+
+ public override void OnClick()
+ {
+ if ( m_Stone.Deleted || m_From.AccessLevel < AccessLevel.Seer || m_Stone.Active || !m_Stone.ValidLocation() )
+ return;
+
+ m_Stone.Active = true;
+ }
+ }
+
+ private class ManagementEntry : RaffleContextMenuEntry
+ {
+ public ManagementEntry( Mobile from, HouseRaffleStone stone)
+ : base( from, stone, 5032 ) // Game Monitor
+ {
+ }
+
+ public override void OnClick()
+ {
+ if ( m_Stone.Deleted || m_From.AccessLevel < AccessLevel.Seer )
+ return;
+
+ m_From.SendGump( new HouseRaffleManagementGump( m_Stone ) );
+ }
+ }
+ }
+}