diff --git a/Scripts/Commands/Generic/Implementors/BaseCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/BaseCommandImplementor.cs index 057d0cabd..15fa711af 100644 --- a/Scripts/Commands/Generic/Implementors/BaseCommandImplementor.cs +++ b/Scripts/Commands/Generic/Implementors/BaseCommandImplementor.cs @@ -17,14 +17,15 @@ namespace Server.Commands.Generic Self = 0x0020, Region = 0x0040, Contained = 0x0080, + IPAddress = 0x0100, - All = Single | Global | Online | Multi | Area | Self | Region | Contained, + All = Single | Global | Online | Multi | Area | Self | Region | Contained | IPAddress, AllMobiles = All & ~Contained, - AllNPCs = All & ~(Online | Self | Contained), - AllItems = All & ~(Online | Self | Region), + AllNPCs = All & ~(IPAddress | Online | Self | Contained), + AllItems = All & ~(IPAddress | Online | Self | Region), Simple = Single | Multi, - Complex = Global | Online | Area | Region | Contained + Complex = Global | Online | Area | Region | Contained | IPAddress } public abstract class BaseCommandImplementor @@ -40,6 +41,8 @@ namespace Server.Commands.Generic Register( new AreaCommandImplementor() ); Register( new SelfCommandImplementor() ); Register( new ContainedCommandImplementor() ); + Register( new IPAddressCommandImplementor() ); + Register( new RangeCommandImplementor() ); Register( new ScreenCommandImplementor() ); Register( new FacetCommandImplementor() ); diff --git a/Scripts/Commands/Generic/Implementors/IPAddressCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/IPAddressCommandImplementor.cs new file mode 100644 index 000000000..0491c4ca9 --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/IPAddressCommandImplementor.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; +using Server; +using Server.Network; + +namespace Server.Commands.Generic +{ + public class IPAddressCommandImplementor : BaseCommandImplementor + { + public IPAddressCommandImplementor() + { + Accessors = new string[]{ "IPAddress" }; + SupportRequirement = CommandSupport.IPAddress; + SupportsConditionals = true; + AccessLevel = AccessLevel.Administrator; + Usage = "IPAddress [condition]"; + Description = "Invokes the command on one mobile from each IP address that is logged in. Optional condition arguments can further restrict the set of objects."; + } + + public override void Compile( Mobile from, BaseCommand command, ref string[] args, ref object obj ) + { + try + { + Extensions ext = Extensions.Parse( from, ref args ); + + bool items, mobiles; + + if ( !CheckObjectTypes( command, ext, out items, out mobiles ) ) + return; + + if ( !mobiles ) // sanity check + { + command.LogFailure( "This command does not support mobiles." ); + return; + } + + ArrayList list = new ArrayList(); + ArrayList addresses = new ArrayList(); + + System.Collections.Generic.List states = NetState.Instances; + + for ( int i = 0; i < states.Count; ++i ) + { + NetState ns = (NetState)states[i]; + Mobile mob = ns.Mobile; + + if ( mob != null && !addresses.Contains( ns.Address ) && ext.IsValid( mob ) ) + { + list.Add( mob ); + addresses.Add( ns.Address ); + } + } + + ext.Filter( list ); + + obj = list; + } + catch ( Exception ex ) + { + from.SendMessage( ex.Message ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/AcceptDuelGump.cs b/Scripts/Engines/ConPVP/AcceptDuelGump.cs new file mode 100644 index 000000000..c07b2fd63 --- /dev/null +++ b/Scripts/Engines/ConPVP/AcceptDuelGump.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Targeting; +using Server.Mobiles; + +namespace Server.Engines.ConPVP +{ + public class AcceptDuelGump : Gump + { + private Mobile m_Challenger, m_Challenged; + private DuelContext m_Context; + private Participant m_Participant; + private int m_Slot; + + 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; + private const int BlackColor32 = 0x000008; + + private bool m_Active = true; + + public AcceptDuelGump( Mobile challenger, Mobile challenged, DuelContext context, Participant p, int slot ) : base( 50, 50 ) + { + m_Challenger = challenger; + m_Challenged = challenged; + m_Context = context; + m_Participant = p; + m_Slot = slot; + + challenged.CloseGump( typeof( AcceptDuelGump ) ); + + Closable = false; + + AddPage( 0 ); + + //AddBackground( 0, 0, 400, 220, 9150 ); + AddBackground( 1, 1, 398, 218, 3600 ); + //AddBackground( 16, 15, 369, 189, 9100 ); + + AddImageTiled( 16, 15, 369, 189, 3604 ); + AddAlphaRegion( 16, 15, 369, 189 ); + + AddImage( 215, -43, 0xEE40 ); + //AddImage( 330, 141, 0x8BA ); + + AddHtml( 22-1, 22, 294, 20, Color( Center( "Duel Challenge" ), BlackColor32 ), false, false ); + AddHtml( 22+1, 22, 294, 20, Color( Center( "Duel Challenge" ), BlackColor32 ), false, false ); + AddHtml( 22, 22-1, 294, 20, Color( Center( "Duel Challenge" ), BlackColor32 ), false, false ); + AddHtml( 22, 22+1, 294, 20, Color( Center( "Duel Challenge" ), BlackColor32 ), false, false ); + AddHtml( 22, 22, 294, 20, Color( Center( "Duel Challenge" ), LabelColor32 ), false, false ); + + string fmt; + + if ( p.Contains( challenger ) ) + fmt = "You have been asked to join sides with {0} in a duel. Do you accept?"; + else + fmt = "You have been challenged to a duel from {0}. Do you accept?"; + + AddHtml( 22-1, 50, 294, 40, Color( String.Format( fmt, challenger.Name ), BlackColor32 ), false, false ); + AddHtml( 22+1, 50, 294, 40, Color( String.Format( fmt, challenger.Name ), BlackColor32 ), false, false ); + AddHtml( 22, 50-1, 294, 40, Color( String.Format( fmt, challenger.Name ), BlackColor32 ), false, false ); + AddHtml( 22, 50+1, 294, 40, Color( String.Format( fmt, challenger.Name ), BlackColor32 ), false, false ); + AddHtml( 22, 50, 294, 40, Color( String.Format( fmt, challenger.Name ), 0xB0C868 ), false, false ); + + AddImageTiled( 32, 88, 264, 1, 9107 ); + AddImageTiled( 42, 90, 264, 1, 9157 ); + + AddRadio( 24, 100, 9727, 9730, true, 1 ); + AddHtml( 60-1, 105, 250, 20, Color( "Yes, I will fight this duel.", BlackColor32 ), false, false ); + AddHtml( 60+1, 105, 250, 20, Color( "Yes, I will fight this duel.", BlackColor32 ), false, false ); + AddHtml( 60, 105-1, 250, 20, Color( "Yes, I will fight this duel.", BlackColor32 ), false, false ); + AddHtml( 60, 105+1, 250, 20, Color( "Yes, I will fight this duel.", BlackColor32 ), false, false ); + AddHtml( 60, 105, 250, 20, Color( "Yes, I will fight this duel.", LabelColor32 ), false, false ); + + AddRadio( 24, 135, 9727, 9730, false, 2 ); + AddHtml( 60-1, 140, 250, 20, Color( "No, I do not wish to fight.", BlackColor32 ), false, false ); + AddHtml( 60+1, 140, 250, 20, Color( "No, I do not wish to fight.", BlackColor32 ), false, false ); + AddHtml( 60, 140-1, 250, 20, Color( "No, I do not wish to fight.", BlackColor32 ), false, false ); + AddHtml( 60, 140+1, 250, 20, Color( "No, I do not wish to fight.", BlackColor32 ), false, false ); + AddHtml( 60, 140, 250, 20, Color( "No, I do not wish to fight.", LabelColor32 ), false, false ); + + AddRadio( 24, 170, 9727, 9730, false, 3 ); + AddHtml( 60-1, 175, 250, 20, Color( "No, knave. Do not ask again.", BlackColor32 ), false, false ); + AddHtml( 60+1, 175, 250, 20, Color( "No, knave. Do not ask again.", BlackColor32 ), false, false ); + AddHtml( 60, 175-1, 250, 20, Color( "No, knave. Do not ask again.", BlackColor32 ), false, false ); + AddHtml( 60, 175+1, 250, 20, Color( "No, knave. Do not ask again.", BlackColor32 ), false, false ); + AddHtml( 60, 175, 250, 20, Color( "No, knave. Do not ask again.", LabelColor32 ), false, false ); + + AddButton( 314, 173, 247, 248, 1, GumpButtonType.Reply, 0 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 15.0 ), new TimerCallback( AutoReject ) ); + } + + public void AutoReject() + { + if ( !m_Active ) + return; + + m_Active = false; + + m_Challenged.CloseGump( typeof( AcceptDuelGump ) ); + + m_Challenger.SendMessage( "{0} seems unresponsive.", m_Challenged.Name ); + m_Challenged.SendMessage( "You decline the challenge." ); + } + + private static Hashtable m_IgnoreLists = new Hashtable(); + + private class IgnoreEntry + { + public Mobile m_Ignored; + public DateTime m_Expire; + + public Mobile Ignored{ get{ return m_Ignored; } } + public bool Expired{ get{ return ( DateTime.Now >= m_Expire ); } } + + private static TimeSpan ExpireDelay = TimeSpan.FromMinutes( 15.0 ); + + public void Refresh() + { + m_Expire = DateTime.Now + ExpireDelay; + } + + public IgnoreEntry( Mobile ignored ) + { + m_Ignored = ignored; + Refresh(); + } + } + + public static void BeginIgnore( Mobile source, Mobile toIgnore ) + { + ArrayList list = (ArrayList)m_IgnoreLists[source]; + + if ( list == null ) + m_IgnoreLists[source] = list = new ArrayList(); + + for ( int i = 0; i < list.Count; ++i ) + { + IgnoreEntry ie = (IgnoreEntry)list[i]; + + if ( ie.Ignored == toIgnore ) + { + ie.Refresh(); + return; + } + else if ( ie.Expired ) + { + list.RemoveAt( i-- ); + } + } + + list.Add( new IgnoreEntry( toIgnore ) ); + } + + public static bool IsIgnored( Mobile source, Mobile check ) + { + ArrayList list = (ArrayList)m_IgnoreLists[source]; + + if ( list == null ) + return false; + + for ( int i = 0; i < list.Count; ++i ) + { + IgnoreEntry ie = (IgnoreEntry)list[i]; + + if ( ie.Expired ) + list.RemoveAt( i-- ); + else if ( ie.Ignored == check ) + return true; + } + + return false; + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID != 1 || !m_Active || !m_Context.Registered ) + return; + + m_Active = false; + + if ( !m_Context.Participants.Contains( m_Participant ) ) + return; + + if ( info.IsSwitched( 1 ) ) + { + PlayerMobile pm = m_Challenged as PlayerMobile; + + if ( pm == null ) + return; + + if ( pm.DuelContext != null ) + { + if ( pm.DuelContext.Initiator == pm ) + pm.SendMessage( 0x22, "You have already started a duel." ); + else + pm.SendMessage( 0x22, "You have already been challenged in a duel." ); + + m_Challenger.SendMessage( "{0} cannot fight because they are already assigned to another duel.", pm.Name ); + } + else if ( DuelContext.CheckCombat( pm ) ) + { + pm.SendMessage( 0x22, "You have recently been in combat with another player and must wait before starting a duel." ); + m_Challenger.SendMessage( "{0} cannot fight because they have recently been in combat with another player.", pm.Name ); + } + else if ( TournamentController.IsActive ) + { + pm.SendMessage( 0x22, "A tournament is currently active and you may not duel." ); + m_Challenger.SendMessage( 0x22, "A tournament is currently active and you may not duel." ); + } + else + { + bool added = false; + + if ( m_Slot >= 0 && m_Slot < m_Participant.Players.Length && m_Participant.Players[m_Slot] == null ) + { + added = true; + m_Participant.Players[m_Slot] = new DuelPlayer( m_Challenged, m_Participant ); + } + else + { + for ( int i = 0; i < m_Participant.Players.Length; ++i ) + { + if ( m_Participant.Players[i] == null ) + { + added = true; + m_Participant.Players[i] = new DuelPlayer( m_Challenged, m_Participant ); + break; + } + } + } + + if ( added ) + { + m_Challenger.SendMessage( "{0} has accepted the request.", m_Challenged.Name ); + m_Challenged.SendMessage( "You have accepted the request from {0}.", m_Challenger.Name ); + + NetState ns = m_Challenger.NetState; + + if ( ns != null ) + { + foreach ( Gump g in ns.Gumps ) + { + if ( g is ParticipantGump ) + { + ParticipantGump pg = (ParticipantGump)g; + + if ( pg.Participant == m_Participant ) + { + m_Challenger.SendGump( new ParticipantGump( m_Challenger, m_Context, m_Participant ) ); + break; + } + } + else if ( g is DuelContextGump ) + { + DuelContextGump dcg = (DuelContextGump)g; + + if ( dcg.Context == m_Context ) + { + m_Challenger.SendGump( new DuelContextGump( m_Challenger, m_Context ) ); + break; + } + } + } + } + } + else + { + m_Challenger.SendMessage( "The participant list was full and so {0} could not join.", m_Challenged.Name ); + m_Challenged.SendMessage( "The participant list was full and so you could not join the fight {1} {0}.", m_Challenger.Name, m_Participant.Contains( m_Challenger ) ? "with" : "against" ); + } + } + } + else + { + if ( info.IsSwitched( 3 ) ) + BeginIgnore( m_Challenged, m_Challenger ); + + m_Challenger.SendMessage( "{0} does not wish to fight.", m_Challenged.Name ); + m_Challenged.SendMessage( "You chose not to fight {1} {0}.", m_Challenger.Name, m_Participant.Contains( m_Challenger ) ? "with" : "against" ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/Arena.cs b/Scripts/Engines/ConPVP/Arena.cs new file mode 100644 index 000000000..5b42136d8 --- /dev/null +++ b/Scripts/Engines/ConPVP/Arena.cs @@ -0,0 +1,839 @@ +using System; +using System.Collections.Generic; +using Server; +using Server.Mobiles; + +namespace Server.Engines.ConPVP +{ + public class ArenaController : Item + { + private Arena m_Arena; + private bool m_IsPrivate; + + [CommandProperty( AccessLevel.GameMaster )] + public Arena Arena{ get{ return m_Arena; } set{} } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsPrivate{ get{ return m_IsPrivate; } set{ m_IsPrivate = value; } } + + public override string DefaultName + { + get { return "arena controller"; } + } + + [Constructable] + public ArenaController() : base( 0x1B7A ) + { + Visible = false; + Movable = false; + + m_Arena = new Arena(); + + m_Instances.Add( this ); + } + + public override void OnDelete() + { + base.OnDelete(); + + m_Instances.Remove( this ); + m_Arena.Delete(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + from.SendGump( new Gumps.PropertiesGump( from, m_Arena ) ); + } + + public ArenaController( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + + writer.Write( (bool) m_IsPrivate ); + + m_Arena.Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_IsPrivate = reader.ReadBool(); + + goto case 0; + } + case 0: + { + m_Arena = new Arena( reader ); + break; + } + } + + m_Instances.Add( this ); + } + + private static List m_Instances = new List(); + + public static List Instances{ get{ return m_Instances; } set{ m_Instances = value; } } + } + + [PropertyObject] + public class ArenaStartPoints + { + private Point3D[] m_Points; + + public Point3D[] Points{ get{ return m_Points; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D EdgeWest{ get{ return m_Points[0]; } set{ m_Points[0] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D EdgeEast{ get{ return m_Points[1]; } set{ m_Points[1] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D EdgeNorth{ get{ return m_Points[2]; } set{ m_Points[2] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D EdgeSouth{ get{ return m_Points[3]; } set{ m_Points[3] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D CornerNW{ get{ return m_Points[4]; } set{ m_Points[4] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D CornerSE{ get{ return m_Points[5]; } set{ m_Points[5] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D CornerSW{ get{ return m_Points[6]; } set{ m_Points[6] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D CornerNE{ get{ return m_Points[7]; } set{ m_Points[7] = value; } } + + public override string ToString() + { + return "..."; + } + + public ArenaStartPoints() : this( new Point3D[8] ) + { + } + + public ArenaStartPoints( Point3D[] points ) + { + m_Points = points; + } + + public ArenaStartPoints( GenericReader reader ) + { + m_Points = new Point3D[reader.ReadEncodedInt()]; + + for ( int i = 0; i < m_Points.Length; ++i ) + m_Points[i] = reader.ReadPoint3D(); + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) m_Points.Length ); + + for ( int i = 0; i < m_Points.Length; ++i ) + writer.Write( (Point3D) m_Points[i] ); + } + } + + [PropertyObject] + public class Arena : IComparable + { + private Map m_Facet; + private Rectangle2D m_Bounds; + private Rectangle2D m_Zone; + private Point3D m_Outside; + private Point3D m_Wall; + private Point3D m_GateIn; + private Point3D m_GateOut; + private ArenaStartPoints m_Points; + private bool m_Active; + private string m_Name; + + private bool m_IsGuarded; + + private Item m_Teleporter; + + private List m_Players; + + private TournamentController m_Tournament; + private Mobile m_Announcer; + + private LadderController m_Ladder; + + [CommandProperty( AccessLevel.GameMaster )] + public LadderController Ladder + { + get{ return m_Ladder; } + set{ m_Ladder = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsGuarded + { + get{ return m_IsGuarded; } + set + { + m_IsGuarded = value; + + if ( m_Region != null ) + m_Region.Disabled = !m_IsGuarded; + } + } + + public Ladder AcquireLadder() + { + if ( m_Ladder != null ) + return m_Ladder.Ladder; + + return Server.Engines.ConPVP.Ladder.Instance; + } + + [CommandProperty( AccessLevel.GameMaster )] + public TournamentController Tournament + { + get{ return m_Tournament; } + set + { + if ( m_Tournament != null ) + m_Tournament.Tournament.Arenas.Remove( this ); + + m_Tournament = value; + + if ( m_Tournament != null ) + m_Tournament.Tournament.Arenas.Add( this ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Announcer + { + get{ return m_Announcer; } + set{ m_Announcer = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Name + { + get{ return m_Name; } + set{ m_Name = value; if ( m_Active ) m_Arenas.Sort(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Map Facet + { + get{ return m_Facet; } + set + { + m_Facet = value; + + if ( m_Teleporter != null ) + m_Teleporter.Map = value; + + if ( m_Region != null ) + { + if ( m_Facet == null ) + { + m_Region.Unregister(); + m_Region = null; + } + else + { + m_Region = new SafeZone( m_Zone, m_Outside, m_Facet, m_IsGuarded ); + } + } + else + { + if ( m_Zone.Start != Point2D.Zero && m_Zone.End != Point2D.Zero && m_Facet != null ) + m_Region = new SafeZone( m_Zone, m_Outside, m_Facet, m_IsGuarded ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Rectangle2D Bounds{ get{ return m_Bounds; } set{ m_Bounds = value; } } + + private SafeZone m_Region; + + public int Spectators + { + get + { + if ( m_Region == null ) + return 0; + + int specs = m_Region.GetPlayerCount() - m_Players.Count; + + if ( specs < 0 ) + specs = 0; + + return specs; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Rectangle2D Zone + { + get{ return m_Zone; } + set + { + m_Zone = value; + + if ( m_Zone.Start != Point2D.Zero && m_Zone.End != Point2D.Zero && m_Facet != null ) + { + if ( m_Region != null ) + m_Region.Unregister(); + + m_Region = new SafeZone( m_Zone, m_Outside, m_Facet, m_IsGuarded ); + } + else + { + if ( m_Region != null ) + m_Region.Unregister(); + + m_Region = null; + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D Outside{ get{ return m_Outside; } set{ m_Outside = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D GateIn{ get{ return m_GateIn; } set{ m_GateIn = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D GateOut{ get{ return m_GateOut; } set{ m_GateOut = value; if ( m_Teleporter != null ) m_Teleporter.Location = m_GateOut; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D Wall{ get{ return m_Wall; } set{ m_Wall = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsOccupied{ get{ return ( m_Players.Count > 0 ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public ArenaStartPoints Points{ get{ return m_Points; } set{} } + + public Item Teleporter{ get{ return m_Teleporter; } set{ m_Teleporter = value; } } + + public List Players{ get{ return m_Players; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Active + { + get{ return m_Active; } + set + { + if ( m_Active == value ) + return; + + m_Active = value; + + if ( m_Active ) + { + m_Arenas.Add( this ); + m_Arenas.Sort(); + } + else + { + m_Arenas.Remove( this ); + } + } + } + + public void Delete() + { + Active = false; + + if ( m_Region != null ) + m_Region.Unregister(); + + m_Region = null; + } + + public override string ToString() + { + return "..."; + } + + public Point3D GetBaseStartPoint( int index ) + { + if ( index < 0 ) + index = 0; + + return m_Points.Points[index % m_Points.Points.Length]; + } + + #region Offsets & Rotation + private static Point2D[] m_EdgeOffsets = new Point2D[] + { + /* + * /\ + * /\/\ + * /\/\/\ + * \/\/\/ + * \/\/\ + * \/\/ + */ + new Point2D( 0, 0 ), + new Point2D( 0, -1 ), + new Point2D( 0, +1 ), + new Point2D( 1, 0 ), + new Point2D( 1, -1 ), + new Point2D( 1, +1 ), + new Point2D( 2, 0 ), + new Point2D( 2, -1 ), + new Point2D( 2, +1 ), + new Point2D( 3, 0 ) + }; + + // nw corner + private static Point2D[] m_CornerOffsets = new Point2D[] + { + /* + * /\ + * /\/\ + * /\/\/\ + * /\/\/\/\ + * \/\/\/\/ + */ + new Point2D( 0, 0 ), + new Point2D( 0, 1 ), + new Point2D( 1, 0 ), + new Point2D( 1, 1 ), + new Point2D( 0, 2 ), + new Point2D( 2, 0 ), + new Point2D( 2, 1 ), + new Point2D( 1, 2 ), + new Point2D( 0, 3 ), + new Point2D( 3, 0 ) + }; + + private static int[][,] m_Rotate = new int[][,] + { + new int[,]{ { +1, 0 }, { 0, +1 } }, // west + new int[,]{ { -1, 0 }, { 0, -1 } }, // east + new int[,]{ { 0, +1 }, { +1, 0 } }, // north + new int[,]{ { 0, -1 }, { -1, 0 } }, // south + new int[,]{ { +1, 0 }, { 0, +1 } }, // nw + new int[,]{ { -1, 0 }, { 0, -1 } }, // se + new int[,]{ { 0, +1 }, { +1, 0 } }, // sw + new int[,]{ { 0, -1 }, { -1, 0 } }, // ne + }; + #endregion + + public void MoveInside( DuelPlayer[] players, int index ) + { + if ( index < 0 ) + index = 0; + else + index %= m_Points.Points.Length; + + Point3D start = GetBaseStartPoint( index ); + + int offset = 0; + + Point2D[] offsets = ( index < 4 ) ? m_EdgeOffsets : m_CornerOffsets; + int[,] matrix = m_Rotate[index]; + + for ( int i = 0; i < players.Length; ++i ) + { + DuelPlayer pl = players[i]; + + if ( pl == null ) + continue; + + Mobile mob = pl.Mobile; + + Point2D p; + + if ( offset < offsets.Length ) + p = offsets[offset++]; + else + p = offsets[offsets.Length - 1]; + + p.X = (p.X * matrix[0, 0]) + (p.Y * matrix[0, 1]); + p.Y = (p.X * matrix[1, 0]) + (p.Y * matrix[1, 1]); + + mob.MoveToWorld( new Point3D( start.X + p.X, start.Y + p.Y, start.Z ), m_Facet ); + mob.Direction = mob.GetDirectionTo( m_Wall ); + + m_Players.Add( mob ); + } + } + + public Arena() + { + m_Points = new ArenaStartPoints(); + m_Players = new List(); + } + + public Arena( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 7: + { + m_IsGuarded = reader.ReadBool(); + + goto case 6; + } + case 6: + { + m_Ladder = reader.ReadItem() as LadderController; + + goto case 5; + } + case 5: + { + m_Tournament = reader.ReadItem() as TournamentController; + m_Announcer = reader.ReadMobile(); + + goto case 4; + } + case 4: + { + m_Name = reader.ReadString(); + + goto case 3; + } + case 3: + { + m_Zone = reader.ReadRect2D(); + + goto case 2; + } + case 2: + { + m_GateIn = reader.ReadPoint3D(); + m_GateOut = reader.ReadPoint3D(); + m_Teleporter = reader.ReadItem(); + + goto case 1; + } + case 1: + { + m_Players = reader.ReadStrongMobileList(); + + goto case 0; + } + case 0: + { + m_Facet = reader.ReadMap(); + m_Bounds = reader.ReadRect2D(); + m_Outside = reader.ReadPoint3D(); + m_Wall = reader.ReadPoint3D(); + + if ( version == 0 ) + { + reader.ReadBool(); + m_Players = new List(); + } + + m_Active = reader.ReadBool(); + m_Points = new ArenaStartPoints( reader ); + + if ( m_Active ) + { + m_Arenas.Add( this ); + m_Arenas.Sort(); + } + + break; + } + } + + if ( m_Zone.Start != Point2D.Zero && m_Zone.End != Point2D.Zero && m_Facet != null ) + m_Region = new SafeZone( m_Zone, m_Outside, m_Facet, m_IsGuarded ); + + if ( IsOccupied ) + Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerCallback( Evict ) ); + + if ( m_Tournament != null ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( AttachToTournament_Sandbox ) ); + } + + private void AttachToTournament_Sandbox() + { + if ( m_Tournament != null ) + m_Tournament.Tournament.Arenas.Add( this ); + } + + [CommandProperty( AccessLevel.Administrator, AccessLevel.Administrator )] + public bool ForceEvict{ get{ return false; } set{ if ( value ) Evict(); } } + + public void Evict() + { + Point3D loc; + Map facet; + + if ( m_Facet == null ) + { + loc = new Point3D( 2715, 2165, 0 ); + facet = Map.Felucca; + } + else + { + loc = m_Outside; + facet = m_Facet; + } + + bool hasBounds = ( m_Bounds.Start != Point2D.Zero && m_Bounds.End != Point2D.Zero ); + + for ( int i = 0; i < m_Players.Count; ++i ) + { + Mobile mob = m_Players[i]; + + if ( mob == null ) + continue; + + if ( mob.Map == Map.Internal ) + { + if ( (m_Facet == null || mob.LogoutMap == m_Facet) && (!hasBounds || m_Bounds.Contains( mob.LogoutLocation )) ) + mob.LogoutLocation = loc; + } + else if ( (m_Facet == null || mob.Map == m_Facet) && (!hasBounds || m_Bounds.Contains( mob.Location )) ) + { + mob.MoveToWorld( loc, facet ); + } + + mob.Combatant = null; + mob.Frozen = false; + DuelContext.Debuff( mob ); + DuelContext.CancelSpell( mob ); + } + + if ( hasBounds ) { + List pets = new List(); + + foreach ( Mobile mob in facet.GetMobilesInBounds( m_Bounds ) ) { + BaseCreature pet = mob as BaseCreature; + + if ( pet != null && pet.Controlled && pet.ControlMaster != null ) { + if ( m_Players.Contains( pet.ControlMaster ) ) { + pets.Add( pet ); + } + } + } + + foreach ( Mobile pet in pets ) { + pet.Combatant = null; + pet.Frozen = false; + + pet.MoveToWorld( loc, facet ); + } + } + + m_Players.Clear(); + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 7 ); + + writer.Write( (bool) m_IsGuarded ); + + writer.Write( (Item) m_Ladder ); + + writer.Write( (Item) m_Tournament ); + writer.Write( (Mobile) m_Announcer ); + + writer.Write( (string) m_Name ); + + writer.Write( (Rectangle2D) m_Zone ); + + writer.Write( (Point3D) m_GateIn ); + writer.Write( (Point3D) m_GateOut ); + writer.Write( (Item) m_Teleporter ); + + writer.Write( m_Players ); + + writer.Write( (Map) m_Facet ); + writer.Write( (Rectangle2D) m_Bounds ); + writer.Write( (Point3D) m_Outside ); + writer.Write( (Point3D) m_Wall ); + writer.Write( (bool) m_Active ); + + m_Points.Serialize( writer ); + } + + private static List m_Arenas = new List(); + + public static List Arenas{ get{ return m_Arenas; } } + + public static Arena FindArena( List players ) + { + Preferences prefs = Preferences.Instance; + + if ( prefs == null ) + return FindArena(); + + if ( m_Arenas.Count == 0 ) + return null; + + if ( players.Count > 0 ) + { + Mobile first = players[0]; + + List allControllers = ArenaController.Instances; + + for ( int i = 0; i < allControllers.Count; ++i ) + { + ArenaController controller = allControllers[i]; + + if ( controller != null && !controller.Deleted && controller.Arena != null && controller.IsPrivate && controller.Map == first.Map && first.InRange( controller, 24 ) ) + { + Multis.BaseHouse house = Multis.BaseHouse.FindHouseAt( controller ); + bool allNear = true; + + for ( int j = 0; j < players.Count; ++j ) + { + Mobile check = players[j]; + bool isNear; + + if ( house == null ) + isNear = ( controller.Map == check.Map && check.InRange( controller, 24 ) ); + else + isNear = ( Multis.BaseHouse.FindHouseAt( check ) == house ); + + if ( !isNear ) + { + allNear = false; + break; + } + } + + if ( allNear ) + return controller.Arena; + } + } + } + + List arenas = new List(); + + for ( int i = 0; i < m_Arenas.Count; ++i ) + { + Arena arena = m_Arenas[i]; + + if ( !arena.IsOccupied ) + arenas.Add( new ArenaEntry( arena ) ); + } + + if ( arenas.Count == 0 ) + return m_Arenas[0]; + + int tc = 0; + + for ( int i = 0; i < arenas.Count; ++i ) + { + ArenaEntry ae = arenas[i]; + + for ( int j = 0; j < players.Count; ++j ) + { + PreferencesEntry pe = prefs.Find( players[j] ); + + if ( pe.Disliked.Contains( ae.m_Arena.Name ) ) + ++ae.m_VotesAgainst; + else + ++ae.m_VotesFor; + } + + tc += ae.Value; + } + + int rn = Utility.Random( tc ); + + for ( int i = 0; i < arenas.Count; ++i ) + { + ArenaEntry ae = arenas[i]; + + if ( rn < ae.Value ) + return ae.m_Arena; + + rn -= ae.Value; + } + + return arenas[Utility.Random( arenas.Count )].m_Arena; + } + + private class ArenaEntry + { + public Arena m_Arena; + public int m_VotesFor; + public int m_VotesAgainst; + + public int Value + { + get + { + return m_VotesFor; + + /*if ( m_VotesFor > m_VotesAgainst ) + return m_VotesFor - m_VotesAgainst; + else if ( m_VotesFor > 0 ) + return 1; + else + return 0;*/ + } + } + + public ArenaEntry( Arena arena ) + { + m_Arena = arena; + } + } + + public static Arena FindArena() + { + if ( m_Arenas.Count == 0 ) + return null; + + int offset = Utility.Random( m_Arenas.Count ); + + for ( int i = 0; i < m_Arenas.Count; ++i ) + { + Arena arena = m_Arenas[(i + offset) % m_Arenas.Count]; + + if ( !arena.IsOccupied ) + return arena; + } + + return m_Arenas[offset]; + } + + public int CompareTo(object obj) + { + Arena c = (Arena)obj; + + string a = m_Name; + string b = c.m_Name; + + if ( a == null && b == null ) + return 0; + else if ( a == null ) + return -1; + else if ( b == null ) + return +1; + + return a.CompareTo( b ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/ArenaGump.cs b/Scripts/Engines/ConPVP/ArenaGump.cs new file mode 100644 index 000000000..53abb3f4f --- /dev/null +++ b/Scripts/Engines/ConPVP/ArenaGump.cs @@ -0,0 +1,296 @@ +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; + +namespace Server.Engines.ConPVP +{ + public class ArenasMoongate : Item + { + public override string DefaultName + { + get { return "arena moongate"; } + } + + [Constructable] + public ArenasMoongate() : base( 0x1FD4 ) + { + Movable = false; + Light = LightType.Circle300; + } + + public ArenasMoongate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + Light = LightType.Circle300; + } + + public bool UseGate( Mobile from ) + { + if ( DuelContext.CheckCombat( from ) ) + { + from.SendMessage( 0x22, "You have recently been in combat with another player and cannot use this moongate." ); + return false; + } + else if ( from.Spell != null ) + { + from.SendLocalizedMessage( 1049616 ); // You are too busy to do that at the moment. + return false; + } + else + { + from.CloseGump( typeof( ArenaGump ) ); + from.SendGump( new ArenaGump( from, this ) ); + + if ( !from.Hidden || from.AccessLevel == AccessLevel.Player ) + Effects.PlaySound( from.Location, from.Map, 0x20E ); + + return true; + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( GetWorldLocation(), 1 ) ) + UseGate( from ); + else + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that + } + + public override bool OnMoveOver( Mobile m ) + { + return ( !m.Player || UseGate( m ) ); + } + } + + public class ArenaGump : Gump + { + private Mobile m_From; + private ArenasMoongate m_Gate; + private List m_Arenas; + + private void Append( StringBuilder sb, LadderEntry le ) + { + if ( le == null ) + return; + + if ( sb.Length > 0 ) + sb.Append( ", " ); + + sb.Append( le.Mobile.Name ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID != 1 ) + return; + + int[] switches = info.Switches; + + if ( switches.Length == 0 ) + return; + + int opt = switches[0]; + + if ( opt < 0 || opt >= m_Arenas.Count ) + return; + + Arena arena = m_Arenas[opt]; + + if ( !m_From.InRange( m_Gate.GetWorldLocation(), 1 ) || m_From.Map != m_Gate.Map ) + { + m_From.SendLocalizedMessage( 1019002 ); // You are too far away to use the gate. + } + else if ( DuelContext.CheckCombat( m_From ) ) + { + m_From.SendMessage( 0x22, "You have recently been in combat with another player and cannot use this moongate." ); + } + else if ( m_From.Spell != null ) + { + m_From.SendLocalizedMessage( 1049616 ); // You are too busy to do that at the moment. + } + else if ( m_From.Map == arena.Facet && arena.Zone.Contains( m_From ) ) + { + m_From.SendLocalizedMessage( 1019003 ); // You are already there. + } + else + { + BaseCreature.TeleportPets( m_From, arena.GateIn, arena.Facet ); + + m_From.Combatant = null; + m_From.Warmode = false; + m_From.Hidden = true; + + m_From.MoveToWorld( arena.GateIn, arena.Facet ); + + Effects.PlaySound( arena.GateIn, arena.Facet, 0x1FE ); + } + } + + public ArenaGump( Mobile from, ArenasMoongate gate ) : base( 50, 50 ) + { + m_From = from; + m_Gate = gate; + m_Arenas = Arena.Arenas; + + AddPage( 0 ); + + int height = 12 + 20 + (m_Arenas.Count * 31) + 24 + 12; + + AddBackground( 0, 0, 499+40, height, 0x2436 ); + + List list = m_Arenas; + + for ( int i = 1; i < list.Count; i += 2 ) + AddImageTiled( 12, 32 + (i * 31), 475+40, 30, 0x2430 ); + + AddAlphaRegion( 10, 10, 479+40, height - 20 ); + + AddColumnHeader( 35, null ); + AddColumnHeader( 115, "Arena" ); + AddColumnHeader( 325, "Participants" ); + AddColumnHeader( 40, "Obs" ); + + AddButton( 499+40 - 12 - 63 - 4 - 63, height - 12 - 24, 247, 248, 1, GumpButtonType.Reply, 0 ); + AddButton( 499+40 - 12 - 63, height - 12 - 24, 241, 242, 2, GumpButtonType.Reply, 0 ); + + for ( int i = 0; i < list.Count; ++i ) + { + Arena ar = list[i]; + + string name = ar.Name; + + if ( name == null ) + name = "(no name)"; + + int x = 12; + int y = 32 + (i * 31); + + int color = ( ar.Players.Count > 0 ? 0xCCFFCC : 0xCCCCCC ); + + AddRadio( x + 3, y + 1, 9727, 9730, false, i ); + x += 35; + + AddBorderedText( x + 5, y + 5, 115 - 5, name, color, 0 ); + x += 115; + + StringBuilder sb = new StringBuilder(); + + if ( ar.Players.Count > 0 ) + { + Ladder ladder = Ladder.Instance; + + if ( ladder == null ) + continue; + + LadderEntry p1 = null, p2 = null, p3 = null, p4 = null; + + for ( int j = 0; j < ar.Players.Count; ++j ) + { + Mobile mob = (Mobile)ar.Players[j]; + LadderEntry c = ladder.Find( mob ); + + if ( p1 == null || c.Index < p1.Index ) + { + p4 = p3; + p3 = p2; + p2 = p1; + p1 = c; + } + else if ( p2 == null || c.Index < p2.Index ) + { + p4 = p3; + p3 = p2; + p2 = c; + } + else if ( p3 == null || c.Index < p3.Index ) + { + p4 = p3; + p3 = c; + } + else if ( p4 == null || c.Index < p4.Index ) + { + p4 = c; + } + } + + Append( sb, p1 ); + Append( sb, p2 ); + Append( sb, p3 ); + Append( sb, p4 ); + + if ( ar.Players.Count > 4 ) + sb.Append( ", ..." ); + } + else + { + sb.Append( "Empty" ); + } + + AddBorderedText( x + 5, y + 5, 325 - 5, sb.ToString(), color, 0 ); + x += 325; + + AddBorderedText( x, y + 5, 40, Center( ar.Spectators.ToString() ), color, 0 ); + } + } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + private void AddBorderedText( int x, int y, int width, string text, int color, int borderColor ) + { + /*AddColoredText( x - 1, y, width, text, borderColor ); + AddColoredText( x + 1, y, width, text, borderColor ); + AddColoredText( x, y - 1, width, text, borderColor ); + AddColoredText( x, y + 1, width, text, borderColor );*/ + /*AddColoredText( x - 1, y - 1, width, text, borderColor ); + AddColoredText( x + 1, y + 1, width, text, borderColor );*/ + AddColoredText( x, y, width, text, color ); + } + + private void AddColoredText( int x, int y, int width, string text, int color ) + { + if ( color == 0 ) + AddHtml( x, y, width, 20, text, false, false ); + else + AddHtml( x, y, width, 20, Color( text, color ), false, false ); + } + + private int m_ColumnX = 12; + + private void AddColumnHeader( int width, string name ) + { + AddBackground( m_ColumnX, 12, width, 20, 0x242C ); + AddImageTiled( m_ColumnX + 2, 14, width - 4, 16, 0x2430 ); + + if ( name != null ) + AddBorderedText( m_ColumnX, 13, width, Center( name ), 0xFFFFFF, 0 ); + + m_ColumnX += width; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/BeginGump.cs b/Scripts/Engines/ConPVP/BeginGump.cs new file mode 100644 index 000000000..973138d16 --- /dev/null +++ b/Scripts/Engines/ConPVP/BeginGump.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.ConPVP +{ + public class BeginGump : Gump + { + 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; + private const int BlackColor32 = 0x000008; + + public BeginGump( int count ) : base( 50, 50 ) + { + AddPage( 0 ); + + const int offset = 50; + + AddBackground( 1, 1, 398, 202-offset, 3600 ); + + AddImageTiled( 16, 15, 369, 173-offset, 3604 ); + AddAlphaRegion( 16, 15, 369, 173-offset ); + + AddImage( 215, -43, 0xEE40 ); + + AddHtml( 22-1, 22, 294, 20, Color( Center( "Duel Countdown" ), BlackColor32 ), false, false ); + AddHtml( 22+1, 22, 294, 20, Color( Center( "Duel Countdown" ), BlackColor32 ), false, false ); + AddHtml( 22, 22-1, 294, 20, Color( Center( "Duel Countdown" ), BlackColor32 ), false, false ); + AddHtml( 22, 22+1, 294, 20, Color( Center( "Duel Countdown" ), BlackColor32 ), false, false ); + AddHtml( 22, 22, 294, 20, Color( Center( "Duel Countdown" ), LabelColor32 ), false, false ); + + AddHtml( 22-1, 50, 294, 80, Color( "The arranged duel is about to begin. During this countdown period you may not cast spells and you may not move. This message will close automatically when the period ends.", BlackColor32 ), false, false ); + AddHtml( 22+1, 50, 294, 80, Color( "The arranged duel is about to begin. During this countdown period you may not cast spells and you may not move. This message will close automatically when the period ends.", BlackColor32 ), false, false ); + AddHtml( 22, 50-1, 294, 80, Color( "The arranged duel is about to begin. During this countdown period you may not cast spells and you may not move. This message will close automatically when the period ends.", BlackColor32 ), false, false ); + AddHtml( 22, 50+1, 294, 80, Color( "The arranged duel is about to begin. During this countdown period you may not cast spells and you may not move. This message will close automatically when the period ends.", BlackColor32 ), false, false ); + AddHtml( 22, 50, 294, 80, Color( "The arranged duel is about to begin. During this countdown period you may not cast spells and you may not move. This message will close automatically when the period ends.", 0xFFCC66 ), false, false ); + + /*AddImageTiled( 32, 128, 264, 1, 9107 ); + AddImageTiled( 42, 130, 264, 1, 9157 ); + + AddHtml( 60-1, 140, 250, 20, Color( String.Format( "Duel will begin in {0} second{1}.", count, count==1?"":"s", BlackColor32 ), BlackColor32 ), false, false ); + AddHtml( 60+1, 140, 250, 20, Color( String.Format( "Duel will begin in {0} second{1}.", count, count==1?"":"s", BlackColor32 ), BlackColor32 ), false, false ); + AddHtml( 60, 140-1, 250, 20, Color( String.Format( "Duel will begin in {0} second{1}.", count, count==1?"":"s", BlackColor32 ), BlackColor32 ), false, false ); + AddHtml( 60, 140+1, 250, 20, Color( String.Format( "Duel will begin in {0} second{1}.", count, count==1?"":"s", BlackColor32 ), BlackColor32 ), false, false ); + AddHtml( 60, 140, 250, 20, Color( String.Format( "Duel will begin in {0} second{1}.", count, count==1?"":"s", 0x66AACC ), 0x66AACC ), false, false );*/ + + AddButton( 314-50, 157-offset, 247, 248, 1, GumpButtonType.Reply, 0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/DuelContext.cs b/Scripts/Engines/ConPVP/DuelContext.cs new file mode 100644 index 000000000..3d4c66238 --- /dev/null +++ b/Scripts/Engines/ConPVP/DuelContext.cs @@ -0,0 +1,2623 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Server; +using Server.Commands; +using Server.Engines.PartySystem; +using Server.Factions; +using Server.Gumps; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Spells; +using Server.Spells.Bushido; +using Server.Spells.Chivalry; +using Server.Spells.Necromancy; +using Server.Spells.Ninjitsu; +using Server.Spells.Seventh; +using Server.Spells.Spellweaving; + +namespace Server.Engines.ConPVP +{ + public delegate void CountdownCallback( int count ); + + public class DuelContext + { + private Mobile m_Initiator; + private ArrayList m_Participants; + private Ruleset m_Ruleset; + private Arena m_Arena; + private bool m_Registered = true; + private bool m_Finished, m_Started; + + private bool m_ReadyWait; + private int m_ReadyCount; + + private bool m_Rematch; + + public bool Rematch{ get{ return m_Rematch; } } + + public bool ReadyWait{ get{ return m_ReadyWait; } } + public int ReadyCount{ get{ return m_ReadyCount; } } + + public bool Registered{ get{ return m_Registered; } } + public bool Finished{ get{ return m_Finished; } } + public bool Started{ get{ return m_Started; } } + + public Mobile Initiator{ get{ return m_Initiator; } } + public ArrayList Participants{ get{ return m_Participants; } } + public Ruleset Ruleset{ get{ return m_Ruleset; } } + public Arena Arena{ get{ return m_Arena; } } + + private bool CantDoAnything( Mobile mob ) + { + if ( m_EventGame != null ) + return m_EventGame.CantDoAnything( mob ); + else + return false; + } + + public static bool IsFreeConsume( Mobile mob ) + { + PlayerMobile pm = mob as PlayerMobile; + + if ( pm == null || pm.DuelContext == null || pm.DuelContext.m_EventGame == null ) + return false; + + return pm.DuelContext.m_EventGame.FreeConsume; + } + + public void DelayBounce( TimeSpan ts, Mobile mob, Container corpse ) + { + Timer.DelayCall( ts, new TimerStateCallback( DelayBounce_Callback ), new object[]{ mob, corpse } ); + } + + public static bool AllowSpecialMove( Mobile from, string name, SpecialMove move ) + { + PlayerMobile pm = from as PlayerMobile; + + if( pm == null ) + return true; + + DuelContext dc = pm.DuelContext; + + return (dc == null || dc.InstAllowSpecialMove( from, name, move )); + } + + public bool InstAllowSpecialMove( Mobile from, string name, SpecialMove move ) + { + + if ( !m_StartedBeginCountdown ) + return true; + + DuelPlayer pl = Find( from ); + + if ( pl == null || pl.Eliminated ) + return true; + + if ( CantDoAnything( from ) ) + return false; + + string title = null; + + if( move is NinjaMove ) + title = "Bushido"; + else if( move is SamuraiMove ) + title = "Ninjitsu"; + + + if ( title == null || name == null || m_Ruleset.GetOption( title, name ) ) + return true; + + from.SendMessage( "The dueling ruleset prevents you from using this move." ); + return false; + } + + public bool AllowSpellCast( Mobile from, Spell spell ) + { + if ( !m_StartedBeginCountdown ) + return true; + + DuelPlayer pl = Find( from ); + + if ( pl == null || pl.Eliminated ) + return true; + + if ( CantDoAnything( from ) ) + return false; + + string title = null, option = null; + + if( spell is ArcanistSpell ) + { + title = "Spellweaving"; + option = spell.Name; + } + else if ( spell is PaladinSpell ) + { + title = "Chivalry"; + option = spell.Name; + } + else if ( spell is NecromancerSpell ) + { + title = "Necromancy"; + option = spell.Name; + } + else if ( spell is NinjaSpell ) + { + title = "Ninjitsu"; + option = spell.Name; + } + else if ( spell is SamuraiSpell ) + { + title = "Bushido"; + option = spell.Name; + } + else if( spell is MagerySpell ) + { + switch( ((MagerySpell)spell).Circle ) + { + case SpellCircle.First: title = "1st Circle"; break; + case SpellCircle.Second: title = "2nd Circle"; break; + case SpellCircle.Third: title = "3rd Circle"; break; + case SpellCircle.Fourth: title = "4th Circle"; break; + case SpellCircle.Fifth: title = "5th Circle"; break; + case SpellCircle.Sixth: title = "6th Circle"; break; + case SpellCircle.Seventh: title = "7th Circle"; break; + case SpellCircle.Eighth: title = "8th Circle"; break; + } + + option = spell.Name; + } + else + { + title = "Other Spell"; + option = spell.Name; + } + + if ( title == null || option == null || m_Ruleset.GetOption( title, option ) ) + return true; + + from.SendMessage( "The dueling ruleset prevents you from casting this spell." ); + return false; + } + + public bool AllowItemEquip( Mobile from, Item item ) + { + if ( !m_StartedBeginCountdown ) + return true; + + DuelPlayer pl = Find( from ); + + if ( pl == null || pl.Eliminated ) + return true; + + if ( item is Dagger || CheckItemEquip( from, item ) ) + return true; + + from.SendMessage( "The dueling ruleset prevents you from equiping this item." ); + return false; + } + + public static bool AllowSpecialAbility( Mobile from, string name, bool message ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return true; + + DuelContext dc = pm.DuelContext; + + return ( dc == null || dc.InstAllowSpecialAbility( from, name, message ) ); + } + + public bool InstAllowSpecialAbility( Mobile from, string name, bool message ) + { + if ( !m_StartedBeginCountdown ) + return true; + + DuelPlayer pl = Find( from ); + + if ( pl == null || pl.Eliminated ) + return true; + + if ( CantDoAnything( from ) ) + return false; + + if ( m_Ruleset.GetOption( "Combat Abilities", name ) ) + return true; + + if ( message ) + from.SendMessage( "The dueling ruleset prevents you from using this combat ability." ); + + return false; + } + + public bool CheckItemEquip( Mobile from, Item item ) + { + if ( item is Fists ) + { + if ( !m_Ruleset.GetOption( "Weapons", "Wrestling" ) ) + return false; + } + else if ( item is BaseArmor ) + { + BaseArmor armor = (BaseArmor)item; + + if ( armor.ProtectionLevel > ArmorProtectionLevel.Regular && !m_Ruleset.GetOption( "Armor", "Magical" ) ) + return false; + + if ( !Core.AOS && armor.Resource != armor.DefaultResource && !m_Ruleset.GetOption( "Armor", "Colored" ) ) + return false; + + if ( armor is BaseShield && !m_Ruleset.GetOption( "Armor", "Shields" ) ) + return false; + } + else if ( item is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)item; + + if ( (weapon.DamageLevel > WeaponDamageLevel.Regular || weapon.AccuracyLevel > WeaponAccuracyLevel.Regular) && !m_Ruleset.GetOption( "Weapons", "Magical" ) ) + return false; + + if ( !Core.AOS && weapon.Resource != CraftResource.Iron && weapon.Resource != CraftResource.None && !m_Ruleset.GetOption( "Weapons", "Runics" ) ) + return false; + + if ( weapon is BaseRanged && !m_Ruleset.GetOption( "Weapons", "Ranged" ) ) + return false; + + if ( !(weapon is BaseRanged) && !m_Ruleset.GetOption( "Weapons", "Melee" ) ) + return false; + + if ( weapon.PoisonCharges > 0 && weapon.Poison != null && !m_Ruleset.GetOption( "Weapons", "Poisoned" ) ) + return false; + } + + return true; + } + + public bool AllowSkillUse( Mobile from, SkillName skill ) + { + if ( !m_StartedBeginCountdown ) + return true; + + DuelPlayer pl = Find( from ); + + if ( pl == null || pl.Eliminated ) + return true; + + if ( CantDoAnything( from ) ) + return false; + + int id = (int)skill; + + if ( id >= 0 && id < SkillInfo.Table.Length ) + { + if ( m_Ruleset.GetOption( "Skills", SkillInfo.Table[id].Name ) ) + return true; + } + + from.SendMessage( "The dueling ruleset prevents you from using this skill." ); + return false; + } + + public bool AllowItemUse( Mobile from, Item item ) + { + if ( !m_StartedBeginCountdown ) + return true; + + DuelPlayer pl = Find( from ); + + if ( pl == null || pl.Eliminated ) + return true; + + if ( item is EtherealMount ) + { + from.SendMessage( "You may not mount an ethereal while dueling." ); + return false; + } + + string title = null, option = null; + + if ( item is BasePotion ) + { + title = "Potions"; + + if ( item is BaseAgilityPotion ) + option = "Agility"; + else if ( item is BaseCurePotion ) + option = "Cure"; + else if ( item is BaseHealPotion ) + option = "Heal"; + else if ( item is NightSightPotion ) + option = "Nightsight"; + else if ( item is BasePoisonPotion ) + option = "Poison"; + else if ( item is BaseStrengthPotion ) + option = "Strength"; + else if ( item is BaseExplosionPotion ) + option = "Explosion"; + else if ( item is BaseRefreshPotion ) + option = "Refresh"; + } + else if ( item is Bandage ) + { + title = "Items"; + option = "Bandages"; + } + else if ( item is TrapableContainer ) + { + if ( ((TrapableContainer)item).TrapType != TrapType.None ) + { + title = "Items"; + option = "Trapped Containers"; + } + } + else if ( item is Bola ) + { + title = "Items"; + option = "Bolas"; + } + else if ( item is OrangePetals ) + { + title = "Items"; + option = "Orange Petals"; + } + else if ( item is LeatherNinjaBelt ) + { + title = "Items"; + option = "Shurikens"; + } + else if ( item is Fukiya ) + { + title = "Items"; + option = "Fukiya Darts"; + } + else if ( item is FireHorn ) + { + title = "Items"; + option = "Fire Horns"; + } + + if ( title != null && option != null && m_StartedBeginCountdown && !m_Started ) + { + from.SendMessage( "You may not use this item before the duel begins." ); + return false; + } + else if ( item is BasePotion && !(item is BaseExplosionPotion) && !(item is BaseRefreshPotion) && IsSuddenDeath ) + { + from.SendMessage( 0x22, "You may not drink potions in sudden death." ); + return false; + } + else if ( item is Bandage && IsSuddenDeath ) + { + from.SendMessage( 0x22, "You may not use bandages in sudden death." ); + return false; + } + + if ( !(item is BaseRefreshPotion) ) + { + if ( CantDoAnything( from ) ) + return false; + } + + if ( title == null || option == null || m_Ruleset.GetOption( title, option ) ) + return true; + + from.SendMessage( "The dueling ruleset prevents you from using this item." ); + return false; + } + + private void DelayBounce_Callback( object state ) + { + object[] states = (object[])state; + Mobile mob = (Mobile) states[0]; + Container corpse = (Container) states[1]; + + RemoveAggressions( mob ); + SendOutside( mob ); + Refresh( mob, corpse ); + Debuff( mob ); + CancelSpell( mob ); + mob.Frozen = false; + } + + public void OnMapChanged( Mobile mob ) + { + OnLocationChanged( mob ); + } + + public void OnLocationChanged( Mobile mob ) + { + if ( !m_Registered || !m_StartedBeginCountdown || m_Finished ) + return; + + Arena arena = m_Arena; + + if ( arena == null ) + return; + + if ( mob.Map == arena.Facet && arena.Bounds.Contains( mob.Location ) ) + return; + + DuelPlayer pl = Find( mob ); + + if ( pl == null || pl.Eliminated ) + return; + + if ( mob.Map == Map.Internal ) { + // they've logged out + + if ( mob.LogoutMap == arena.Facet && arena.Bounds.Contains( mob.LogoutLocation ) ) { + // they logged out inside the arena.. set them to eject on login + + mob.LogoutLocation = arena.Outside; + } + } + + pl.Eliminated = true; + + mob.LocalOverheadMessage( MessageType.Regular, 0x22, false, "You have forfeited your position in the duel." ); + mob.NonlocalOverheadMessage( MessageType.Regular, 0x22, false, String.Format( "{0} has forfeited by leaving the dueling arena.", mob.Name ) ); + + Participant winner = CheckCompletion(); + + if ( winner != null ) + Finish( winner ); + } + + private bool m_Yielding; + + public void OnDeath( Mobile mob, Container corpse ) + { + if ( !m_Registered || !m_Started ) + return; + + DuelPlayer pl = Find( mob ); + + if ( pl != null && !pl.Eliminated ) + { + if ( m_EventGame != null && !m_EventGame.OnDeath( mob, corpse ) ) + return; + + pl.Eliminated = true; + + Requip( mob, corpse ); + DelayBounce( TimeSpan.FromSeconds( 4.0 ), mob, corpse ); + + Participant winner = CheckCompletion(); + + if ( winner != null ) + { + Finish( winner ); + } + else if ( !m_Yielding ) + { + mob.LocalOverheadMessage( MessageType.Regular, 0x22, false, "You have been defeated." ); + mob.NonlocalOverheadMessage( MessageType.Regular, 0x22, false, String.Format( "{0} has been defeated.", mob.Name ) ); + } + } + } + + public bool CheckFull() + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + if ( p.HasOpenSlot ) + return false; + } + + return true; + } + + public void Requip( Mobile from, Container cont ) + { + Corpse corpse = cont as Corpse; + + if ( corpse == null ) + return; + + List items = new List( corpse.Items ); + + bool gathered = false; + bool didntFit = false; + + Container pack = from.Backpack; + + for ( int i = 0; !didntFit && i < items.Count; ++i ) + { + Item item = items[i]; + Point3D loc = item.Location; + + if ( (item.Layer == Layer.Hair || item.Layer == Layer.FacialHair) || !item.Movable ) + continue; + + if ( pack != null ) + { + pack.DropItem( item ); + gathered = true; + } + else + { + didntFit = true; + } + } + + corpse.Carved = true; + + if ( corpse.ItemID == 0x2006 ) + { + corpse.ProcessDelta(); + corpse.SendRemovePacket(); + corpse.ItemID = Utility.Random( 0xECA, 9 ); // bone graphic + corpse.Hue = 0; + corpse.ProcessDelta(); + + Mobile killer = from.FindMostRecentDamager( false ); + + if ( killer != null && killer.Player ) + killer.AddToBackpack( new Head( m_Tournament == null ? HeadType.Duel : HeadType.Tournament, from.Name ) ); + } + + from.PlaySound( 0x3E3 ); + + if ( gathered && !didntFit ) + from.SendLocalizedMessage( 1062471 ); // You quickly gather all of your belongings. + else if ( gathered && didntFit ) + from.SendLocalizedMessage( 1062472 ); // You gather some of your belongings. The rest remain on the corpse. + } + + public void Refresh( Mobile mob, Container cont ) + { + if ( !mob.Alive ) + { + mob.Resurrect(); + + DeathRobe robe = mob.FindItemOnLayer( Layer.OuterTorso ) as DeathRobe; + + if ( robe != null ) + robe.Delete(); + + if ( cont is Corpse ) + { + Corpse corpse = (Corpse) cont; + + for ( int i = 0; i < corpse.EquipItems.Count; ++i ) + { + Item item = corpse.EquipItems[i]; + + if ( item.Movable && item.Layer != Layer.Hair && item.Layer != Layer.FacialHair && item.IsChildOf( mob.Backpack ) ) + mob.EquipItem( item ); + } + } + } + + mob.Hits = mob.HitsMax; + mob.Stam = mob.StamMax; + mob.Mana = mob.ManaMax; + + mob.Poison = null; + } + + public void SendOutside( Mobile mob ) + { + if ( m_Arena == null ) + return; + + mob.Combatant = null; + mob.MoveToWorld( m_Arena.Outside, m_Arena.Facet ); + } + + private Point3D m_GatePoint; + private Map m_GateFacet; + + public void Finish( Participant winner ) + { + if ( m_Finished ) + return; + + EndAutoTie(); + StopSDTimers(); + + m_Finished = true; + + for ( int i = 0; i < winner.Players.Length; ++i ) + { + DuelPlayer pl = winner.Players[i]; + + if ( pl != null && !pl.Eliminated ) + DelayBounce( TimeSpan.FromSeconds( 8.0 ), pl.Mobile, null ); + } + + winner.Broadcast( 0x59, null, winner.Players.Length == 1 ? "{0} has won the duel." : "{0} and {1} team have won the duel.", winner.Players.Length == 1 ? "You have won the duel." : "Your team has won the duel." ); + + if ( m_Tournament != null && winner.TournyPart != null ) + { + m_Match.Winner = winner.TournyPart; + winner.TournyPart.WonMatch( m_Match ); + m_Tournament.HandleWon( m_Arena, m_Match, winner.TournyPart ); + } + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant loser = (Participant)m_Participants[i]; + + if ( loser != winner ) + { + loser.Broadcast( 0x22, null, loser.Players.Length == 1 ? "{0} has lost the duel." : "{0} and {1} team have lost the duel.", loser.Players.Length == 1 ? "You have lost the duel." : "Your team has lost the duel." ); + + if ( m_Tournament != null && loser.TournyPart != null ) + loser.TournyPart.LostMatch( m_Match ); + } + + for ( int j = 0; j < loser.Players.Length; ++j ) + { + if ( loser.Players[j] != null ) + { + RemoveAggressions( loser.Players[j].Mobile ); + loser.Players[j].Mobile.Delta( MobileDelta.Noto ); + loser.Players[j].Mobile.CloseGump( typeof( BeginGump ) ); + + if ( m_Tournament != null ) + loser.Players[j].Mobile.SendEverything(); + } + } + } + + if ( IsOneVsOne ) + { + DuelPlayer dp1 = ((Participant)m_Participants[0]).Players[0]; + DuelPlayer dp2 = ((Participant)m_Participants[1]).Players[0]; + + if ( dp1 != null && dp2 != null ) + { + Award( dp1.Mobile, dp2.Mobile, dp1.Participant == winner ); + Award( dp2.Mobile, dp1.Mobile, dp2.Participant == winner ); + } + } + + if ( m_EventGame != null ) + m_EventGame.OnStop(); + + Timer.DelayCall( TimeSpan.FromSeconds( 9.0 ), new TimerCallback( UnregisterRematch ) ); + } + + public void Award( Mobile us, Mobile them, bool won ) + { + Ladder ladder = ( m_Arena == null ? Ladder.Instance : m_Arena.AcquireLadder() ); + + if ( ladder == null ) + return; + + LadderEntry ourEntry = ladder.Find( us ); + LadderEntry theirEntry = ladder.Find( them ); + + if ( ourEntry == null || theirEntry == null ) + return; + + int xpGain = Ladder.GetExperienceGain( ourEntry, theirEntry, won ); + + if ( xpGain == 0 ) + return; + + if ( m_Tournament != null ) + xpGain *= ( xpGain > 0 ? 5 : 2 ); + + if ( won ) + ++ourEntry.Wins; + else + ++ourEntry.Losses; + + int oldLevel = Ladder.GetLevel( ourEntry.Experience ); + + ourEntry.Experience += xpGain; + + if ( ourEntry.Experience < 0 ) + ourEntry.Experience = 0; + + ladder.UpdateEntry( ourEntry ); + + int newLevel = Ladder.GetLevel( ourEntry.Experience ); + + if ( newLevel > oldLevel ) + us.SendMessage( 0x59, "You have achieved level {0}!", newLevel ); + else if ( newLevel < oldLevel ) + us.SendMessage( 0x22, "You have lost a level. You are now at {0}.", newLevel ); + } + + public void UnregisterRematch() + { + Unregister(true); + } + + public void Unregister() + { + Unregister(false); + } + + public void Unregister( bool queryRematch ) + { + DestroyWall(); + + if ( !m_Registered ) + return; + + m_Registered = false; + + if ( m_Arena != null ) + m_Arena.Evict(); + + StopSDTimers(); + + Type[] types = new Type[]{ typeof( BeginGump ), typeof( DuelContextGump ), typeof( ParticipantGump ), typeof( PickRulesetGump ), typeof( ReadyGump ), typeof( ReadyUpGump ), typeof( RulesetGump ) }; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = (DuelPlayer)p.Players[j]; + + if ( pl == null ) + continue; + + if ( pl.Mobile is PlayerMobile ) + ((PlayerMobile)pl.Mobile).DuelPlayer = null; + + for ( int k = 0; k < types.Length; ++k ) + pl.Mobile.CloseGump( types[k] ); + } + } + + if ( queryRematch && m_Tournament == null ) + QueryRematch(); + } + + public void QueryRematch() + { + DuelContext dc = new DuelContext( m_Initiator, m_Ruleset.Layout, false ); + + dc.m_Ruleset = m_Ruleset; + dc.m_Rematch = true; + + dc.m_Participants.Clear(); + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant oldPart = (Participant)m_Participants[i]; + Participant newPart = new Participant( dc, oldPart.Players.Length ); + + for ( int j = 0; j < oldPart.Players.Length; ++j ) + { + DuelPlayer oldPlayer = oldPart.Players[j]; + + if ( oldPlayer != null ) + newPart.Players[j] = new DuelPlayer( oldPlayer.Mobile, newPart ); + } + + dc.m_Participants.Add( newPart ); + } + + dc.CloseAllGumps(); + dc.SendReadyUpGump(); + } + + public DuelPlayer Find( Mobile mob ) + { + if ( mob is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)mob; + + if ( pm.DuelContext == this ) + return pm.DuelPlayer; + + return null; + } + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + DuelPlayer pl = p.Find( mob ); + + if ( pl != null ) + return pl; + } + + return null; + } + + public bool IsAlly( Mobile m1, Mobile m2 ) + { + DuelPlayer pl1 = Find( m1 ); + DuelPlayer pl2 = Find( m2 ); + + return ( pl1 != null && pl2 != null && pl1.Participant == pl2.Participant ); + } + + public Participant CheckCompletion() + { + Participant winner = null; + + bool hasWinner = false; + int eliminated = 0; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + if ( p.Eliminated ) + { + ++eliminated; + + if ( eliminated == (m_Participants.Count - 1) ) + hasWinner = true; + } + else + { + winner = p; + } + } + + if ( hasWinner ) + return winner == null ? (Participant) m_Participants[0] : winner; + + return null; + } + + private Timer m_Countdown; + + public void StartCountdown( int count, CountdownCallback cb ) + { + cb(count); + m_Countdown=Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ), count, new TimerStateCallback( Countdown_Callback ), new object[]{ count-1, cb } ); + } + + public void StopCountdown() + { + if ( m_Countdown != null ) + m_Countdown.Stop(); + + m_Countdown = null; + } + + private void Countdown_Callback( object state ) + { + object[] states = (object[])state; + + int count = (int)states[0]; + CountdownCallback cb = (CountdownCallback)states[1]; + + if ( count==0 ) + { + if ( m_Countdown != null ) + m_Countdown.Stop(); + + m_Countdown=null; + } + + cb( count ); + + states[0] = count - 1; + } + + private Timer m_AutoTieTimer; + private bool m_Tied; + + public bool Tied{ get{ return m_Tied; } } + + private bool m_IsSuddenDeath; + + public bool IsSuddenDeath{ get{ return m_IsSuddenDeath; } set{ m_IsSuddenDeath = value; } } + + private Timer m_SDWarnTimer, m_SDActivateTimer; + + public void StopSDTimers() + { + if ( m_SDWarnTimer != null ) + m_SDWarnTimer.Stop(); + + m_SDWarnTimer = null; + + if ( m_SDActivateTimer != null ) + m_SDActivateTimer.Stop(); + + m_SDActivateTimer = null; + } + + public void StartSuddenDeath( TimeSpan timeUntilActive ) + { + if ( m_SDWarnTimer != null ) + m_SDWarnTimer.Stop(); + + m_SDWarnTimer = Timer.DelayCall( TimeSpan.FromMinutes( timeUntilActive.TotalMinutes * 0.9 ), new TimerCallback( WarnSuddenDeath ) ); + + if ( m_SDActivateTimer != null ) + m_SDActivateTimer.Stop(); + + m_SDActivateTimer = Timer.DelayCall( timeUntilActive, new TimerCallback( ActivateSuddenDeath ) ); + } + + public void WarnSuddenDeath() + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl == null || pl.Eliminated ) + continue; + + pl.Mobile.SendSound( 0x1E1 ); + pl.Mobile.SendMessage( 0x22, "Warning! Warning! Warning!" ); + pl.Mobile.SendMessage( 0x22, "Sudden death will be active soon!" ); + } + } + + if ( m_Tournament != null ) + m_Tournament.Alert( m_Arena, "Sudden death will be active soon!" ); + + if ( m_SDWarnTimer != null ) + m_SDWarnTimer.Stop(); + + m_SDWarnTimer = null; + } + + public static bool CheckSuddenDeath( Mobile mob ) + { + if ( mob is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)mob; + + if ( pm.DuelPlayer != null && !pm.DuelPlayer.Eliminated && pm.DuelContext != null && pm.DuelContext.IsSuddenDeath ) + return true; + } + + return false; + } + + public void ActivateSuddenDeath() + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl == null || pl.Eliminated ) + continue; + + pl.Mobile.SendSound( 0x1E1 ); + pl.Mobile.SendMessage( 0x22, "Warning! Warning! Warning!" ); + pl.Mobile.SendMessage( 0x22, "Sudden death has ACTIVATED. You are now unable to perform any beneficial actions." ); + } + } + + if ( m_Tournament != null ) + m_Tournament.Alert( m_Arena, "Sudden death has been activated!" ); + + m_IsSuddenDeath = true; + + if ( m_SDActivateTimer != null ) + m_SDActivateTimer.Stop(); + + m_SDActivateTimer = null; + } + + public void BeginAutoTie() + { + if ( m_AutoTieTimer != null ) + m_AutoTieTimer.Stop(); + + TimeSpan ts = ( m_Tournament == null || m_Tournament.TournyType == TournyType.Standard ) + ? AutoTieDelay + : TimeSpan.FromMinutes( 90.0 ); + + m_AutoTieTimer = Timer.DelayCall( ts, new TimerCallback( InvokeAutoTie ) ); + } + + public void EndAutoTie() + { + if ( m_AutoTieTimer != null ) + m_AutoTieTimer.Stop(); + + m_AutoTieTimer = null; + } + + public void InvokeAutoTie() + { + m_AutoTieTimer = null; + + if ( !m_Started || m_Finished ) + return; + + m_Tied = true; + m_Finished = true; + + StopSDTimers(); + + ArrayList remaining = new ArrayList(); + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + if ( p.Eliminated ) + { + p.Broadcast( 0x22, null, p.Players.Length == 1 ? "{0} has lost the duel." : "{0} and {1} team have lost the duel.", p.Players.Length == 1 ? "You have lost the duel." : "Your team has lost the duel." ); + } + else + { + p.Broadcast( 0x59, null, p.Players.Length == 1 ? "{0} has tied the duel due to time expiration." : "{0} and {1} team have tied the duel due to time expiration.", p.Players.Length == 1 ? "You have tied the duel due to time expiration." : "Your team has tied the duel due to time expiration." ); + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl != null && !pl.Eliminated ) + DelayBounce( TimeSpan.FromSeconds( 8.0 ), pl.Mobile, null ); + } + + if ( p.TournyPart != null ) + remaining.Add( p.TournyPart ); + } + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl != null ) + { + pl.Mobile.Delta( MobileDelta.Noto ); + pl.Mobile.SendEverything(); + } + } + } + + if ( m_Tournament != null ) + m_Tournament.HandleTie( m_Arena, m_Match, remaining ); + + Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), new TimerCallback( Unregister ) ); + } + + public bool IsOneVsOne + { + get + { + if ( m_Participants.Count != 2 ) + return false; + + if ( ((Participant)m_Participants[0]).Players.Length != 1 ) + return false; + + if ( ((Participant)m_Participants[1]).Players.Length != 1 ) + return false; + + return true; + } + } + + public static void Initialize() + { + EventSink.Speech += new SpeechEventHandler( EventSink_Speech ); + EventSink.Login += new LoginEventHandler( EventSink_Login ); + + CommandSystem.Register( "vli", AccessLevel.GameMaster, new CommandEventHandler( vli_oc ) ); + } + + private static void vli_oc( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, false, Targeting.TargetFlags.None, new TargetCallback( vli_ot ) ); + } + + private static void vli_ot( Mobile from, object obj ) + { + if ( obj is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)obj; + + Ladder ladder = Ladder.Instance; + + if ( ladder == null ) + return; + + LadderEntry entry = ladder.Find( pm ); + + if ( entry != null ) + from.SendGump( new PropertiesGump( from, entry ) ); + } + } + + private static TimeSpan CombatDelay = TimeSpan.FromSeconds( 30.0 ); + private static TimeSpan AutoTieDelay = TimeSpan.FromMinutes( 15.0 ); + + public static bool CheckCombat( Mobile m ) + { + for ( int i = 0; i < m.Aggressed.Count; ++i ) + { + AggressorInfo info = m.Aggressed[i]; + + if ( info.Defender.Player && (DateTime.Now - info.LastCombatTime) < CombatDelay ) + return true; + } + + for ( int i = 0; i < m.Aggressors.Count; ++i ) + { + AggressorInfo info = m.Aggressors[i]; + + if ( info.Attacker.Player && (DateTime.Now - info.LastCombatTime) < CombatDelay ) + return true; + } + + return false; + } + + private static void EventSink_Login( LoginEventArgs e ) + { + PlayerMobile pm = e.Mobile as PlayerMobile; + + if ( pm == null ) + return; + + DuelContext dc = pm.DuelContext; + + if ( dc == null ) + return; + + if ( dc.ReadyWait && pm.DuelPlayer.Ready && !dc.Started && !dc.StartedBeginCountdown && !dc.Finished ) + { + if ( dc.m_Tournament == null ) + pm.SendGump( new ReadyGump( pm, dc, dc.m_ReadyCount ) ); + } + else if ( dc.ReadyWait && !dc.StartedBeginCountdown && !dc.Started && !dc.Finished ) + { + if ( dc.m_Tournament == null ) + pm.SendGump( new ReadyUpGump( pm, dc ) ); + } + else if ( dc.Initiator == pm && !dc.ReadyWait && !dc.StartedBeginCountdown && !dc.Started && !dc.Finished ) + pm.SendGump( new DuelContextGump( pm, dc ) ); + } + + private static void ViewLadder_OnTarget( Mobile from, object obj, object state ) + { + if ( obj is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)obj; + Ladder ladder = (Ladder)state; + + LadderEntry entry = ladder.Find( pm ); + + if ( entry == null ) + return; // sanity + + string text = String.Format( "{{0}} are ranked {0} at level {1}.", LadderGump.Rank( entry.Index + 1 ), Ladder.GetLevel( entry.Experience ) ); + + pm.PrivateOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( text, from==pm?"You":"They" ), from.NetState ); + } + else if ( obj is Mobile ) + { + Mobile mob = (Mobile)obj; + + if ( mob.Body.IsHuman ) + mob.PrivateOverheadMessage( MessageType.Regular, mob.SpeechHue, false, "I'm not a duelist, and quite frankly, I resent the implication.", from.NetState ); + else + mob.PrivateOverheadMessage( MessageType.Regular, 0x3B2, true, "It's probably better than you.", from.NetState ); + } + else + { + from.SendMessage( "That's not a player." ); + } + } + + private static void EventSink_Speech( SpeechEventArgs e ) + { + if ( e.Handled ) + return; + + PlayerMobile pm = e.Mobile as PlayerMobile; + + if ( pm == null ) + return; + + if ( Insensitive.Contains( e.Speech, "i wish to duel" ) ) + { + if ( !pm.CheckAlive() ) + { + } + else if ( pm.Region.IsPartOf( typeof( Regions.Jail ) ) ) + { + } + else if ( CheckCombat( pm ) ) + { + e.Mobile.SendMessage( 0x22, "You have recently been in combat with another player and must wait before starting a duel." ); + } + else if ( pm.DuelContext != null ) + { + if ( pm.DuelContext.Initiator == pm ) + e.Mobile.SendMessage( 0x22, "You have already started a duel." ); + else + e.Mobile.SendMessage( 0x22, "You have already been challenged in a duel." ); + } + else if ( TournamentController.IsActive ) + { + e.Mobile.SendMessage( 0x22, "You may not start a duel while a tournament is active." ); + } + else + { + pm.SendGump( new DuelContextGump( pm, new DuelContext( pm, RulesetLayout.Root ) ) ); + e.Handled = true; + } + } + else if ( Insensitive.Equals( e.Speech, "change arena preferences" ) ) + { + if ( !pm.CheckAlive() ) + { + } + else + { + Preferences prefs = Preferences.Instance; + + if ( prefs != null ) + { + e.Mobile.CloseGump( typeof( PreferencesGump ) ); + e.Mobile.SendGump( new PreferencesGump( e.Mobile, prefs ) ); + } + } + } + else if ( Insensitive.Equals( e.Speech, "showladder" ) ) + { + e.Blocked=true; + if ( !pm.CheckAlive() ) + { + } + else + { + Ladder instance = Ladder.Instance; + + if ( instance == null ) + { + //pm.SendMessage( "Ladder not yet initialized." ); + } + else + { + LadderEntry entry = instance.Find( pm ); + + if ( entry == null ) + return; // sanity + + string text = String.Format( "{{0}} {{1}} ranked {0} at level {1}.", LadderGump.Rank( entry.Index + 1 ), Ladder.GetLevel( entry.Experience ) ); + + pm.LocalOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( text, "You", "are" ) ); + pm.NonlocalOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( text, pm.Name, "is" ) ); + + //pm.PublicOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( "Level {0} with {1} win{2} and {3} loss{4}.", Ladder.GetLevel( entry.Experience ), entry.Wins, entry.Wins==1?"":"s", entry.Losses, entry.Losses==1?"":"es" ) ); + //pm.PublicOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( "Level {0} with {1} win{2} and {3} loss{4}.", Ladder.GetLevel( entry.Experience ), entry.Wins, entry.Wins==1?"":"s", entry.Losses, entry.Losses==1?"":"es" ) ); + } + } + } + else if ( Insensitive.Equals( e.Speech, "viewladder" ) ) + { + e.Blocked=true; + + if ( !pm.CheckAlive() ) + { + } + else + { + Ladder instance = Ladder.Instance; + + if ( instance == null ) + { + //pm.SendMessage( "Ladder not yet initialized." ); + } + else + { + pm.SendMessage( "Target a player to view their ranking and level." ); + pm.BeginTarget( 16, false, Targeting.TargetFlags.None, new TargetStateCallback( ViewLadder_OnTarget ), instance ); + } + } + } + else if ( Insensitive.Contains( e.Speech, "i yield" ) ) + { + if ( !pm.CheckAlive() ) + { + } + else if ( pm.DuelContext == null ) + { + } + else if ( pm.DuelContext.Finished ) + { + e.Mobile.SendMessage( 0x22, "The duel is already finished." ); + } + else if ( !pm.DuelContext.Started ) + { + DuelContext dc = pm.DuelContext; + Mobile init = dc.Initiator; + + if ( pm.DuelContext.StartedBeginCountdown ) + { + e.Mobile.SendMessage( 0x22, "The duel has not yet started." ); + } + else + { + DuelPlayer pl = pm.DuelContext.Find( pm ); + + if ( pl == null ) + return; + + Participant p = pl.Participant; + + if ( !pm.DuelContext.ReadyWait ) // still setting stuff up + { + p.Broadcast( 0x22, null, "{0} has yielded.", "You have yielded." ); + + if ( init == pm ) + { + dc.Unregister(); + } + else + { + p.Nullify( pl ); + pm.DuelPlayer=null; + + NetState ns = init.NetState; + + if ( ns != null ) + { + foreach ( Gump g in ns.Gumps ) + { + if ( g is ParticipantGump ) + { + ParticipantGump pg = (ParticipantGump)g; + + if ( pg.Participant == p ) + { + init.SendGump( new ParticipantGump( init, dc, p ) ); + break; + } + } + else if ( g is DuelContextGump ) + { + DuelContextGump dcg = (DuelContextGump)g; + + if ( dcg.Context == dc ) + { + init.SendGump( new DuelContextGump( init, dc ) ); + break; + } + } + } + } + } + } + else if ( !pm.DuelContext.StartedReadyCountdown ) // at ready stage + { + p.Broadcast( 0x22, null, "{0} has yielded.", "You have yielded." ); + + dc.m_Yielding=true; + dc.RejectReady( pm, null ); + dc.m_Yielding=false; + + if ( init == pm ) + { + dc.Unregister(); + } + else if ( dc.m_Registered ) + { + p.Nullify( pl ); + pm.DuelPlayer=null; + + NetState ns = init.NetState; + + if ( ns != null ) + { + bool send=true; + + foreach ( Gump g in ns.Gumps ) + { + if ( g is ParticipantGump ) + { + ParticipantGump pg = (ParticipantGump)g; + + if ( pg.Participant == p ) + { + init.SendGump( new ParticipantGump( init, dc, p ) ); + send=false; + break; + } + } + else if ( g is DuelContextGump ) + { + DuelContextGump dcg = (DuelContextGump)g; + + if ( dcg.Context == dc ) + { + init.SendGump( new DuelContextGump( init, dc ) ); + send=false; + break; + } + } + } + + if ( send ) + init.SendGump( new DuelContextGump( init, dc ) ); + } + } + } + else + { + if ( pm.DuelContext.m_Countdown != null ) + pm.DuelContext.m_Countdown.Stop(); + pm.DuelContext.m_Countdown= null; + + pm.DuelContext.m_StartedReadyCountdown=false; + p.Broadcast( 0x22, null, "{0} has yielded.", "You have yielded." ); + + dc.m_Yielding=true; + dc.RejectReady( pm, null ); + dc.m_Yielding=false; + + if ( init == pm ) + { + dc.Unregister(); + } + else if ( dc.m_Registered ) + { + p.Nullify( pl ); + pm.DuelPlayer=null; + + NetState ns = init.NetState; + + if ( ns != null ) + { + bool send=true; + + foreach ( Gump g in ns.Gumps ) + { + if ( g is ParticipantGump ) + { + ParticipantGump pg = (ParticipantGump)g; + + if ( pg.Participant == p ) + { + init.SendGump( new ParticipantGump( init, dc, p ) ); + send=false; + break; + } + } + else if ( g is DuelContextGump ) + { + DuelContextGump dcg = (DuelContextGump)g; + + if ( dcg.Context == dc ) + { + init.SendGump( new DuelContextGump( init, dc ) ); + send=false; + break; + } + } + } + + if ( send ) + init.SendGump( new DuelContextGump( init, dc ) ); + } + } + } + } + } + else + { + DuelPlayer pl = pm.DuelContext.Find( pm ); + + if ( pl != null ) + { + if ( pm.DuelContext.IsOneVsOne ) + { + e.Mobile.SendMessage( 0x22, "You may not yield a 1 on 1 match." ); + } + else if ( pl.Eliminated ) + { + e.Mobile.SendMessage( 0x22, "You have already been eliminated." ); + } + else + { + pm.LocalOverheadMessage( MessageType.Regular, 0x22, false, "You have yielded." ); + pm.NonlocalOverheadMessage( MessageType.Regular, 0x22, false, String.Format( "{0} has yielded.", pm.Name ) ); + + pm.DuelContext.m_Yielding=true; + pm.Kill(); + pm.DuelContext.m_Yielding=false; + + if ( pm.Alive ) // invul, ... + { + pl.Eliminated = true; + + pm.DuelContext.RemoveAggressions( pm ); + pm.DuelContext.SendOutside( pm ); + pm.DuelContext.Refresh( pm, null ); + Debuff( pm ); + CancelSpell( pm ); + pm.Frozen = false; + + Participant winner = pm.DuelContext.CheckCompletion(); + + if ( winner != null ) + pm.DuelContext.Finish( winner ); + } + } + } + else + { + e.Mobile.SendMessage( 0x22, "BUG: Unable to find duel context." ); + } + } + } + } + + public DuelContext( Mobile initiator, RulesetLayout layout ) : this( initiator, layout, true ) + { + } + + public DuelContext( Mobile initiator, RulesetLayout layout, bool addNew ) + { + m_Initiator = initiator; + m_Participants = new ArrayList(); + m_Ruleset = new Ruleset( layout ); + m_Ruleset.ApplyDefault( layout.Defaults[0] ); + + if ( addNew ) + { + m_Participants.Add( new Participant( this, 1 ) ); + m_Participants.Add( new Participant( this, 1 ) ); + + ((Participant)m_Participants[0]).Add( initiator ); + } + } + + public void CloseAllGumps() + { + Type[] types = new Type[]{ typeof( DuelContextGump ), typeof( ParticipantGump ), typeof( RulesetGump ) }; + int[] defs = new int[]{ -1, -1, -1 }; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl == null ) + continue; + + Mobile mob = pl.Mobile; + + for ( int k = 0; k < types.Length; ++k ) + mob.CloseGump( types[k] ); + //mob.CloseGump( types[k], defs[k] ); + } + } + } + + public void RejectReady( Mobile rejector, string page ) + { + if ( m_StartedReadyCountdown ) + return; // sanity + + Type[] types = new Type[]{ typeof( DuelContextGump ), typeof( ReadyUpGump ), typeof( ReadyGump ) }; + int[] defs = new int[]{ -1, -1, -1 }; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl == null ) + continue; + + pl.Ready = false; + + Mobile mob = pl.Mobile; + + if ( page == null ) // yield + { + if ( mob != rejector ) + mob.SendMessage( 0x22, "{0} has yielded.", rejector.Name ); + } + else + { + if ( mob == rejector ) + mob.SendMessage( 0x22, "You have rejected the {0}.", m_Rematch ? "rematch" : page ); + else + mob.SendMessage( 0x22, "{0} has rejected the {1}.", rejector.Name, m_Rematch ? "rematch" : page ); + } + + for ( int k = 0; k < types.Length; ++k ) + mob.CloseGump( types[k] ); + //mob.CloseGump( types[k], defs[k] ); + } + } + + if ( m_Rematch ) + Unregister(); + else if ( !m_Yielding ) + m_Initiator.SendGump( new DuelContextGump( m_Initiator, this ) ); + + m_ReadyWait = false; + m_ReadyCount = 0; + } + + public void SendReadyGump() + { + SendReadyGump( -1 ); + } + + public static void Debuff( Mobile mob ) + { + mob.RemoveStatMod( "[Magic] Str Offset" ); + mob.RemoveStatMod( "[Magic] Dex Offset" ); + mob.RemoveStatMod( "[Magic] Int Offset" ); + mob.RemoveStatMod( "Concussion" ); + + OrangePetals.RemoveContext( mob ); + + mob.Paralyzed = false; + mob.Hidden = false; + + if ( !Core.AOS ) + { + mob.MagicDamageAbsorb = 0; + mob.MeleeDamageAbsorb = 0; + Spells.Second.ProtectionSpell.Registry.Remove( mob ); + + mob.EndAction( typeof( DefensiveSpell ) ); + } + + TransformationSpellHelper.RemoveContext( mob, true ); + AnimalForm.RemoveContext( mob, true ); + + if( DisguiseTimers.IsDisguised( mob ) ) + DisguiseTimers.StopTimer( mob ); + + if( !mob.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + mob.BodyMod = 0; + mob.HueMod = -1; + mob.EndAction( typeof( PolymorphSpell ) ); + } + + BaseArmor.ValidateMobile( mob ); + BaseClothing.ValidateMobile( mob ); + + mob.Hits = mob.HitsMax; + mob.Stam = mob.StamMax; + mob.Mana = mob.ManaMax; + + mob.Poison = null; + } + + public static void CancelSpell( Mobile mob ) + { + if ( mob.Spell is Spells.Spell ) + ((Spells.Spell)mob.Spell).Disturb( Spells.DisturbType.Kill ); + + Targeting.Target.Cancel( mob ); + } + + private bool m_StartedBeginCountdown; + private bool m_StartedReadyCountdown; + + public bool StartedBeginCountdown{ get{ return m_StartedBeginCountdown; } } + public bool StartedReadyCountdown{ get{ return m_StartedReadyCountdown; } } + + private class InternalWall : Item + { + public InternalWall() : base( 0x80 ) + { + Movable = false; + } + + public void Appear( Point3D loc, Map map ) + { + MoveToWorld( loc, map ); + + Effects.SendLocationParticles( this, 0x376A, 9, 10, 5025 ); + } + + public InternalWall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + } + + private ArrayList m_Walls = new ArrayList(); + + public void DestroyWall() + { + for ( int i = 0; i < m_Walls.Count; ++i ) + ((Item)m_Walls[i]).Delete(); + + m_Walls.Clear(); + } + + public void CreateWall() + { + if ( m_Arena == null ) + return; + + Point3D start = m_Arena.Points.EdgeWest; + Point3D wall = m_Arena.Wall; + + int dx = start.X - wall.X; + int dy = start.Y - wall.Y; + int rx = dx - dy; + int ry = dx + dy; + + bool eastToWest; + + if ( rx >= 0 && ry >= 0 ) + eastToWest = false; + else if ( rx >= 0 ) + eastToWest = true; + else if ( ry >= 0 ) + eastToWest = true; + else + eastToWest = false; + + Effects.PlaySound( wall, m_Arena.Facet, 0x1F6 ); + + for ( int i = -1; i <= 1; ++i ) + { + Point3D loc = new Point3D( eastToWest ? wall.X + i : wall.X, eastToWest ? wall.Y : wall.Y + i, wall.Z ); + + InternalWall created = new InternalWall(); + + created.Appear( loc, m_Arena.Facet ); + + m_Walls.Add( created ); + } + } + + public void BuildParties() + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + if ( p.Players.Length > 1 ) + { + ArrayList players = new ArrayList(); + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer dp = p.Players[j]; + + if ( dp == null ) + continue; + + players.Add( dp.Mobile ); + } + + if ( players.Count > 1 ) + { + for ( int leaderIndex = 0; (leaderIndex+1) < players.Count; leaderIndex += Party.Capacity ) + { + Mobile leader = (Mobile) players[leaderIndex]; + Party party = Party.Get( leader ); + + if ( party == null ) + { + leader.Party = party = new Party( leader ); + } + else if ( party.Leader != leader ) + { + party.SendPublicMessage( leader, "I leave this party to fight in a duel." ); + party.Remove( leader ); + leader.Party = party = new Party( leader ); + } + + for ( int j = leaderIndex+1; j < players.Count && j < leaderIndex+Party.Capacity; ++j ) + { + Mobile player = (Mobile)players[j]; + Party existing = Party.Get( player ); + + if ( existing == party ) + continue; + + if ( (party.Members.Count + party.Candidates.Count) >= Party.Capacity ) + { + player.SendMessage( "You could not be added to the team party because it is at full capacity." ); + leader.SendMessage( "{0} could not be added to the team party because it is at full capacity." ); + } + else + { + if ( existing != null ) + { + existing.SendPublicMessage( player, "I leave this party to fight in a duel." ); + existing.Remove( player ); + } + + party.OnAccept( player, true ); + } + } + } + } + } + } + } + + public void ClearIllegalItems() + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl == null ) + continue; + + ClearIllegalItems( pl.Mobile ); + } + } + } + + public void ClearIllegalItems( Mobile mob ) + { + if ( mob.StunReady && !AllowSpecialAbility( mob, "Stun", false ) ) + mob.StunReady = false; + + if ( mob.DisarmReady && !AllowSpecialAbility( mob, "Disarm", false ) ) + mob.DisarmReady = false; + + Container pack = mob.Backpack; + + if ( pack == null ) + return; + + for ( int i = mob.Items.Count - 1; i >= 0; --i ) + { + if ( i >= mob.Items.Count ) + continue; // sanity + + Item item = mob.Items[i]; + + if ( !CheckItemEquip( mob, item ) ) + { + pack.DropItem( item ); + + if ( item is BaseWeapon ) + mob.SendLocalizedMessage( 1062001, item.Name == null ? "#" + item.LabelNumber.ToString() : item.Name ); // You can no longer wield your ~1_WEAPON~ + else if ( item is BaseArmor && !(item is BaseShield) ) + mob.SendLocalizedMessage( 1062002, item.Name == null ? "#" + item.LabelNumber.ToString() : item.Name ); // You can no longer wear your ~1_ARMOR~ + else + mob.SendLocalizedMessage( 1062003, item.Name == null ? "#" + item.LabelNumber.ToString() : item.Name ); // You can no longer equip your ~1_SHIELD~ + } + } + + Item inHand = mob.Holding; + + if ( inHand != null && !CheckItemEquip( mob, inHand ) ) + { + mob.Holding = null; + + BounceInfo bi = inHand.GetBounce(); + + if ( bi.m_Parent == mob ) + pack.DropItem( inHand ); + else + inHand.Bounce( mob ); + + inHand.ClearBounce(); + } + } + + public void SendBeginGump( int count ) + { + if ( !m_Registered || m_Finished ) + return; + + if ( count == 10 ) + { + CreateWall(); + BuildParties(); + ClearIllegalItems(); + } + else if ( count == 0 ) + { + DestroyWall(); + } + + m_StartedBeginCountdown = true; + + if ( count == 0 ) + { + m_Started = true; + BeginAutoTie(); + } + + Type[] types = new Type[]{ typeof( ReadyGump ), typeof( ReadyUpGump ), typeof( BeginGump ) }; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl == null ) + continue; + + Mobile mob = pl.Mobile; + + if ( count > 0 ) + { + if ( count == 10 ) + CloseAndSendGump( mob, new BeginGump( count ), types ); + + mob.Frozen = true; + } + else + { + mob.CloseGump( typeof( BeginGump ) ); + mob.Frozen = false; + } + } + } + } + + private ArrayList m_Entered = new ArrayList(); + + private class ReturnEntry + { + private Mobile m_Mobile; + private Point3D m_Location; + private Map m_Facet; + private DateTime m_Expire; + + public Mobile Mobile{ get{ return m_Mobile; } } + public Point3D Location{ get{ return m_Location; } } + public Map Facet{ get{ return m_Facet; } } + + public void Return() + { + if ( m_Facet == Map.Internal || m_Facet == null ) + return; + + if ( m_Mobile.Map == Map.Internal ) + { + m_Mobile.LogoutLocation = m_Location; + m_Mobile.LogoutMap = m_Facet; + } + else + { + m_Mobile.Location = m_Location; + m_Mobile.Map = m_Facet; + } + } + + public ReturnEntry( Mobile mob ) + { + m_Mobile = mob; + + Update(); + } + + public ReturnEntry( Mobile mob, Point3D loc, Map facet ) + { + m_Mobile = mob; + m_Location = loc; + m_Facet = facet; + m_Expire = DateTime.Now + TimeSpan.FromMinutes( 30.0 ); + } + + public bool Expired{ get{ return ( DateTime.Now >= m_Expire ); } } + + public void Update() + { + m_Expire = DateTime.Now + TimeSpan.FromMinutes( 30.0 ); + + if ( m_Mobile.Map == Map.Internal ) + { + m_Facet = m_Mobile.LogoutMap; + m_Location = m_Mobile.LogoutLocation; + } + else + { + m_Facet = m_Mobile.Map; + m_Location = m_Mobile.Location; + } + } + } + + private class ExitTeleporter : Item + { + private ArrayList m_Entries; + + public override string DefaultName + { + get { return "return teleporter"; } + } + + public ExitTeleporter() : base( 0x1822 ) + { + m_Entries = new ArrayList(); + + Hue = 0x482; + Movable = false; + } + + public void Register( Mobile mob ) + { + ReturnEntry entry = Find( mob ); + + if ( entry != null ) + { + entry.Update(); + return; + } + + m_Entries.Add( new ReturnEntry( mob ) ); + } + + private ReturnEntry Find( Mobile mob ) + { + for ( int i = 0; i < m_Entries.Count; ++i ) + { + ReturnEntry entry = (ReturnEntry)m_Entries[i]; + + if ( entry.Mobile == mob ) + return entry; + else if ( entry.Expired ) + m_Entries.RemoveAt( i-- ); + } + + return null; + } + + public override bool OnMoveOver( Mobile m ) + { + if ( !base.OnMoveOver( m ) ) + return false; + + ReturnEntry entry = Find( m ); + + if ( entry != null ) + { + entry.Return(); + + Effects.PlaySound( GetWorldLocation(), Map, 0x1FE ); + Effects.PlaySound( m.Location, m.Map, 0x1FE ); + + m_Entries.Remove( entry ); + + return false; + } + else + { + m.SendLocalizedMessage( 1049383 ); // The teleporter doesn't seem to work for you. + return true; + } + } + + public ExitTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.WriteEncodedInt( (int) m_Entries.Count ); + + for ( int i = 0; i < m_Entries.Count; ++i ) + { + ReturnEntry entry = (ReturnEntry)m_Entries[i]; + + writer.Write( (Mobile) entry.Mobile ); + writer.Write( (Point3D) entry.Location ); + writer.Write( (Map) entry.Facet ); + + if ( entry.Expired ) + m_Entries.RemoveAt( i-- ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + int count = reader.ReadEncodedInt(); + + m_Entries = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + Mobile mob = reader.ReadMobile(); + Point3D loc = reader.ReadPoint3D(); + Map map = reader.ReadMap(); + + m_Entries.Add( new ReturnEntry( mob, loc, map ) ); + } + + break; + } + } + } + } + + private class ArenaMoongate : ConfirmationMoongate + { + private ExitTeleporter m_Teleporter; + + public override string DefaultName + { + get { return "spectator moongate"; } + } + + public ArenaMoongate( Point3D target, Map map, ExitTeleporter tp ) : base( target, map ) + { + m_Teleporter = tp; + + ItemID = 0x1FD4; + Dispellable = false; + + GumpWidth = 300; + GumpHeight = 150; + MessageColor = 0xFFC000; + MessageString = "Are you sure you wish to spectate this duel?"; + TitleColor = 0x7800; + TitleNumber = 1062051; // Gate Warning + + Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), new TimerCallback( Delete ) ); + } + + public override void CheckGate(Mobile m, int range) + { + if ( DuelContext.CheckCombat( m ) ) + { + m.SendMessage( 0x22, "You have recently been in combat with another player and cannot use this moongate." ); + } + else + { + base.CheckGate( m, range ); + } + } + + public override void UseGate( Mobile m ) + { + if ( DuelContext.CheckCombat( m ) ) + { + m.SendMessage( 0x22, "You have recently been in combat with another player and cannot use this moongate." ); + } + else + { + if ( m_Teleporter != null && !m_Teleporter.Deleted ) + m_Teleporter.Register( m ); + + base.UseGate( m ); + } + } + + public void Appear( Point3D loc, Map map ) + { + Effects.PlaySound( loc, map, 0x20E ); + MoveToWorld( loc, map ); + } + + public ArenaMoongate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + } + + public void RemoveAggressions( Mobile mob ) + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer dp = (DuelPlayer)p.Players[j]; + + if ( dp == null || dp.Mobile == mob ) + continue; + + mob.RemoveAggressed( dp.Mobile ); + mob.RemoveAggressor( dp.Mobile ); + dp.Mobile.RemoveAggressed( mob ); + dp.Mobile.RemoveAggressor( mob ); + } + } + } + + public void SendReadyUpGump() + { + if ( !m_Registered ) + return; + + m_ReadyWait = true; + m_ReadyCount = -1; + + Type[] types = new Type[]{ typeof( ReadyUpGump ) }; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl == null ) + continue; + + Mobile mob = pl.Mobile; + + if ( mob != null ) + { + if ( m_Tournament == null ) + CloseAndSendGump( mob, new ReadyUpGump( mob, this ), types ); + } + } + } + } + + public string ValidateStart() + { + if ( m_Tournament == null && TournamentController.IsActive ) + return "a tournament is active"; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer dp = p.Players[j]; + + if ( dp == null ) + return "a slot is empty"; + + if ( dp.Mobile.Region.IsPartOf( typeof( Regions.Jail ) ) ) + return String.Format( "{0} is in jail", dp.Mobile.Name ); + + if ( Sigil.ExistsOn( dp.Mobile ) ) + return String.Format( "{0} is holding a sigil", dp.Mobile.Name ); + + if ( !dp.Mobile.Alive ) + { + if ( m_Tournament == null ) + return String.Format( "{0} is dead", dp.Mobile.Name ); + else + dp.Mobile.Resurrect(); + } + + if ( m_Tournament == null && CheckCombat( dp.Mobile ) ) + return String.Format( "{0} is in combat", dp.Mobile.Name ); + + if ( dp.Mobile.Mounted ) + { + IMount mount = dp.Mobile.Mount; + + if ( m_Tournament != null && mount != null ) + mount.Rider = null; + else + return String.Format( "{0} is mounted", dp.Mobile.Name ); + } + } + } + + return null; + } + + public Arena m_OverrideArena; + public Tournament m_Tournament; + public TournyMatch m_Match; + public EventGame m_EventGame; + + public void SendReadyGump( int count ) + { + if ( !m_Registered ) + return; + + if ( count != -1 ) + m_StartedReadyCountdown = true; + + m_ReadyCount = count; + + if ( count == 0 ) + { + string error = ValidateStart(); + + if ( error != null ) + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer dp = p.Players[j]; + + if ( dp != null ) + dp.Mobile.SendMessage( "The duel could not be started because {0}.", error ); + } + } + + StartCountdown( 10, new CountdownCallback( SendReadyGump ) ); + + return; + } + + m_ReadyWait = false; + + List players = new List(); + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer dp = p.Players[j]; + + if ( dp != null ) + players.Add( dp.Mobile ); + } + } + + Arena arena = m_OverrideArena; + + if ( arena == null ) + arena = Arena.FindArena( players ); + + if ( arena == null ) + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer dp = p.Players[j]; + + if ( dp != null ) + dp.Mobile.SendMessage( "The duel could not be started because there are no arenas. If you want to stop waiting for a free arena, yield the duel." ); + } + } + + StartCountdown( 10, new CountdownCallback( SendReadyGump ) ); + return; + } + + if ( !arena.IsOccupied ) + { + m_Arena = arena; + + if ( m_Initiator.Map == Map.Internal ) + { + m_GatePoint = m_Initiator.LogoutLocation; + m_GateFacet = m_Initiator.LogoutMap; + } + else + { + m_GatePoint = m_Initiator.Location; + m_GateFacet = m_Initiator.Map; + } + + ExitTeleporter tp = arena.Teleporter as ExitTeleporter; + + if ( tp == null ) + { + arena.Teleporter = tp = new ExitTeleporter(); + tp.MoveToWorld( arena.GateOut == Point3D.Zero ? arena.Outside : arena.GateOut, arena.Facet ); + } + + ArenaMoongate mg = new ArenaMoongate( arena.GateIn == Point3D.Zero ? arena.Outside : arena.GateIn, arena.Facet, tp ); + + m_StartedBeginCountdown = true; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl == null ) + continue; + + tp.Register( pl.Mobile ); + + pl.Mobile.Frozen = false; // reset timer just in case + pl.Mobile.Frozen = true; + + Debuff( pl.Mobile ); + CancelSpell( pl.Mobile ); + + pl.Mobile.Delta( MobileDelta.Noto ); + } + + arena.MoveInside( p.Players, i ); + } + + if ( m_EventGame != null ) + m_EventGame.OnStart(); + + StartCountdown( 10, new CountdownCallback( SendBeginGump ) ); + + mg.Appear( m_GatePoint, m_GateFacet ); + } + else + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer dp = p.Players[j]; + + if ( dp != null ) + dp.Mobile.SendMessage( "The duel could not be started because all arenas are full. If you want to stop waiting for a free arena, yield the duel." ); + } + } + + StartCountdown( 10, new CountdownCallback( SendReadyGump ) ); + } + + return; + } + + m_ReadyWait = true; + + bool isAllReady = true; + + Type[] types = new Type[]{ typeof( ReadyGump ) }; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + Participant p = (Participant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl == null ) + continue; + + Mobile mob = pl.Mobile; + + if ( pl.Ready ) + { + if ( m_Tournament == null ) + CloseAndSendGump( mob, new ReadyGump( mob, this, count ), types ); + } + else + { + isAllReady = false; + } + } + } + + if ( count == -1 && isAllReady ) + StartCountdown( 3, new CountdownCallback( SendReadyGump ) ); + } + + public static void CloseAndSendGump( Mobile mob, Gump g, params Type[] types ) + { + CloseAndSendGump( mob.NetState, g, types ); + } + + public static void CloseAndSendGump( NetState ns, Gump g, params Type[] types ) + { + if ( ns == null ) + return; + + for ( int i = 0; i < types.Length; ++i ) + ns.Send( new CloseGump( Gump.GetTypeID( types[i] ), 0 ) ); + + g.SendTo( ns ); + + /*ns.AddGump( g ); + + Packet[] packets = new Packet[types.Length + 1]; + + for ( int i = 0; i < types.Length; ++i ) + packets[i] = new CloseGump( Gump.GetTypeID( types[i] ), 0 ); + + packets[types.Length] = (Packet) typeof( Gump ).InvokeMember( "Compile", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, g, null, null ); + + bool compress = ns.CompressionEnabled; + ns.CompressionEnabled = false; + ns.Send( BindPackets( compress, packets ) ); + ns.CompressionEnabled = compress;*/ + } + + public static Packet BindPackets( bool compress, params Packet[] packets ) + { + if ( packets.Length == 0 ) + throw new ArgumentException( "No packets to bind", "packets" ); + + byte[][] compiled = new byte[packets.Length][]; + int[] lengths = new int[packets.Length]; + + int length = 0; + + for ( int i = 0; i < packets.Length; ++i ) + { + compiled[i] = packets[i].Compile( compress, out lengths[i] ); + length += lengths[i]; + } + + return new BoundPackets( length, compiled, lengths ); + } + + private class BoundPackets : Packet + { + public BoundPackets( int length, byte[][] compiled, int[] lengths ) : base( 0, length ) + { + m_Stream.Seek( 0, System.IO.SeekOrigin.Begin ); + + for ( int i = 0; i < compiled.Length; ++i ) + m_Stream.Write( compiled[i], 0, lengths[i] ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/DuelContextGump.cs b/Scripts/Engines/ConPVP/DuelContextGump.cs new file mode 100644 index 000000000..98268f38a --- /dev/null +++ b/Scripts/Engines/ConPVP/DuelContextGump.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.ConPVP +{ + public class DuelContextGump : Gump + { + private Mobile m_From; + private DuelContext m_Context; + + public Mobile From{ get{ return m_From; } } + public DuelContext Context{ get{ return m_Context; } } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public void AddGoldenButton( int x, int y, int bid ) + { + AddButton( x , y , 0xD2, 0xD2, bid, GumpButtonType.Reply, 0 ); + AddButton( x+3, y+3, 0xD8, 0xD8, bid, GumpButtonType.Reply, 0 ); + } + + public void AddGoldenButtonLabeled( int x, int y, int bid, string text ) + { + AddGoldenButton( x, y, bid ); + AddHtml( x + 25, y, 200, 20, text, false, false ); + } + + public DuelContextGump( Mobile from, DuelContext context ) : base( 50, 50 ) + { + m_From = from; + m_Context = context; + + from.CloseGump( typeof( RulesetGump ) ); + from.CloseGump( typeof( DuelContextGump ) ); + from.CloseGump( typeof( ParticipantGump ) ); + + int count = context.Participants.Count; + + if ( count < 3 ) + count = 3; + + int height = 35 + 10 + 22 + 30 + 22 + 22 + 2 + (count * 22) + 2 + 30; + + AddPage( 0 ); + + AddBackground( 0, 0, 300, height, 9250 ); + AddBackground( 10, 10, 280, height - 20, 0xDAC ); + + AddHtml( 35, 25, 230, 20, Center( "Duel Setup" ), false, false ); + + int x = 35; + int y = 47; + + AddGoldenButtonLabeled( x, y, 1, "Rules" ); y += 22; + AddGoldenButtonLabeled( x, y, 2, "Start" ); y += 22; + AddGoldenButtonLabeled( x, y, 3, "Add Participant" ); y += 30; + + AddHtml( 35, y, 230, 20, Center( "Participants" ), false, false ); y += 22; + + for ( int i = 0; i < context.Participants.Count; ++i ) + { + Participant p = (Participant)context.Participants[i]; + + AddGoldenButtonLabeled( x, y, 4 + i, String.Format( p.Count == 1 ? "Player {0}: {3}" : "Team {0}: {1}/{2}: {3}", 1 + i, p.FilledSlots, p.Count, p.NameList ) ); y += 22; + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( !m_Context.Registered ) + return; + + int index = info.ButtonID; + + switch ( index ) + { + case -1: // CloseGump + { + break; + } + case 0: // closed + { + m_Context.Unregister(); + break; + } + case 1: // Rules + { + //m_From.SendGump( new RulesetGump( m_From, m_Context.Ruleset, m_Context.Ruleset.Layout, m_Context ) ); + m_From.SendGump( new PickRulesetGump( m_From, m_Context, m_Context.Ruleset ) ); + break; + } + case 2: // Start + { + if ( m_Context.CheckFull() ) + { + m_Context.CloseAllGumps(); + m_Context.SendReadyUpGump(); + //m_Context.SendReadyGump(); + } + else + { + m_From.SendMessage( "You cannot start the duel before all participating players have been assigned." ); + m_From.SendGump( new DuelContextGump( m_From, m_Context ) ); + } + + break; + } + case 3: // New Participant + { + if ( m_Context.Participants.Count < 10 ) + m_Context.Participants.Add( new Participant( m_Context, 1 ) ); + else + m_From.SendMessage( "The number of participating parties may not be increased further." ); + + m_From.SendGump( new DuelContextGump( m_From, m_Context ) ); + + break; + } + default: // Participant + { + index -= 4; + + if ( index >= 0 && index < m_Context.Participants.Count ) + m_From.SendGump( new ParticipantGump( m_From, m_Context, (Participant)m_Context.Participants[index] ) ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/DuelTeleporterAddon.cs b/Scripts/Engines/ConPVP/DuelTeleporterAddon.cs new file mode 100644 index 000000000..baf847ba8 --- /dev/null +++ b/Scripts/Engines/ConPVP/DuelTeleporterAddon.cs @@ -0,0 +1,96 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Engines.ConPVP +{ + public enum DuelTeleporterType + { + Squares = 6095, + Buds = 6104, + Flowers = 6113, + Spikes = 6122, + Arrows = 6140, + Links = 6149 + } + + public class DuelTeleporterAddon : BaseAddon + { + [CommandProperty( AccessLevel.GameMaster )] + public DuelTeleporterType Type + { + get + { + if ( Components.Count > 0 ) + return (DuelTeleporterType)(((Item)Components[0]).ItemID); + + return DuelTeleporterType.Squares; + } + set + { + for ( int i = 0; i < Components.Count && i < 9; ++i ) + ((Item)Components[i]).ItemID = i + (int)value; + } + } + + [Constructable] + public DuelTeleporterAddon() : this( DuelTeleporterType.Squares ) + { + } + + [Constructable] + public DuelTeleporterAddon( DuelTeleporterType type ) + { + int itemID = (int)type; + + AddComponent( new AddonComponent( itemID + 0 ), -1, -1, 5 ); + AddComponent( new AddonComponent( itemID + 1 ), -1, 0, 5 ); + AddComponent( new AddonComponent( itemID + 2 ), 0, -1, 5 ); + AddComponent( new AddonComponent( itemID + 3 ), -1, +1, 5 ); + AddComponent( new AddonComponent( itemID + 4 ), 0, 0, 5 ); + AddComponent( new AddonComponent( itemID + 5 ), +1, -1, 5 ); + AddComponent( new AddonComponent( itemID + 6 ), 0, +1, 5 ); + AddComponent( new AddonComponent( itemID + 7 ), +1, 0, 5 ); + AddComponent( new AddonComponent( itemID + 8 ), +1, +1, 5 ); + + AddComponent( new AddonComponent( 0x759 ), -2, -2, 0 ); + AddComponent( new AddonComponent( 0x75A ), +2, +2, 0 ); + AddComponent( new AddonComponent( 0x75B ), -2, +2, 0 ); + AddComponent( new AddonComponent( 0x75C ), +2, -2, 0 ); + + AddComponent( new AddonComponent( 0x751 ), -1, +2, 0 ); + AddComponent( new AddonComponent( 0x751 ), 0, +2, 0 ); + AddComponent( new AddonComponent( 0x751 ), +1, +2, 0 ); + + AddComponent( new AddonComponent( 0x752 ), +2, -1, 0 ); + AddComponent( new AddonComponent( 0x752 ), +2, 0, 0 ); + AddComponent( new AddonComponent( 0x752 ), +2, +1, 0 ); + + AddComponent( new AddonComponent( 0x753 ), -1, -2, 0 ); + AddComponent( new AddonComponent( 0x753 ), 0, -2, 0 ); + AddComponent( new AddonComponent( 0x753 ), +1, -2, 0 ); + + AddComponent( new AddonComponent( 0x754 ), -2, -1, 0 ); + AddComponent( new AddonComponent( 0x754 ), -2, 0, 0 ); + AddComponent( new AddonComponent( 0x754 ), -2, +1, 0 ); + } + + public DuelTeleporterAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/Games/EventGame.cs b/Scripts/Engines/ConPVP/Games/EventGame.cs new file mode 100644 index 000000000..6207f1910 --- /dev/null +++ b/Scripts/Engines/ConPVP/Games/EventGame.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server; +using Server.Items; + +namespace Server.Engines.ConPVP +{ + public abstract class EventController : Item + { + public abstract EventGame Construct( DuelContext dc ); + + public abstract string Title { get; } + + public abstract string GetTeamName( int teamID ); + + public EventController() + : base( 0x1B7A ) + { + Visible = false; + Movable = false; + } + + public EventController( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + from.SendGump( new Gumps.PropertiesGump( from, this ) ); + } + } + + public abstract class EventGame + { + protected DuelContext m_Context; + + public DuelContext Context { get { return m_Context; } } + + public virtual bool FreeConsume { get { return true; } } + + public EventGame( DuelContext context ) + { + m_Context = context; + } + + public virtual bool OnDeath( Mobile mob, Container corpse ) + { + return true; + } + + public virtual bool CantDoAnything( Mobile mob ) + { + return false; + } + + public virtual void OnStart() + { + } + + public virtual void OnStop() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/Ladder.cs b/Scripts/Engines/ConPVP/Ladder.cs new file mode 100644 index 000000000..fbf51b032 --- /dev/null +++ b/Scripts/Engines/ConPVP/Ladder.cs @@ -0,0 +1,378 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Engines.ConPVP +{ + public class LadderController : Item + { + private Ladder m_Ladder; + + //[CommandProperty( AccessLevel.GameMaster )] + public Ladder Ladder{ get{ return m_Ladder; } set{} } + + public override string DefaultName + { + get { return "ladder controller"; } + } + + [Constructable] + public LadderController() : base( 0x1B7A ) + { + Visible = false; + Movable = false; + + m_Ladder = new Ladder(); + + if ( Ladder.Instance == null ) + Ladder.Instance = m_Ladder; + } + + public override void Delete() + { + if ( Ladder.Instance != m_Ladder ) + base.Delete(); + } + + public LadderController( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + + m_Ladder.Serialize( writer ); + + writer.Write( (bool) ( Server.Engines.ConPVP.Ladder.Instance == m_Ladder ) ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + case 0: + { + m_Ladder = new Ladder( reader ); + + if ( version < 1 || reader.ReadBool() ) + Server.Engines.ConPVP.Ladder.Instance = m_Ladder; + + break; + } + } + } + } + + public class Ladder + { + private static int[] m_ShortLevels = new int[] + { + 1, + 2, + 3, 3, + 4, 4, + 5, 5, 5, + 6, 6, 6, + 7, 7, 7, 7, + 8, 8, 8, 8, + 9, 9, 9, 9, 9 + }; + + public static int GetLevel( int xp ) + { + if ( xp >= 22500 ) + return 50; + else if ( xp >= 2500 ) + return (10 + ((xp - 2500) / 500)); + else if ( xp < 0 ) + xp = 0; + + return m_ShortLevels[xp / 100]; + } + + private static int[] m_BaseXP = new int[] + { + 0, 100, 200, 400, 600, 900, 1200, 1600, 2000, 2500 + }; + + public static void GetLevelInfo( int level, out int xpBase, out int xpAdvance ) + { + if ( level >= 10 ) + { + xpBase = 2500 + ((level-10)*500); + xpAdvance = 500; + } + else + { + xpBase = m_BaseXP[level-1]; + xpAdvance = m_BaseXP[level] - xpBase; + } + } + + private static int[] m_LossFactors = new int[] + { + 10, + 11, 11, + 25, 25, + 43, 43, + 67, 67 + }; + + public static int GetLossFactor( int level ) + { + if ( level >= 10 ) + return 100; + + return m_LossFactors[level - 1]; + } + + private static int[,] m_OffsetScalar = new int[,] + { + /* { win, los } */ + /* -6 */ { 175, 25 }, + /* -5 */ { 165, 35 }, + /* -4 */ { 155, 45 }, + /* -3 */ { 145, 55 }, + /* -2 */ { 130, 70 }, + /* -1 */ { 115, 85 }, + /* 0 */ { 100, 100 }, + /* +1 */ { 90, 110 }, + /* +2 */ { 80, 120 }, + /* +3 */ { 70, 130 }, + /* +4 */ { 60, 140 }, + /* +5 */ { 50, 150 }, + /* +6 */ { 40, 160 } + }; + + public static int GetOffsetScalar( int ourLevel, int theirLevel, bool win ) + { + int x = ourLevel - theirLevel; + + if ( x < -6 || x > +6 ) + return 0; + + int y = win ? 0 : 1; + + return m_OffsetScalar[x+6, y]; + } + + public static int GetExperienceGain( LadderEntry us, LadderEntry them, bool weWon ) + { + if ( us == null || them == null ) + return 0; + + int ourLevel = GetLevel( us.Experience ); + int theirLevel = GetLevel( them.Experience ); + + int scalar = GetOffsetScalar( ourLevel, theirLevel, weWon ); + + if ( scalar == 0 ) + return 0; + + int xp = 25 * scalar; + + if ( !weWon ) + xp = (xp * GetLossFactor( ourLevel )) / 100; + + xp /= 100; + + if ( xp <= 0 ) + xp = 1; + + return xp * (weWon ? 1 : -1); + } + + private Hashtable m_Table; + private ArrayList m_Entries; + + public ArrayList ToArrayList() + { + return m_Entries; + } + + private int Swap( int idx, int newIdx ) + { + object hold = m_Entries[idx]; + + m_Entries[idx] = m_Entries[newIdx]; + m_Entries[newIdx] = hold; + + ((LadderEntry)m_Entries[idx]).Index = idx; + ((LadderEntry)m_Entries[newIdx]).Index = newIdx; + + return newIdx; + } + + public void UpdateEntry( LadderEntry entry ) + { + int index = entry.Index; + + if ( index >= 0 && index < m_Entries.Count ) + { + // sanity + + int c; + + while ( (index - 1) >= 0 && (c = entry.CompareTo( m_Entries[index - 1] )) < 0 ) + index = Swap( index, index - 1 ); + + while ( (index + 1) < m_Entries.Count && (c = entry.CompareTo( m_Entries[index + 1] )) > 0 ) + index = Swap( index, index + 1 ); + } + } + + public LadderEntry Find( Mobile mob ) + { + LadderEntry entry = (LadderEntry) m_Table[mob]; + + if ( entry == null ) + { + m_Table[mob] = entry = new LadderEntry( mob, this ); + entry.Index = m_Entries.Count; + m_Entries.Add( entry ); + } + + return entry; + } + + public LadderEntry FindNoCreate( Mobile mob ) + { + return m_Table[mob] as LadderEntry; + } + + private static Ladder m_Instance; + + public static Ladder Instance{ get{ return m_Instance; } set{ m_Instance = value; } } + + public Ladder() + { + m_Table = new Hashtable(); + m_Entries = new ArrayList(); + } + + public Ladder( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + case 0: + { + int count = reader.ReadEncodedInt(); + + m_Table = new Hashtable( count ); + m_Entries = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + LadderEntry entry = new LadderEntry( reader, this, version ); + + if ( entry.Mobile != null ) + { + m_Table[entry.Mobile] = entry; + entry.Index = m_Entries.Count; + m_Entries.Add( entry ); + } + } + + if ( version == 0 ) + { + m_Entries.Sort(); + + for ( int i = 0; i < m_Entries.Count; ++i ) + { + LadderEntry entry = (LadderEntry)m_Entries[i]; + + entry.Index = i; + } + } + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 1 ); // version; + + writer.WriteEncodedInt( (int) m_Entries.Count ); + + for ( int i = 0; i < m_Entries.Count; ++i ) + ((LadderEntry)m_Entries[i]).Serialize( writer ); + } + } + + public class LadderEntry : IComparable + { + private Mobile m_Mobile; + private int m_Experience; + private int m_Wins; + private int m_Losses; + private int m_Index; + private Ladder m_Ladder; + + public Mobile Mobile{ get{ return m_Mobile; } } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public int Experience{ get{ return m_Experience; } set{ m_Experience = value; m_Ladder.UpdateEntry(this); } } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public int Wins{ get{ return m_Wins; } set{ m_Wins = value; } } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public int Losses{ get{ return m_Losses; } set{ m_Losses = value; } } + + public int Index{ get{ return m_Index; } set{ m_Index = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Rank{ get{ return m_Index; } } + + public LadderEntry( Mobile mob, Ladder ladder ) + { + m_Ladder = ladder; + m_Mobile = mob; + } + + public LadderEntry( GenericReader reader, Ladder ladder, int version ) + { + m_Ladder = ladder; + + switch ( version ) + { + case 1: + case 0: + { + m_Mobile = reader.ReadMobile(); + m_Experience = reader.ReadEncodedInt(); + m_Wins = reader.ReadEncodedInt(); + m_Losses = reader.ReadEncodedInt(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (Mobile) m_Mobile ); + writer.WriteEncodedInt( (int) m_Experience ); + writer.WriteEncodedInt( (int) m_Wins ); + writer.WriteEncodedInt( (int) m_Losses ); + } + + public int CompareTo( object obj ) + { + return ((LadderEntry)obj).m_Experience - m_Experience; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/LadderGump.cs b/Scripts/Engines/ConPVP/LadderGump.cs new file mode 100644 index 000000000..878e7ee1d --- /dev/null +++ b/Scripts/Engines/ConPVP/LadderGump.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.ConPVP +{ + public class LadderItem : Item + { + private LadderController m_Ladder; + + [CommandProperty( AccessLevel.GameMaster )] + public LadderController Ladder + { + get{ return m_Ladder; } + set{ m_Ladder = value; } + } + + public override string DefaultName + { + get { return "1v1 leaderboard"; } + } + + [Constructable] + public LadderItem() : base( 0x117F ) + { + Movable = false; + } + + public LadderItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + + writer.Write( (Item) m_Ladder ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Ladder = reader.ReadItem() as LadderController; + break; + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( GetWorldLocation(), 2 ) ) + { + Ladder ladder = Server.Engines.ConPVP.Ladder.Instance; + + if ( m_Ladder != null ) + ladder = m_Ladder.Ladder; + + if ( ladder != null ) + { + from.CloseGump( typeof( LadderGump ) ); + from.SendGump( new LadderGump( ladder, 0 ) ); + } + } + else + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that + } + } + + public class LadderGump : Gump + { + private Ladder m_Ladder; + private int m_Page; + + public LadderGump( Ladder ladder ) : this( ladder, 0 ) + { + } + + public static string Rank( int num ) + { + string numStr = num.ToString( "N0" ); + + if ( (num%100) > 10 && (num%100) < 20 ) + return numStr + "th"; + + switch ( num % 10 ) + { + case 1: return numStr + "st"; + case 2: return numStr + "nd"; + case 3: return numStr + "rd"; + default: return numStr + "th"; + } + } + + public override void OnResponse(NetState sender, RelayInfo info) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 1 && m_Page > 0 ) + from.SendGump( new LadderGump( m_Ladder, m_Page - 1 ) ); + else if ( info.ButtonID == 2 && ((m_Page+1)*15) lc ) + end = lc; + + int ct = end-start; + + int height = 12 + 20 + (ct*20) + 23 + 12; + + AddBackground( 0, 0, 499, height, 0x2436 ); + + for ( int i = start + 1; i < end; i += 2 ) + AddImageTiled( 12, 32 + ((i - start) * 20), 475, 20, 0x2430 ); + + AddAlphaRegion( 10, 10, 479, height - 20 ); + + if ( page > 0 ) + AddButton( 446, height-12-2-16, 0x15E3, 0x15E7, 1, GumpButtonType.Reply, 0 ); + else + AddImage( 446, height-12-2-16, 0x2626 ); + + if ( ((page+1)*15)= xpAdvance ) + width = 109; // level 50 + else + width = ((109 * xpOffset) + (xpAdvance / 2)) / (xpAdvance - 1); + + //AddImageTiled( 21, y + 6, width, 8, 0x2617 ); + AddImageTiled( x + 3, y + 4, width, 11, 0x806 ); + AddBorderedText( x, y, 115, Center( level.ToString() ), 0xFFFFFF, 0 ); + x += 115; + + Mobile mob = entry.Mobile; + + if ( mob.Guild != null ) + AddBorderedText( x, y, 50, Center( mob.Guild.Abbreviation ), 0xFFFFFF, 0 ); + + x += 50; + + AddBorderedText( x + 5, y, 115 - 5, ( mob.Name ), 0xFFFFFF, 0 ); + x += 115; + + AddBorderedText( x, y, 60, Center( entry.Wins.ToString() ), 0xFFFFFF, 0 ); + x += 60; + + AddBorderedText( x, y, 60, Center( entry.Losses.ToString() ), 0xFFFFFF, 0 ); + x += 60; + + //AddBorderedText( 292 + 15, y, 115 - 30, String.Format( "{0}
/
{1}
", entry.Wins, entry.Losses ), 0xFFC000, 0 ); + } + } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + private void AddBorderedText( int x, int y, int width, string text, int color, int borderColor ) + { + /*AddColoredText( x - 1, y, width, text, borderColor ); + AddColoredText( x + 1, y, width, text, borderColor ); + AddColoredText( x, y - 1, width, text, borderColor ); + AddColoredText( x, y + 1, width, text, borderColor );*/ + /*AddColoredText( x - 1, y - 1, width, text, borderColor ); + AddColoredText( x + 1, y + 1, width, text, borderColor );*/ + AddColoredText( x, y, width, text, color ); + } + + private void AddColoredText( int x, int y, int width, string text, int color ) + { + if ( color == 0 ) + AddHtml( x, y, width, 20, text, false, false ); + else + AddHtml( x, y, width, 20, Color( text, color ), false, false ); + } + + private int m_ColumnX = 12; + + private void AddColumnHeader( int width, string name ) + { + AddBackground( m_ColumnX, 12, width, 20, 0x242C ); + AddImageTiled( m_ColumnX + 2, 14, width - 4, 16, 0x2430 ); + AddBorderedText( m_ColumnX, 13, width, Center( name ), 0xFFFFFF, 0 ); + + m_ColumnX += width; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/Participant.cs b/Scripts/Engines/ConPVP/Participant.cs new file mode 100644 index 000000000..f4c6a5a6d --- /dev/null +++ b/Scripts/Engines/ConPVP/Participant.cs @@ -0,0 +1,236 @@ +using System; +using System.Text; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.ConPVP +{ + public class Participant + { + private DuelContext m_Context; + private DuelPlayer[] m_Players; + private TournyParticipant m_TournyPart; + + public int Count{ get{ return m_Players.Length; } } + public DuelPlayer[] Players{ get{ return m_Players; } } + public DuelContext Context{ get{ return m_Context; } } + public TournyParticipant TournyPart{ get{ return m_TournyPart; } set{ m_TournyPart = value; } } + + public DuelPlayer Find( Mobile mob ) + { + if ( mob is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)mob; + + if ( pm.DuelContext == m_Context && pm.DuelPlayer.Participant == this ) + return pm.DuelPlayer; + + return null; + } + + for ( int i = 0; i < m_Players.Length; ++i ) + { + if ( m_Players[i] != null && m_Players[i].Mobile == mob ) + return m_Players[i]; + } + + return null; + } + + public bool Contains( Mobile mob ) + { + return ( Find( mob ) != null ); + } + + public void Broadcast( int hue, string message, string nonLocalOverhead, string localOverhead ) + { + for ( int i = 0; i < m_Players.Length; ++i ) + { + if ( m_Players[i] != null ) + { + if ( message != null ) + m_Players[i].Mobile.SendMessage( hue, message ); + + if ( nonLocalOverhead != null ) + m_Players[i].Mobile.NonlocalOverheadMessage( Network.MessageType.Regular, hue, false, String.Format( nonLocalOverhead, m_Players[i].Mobile.Name, m_Players[i].Mobile.Female ? "her" : "his" ) ); + + if ( localOverhead != null ) + m_Players[i].Mobile.LocalOverheadMessage( Network.MessageType.Regular, hue, false, localOverhead ); + } + } + } + + public int FilledSlots + { + get + { + int count = 0; + + for ( int i = 0; i < m_Players.Length; ++i ) + { + if ( m_Players[i] != null ) + ++count; + } + + return count; + } + } + + public bool HasOpenSlot + { + get + { + for ( int i = 0; i < m_Players.Length; ++i ) + { + if ( m_Players[i] == null ) + return true; + } + + return false; + } + } + + public bool Eliminated + { + get + { + for ( int i = 0; i < m_Players.Length; ++i ) + { + if ( m_Players[i] != null && !m_Players[i].Eliminated ) + return false; + } + + return true; + } + } + + public string NameList + { + get + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < m_Players.Length; ++i ) + { + if ( m_Players[i] == null ) + continue; + + Mobile mob = m_Players[i].Mobile; + + if ( sb.Length > 0 ) + sb.Append( ", " ); + + sb.Append( mob.Name ); + } + + if ( sb.Length == 0 ) + return "Empty"; + + return sb.ToString(); + } + } + + public void Nullify( DuelPlayer player ) + { + if ( player == null ) + return; + + int index = Array.IndexOf( m_Players, player ); + + if ( index == -1 ) + return; + + m_Players[index] = null; + } + + public void Remove( DuelPlayer player ) + { + if ( player == null ) + return; + + int index = Array.IndexOf( m_Players, player ); + + if ( index == -1 ) + return; + + DuelPlayer[] old = m_Players; + m_Players = new DuelPlayer[old.Length - 1]; + + for ( int i = 0; i < index; ++i ) + m_Players[i] = old[i]; + + for ( int i = index + 1; i < old.Length; ++i ) + m_Players[i - 1] = old[i]; + } + + public void Remove( Mobile player ) + { + Remove( Find( player ) ); + } + + public void Add( Mobile player ) + { + if ( Contains( player ) ) + return; + + for ( int i = 0; i < m_Players.Length; ++i ) + { + if ( m_Players[i] == null ) + { + m_Players[i] = new DuelPlayer( player, this ); + return; + } + } + + Resize( m_Players.Length + 1 ); + m_Players[m_Players.Length - 1] = new DuelPlayer( player, this ); + } + + public void Resize( int count ) + { + DuelPlayer[] old = m_Players; + m_Players = new DuelPlayer[count]; + + if ( old != null ) + { + int ct = 0; + + for ( int i = 0; i < old.Length; ++i ) + { + if ( old[i] != null && ct < count ) + m_Players[ct++] = old[i]; + } + } + } + + public Participant( DuelContext context, int count ) + { + m_Context = context; + //m_Stakes = new StakesContainer( context, this ); + Resize( count ); + } + } + + public class DuelPlayer + { + private Mobile m_Mobile; + private bool m_Eliminated; + private bool m_Ready; + private Participant m_Participant; + + public Mobile Mobile{ get{ return m_Mobile; } } + public bool Ready{ get{ return m_Ready; } set{ m_Ready = value; } } + public bool Eliminated{ get{ return m_Eliminated; } set{ m_Eliminated = value; if ( m_Participant.Context.m_Tournament != null && m_Eliminated ){ m_Participant.Context.m_Tournament.OnEliminated( this ); m_Mobile.SendEverything(); } } } + public Participant Participant{ get{ return m_Participant; } set{ m_Participant = value; } } + + public DuelPlayer( Mobile mob, Participant p ) + { + m_Mobile = mob; + m_Participant = p; + + if ( mob is PlayerMobile ) + ((PlayerMobile)mob).DuelPlayer = this; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/ParticipantGump.cs b/Scripts/Engines/ConPVP/ParticipantGump.cs new file mode 100644 index 000000000..e4c24feaf --- /dev/null +++ b/Scripts/Engines/ConPVP/ParticipantGump.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Network; +using Server.Targeting; +using Server.Mobiles; + +namespace Server.Engines.ConPVP +{ + public class ParticipantGump : Gump + { + private Mobile m_From; + private DuelContext m_Context; + private Participant m_Participant; + + public Mobile From{ get{ return m_From; } } + public DuelContext Context{ get{ return m_Context; } } + public Participant Participant{ get{ return m_Participant; } } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public void AddGoldenButton( int x, int y, int bid ) + { + AddButton( x , y , 0xD2, 0xD2, bid, GumpButtonType.Reply, 0 ); + AddButton( x+3, y+3, 0xD8, 0xD8, bid, GumpButtonType.Reply, 0 ); + } + + public void AddGoldenButtonLabeled( int x, int y, int bid, string text ) + { + AddGoldenButton( x, y, bid ); + AddHtml( x + 25, y, 200, 20, text, false, false ); + } + + public ParticipantGump( Mobile from, DuelContext context, Participant p ) : base( 50, 50 ) + { + m_From = from; + m_Context = context; + m_Participant = p; + + from.CloseGump( typeof( RulesetGump ) ); + from.CloseGump( typeof( DuelContextGump ) ); + from.CloseGump( typeof( ParticipantGump ) ); + + int count = p.Players.Length; + + if ( count < 4 ) + count = 4; + + AddPage( 0 ); + + int height = 35 + 10 + 22 + 22 + 30 + 22 + 2 + (count * 22) + 2 + 30; + + AddBackground( 0, 0, 300, height, 9250 ); + AddBackground( 10, 10, 280, height - 20, 0xDAC ); + + AddButton( 240, 25, 0xFB1, 0xFB3, 3, GumpButtonType.Reply, 0 ); + + //AddButton( 223, 54, 0x265A, 0x265A, 4, GumpButtonType.Reply, 0 ); + + AddHtml( 35, 25, 230, 20, Center( "Participant Setup" ), false, false ); + + int x = 35; + int y = 47; + + AddHtml( x, y, 200, 20, String.Format( "Team Size: {0}", p.Players.Length ), false, false ); y += 22; + + AddGoldenButtonLabeled( x + 20, y, 1, "Increase" ); y += 22; + AddGoldenButtonLabeled( x + 20, y, 2, "Decrease" ); y += 30; + + AddHtml( 35, y, 230, 20, Center( "Players" ), false, false ); y += 22; + + for ( int i = 0; i < p.Players.Length; ++i ) + { + DuelPlayer pl = p.Players[i]; + + AddGoldenButtonLabeled( x, y, 5 + i, String.Format( "{0}: {1}", 1 + i, pl == null ? "Empty" : pl.Mobile.Name ) ); y += 22; + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( !m_Context.Registered ) + return; + + int bid = info.ButtonID; + + if ( bid == 0 ) + { + m_From.SendGump( new DuelContextGump( m_From, m_Context ) ); + } + else if ( bid == 1 ) + { + if ( m_Participant.Count < 8 ) + m_Participant.Resize( m_Participant.Count + 1 ); + else + m_From.SendMessage( "You may not raise the team size any further." ); + + m_From.SendGump( new ParticipantGump( m_From, m_Context, m_Participant ) ); + } + else if ( bid == 2 ) + { + if ( m_Participant.Count > 1 && m_Participant.Count > m_Participant.FilledSlots ) + m_Participant.Resize( m_Participant.Count - 1 ); + else + m_From.SendMessage( "You may not lower the team size any further." ); + + m_From.SendGump( new ParticipantGump( m_From, m_Context, m_Participant ) ); + } + else if ( bid == 3 ) + { + if ( m_Participant.FilledSlots > 0 ) + { + m_From.SendMessage( "There is at least one currently active player. You must remove them first." ); + m_From.SendGump( new ParticipantGump( m_From, m_Context, m_Participant ) ); + } + else if ( m_Context.Participants.Count > 2 ) + { + /*Container cont = m_Participant.Stakes; + + if ( cont != null ) + cont.Delete();*/ + + m_Context.Participants.Remove( m_Participant ); + m_From.SendGump( new DuelContextGump( m_From, m_Context ) ); + } + else + { + m_From.SendMessage( "Duels must have at least two participating parties." ); + m_From.SendGump( new ParticipantGump( m_From, m_Context, m_Participant ) ); + } + } + /*else if ( bid == 4 ) + { + m_From.SendGump( new ParticipantGump( m_From, m_Context, m_Participant ) ); + + Container cont = m_Participant.Stakes; + + if ( cont != null && !cont.Deleted ) + { + cont.DisplayTo( m_From ); + + Item[] checks = cont.FindItemsByType( typeof( BankCheck ) ); + + int gold = cont.TotalGold; + + for ( int i = 0; i < checks.Length; ++i ) + gold += ((BankCheck)checks[i]).Worth; + + m_From.SendMessage( "This container has {0} item{1} and {2} stone{3}. In gold or check form there is a total of {4:D}gp.", cont.TotalItems, cont.TotalItems==1?"":"s", cont.TotalWeight, cont.TotalWeight==1?"":"s", gold ); + } + }*/ + else + { + bid -= 5; + + if ( bid >= 0 && bid < m_Participant.Players.Length ) + { + if ( m_Participant.Players[bid] == null ) + { + m_From.Target = new ParticipantTarget( m_Context, m_Participant, bid ); + m_From.SendMessage( "Target a player." ); + } + else + { + m_Participant.Players[bid].Mobile.SendMessage( "You have been removed from the duel." ); + + if ( m_Participant.Players[bid].Mobile is PlayerMobile ) + ((PlayerMobile)(m_Participant.Players[bid].Mobile)).DuelPlayer = null; + + m_Participant.Players[bid] = null; + m_From.SendMessage( "They have been removed from the duel." ); + m_From.SendGump( new ParticipantGump( m_From, m_Context, m_Participant ) ); + } + } + } + } + + private class ParticipantTarget : Target + { + private DuelContext m_Context; + private Participant m_Participant; + private int m_Index; + + public ParticipantTarget( DuelContext context, Participant p, int index ) : base( 12, false, TargetFlags.None ) + { + m_Context = context; + m_Participant = p; + m_Index = index; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !m_Context.Registered ) + return; + + int index = m_Index; + + if ( index < 0 || index >= m_Participant.Players.Length ) + return; + + Mobile mob = targeted as Mobile; + + if ( mob == null ) + { + from.SendMessage( "That is not a player." ); + } + else if ( !mob.Player ) + { + if ( mob.Body.IsHuman ) + mob.SayTo( from, 1005443 ); // Nay, I would rather stay here and watch a nail rust. + else + mob.SayTo( from, 1005444 ); // The creature ignores your offer. + } + else if ( AcceptDuelGump.IsIgnored( mob, from ) || mob.Blessed ) + { + from.SendMessage( "They ignore your offer." ); + } + else + { + PlayerMobile pm = mob as PlayerMobile; + + if ( pm == null ) + return; + + if ( pm.DuelContext != null ) + from.SendMessage( "{0} cannot fight because they are already assigned to another duel.", pm.Name ); + else if ( DuelContext.CheckCombat( pm ) ) + from.SendMessage( "{0} cannot fight because they have recently been in combat with another player.", pm.Name ); + else if ( mob.HasGump( typeof( AcceptDuelGump ) ) ) + from.SendMessage( "{0} has already been offered a duel." ); + else + { + from.SendMessage( "You send {0} to {1}.", m_Participant.Find( from ) == null ? "a challenge" : "an invitation", mob.Name ); + mob.SendGump( new AcceptDuelGump( from, mob, m_Context, m_Participant, m_Index ) ); + } + } + } + + protected override void OnTargetFinish( Mobile from ) + { + from.SendGump( new ParticipantGump( from, m_Context, m_Participant ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/PickRulesetGump.cs b/Scripts/Engines/ConPVP/PickRulesetGump.cs new file mode 100644 index 000000000..46b357c64 --- /dev/null +++ b/Scripts/Engines/ConPVP/PickRulesetGump.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.ConPVP +{ + public class PickRulesetGump : Gump + { + private Mobile m_From; + private DuelContext m_Context; + private Ruleset m_Ruleset; + private Ruleset[] m_Defaults; + private Ruleset[] m_Flavors; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public PickRulesetGump( Mobile from, DuelContext context, Ruleset ruleset ) : base( 50, 50 ) + { + m_From = from; + m_Context = context; + m_Ruleset = ruleset; + m_Defaults = ruleset.Layout.Defaults; + m_Flavors = ruleset.Layout.Flavors; + + int height = 25 + 20 + ((m_Defaults.Length + 1) * 22) + 6 + 20 + (m_Flavors.Length * 22) + 25; + + AddPage( 0 ); + + AddBackground( 0, 0, 260, height, 9250 ); + AddBackground( 10, 10, 240, height - 20, 0xDAC ); + + AddHtml( 35, 25, 190, 20, Center( "Rules" ), false, false ); + + int y = 25 + 20; + + for ( int i = 0; i < m_Defaults.Length; ++i ) + { + Ruleset cur = m_Defaults[i]; + + AddHtml( 35 + 14, y, 176, 20, cur.Title, false, false ); + + if ( ruleset.Base == cur && !ruleset.Changed ) + AddImage( 35, y + 4, 0x939 ); + else if ( ruleset.Base == cur ) + AddButton( 35, y + 4, 0x93A, 0x939, 2 + i, GumpButtonType.Reply, 0 ); + else + AddButton( 35, y + 4, 0x938, 0x939, 2 + i, GumpButtonType.Reply, 0 ); + + y += 22; + } + + AddHtml( 35 + 14, y, 176, 20, "Custom", false, false ); + AddButton( 35, y + 4, ruleset.Changed ? 0x939 : 0x938, 0x939, 1, GumpButtonType.Reply, 0 ); + + y += 22; + y += 6; + + AddHtml( 35, y, 190, 20, Center( "Flavors" ), false, false ); + y += 20; + + for ( int i = 0; i < m_Flavors.Length; ++i ) + { + Ruleset cur = m_Flavors[i]; + + AddHtml( 35 + 14, y, 176, 20, cur.Title, false, false ); + + if ( ruleset.Flavors.Contains( cur ) ) + AddButton( 35, y + 4, 0x939, 0x938, 2 + m_Defaults.Length + i, GumpButtonType.Reply, 0 ); + else + AddButton( 35, y + 4, 0x938, 0x939, 2 + m_Defaults.Length + i, GumpButtonType.Reply, 0 ); + + y += 22; + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Context != null && !m_Context.Registered ) + return; + + switch ( info.ButtonID ) + { + case 0: // closed + { + if ( m_Context != null ) + m_From.SendGump( new DuelContextGump( m_From, m_Context ) ); + + break; + } + case 1: // customize + { + m_From.SendGump( new RulesetGump( m_From, m_Ruleset, m_Ruleset.Layout, m_Context ) ); + break; + } + default: + { + int idx = info.ButtonID - 2; + + if ( idx >= 0 && idx < m_Defaults.Length ) + { + m_Ruleset.ApplyDefault( m_Defaults[idx] ); + m_From.SendGump( new PickRulesetGump( m_From, m_Context, m_Ruleset ) ); + } + else + { + idx -= m_Defaults.Length; + + if ( idx >= 0 && idx < m_Flavors.Length ) + { + if ( m_Ruleset.Flavors.Contains( m_Flavors[idx] ) ) + m_Ruleset.RemoveFlavor( m_Flavors[idx] ); + else + m_Ruleset.AddFlavor( m_Flavors[idx] ); + + m_From.SendGump( new PickRulesetGump( m_From, m_Context, m_Ruleset ) ); + } + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/Preferences.cs b/Scripts/Engines/ConPVP/Preferences.cs new file mode 100644 index 000000000..3edde5f63 --- /dev/null +++ b/Scripts/Engines/ConPVP/Preferences.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.ConPVP +{ + public class PreferencesController : Item + { + private Preferences m_Preferences; + + //[CommandProperty( AccessLevel.GameMaster )] + public Preferences Preferences{ get{ return m_Preferences; } set{} } + + public override string DefaultName + { + get { return "preferences controller"; } + } + + [Constructable] + public PreferencesController() : base( 0x1B7A ) + { + Visible = false; + Movable = false; + + m_Preferences = new Preferences(); + + if ( Preferences.Instance == null ) + Preferences.Instance = m_Preferences; + else + Delete(); + } + + public override void Delete() + { + if ( Preferences.Instance != m_Preferences ) + base.Delete(); + } + + public PreferencesController( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + m_Preferences.Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Preferences = new Preferences( reader ); + Preferences.Instance = m_Preferences; + break; + } + } + } + } + + public class Preferences + { + private ArrayList m_Entries; + private Hashtable m_Table; + + public ArrayList Entries{ get{ return m_Entries; } } + + public PreferencesEntry Find( Mobile mob ) + { + PreferencesEntry entry = (PreferencesEntry) m_Table[mob]; + + if ( entry == null ) + { + m_Table[mob] = entry = new PreferencesEntry( mob, this ); + m_Entries.Add( entry ); + } + + return entry; + } + + private static Preferences m_Instance; + + public static Preferences Instance{ get{ return m_Instance; } set{ m_Instance = value; } } + + public Preferences() + { + m_Table = new Hashtable(); + m_Entries = new ArrayList(); + } + + public Preferences( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + int count = reader.ReadEncodedInt(); + + m_Table = new Hashtable( count ); + m_Entries = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + PreferencesEntry entry = new PreferencesEntry( reader, this, version ); + + if ( entry.Mobile != null ) + { + m_Table[entry.Mobile] = entry; + m_Entries.Add( entry ); + } + } + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version; + + writer.WriteEncodedInt( (int) m_Entries.Count ); + + for ( int i = 0; i < m_Entries.Count; ++i ) + ((PreferencesEntry)m_Entries[i]).Serialize( writer ); + } + } + + public class PreferencesEntry + { + private Mobile m_Mobile; + private ArrayList m_Disliked; + private Preferences m_Preferences; + + public Mobile Mobile{ get{ return m_Mobile; } } + public ArrayList Disliked{ get{ return m_Disliked; } } + + public PreferencesEntry( Mobile mob, Preferences prefs ) + { + m_Preferences = prefs; + m_Mobile = mob; + m_Disliked = new ArrayList(); + } + + public PreferencesEntry( GenericReader reader, Preferences prefs, int version ) + { + m_Preferences = prefs; + + switch ( version ) + { + case 0: + { + m_Mobile = reader.ReadMobile(); + + int count = reader.ReadEncodedInt(); + + m_Disliked = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + m_Disliked.Add( reader.ReadString() ); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (Mobile) m_Mobile ); + + writer.WriteEncodedInt( (int) m_Disliked.Count ); + + for ( int i = 0; i < m_Disliked.Count; ++i ) + writer.Write( (string) m_Disliked[i] ); + } + } + + public class PreferencesGump : Gump + { + private Mobile m_From; + private PreferencesEntry m_Entry; + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Entry == null ) + return; + + if ( info.ButtonID != 1 ) + return; + + m_Entry.Disliked.Clear(); + + List arenas = Arena.Arenas; + + for ( int i = 0; i < info.Switches.Length; ++i ) + { + int idx = info.Switches[i]; + + if ( idx >= 0 && idx < arenas.Count ) + m_Entry.Disliked.Add( arenas[idx].Name ); + } + } + + public PreferencesGump( Mobile from, Preferences prefs ) : base( 50, 50 ) + { + m_From = from; + m_Entry = prefs.Find( from ); + + if ( m_Entry == null ) + return; + + List arenas = Arena.Arenas; + + AddPage( 0 ); + + int height = 12 + 20 + (arenas.Count * 31) + 24 + 12; + + AddBackground( 0, 0, 499+40-365, height, 0x2436 ); + + for ( int i = 1; i < arenas.Count; i += 2 ) + AddImageTiled( 12, 32 + (i * 31), 475+40-365, 30, 0x2430 ); + + AddAlphaRegion( 10, 10, 479+40-365, height - 20 ); + + AddColumnHeader( 35, null ); + AddColumnHeader( 115, "Arena" ); + + AddButton( 499+40-365 - 12 - 63 - 4 - 63, height - 12 - 24, 247, 248, 1, GumpButtonType.Reply, 0 ); + AddButton( 499+40-365 - 12 - 63, height - 12 - 24, 241, 242, 2, GumpButtonType.Reply, 0 ); + + for ( int i = 0; i < arenas.Count; ++i ) + { + Arena ar = arenas[i]; + + string name = ar.Name; + + if ( name == null ) + name = "(no name)"; + + int x = 12; + int y = 32 + (i * 31); + + int color = 0xCCFFCC; + + AddCheck( x + 3, y + 1, 9730, 9727, m_Entry.Disliked.Contains(name), i ); + x += 35; + + AddBorderedText( x + 5, y + 5, 115 - 5, name, color, 0 ); + x += 115; + } + } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + private void AddBorderedText( int x, int y, int width, string text, int color, int borderColor ) + { + /*AddColoredText( x - 1, y, width, text, borderColor ); + AddColoredText( x + 1, y, width, text, borderColor ); + AddColoredText( x, y - 1, width, text, borderColor ); + AddColoredText( x, y + 1, width, text, borderColor );*/ + /*AddColoredText( x - 1, y - 1, width, text, borderColor ); + AddColoredText( x + 1, y + 1, width, text, borderColor );*/ + AddColoredText( x, y, width, text, color ); + } + + private void AddColoredText( int x, int y, int width, string text, int color ) + { + if ( color == 0 ) + AddHtml( x, y, width, 20, text, false, false ); + else + AddHtml( x, y, width, 20, Color( text, color ), false, false ); + } + + private int m_ColumnX = 12; + + private void AddColumnHeader( int width, string name ) + { + AddBackground( m_ColumnX, 12, width, 20, 0x242C ); + AddImageTiled( m_ColumnX + 2, 14, width - 4, 16, 0x2430 ); + + if ( name != null ) + AddBorderedText( m_ColumnX, 13, width, Center( name ), 0xFFFFFF, 0 ); + + m_ColumnX += width; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/ReadyGump.cs b/Scripts/Engines/ConPVP/ReadyGump.cs new file mode 100644 index 000000000..610e68b30 --- /dev/null +++ b/Scripts/Engines/ConPVP/ReadyGump.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.ConPVP +{ + public class ReadyGump : Gump + { + private Mobile m_From; + private DuelContext m_Context; + private int m_Count; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public ReadyGump( Mobile from, DuelContext context, int count ) : base( 50, 50 ) + { + m_From = from; + m_Context = context; + m_Count = count; + + ArrayList parts = context.Participants; + + int height = 25 + 20; + + for ( int i = 0; i < parts.Count; ++i ) + { + Participant p = (Participant)parts[i]; + + height += 4; + + if ( p.Players.Length > 1 ) + height += 22; + + height += (p.Players.Length * 22); + } + + height += 25; + + Closable = false; + Dragable = false; + + AddPage( 0 ); + + AddBackground( 0, 0, 260, height, 9250 ); + AddBackground( 10, 10, 240, height - 20, 0xDAC ); + + if ( count == -1 ) + { + AddHtml( 35, 25, 190, 20, Center( "Ready" ), false, false ); + } + else + { + AddHtml( 35, 25, 190, 20, Center( "Starting" ), false, false ); + AddHtml( 35, 25, 190, 20, "
" + count.ToString(), false, false ); + } + + int y = 25 + 20; + + for ( int i = 0; i < parts.Count; ++i ) + { + Participant p = (Participant)parts[i]; + + y += 4; + + bool isAllReady = true; + int yStore = y; + int offset = 0; + + if ( p.Players.Length > 1 ) + { + AddHtml( 35 + 14, y, 176, 20, String.Format( "Participant #{0}", i + 1 ), false, false ); + y += 22; + offset = 10; + } + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + if ( pl != null && pl.Ready ) + { + AddImage( 35 + offset, y + 4, 0x939 ); + } + else + { + AddImage( 35 + offset, y + 4, 0x938 ); + isAllReady = false; + } + + string name = ( pl == null ? "(Empty)" : pl.Mobile.Name ); + + AddHtml( 35 + offset + 14, y, 166, 20, name, false, false ); + + y += 22; + } + + if ( p.Players.Length > 1 ) + AddImage( 35, yStore + 4, isAllReady ? 0x939 : 0x938 ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/ReadyUpGump.cs b/Scripts/Engines/ConPVP/ReadyUpGump.cs new file mode 100644 index 000000000..c981429bd --- /dev/null +++ b/Scripts/Engines/ConPVP/ReadyUpGump.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; + +namespace Server.Engines.ConPVP +{ + public class ReadyUpGump : Gump + { + private Mobile m_From; + private DuelContext m_Context; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public void AddGoldenButton( int x, int y, int bid ) + { + AddButton( x , y , 0xD2, 0xD2, bid, GumpButtonType.Reply, 0 ); + AddButton( x+3, y+3, 0xD8, 0xD8, bid, GumpButtonType.Reply, 0 ); + } + + public ReadyUpGump( Mobile from, DuelContext context ) : base( 50, 50 ) + { + m_From = from; + m_Context = context; + + Closable = false; + AddPage( 0 ); + + if ( context.Rematch ) + { + int height = 25 + 20 + 10 + 22 + 25; + + AddBackground( 0, 0, 210, height, 9250 ); + AddBackground( 10, 10, 190, height - 20, 0xDAC ); + + AddHtml( 35, 25, 140, 20, Center( "Rematch?" ), false, false ); + + AddButton( 35, 55, 247, 248, 1, GumpButtonType.Reply, 0 ); + AddButton( 115, 55, 242, 241, 2, GumpButtonType.Reply, 0 ); + } + else + { + #region Participants + AddPage( 1 ); + + ArrayList parts = context.Participants; + + int height = 25 + 20; + + for ( int i = 0; i < parts.Count; ++i ) + { + Participant p = (Participant)parts[i]; + + height += 4; + + if ( p.Players.Length > 1 ) + height += 22; + + height += (p.Players.Length * 22); + } + + height += 10 + 22 + 25; + + AddBackground( 0, 0, 260, height, 9250 ); + AddBackground( 10, 10, 240, height - 20, 0xDAC ); + + AddHtml( 35, 25, 190, 20, Center( "Participants" ), false, false ); + + int y = 20 + 25; + + for ( int i = 0; i < parts.Count; ++i ) + { + Participant p = (Participant)parts[i]; + + y += 4; + + int offset = 0; + + if ( p.Players.Length > 1 ) + { + AddHtml( 35, y, 176, 20, String.Format( "Team #{0}", i + 1 ), false, false ); + y += 22; + offset = 10; + } + + for ( int j = 0; j < p.Players.Length; ++j ) + { + DuelPlayer pl = p.Players[j]; + + string name = ( pl == null ? "(Empty)" : pl.Mobile.Name ); + + AddHtml( 35 + offset, y, 166, 20, name, false, false ); + + y += 22; + } + } + + y += 8; + + AddHtml( 35, y, 176, 20, "Continue?", false, false ); + + y -= 2; + + AddButton( 102, y, 247, 248, 0, GumpButtonType.Page, 2 ); + AddButton( 169, y, 242, 241, 2, GumpButtonType.Reply, 0 ); + #endregion + + #region Rules + AddPage( 2 ); + + Ruleset ruleset = context.Ruleset; + Ruleset basedef = ruleset.Base; + + height = 25 + 20 + 5 + 20 + 20 + 4; + + int changes = 0; + + BitArray defs; + + if ( ruleset.Flavors.Count > 0 ) + { + defs = new BitArray( basedef.Options ); + + for ( int i = 0; i < ruleset.Flavors.Count; ++i ) + defs.Or( ((Ruleset)ruleset.Flavors[i]).Options ); + + height += ruleset.Flavors.Count * 18; + } + else + { + defs = basedef.Options; + } + + BitArray opts = ruleset.Options; + + for ( int i = 0; i < opts.Length; ++i ) + { + if ( defs[i] != opts[i] ) + ++changes; + } + + height += (changes * 22); + + height += 10 + 22 + 25; + + AddBackground( 0, 0, 260, height, 9250 ); + AddBackground( 10, 10, 240, height - 20, 0xDAC ); + + AddHtml( 35, 25, 190, 20, Center( "Rules" ), false, false ); + + AddHtml( 35, 50, 190, 20, String.Format( "Set: {0}", basedef.Title ), false, false ); + + y = 70; + + for ( int i = 0; i < ruleset.Flavors.Count; ++i, y += 18 ) + AddHtml( 35, y, 190, 20, String.Format( " + {0}", ((Ruleset)ruleset.Flavors[i]).Title ), false, false ); + + y += 4; + + if ( changes > 0 ) + { + AddHtml( 35, y, 190, 20, "Modifications:", false, false ); + y += 20; + + for ( int i = 0; i < opts.Length; ++i ) + { + if ( defs[i] != opts[i] ) + { + string name = ruleset.Layout.FindByIndex( i ); + + if ( name != null ) // sanity + { + AddImage( 35, y, opts[i] ? 0xD3 : 0xD2 ); + AddHtml( 60, y, 165, 22, name, false, false ); + } + + y += 22; + } + } + } + else + { + AddHtml( 35, y, 190, 20, "Modifications: None", false, false ); + y += 20; + } + + y += 8; + + AddHtml( 35, y, 176, 20, "Continue?", false, false ); + + y -= 2; + + AddButton( 102, y, 247, 248, 1, GumpButtonType.Reply, 0 ); + AddButton( 169, y, 242, 241, 3, GumpButtonType.Reply, 0 ); + #endregion + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( !m_Context.Registered || !m_Context.ReadyWait ) + return; + + switch ( info.ButtonID ) + { + case 1: // okay + { + PlayerMobile pm = m_From as PlayerMobile; + + if ( pm == null ) + break; + + pm.DuelPlayer.Ready = true; + m_Context.SendReadyGump(); + + break; + } + case 2: // reject participants + { + m_Context.RejectReady( m_From, "participants" ); + break; + } + case 3: // reject rules + { + m_Context.RejectReady( m_From, "rules" ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/Ruleset.cs b/Scripts/Engines/ConPVP/Ruleset.cs new file mode 100644 index 000000000..2a54eb6e6 --- /dev/null +++ b/Scripts/Engines/ConPVP/Ruleset.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections; + +namespace Server.Engines.ConPVP +{ + public class Ruleset + { + private RulesetLayout m_Layout; + private BitArray m_Options; + private string m_Title; + + private Ruleset m_Base; + private ArrayList m_Flavors = new ArrayList(); + private bool m_Changed; + + public RulesetLayout Layout{ get{ return m_Layout; } } + public BitArray Options{ get{ return m_Options; } } + public string Title{ get{ return m_Title; } set{ m_Title = value; } } + + public Ruleset Base{ get{ return m_Base; } } + public ArrayList Flavors{ get{ return m_Flavors; } } + public bool Changed{ get{ return m_Changed; } set{ m_Changed = value; } } + + public void ApplyDefault( Ruleset newDefault ) + { + m_Base = newDefault; + m_Changed = false; + + m_Options = new BitArray( newDefault.m_Options ); + + ApplyFlavorsTo( this ); + } + + public void ApplyFlavorsTo( Ruleset ruleset ) + { + for ( int i = 0; i < m_Flavors.Count; ++i ) + { + Ruleset flavor = (Ruleset)m_Flavors[i]; + + m_Options.Or( flavor.m_Options ); + } + } + + public void AddFlavor( Ruleset flavor ) + { + if ( m_Flavors.Contains( flavor ) ) + return; + + m_Flavors.Add( flavor ); + m_Options.Or( flavor.m_Options ); + } + + public void RemoveFlavor( Ruleset flavor ) + { + if ( !m_Flavors.Contains( flavor ) ) + return; + + m_Flavors.Remove( flavor ); + m_Options.And( flavor.m_Options.Not() ); + flavor.m_Options.Not(); + } + + public void SetOptionRange( string title, bool value ) + { + RulesetLayout layout = m_Layout.FindByTitle( title ); + + if ( layout == null ) + return; + + for ( int i = 0; i < layout.TotalLength; ++i ) + m_Options[i + layout.Offset] = value; + + m_Changed = true; + } + + public bool GetOption( string title, string option ) + { + int index = 0; + RulesetLayout layout = m_Layout.FindByOption( title, option, ref index ); + + if ( layout == null ) + return true; + + return m_Options[layout.Offset + index]; + } + + public void SetOption( string title, string option, bool value ) + { + int index = 0; + RulesetLayout layout = m_Layout.FindByOption( title, option, ref index ); + + if ( layout == null ) + return; + + m_Options[layout.Offset + index] = value; + + m_Changed = true; + } + + public Ruleset( RulesetLayout layout ) + { + m_Layout = layout; + m_Options = new BitArray( layout.TotalLength ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/RulesetGump.cs b/Scripts/Engines/ConPVP/RulesetGump.cs new file mode 100644 index 000000000..823b4b1cb --- /dev/null +++ b/Scripts/Engines/ConPVP/RulesetGump.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.ConPVP +{ + public class RulesetGump : Gump + { + private Mobile m_From; + private Ruleset m_Ruleset; + private RulesetLayout m_Page; + private DuelContext m_DuelContext; + private bool m_ReadOnly; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public void AddGoldenButton( int x, int y, int bid ) + { + AddButton( x , y , 0xD2, 0xD2, bid, GumpButtonType.Reply, 0 ); + AddButton( x+3, y+3, 0xD8, 0xD8, bid, GumpButtonType.Reply, 0 ); + } + + public RulesetGump( Mobile from, Ruleset ruleset, RulesetLayout page, DuelContext duelContext ) : this( from, ruleset, page, duelContext, false ) + { + } + + public RulesetGump( Mobile from, Ruleset ruleset, RulesetLayout page, DuelContext duelContext, bool readOnly ) : base( readOnly ? 310 : 50, 50 ) + { + m_From = from; + m_Ruleset = ruleset; + m_Page = page; + m_DuelContext = duelContext; + m_ReadOnly = readOnly; + + Dragable = !readOnly; + + from.CloseGump( typeof( RulesetGump ) ); + from.CloseGump( typeof( DuelContextGump ) ); + from.CloseGump( typeof( ParticipantGump ) ); + + RulesetLayout depthCounter = page; + int depth = 0; + + while ( depthCounter != null ) + { + ++depth; + depthCounter = depthCounter.Parent; + } + + int count = page.Children.Length + page.Options.Length; + + AddPage( 0 ); + + int height = 35 + 10 + 2 + (count * 22) + 2 + 30; + + AddBackground( 0, 0, 260, height, 9250 ); + AddBackground( 10, 10, 240, height - 20, 0xDAC ); + + AddHtml( 35, 25, 190, 20, Center( page.Title ), false, false ); + + int x = 35; + int y = 47; + + for ( int i = 0; i < page.Children.Length; ++i ) + { + AddGoldenButton( x, y, 1 + i ); + AddHtml( x + 25, y, 250, 22, page.Children[i].Title, false, false ); + + y += 22; + } + + for ( int i = 0; i < page.Options.Length; ++i ) + { + bool enabled = ruleset.Options[page.Offset + i]; + + if ( readOnly ) + AddImage( x, y, enabled ? 0xD3 : 0xD2 ); + else + AddCheck( x, y, 0xD2, 0xD3, enabled, i ); + + AddHtml( x + 25, y, 250, 22, page.Options[i], false, false ); + + y += 22; + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_DuelContext != null && !m_DuelContext.Registered ) + return; + + if ( !m_ReadOnly ) + { + BitArray opts = new BitArray( m_Page.Options.Length ); + + for ( int i = 0; i < info.Switches.Length; ++i ) + { + int sid = info.Switches[i]; + + if ( sid >= 0 && sid < m_Page.Options.Length ) + opts[sid] = true; + } + + for ( int i = 0; i < opts.Length; ++i ) + { + if ( m_Ruleset.Options[m_Page.Offset + i] != opts[i] ) + { + m_Ruleset.Options[m_Page.Offset + i] = opts[i]; + m_Ruleset.Changed = true; + } + } + } + + int bid = info.ButtonID; + + if ( bid == 0 ) + { + if ( m_Page.Parent != null ) + m_From.SendGump( new RulesetGump( m_From, m_Ruleset, m_Page.Parent, m_DuelContext, m_ReadOnly ) ); + else if ( !m_ReadOnly ) + m_From.SendGump( new PickRulesetGump( m_From, m_DuelContext, m_Ruleset ) ); + } + else + { + bid -= 1; + + if ( bid >= 0 && bid < m_Page.Children.Length ) + m_From.SendGump( new RulesetGump( m_From, m_Ruleset, m_Page.Children[bid], m_DuelContext, m_ReadOnly ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/RulesetLayout.cs b/Scripts/Engines/ConPVP/RulesetLayout.cs new file mode 100644 index 000000000..2cd899ae5 --- /dev/null +++ b/Scripts/Engines/ConPVP/RulesetLayout.cs @@ -0,0 +1,784 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Engines.ConPVP +{ + public class RulesetLayout + { + private static RulesetLayout m_Root; + + public static RulesetLayout Root + { + get + { + if ( m_Root == null ) + { + ArrayList entries = new ArrayList(); + + entries.Add( new RulesetLayout( "Spells", new RulesetLayout[] + { + new RulesetLayout( "1st Circle", "Spells", new string[] + { + "Reactive Armor", "Clumsy", + "Create Food", "Feeblemind", + "Heal", "Magic Arrow", + "Night Sight", "Weaken" + } ), + new RulesetLayout( "2nd Circle", "Spells", new string[] + { + "Agility", "Cunning", + "Cure", "Harm", + "Magic Trap", "Untrap", + "Protection", "Strength" + } ), + new RulesetLayout( "3rd Circle", "Spells", new string[] + { + "Bless", "Fireball", + "Magic Lock", "Poison", + "Telekinesis", "Teleport", + "Unlock Spell", "Wall of Stone" + } ), + new RulesetLayout( "4th Circle", "Spells", new string[] + { + "Arch Cure", "Arch Protection", + "Curse", "Fire Field", + "Greater Heal", "Lightning", + "Mana Drain", "Recall" + } ), + new RulesetLayout( "5th Circle", "Spells", new string[] + { + "Blade Spirits", "Dispel Field", + "Incognito", "Magic Reflection", + "Mind Blast", "Paralyze", + "Poison Field", "Summon Creature" + } ), + new RulesetLayout( "6th Circle", "Spells", new string[] + { + "Dispel", "Energy Bolt", + "Explosion", "Invisibility", + "Mark", "Mass Curse", + "Paralyze Field", "Reveal" + } ), + new RulesetLayout( "7th Circle", "Spells", new string[] + { + "Chain Lightning", "Energy Field", + "Flame Strike", "Gate Travel", + "Mana Vampire", "Mass Dispel", + "Meteor Swarm", "Polymorph" + } ), + new RulesetLayout( "8th Circle", "Spells", new string[] + { + "Earthquake", "Energy Vortex", + "Resurrection", "Air Elemental", + "Summon Daemon", "Earth Elemental", + "Fire Elemental", "Water Elemental" + } ) + } ) ); + + if ( Core.AOS ) + { + entries.Add( new RulesetLayout( "Chivalry", new string[] + { + "Cleanse by Fire", + "Close Wounds", + "Consecrate Weapon", + "Dispel Evil", + "Divine Fury", + "Enemy of One", + "Holy Light", + "Noble Sacrifice", + "Remove Curse", + "Sacred Journey" + } ) ); + + entries.Add( new RulesetLayout( "Necromancy", new string[] + { + "Animate Dead", + "Blood Oath", + "Corpse Skin", + "Curse Weapon", + "Evil Omen", + "Horrific Beast", + "Lich Form", + "Mind Rot", + "Pain Spike", + "Poison Strike", + "Strangle", + "Summon Familiar", + "Vampiric Embrace", + "Vengeful Spirit", + "Wither", + "Wraith Form" + } ) ); + + if ( Core.SE ) + { + entries.Add( new RulesetLayout( "Bushido", new string[] + { + "Confidence", + "Counter Attack", + "Evasion", + "Honorable Execution", + "Lightning Strike", + "Momentum Strike" + } ) ); + + entries.Add( new RulesetLayout( "Ninjitsu", new string[] + { + "Animal Form", + "Backstab", + "Death Strike", + "Focus Attack", + "Ki Attack", + "Mirror Image", + "Shadow Jump", + "Suprise Attack" + } ) ); + + if( Core.ML ) + { + entries.Add( new RulesetLayout( "Spellweaving", new string[] + { + "Arcane Circle", + "Arcane Empowerment", + "Attune Weapon", + "Dryad Allure", + "Essence of Wind", + "Ethereal Voyage", + "Gift of Life", + "Gift of Renewal", + "Immolating Weapon", + "Nature's Fury", + "Reaper Form", + "Summon Fey", + "Summon Fiend", + "Thunderstorm", + "Wildfire", + "Word of Death" + } ) ); + } + } + } + + if ( Core.AOS ) + { + if ( Core.SE ) + { + entries.Add( new RulesetLayout( "Combat Abilities", new string[] + { + "Stun", + "Disarm", + "Armor Ignore", + "Bleed Attack", + "Concussion Blow", + "Crushing Blow", + "Disarm", + "Dismount", + "Double Strike", + "Infectious Strike", + "Mortal Strike", + "Moving Shot", + "Paralyzing Blow", + "Shadow Strike", + "Whirlwind Attack", + "Riding Swipe", + "Frenzied Whirlwind", + "Block", + "Defense Mastery", + "Nerve Strike", + "Talon Strike", + "Feint", + "Dual Wield", + "Double Shot", + "Armor Pierce" + } ) ); + + //TODO: ADD ML ABILITIES + } + else + { + entries.Add( new RulesetLayout( "Combat Abilities", new string[] + { + "Stun", + "Disarm", + "Armor Ignore", + "Bleed Attack", + "Concussion Blow", + "Crushing Blow", + "Disarm", + "Dismount", + "Double Strike", + "Infectious Strike", + "Mortal Strike", + "Moving Shot", + "Paralyzing Blow", + "Shadow Strike", + "Whirlwind Attack" + } ) ); + } + } + else + { + entries.Add( new RulesetLayout( "Combat Abilities", new string[] + { + "Stun", + "Disarm", + "Concussion Blow", + "Crushing Blow", + "Paralyzing Blow" + } ) ); + } + + entries.Add( new RulesetLayout( "Skills", new string[] + { + "Anatomy", + "Detect Hidden", + "Evaluating Intelligence", + "Hiding", + "Poisoning", + "Snooping", + "Stealing", + "Spirit Speak", + "Stealth" + } ) ); + + if ( Core.AOS ) + { + entries.Add( new RulesetLayout( "Weapons", new string[] + { + "Magical", + "Melee", + "Ranged", + "Poisoned", + "Wrestling" + } ) ); + + entries.Add( new RulesetLayout( "Armor", new string[] + { + "Magical", + "Shields" + } ) ); + } + else + { + entries.Add( new RulesetLayout( "Weapons", new string[] + { + "Magical", + "Melee", + "Ranged", + "Poisoned", + "Wrestling", + "Runics" + } ) ); + + entries.Add( new RulesetLayout( "Armor", new string[] + { + "Magical", + "Shields", + "Colored" + } ) ); + } + + if( Core.SE ) + { + entries.Add( new RulesetLayout( "Items", new RulesetLayout[] + { + new RulesetLayout( "Potions", new string[] + { + "Agility", + "Cure", + "Explosion", + "Heal", + "Nightsight", + "Poison", + "Refresh", + "Strength" + } ) + }, + new string[] + { + "Bandages", + "Trapped Containers", + "Bolas", + "Orange Petals", + "Shurikens", + "Fukiya Darts", + "Fire Horns" + } ) ); + } + else + { + entries.Add( new RulesetLayout( "Items", new RulesetLayout[] + { + new RulesetLayout( "Potions", new string[] + { + "Agility", + "Cure", + "Explosion", + "Heal", + "Nightsight", + "Poison", + "Refresh", + "Strength" + } ) + }, + new string[] + { + "Bandages", + "Trapped Containers", + "Bolas", + "Orange Petals" + } ) ); + } + + m_Root = new RulesetLayout( "Rules", (RulesetLayout[])entries.ToArray( typeof( RulesetLayout ) ) ); + m_Root.ComputeOffsets(); + + // Set up default rulesets + + if( !Core.AOS ) + { + + #region Mage 5x + Ruleset m5x = new Ruleset( m_Root ); + + m5x.Title = "Mage 5x"; + + m5x.SetOptionRange( "Spells", true ); + + m5x.SetOption( "Spells", "Wall of Stone", false ); + m5x.SetOption( "Spells", "Fire Field", false ); + m5x.SetOption( "Spells", "Poison Field", false ); + m5x.SetOption( "Spells", "Energy Field", false ); + m5x.SetOption( "Spells", "Reactive Armor", false ); + m5x.SetOption( "Spells", "Protection", false ); + m5x.SetOption( "Spells", "Teleport", false ); + m5x.SetOption( "Spells", "Wall of Stone", false ); + m5x.SetOption( "Spells", "Arch Protection", false ); + m5x.SetOption( "Spells", "Recall", false ); + m5x.SetOption( "Spells", "Blade Spirits", false ); + m5x.SetOption( "Spells", "Incognito", false ); + m5x.SetOption( "Spells", "Magic Reflection", false ); + m5x.SetOption( "Spells", "Paralyze", false ); + m5x.SetOption( "Spells", "Summon Creature", false ); + m5x.SetOption( "Spells", "Invisibility", false ); + m5x.SetOption( "Spells", "Mark", false ); + m5x.SetOption( "Spells", "Paralyze Field", false ); + m5x.SetOption( "Spells", "Energy Field", false ); + m5x.SetOption( "Spells", "Gate Travel", false ); + m5x.SetOption( "Spells", "Polymorph", false ); + m5x.SetOption( "Spells", "Energy Vortex", false ); + m5x.SetOption( "Spells", "Air Elemental", false ); + m5x.SetOption( "Spells", "Summon Daemon", false ); + m5x.SetOption( "Spells", "Earth Elemental", false ); + m5x.SetOption( "Spells", "Fire Elemental", false ); + m5x.SetOption( "Spells", "Water Elemental", false ); + m5x.SetOption( "Spells", "Earthquake", false ); + m5x.SetOption( "Spells", "Meteor Swarm", false ); + m5x.SetOption( "Spells", "Chain Lightning", false ); + m5x.SetOption( "Spells", "Resurrection", false ); + + m5x.SetOption( "Weapons", "Wrestling", true ); + + m5x.SetOption( "Skills", "Anatomy", true ); + m5x.SetOption( "Skills", "Detect Hidden", true ); + m5x.SetOption( "Skills", "Evaluating Intelligence", true ); + + m5x.SetOption( "Items", "Trapped Containers", true ); + #endregion + + #region Mage 7x + Ruleset m7x = new Ruleset( m_Root ); + + m7x.Title = "Mage 7x"; + + m7x.SetOptionRange( "Spells", true ); + + m7x.SetOption( "Spells", "Wall of Stone", false ); + m7x.SetOption( "Spells", "Fire Field", false ); + m7x.SetOption( "Spells", "Poison Field", false ); + m7x.SetOption( "Spells", "Energy Field", false ); + m7x.SetOption( "Spells", "Reactive Armor", false ); + m7x.SetOption( "Spells", "Protection", false ); + m7x.SetOption( "Spells", "Teleport", false ); + m7x.SetOption( "Spells", "Wall of Stone", false ); + m7x.SetOption( "Spells", "Arch Protection", false ); + m7x.SetOption( "Spells", "Recall", false ); + m7x.SetOption( "Spells", "Blade Spirits", false ); + m7x.SetOption( "Spells", "Incognito", false ); + m7x.SetOption( "Spells", "Magic Reflection", false ); + m7x.SetOption( "Spells", "Paralyze", false ); + m7x.SetOption( "Spells", "Summon Creature", false ); + m7x.SetOption( "Spells", "Invisibility", false ); + m7x.SetOption( "Spells", "Mark", false ); + m7x.SetOption( "Spells", "Paralyze Field", false ); + m7x.SetOption( "Spells", "Energy Field", false ); + m7x.SetOption( "Spells", "Gate Travel", false ); + m7x.SetOption( "Spells", "Polymorph", false ); + m7x.SetOption( "Spells", "Energy Vortex", false ); + m7x.SetOption( "Spells", "Air Elemental", false ); + m7x.SetOption( "Spells", "Summon Daemon", false ); + m7x.SetOption( "Spells", "Earth Elemental", false ); + m7x.SetOption( "Spells", "Fire Elemental", false ); + m7x.SetOption( "Spells", "Water Elemental", false ); + m7x.SetOption( "Spells", "Earthquake", false ); + m7x.SetOption( "Spells", "Meteor Swarm", false ); + m7x.SetOption( "Spells", "Chain Lightning", false ); + m7x.SetOption( "Spells", "Resurrection", false ); + + m7x.SetOption( "Combat Abilities", "Stun", true ); + + m7x.SetOption( "Skills", "Anatomy", true ); + m7x.SetOption( "Skills", "Detect Hidden", true ); + m7x.SetOption( "Skills", "Poisoning", true ); + m7x.SetOption( "Skills", "Evaluating Intelligence", true ); + + m7x.SetOption( "Weapons", "Wrestling", true ); + + m7x.SetOption( "Potions", "Refresh", true ); + m7x.SetOption( "Items", "Trapped Containers", true ); + m7x.SetOption( "Items", "Bandages", true ); + #endregion + + #region Standard 7x + Ruleset s7x = new Ruleset( m_Root ); + + s7x.Title = "Standard 7x"; + + s7x.SetOptionRange( "Spells", true ); + + s7x.SetOption( "Spells", "Wall of Stone", false ); + s7x.SetOption( "Spells", "Fire Field", false ); + s7x.SetOption( "Spells", "Poison Field", false ); + s7x.SetOption( "Spells", "Energy Field", false ); + s7x.SetOption( "Spells", "Teleport", false ); + s7x.SetOption( "Spells", "Wall of Stone", false ); + s7x.SetOption( "Spells", "Arch Protection", false ); + s7x.SetOption( "Spells", "Recall", false ); + s7x.SetOption( "Spells", "Blade Spirits", false ); + s7x.SetOption( "Spells", "Incognito", false ); + s7x.SetOption( "Spells", "Magic Reflection", false ); + s7x.SetOption( "Spells", "Paralyze", false ); + s7x.SetOption( "Spells", "Summon Creature", false ); + s7x.SetOption( "Spells", "Invisibility", false ); + s7x.SetOption( "Spells", "Mark", false ); + s7x.SetOption( "Spells", "Paralyze Field", false ); + s7x.SetOption( "Spells", "Energy Field", false ); + s7x.SetOption( "Spells", "Gate Travel", false ); + s7x.SetOption( "Spells", "Polymorph", false ); + s7x.SetOption( "Spells", "Energy Vortex", false ); + s7x.SetOption( "Spells", "Air Elemental", false ); + s7x.SetOption( "Spells", "Summon Daemon", false ); + s7x.SetOption( "Spells", "Earth Elemental", false ); + s7x.SetOption( "Spells", "Fire Elemental", false ); + s7x.SetOption( "Spells", "Water Elemental", false ); + s7x.SetOption( "Spells", "Earthquake", false ); + s7x.SetOption( "Spells", "Meteor Swarm", false ); + s7x.SetOption( "Spells", "Chain Lightning", false ); + s7x.SetOption( "Spells", "Resurrection", false ); + + s7x.SetOptionRange( "Combat Abilities", true ); + + s7x.SetOption( "Skills", "Anatomy", true ); + s7x.SetOption( "Skills", "Detect Hidden", true ); + s7x.SetOption( "Skills", "Poisoning", true ); + s7x.SetOption( "Skills", "Evaluating Intelligence", true ); + + s7x.SetOptionRange( "Weapons", true ); + s7x.SetOption( "Weapons", "Runics", false ); + s7x.SetOptionRange( "Armor", true ); + + s7x.SetOption( "Potions", "Refresh", true ); + s7x.SetOption( "Items", "Bandages", true ); + s7x.SetOption( "Items", "Trapped Containers", true ); + #endregion + + m_Root.Defaults = new Ruleset[] { m5x, m7x, s7x }; + } + else + { + #region Standard All Skills + + Ruleset all = new Ruleset( m_Root ); + + all.Title = "Standard All Skills"; + + + all.SetOptionRange( "Spells", true ); + + all.SetOption( "Spells", "Wall of Stone", false ); + all.SetOption( "Spells", "Fire Field", false ); + all.SetOption( "Spells", "Poison Field", false ); + all.SetOption( "Spells", "Energy Field", false ); + all.SetOption( "Spells", "Teleport", false ); + all.SetOption( "Spells", "Wall of Stone", false ); + all.SetOption( "Spells", "Arch Protection", false ); + all.SetOption( "Spells", "Recall", false ); + all.SetOption( "Spells", "Blade Spirits", false ); + all.SetOption( "Spells", "Incognito", false ); + all.SetOption( "Spells", "Magic Reflection", false ); + all.SetOption( "Spells", "Paralyze", false ); + all.SetOption( "Spells", "Summon Creature", false ); + all.SetOption( "Spells", "Invisibility", false ); + all.SetOption( "Spells", "Mark", false ); + all.SetOption( "Spells", "Paralyze Field", false ); + all.SetOption( "Spells", "Energy Field", false ); + all.SetOption( "Spells", "Gate Travel", false ); + all.SetOption( "Spells", "Polymorph", false ); + all.SetOption( "Spells", "Energy Vortex", false ); + all.SetOption( "Spells", "Air Elemental", false ); + all.SetOption( "Spells", "Summon Daemon", false ); + all.SetOption( "Spells", "Earth Elemental", false ); + all.SetOption( "Spells", "Fire Elemental", false ); + all.SetOption( "Spells", "Water Elemental", false ); + all.SetOption( "Spells", "Earthquake", false ); + all.SetOption( "Spells", "Meteor Swarm", false ); + all.SetOption( "Spells", "Chain Lightning", false ); + all.SetOption( "Spells", "Resurrection", false ); + + all.SetOptionRange( "Necromancy", true ); + all.SetOption( "Necromancy", "Summon Familiar", false ); + all.SetOption( "Necromancy", "Vengeful Spirit", false ); + all.SetOption( "Necromancy", "Animate Dead", false ); + all.SetOption( "Necromancy", "Wither", false ); + all.SetOption( "Necromancy", "Poison Strike", false ); + + all.SetOptionRange( "Chivalry", true ); + all.SetOption( "Chivalry", "Sacred Journey", false ); + all.SetOption( "Chivalry", "Enemy of One", false ); + all.SetOption( "Chivalry", "Noble Sacrifice", false ); + + all.SetOptionRange( "Combat Abilities", true ); + all.SetOption( "Combat Abilities", "Paralyzing Blow", false ); + all.SetOption( "Combat Abilities", "Shadow Strike", false ); + + all.SetOption( "Skills", "Anatomy", true ); + all.SetOption( "Skills", "Detect Hidden", true ); + all.SetOption( "Skills", "Poisoning", true ); + all.SetOption( "Skills", "Spirit Speak", true ); + all.SetOption( "Skills", "Evaluating Intelligence", true ); + + all.SetOptionRange( "Weapons", true ); + all.SetOption( "Weapons", "Poisoned", false ); + + all.SetOptionRange( "Armor", true ); + + all.SetOptionRange( "Ninjitsu", true ); + all.SetOption( "Ninjitsu", "Animal Form", false ); + all.SetOption( "Ninjitsu", "Mirror Image", false ); + all.SetOption( "Ninjitsu", "Backstab", false ); + all.SetOption( "Ninjitsu", "Suprise Attack", false ); + all.SetOption( "Ninjitsu", "Shadow Jump", false ); + + all.SetOptionRange( "Bushido", true ); + + all.SetOptionRange( "Spellweaving", true ); + all.SetOption( "Spellweaving", "Gift of Life", false ); + all.SetOption( "Spellweaving", "Summon Fey", false ); + all.SetOption( "Spellweaving", "Summon Fiend", false ); + all.SetOption( "Spellweaving", "Nature's Fury", false ); + + all.SetOption( "Potions", "Refresh", true ); + all.SetOption( "Items", "Bandages", true ); + all.SetOption( "Items", "Trapped Containers", true ); + + m_Root.Defaults = new Ruleset[] { all }; + #endregion + } + + // Set up flavors + + Ruleset pots = new Ruleset( m_Root ); + + pots.Title = "Potions"; + + pots.SetOptionRange( "Potions", true ); + pots.SetOption( "Potions", "Explosion", false ); + + Ruleset para = new Ruleset( m_Root ); + + para.Title = "Paralyze"; + para.SetOption( "Spells", "Paralyze", true ); + para.SetOption( "Spells", "Paralyze Field", true ); + para.SetOption( "Combat Abilities", "Paralyzing Blow", true ); + + Ruleset fields = new Ruleset( m_Root ); + + fields.Title = "Fields"; + fields.SetOption( "Spells", "Wall of Stone", true ); + fields.SetOption( "Spells", "Fire Field", true ); + fields.SetOption( "Spells", "Poison Field", true ); + fields.SetOption( "Spells", "Energy Field", true ); + fields.SetOption( "Spells", "Wildfire", true ); + + Ruleset area = new Ruleset( m_Root ); + + area.Title = "Area Effect"; + area.SetOption( "Spells", "Earthquake", true ); + area.SetOption( "Spells", "Meteor Swarm", true ); + area.SetOption( "Spells", "Chain Lightning", true ); + area.SetOption( "Necromancy", "Wither", true ); + area.SetOption( "Necromancy", "Poison Strike", true ); + + Ruleset summons = new Ruleset( m_Root ); + + summons.Title = "Summons"; + summons.SetOption( "Spells", "Blade Spirits", true ); + summons.SetOption( "Spells", "Energy Vortex", true ); + summons.SetOption( "Spells", "Air Elemental", true ); + summons.SetOption( "Spells", "Summon Daemon", true ); + summons.SetOption( "Spells", "Earth Elemental", true ); + summons.SetOption( "Spells", "Fire Elemental", true ); + summons.SetOption( "Spells", "Water Elemental", true ); + summons.SetOption( "Necromancy", "Summon Familiar", true ); + summons.SetOption( "Necromancy", "Vengeful Spirit", true ); + summons.SetOption( "Necromancy", "Animate Dead", true ); + summons.SetOption( "Ninjitsu", "Mirror Image", true ); + summons.SetOption( "Spellweaving", "Summon Fey", true ); + summons.SetOption( "Spellweaving", "Summon Fiend", true ); + summons.SetOption( "Spellweaving", "Nature's Fury", true ); + + m_Root.Flavors = new Ruleset[]{ pots, para, fields, area, summons }; + } + + return m_Root; + } + } + + private string m_Title, m_Description; + private string[] m_Options; + + private int m_Offset, m_TotalLength; + + private Ruleset[] m_Defaults; + private Ruleset[] m_Flavors; + + private RulesetLayout m_Parent; + private RulesetLayout[] m_Children; + + public string Title{ get{ return m_Title; } } + public string Description{ get{ return m_Description; } } + public string[] Options{ get{ return m_Options; } } + + public int Offset{ get{ return m_Offset; } } + public int TotalLength{ get{ return m_TotalLength; } } + + public RulesetLayout Parent{ get{ return m_Parent; } } + public RulesetLayout[] Children{ get{ return m_Children; } } + + public Ruleset[] Defaults{ get{ return m_Defaults; } set{ m_Defaults = value; } } + public Ruleset[] Flavors{ get{ return m_Flavors; } set{ m_Flavors = value; } } + + public RulesetLayout FindByTitle( string title ) + { + if ( m_Title == title ) + return this; + + for ( int i = 0; i < m_Children.Length; ++i ) + { + RulesetLayout layout = m_Children[i].FindByTitle( title ); + + if ( layout != null ) + return layout; + } + + return null; + } + + public string FindByIndex( int index ) + { + if ( index >= m_Offset && index < (m_Offset + m_Options.Length) ) + return m_Description + ": " + m_Options[index - m_Offset]; + + for ( int i = 0; i < m_Children.Length; ++i ) + { + string opt = m_Children[i].FindByIndex( index ); + + if ( opt != null ) + return opt; + } + + return null; + } + + public RulesetLayout FindByOption( string title, string option, ref int index ) + { + if ( title == null || m_Title == title ) + { + index = GetOptionIndex( option ); + + if ( index >= 0 ) + return this; + + title = null; + } + + for ( int i = 0; i < m_Children.Length; ++i ) + { + RulesetLayout layout = m_Children[i].FindByOption( title, option, ref index ); + + if ( layout != null ) + return layout; + } + + return null; + } + + public int GetOptionIndex( string option ) + { + return Array.IndexOf( m_Options, option ); + } + + public void ComputeOffsets() + { + int offset = 0; + + RecurseComputeOffsets( ref offset ); + } + + private int RecurseComputeOffsets( ref int offset ) + { + m_Offset = offset; + + offset += m_Options.Length; + m_TotalLength += m_Options.Length; + + for ( int i = 0; i < m_Children.Length; ++i ) + m_TotalLength += m_Children[i].RecurseComputeOffsets( ref offset ); + + return m_TotalLength; + } + + public RulesetLayout( string title, string[] options ) : this( title, title, new RulesetLayout[0], options ) + { + } + + public RulesetLayout( string title, string description, string[] options ) : this( title, description, new RulesetLayout[0], options ) + { + } + + public RulesetLayout( string title, RulesetLayout[] children ) : this( title, title, children, new string[0] ) + { + } + + public RulesetLayout( string title, string description, RulesetLayout[] children ) : this( title, description, children, new string[0] ) + { + } + + public RulesetLayout( string title, RulesetLayout[] children, string[] options ) : this( title, title, children, options ) + { + } + + public RulesetLayout( string title, string description, RulesetLayout[] children, string[] options ) + { + m_Title = title; + m_Description = description; + m_Children = children; + m_Options = options; + + for ( int i = 0; i < children.Length; ++i ) + children[i].m_Parent = this; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/SafeZone.cs b/Scripts/Engines/ConPVP/SafeZone.cs new file mode 100644 index 000000000..244a7d798 --- /dev/null +++ b/Scripts/Engines/ConPVP/SafeZone.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Regions; + +namespace Server.Engines.ConPVP +{ + public class SafeZone : GuardedRegion + { + public static readonly int SafeZonePriority = HouseRegion.HousePriority + 1; + + /*public override bool AllowReds{ get{ return true; } }*/ + + public SafeZone( Rectangle2D area, Point3D goloc, Map map, bool isGuarded ) : base( null, map, SafeZonePriority, area ) + { + GoLocation = goloc; + + this.Disabled = !isGuarded; + + Register(); + } + + public override bool AllowHousing( Mobile from, Point3D p ) + { + if ( from.AccessLevel < AccessLevel.GameMaster ) + return false; + + return base.AllowHousing( from, p ); + } + + public override bool OnMoveInto( Mobile m, Direction d, Point3D newLocation, Point3D oldLocation ) + { + if ( m.Player && Factions.Sigil.ExistsOn( m ) ) + { + m.SendMessage( 0x22, "You are holding a sigil and cannot enter this zone." ); + return false; + } + + PlayerMobile pm = m as PlayerMobile; + + if ( pm == null && m is BaseCreature ) + { + BaseCreature bc = (BaseCreature)m; + + if ( bc.Summoned ) + pm = bc.SummonMaster as PlayerMobile; + } + + if ( pm != null && pm.DuelContext != null && pm.DuelContext.StartedBeginCountdown ) + return true; + + if ( DuelContext.CheckCombat( m ) ) + { + m.SendMessage( 0x22, "You have recently been in combat and cannot enter this zone." ); + return false; + } + + return base.OnMoveInto( m, d, newLocation, oldLocation ); + } + + public override void OnEnter( Mobile m ) + { + m.SendMessage( "You have entered a dueling safezone. No combat other than duels are allowed in this zone." ); + } + + public override void OnExit( Mobile m ) + { + m.SendMessage( "You have left a dueling safezone. Combat is now unrestricted." ); + } + + public override bool CanUseStuckMenu( Mobile m ) + { + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/StakesContainer.cs b/Scripts/Engines/ConPVP/StakesContainer.cs new file mode 100644 index 000000000..1b61a0f94 --- /dev/null +++ b/Scripts/Engines/ConPVP/StakesContainer.cs @@ -0,0 +1,127 @@ +using System; +using System.Text; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Engines.ConPVP +{ +#if false + [Flipable( 0x9A8, 0xE80 )] + public class StakesContainer : LockableContainer + { + private Mobile m_Initiator; + private Participant m_Participant; + private Hashtable m_Owners; + + public override bool CheckItemUse( Mobile from, Item item ) + { + Mobile owner = (Mobile)m_Owners[item]; + + if ( owner != null && owner != from ) + return false; + + return base.CheckItemUse( from, item ); + } + + public override bool CheckTarget( Mobile from, Server.Targeting.Target targ, object targeted ) + { + Mobile owner = (Mobile)m_Owners[targeted]; + + if ( owner != null && owner != from ) + return false; + + return base.CheckTarget( from, targ, targeted ); + } + + public override bool CheckLift(Mobile from, Item item) + { + Mobile owner = (Mobile)m_Owners[item]; + + if ( owner != null && owner != from ) + return false; + + return base.CheckLift( from, item ); + } + + public void ReturnItems() + { + ArrayList items = new ArrayList( this.Items ); + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = (Item)items[i]; + Mobile owner = (Mobile)m_Owners[item]; + + if ( owner == null || owner.Deleted ) + owner = m_Initiator; + + if ( owner == null || owner.Deleted ) + return; + + if ( item.LootType != LootType.Blessed || !owner.PlaceInBackpack( item ) ) + owner.BankBox.DropItem( item ); + } + } + + public override bool TryDropItem( Mobile from, Item dropped, bool sendFullMessage ) + { + if ( m_Participant == null || !m_Participant.Contains( from ) ) + { + if ( sendFullMessage ) + from.SendMessage( "You are not allowed to place items here." ); + + return false; + } + + if ( dropped is Container || dropped.Stackable ) + { + if ( sendFullMessage ) + from.SendMessage( "That item cannot be used as stakes." ); + + return false; + } + + if ( !base.TryDropItem( from, dropped, sendFullMessage ) ) + return false; + + if ( from != null ) + m_Owners[dropped] = from; + + return true; + } + + public override void RemoveItem( Item item ) + { + base.RemoveItem( item ); + m_Owners.Remove( item ); + } + + public StakesContainer( DuelContext context, Participant participant ) : base( 0x9A8 ) + { + Movable = false; + m_Initiator = context.Initiator; + m_Participant = participant; + m_Owners = new Hashtable(); + } + + public StakesContainer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +#endif +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/Tournament.cs b/Scripts/Engines/ConPVP/Tournament.cs new file mode 100644 index 000000000..cdc6f0575 --- /dev/null +++ b/Scripts/Engines/ConPVP/Tournament.cs @@ -0,0 +1,3377 @@ +using System; +using System.Text; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Network; +using Server.ContextMenus; +using Server.Mobiles; +using Server.Targeting; +using System.Collections.Generic; + +namespace Server.Engines.ConPVP +{ + public enum TournamentStage + { + Inactive, + Signup, + Fighting + } + + public enum GroupingType + { + HighVsLow, + Nearest, + Random + } + + public enum TieType + { + Random, + Highest, + Lowest, + FullElimination, + FullAdvancement + } + + public class TournamentRegistrar : Banker + { + private TournamentController m_Tournament; + + [CommandProperty( AccessLevel.GameMaster )] + public TournamentController Tournament{ get{ return m_Tournament; } set{ m_Tournament = value; } } + + [Constructable] + public TournamentRegistrar() + { + Timer.DelayCall( TimeSpan.FromSeconds( 30.0 ), TimeSpan.FromSeconds( 30.0 ), new TimerCallback( Announce_Callback ) ); + } + + private void Announce_Callback() + { + Tournament tourny = null; + + if ( m_Tournament != null ) + tourny = m_Tournament.Tournament; + + if ( tourny != null && tourny.Stage == TournamentStage.Signup ) + PublicOverheadMessage( MessageType.Regular, 0x35, false, "Come one, come all! Do you aspire to be a fighter of great renown? Join this tournament and show the world your abilities." ); + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + Tournament tourny = null; + + if ( m_Tournament != null ) + tourny = m_Tournament.Tournament; + + if ( InRange( m, 4 ) && !InRange( oldLocation, 4 ) && tourny != null && tourny.Stage == TournamentStage.Signup && m.CanBeginAction( this ) ) + { + Ladder ladder = Ladder.Instance; + + if ( ladder != null ) + { + LadderEntry entry = ladder.Find( m ); + + if ( entry != null && Ladder.GetLevel( entry.Experience ) < tourny.LevelRequirement ) + return; + } + + if ( tourny.HasParticipant( m ) ) + return; + + PrivateOverheadMessage( MessageType.Regular, 0x35, false, String.Format( "Hello m'{0}. Dost thou wish to enter this tournament? You need only to write your name in this book.", m.Female ? "Lady" : "Lord" ), m.NetState ); + m.BeginAction( this ); + Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), new TimerStateCallback( ReleaseLock_Callback ), m ); + } + } + + private void ReleaseLock_Callback( object obj ) + { + ((Mobile)obj).EndAction( this ); + } + + public TournamentRegistrar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( (Item) m_Tournament ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Tournament = reader.ReadItem() as TournamentController; + break; + } + } + + Timer.DelayCall( TimeSpan.FromSeconds( 30.0 ), TimeSpan.FromSeconds( 30.0 ), new TimerCallback( Announce_Callback ) ); + } + } + + public class TournamentSignupItem : Item + { + private TournamentController m_Tournament; + private Mobile m_Registrar; + + [CommandProperty( AccessLevel.GameMaster )] + public TournamentController Tournament{ get{ return m_Tournament; } set{ m_Tournament = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Registrar{ get{ return m_Registrar; } set{ m_Registrar = value; } } + + public override string DefaultName + { + get { return "tournament signup book"; } + } + + [Constructable] + public TournamentSignupItem() : base( 4029 ) + { + Movable = false; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that + } + else if ( m_Tournament != null ) + { + Tournament tourny = m_Tournament.Tournament; + + if ( tourny != null ) + { + if ( m_Registrar != null ) + m_Registrar.Direction = m_Registrar.GetDirectionTo( this ); + + switch ( tourny.Stage ) + { + case TournamentStage.Fighting: + { + if ( m_Registrar != null ) + { + if ( tourny.HasParticipant( from ) ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "Excuse me? You are already signed up.", from.NetState ); + } + else + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "The tournament has already begun. You are too late to signup now.", from.NetState ); + } + } + + break; + } + case TournamentStage.Inactive: + { + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "The tournament is closed.", from.NetState ); + + break; + } + case TournamentStage.Signup: + { + Ladder ladder = Ladder.Instance; + + if ( ladder != null ) + { + LadderEntry entry = ladder.Find( from ); + + if ( entry != null && Ladder.GetLevel( entry.Experience ) < tourny.LevelRequirement ) + { + if ( m_Registrar != null ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "You have not yet proven yourself a worthy dueler.", from.NetState ); + } + + break; + } + } + + if ( from.HasGump( typeof( AcceptTeamGump ) ) ) + { + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "You must first respond to the offer I've given you.", from.NetState ); + } + else if ( from.HasGump( typeof( AcceptDuelGump ) ) ) + { + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "You must first cancel your duel offer.", from.NetState ); + } + else if ( from is PlayerMobile && ((PlayerMobile)from).DuelContext != null ) + { + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "You are already participating in a duel.", from.NetState ); + } + else if ( !tourny.HasParticipant( from ) ) + { + ArrayList players = new ArrayList(); + players.Add(from); + from.CloseGump( typeof( ConfirmSignupGump ) ); + from.SendGump( new ConfirmSignupGump( from, m_Registrar, tourny, players ) ); + } + else if ( m_Registrar != null ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "You have already entered this tournament.", from.NetState ); + } + + break; + } + } + } + } + } + + public TournamentSignupItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( (Item) m_Tournament ); + writer.Write( (Mobile) m_Registrar ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Tournament = reader.ReadItem() as TournamentController; + m_Registrar = reader.ReadMobile(); + break; + } + } + } + } + + public class ConfirmSignupGump : Gump + { + private Mobile m_From; + private Tournament m_Tournament; + private ArrayList m_Players; + private Mobile m_Registrar; + + private const int BlackColor32 = 0x000008; + private const int LabelColor32 = 0xFFFFFF; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + private void AddBorderedText( int x, int y, int width, int height, string text, int color, int borderColor ) + { + AddColoredText( x - 1, y - 1, width, height, text, borderColor ); + AddColoredText( x - 1, y + 1, width, height, text, borderColor ); + AddColoredText( x + 1, y - 1, width, height, text, borderColor ); + AddColoredText( x + 1, y + 1, width, height, text, borderColor ); + AddColoredText( x, y, width, height, text, color ); + } + + private void AddColoredText( int x, int y, int width, int height, string text, int color ) + { + if ( color == 0 ) + AddHtml( x, y, width, height, text, false, false ); + else + AddHtml( x, y, width, height, Color( text, color ), false, false ); + } + + public void AddGoldenButton( int x, int y, int bid ) + { + AddButton( x , y , 0xD2, 0xD2, bid, GumpButtonType.Reply, 0 ); + AddButton( x+3, y+3, 0xD8, 0xD8, bid, GumpButtonType.Reply, 0 ); + } + + public ConfirmSignupGump( Mobile from, Mobile registrar, Tournament tourny, ArrayList players ) : base( 50, 50 ) + { + m_From = from; + m_Registrar = registrar; + m_Tournament = tourny; + m_Players = players; + + m_From.CloseGump( typeof( AcceptTeamGump ) ); + m_From.CloseGump( typeof( AcceptDuelGump ) ); + m_From.CloseGump( typeof( DuelContextGump ) ); + m_From.CloseGump( typeof( ConfirmSignupGump ) ); + + #region Rules + Ruleset ruleset = tourny.Ruleset; + Ruleset basedef = ruleset.Base; + + int height = 185 + 60 + 12; + + int changes = 0; + + BitArray defs; + + if ( ruleset.Flavors.Count > 0 ) + { + defs = new BitArray( basedef.Options ); + + for ( int i = 0; i < ruleset.Flavors.Count; ++i ) + defs.Or( ((Ruleset)ruleset.Flavors[i]).Options ); + + height += ruleset.Flavors.Count * 18; + } + else + { + defs = basedef.Options; + } + + BitArray opts = ruleset.Options; + + for ( int i = 0; i < opts.Length; ++i ) + { + if ( defs[i] != opts[i] ) + ++changes; + } + + height += (changes * 22); + + height += 10 + 22 + 25 + 25; + + if ( tourny.PlayersPerParticipant > 1 ) + height += 36 + (tourny.PlayersPerParticipant * 20); + #endregion + + Closable = false; + + AddPage( 0 ); + + //AddBackground( 0, 0, 400, 220, 9150 ); + AddBackground( 1, 1, 398, height, 3600 ); + //AddBackground( 16, 15, 369, 189, 9100 ); + + AddImageTiled( 16, 15, 369, height - 29, 3604 ); + AddAlphaRegion( 16, 15, 369, height - 29 ); + + AddImage( 215, -43, 0xEE40 ); + //AddImage( 330, 141, 0x8BA ); + + StringBuilder sb = new StringBuilder(); + + if ( tourny.TournyType == TournyType.FreeForAll ) + { + sb.Append( "FFA" ); + } + else if ( tourny.TournyType == TournyType.RandomTeam ) + { + sb.Append( "Team" ); + } + else if ( tourny.TournyType == TournyType.RedVsBlue ) + { + sb.Append( "Red v Blue" ); + } + else + { + for ( int i = 0; i < tourny.ParticipantsPerMatch; ++i ) + { + if ( sb.Length > 0 ) + sb.Append( 'v' ); + + sb.Append( tourny.PlayersPerParticipant ); + } + } + + if ( tourny.EventController != null ) + sb.Append( ' ' ).Append( tourny.EventController.Title ); + + sb.Append( " Tournament Signup" ); + + AddBorderedText( 22, 22, 294, 20, Center( sb.ToString() ), LabelColor32, BlackColor32 ); + AddBorderedText( 22, 50, 294, 40, "You have requested to join the tournament. Do you accept the rules?", 0xB0C868, BlackColor32 ); + + AddImageTiled( 32, 88, 264, 1, 9107 ); + AddImageTiled( 42, 90, 264, 1, 9157 ); + + #region Rules + int y = 100; + + string groupText = null; + + switch ( tourny.GroupType ) + { + case GroupingType.HighVsLow: groupText = "High vs Low"; break; + case GroupingType.Nearest: groupText = "Closest opponent"; break; + case GroupingType.Random: groupText = "Random"; break; + } + + AddBorderedText( 35, y, 190, 20, String.Format( "Grouping: {0}", groupText ), LabelColor32, BlackColor32 ); + y += 20; + + string tieText = null; + + switch ( tourny.TieType ) + { + case TieType.Random: tieText = "Random"; break; + case TieType.Highest: tieText = "Highest advances"; break; + case TieType.Lowest: tieText = "Lowest advances"; break; + case TieType.FullAdvancement: tieText = ( tourny.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances" ); break; + case TieType.FullElimination: tieText = ( tourny.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated" ); break; + } + + AddBorderedText( 35, y, 190, 20, String.Format( "Tiebreaker: {0}", tieText ), LabelColor32, BlackColor32 ); + y += 20; + + string sdText = "Off"; + + if ( tourny.SuddenDeath > TimeSpan.Zero ) + { + sdText = String.Format( "{0}:{1:D2}", (int) tourny.SuddenDeath.TotalMinutes, tourny.SuddenDeath.Seconds ); + + if ( tourny.SuddenDeathRounds > 0 ) + sdText = String.Format( "{0} (first {1} rounds)", sdText, tourny.SuddenDeathRounds ); + else + sdText = String.Format( "{0} (all rounds)", sdText ); + } + + AddBorderedText( 35, y, 240, 20, String.Format( "Sudden Death: {0}", sdText ), LabelColor32, BlackColor32 ); + y += 20; + + y += 6; + AddImageTiled( 32, y-1, 264, 1, 9107 ); + AddImageTiled( 42, y+1, 264, 1, 9157 ); + y += 6; + + AddBorderedText( 35, y, 190, 20, String.Format( "Ruleset: {0}", basedef.Title ), LabelColor32, BlackColor32 ); + y += 20; + + for ( int i = 0; i < ruleset.Flavors.Count; ++i, y += 18 ) + AddBorderedText( 35, y, 190, 20, String.Format( " + {0}", ((Ruleset)ruleset.Flavors[i]).Title ), LabelColor32, BlackColor32 ); + + y += 4; + + if ( changes > 0 ) + { + AddBorderedText( 35, y, 190, 20, "Modifications:", LabelColor32, BlackColor32 ); + y += 20; + + for ( int i = 0; i < opts.Length; ++i ) + { + if ( defs[i] != opts[i] ) + { + string name = ruleset.Layout.FindByIndex( i ); + + if ( name != null ) // sanity + { + AddImage( 35, y, opts[i] ? 0xD3 : 0xD2 ); + AddBorderedText( 60, y, 165, 22, name, LabelColor32, BlackColor32 ); + } + + y += 22; + } + } + } + else + { + AddBorderedText( 35, y, 190, 20, "Modifications: None", LabelColor32, BlackColor32 ); + y += 20; + } + #endregion + + #region Team + if ( tourny.PlayersPerParticipant > 1 ) + { + y += 8; + AddImageTiled( 32, y-1, 264, 1, 9107 ); + AddImageTiled( 42, y+1, 264, 1, 9157 ); + y += 8; + + AddBorderedText( 35, y, 190, 20, "Your Team", LabelColor32, BlackColor32 ); + y += 20; + + for ( int i = 0; i < players.Count; ++i, y += 20 ) + { + if ( i == 0 ) + AddImage( 35, y, 0xD2 ); + else + AddGoldenButton( 35, y, 1 + i ); + + AddBorderedText( 60, y, 200, 20, ((Mobile)players[i]).Name, LabelColor32, BlackColor32 ); + } + + for ( int i = players.Count; i < tourny.PlayersPerParticipant; ++i, y += 20 ) + { + if ( i == 0 ) + AddImage( 35, y, 0xD2 ); + else + AddGoldenButton( 35, y, 1 + i ); + + AddBorderedText( 60, y, 200, 20, "(Empty)", LabelColor32, BlackColor32 ); + } + } + #endregion + + y += 8; + AddImageTiled( 32, y-1, 264, 1, 9107 ); + AddImageTiled( 42, y+1, 264, 1, 9157 ); + y += 8; + + AddRadio( 24, y, 9727, 9730, true, 1 ); + AddBorderedText( 60, y+5, 250, 20, "Yes, I wish to join the tournament.", LabelColor32, BlackColor32 ); + y += 35; + + AddRadio( 24, y, 9727, 9730, false, 2 ); + AddBorderedText( 60, y+5, 250, 20, "No, I do not wish to join.", LabelColor32, BlackColor32 ); + y += 35; + + y -= 3; + AddButton( 314, y, 247, 248, 1, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 && info.IsSwitched( 1 ) ) + { + Tournament tourny = m_Tournament; + Mobile from = m_From; + + switch ( tourny.Stage ) + { + case TournamentStage.Fighting: + { + if ( m_Registrar != null ) + { + if ( m_Tournament.HasParticipant( from ) ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "Excuse me? You are already signed up.", from.NetState ); + } + else + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "The tournament has already begun. You are too late to signup now.", from.NetState ); + } + } + + break; + } + case TournamentStage.Inactive: + { + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "The tournament is closed.", from.NetState ); + + break; + } + case TournamentStage.Signup: + { + if ( m_Players.Count != tourny.PlayersPerParticipant ) + { + if ( m_Registrar != null ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "You have not yet chosen your team.", from.NetState ); + } + + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + break; + } + + Ladder ladder = Ladder.Instance; + + for ( int i = 0; i < m_Players.Count; ++i ) + { + Mobile mob = (Mobile)m_Players[i]; + + LadderEntry entry = ( ladder == null ? null : ladder.Find( mob ) ); + + if ( entry != null && Ladder.GetLevel( entry.Experience ) < tourny.LevelRequirement ) + { + if ( m_Registrar != null ) + { + if ( mob == from ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "You have not yet proven yourself a worthy dueler.", from.NetState ); + } + else + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, String.Format( "{0} has not yet proven themselves a worthy dueler.", mob.Name ), from.NetState ); + } + } + + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + return; + } + else if ( tourny.HasParticipant( mob ) ) + { + if ( m_Registrar != null ) + { + if ( mob == from ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "You have already entered this tournament.", from.NetState ); + } + else + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, String.Format( "{0} has already entered this tournament.", mob.Name ), from.NetState ); + } + } + + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + return; + } + else if ( mob is PlayerMobile && ((PlayerMobile)mob).DuelContext != null ) + { + if ( m_Registrar != null ) + { + if ( mob == from ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, "You are already assigned to a duel. You must yield it before joining this tournament.", from.NetState ); + } + else + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, String.Format( "{0} is already assigned to a duel. They must yield it before joining this tournament.", mob.Name ), from.NetState ); + } + } + + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + return; + } + } + + if ( m_Registrar != null ) + { + string fmt; + + if ( tourny.PlayersPerParticipant == 1 ) + fmt = "As you say m'{0}. I've written your name to the bracket. The tournament will begin {1}."; + else if ( tourny.PlayersPerParticipant == 2 ) + fmt = "As you wish m'{0}. The tournament will begin {1}, but first you must name your partner."; + else + fmt = "As you wish m'{0}. The tournament will begin {1}, but first you must name your team."; + + string timeUntil; + int minutesUntil = (int)Math.Round( ( (tourny.SignupStart + tourny.SignupPeriod) - DateTime.Now ).TotalMinutes ); + + if ( minutesUntil == 0 ) + timeUntil = "momentarily"; + else + timeUntil = String.Format( "in {0} minute{1}", minutesUntil, minutesUntil == 1 ? "" : "s" ); + + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x35, false, String.Format( fmt, from.Female ? "Lady" : "Lord", timeUntil ), from.NetState ); + } + + TournyParticipant part = new TournyParticipant( from ); + part.Players.Clear(); + part.Players.AddRange( m_Players ); + + tourny.Participants.Add( part ); + + break; + } + } + } + else if ( info.ButtonID > 1 ) + { + int index = info.ButtonID-1; + + if ( index > 0 && index < m_Players.Count ) + { + m_Players.RemoveAt( index ); + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + } + else if ( m_Players.Count < m_Tournament.PlayersPerParticipant ) + { + m_From.BeginTarget( 12, false, TargetFlags.None, new TargetCallback( AddPlayer_OnTarget ) ); + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + } + } + } + + private void AddPlayer_OnTarget( Mobile from, object obj ) + { + Mobile mob = obj as Mobile; + + if ( mob == null || mob == from ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "Excuse me?", from.NetState ); + } + else if ( !mob.Player ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( mob.Body.IsHuman ) + mob.SayTo( from, 1005443 ); // Nay, I would rather stay here and watch a nail rust. + else + mob.SayTo( from, 1005444 ); // The creature ignores your offer. + } + else if ( AcceptDuelGump.IsIgnored( mob, from ) || mob.Blessed ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "They ignore your invitation.", from.NetState ); + } + else + { + PlayerMobile pm = mob as PlayerMobile; + + if ( pm == null ) + return; + + if ( pm.DuelContext != null ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "They are already assigned to another duel.", from.NetState ); + } + else if ( mob.HasGump( typeof( AcceptTeamGump ) ) ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "They have already been offered a partnership.", from.NetState ); + } + else if ( mob.HasGump( typeof( ConfirmSignupGump ) ) ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "They are already trying to join this tournament.", from.NetState ); + } + else if ( m_Players.Contains( mob ) ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "You have already named them as a team member.", from.NetState ); + } + else if ( m_Tournament.HasParticipant( mob ) ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "They have already entered this tournament.", from.NetState ); + } + else if ( m_Players.Count >= m_Tournament.PlayersPerParticipant ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "Your team is full.", from.NetState ); + } + else + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + mob.SendGump( new AcceptTeamGump( from, mob, m_Tournament, m_Registrar, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x59, false, String.Format( "As you command m'{0}. I've given your offer to {1}.", from.Female ? "Lady" : "Lord", mob.Name ), from.NetState ); + } + } + } + } + + public class AcceptTeamGump : Gump + { + private bool m_Active; + + private Mobile m_From; + private Mobile m_Requested; + private Tournament m_Tournament; + private Mobile m_Registrar; + private ArrayList m_Players; + + private const int BlackColor32 = 0x000008; + private const int LabelColor32 = 0xFFFFFF; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + private void AddBorderedText( int x, int y, int width, int height, string text, int color, int borderColor ) + { + AddColoredText( x - 1, y - 1, width, height, text, borderColor ); + AddColoredText( x - 1, y + 1, width, height, text, borderColor ); + AddColoredText( x + 1, y - 1, width, height, text, borderColor ); + AddColoredText( x + 1, y + 1, width, height, text, borderColor ); + AddColoredText( x, y, width, height, text, color ); + } + + private void AddColoredText( int x, int y, int width, int height, string text, int color ) + { + if ( color == 0 ) + AddHtml( x, y, width, height, text, false, false ); + else + AddHtml( x, y, width, height, Color( text, color ), false, false ); + } + + public AcceptTeamGump( Mobile from, Mobile requested, Tournament tourny, Mobile registrar, ArrayList players ) : base( 50, 50 ) + { + m_From = from; + m_Requested = requested; + m_Tournament = tourny; + m_Registrar = registrar; + m_Players = players; + + m_Active = true; + + #region Rules + Ruleset ruleset = tourny.Ruleset; + Ruleset basedef = ruleset.Base; + + int height = 185 + 35 + 60 + 12; + + int changes = 0; + + BitArray defs; + + if ( ruleset.Flavors.Count > 0 ) + { + defs = new BitArray( basedef.Options ); + + for ( int i = 0; i < ruleset.Flavors.Count; ++i ) + defs.Or( ((Ruleset)ruleset.Flavors[i]).Options ); + + height += ruleset.Flavors.Count * 18; + } + else + { + defs = basedef.Options; + } + + BitArray opts = ruleset.Options; + + for ( int i = 0; i < opts.Length; ++i ) + { + if ( defs[i] != opts[i] ) + ++changes; + } + + height += (changes * 22); + + height += 10 + 22 + 25 + 25; + #endregion + + Closable = false; + + AddPage( 0 ); + + AddBackground( 1, 1, 398, height, 3600 ); + + AddImageTiled( 16, 15, 369, height - 29, 3604 ); + AddAlphaRegion( 16, 15, 369, height - 29 ); + + AddImage( 215, -43, 0xEE40 ); + + StringBuilder sb = new StringBuilder(); + + if ( tourny.TournyType == TournyType.FreeForAll ) + { + sb.Append( "FFA" ); + } + else if ( tourny.TournyType == TournyType.RandomTeam ) + { + sb.Append( tourny.ParticipantsPerMatch ); + sb.Append( "-Team" ); + } + else if ( tourny.TournyType == TournyType.RedVsBlue ) + { + sb.Append( "Red v Blue" ); + } + else + { + for ( int i = 0; i < tourny.ParticipantsPerMatch; ++i ) + { + if ( sb.Length > 0 ) + sb.Append( 'v' ); + + sb.Append( tourny.PlayersPerParticipant ); + } + } + + if ( tourny.EventController != null ) + sb.Append( ' ' ).Append( tourny.EventController.Title ); + + sb.Append( " Tournament Invitation" ); + + AddBorderedText( 22, 22, 294, 20, Center( sb.ToString() ), LabelColor32, BlackColor32 ); + + AddBorderedText( 22, 50, 294, 40, + String.Format( "You have been asked to partner with {0} in a tournament. Do you accept?", from.Name ), + 0xB0C868, BlackColor32 ); + + AddImageTiled( 32, 88, 264, 1, 9107 ); + AddImageTiled( 42, 90, 264, 1, 9157 ); + + #region Rules + int y = 100; + + string groupText = null; + + switch ( tourny.GroupType ) + { + case GroupingType.HighVsLow: groupText = "High vs Low"; break; + case GroupingType.Nearest: groupText = "Closest opponent"; break; + case GroupingType.Random: groupText = "Random"; break; + } + + AddBorderedText( 35, y, 190, 20, String.Format( "Grouping: {0}", groupText ), LabelColor32, BlackColor32 ); + y += 20; + + string tieText = null; + + switch ( tourny.TieType ) + { + case TieType.Random: tieText = "Random"; break; + case TieType.Highest: tieText = "Highest advances"; break; + case TieType.Lowest: tieText = "Lowest advances"; break; + case TieType.FullAdvancement: tieText = ( tourny.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances" ); break; + case TieType.FullElimination: tieText = ( tourny.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated" ); break; + } + + AddBorderedText( 35, y, 190, 20, String.Format( "Tiebreaker: {0}", tieText ), LabelColor32, BlackColor32 ); + y += 20; + + string sdText = "Off"; + + if ( tourny.SuddenDeath > TimeSpan.Zero ) + { + sdText = String.Format( "{0}:{1:D2}", (int) tourny.SuddenDeath.TotalMinutes, tourny.SuddenDeath.Seconds ); + + if ( tourny.SuddenDeathRounds > 0 ) + sdText = String.Format( "{0} (first {1} rounds)", sdText, tourny.SuddenDeathRounds ); + else + sdText = String.Format( "{0} (all rounds)", sdText ); + } + + AddBorderedText( 35, y, 240, 20, String.Format( "Sudden Death: {0}", sdText ), LabelColor32, BlackColor32 ); + y += 20; + + y += 6; + AddImageTiled( 32, y-1, 264, 1, 9107 ); + AddImageTiled( 42, y+1, 264, 1, 9157 ); + y += 6; + + AddBorderedText( 35, y, 190, 20, String.Format( "Ruleset: {0}", basedef.Title ), LabelColor32, BlackColor32 ); + y += 20; + + for ( int i = 0; i < ruleset.Flavors.Count; ++i, y += 18 ) + AddBorderedText( 35, y, 190, 20, String.Format( " + {0}", ((Ruleset)ruleset.Flavors[i]).Title ), LabelColor32, BlackColor32 ); + + y += 4; + + if ( changes > 0 ) + { + AddBorderedText( 35, y, 190, 20, "Modifications:", LabelColor32, BlackColor32 ); + y += 20; + + for ( int i = 0; i < opts.Length; ++i ) + { + if ( defs[i] != opts[i] ) + { + string name = ruleset.Layout.FindByIndex( i ); + + if ( name != null ) // sanity + { + AddImage( 35, y, opts[i] ? 0xD3 : 0xD2 ); + AddBorderedText( 60, y, 165, 22, name, LabelColor32, BlackColor32 ); + } + + y += 22; + } + } + } + else + { + AddBorderedText( 35, y, 190, 20, "Modifications: None", LabelColor32, BlackColor32 ); + y += 20; + } + #endregion + + y += 8; + AddImageTiled( 32, y-1, 264, 1, 9107 ); + AddImageTiled( 42, y+1, 264, 1, 9157 ); + y += 8; + + AddRadio( 24, y, 9727, 9730, true, 1 ); + AddBorderedText( 60, y+5, 250, 20, "Yes, I will join them.", LabelColor32, BlackColor32 ); + y += 35; + + AddRadio( 24, y, 9727, 9730, false, 2 ); + AddBorderedText( 60, y+5, 250, 20, "No, I do not wish to fight.", LabelColor32, BlackColor32 ); + y += 35; + + AddRadio( 24, y, 9727, 9730, false, 3 ); + AddBorderedText( 60, y+5, 270, 20, "No, most certainly not. Do not ask again.", LabelColor32, BlackColor32 ); + y += 35; + + y -= 3; + AddButton( 314, y, 247, 248, 1, GumpButtonType.Reply, 0 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 15.0 ), new TimerCallback( AutoReject ) ); + } + + public void AutoReject() + { + if ( !m_Active ) + return; + + m_Active = false; + + m_Requested.CloseGump( typeof( AcceptTeamGump ) ); + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, String.Format( "{0} seems unresponsive.", m_Requested.Name ), m_From.NetState ); + + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, String.Format( "You have declined the partnership with {0}.", m_From.Name ), m_Requested.NetState ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = m_From; + Mobile mob = m_Requested; + + if ( info.ButtonID != 1 || !m_Active ) + return; + + m_Active = false; + + if ( info.IsSwitched( 1 ) ) + { + PlayerMobile pm = mob as PlayerMobile; + + if ( pm == null ) + return; + + if ( AcceptDuelGump.IsIgnored( mob, from ) || mob.Blessed ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "They ignore your invitation.", from.NetState ); + } + else if ( pm.DuelContext != null ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "They are already assigned to another duel.", from.NetState ); + } + else if ( m_Players.Contains( mob ) ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "You have already named them as a team member.", from.NetState ); + } + else if ( m_Tournament.HasParticipant( mob ) ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "They have already entered this tournament.", from.NetState ); + } + else if ( m_Players.Count >= m_Tournament.PlayersPerParticipant ) + { + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, "Your team is full.", from.NetState ); + } + else + { + m_Players.Add( mob ); + + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x59, false, String.Format( "{0} has accepted your offer of partnership.", mob.Name ), from.NetState ); + + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x59, false, String.Format( "You have accepted the partnership with {0}.", from.Name ), mob.NetState ); + } + } + } + else + { + if ( info.IsSwitched( 3 ) ) + AcceptDuelGump.BeginIgnore( m_Requested, m_From ); + + m_From.SendGump( new ConfirmSignupGump( m_From, m_Registrar, m_Tournament, m_Players ) ); + + if ( m_Registrar != null ) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, String.Format( "{0} has declined your offer of partnership.", mob.Name ), from.NetState ); + + m_Registrar.PrivateOverheadMessage( MessageType.Regular, + 0x22, false, String.Format( "You have declined the partnership with {0}.", from.Name ), mob.NetState ); + } + } + } + } + + public class TournamentController : Item + { + private Tournament m_Tournament; + + [CommandProperty( AccessLevel.GameMaster )] + public Tournament Tournament{ get{ return m_Tournament; } set{} } + + private static ArrayList m_Instances = new ArrayList(); + + public static bool IsActive + { + get + { + for ( int i = 0; i < m_Instances.Count; ++i ) + { + TournamentController controller = (TournamentController)m_Instances[i]; + + if ( controller != null && !controller.Deleted && controller.Tournament != null && controller.Tournament.Stage!=TournamentStage.Inactive ) + return true; + } + + return false; + } + } + + public override string DefaultName + { + get { return "tournament controller"; } + } + + [Constructable] + public TournamentController() : base( 0x1B7A ) + { + Visible = false; + Movable = false; + + m_Tournament = new Tournament(); + m_Instances.Add( this ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.AccessLevel >= AccessLevel.GameMaster && m_Tournament != null ) + { + list.Add( new EditEntry( m_Tournament ) ); + + if ( m_Tournament.CurrentStage == TournamentStage.Inactive ) + list.Add( new StartEntry( m_Tournament ) ); + } + } + + private class EditEntry : ContextMenuEntry + { + private Tournament m_Tournament; + + public EditEntry( Tournament tourny ) : base( 5101 ) + { + m_Tournament = tourny; + } + + public override void OnClick() + { + Owner.From.SendGump( new PropertiesGump( Owner.From, m_Tournament ) ); + } + } + + private class StartEntry : ContextMenuEntry + { + private Tournament m_Tournament; + + public StartEntry( Tournament tourny ) : base( 5113 ) + { + m_Tournament = tourny; + } + + public override void OnClick() + { + if ( m_Tournament.Stage == TournamentStage.Inactive ) + { + m_Tournament.SignupStart = DateTime.Now; + m_Tournament.Stage = TournamentStage.Signup; + m_Tournament.Participants.Clear(); + m_Tournament.Pyramid.Levels.Clear(); + m_Tournament.Alert( "Hear ye! Hear ye!", "Tournament signup has opened. You can enter by signing up with the registrar." ); + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster && m_Tournament != null ) + { + from.CloseGump( typeof( PickRulesetGump ) ); + from.CloseGump( typeof( RulesetGump ) ); + from.SendGump( new PickRulesetGump( from, null, m_Tournament.Ruleset ) ); + } + } + + public TournamentController( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + m_Tournament.Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Tournament = new Tournament( reader ); + break; + } + } + + m_Instances.Add( this ); + } + + public override void OnDelete() + { + base.OnDelete(); + + m_Instances.Remove( this ); + } + } + + public enum TournyType + { + Standard, + FreeForAll, + RandomTeam, + RedVsBlue + } + + [PropertyObject] + public class Tournament + { + private int m_ParticipantsPerMatch; + private int m_PlayersPerParticipant; + private int m_LevelRequirement; + private TournyPyramid m_Pyramid; + private Ruleset m_Ruleset; + + private ArrayList m_Arenas; + private ArrayList m_Participants; + private ArrayList m_Undefeated; + + private TimeSpan m_SignupPeriod; + private DateTime m_SignupStart; + + private TournamentStage m_Stage; + + private GroupingType m_GroupType; + private TieType m_TieType; + private TimeSpan m_SuddenDeath; + + private TournyType m_TournyType; + + private int m_SuddenDeathRounds; + + private EventController m_EventController; + + public bool IsNotoRestricted { get { return ( m_TournyType != TournyType.Standard ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public EventController EventController + { + get { return m_EventController; } + set { m_EventController = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int SuddenDeathRounds + { + get{ return m_SuddenDeathRounds; } + set{ m_SuddenDeathRounds = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TournyType TournyType + { + get{ return m_TournyType; } + set{ m_TournyType = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public GroupingType GroupType + { + get{ return m_GroupType; } + set{ m_GroupType = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TieType TieType + { + get{ return m_TieType; } + set{ m_TieType = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan SuddenDeath + { + get{ return m_SuddenDeath; } + set{ m_SuddenDeath = value; } + } + + public Ruleset Ruleset + { + get{ return m_Ruleset; } + set{ m_Ruleset = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int ParticipantsPerMatch + { + get{ return m_ParticipantsPerMatch; } + set{ if ( value < 2 ) value = 2; else if ( value > 10 ) value = 10; m_ParticipantsPerMatch = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int PlayersPerParticipant + { + get{ return m_PlayersPerParticipant; } + set{ if ( value < 1 ) value = 1; else if ( value > 10 ) value = 10; m_PlayersPerParticipant = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int LevelRequirement + { + get{ return m_LevelRequirement; } + set{ m_LevelRequirement = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan SignupPeriod + { + get{ return m_SignupPeriod; } + set{ m_SignupPeriod = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime SignupStart + { + get{ return m_SignupStart; } + set{ m_SignupStart = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TournamentStage CurrentStage + { + get{ return m_Stage; } + } + + public TournamentStage Stage + { + get{ return m_Stage; } + set{ m_Stage = value; } + } + + public TournyPyramid Pyramid + { + get{ return m_Pyramid; } + set{ m_Pyramid = value; } + } + + public ArrayList Arenas + { + get{ return m_Arenas; } + set{ m_Arenas = value; } + } + + public ArrayList Participants + { + get{ return m_Participants; } + set{ m_Participants = value; } + } + + public ArrayList Undefeated + { + get{ return m_Undefeated; } + set{ m_Undefeated = value; } + } + + public bool HasParticipant( Mobile mob ) + { + for ( int i = 0; i < m_Participants.Count; ++i ) + { + TournyParticipant part = (TournyParticipant)m_Participants[i]; + + if ( part.Players.Contains( mob ) ) + return true; + } + + return false; + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 4 ); // version + + writer.Write( (Item) m_EventController ); + + writer.WriteEncodedInt( (int) m_SuddenDeathRounds ); + + writer.WriteEncodedInt( (int) m_TournyType ); + + writer.WriteEncodedInt( (int) m_GroupType ); + writer.WriteEncodedInt( (int) m_TieType ); + writer.Write( (TimeSpan) m_SuddenDeath ); + + writer.WriteEncodedInt( (int) m_ParticipantsPerMatch ); + writer.WriteEncodedInt( (int) m_PlayersPerParticipant ); + writer.Write( (TimeSpan) m_SignupPeriod ); + } + + public Tournament( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 4: + { + m_EventController = reader.ReadItem() as EventController; + + goto case 3; + } + case 3: + { + m_SuddenDeathRounds = reader.ReadEncodedInt(); + + goto case 2; + } + case 2: + { + m_TournyType = (TournyType)reader.ReadEncodedInt(); + + goto case 1; + } + case 1: + { + m_GroupType = (GroupingType)reader.ReadEncodedInt(); + m_TieType = (TieType)reader.ReadEncodedInt(); + m_SignupPeriod = reader.ReadTimeSpan(); + + goto case 0; + } + case 0: + { + if ( version < 3 ) + m_SuddenDeathRounds = 3; + + m_ParticipantsPerMatch = reader.ReadEncodedInt(); + m_PlayersPerParticipant = reader.ReadEncodedInt(); + m_SignupPeriod = reader.ReadTimeSpan(); + m_Stage = TournamentStage.Inactive; + m_Pyramid = new TournyPyramid(); + m_Ruleset = new Ruleset( RulesetLayout.Root ); + m_Ruleset.ApplyDefault( m_Ruleset.Layout.Defaults[0] ); + m_Participants = new ArrayList(); + m_Undefeated = new ArrayList(); + m_Arenas = new ArrayList(); + + break; + } + } + + Timer.DelayCall( SliceInterval, SliceInterval, new TimerCallback( Slice ) ); + } + + public Tournament() + { + m_ParticipantsPerMatch = 2; + m_PlayersPerParticipant = 1; + m_Pyramid = new TournyPyramid(); + m_Ruleset = new Ruleset( RulesetLayout.Root ); + m_Ruleset.ApplyDefault( m_Ruleset.Layout.Defaults[0] ); + m_Participants = new ArrayList(); + m_Undefeated = new ArrayList(); + m_Arenas = new ArrayList(); + m_SignupPeriod = TimeSpan.FromMinutes( 10.0 ); + + Timer.DelayCall( SliceInterval, SliceInterval, new TimerCallback( Slice ) ); + } + + public void HandleTie( Arena arena, TournyMatch match, ArrayList remaining ) + { + if ( remaining.Count == 1 ) + HandleWon( arena, match, (TournyParticipant)remaining[0] ); + + if ( remaining.Count < 2 ) + return; + + StringBuilder sb = new StringBuilder(); + + sb.Append( "The match has ended in a tie " ); + + if ( remaining.Count == 2 ) + sb.Append( "between " ); + else + sb.Append( "among " ); + + sb.Append( remaining.Count ); + + if ( ((TournyParticipant)remaining[0]).Players.Count == 1 ) + sb.Append( " players: " ); + else + sb.Append( " teams: " ); + + bool hasAppended = false; + + for ( int j = 0; j < match.Participants.Count; ++j ) + { + TournyParticipant part = (TournyParticipant)match.Participants[j]; + + if ( remaining.Contains( part ) ) + { + if ( hasAppended ) + sb.Append( ", " ); + + sb.Append( part.NameList ); + hasAppended = true; + } + else + { + m_Undefeated.Remove( part ); + } + } + + sb.Append( ". " ); + + string whole = ( remaining.Count == 2 ? "both" : "all" ); + + TieType tieType = m_TieType; + + if ( tieType == TieType.FullElimination && remaining.Count >= m_Undefeated.Count ) + tieType = TieType.FullAdvancement; + + switch ( m_TieType ) + { + case TieType.FullAdvancement: + { + sb.AppendFormat( "In accordance with the rules, {0} parties are advanced.", whole ); + break; + } + case TieType.FullElimination: + { + for ( int j = 0; j < remaining.Count; ++j ) + m_Undefeated.Remove( remaining[j] ); + + sb.AppendFormat( "In accordance with the rules, {0} parties are eliminated.", whole ); + break; + } + case TieType.Random: + { + TournyParticipant advanced = (TournyParticipant)remaining[Utility.Random( remaining.Count )]; + + for ( int i = 0; i < remaining.Count; ++i ) + { + if ( remaining[i] != advanced ) + m_Undefeated.Remove( remaining[i] ); + } + + if ( advanced != null ) + sb.AppendFormat( "In accordance with the rules, {0} {1} advanced.", advanced.NameList, advanced.Players.Count == 1 ? "is" : "are" ); + + break; + } + case TieType.Highest: + { + TournyParticipant advanced = null; + + for ( int i = 0; i < remaining.Count; ++i ) + { + TournyParticipant part = (TournyParticipant)remaining[i]; + + if ( advanced == null || part.TotalLadderXP > advanced.TotalLadderXP ) + advanced = part; + } + + for ( int i = 0; i < remaining.Count; ++i ) + { + if ( remaining[i] != advanced ) + m_Undefeated.Remove( remaining[i] ); + } + + if ( advanced != null ) + sb.AppendFormat( "In accordance with the rules, {0} {1} advanced.", advanced.NameList, advanced.Players.Count == 1 ? "is" : "are" ); + + break; + } + case TieType.Lowest: + { + TournyParticipant advanced = null; + + for ( int i = 0; i < remaining.Count; ++i ) + { + TournyParticipant part = (TournyParticipant)remaining[i]; + + if ( advanced == null || part.TotalLadderXP < advanced.TotalLadderXP ) + advanced = part; + } + + for ( int i = 0; i < remaining.Count; ++i ) + { + if ( remaining[i] != advanced ) + m_Undefeated.Remove( remaining[i] ); + } + + if ( advanced != null ) + sb.AppendFormat( "In accordance with the rules, {0} {1} advanced.", advanced.NameList, advanced.Players.Count == 1 ? "is" : "are" ); + + break; + } + } + + Alert( arena, sb.ToString() ); + } + + public void OnEliminated( DuelPlayer player ) + { + Participant part = player.Participant; + + if ( !part.Eliminated ) + return; + + if ( m_TournyType == TournyType.FreeForAll ) + { + int rem = 0; + + for ( int i = 0; i < part.Context.Participants.Count; ++i ) + { + Participant check = (Participant)part.Context.Participants[i]; + + if ( check != null && !check.Eliminated ) + ++rem; + } + + TournyParticipant tp = part.TournyPart; + + if ( tp == null ) + return; + + if ( rem == 1 ) + GiveAwards( tp.Players, TrophyRank.Silver, ComputeCashAward() / 2 ); + else if ( rem == 2 ) + GiveAwards( tp.Players, TrophyRank.Bronze, ComputeCashAward() / 4 ); + } + } + + public void HandleWon( Arena arena, TournyMatch match, TournyParticipant winner ) + { + StringBuilder sb = new StringBuilder(); + + sb.Append( "The match is complete. " ); + sb.Append( winner.NameList ); + + if ( winner.Players.Count > 1 ) + sb.Append( " have bested " ); + else + sb.Append( " has bested " ); + + if ( match.Participants.Count > 2 ) + sb.AppendFormat( "{0} other {1}: ", match.Participants.Count - 1, winner.Players.Count == 1 ? "players" : "teams" ); + + bool hasAppended = false; + + for ( int j = 0; j < match.Participants.Count; ++j ) + { + TournyParticipant part = (TournyParticipant)match.Participants[j]; + + if ( part == winner ) + continue; + + m_Undefeated.Remove( part ); + + if ( hasAppended ) + sb.Append( ", " ); + + sb.Append( part.NameList ); + hasAppended = true; + } + + sb.Append( "." ); + + if ( m_TournyType == TournyType.Standard ) + Alert( arena, sb.ToString() ); + } + + private static readonly TimeSpan SliceInterval = TimeSpan.FromSeconds( 12.0 ); + + private int ComputeCashAward() + { + return m_Participants.Count * m_PlayersPerParticipant * 2500; + } + + private void GiveAwards() + { + switch ( m_TournyType ) + { + case TournyType.FreeForAll: + { + if ( m_Pyramid.Levels.Count < 1 ) + break; + + PyramidLevel top = m_Pyramid.Levels[m_Pyramid.Levels.Count - 1] as PyramidLevel; + + if ( top.FreeAdvance != null || top.Matches.Count != 1 ) + break; + + TournyMatch match = top.Matches[0] as TournyMatch; + TournyParticipant winner = match.Winner; + + if ( winner != null ) + GiveAwards( winner.Players, TrophyRank.Gold, ComputeCashAward() ); + + break; + } + case TournyType.Standard: + { + if ( m_Pyramid.Levels.Count < 2 ) + break; + + PyramidLevel top = m_Pyramid.Levels[m_Pyramid.Levels.Count - 1] as PyramidLevel; + + if ( top.FreeAdvance != null || top.Matches.Count != 1 ) + break; + + int cash = ComputeCashAward(); + + TournyMatch match = top.Matches[0] as TournyMatch; + TournyParticipant winner = match.Winner; + + for ( int i = 0; i < match.Participants.Count; ++i ) + { + TournyParticipant part = (TournyParticipant) match.Participants[i]; + + if ( part == winner ) + GiveAwards( part.Players, TrophyRank.Gold, cash ); + else + GiveAwards( part.Players, TrophyRank.Silver, cash / 2 ); + } + + PyramidLevel next = m_Pyramid.Levels[m_Pyramid.Levels.Count - 2] as PyramidLevel; + + if ( next.Matches.Count > 2 ) + break; + + for ( int i = 0; i < next.Matches.Count; ++i ) + { + match = (TournyMatch)next.Matches[i]; + winner = match.Winner; + + for ( int j = 0; j < match.Participants.Count; ++j ) + { + TournyParticipant part = (TournyParticipant) match.Participants[j]; + + if ( part != winner ) + GiveAwards( part.Players, TrophyRank.Bronze, cash / 4 ); + } + } + + break; + } + } + } + + private void GiveAwards( ArrayList players, TrophyRank rank, int cash ) + { + if ( players.Count == 0 ) + return; + + if ( players.Count > 1 ) + cash /= ( players.Count - 1 ); + + cash += 500; + cash /= 1000; + cash *= 1000; + + StringBuilder sb = new StringBuilder(); + + if ( m_TournyType == TournyType.FreeForAll ) + { + sb.Append( m_Participants.Count * m_PlayersPerParticipant ); + sb.Append( "-man FFA" ); + } + else if ( m_TournyType == TournyType.RandomTeam ) + { + sb.Append( m_ParticipantsPerMatch ); + sb.Append( "-team" ); + } + else if ( m_TournyType == TournyType.RedVsBlue ) + { + sb.Append( "Red v Blue" ); + } + else + { + for ( int i = 0; i < m_ParticipantsPerMatch; ++i ) + { + if ( sb.Length > 0 ) + sb.Append( 'v' ); + + sb.Append( m_PlayersPerParticipant ); + } + } + + if ( m_EventController != null ) + sb.Append( ' ' ).Append( m_EventController.Title ); + + sb.Append( " Champion" ); + + string title = sb.ToString(); + + for ( int i = 0; i < players.Count; ++i ) + { + Mobile mob = (Mobile) players[i]; + + if ( mob == null || mob.Deleted ) + continue; + + Item item = new Trophy( title, rank ); + + if ( !mob.PlaceInBackpack( item ) ) + mob.BankBox.DropItem( item ); + + if ( cash > 0 ) + { + item = new BankCheck( cash ); + + if ( !mob.PlaceInBackpack( item ) ) + mob.BankBox.DropItem( item ); + + mob.SendMessage( "You have been awarded a {0} trophy and {1:N0}gp for your participation in this tournament.", rank.ToString().ToLower(), cash ); + } + else + { + mob.SendMessage( "You have been awarded a {0} trophy for your participation in this tournament.", rank.ToString().ToLower() ); + } + } + } + + public void Slice() + { + if ( m_Stage == TournamentStage.Signup ) + { + TimeSpan until = ( m_SignupStart + m_SignupPeriod ) - DateTime.Now; + + if ( until <= TimeSpan.Zero ) + { + for ( int i = m_Participants.Count - 1; i >= 0; --i ) + { + TournyParticipant part = (TournyParticipant)m_Participants[i]; + bool bad = false; + + for ( int j = 0; j < part.Players.Count; ++j ) + { + Mobile check = (Mobile) part.Players[j]; + + if ( check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive || Factions.Sigil.ExistsOn( check ) || check.Region.IsPartOf( typeof( Regions.Jail ) ) ) + { + bad = true; + break; + } + } + + if ( bad ) + { + for ( int j = 0; j < part.Players.Count; ++j ) + ((Mobile)part.Players[j]).SendMessage( "You have been disqualified from the tournament." ); + + m_Participants.RemoveAt( i ); + } + } + + if ( m_Participants.Count >= 2 ) + { + m_Stage = TournamentStage.Fighting; + + m_Undefeated.Clear(); + + m_Pyramid.Levels.Clear(); + m_Pyramid.AddLevel( m_ParticipantsPerMatch, m_Participants, m_GroupType, m_TournyType ); + + PyramidLevel level = (PyramidLevel)m_Pyramid.Levels[0]; + + if ( level.FreeAdvance != null ) + m_Undefeated.Add( level.FreeAdvance ); + + for ( int i = 0; i < level.Matches.Count; ++i ) + { + TournyMatch match = (TournyMatch)level.Matches[i]; + + m_Undefeated.AddRange( match.Participants ); + } + + Alert( "Hear ye! Hear ye!", "The tournament will begin shortly." ); + } + else + { + Alert( "Is this all?", "Pitiful. Signup extended." ); + m_SignupStart = DateTime.Now; + } + } + else if ( Math.Abs( until.TotalSeconds - TimeSpan.FromMinutes( 1.0 ).TotalSeconds ) < (SliceInterval.TotalSeconds/2) ) + { + Alert( "Last call!", "If you wish to enter the tournament, sign up with the registrar now." ); + } + else if ( Math.Abs( until.TotalSeconds - TimeSpan.FromMinutes( 5.0 ).TotalSeconds ) < (SliceInterval.TotalSeconds/2) ) + { + Alert( "The tournament will begin in 5 minutes.", "Sign up now before it's too late." ); + } + } + else if ( m_Stage == TournamentStage.Fighting ) + { + if ( m_Undefeated.Count == 1 ) + { + TournyParticipant winner = (TournyParticipant)m_Undefeated[0]; + + try + { + if ( m_EventController != null ) + Alert( "The tournament has completed!", String.Format( "Team {0} has won!", m_EventController.GetTeamName( ((TournyMatch)((PyramidLevel)m_Pyramid.Levels[0]).Matches[0]).Participants.IndexOf( winner ) ) ) ); + else if ( m_TournyType == TournyType.RandomTeam ) + Alert( "The tournament has completed!", String.Format( "Team {0} has won!", ((TournyMatch)((PyramidLevel)m_Pyramid.Levels[0]).Matches[0]).Participants.IndexOf( winner ) + 1 ) ); + else if ( m_TournyType == TournyType.RedVsBlue ) + Alert( "The tournament has completed!", String.Format( "Team {0} has won!", ((TournyMatch)((PyramidLevel)m_Pyramid.Levels[0]).Matches[0]).Participants.IndexOf( winner ) == 0 ? "Red" : "Blue" ) ); + else + Alert( "The tournament has completed!", String.Format( "{0} {1} the champion{2}.", winner.NameList, winner.Players.Count > 1 ? "are" : "is", winner.Players.Count == 1 ? "" : "s" ) ); + } + catch + { + } + + GiveAwards(); + + m_Stage = TournamentStage.Inactive; + m_Undefeated.Clear(); + } + else if ( m_Pyramid.Levels.Count > 0 ) + { + PyramidLevel activeLevel = (PyramidLevel)m_Pyramid.Levels[m_Pyramid.Levels.Count - 1]; + bool stillGoing = false; + + for ( int i = 0; i < activeLevel.Matches.Count; ++i ) + { + TournyMatch match = (TournyMatch)activeLevel.Matches[i]; + + if ( match.Winner == null ) + { + stillGoing = true; + + if ( !match.InProgress ) + { + for ( int j = 0; j < m_Arenas.Count; ++j ) + { + Arena arena = (Arena)m_Arenas[j]; + + if ( !arena.IsOccupied ) + { + match.Start( arena, this ); + break; + } + } + } + } + } + + if ( !stillGoing ) + { + for ( int i = m_Undefeated.Count - 1; i >= 0; --i ) + { + TournyParticipant part = (TournyParticipant)m_Undefeated[i]; + bool bad = false; + + for ( int j = 0; j < part.Players.Count; ++j ) + { + Mobile check = (Mobile) part.Players[j]; + + if ( check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive || Factions.Sigil.ExistsOn( check ) || check.Region.IsPartOf( typeof( Regions.Jail ) ) ) + { + bad = true; + break; + } + } + + if ( bad ) + { + for ( int j = 0; j < part.Players.Count; ++j ) + ((Mobile)part.Players[j]).SendMessage( "You have been disqualified from the tournament." ); + + m_Undefeated.RemoveAt( i ); + + if ( m_Undefeated.Count == 1 ) + { + TournyParticipant winner = (TournyParticipant)m_Undefeated[0]; + + try + { + if ( m_EventController != null ) + Alert( "The tournament has completed!", String.Format( "Team {0} has won", m_EventController.GetTeamName( ( (TournyMatch) ( (PyramidLevel) m_Pyramid.Levels[0] ).Matches[0] ).Participants.IndexOf( winner ) ) ) ); + else if ( m_TournyType == TournyType.RandomTeam ) + Alert( "The tournament has completed!", String.Format( "Team {0} has won!", ((TournyMatch)((PyramidLevel)m_Pyramid.Levels[0]).Matches[0]).Participants.IndexOf( winner ) + 1 ) ); + else if ( m_TournyType == TournyType.RedVsBlue ) + Alert( "The tournament has completed!", String.Format( "Team {0} has won!", ((TournyMatch)((PyramidLevel)m_Pyramid.Levels[0]).Matches[0]).Participants.IndexOf( winner ) == 0 ? "Red" : "Blue" ) ); + else + Alert( "The tournament has completed!", String.Format( "{0} {1} the champion{2}.", winner.NameList, winner.Players.Count > 1 ? "are" : "is", winner.Players.Count == 1 ? "" : "s" ) ); + } + catch + { + } + + GiveAwards(); + + m_Stage = TournamentStage.Inactive; + m_Undefeated.Clear(); + break; + } + } + } + + if ( m_Undefeated.Count > 1 ) + m_Pyramid.AddLevel( m_ParticipantsPerMatch, m_Undefeated, m_GroupType, m_TournyType ); + } + } + } + } + + public void Alert( params string[] alerts ) + { + for ( int i = 0; i < m_Arenas.Count; ++i ) + Alert( (Arena) m_Arenas[i], alerts ); + } + + public void Alert( Arena arena, params string[] alerts ) + { + if ( arena != null && arena.Announcer != null ) + { + for ( int j = 0; j < alerts.Length; ++j ) + Timer.DelayCall( TimeSpan.FromSeconds( Math.Max( j-0.5, 0.0 ) ), new TimerStateCallback( Alert_Callback ), new object[]{ arena.Announcer, alerts[j] } ); + } + } + + private void Alert_Callback( object state ) + { + object[] states = (object[])state; + + if ( states[0] != null ) + ((Mobile)states[0]).PublicOverheadMessage( MessageType.Regular, 0x35, false, (string)states[1] ); + } + } + + public class TournyPyramid + { + private ArrayList m_Levels; + + public ArrayList Levels + { + get{ return m_Levels; } + set{ m_Levels = value; } + } + + public TournyPyramid() + { + m_Levels = new ArrayList(); + } + + public void AddLevel( int partsPerMatch, ArrayList participants, GroupingType groupType, TournyType tournyType ) + { + ArrayList copy = new ArrayList( participants ); + + if ( groupType == GroupingType.Nearest || groupType == GroupingType.HighVsLow ) + copy.Sort(); + + PyramidLevel level = new PyramidLevel(); + + switch ( tournyType ) + { + case TournyType.RedVsBlue: + { + TournyParticipant[] parts = new TournyParticipant[2]; + + for ( int i = 0; i < parts.Length; ++i ) + parts[i] = new TournyParticipant( new ArrayList() ); + + for ( int i = 0; i < copy.Count; ++i ) + { + ArrayList players = ((TournyParticipant)copy[i]).Players; + + for ( int j = 0; j < players.Count; ++j ) + { + Mobile mob = (Mobile) players[j]; + + if ( mob.Kills >= 5 ) + parts[0].Players.Add( mob ); + else + parts[1].Players.Add( mob ); + } + } + + level.Matches.Add( new TournyMatch( new ArrayList( parts ) ) ); + break; + } + case TournyType.RandomTeam: + { + TournyParticipant[] parts = new TournyParticipant[partsPerMatch]; + + for ( int i = 0; i < partsPerMatch; ++i ) + parts[i] = new TournyParticipant( new ArrayList() ); + + for ( int i = 0; i < copy.Count; ++i ) + parts[i % parts.Length].Players.AddRange( ((TournyParticipant)copy[i]).Players ); + + level.Matches.Add( new TournyMatch( new ArrayList( parts ) ) ); + break; + } + case TournyType.FreeForAll: + { + level.Matches.Add( new TournyMatch( copy ) ); + break; + } + case TournyType.Standard: + { + if ( partsPerMatch == 2 && (participants.Count % 2) == 1 ) + { + int lowAdvances = int.MaxValue; + + for ( int i = 0; i < participants.Count; ++i ) + { + TournyParticipant p = (TournyParticipant)participants[i]; + + if ( p.FreeAdvances < lowAdvances ) + lowAdvances = p.FreeAdvances; + } + + ArrayList toAdvance = new ArrayList(); + + for ( int i = 0; i < participants.Count; ++i ) + { + TournyParticipant p = (TournyParticipant)participants[i]; + + if ( p.FreeAdvances == lowAdvances ) + toAdvance.Add( p ); + } + + if ( toAdvance.Count == 0 ) + toAdvance = copy; // sanity + + int idx = Utility.Random( toAdvance.Count ); + + ((TournyParticipant)toAdvance[idx]).AddLog( "Advanced automatically due to an odd number of challengers." ); + level.FreeAdvance = (TournyParticipant)toAdvance[idx]; + ++level.FreeAdvance.FreeAdvances; + copy.Remove( toAdvance[idx] ); + } + + while ( copy.Count >= partsPerMatch ) + { + ArrayList thisMatch = new ArrayList(); + + for ( int i = 0; i < partsPerMatch; ++i ) + { + int idx = 0; + + switch ( groupType ) + { + case GroupingType.HighVsLow: idx = (i * (copy.Count - 1)) / (partsPerMatch - 1); break; + case GroupingType.Nearest: idx = 0; break; + case GroupingType.Random: idx = Utility.Random( copy.Count ); break; + } + + thisMatch.Add( copy[idx] ); + copy.RemoveAt( idx ); + } + + level.Matches.Add( new TournyMatch( thisMatch ) ); + } + + if ( copy.Count > 1 ) + level.Matches.Add( new TournyMatch( copy ) ); + + break; + } + } + + m_Levels.Add( level ); + } + } + + public class PyramidLevel + { + private ArrayList m_Matches; + private TournyParticipant m_FreeAdvance; + + public ArrayList Matches + { + get{ return m_Matches; } + set{ m_Matches = value; } + } + + public TournyParticipant FreeAdvance + { + get{ return m_FreeAdvance; } + set{ m_FreeAdvance = value; } + } + + public PyramidLevel() + { + m_Matches = new ArrayList(); + } + } + + public class TournyMatch + { + private ArrayList m_Participants; + private TournyParticipant m_Winner; + private DuelContext m_Context; + + public ArrayList Participants + { + get{ return m_Participants; } + set{ m_Participants = value; } + } + + public TournyParticipant Winner + { + get{ return m_Winner; } + set{ m_Winner = value; } + } + + public DuelContext Context + { + get{ return m_Context; } + set{ m_Context = value; } + } + + public bool InProgress + { + get{ return ( m_Context != null && m_Context.Registered ); } + } + + public void Start( Arena arena, Tournament tourny ) + { + TournyParticipant first = (TournyParticipant)m_Participants[0]; + + DuelContext dc = new DuelContext( (Mobile)first.Players[0], tourny.Ruleset.Layout, false ); + dc.Ruleset.Options.SetAll(false); + dc.Ruleset.Options.Or(tourny.Ruleset.Options); + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + TournyParticipant tournyPart = (TournyParticipant)m_Participants[i]; + Participant duelPart = new Participant( dc, tournyPart.Players.Count ); + + duelPart.TournyPart = tournyPart; + + for ( int j = 0; j < tournyPart.Players.Count; ++j ) + duelPart.Add( (Mobile) tournyPart.Players[j] ); + + for ( int j = 0; j < duelPart.Players.Length; ++j ) + { + if ( duelPart.Players[j] != null ) + duelPart.Players[j].Ready = true; + } + + dc.Participants.Add( duelPart ); + } + + if ( tourny.EventController != null ) + dc.m_EventGame = tourny.EventController.Construct( dc ); + + dc.m_Tournament = tourny; + dc.m_Match = this; + + dc.m_OverrideArena = arena; + + if ( tourny.SuddenDeath > TimeSpan.Zero && (tourny.SuddenDeathRounds == 0 || tourny.Pyramid.Levels.Count <= tourny.SuddenDeathRounds) ) + dc.StartSuddenDeath( tourny.SuddenDeath ); + + dc.SendReadyGump( 0 ); + + if ( dc.StartedBeginCountdown ) + { + m_Context = dc; + + for ( int i = 0; i < m_Participants.Count; ++i ) + { + TournyParticipant p = (TournyParticipant)m_Participants[i]; + + for ( int j = 0; j < p.Players.Count; ++j ) + { + Mobile mob = (Mobile)p.Players[j]; + + foreach ( Mobile view in mob.GetMobilesInRange( 18 ) ) + { + if ( !mob.CanSee( view ) ) + mob.Send( view.RemovePacket ); + } + + mob.LocalOverheadMessage( MessageType.Emote, 0x3B2, false, "* Your mind focuses intently on the fight and all other distractions fade away *" ); + } + } + } + else + { + dc.Unregister(); + dc.StopCountdown(); + } + } + + public TournyMatch( ArrayList participants ) + { + m_Participants = participants; + + for ( int i = 0; i < participants.Count; ++i ) + { + TournyParticipant part = (TournyParticipant)participants[i]; + + StringBuilder sb = new StringBuilder(); + + sb.Append( "Matched in a duel against " ); + + if ( participants.Count > 2 ) + sb.AppendFormat( "{0} other {1}: ", participants.Count - 1, part.Players.Count == 1 ? "players" : "teams" ); + + bool hasAppended = false; + + for ( int j = 0; j < participants.Count; ++j ) + { + if ( i == j ) + continue; + + if ( hasAppended ) + sb.Append( ", " ); + + sb.Append( ((TournyParticipant)participants[j]).NameList ); + hasAppended = true; + } + + sb.Append( "." ); + + part.AddLog( sb.ToString() ); + } + } + } + + public class TournyParticipant : IComparable + { + private ArrayList m_Players; + private ArrayList m_Log; + private int m_FreeAdvances; + + public ArrayList Players + { + get{ return m_Players; } + set{ m_Players = value; } + } + + public ArrayList Log + { + get{ return m_Log; } + set{ m_Log = value; } + } + + public int FreeAdvances + { + get{ return m_FreeAdvances; } + set{ m_FreeAdvances = value; } + } + + public int TotalLadderXP + { + get + { + Ladder ladder = Ladder.Instance; + + if ( ladder == null ) + return 0; + + int total = 0; + + for ( int i = 0; i < m_Players.Count; ++i ) + { + Mobile mob = (Mobile)m_Players[i]; + LadderEntry entry = ladder.Find( mob ); + + if ( entry != null ) + total += entry.Experience; + } + + return total; + } + } + + public string NameList + { + get + { + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < m_Players.Count; ++i ) + { + if ( m_Players[i] == null ) + continue; + + Mobile mob = (Mobile) m_Players[i]; + + if ( sb.Length > 0 ) + { + if ( m_Players.Count == 2 ) + sb.Append( " and " ); + else if ( (i+1) < m_Players.Count ) + sb.Append( ", " ); + else + sb.Append( ", and " ); + } + + sb.Append( mob.Name ); + } + + if ( sb.Length == 0 ) + return "Empty"; + + return sb.ToString(); + } + } + + public void AddLog( string text ) + { + m_Log.Add( text ); + } + + public void AddLog( string format, params object[] args ) + { + AddLog( String.Format( format, args ) ); + } + + public void WonMatch( TournyMatch match ) + { + AddLog( "Match won." ); + } + + public void LostMatch( TournyMatch match ) + { + AddLog( "Match lost." ); + } + + public TournyParticipant( Mobile owner ) + { + m_Log = new ArrayList(); + m_Players = new ArrayList(); + m_Players.Add( owner ); + } + + public TournyParticipant( ArrayList players ) + { + m_Log = new ArrayList(); + m_Players = players; + } + + public int CompareTo( object obj ) + { + TournyParticipant p = (TournyParticipant)obj; + + return p.TotalLadderXP - this.TotalLadderXP; + } + } + + public enum TournyBracketGumpType + { + Index, + Rules_Info, + Participant_List, + Participant_Info, + Round_List, + Round_Info, + Match_Info, + Player_Info + } + + public class TournamentBracketGump : Gump + { + private Mobile m_From; + private Tournament m_Tournament; + private TournyBracketGumpType m_Type; + private ArrayList m_List; + private int m_Page; + private int m_PerPage; + private object m_Object; + + private const int BlackColor32 = 0x000008; + private const int LabelColor32 = 0xFFFFFF; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + private void AddBorderedText( int x, int y, int width, int height, string text, int color, int borderColor ) + { + AddColoredText( x - 1, y - 1, width, height, text, borderColor ); + AddColoredText( x - 1, y + 1, width, height, text, borderColor ); + AddColoredText( x + 1, y - 1, width, height, text, borderColor ); + AddColoredText( x + 1, y + 1, width, height, text, borderColor ); + AddColoredText( x, y, width, height, text, color ); + } + + private void AddColoredText( int x, int y, int width, int height, string text, int color ) + { + if ( color == 0 ) + AddHtml( x, y, width, height, text, false, false ); + else + AddHtml( x, y, width, height, Color( text, color ), false, false ); + } + + public void AddRightArrow( int x, int y, int bid, string text ) + { + AddButton( x, y, 0x15E1, 0x15E5, bid, GumpButtonType.Reply, 0 ); + + if ( text != null ) + AddHtml( x + 20, y - 1, 230, 20, text, false, false ); + } + + public void AddRightArrow( int x, int y, int bid ) + { + AddRightArrow( x, y, bid, null ); + } + + public void AddLeftArrow( int x, int y, int bid, string text ) + { + AddButton( x, y, 0x15E3, 0x15E7, bid, GumpButtonType.Reply, 0 ); + + if ( text != null ) + AddHtml( x + 20, y - 1, 230, 20, text, false, false ); + } + + public void AddLeftArrow( int x, int y, int bid ) + { + AddLeftArrow( x, y, bid, null ); + } + + public int ToButtonID( int type, int index ) + { + return 1 + (index * 7) + type; + } + + public bool FromButtonID( int bid, out int type, out int index ) + { + type = ( bid - 1 ) % 7; + index = ( bid - 1 ) / 7; + return ( bid >= 1 ); + } + + public void StartPage( out int index, out int count, out int y, int perPage ) + { + m_PerPage = perPage; + + index = Math.Max( m_Page * perPage, 0 ); + count = Math.Max( Math.Min( m_List.Count - index, perPage ), 0 ); + + y = 53 + ((12 - perPage) * 18); + + if ( m_Page > 0 ) + AddLeftArrow( 242, 35, ToButtonID( 1, 0 ) ); + + if ( (m_Page + 1) * perPage < m_List.Count ) + AddRightArrow( 260, 35, ToButtonID( 1, 1 ) ); + } + + public TournamentBracketGump( Mobile from, Tournament tourny, TournyBracketGumpType type, ArrayList list, int page, object obj ) : base( 50, 50 ) + { + m_From = from; + m_Tournament = tourny; + m_Type = type; + m_List = list; + m_Page = page; + m_Object = obj; + m_PerPage = 12; + + switch ( type ) + { + case TournyBracketGumpType.Index: + { + AddPage( 0 ); + AddBackground( 0, 0, 300, 300, 9380 ); + + StringBuilder sb = new StringBuilder(); + + if ( tourny.TournyType == TournyType.FreeForAll ) + { + sb.Append( "FFA" ); + } + else if ( tourny.TournyType == TournyType.RandomTeam ) + { + sb.Append( "Team" ); + } + else if ( tourny.TournyType == TournyType.RedVsBlue ) + { + sb.Append( "Red v Blue" ); + } + else + { + for ( int i = 0; i < tourny.ParticipantsPerMatch; ++i ) + { + if ( sb.Length > 0 ) + sb.Append( 'v' ); + + sb.Append( tourny.PlayersPerParticipant ); + } + } + + if ( tourny.EventController != null ) + sb.Append( ' ' ).Append( tourny.EventController.Title ); + + sb.Append( " Tournament Bracket" ); + + AddHtml( 25, 35, 250, 20, Center( sb.ToString() ), false, false ); + + AddRightArrow( 25, 53, ToButtonID( 0, 4 ), "Rules" ); + AddRightArrow( 25, 71, ToButtonID( 0, 1 ), "Participants" ); + + if ( m_Tournament.Stage == TournamentStage.Signup ) + { + TimeSpan until = ( m_Tournament.SignupStart + m_Tournament.SignupPeriod ) - DateTime.Now; + string text; + int secs = (int) until.TotalSeconds; + + if ( secs > 0 ) + { + int mins = secs / 60; + secs %= 60; + + if ( mins > 0 && secs > 0 ) + text = String.Format( "The tournament will begin in {0} minute{1} and {2} second{3}.", mins, mins==1?"":"s", secs, secs==1?"":"s" ); + else if ( mins > 0 ) + text = String.Format( "The tournament will begin in {0} minute{1}.", mins, mins==1?"":"s" ); + else if ( secs > 0 ) + text = String.Format( "The tournament will begin in {0} second{1}.", secs, secs==1?"":"s" ); + else + text = "The tournament will begin shortly."; + } + else + { + text = "The tournament will begin shortly."; + } + + AddHtml( 25, 92, 250, 40, text, false, false ); + } + else + { + AddRightArrow( 25, 89, ToButtonID( 0, 2 ), "Rounds" ); + } + + break; + } + case TournyBracketGumpType.Rules_Info: + { + Ruleset ruleset = tourny.Ruleset; + Ruleset basedef = ruleset.Base; + + BitArray defs; + + if ( ruleset.Flavors.Count > 0 ) + { + defs = new BitArray( basedef.Options ); + + for ( int i = 0; i < ruleset.Flavors.Count; ++i ) + defs.Or( ((Ruleset)ruleset.Flavors[i]).Options ); + } + else + { + defs = basedef.Options; + } + + int changes = 0; + + BitArray opts = ruleset.Options; + + for ( int i = 0; i < opts.Length; ++i ) + { + if ( defs[i] != opts[i] ) + ++changes; + } + + AddPage( 0 ); + AddBackground( 0, 0, 300, 60 + 18 + 20 + 20 + 20 + 8 + 20 + (ruleset.Flavors.Count * 18) + 4 + 20 + (changes * 22) + 6, 9380 ); + + AddLeftArrow( 25, 11, ToButtonID( 0, 0 ) ); + AddHtml( 25, 35, 250, 20, Center( "Rules" ), false, false ); + + int y = 53; + + string groupText = null; + + switch ( tourny.GroupType ) + { + case GroupingType.HighVsLow: groupText = "High vs Low"; break; + case GroupingType.Nearest: groupText = "Closest opponent"; break; + case GroupingType.Random: groupText = "Random"; break; + } + + AddHtml( 35, y, 190, 20, String.Format( "Grouping: {0}", groupText ), false, false ); + y += 20; + + string tieText = null; + + switch ( tourny.TieType ) + { + case TieType.Random: tieText = "Random"; break; + case TieType.Highest: tieText = "Highest advances"; break; + case TieType.Lowest: tieText = "Lowest advances"; break; + case TieType.FullAdvancement: tieText = ( tourny.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances" ); break; + case TieType.FullElimination: tieText = ( tourny.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated" ); break; + } + + AddHtml( 35, y, 190, 20, String.Format( "Tiebreaker: {0}", tieText ), false, false ); + y += 20; + + string sdText = "Off"; + + if ( tourny.SuddenDeath > TimeSpan.Zero ) + { + sdText = String.Format( "{0}:{1:D2}", (int) tourny.SuddenDeath.TotalMinutes, tourny.SuddenDeath.Seconds ); + + if ( tourny.SuddenDeathRounds > 0 ) + sdText = String.Format( "{0} (first {1} rounds)", sdText, tourny.SuddenDeathRounds ); + else + sdText = String.Format( "{0} (all rounds)", sdText ); + } + + AddHtml( 35, y, 240, 20, String.Format( "Sudden Death: {0}", sdText ), false, false ); + y += 20; + + y += 8; + + AddHtml( 35, y, 190, 20, String.Format( "Ruleset: {0}", basedef.Title ), false, false ); + y += 20; + + for ( int i = 0; i < ruleset.Flavors.Count; ++i, y += 18 ) + AddHtml( 35, y, 190, 20, String.Format( " + {0}", ((Ruleset)ruleset.Flavors[i]).Title ), false, false ); + + y += 4; + + if ( changes > 0 ) + { + AddHtml( 35, y, 190, 20, "Modifications:", false, false ); + y += 20; + + for ( int i = 0; i < opts.Length; ++i ) + { + if ( defs[i] != opts[i] ) + { + string name = ruleset.Layout.FindByIndex( i ); + + if ( name != null ) // sanity + { + AddImage( 35, y, opts[i] ? 0xD3 : 0xD2 ); + AddHtml( 60, y, 165, 22, name, false, false ); + } + + y += 22; + } + } + } + else + { + AddHtml( 35, y, 190, 20, "Modifications: None", false, false ); + y += 20; + } + + break; + } + case TournyBracketGumpType.Participant_List: + { + AddPage( 0 ); + AddBackground( 0, 0, 300, 300, 9380 ); + + if ( m_List == null ) + m_List = new ArrayList( tourny.Participants ); + + AddLeftArrow( 25, 11, ToButtonID( 0, 0 ) ); + AddHtml( 25, 35, 250, 20, Center( String.Format( "{0} Participant{1}", m_List.Count, m_List.Count == 1 ? "" : "s" ) ), false, false ); + + int index, count, y; + StartPage( out index, out count, out y, 12 ); + + for ( int i = 0; i < count; ++i, y += 18 ) + { + TournyParticipant part = (TournyParticipant)m_List[index + i]; + string name = part.NameList; + + if ( m_Tournament.TournyType != TournyType.Standard && part.Players.Count == 1 ) + { + PlayerMobile pm = part.Players[0] as PlayerMobile; + + if ( pm != null && pm.DuelPlayer != null ) + name = Color( name, pm.DuelPlayer.Eliminated ? 0x6633333 : 0x336666 ); + } + + AddRightArrow( 25, y, ToButtonID( 2, index + i ), name ); + } + + break; + } + case TournyBracketGumpType.Participant_Info: + { + TournyParticipant part = obj as TournyParticipant; + + if ( part == null ) + break; + + AddPage( 0 ); + AddBackground( 0, 0, 300, 60 + 18 + 20 + (part.Players.Count * 18) + 20 + 20 + 160, 9380 ); + + AddLeftArrow( 25, 11, ToButtonID( 0, 1 ) ); + AddHtml( 25, 35, 250, 20, Center( "Participants" ), false, false ); + + int y = 53; + + AddHtml( 25, y, 200, 20, part.Players.Count == 1 ? "Players" : "Team", false, false ); + y += 20; + + for ( int i = 0; i < part.Players.Count; ++i ) + { + Mobile mob = (Mobile)part.Players[i]; + string name = mob.Name; + + if ( m_Tournament.TournyType != TournyType.Standard ) + { + PlayerMobile pm = mob as PlayerMobile; + + if ( pm != null && pm.DuelPlayer != null ) + name = Color( name, pm.DuelPlayer.Eliminated ? 0x6633333 : 0x336666 ); + } + + AddRightArrow( 35, y, ToButtonID( 4, i ), name ); + y += 18; + } + + AddHtml( 25, y, 200, 20, String.Format( "Free Advances: {0}", part.FreeAdvances == 0 ? "None" : part.FreeAdvances.ToString() ), false, false ); + y += 20; + + AddHtml( 25, y, 200, 20, "Log:", false, false ); + y += 20; + + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < part.Log.Count; ++i ) + { + if ( sb.Length > 0 ) + sb.Append( "
" ); + + sb.Append( part.Log[i] ); + } + + if ( sb.Length == 0 ) + sb.Append( "Nothing logged yet." ); + + AddHtml( 25, y, 250, 150, Color( sb.ToString(), BlackColor32 ), false, true ); + + break; + } + case TournyBracketGumpType.Player_Info: + { + AddPage( 0 ); + AddBackground( 0, 0, 300, 300, 9380 ); + + AddLeftArrow( 25, 11, ToButtonID( 0, 3 ) ); + AddHtml( 25, 35, 250, 20, Center( "Participants" ), false, false ); + + Mobile mob = obj as Mobile; + + if ( mob == null ) + break; + + Ladder ladder = Ladder.Instance; + LadderEntry entry = ( ladder == null ? null : ladder.Find( mob ) ); + + AddHtml( 25, 53, 250, 20, String.Format( "Name: {0}", mob.Name ), false, false ); + AddHtml( 25, 73, 250, 20, String.Format( "Guild: {0}", mob.Guild == null ? "None" : mob.Guild.Name + " [" + mob.Guild.Abbreviation + "]" ), false, false ); + AddHtml( 25, 93, 250, 20, String.Format( "Rank: {0}", entry == null ? "N/A" : LadderGump.Rank( entry.Index + 1 ) ), false, false ); + AddHtml( 25, 113, 250, 20, String.Format( "Level: {0}", entry == null ? 0 : Ladder.GetLevel( entry.Experience ) ), false, false ); + AddHtml( 25, 133, 250, 20, String.Format( "Wins: {0:N0}", entry == null ? 0 : entry.Wins ), false, false ); + AddHtml( 25, 153, 250, 20, String.Format( "Losses: {0:N0}", entry == null ? 0 : entry.Losses ), false, false ); + + break; + } + case TournyBracketGumpType.Round_List: + { + AddPage( 0 ); + AddBackground( 0, 0, 300, 300, 9380 ); + + AddLeftArrow( 25, 11, ToButtonID( 0, 0 ) ); + AddHtml( 25, 35, 250, 20, Center( "Rounds" ), false, false ); + + if ( m_List == null ) + m_List = new ArrayList( tourny.Pyramid.Levels ); + + int index, count, y; + StartPage( out index, out count, out y, 12 ); + + for ( int i = 0; i < count; ++i, y += 18 ) + { + PyramidLevel level = (PyramidLevel)m_List[index + i]; + + AddRightArrow( 25, y, ToButtonID( 3, index + i ), "Round #" + (index + i + 1) ); + } + + break; + } + case TournyBracketGumpType.Round_Info: + { + AddPage( 0 ); + AddBackground( 0, 0, 300, 300, 9380 ); + + AddLeftArrow( 25, 11, ToButtonID( 0, 2 ) ); + AddHtml( 25, 35, 250, 20, Center( "Rounds" ), false, false ); + + PyramidLevel level = m_Object as PyramidLevel; + + if ( level == null ) + break; + + if ( m_List == null ) + m_List = new ArrayList( level.Matches ); + + AddRightArrow( 25, 53, ToButtonID( 5, 0 ), String.Format( "Free Advance: {0}", level.FreeAdvance == null ? "None" : level.FreeAdvance.NameList ) ); + + AddHtml( 25, 73, 200, 20, String.Format( "{0} Match{1}", m_List.Count, m_List.Count == 1 ? "" : "es" ), false, false ); + + int index, count, y; + StartPage( out index, out count, out y, 10 ); + + for ( int i = 0; i < count; ++i, y += 18 ) + { + TournyMatch match = (TournyMatch)m_List[index + i]; + + int color = -1; + + if ( match.InProgress ) + color = 0x336666; + else if ( match.Context != null && match.Winner == null ) + color = 0x666666; + + StringBuilder sb = new StringBuilder(); + + if ( m_Tournament.TournyType == TournyType.Standard ) + { + for ( int j = 0; j < match.Participants.Count; ++j ) + { + if ( sb.Length > 0 ) + sb.Append( " vs " ); + + TournyParticipant part = (TournyParticipant)match.Participants[j]; + string txt = part.NameList; + + if ( color == -1 && match.Context != null && match.Winner == part ) + txt = Color( txt, 0x336633 ); + else if ( color == -1 && match.Context != null ) + txt = Color( txt, 0x663333 ); + + sb.Append( txt ); + } + } + else if ( m_Tournament.EventController != null || m_Tournament.TournyType == TournyType.RandomTeam || m_Tournament.TournyType == TournyType.RedVsBlue ) + { + for ( int j = 0; j < match.Participants.Count; ++j ) + { + if ( sb.Length > 0 ) + sb.Append( " vs " ); + + TournyParticipant part = (TournyParticipant)match.Participants[j]; + string txt; + + if ( m_Tournament.EventController != null ) + txt = String.Format( "Team {0} ({1})", m_Tournament.EventController.GetTeamName( j ), part.Players.Count ); + else if ( m_Tournament.TournyType == TournyType.RandomTeam ) + txt = String.Format( "Team {0} ({1})", j + 1, part.Players.Count ); + else + txt = String.Format( "Team {0} ({1})", j == 0 ? "Red" : "Blue", part.Players.Count ); + + if ( color == -1 && match.Context != null && match.Winner == part ) + txt = Color( txt, 0x336633 ); + else if ( color == -1 && match.Context != null ) + txt = Color( txt, 0x663333 ); + + sb.Append( txt ); + } + } + else if ( m_Tournament.TournyType == TournyType.FreeForAll ) + { + sb.Append( "Free For All" ); + } + + string str = sb.ToString(); + + if ( color >= 0 ) + str = Color( str, color ); + + AddRightArrow( 25, y, ToButtonID( 5, index + i + 1 ), str ); + } + + break; + } + case TournyBracketGumpType.Match_Info: + { + TournyMatch match = obj as TournyMatch; + + if ( match == null ) + break; + + int ct = ( m_Tournament.TournyType == TournyType.FreeForAll ? 2 : match.Participants.Count ); + + AddPage( 0 ); + AddBackground( 0, 0, 300, 60 + 18 + 20 + 20 + 20 + (ct*18) + 6, 9380 ); + + AddLeftArrow( 25, 11, ToButtonID( 0, 5 ) ); + AddHtml( 25, 35, 250, 20, Center( "Rounds" ), false, false ); + + AddHtml( 25, 53, 250, 20, String.Format( "Winner: {0}", match.Winner == null ? "N/A" : match.Winner.NameList ), false, false ); + AddHtml( 25, 73, 250, 20, String.Format( "State: {0}", match.InProgress ? "In progress" : match.Context != null ? "Complete" : "Waiting" ), false, false ); + AddHtml( 25, 93, 250, 20, String.Format( "Participants:" ), false, false ); + + if ( m_Tournament.TournyType == TournyType.Standard ) + { + for ( int i = 0; i < match.Participants.Count; ++i ) + { + TournyParticipant part = (TournyParticipant)match.Participants[i]; + + AddRightArrow( 25, 113 + (i * 18), ToButtonID( 6, i ), part.NameList ); + } + } + else if ( m_Tournament.EventController != null || m_Tournament.TournyType == TournyType.RandomTeam || m_Tournament.TournyType == TournyType.RedVsBlue ) + { + for ( int i = 0; i < match.Participants.Count; ++i ) + { + TournyParticipant part = (TournyParticipant)match.Participants[i]; + + if ( m_Tournament.EventController != null ) + AddRightArrow( 25, 113 + (i * 18), ToButtonID( 6, i ), String.Format( "Team {0} ({1})", m_Tournament.EventController.GetTeamName( i ), part.Players.Count ) ); + else if ( m_Tournament.TournyType == TournyType.RandomTeam ) + AddRightArrow( 25, 113 + (i * 18), ToButtonID( 6, i ), String.Format( "Team {0} ({1})", i+1, part.Players.Count ) ); + else + AddRightArrow( 25, 113 + (i * 18), ToButtonID( 6, i ), String.Format( "Team {0} ({1})", i==0?"Red":"Blue", part.Players.Count ) ); + } + } + else if ( m_Tournament.TournyType == TournyType.FreeForAll ) + { + AddHtml( 25, 113, 250, 20, "Free For All", false, false ); + } + + break; + } + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int type, index; + + if ( !FromButtonID( info.ButtonID, out type, out index ) ) + return; + + switch ( type ) + { + case 0: + { + switch ( index ) + { + case 0: m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Index, null, 0, null ) ); break; + case 1: m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Participant_List, null, 0, null ) ); break; + case 2: m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Round_List, null, 0, null ) ); break; + case 4: m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Rules_Info, null, 0, null ) ); break; + case 3: + { + Mobile mob = m_Object as Mobile; + + for ( int i = 0; i < m_Tournament.Participants.Count; ++i ) + { + TournyParticipant part = (TournyParticipant)m_Tournament.Participants[i]; + + if ( part.Players.Contains( mob ) ) + { + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Participant_Info, null, 0, part ) ); + break; + } + } + + break; + } + case 5: + { + TournyMatch match = m_Object as TournyMatch; + + if ( match == null ) + break; + + for ( int i = 0; i < m_Tournament.Pyramid.Levels.Count; ++i ) + { + PyramidLevel level = (PyramidLevel)m_Tournament.Pyramid.Levels[i]; + + if ( level.Matches.Contains( match ) ) + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Round_Info, null, 0, level ) ); + } + + break; + } + } + + break; + } + case 1: + { + switch ( index ) + { + case 0: + { + if ( m_List != null && m_Page > 0 ) + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, m_Type, m_List, m_Page - 1, m_Object ) ); + + break; + } + case 1: + { + if ( m_List != null && ((m_Page + 1) * m_PerPage) < m_List.Count ) + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, m_Type, m_List, m_Page + 1, m_Object ) ); + + break; + } + } + + break; + } + case 2: + { + if ( m_Type != TournyBracketGumpType.Participant_List ) + break; + + if ( index >= 0 && index < m_List.Count ) + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Participant_Info, null, 0, m_List[index] ) ); + + break; + } + case 3: + { + if ( m_Type != TournyBracketGumpType.Round_List ) + break; + + if ( index >= 0 && index < m_List.Count ) + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Round_Info, null, 0, m_List[index] ) ); + + break; + } + case 4: + { + if ( m_Type != TournyBracketGumpType.Participant_Info ) + break; + + TournyParticipant part = m_Object as TournyParticipant; + + if ( part != null && index >= 0 && index < part.Players.Count ) + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Player_Info, null, 0, part.Players[index] ) ); + + break; + } + case 5: + { + if ( m_Type != TournyBracketGumpType.Round_Info ) + break; + + PyramidLevel level = m_Object as PyramidLevel; + + if ( level == null ) + break; + + if ( index == 0 ) + { + if ( level.FreeAdvance != null ) + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Participant_Info, null, 0, level.FreeAdvance ) ); + else + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, m_Type, m_List, m_Page, m_Object ) ); + } + else if ( index >= 1 && index <= level.Matches.Count ) + { + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Match_Info, null, 0, level.Matches[index-1] ) ); + } + + break; + } + case 6: + { + if ( m_Type != TournyBracketGumpType.Match_Info ) + break; + + TournyMatch match = m_Object as TournyMatch; + + if ( match != null && index >= 0 && index < match.Participants.Count ) + m_From.SendGump( new TournamentBracketGump( m_From, m_Tournament, TournyBracketGumpType.Participant_Info, null, 0, match.Participants[index] ) ); + + break; + } + } + } + } + + public class TournamentBracketItem : Item + { + private TournamentController m_Tournament; + + [CommandProperty( AccessLevel.GameMaster )] + public TournamentController Tournament{ get{ return m_Tournament; } set{ m_Tournament = value; } } + + public override string DefaultName + { + get { return "tournament bracket"; } + } + + [Constructable] + public TournamentBracketItem() : base( 3774 ) + { + Movable = false; + } + + public override void OnDoubleClick(Mobile from) + { + if ( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that + } + else if ( m_Tournament != null ) + { + Tournament tourny = m_Tournament.Tournament; + + if ( tourny != null ) + { + from.CloseGump( typeof( TournamentBracketGump ) ); + from.SendGump( new TournamentBracketGump( from, tourny, TournyBracketGumpType.Index, null, 0, null ) ); + + /*if ( tourny.Stage == TournamentStage.Fighting && tourny.Pyramid.Levels.Count > 0 ) + from.SendGump( new TournamentBracketGump( tourny, (PyramidLevel)tourny.Pyramid.Levels[tourny.Pyramid.Levels.Count - 1] ) ); + else + from.SendGump( new TournamentBracketGump( tourny, 0 ) );*/ + } + } + } + + public TournamentBracketItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( (Item) m_Tournament ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Tournament = reader.ReadItem() as TournamentController; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/ConPVP/Trophy.cs b/Scripts/Engines/ConPVP/Trophy.cs new file mode 100644 index 000000000..b9ab66de2 --- /dev/null +++ b/Scripts/Engines/ConPVP/Trophy.cs @@ -0,0 +1,111 @@ +using System; +using System.Text; +using Server; +using Server.Items; + +namespace Server.Items +{ + public enum TrophyRank + { + Bronze, + Silver, + Gold + } + + [Flipable( 5020, 4647 )] + public class Trophy : Item + { + private string m_Title; + private TrophyRank m_Rank; + private Mobile m_Owner; + private DateTime m_Date; + + [CommandProperty( AccessLevel.GameMaster )] + public string Title{ get{ return m_Title; } set{ m_Title = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public TrophyRank Rank{ get{ return m_Rank; } set{ m_Rank = value; UpdateStyle(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner{ get{ return m_Owner; } set{ m_Owner = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime Date{ get{ return m_Date; } } + + [Constructable] + public Trophy( string title, TrophyRank rank ) : base( 5020 ) + { + m_Title = title; + m_Rank = rank; + m_Date = DateTime.Now; + + LootType = LootType.Blessed; + + UpdateStyle(); + } + + public Trophy( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (string) m_Title ); + writer.Write( (int) m_Rank ); + writer.Write( (Mobile) m_Owner ); + writer.Write( (DateTime) m_Date ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Title = reader.ReadString(); + m_Rank = (TrophyRank) reader.ReadInt(); + m_Owner = reader.ReadMobile(); + m_Date = reader.ReadDateTime(); + + if ( version == 0 ) + LootType = LootType.Blessed; + } + + public override void OnAdded( object parent ) + { + base.OnAdded( parent ); + + if ( m_Owner == null ) + m_Owner = this.RootParent as Mobile; + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( m_Owner != null ) + LabelTo( from, "{0} -- {1}", m_Title, m_Owner.RawName ); + else if ( m_Title != null ) + LabelTo( from, m_Title ); + + if ( m_Date != DateTime.MinValue ) + LabelTo( from, m_Date.ToString( "d" ) ); + } + + public void UpdateStyle() + { + Name = String.Format( "{0} trophy", m_Rank.ToString().ToLower() ); + + switch ( m_Rank ) + { + case TrophyRank.Gold: Hue = 2213; break; + case TrophyRank.Silver: Hue = 0; break; + case TrophyRank.Bronze: Hue = 2206; break; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Core/Ethic.cs b/Scripts/Engines/Ethics/Core/Ethic.cs index eb5043b8f..f44965676 100644 --- a/Scripts/Engines/Ethics/Core/Ethic.cs +++ b/Scripts/Engines/Ethics/Core/Ethic.cs @@ -134,6 +134,9 @@ namespace Server.Ethics } else { + if ( e.Mobile is PlayerMobile && ( e.Mobile as PlayerMobile ).DuelContext != null ) + return; + Ethic ethic = pl.Ethic; for ( int i = 0; i < ethic.Definition.Powers.Length; ++i ) diff --git a/Scripts/Engines/Factions/Core/Faction.cs b/Scripts/Engines/Factions/Core/Faction.cs index fa7275a36..c4d01cbdf 100644 --- a/Scripts/Engines/Factions/Core/Faction.cs +++ b/Scripts/Engines/Factions/Core/Faction.cs @@ -1103,6 +1103,11 @@ namespace Server.Factions if ( victimState == null ) return; + #region Dueling + if ( victim.Region.IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) ) + return; + #endregion + if ( killer == victim || killerState.Faction != victimState.Faction ) ApplySkillLoss( victim ); diff --git a/Scripts/Engines/Factions/Core/StrongholdRegion.cs b/Scripts/Engines/Factions/Core/StrongholdRegion.cs index 03b76fdf6..82b70507c 100644 --- a/Scripts/Engines/Factions/Core/StrongholdRegion.cs +++ b/Scripts/Engines/Factions/Core/StrongholdRegion.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using Server; +using Server.Mobiles; using Server.Regions; namespace Server.Factions @@ -29,6 +30,15 @@ namespace Server.Factions if ( m.AccessLevel >= AccessLevel.Counselor || Contains( oldLocation ) ) return true; + + if ( m is PlayerMobile ) { + PlayerMobile pm = (PlayerMobile)m; + + if ( pm.DuelContext != null ) { + m.SendMessage( "You may not enter this area while participating in a duel or a tournament." ); + return false; + } + } return ( Faction.Find( m, true, true ) != null ); } diff --git a/Scripts/Engines/Harvest/Fishing.cs b/Scripts/Engines/Harvest/Fishing.cs index 85664253f..8b044c351 100644 --- a/Scripts/Engines/Harvest/Fishing.cs +++ b/Scripts/Engines/Harvest/Fishing.cs @@ -252,7 +252,7 @@ namespace Server.Engines.Harvest { SOS sos = messages[i]; - if ( ( from.Map == Map.Felucca || from.Map == Map.Trammel ) && from.InRange( sos.TargetLocation, 60 ) ) + if ( from.Map == sos.TargetMap && from.InRange( sos.TargetLocation, 60 ) ) { Item preLoot = null; @@ -345,7 +345,7 @@ namespace Server.Engines.Harvest if ( sos.IsAncient ) chest.Hue = 0x481; - TreasureMapChest.Fill( chest, Math.Max( 1, Math.Max( 4, sos.Level ) ) ); + TreasureMapChest.Fill( chest, Math.Max( 1, Math.Min( 4, sos.Level ) ) ); if ( sos.IsAncient ) chest.DropItem( new FabledFishingNet() ); diff --git a/Scripts/Engines/Help/SpeechLog.cs b/Scripts/Engines/Help/SpeechLog.cs index 70b8183e5..9f5ed3de8 100644 --- a/Scripts/Engines/Help/SpeechLog.cs +++ b/Scripts/Engines/Help/SpeechLog.cs @@ -22,7 +22,7 @@ namespace Server.Engines.Help public static void Initialize() { - CommandSystem.Register( "SpeechLog", AccessLevel.GameMaster, new CommandEventHandler( SpeechLog_OnCommand ) ); + CommandSystem.Register( "SpeechLog", AccessLevel.Counselor, new CommandEventHandler( SpeechLog_OnCommand ) ); } [Usage( "SpeechLog" )] diff --git a/Scripts/Engines/Reports/Reports.cs b/Scripts/Engines/Reports/Reports.cs index 091c96fe8..953c03fa3 100644 --- a/Scripts/Engines/Reports/Reports.cs +++ b/Scripts/Engines/Reports/Reports.cs @@ -8,6 +8,7 @@ using Server.Mobiles; using Server.Network; using Server.Factions; using Server.Accounting; +using Server.Engines.ConPVP; namespace Server.Engines.Reports { @@ -83,6 +84,9 @@ namespace Server.Engines.Reports public static void FillSnapshot( Snapshot ss ) { ss.Children.Add( CompileGeneralStats() ); + ss.Children.Add( CompilePCByDL() ); + ss.Children.Add( CompileTop15() ); + ss.Children.Add( CompileDislikedArenas() ); ss.Children.Add( CompileStatChart() ); PersistableObject[] obs = CompileSkillReports(); @@ -122,6 +126,129 @@ namespace Server.Engines.Reports return report; } + private static Chart CompilePCByDL() + { + BarGraph chart = new BarGraph( "Player Count By Dueling Level", "graphs_pc_by_dl", 5, "Dueling Level", "Players", BarGraphRenderMode.Bars ); + + int lastLevel = -1; + ChartItem lastItem = null; + + Ladder ladder = Ladder.Instance; + + if ( ladder != null ) + { + ArrayList entries = ladder.ToArrayList(); + + for ( int i = entries.Count - 1; i >= 0; --i ) + { + LadderEntry entry = (LadderEntry)entries[i]; + int level = Ladder.GetLevel( entry.Experience ); + + if ( lastItem == null || level != lastLevel ) + { + chart.Items.Add( lastItem = new ChartItem( level.ToString(), 1 ) ); + lastLevel = level; + } + else + { + lastItem.Value++; + } + } + } + + return chart; + } + + private static Report CompileTop15() + { + Report report = new Report( "Top 15 Duelists", "80%" ); + + report.Columns.Add( "6%", "center", "Rank" ); + report.Columns.Add( "6%", "center", "Level" ); + report.Columns.Add( "6%", "center", "Guild" ); + report.Columns.Add( "70%", "left", "Name" ); + report.Columns.Add( "6%", "center", "Wins" ); + report.Columns.Add( "6%", "center", "Losses" ); + + Ladder ladder = Ladder.Instance; + + if ( ladder != null ) + { + ArrayList entries = ladder.ToArrayList(); + + for ( int i = 0; i < entries.Count && i < 15; ++i ) + { + LadderEntry entry = (LadderEntry)entries[i]; + int level = Ladder.GetLevel( entry.Experience ); + string guild = ""; + + if ( entry.Mobile.Guild != null ) + guild = entry.Mobile.Guild.Abbreviation; + + ReportItem item = new ReportItem(); + + item.Values.Add( LadderGump.Rank( entry.Index + 1 ) ); + item.Values.Add( level.ToString(), "N0" ); + item.Values.Add( guild ); + item.Values.Add( entry.Mobile.Name ); + item.Values.Add( entry.Wins.ToString(), "N0" ); + item.Values.Add( entry.Losses.ToString(), "N0" ); + + report.Items.Add( item ); + } + } + + return report; + } + + private static Chart CompileDislikedArenas() + { + PieChart chart = new PieChart( "Most Disliked Arenas", "graphs_arenas_disliked", false ); + + Preferences prefs = Preferences.Instance; + + if ( prefs != null ) + { + List arenas = Arena.Arenas; + + for ( int i = 0; i < arenas.Count; ++i ) + { + Arena arena = arenas[i]; + + string name = arena.Name; + + if ( name != null ) + chart.Items.Add( name, 0 ); + } + + ArrayList entries = prefs.Entries; + + for ( int i = 0; i < entries.Count; ++i ) + { + PreferencesEntry entry = (PreferencesEntry)entries[i]; + ArrayList list = entry.Disliked; + + for ( int j = 0; j < list.Count; ++j ) + { + string disliked = (string)list[j]; + + for ( int k = 0; k < chart.Items.Count; ++k ) + { + ChartItem item = chart.Items[k]; + + if ( item.Name == disliked ) + { + ++item.Value; + break; + } + } + } + } + } + + return chart; + } + public static Chart CompileStatChart() { PieChart chart = new PieChart( "Stat Distribution", "graphs_strdexint_distrib", true ); diff --git a/Scripts/Gumps/HouseGumpAOS.cs b/Scripts/Gumps/HouseGumpAOS.cs index 17ed6b1ff..587fc58cf 100644 --- a/Scripts/Gumps/HouseGumpAOS.cs +++ b/Scripts/Gumps/HouseGumpAOS.cs @@ -191,7 +191,7 @@ namespace Server.Gumps 660, 666, 672, 898, 970, 974, 982 }; - + private static List _HouseSigns = new List(); public HouseGumpAOS( HouseGumpPageAOS page, Mobile from, BaseHouse house ) : base( 50, 40 ) @@ -497,7 +497,7 @@ namespace Server.Gumps { _HouseSigns.Add( 2980 + ( i * 2 ) ); } - + // Add library and beekeeper signs ( ML ) _HouseSigns.Add( 2966 ); _HouseSigns.Add( 3140 ); diff --git a/Scripts/Items/Containers/FillableContainers.cs b/Scripts/Items/Containers/FillableContainers.cs index 99d2579f8..b13c80167 100644 --- a/Scripts/Items/Containers/FillableContainers.cs +++ b/Scripts/Items/Containers/FillableContainers.cs @@ -496,7 +496,7 @@ namespace Server.Items { base.Serialize( writer ); - writer.Write( (int)0 ); // version + writer.Write( (int) 1 ); // version } public override void Deserialize( GenericReader reader ) diff --git a/Scripts/Items/Skill Items/Lumberjack/Log.cs b/Scripts/Items/Skill Items/Lumberjack/Log.cs index c5c3dbeb8..94e68a2dd 100644 --- a/Scripts/Items/Skill Items/Lumberjack/Log.cs +++ b/Scripts/Items/Skill Items/Lumberjack/Log.cs @@ -17,6 +17,7 @@ namespace Server.Items int ICommodity.DescriptionNumber { get { return CraftResources.IsStandard( m_Resource ) ? LabelNumber : 1075062 + ( (int)m_Resource - (int)CraftResource.RegularWood ); } } bool ICommodity.IsDeedable { get { return true; } } + [Constructable] public Log() : this( 1 ) { diff --git a/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs b/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs index 16c533739..b8d3be2e4 100644 --- a/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs +++ b/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs @@ -222,7 +222,6 @@ namespace Server.Items BasePotion pot = (BasePotion)item; int toHold = Math.Min( 100 - m_Held, pot.Amount ); - if ( toHold <= 0 ) { from.SendLocalizedMessage( 502233 ); // The keg will not hold any more! diff --git a/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/BaseAgilityPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/BaseAgilityPotion.cs index 0ced4dab7..b0cd11523 100644 --- a/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/BaseAgilityPotion.cs +++ b/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/BaseAgilityPotion.cs @@ -50,7 +50,8 @@ namespace Server.Items { BasePotion.PlayDrinkEffect( from ); - this.Consume(); + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( from ) ) + this.Consume(); } } } diff --git a/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs b/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs index 171841bba..e65739b34 100644 --- a/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs +++ b/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs @@ -173,7 +173,10 @@ namespace Server.Items m.PlaySound( 0x2D6 ); - m.AddToBackpack( new Bottle() ); + #region Dueling + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( m ) ) + m.AddToBackpack( new Bottle() ); + #endregion if ( m.Body.IsHuman && !m.Mounted ) m.Animate( 34, 5, 1, true, false, 0 ); diff --git a/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/BaseCurePotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/BaseCurePotion.cs index 24ff57728..c64c57b5c 100644 --- a/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/BaseCurePotion.cs +++ b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/BaseCurePotion.cs @@ -97,7 +97,8 @@ namespace Server.Items from.FixedParticles( 0x373A, 10, 15, 5012, EffectLayer.Waist ); from.PlaySound( 0x1E0 ); - this.Consume(); + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( from ) ) + this.Consume(); } else { diff --git a/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs index 6002a7b7c..170fe0373 100644 --- a/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs +++ b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs @@ -56,7 +56,8 @@ namespace Server.Items BasePotion.PlayDrinkEffect( from ); - this.Consume(); + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( from ) ) + this.Consume(); Timer.DelayCall( TimeSpan.FromSeconds( Delay ), new TimerStateCallback( ReleaseHealLock ), from ); } diff --git a/Scripts/Items/Skill Items/Magical/Potions/NightSight.cs b/Scripts/Items/Skill Items/Magical/Potions/NightSight.cs index 71654329a..ceb0e7017 100644 --- a/Scripts/Items/Skill Items/Magical/Potions/NightSight.cs +++ b/Scripts/Items/Skill Items/Magical/Potions/NightSight.cs @@ -40,7 +40,8 @@ namespace Server.Items BasePotion.PlayDrinkEffect( from ); - this.Consume(); + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( from ) ) + this.Consume(); } else { diff --git a/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/BasePoisonPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/BasePoisonPotion.cs index 95a3ea96b..57b8f05ae 100644 --- a/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/BasePoisonPotion.cs +++ b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/BasePoisonPotion.cs @@ -43,7 +43,8 @@ namespace Server.Items BasePotion.PlayDrinkEffect( from ); - this.Consume(); + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( from ) ) + this.Consume(); } } } \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/BaseRefreshPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/BaseRefreshPotion.cs index aed40b466..1cd18e793 100644 --- a/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/BaseRefreshPotion.cs +++ b/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/BaseRefreshPotion.cs @@ -37,7 +37,8 @@ namespace Server.Items BasePotion.PlayDrinkEffect( from ); - this.Consume(); + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( from ) ) + this.Consume(); } else { diff --git a/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/BaseStrengthPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/BaseStrengthPotion.cs index a4292ede7..68fb6ba33 100644 --- a/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/BaseStrengthPotion.cs +++ b/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/BaseStrengthPotion.cs @@ -50,7 +50,8 @@ namespace Server.Items { BasePotion.PlayDrinkEffect( from ); - this.Consume(); + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( from ) ) + this.Consume(); } } } diff --git a/Scripts/Items/Skill Items/Misc/Bandage.cs b/Scripts/Items/Skill Items/Misc/Bandage.cs index 5c765e664..a073dc79d 100644 --- a/Scripts/Items/Skill Items/Misc/Bandage.cs +++ b/Scripts/Items/Skill Items/Misc/Bandage.cs @@ -94,7 +94,8 @@ namespace Server.Items { if ( BandageContext.BeginHeal( from, (Mobile)targeted ) != null ) { - m_Bandage.Consume(); + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( from ) ) + m_Bandage.Consume(); } } else @@ -509,7 +510,6 @@ namespace Server.Items if ( !onSelf ) patient.SendLocalizedMessage( 1008078, false, healer.Name ); // : Attempting to heal you. - healer.SendLocalizedMessage( 500956 ); // You begin applying the bandages. return context; } diff --git a/Scripts/Items/Special/Solen Items/BallOfSummoning.cs b/Scripts/Items/Special/Solen Items/BallOfSummoning.cs index 08d4a79ad..9f3e0d5ab 100644 --- a/Scripts/Items/Special/Solen Items/BallOfSummoning.cs +++ b/Scripts/Items/Special/Solen Items/BallOfSummoning.cs @@ -256,7 +256,7 @@ namespace Server.Items { MessageHelper.SendLocalizedMessageTo( this, from, 1054127, 0x22 ); // The Crystal Ball fills with a red mist. You appear to have let your bond to your pet deteriorate. } - else if ( from.Map == Map.Ilshenar || from.Region.IsPartOf( typeof( DungeonRegion ) ) || from.Region.IsPartOf( typeof( Jail ) ) ) + else if ( from.Map == Map.Ilshenar || from.Region.IsPartOf( typeof( DungeonRegion ) ) || from.Region.IsPartOf( typeof( Jail ) ) || from.Region.IsPartOf( typeof( Server.Engines.ConPVP.SafeZone ) ) ) { from.Send( new AsciiMessage( this.Serial, this.ItemID, MessageType.Regular, 0x22, 3, "", "You cannot summon your pet to this location." ) ); } @@ -464,4 +464,4 @@ namespace Server.Items } } } -} +} \ No newline at end of file diff --git a/Scripts/Items/Special/Solen Items/BraceletOfBinding.cs b/Scripts/Items/Special/Solen Items/BraceletOfBinding.cs index 0cf8ff501..ffd16716a 100644 --- a/Scripts/Items/Special/Solen Items/BraceletOfBinding.cs +++ b/Scripts/Items/Special/Solen Items/BraceletOfBinding.cs @@ -471,4 +471,4 @@ namespace Server.Items } } } -} +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/WeaponAbility.cs b/Scripts/Items/Weapons/Abilities/WeaponAbility.cs index 8f3ef1b7c..66e43ac89 100644 --- a/Scripts/Items/Weapons/Abilities/WeaponAbility.cs +++ b/Scripts/Items/Weapons/Abilities/WeaponAbility.cs @@ -189,6 +189,61 @@ namespace Server.Items return false; } + #region Dueling + string option = null; + + if ( this is ArmorIgnore ) + option = "Armor Ignore"; + else if ( this is BleedAttack ) + option = "Bleed Attack"; + else if ( this is ConcussionBlow ) + option = "Concussion Blow"; + else if ( this is CrushingBlow ) + option = "Crushing Blow"; + else if ( this is Disarm ) + option = "Disarm"; + else if ( this is Dismount ) + option = "Dismount"; + else if ( this is DoubleStrike ) + option = "Double Strike"; + else if ( this is InfectiousStrike ) + option = "Infectious Strike"; + else if ( this is MortalStrike ) + option = "Mortal Strike"; + else if ( this is MovingShot ) + option = "Moving Shot"; + else if ( this is ParalyzingBlow ) + option = "Paralyzing Blow"; + else if ( this is ShadowStrike ) + option = "Shadow Strike"; + else if ( this is WhirlwindAttack ) + option = "Whirlwind Attack"; + else if ( this is RidingSwipe ) + option = "Riding Swipe"; + else if ( this is FrenziedWhirlwind ) + option = "Frenzied Whirlwind"; + else if ( this is Block ) + option = "Block"; + else if ( this is DefenseMastery ) + option = "Defense Mastery"; + else if ( this is NerveStrike ) + option = "Nerve Strike"; + else if ( this is TalonStrike ) + option = "Talon Strike"; + else if ( this is Feint ) + option = "Feint"; + else if ( this is DualWield ) + option = "Dual Wield"; + else if ( this is DoubleShot ) + option = "Double Shot"; + else if ( this is ArmorPierce ) + option = "Armor Pierce"; + + + if ( option != null && !Engines.ConPVP.DuelContext.AllowSpecialAbility( from, option, true ) ) + return false; + #endregion + return CheckSkills( from ) && CheckMana( from, false ); } diff --git a/Scripts/Items/Weapons/Axes/BaseAxe.cs b/Scripts/Items/Weapons/Axes/BaseAxe.cs index e73fca1e9..ed42495da 100644 --- a/Scripts/Items/Weapons/Axes/BaseAxe.cs +++ b/Scripts/Items/Weapons/Axes/BaseAxe.cs @@ -153,7 +153,7 @@ namespace Server.Items { base.OnHit( attacker, defender, damageBonus ); - if ( !Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() ) + if ( !Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() && Engines.ConPVP.DuelContext.AllowSpecialAbility( attacker, "Concussion Blow", false ) ) { StatMod mod = defender.GetStatMod( "Concussion" ); diff --git a/Scripts/Items/Weapons/BaseWeapon.cs b/Scripts/Items/Weapons/BaseWeapon.cs index bc0ebe600..241fea3c7 100644 --- a/Scripts/Items/Weapons/BaseWeapon.cs +++ b/Scripts/Items/Weapons/BaseWeapon.cs @@ -1019,8 +1019,18 @@ namespace Server.Items PlayerMobile p = attacker as PlayerMobile; canSwing = ( p == null || p.PeacedUntil <= DateTime.Now ); + } } + + #region Dueling + if ( attacker is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)attacker; + + if ( pm.DuelContext != null && !pm.DuelContext.CheckItemEquip( attacker, this ) ) + canSwing = false; } + #endregion if ( canSwing && attacker.HarmfulCheck( defender ) ) { diff --git a/Scripts/Items/Weapons/Fists.cs b/Scripts/Items/Weapons/Fists.cs index ddab2f6ff..c4047b8ef 100644 --- a/Scripts/Items/Weapons/Fists.cs +++ b/Scripts/Items/Weapons/Fists.cs @@ -217,6 +217,11 @@ namespace Server.Items { Mobile m = e.Mobile; + #region Dueling + if ( !Engines.ConPVP.DuelContext.AllowSpecialAbility( m, "Disarm", true ) ) + return; + #endregion + double armsValue = m.Skills[SkillName.ArmsLore].Value; double wresValue = m.Skills[SkillName.Wrestling].Value; @@ -242,6 +247,11 @@ namespace Server.Items { Mobile m = e.Mobile; + #region Dueling + if ( !Engines.ConPVP.DuelContext.AllowSpecialAbility( m, "Stun", true ) ) + return; + #endregion + double anatValue = m.Skills[SkillName.Anatomy].Value; double wresValue = m.Skills[SkillName.Wrestling].Value; diff --git a/Scripts/Items/Weapons/Maces/BaseBashing.cs b/Scripts/Items/Weapons/Maces/BaseBashing.cs index 7c1c1a988..5e38b4c08 100644 --- a/Scripts/Items/Weapons/Maces/BaseBashing.cs +++ b/Scripts/Items/Weapons/Maces/BaseBashing.cs @@ -46,7 +46,7 @@ namespace Server.Items { double damage = base.GetBaseDamage( attacker ); - if ( !Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() ) + if ( !Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() && Engines.ConPVP.DuelContext.AllowSpecialAbility( attacker, "Crushing Blow", false ) ) { damage *= 1.5; diff --git a/Scripts/Items/Weapons/PoleArms/BasePoleArm.cs b/Scripts/Items/Weapons/PoleArms/BasePoleArm.cs index 9792db3b6..2ada1ef88 100644 --- a/Scripts/Items/Weapons/PoleArms/BasePoleArm.cs +++ b/Scripts/Items/Weapons/PoleArms/BasePoleArm.cs @@ -107,7 +107,7 @@ namespace Server.Items { base.OnHit( attacker, defender, damageBonus ); - if ( !Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() ) + if ( !Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() && Engines.ConPVP.DuelContext.AllowSpecialAbility( attacker, "Concussion Blow", false ) ) { StatMod mod = defender.GetStatMod( "Concussion" ); diff --git a/Scripts/Items/Weapons/Ranged/BaseRanged.cs b/Scripts/Items/Weapons/Ranged/BaseRanged.cs index 47c1befd3..5f3e7f748 100644 --- a/Scripts/Items/Weapons/Ranged/BaseRanged.cs +++ b/Scripts/Items/Weapons/Ranged/BaseRanged.cs @@ -68,6 +68,16 @@ namespace Server.Items } } + #region Dueling + if ( attacker is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)attacker; + + if ( pm.DuelContext != null && !pm.DuelContext.CheckItemEquip( attacker, this ) ) + canSwing = false; + } + #endregion + if ( canSwing && attacker.HarmfulCheck( defender ) ) { attacker.DisruptiveAction(); diff --git a/Scripts/Items/Weapons/SpearsAndForks/BaseSpear.cs b/Scripts/Items/Weapons/SpearsAndForks/BaseSpear.cs index 8778a4934..e4487040e 100644 --- a/Scripts/Items/Weapons/SpearsAndForks/BaseSpear.cs +++ b/Scripts/Items/Weapons/SpearsAndForks/BaseSpear.cs @@ -39,7 +39,7 @@ namespace Server.Items { base.OnHit( attacker, defender, damageBonus ); - if ( !Core.AOS && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() ) + if ( !Core.AOS && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() && Engines.ConPVP.DuelContext.AllowSpecialAbility( attacker, "Paralyzing Blow", false ) ) { defender.SendMessage( "You receive a paralyzing blow!" ); // Is this not localized? defender.Freeze( TimeSpan.FromSeconds( 2.0 ) ); diff --git a/Scripts/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs b/Scripts/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs index 510ea7d9b..891e56900 100644 --- a/Scripts/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs +++ b/Scripts/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs @@ -112,7 +112,11 @@ namespace Server.Items Mobile targ = (Mobile) target; Container pack = targ.Backpack; - if ( pack != null && pack.FindItemByType( new Type[]{ typeof( SnowPile ), typeof( PileOfGlacialSnow ) } ) != null ) + if ( from.Region.IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) || targ.Region.IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) ) + { + from.SendMessage( "You may not throw snow here." ); + } + else if ( pack != null && pack.FindItemByType( new Type[]{ typeof( SnowPile ), typeof( PileOfGlacialSnow ) } ) != null ) { if ( from.BeginAction( typeof( SnowPile ) ) ) { diff --git a/Scripts/Misc/Gifts/Winter2004/SnowPile.cs b/Scripts/Misc/Gifts/Winter2004/SnowPile.cs index c51e5632e..7344ff718 100644 --- a/Scripts/Misc/Gifts/Winter2004/SnowPile.cs +++ b/Scripts/Misc/Gifts/Winter2004/SnowPile.cs @@ -98,7 +98,11 @@ namespace Server.Items Mobile targ = (Mobile) target; Container pack = targ.Backpack; - if ( pack != null && pack.FindItemByType( new Type[]{ typeof( SnowPile ), typeof( PileOfGlacialSnow ) } ) != null ) + if ( from.Region.IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) || targ.Region.IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) ) + { + from.SendMessage( "You may not throw snow here." ); + } + else if ( pack != null && pack.FindItemByType( new Type[]{ typeof( SnowPile ), typeof( PileOfGlacialSnow ) } ) != null ) { if ( from.BeginAction( typeof( SnowPile ) ) ) { diff --git a/Scripts/Misc/Guild.cs b/Scripts/Misc/Guild.cs index a6bbf998d..8595dd3aa 100644 --- a/Scripts/Misc/Guild.cs +++ b/Scripts/Misc/Guild.cs @@ -1779,8 +1779,6 @@ namespace Server.Guilds } } - - [CommandProperty( AccessLevel.GameMaster )] public DateTime LastFealty { @@ -1803,7 +1801,6 @@ namespace Server.Guilds } } - public List Allies { get @@ -1877,6 +1874,5 @@ namespace Server.Guilds } #endregion - } } diff --git a/Scripts/Misc/Notoriety.cs b/Scripts/Misc/Notoriety.cs index 4494871c4..5a8b08b79 100644 --- a/Scripts/Misc/Notoriety.cs +++ b/Scripts/Misc/Notoriety.cs @@ -65,6 +65,58 @@ namespace Server.Misc if( from == null || target == null || from.AccessLevel > AccessLevel.Player || target.AccessLevel > AccessLevel.Player ) return true; + #region Dueling + PlayerMobile pmFrom = from as PlayerMobile; + PlayerMobile pmTarg = target as PlayerMobile; + + if( pmFrom == null && from is BaseCreature ) + { + BaseCreature bcFrom = (BaseCreature)from; + + if( bcFrom.Summoned ) + pmFrom = bcFrom.SummonMaster as PlayerMobile; + } + + if( pmTarg == null && target is BaseCreature ) + { + BaseCreature bcTarg = (BaseCreature)target; + + if( bcTarg.Summoned ) + pmTarg = bcTarg.SummonMaster as PlayerMobile; + } + + if( pmFrom!= null && pmTarg != null ) + { + if( pmFrom.DuelContext != pmTarg.DuelContext && ((pmFrom.DuelContext != null && pmFrom.DuelContext.Started) || (pmTarg.DuelContext != null && pmTarg.DuelContext.Started)) ) + return false; + + if( pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext && ((pmFrom.DuelContext.StartedReadyCountdown && !pmFrom.DuelContext.Started) || pmFrom.DuelContext.Tied || pmFrom.DuelPlayer.Eliminated || pmTarg.DuelPlayer.Eliminated) ) + return false; + + if( pmFrom.DuelPlayer != null && !pmFrom.DuelPlayer.Eliminated && pmFrom.DuelContext != null && pmFrom.DuelContext.IsSuddenDeath ) + return false; + + if( pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext && pmFrom.DuelContext.m_Tournament != null && pmFrom.DuelContext.m_Tournament.IsNotoRestricted && pmFrom.DuelPlayer != null && pmTarg.DuelPlayer != null && pmFrom.DuelPlayer.Participant != pmTarg.DuelPlayer.Participant ) + return false; + + if( pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext && pmFrom.DuelContext.Started ) + return true; + } + + if( (pmFrom != null && pmFrom.DuelContext != null && pmFrom.DuelContext.Started) || (pmTarg != null && pmTarg.DuelContext != null && pmTarg.DuelContext.Started) ) + return false; + + Engines.ConPVP.SafeZone sz = from.Region.GetRegion( typeof( Engines.ConPVP.SafeZone ) ) as Engines.ConPVP.SafeZone; + + if( sz != null /*&& sz.IsDisabled()*/ ) + return false; + + sz = target.Region.GetRegion( typeof( Engines.ConPVP.SafeZone ) ) as Engines.ConPVP.SafeZone; + + if( sz != null /*&& sz.IsDisabled()*/ ) + return false; + #endregion + Map map = from.Map; #region Factions @@ -104,6 +156,55 @@ namespace Server.Misc if( from == null || target == null || from.AccessLevel > AccessLevel.Player || target.AccessLevel > AccessLevel.Player ) return true; + #region Dueling + PlayerMobile pmFrom = from as PlayerMobile; + PlayerMobile pmTarg = target as PlayerMobile; + + if( pmFrom == null && from is BaseCreature ) + { + BaseCreature bcFrom = (BaseCreature)from; + + if( bcFrom.Summoned ) + pmFrom = bcFrom.SummonMaster as PlayerMobile; + } + + if( pmTarg == null && target is BaseCreature ) + { + BaseCreature bcTarg = (BaseCreature)target; + + if( bcTarg.Summoned ) + pmTarg = bcTarg.SummonMaster as PlayerMobile; + } + + if( pmFrom!= null && pmTarg != null ) + { + if( pmFrom.DuelContext != pmTarg.DuelContext && ((pmFrom.DuelContext != null && pmFrom.DuelContext.Started) || (pmTarg.DuelContext != null && pmTarg.DuelContext.Started)) ) + return false; + + if( pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext && ((pmFrom.DuelContext.StartedReadyCountdown && !pmFrom.DuelContext.Started) || pmFrom.DuelContext.Tied || pmFrom.DuelPlayer.Eliminated || pmTarg.DuelPlayer.Eliminated) ) + return false; + + if( pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext && pmFrom.DuelContext.m_Tournament != null && pmFrom.DuelContext.m_Tournament.IsNotoRestricted && pmFrom.DuelPlayer != null && pmTarg.DuelPlayer != null && pmFrom.DuelPlayer.Participant == pmTarg.DuelPlayer.Participant ) + return false; + + if( pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext && pmFrom.DuelContext.Started ) + return true; + } + + if( (pmFrom != null && pmFrom.DuelContext != null && pmFrom.DuelContext.Started) || (pmTarg != null && pmTarg.DuelContext != null && pmTarg.DuelContext.Started) ) + return false; + + Engines.ConPVP.SafeZone sz = from.Region.GetRegion( typeof( Engines.ConPVP.SafeZone ) ) as Engines.ConPVP.SafeZone; + + if( sz != null /*&& sz.IsDisabled()*/ ) + return false; + + sz = target.Region.GetRegion( typeof( Engines.ConPVP.SafeZone ) ) as Engines.ConPVP.SafeZone; + + if( sz != null /*&& sz.IsDisabled()*/ ) + return false; + #endregion + Map map = from.Map; if( map != null && (map.Rules & MapRules.HarmfulRestrictions) == 0 ) @@ -269,6 +370,17 @@ namespace Server.Misc if( Core.AOS && (target.Blessed || (target is BaseVendor && ((BaseVendor)target).IsInvulnerable) || target is PlayerVendor || target is TownCrier) ) return Notoriety.Invulnerable; + #region Dueling + if( source is PlayerMobile && target is PlayerMobile ) + { + PlayerMobile pmFrom = (PlayerMobile)source; + PlayerMobile pmTarg = (PlayerMobile)target; + + if( pmFrom.DuelContext != null && pmFrom.DuelContext.StartedBeginCountdown && !pmFrom.DuelContext.Finished && pmFrom.DuelContext == pmTarg.DuelContext ) + return pmFrom.DuelContext.IsAlly( pmFrom, pmTarg ) ? Notoriety.Ally : Notoriety.Enemy; + } + #endregion + if( target.AccessLevel > AccessLevel.Player ) return Notoriety.CanBeAttacked; diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs index 06b4a81e9..5a9d9a70c 100644 --- a/Scripts/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs @@ -44,9 +44,9 @@ namespace Server.Mobiles CantWalk = true; if ( Utility.RandomBool() ) - PackItem( new SulfurousAsh( 4 ) ); + PackItem( new SulfurousAsh( 4 ) ); else - PackItem( new BlackPearl( 4 ) ); + PackItem( new BlackPearl( 4 ) ); //PackItem( new SpecialFishingNet() ); } diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/Kraken.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/Kraken.cs index 5ad696198..aa91a70ac 100644 --- a/Scripts/Mobiles/Monsters/Reptile/Melee/Kraken.cs +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/Kraken.cs @@ -48,10 +48,10 @@ namespace Server.Mobiles Rope rope = new Rope(); rope.ItemID = 0x14F8; PackItem( rope ); - + if( Utility.RandomDouble() < .05 ) PackItem( new MessageInABottle() ); - + PackItem( new SpecialFishingNet() ); //Confirm? } diff --git a/Scripts/Mobiles/PlayerMobile.cs b/Scripts/Mobiles/PlayerMobile.cs index dc58dcec4..2170d26ed 100644 --- a/Scripts/Mobiles/PlayerMobile.cs +++ b/Scripts/Mobiles/PlayerMobile.cs @@ -6,6 +6,7 @@ using Server.Items; using Server.Gumps; using Server.Multis; using Server.Engines.Help; +using Server.Engines.ConPVP; using Server.ContextMenus; using Server.Network; using Server.Spells; @@ -1415,6 +1416,11 @@ namespace Server.Mobiles public override bool AllowItemUse( Item item ) { + #region Dueling + if ( m_DuelContext != null && !m_DuelContext.AllowItemUse( this, item ) ) + return false; + #endregion + return DesignContext.Check( this ); } @@ -1442,6 +1448,11 @@ namespace Server.Mobiles } } + #region Dueling + if ( m_DuelContext != null && !m_DuelContext.AllowSkillUse( this, skill ) ) + return false; + #endregion + return DesignContext.Check( this ); } @@ -1516,7 +1527,7 @@ namespace Server.Mobiles if ( Alive && house.InternalizedVendors.Count > 0 && house.IsOwner( this ) ) list.Add( new CallbackEntry( 6204, new ContextCallback( GetVendor ) ) ); - if ( house.IsAosRules ) + if ( house.IsAosRules && !Region.IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) ) // Dueling list.Add( new CallbackEntry( 6207, new ContextCallback( LeaveHouse ) ) ); } @@ -1801,6 +1812,11 @@ namespace Server.Mobiles if ( !base.CheckEquip( item ) ) return false; + #region Dueling + if ( m_DuelContext != null && !m_DuelContext.AllowItemEquip( this, item ) ) + return false; + #endregion + #region Factions FactionItem factionItem = FactionItem.Find( item ); @@ -1960,6 +1976,11 @@ namespace Server.Mobiles { CheckLightLevels( false ); + #region Dueling + if ( m_DuelContext != null ) + m_DuelContext.OnLocationChanged( this ); + #endregion + DesignContext context = m_DesignContext; if ( context == null || m_NoRecursion ) @@ -1997,6 +2018,16 @@ namespace Server.Mobiles if ( m is BaseCreature && !((BaseCreature)m).Controlled ) return ( !Alive || !m.Alive || IsDeadBondedPet || m.IsDeadBondedPet ) || ( Hidden && AccessLevel > AccessLevel.Player ); + #region Dueling + if ( Region.IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) && m is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile) m; + + if ( pm.DuelContext == null || pm.DuelPlayer == null || !pm.DuelContext.Started || pm.DuelContext.Finished || pm.DuelPlayer.Eliminated ) + return true; + } + #endregion + return base.OnMoveOver( m ); } @@ -2013,6 +2044,11 @@ namespace Server.Mobiles if ( (Map != Faction.Facet && oldMap == Faction.Facet) || (Map == Faction.Facet && oldMap != Faction.Facet) ) InvalidateProperties(); + #region Dueling + if ( m_DuelContext != null ) + m_DuelContext.OnMapChanged( this ); + #endregion + DesignContext context = m_DesignContext; if ( context == null || m_NoRecursion ) @@ -2179,6 +2215,11 @@ namespace Server.Mobiles { if ( InsuranceEnabled && item.Insured ) { + #region Dueling + if ( m_DuelPlayer != null && m_DuelContext != null && m_DuelContext.Registered && m_DuelContext.Started && !m_DuelPlayer.Eliminated ) + return true; + #endregion + if ( AutoRenewInsurance ) { int cost = ( m_InsuranceAward == null ? 600 : 300 ); @@ -2334,16 +2375,22 @@ namespace Server.Mobiles killer = master; } - if ( this.Young ) + if ( this.Young && m_DuelContext == null ) { if ( YoungDeathTeleport() ) Timer.DelayCall( TimeSpan.FromSeconds( 2.5 ), new TimerCallback( SendYoungDeathNotice ) ); } - Faction.HandleDeath( this, killer ); + if ( m_DuelContext == null || !m_DuelContext.Registered || !m_DuelContext.Started || m_DuelPlayer == null || m_DuelPlayer.Eliminated ) + Faction.HandleDeath( this, killer ); Server.Guilds.Guild.HandleDeath( this, killer ); + #region Dueling + if ( m_DuelContext != null ) + m_DuelContext.OnDeath( this, c ); + #endregion + if( m_BuffTable != null ) { List list = new List(); @@ -2627,7 +2674,7 @@ namespace Server.Mobiles public override bool CheckPoisonImmunity( Mobile from, Poison poison ) { - if ( this.Young ) + if ( this.Young && (DuelContext == null || !DuelContext.Started || DuelContext.Finished) ) return true; return base.CheckPoisonImmunity( from, poison ); @@ -2635,7 +2682,7 @@ namespace Server.Mobiles public override void OnPoisonImmunity( Mobile from, Poison poison ) { - if ( this.Young ) + if ( this.Young && (DuelContext == null || !DuelContext.Started || DuelContext.Finished) ) SendLocalizedMessage( 502808 ); // You would have been poisoned, were you not new to the land of Britannia. Be careful in the future. else base.OnPoisonImmunity( from, poison ); @@ -2976,7 +3023,7 @@ namespace Server.Mobiles m_BOBFilter = new Engines.BulkOrders.BOBFilter(); if( m_GuildRank == null ) - m_GuildRank = Guilds.RankDefinition.Member; //Default to member if going from older verstion to new version (only time it should be null) + m_GuildRank = Guilds.RankDefinition.Member; //Default to member if going from older version to new version (only time it should be null) if( m_LastOnline == DateTime.MinValue && Account != null ) m_LastOnline = ((Account)Account).LastLogin; @@ -3190,6 +3237,24 @@ namespace Server.Mobiles if ( m is PlayerMobile && ((PlayerMobile)m).m_VisList.Contains( this ) ) return true; + if ( m_DuelContext != null && m_DuelPlayer != null && !m_DuelContext.Finished && m_DuelContext.m_Tournament != null && !m_DuelPlayer.Eliminated ) + { + Mobile owner = m; + + if ( owner is BaseCreature ) + { + BaseCreature bc = (BaseCreature)owner; + + Mobile master = bc.GetMaster(); + + if( master != null ) + owner = master; + } + + if ( m.AccessLevel == AccessLevel.Player && owner is PlayerMobile && ((PlayerMobile)owner).DuelContext != m_DuelContext ) + return false; + } + return base.CanSee( m ); } @@ -3375,6 +3440,37 @@ namespace Server.Mobiles } #endregion + #region Dueling + private Engines.ConPVP.DuelContext m_DuelContext; + private Engines.ConPVP.DuelPlayer m_DuelPlayer; + + public Engines.ConPVP.DuelContext DuelContext + { + get{ return m_DuelContext; } + } + + public Engines.ConPVP.DuelPlayer DuelPlayer + { + get{ return m_DuelPlayer; } + set + { + bool wasInTourny = ( m_DuelContext != null && !m_DuelContext.Finished && m_DuelContext.m_Tournament != null ); + + m_DuelPlayer = value; + + if ( m_DuelPlayer == null ) + m_DuelContext = null; + else + m_DuelContext = m_DuelPlayer.Participant.Context; + + bool isInTourny = ( m_DuelContext != null && !m_DuelContext.Finished && m_DuelContext.m_Tournament != null ); + + if ( wasInTourny != isInTourny ) + SendEverything(); + } + } + #endregion + #region Quests private QuestSystem m_Quest; private List m_DoneQuests; @@ -3509,6 +3605,20 @@ namespace Server.Mobiles if ( checkTurning && (dir & Direction.Mask) != (this.Direction & Direction.Mask) ) return Mobile.RunMount; // We are NOT actually moving (just a direction change) + #region Dueling / CTF + + if ( m_DuelContext != null && m_DuelContext.m_EventGame is CTFGame ) + { + CTFGame game = (CTFGame)m_DuelContext.m_EventGame; + CTFTeamInfo team = game.GetTeamInfo( this ); + + //We don't want to just search through all the contents of our pack everytime we take a step. + if ( game.Controller != null && game.Controller.SlowFlagBearer && team != null && team.Flag != null && team.Flag.RootParent == this ) + return Mobile.WalkFoot; + } + + #endregion + TransformContext context = TransformationSpellHelper.GetContext( this ); if ( context != null && context.Type == typeof( ReaperFormSpell ) ) diff --git a/Scripts/Skills/Peacemaking.cs b/Scripts/Skills/Peacemaking.cs index 0c62e3005..d7602d972 100644 --- a/Scripts/Skills/Peacemaking.cs +++ b/Scripts/Skills/Peacemaking.cs @@ -54,6 +54,14 @@ namespace Server.SkillHandlers { from.SendLocalizedMessage( 1049528 ); // You cannot calm that! } + else if ( from.Region.IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) ) + { + from.SendMessage( "You may not peacemake here." ); + } + else if ( ((Mobile)targeted).Region.IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) ) + { + from.SendMessage( "You may not peacemake there." ); + } else if ( !m_Instrument.IsChildOf( from.Backpack ) ) { from.SendLocalizedMessage( 1062488 ); // The instrument you are trying to play is no longer in your backpack! diff --git a/Scripts/Skills/Poisoning.cs b/Scripts/Skills/Poisoning.cs index 57d695998..f6d4c205b 100644 --- a/Scripts/Skills/Poisoning.cs +++ b/Scripts/Skills/Poisoning.cs @@ -81,8 +81,11 @@ namespace Server.SkillHandlers from.PlaySound( 0x4F ); - m_Potion.Consume(); - from.AddToBackpack( new Bottle() ); + if ( !Engines.ConPVP.DuelContext.IsFreeConsume( from ) ) + { + m_Potion.Consume(); + from.AddToBackpack( new Bottle() ); + } } else // Target can't be poisoned { diff --git a/Scripts/Spells/Base/SpecialMove.cs b/Scripts/Spells/Base/SpecialMove.cs index 6de975db3..41277620b 100644 --- a/Scripts/Spells/Base/SpecialMove.cs +++ b/Scripts/Spells/Base/SpecialMove.cs @@ -157,6 +157,30 @@ namespace Server.Spells return false; } + #region Dueling + string option = null; + + if ( this is Backstab ) + option = "Backstab"; + else if ( this is DeathStrike ) + option = "Death Strike"; + else if ( this is FocusAttack ) + option = "Focus Attack"; + else if ( this is KiAttack ) + option = "Ki Attack"; + else if ( this is SurpriseAttack ) + option = "Surprise Attack"; + else if ( this is HonorableExecution ) + option = "Honorable Execution"; + else if ( this is LightningStrike ) + option = "Lightning Strike"; + else if ( this is MomentumStrike ) + option = "Momentum Strike"; + + if ( option != null && !Engines.ConPVP.DuelContext.AllowSpecialMove( from, option, this ) ) + return false; + #endregion + return CheckSkills( from ) && CheckMana( from, false ); } diff --git a/Scripts/Spells/Base/Spell.cs b/Scripts/Spells/Base/Spell.cs index a6d5166cc..b3cbd12b5 100644 --- a/Scripts/Spells/Base/Spell.cs +++ b/Scripts/Spells/Base/Spell.cs @@ -237,6 +237,9 @@ namespace Server.Spells if ( AosAttributes.GetValue( m_Caster, AosAttribute.LowerRegCost ) > Utility.Random( 100 ) ) return true; + if ( Engines.ConPVP.DuelContext.IsFreeConsume( m_Caster ) ) + return true; + Container pack = m_Caster.Backpack; if ( pack == null ) @@ -508,6 +511,11 @@ namespace Server.Spells { m_Caster.SendLocalizedMessage( 1072060 ); // You cannot cast a spell while calmed. } + #region Dueling + else if ( m_Caster is PlayerMobile && ((PlayerMobile)m_Caster).DuelContext != null && !((PlayerMobile)m_Caster).DuelContext.AllowSpellCast( m_Caster, this ) ) + { + } + #endregion else if ( m_Caster.Mana >= ScaleMana( GetMana() ) ) { if ( m_Caster.Spell == null && m_Caster.CheckSpellCast( this ) && CheckCast() && m_Caster.Region.OnBeginSpellCast( m_Caster, this ) ) diff --git a/Scripts/Spells/Base/SpellHelper.cs b/Scripts/Spells/Base/SpellHelper.cs index 751b3e185..98782c635 100644 --- a/Scripts/Spells/Base/SpellHelper.cs +++ b/Scripts/Spells/Base/SpellHelper.cs @@ -363,6 +363,33 @@ namespace Server.Spells if( to.Hidden && to.AccessLevel > from.AccessLevel ) return false; + #region Dueling + PlayerMobile pmFrom = from as PlayerMobile; + PlayerMobile pmTarg = to as PlayerMobile; + + if ( pmFrom == null && from is BaseCreature ) + { + BaseCreature bcFrom = (BaseCreature) from; + + if ( bcFrom.Summoned ) + pmFrom = bcFrom.SummonMaster as PlayerMobile; + } + + if ( pmTarg == null && to is BaseCreature ) + { + BaseCreature bcTarg = (BaseCreature) to; + + if ( bcTarg.Summoned ) + pmTarg = bcTarg.SummonMaster as PlayerMobile; + } + + if ( pmFrom != null && pmTarg != null ) + { + if ( pmFrom.DuelContext != null && pmFrom.DuelContext == pmTarg.DuelContext && pmFrom.DuelContext.Started && pmFrom.DuelPlayer != null && pmTarg.DuelPlayer != null ) + return ( pmFrom.DuelPlayer.Participant != pmTarg.DuelPlayer.Participant ); + } + #endregion + Guild fromGuild = GetGuildFor( from ); Guild toGuild = GetGuildFor( to ); @@ -550,6 +577,7 @@ namespace Server.Spells new TravelValidator( IsCrystalCave ), new TravelValidator( IsDoomGauntlet ), new TravelValidator( IsDoomFerry ), + new TravelValidator( IsSafeZone ), new TravelValidator( IsFactionStronghold ), new TravelValidator( IsChampionSpawn ), new TravelValidator( IsTokunoDungeon ), @@ -561,14 +589,14 @@ namespace Server.Spells private static bool[,] m_Rules = new bool[,] { - /*T2A(Fel) Ilshenar Wind(Tram), Wind(Fel), Dungeons(Fel), Solen(Tram), Solen(Fel), CrystalCave(Malas), Gauntlet(Malas), Gauntlet(Ferry), Stronghold, ChampionSpawn, Dungeons(Tokuno[Malas]), LampRoom(Doom), GuardianRoom(Doom), Heartwood, MLDungeons */ -/* Recall From */ { false, true, true, false, false, true, false, false, false, false, true, false, true, false, false, false, false }, -/* Recall To */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }, -/* Gate From */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }, -/* Gate To */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }, -/* Mark In */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }, -/* Tele From */ { true, true, true, true, true, true, true, false, true, true, false, true, true, true, true, false, true }, -/* Tele To */ { true, true, true, true, true, true, true, false, true, false, false, true, true, true, true, false, false }, + /*T2A(Fel) Ilshenar Wind(Tram), Wind(Fel), Dungeons(Fel), Solen(Tram), Solen(Fel), CrystalCave(Malas), Gauntlet(Malas), Gauntlet(Ferry), SafeZone, Stronghold, ChampionSpawn, Dungeons(Tokuno[Malas]), LampRoom(Doom), GuardianRoom(Doom), Heartwood, MLDungeons */ +/* Recall From */ { false, true, true, false, false, true, false, false, false, false, true, true, false, true, false, false, false, false }, +/* Recall To */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }, +/* Gate From */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }, +/* Gate To */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }, +/* Mark In */ { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }, +/* Tele From */ { true, true, true, true, true, true, true, false, true, true, true, false, true, true, true, true, false, true }, +/* Tele To */ { true, true, true, true, true, true, true, false, true, false, false, false, true, true, true, true, false, false }, }; public static void SendInvalidMessage( Mobile caster, TravelCheckType type ) @@ -706,6 +734,26 @@ namespace Server.Spells || ( x >= 1188 && y >= 509 && x < 1201 && y < 513 ); } + public static bool IsSafeZone( Map map, Point3D loc ) + { + #region Duels + if ( Region.Find( loc, map ).IsPartOf( typeof( Engines.ConPVP.SafeZone ) ) ) + { + if ( m_TravelType == TravelCheckType.TeleportTo || m_TravelType == TravelCheckType.TeleportFrom ) + { + PlayerMobile pm = m_TravelCaster as PlayerMobile; + + if ( pm != null && pm.DuelPlayer != null && !pm.DuelPlayer.Eliminated ) + return true; + } + + return true; + } + #endregion + + return false; + } + public static bool IsFactionStronghold( Map map, Point3D loc ) { /*// Teleporting is allowed, but only for faction members @@ -821,7 +869,19 @@ namespace Server.Spells if( map == null ) return false; - GuardedRegion reg = (GuardedRegion)Region.Find( loc, map ).GetRegion( typeof( GuardedRegion ) ); + #region Dueling + Engines.ConPVP.SafeZone sz = (Engines.ConPVP.SafeZone) Region.Find( loc, map ).GetRegion( typeof( Engines.ConPVP.SafeZone ) ); + + if ( sz != null ) + { + PlayerMobile pm = (PlayerMobile) caster; + + if ( pm == null || pm.DuelContext == null || !pm.DuelContext.Started || pm.DuelPlayer == null || pm.DuelPlayer.Eliminated ) + return true; + } + #endregion + + GuardedRegion reg = (GuardedRegion) Region.Find( loc, map ).GetRegion( typeof( GuardedRegion ) ); return (reg != null && !reg.IsDisabled()); } diff --git a/Scripts/Spells/Chivalry/CleanseByFire.cs b/Scripts/Spells/Chivalry/CleanseByFire.cs index 9059ff72c..2d8a4c330 100644 --- a/Scripts/Spells/Chivalry/CleanseByFire.cs +++ b/Scripts/Spells/Chivalry/CleanseByFire.cs @@ -25,6 +25,17 @@ namespace Server.Spells.Chivalry { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Chivalry/CloseWounds.cs b/Scripts/Spells/Chivalry/CloseWounds.cs index 6b2eb6a3a..d77bd839c 100644 --- a/Scripts/Spells/Chivalry/CloseWounds.cs +++ b/Scripts/Spells/Chivalry/CloseWounds.cs @@ -26,6 +26,17 @@ namespace Server.Spells.Chivalry { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Chivalry/RemoveCurse.cs b/Scripts/Spells/Chivalry/RemoveCurse.cs index c07ea15ed..dcbd15dfa 100644 --- a/Scripts/Spells/Chivalry/RemoveCurse.cs +++ b/Scripts/Spells/Chivalry/RemoveCurse.cs @@ -27,6 +27,17 @@ namespace Server.Spells.Chivalry { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Eighth/Resurrection.cs b/Scripts/Spells/Eighth/Resurrection.cs index 20e427bb3..b9c1219a8 100644 --- a/Scripts/Spells/Eighth/Resurrection.cs +++ b/Scripts/Spells/Eighth/Resurrection.cs @@ -22,6 +22,17 @@ namespace Server.Spells.Eighth { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/First/Heal.cs b/Scripts/Spells/First/Heal.cs index 70a42440c..6527d9abc 100644 --- a/Scripts/Spells/First/Heal.cs +++ b/Scripts/Spells/First/Heal.cs @@ -23,6 +23,17 @@ namespace Server.Spells.First { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Fourth/GreaterHeal.cs b/Scripts/Spells/Fourth/GreaterHeal.cs index d54d7e533..f9407f2ef 100644 --- a/Scripts/Spells/Fourth/GreaterHeal.cs +++ b/Scripts/Spells/Fourth/GreaterHeal.cs @@ -24,6 +24,17 @@ namespace Server.Spells.Fourth { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Necromancy/SummonFamiliar.cs b/Scripts/Spells/Necromancy/SummonFamiliar.cs index ef9ed9c7c..906fa6ee7 100644 --- a/Scripts/Spells/Necromancy/SummonFamiliar.cs +++ b/Scripts/Spells/Necromancy/SummonFamiliar.cs @@ -156,8 +156,13 @@ namespace Server.Spells.Necromancy double spirit = m_From.Skills[SkillName.SpiritSpeak].Value; BaseCreature check = (BaseCreature)SummonFamiliarSpell.Table[m_From]; - - if ( check != null && !check.Deleted ) + + #region Dueling + if ( m_From is PlayerMobile && ( (PlayerMobile)m_From ).DuelContext != null && !( (PlayerMobile)m_From ).DuelContext.AllowSpellCast( m_From, m_Spell ) ) + { + } + #endregion + else if ( check != null && !check.Deleted ) { m_From.SendLocalizedMessage( 1061605 ); // You already have a familiar. } diff --git a/Scripts/Spells/Ninjitsu/AnimalForm.cs b/Scripts/Spells/Ninjitsu/AnimalForm.cs index 87f7126c9..17a470ed3 100644 --- a/Scripts/Spells/Ninjitsu/AnimalForm.cs +++ b/Scripts/Spells/Ninjitsu/AnimalForm.cs @@ -474,11 +474,16 @@ namespace Server.Spells.Ninjitsu } else if (BaseFormTalisman.EntryEnabled(sender.Mobile, entry.Type)) { - if (AnimalForm.Morph(m_Caster, entryID) == MorphResult.Fail) + #region Dueling + if ( m_Caster is PlayerMobile && ((PlayerMobile)m_Caster).DuelContext != null && !((PlayerMobile)m_Caster).DuelContext.AllowSpellCast( m_Caster, m_Spell ) ) { - m_Caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 502632); // The spell fizzles. - m_Caster.FixedParticles(0x3735, 1, 30, 9503, EffectLayer.Waist); - m_Caster.PlaySound(0x5C); + } + #endregion + else if (AnimalForm.Morph(m_Caster, entryID) == MorphResult.Fail) + { + m_Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502632 ); // The spell fizzles. + m_Caster.FixedParticles( 0x3735, 1, 30, 9503, EffectLayer.Waist ); + m_Caster.PlaySound( 0x5C ); } else { diff --git a/Scripts/Spells/Second/Agility.cs b/Scripts/Spells/Second/Agility.cs index 72710f181..dade685c3 100644 --- a/Scripts/Spells/Second/Agility.cs +++ b/Scripts/Spells/Second/Agility.cs @@ -20,6 +20,17 @@ namespace Server.Spells.Second { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Second/Cunning.cs b/Scripts/Spells/Second/Cunning.cs index f74e24512..42b94078d 100644 --- a/Scripts/Spells/Second/Cunning.cs +++ b/Scripts/Spells/Second/Cunning.cs @@ -20,6 +20,17 @@ namespace Server.Spells.Second { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Second/Cure.cs b/Scripts/Spells/Second/Cure.cs index e614546fa..7c3732c68 100644 --- a/Scripts/Spells/Second/Cure.cs +++ b/Scripts/Spells/Second/Cure.cs @@ -20,6 +20,17 @@ namespace Server.Spells.Second { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Second/Strength.cs b/Scripts/Spells/Second/Strength.cs index 55ac6356a..70e63b307 100644 --- a/Scripts/Spells/Second/Strength.cs +++ b/Scripts/Spells/Second/Strength.cs @@ -20,6 +20,17 @@ namespace Server.Spells.Second { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Sixth/Invisibility.cs b/Scripts/Spells/Sixth/Invisibility.cs index c535dde40..35596f69a 100644 --- a/Scripts/Spells/Sixth/Invisibility.cs +++ b/Scripts/Spells/Sixth/Invisibility.cs @@ -22,6 +22,17 @@ namespace Server.Spells.Sixth { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Third/Bless.cs b/Scripts/Spells/Third/Bless.cs index 0ccb2ad3d..534eb7430 100644 --- a/Scripts/Spells/Third/Bless.cs +++ b/Scripts/Spells/Third/Bless.cs @@ -20,6 +20,17 @@ namespace Server.Spells.Third { } + public override bool CheckCast() + { + if ( Engines.ConPVP.DuelContext.CheckSuddenDeath( Caster ) ) + { + Caster.SendMessage( 0x22, "You cannot cast this spell when in sudden death." ); + return false; + } + + return base.CheckCast(); + } + public override void OnCast() { Caster.Target = new InternalTarget( this ); diff --git a/Scripts/Spells/Third/Poison.cs b/Scripts/Spells/Third/Poison.cs index 08e9770c9..184f8408d 100644 --- a/Scripts/Spells/Third/Poison.cs +++ b/Scripts/Spells/Third/Poison.cs @@ -71,7 +71,28 @@ namespace Server.Spells.Third } else { - double total = Caster.Skills[SkillName.Magery].Value + Caster.Skills[SkillName.Poisoning].Value; + //double total = Caster.Skills[SkillName.Magery].Value + Caster.Skills[SkillName.Poisoning].Value; + + #region Dueling + double total = Caster.Skills[SkillName.Magery].Value; + + if ( Caster is Mobiles.PlayerMobile ) + { + Mobiles.PlayerMobile pm = (Mobiles.PlayerMobile)Caster; + + if ( pm.DuelContext != null && pm.DuelContext.Started && !pm.DuelContext.Finished && !pm.DuelContext.Ruleset.GetOption( "Skills", "Poisoning" ) ) + { + } + else + { + total += Caster.Skills[SkillName.Poisoning].Value; + } + } + else + { + total += Caster.Skills[SkillName.Poisoning].Value; + } + #endregion double dist = Caster.GetDistanceToSqrt( m ); diff --git a/Server/Mobile.cs b/Server/Mobile.cs index dc5e0279c..9ac65a5a2 100644 --- a/Server/Mobile.cs +++ b/Server/Mobile.cs @@ -8176,7 +8176,7 @@ namespace Server return this == m || ( m.m_Map == m_Map && - (!m.Hidden || (m_AccessLevel != AccessLevel.Player && (m_AccessLevel >= m.AccessLevel || m_AccessLevel >= AccessLevel.Developer))) && + (!m.Hidden || (m_AccessLevel != AccessLevel.Player && (m_AccessLevel >= m.AccessLevel || m_AccessLevel >= AccessLevel.Administrator))) && ((m.Alive || (Core.SE && Skills.SpiritSpeak.Value >= 100.0)) || !Alive || m_AccessLevel > AccessLevel.Player || m.Warmode)); }