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 ) ); + } + } + } +}