ModernUO/Scripts/Engines/ConPVP/Arena.cs
2018-09-14 08:48:39 -07:00

842 lines
18 KiB
C#

using System;
using System.Collections.Generic;
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 => m_Arena;
set{} }
[CommandProperty( AccessLevel.GameMaster )]
public bool IsPrivate{ get => m_IsPrivate;
set => m_IsPrivate = value;
}
public override string DefaultName => "arena controller";
[Constructible]
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<ArenaController> m_Instances = new List<ArenaController>();
public static List<ArenaController> Instances{ get => m_Instances;
set => m_Instances = value;
}
}
[PropertyObject]
public class ArenaStartPoints
{
private Point3D[] m_Points;
public Point3D[] Points => m_Points;
[CommandProperty( AccessLevel.GameMaster )]
public Point3D EdgeWest{ get => m_Points[0];
set => m_Points[0] = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D EdgeEast{ get => m_Points[1];
set => m_Points[1] = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D EdgeNorth{ get => m_Points[2];
set => m_Points[2] = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D EdgeSouth{ get => m_Points[3];
set => m_Points[3] = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D CornerNW{ get => m_Points[4];
set => m_Points[4] = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D CornerSE{ get => m_Points[5];
set => m_Points[5] = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D CornerSW{ get => m_Points[6];
set => m_Points[6] = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D CornerNE{ get => 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<Mobile> m_Players;
private TournamentController m_Tournament;
private Mobile m_Announcer;
private LadderController m_Ladder;
[CommandProperty( AccessLevel.GameMaster )]
public LadderController Ladder
{
get => m_Ladder;
set => m_Ladder = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public bool IsGuarded
{
get => 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 => 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 => m_Announcer;
set => m_Announcer = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public string Name
{
get => m_Name;
set{ m_Name = value; if ( m_Active ) m_Arenas.Sort(); }
}
[CommandProperty( AccessLevel.GameMaster )]
public Map Facet
{
get => m_Facet;
set
{
m_Facet = value;
if ( m_Teleporter != null )
m_Teleporter.Map = value;
if ( m_Region != null )
m_Region.Unregister();
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 );
else
m_Region = null;
}
}
[CommandProperty( AccessLevel.GameMaster )]
public Rectangle2D Bounds{ get => 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 => 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 => m_Outside;
set => m_Outside = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D GateIn{ get => m_GateIn;
set => m_GateIn = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D GateOut{ get => m_GateOut;
set{ m_GateOut = value; if ( m_Teleporter != null ) m_Teleporter.Location = m_GateOut; } }
[CommandProperty( AccessLevel.GameMaster )]
public Point3D Wall{ get => m_Wall;
set => m_Wall = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public bool IsOccupied => ( m_Players.Count > 0 );
[CommandProperty( AccessLevel.GameMaster )]
public ArenaStartPoints Points{ get => m_Points;
set{} }
public Item Teleporter{ get => m_Teleporter;
set => m_Teleporter = value;
}
public List<Mobile> Players => m_Players;
[CommandProperty( AccessLevel.GameMaster )]
public bool Active
{
get => 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[]
{
/*
* /\
* /\/\
* /\/\/\
* \/\/\/
* \/\/\
* \/\/
*/
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[]
{
/*
* /\
* /\/\
* /\/\/\
* /\/\/\/\
* \/\/\/\/
*/
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[]
{
new[,]{ { +1, 0 }, { 0, +1 } }, // west
new[,]{ { -1, 0 }, { 0, -1 } }, // east
new[,]{ { 0, +1 }, { +1, 0 } }, // north
new[,]{ { 0, -1 }, { -1, 0 } }, // south
new[,]{ { +1, 0 }, { 0, +1 } }, // nw
new[,]{ { -1, 0 }, { 0, -1 } }, // se
new[,]{ { 0, +1 }, { +1, 0 } }, // sw
new[,]{ { 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<Mobile>();
}
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<Mobile>();
}
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 => 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<Mobile> pets = new List<Mobile>();
foreach ( Mobile mob in facet.GetMobilesInBounds( m_Bounds ) ) {
if ( mob is BaseCreature pet && pet.Controlled && pet.ControlMaster != null && 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<Arena> m_Arenas = new List<Arena>();
public static List<Arena> Arenas => m_Arenas;
public static Arena FindArena( List<Mobile> 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<ArenaController> 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<ArenaEntry> arenas = new List<ArenaEntry>();
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 => m_VotesFor;
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 );
}
}
}