ModernUO/Scripts/Engines/ConPVP/Games/BombingRun.cs
2018-09-14 13:22:16 -07:00

1929 lines
43 KiB
C#

using System;
using System.Collections;
using System.Text;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Targeting;
using Server.Gumps;
namespace Server.Engines.ConPVP
{
public class BRBomb : Item
{
public override string DefaultName => "da bomb";
private BRGame m_Game;
private Mobile m_Thrower;
private EffectTimer m_Timer;
private bool m_Flying;
private ArrayList m_Helpers;
private Mobile FindOwner( object parent )
{
if ( parent is Item item )
return item.RootParent as Mobile;
if ( parent is Mobile mobile )
return mobile;
return null;
}
public BRBomb( BRGame game ) : base( 0x103C ) // 0x103C = bread, 0x1042 = pie, 0x1364 = rock, 0x13a8 = pillow, 0x2256 = bagball
{
Movable = false;
Hue = 0x35;
m_Game = game;
m_Helpers = new ArrayList();
m_Timer = new EffectTimer( this );
m_Timer.Start();
}
private class EffectTimer : Timer
{
private BRBomb m_Bomb;
private int m_Count;
public EffectTimer( BRBomb bomb ) : base( TimeSpan.Zero, TimeSpan.FromSeconds( 1.0 ) )
{
m_Bomb = bomb;
m_Count = 0;
Priority = TimerPriority.FiftyMS;
}
protected override void OnTick()
{
if ( m_Bomb.Parent == null && m_Bomb.m_Game?.Controller != null )
{
if ( !m_Bomb.m_Flying && m_Bomb.Map != Map.Internal )
Effects.SendLocationEffect( m_Bomb.GetWorldLocation(), m_Bomb.Map, 0x377A, 16, 10, m_Bomb.Hue, 0 );
if ( m_Bomb.Location != m_Bomb.m_Game.Controller.BombHome )
{
if ( ++m_Count >= 30 )
{
m_Bomb.m_Game.ReturnBomb();
m_Bomb.m_Game.Alert( "The bomb has been returned to it's starting point." );
m_Count = 0;
m_Bomb.m_Helpers.Clear();
}
}
else
{
m_Count = 0;
}
}
else
{
m_Count = 0;
}
}
}
public BRBomb( Serial serial ) : base( serial )
{
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize (reader);
int version = reader.ReadInt();
switch ( version )
{
case 0:
{
break;
}
}
Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Delete ) ).Start(); // delete this after the world loads
}
public override void Serialize(GenericWriter writer)
{
base.Serialize (writer);
writer.Write( (int) 0 ); // version
}
public override void OnAdded(IEntity parent)
{
base.OnAdded( parent );
Mobile mob = FindOwner( parent );
if ( mob != null )
{
mob.SolidHueOverride = 0x0499;
//int fighterRank = mob.Skills.Tactics.Value + mob.Skills.Anatomy + mob.Skills.Healing.Value + mob.Skills.Swords.Value + mob.Skills.Macing.Value + mob.Skills.Fencing.Value + mob.Skills.Archery.Value + mob.Skills.Parrying.Value;
//int mageRank = mob.Skills.Magery.Value + mob.Skills.Meditation.Value + mob.Skills.EvalInt.Value + mob.Skills.Inscribe.Value + mob.Skills.Wrestling.Value + mob.Skills.MagicResist;
//int theifRank = mob.Skills.Stealing.Value + mob.Skills.Snooping.Value + mob.Skills.Hiding.Value + mob.Skills.Stealth.Value + mob.Skills.RemoveTrap.Value + mob.Skills.Lockpicking.Value + mob.Skills.ItemID.Value;
// To Do: Give them a special ability while holding the ball maybe based on skills?
}
}
public override void OnRemoved(IEntity parent)
{
base.OnRemoved( parent );
Mobile mob = FindOwner( parent );
if ( mob != null && m_Game != null )
mob.SolidHueOverride = m_Game.GetColor( mob );
}
public void DropTo( Mobile mob, Mobile killer )
{
if ( mob != null && !mob.Deleted )
MoveToWorld( mob.Location, mob.Map );
else if ( killer != null && !killer.Deleted )
MoveToWorld( killer.Location, killer.Map );
else if ( m_Game != null )
m_Game.ReturnBomb();
}
public override bool OnMoveOver( Mobile m )
{
if ( m_Flying || !Visible || m_Game == null || m == null || !m.Alive )
return true;
BRTeamInfo useTeam = m_Game.GetTeamInfo( m );
if ( useTeam == null )
return true;
return TakeBomb( m, useTeam, "picked up" );
}
public override void OnLocationChange(Point3D oldLocation)
{
base.OnLocationChange(oldLocation);
if ( m_Flying || !Visible || m_Game == null || Parent != null )
return;
IPooledEnumerable<NetState> eable = GetClientsInRange( 0 );
foreach ( NetState ns in eable )
{
Mobile m = ns.Mobile;
if ( m == null || !m.Player || !m.Alive )
continue;
BRTeamInfo useTeam = m_Game.GetTeamInfo( m );
if ( useTeam != null )
TakeBomb( m, useTeam, "got" );
}
}
public override void OnAfterDelete()
{
base.OnAfterDelete ();
m_Timer?.Stop();
}
public override void OnDoubleClick( Mobile m )
{
if ( m_Game == null || !Visible || m == null || !m.Alive )
return;
if ( !m_Flying && IsChildOf( m.Backpack ) )
{
m.Target = new BombTarget( this, m );
}
else if ( Parent == null )
{
if ( m.InRange( Location, 1 ) && m.Location.Z != Z )
{
BRTeamInfo useTeam = m_Game.GetTeamInfo( m );
if ( useTeam == null )
return;
TakeBomb( m, useTeam, "grabbed" );
}
}
}
private class BombTarget : Target
{
private BRBomb m_Bomb;
private Mobile m_Mob;
private bool m_Resend = true;
public BombTarget( BRBomb bomb, Mobile from ) : base( 10, true, TargetFlags.None )
{
CheckLOS = false;
m_Bomb = bomb;
m_Mob = from;
m_Mob.SendMessage( 0x26, "Where do you want to throw it?" );
}
protected override void OnTarget( Mobile from, object targeted )
{
m_Resend = !m_Bomb.OnBombTarget( from, targeted );
}
protected override void OnTargetUntargetable( Mobile from, object targeted )
{
m_Resend = !m_Bomb.OnBombTarget( from, targeted );
}
protected override void OnTargetFinish( Mobile from )
{
base.OnTargetFinish( from );
// has to be delayed in case some other target canceled us...
if ( m_Resend )
Timer.DelayCall( TimeSpan.Zero, new TimerCallback( ResendBombTarget ) ).Start();
}
private void ResendBombTarget()
{
// Make sure they still have the bomb, then give them the target back
if ( m_Bomb != null && !m_Bomb.Deleted && m_Mob != null && !m_Mob.Deleted && m_Mob.Alive )
{
if ( m_Bomb.IsChildOf( m_Mob ) )
m_Mob.Target = new BombTarget( m_Bomb, m_Mob );
}
}
}
private bool OnBombTarget( Mobile from, object obj )
{
if ( m_Game == null )
return true;
if ( !IsChildOf( from.Backpack ) )
return true;
// don't let them throw it to themselves
if ( obj == from )
return false;
if ( !(obj is IPoint3D) )
return false;
Point3D pt = new Point3D( (IPoint3D)obj );
if ( obj is Mobile )
pt.Z += 10;
else if ( obj is Item item )
pt.Z += item.ItemData.CalcHeight + 1;
m_Flying = true;
Visible = false;
m_Thrower = from;
MoveToWorld( GetWorldLocation(), from.Map );
BeginFlight( pt );
return true;
}
private void HitObject( Point3D ballLoc, int objZ, int objHeight )
{
DoAnim( GetWorldLocation(), ballLoc, Map );
MoveToWorld( ballLoc );
m_Path.Clear();
m_PathIdx = 0;
Timer.DelayCall( TimeSpan.FromSeconds( 0.05 ), new TimerCallback( ContinueFlight ) ).Start();
}
private bool CheckCatch( Mobile m, Point3D myLoc )
{
if ( m == null || !m.Alive || !m.Player || m_Game == null )
return false;
if ( m_Game.GetTeamInfo( m ) == null )
return false;
int zdiff = myLoc.Z - m.Z;
if ( zdiff < 0 )
return false;
if ( zdiff < 12 )
return true;
if ( zdiff < 16 )
return Utility.RandomBool(); // 50% chance
return false;
}
private void DoAnim( Point3D start, Point3D end, Map map )
{
Effects.SendMovingEffect( new Entity( Serial.Zero, start, map ), new Entity( Serial.Zero, end, map ),
ItemID, 15, 0, false, false, Hue, 0 );
}
private void DoCatch( Mobile m )
{
m_Flying = false;
Visible = true;
if ( m == null || !m.Alive || !m.Player || m_Game == null )
return;
BRTeamInfo useTeam = m_Game.GetTeamInfo( m );
if ( useTeam == null )
return;
DoAnim( GetWorldLocation(), m.Location, m.Map );
string verb = "caught";
if ( m_Thrower != null && m_Game.GetTeamInfo( m_Thrower ) != useTeam )
verb = "intercepted";
if ( !TakeBomb( m, useTeam, verb ) )
MoveToWorld( m.Location, m.Map );
}
private Point3DList m_Path = new Point3DList();
private int m_PathIdx = 0;
private void BeginFlight( Point3D dest )
{
Point3D org = GetWorldLocation();
org.Z += 10; // always add 10 at the start cause we're coming from a mobile's eye level
/*if ( org.X > dest.X || ( org.X == dest.X && org.Y > dest.Y ) || ( org.X == dest.X && org.Y == dest.Y && org.Z > dest.Z ) )
{
Point3D swap = org;
org = dest;
dest = swap;
}*/
ArrayList list = new ArrayList();
double rise, run, zslp;
double dist3d, dist2d;
double x, y, z;
int xd, yd, zd;
Point3D p;
xd = dest.X - org.X;
yd = dest.Y - org.Y;
zd = dest.Z - org.Z;
dist2d = Math.Sqrt( xd * xd + yd * yd );
if ( zd != 0 )
dist3d = Math.Sqrt( dist2d * dist2d + zd * zd );
else
dist3d = dist2d;
rise = ( (float) yd ) / dist3d;
run = ( (float) xd ) / dist3d;
zslp = ( (float) zd ) / dist3d;
x = (int) org.X;
y = (int) org.Y;
z = (int) org.Z;
while ( Utility.NumberBetween( x, dest.X, org.X, 0.5 ) && Utility.NumberBetween( y, dest.Y, org.Y, 0.5 ) && Utility.NumberBetween( z, dest.Z, org.Z, 0.5 ) )
{
int ix = (int) Math.Round( x );
int iy = (int) Math.Round( y );
int iz = (int) Math.Round( z );
if ( list.Count > 0 )
{
p = (Point3D)list[list.Count-1];
if ( p.X != ix || p.Y != iy || p.Z != iz )
list.Add( new Point3D( ix, iy, iz ) );
}
else
{
list.Add( new Point3D( ix, iy, iz ) );
}
x += run;
y += rise;
z += zslp;
}
if ( list.Count > 0 )
{
if ( ((Point3D)list[list.Count-1]) != dest )
list.Add( dest );
}
/*if ( dist3d > 4 && ( dest.X != org.X || dest.Y != org.Y ) )
{
int count = list.Count;
int i;
int climb = count / 2;
if ( climb > 3 )
climb = 3;
for ( i = 0; i < climb; i++ )
{
p = ((Point3D)list[i]);
p.Z += (i+1) * 4;
list[i] = p;
}
for ( ; i < count - climb; i++ )
{
p = ((Point3D)list[i]);
p.Z += 16;
list[i] = p;
}
for ( i = climb; i > 0; i-- )
{
p = ((Point3D)list[i]);
p.Z += i * 4;
list[i] = p;
}
}*/
if ( dist2d > 1 )
{
int count = list.Count;
double height = count * 2 * ( Utility.RandomDouble() * 0.40 + 0.10 ); // 10 - 50%
double coeff = ( -height / ( (count*count)/4.0 ) );
for ( int i = 0; i < count; i++ )
{
p = ((Point3D)list[i]);
int xp = ( i - (count / 2) );
p.Z += (int)Math.Ceiling( coeff * xp * xp + height );
list[i] = p;
}
}
m_Path.Clear();
for (int i = 0; i < list.Count; i++)
m_Path.Add( (Point3D)list[i] );
m_PathIdx = 0;
ContinueFlight();
}
private void ContinueFlight()
{
int height;
bool found = false;
if ( m_PathIdx < m_Path.Count && Map?.Tiles != null && Map != Map.Internal )
{
int pathCheckEnd = m_PathIdx + 5;
if ( m_Path.Count < pathCheckEnd )
pathCheckEnd = m_Path.Count;
Visible = false;
if ( m_PathIdx > 0 ) // move to the next location
MoveToWorld( m_Path[m_PathIdx - 1] );
Point3D pTop = new Point3D( GetWorldLocation() ), pBottom = new Point3D( m_Path[pathCheckEnd-1] );
Utility.FixPoints( ref pTop, ref pBottom );
for ( int i = m_PathIdx; i < pathCheckEnd; i++ )
{
Point3D point = m_Path[i];
LandTile landTile = Map.Tiles.GetLandTile( point.X, point.Y );
int landZ = 0, landAvg = 0, landTop = 0;
Map.GetAverageZ( point.X, point.Y, ref landZ, ref landAvg, ref landTop );
if ( landZ <= point.Z && landTop >= point.Z && !landTile.Ignored )
{
HitObject( point, landTop, 0 );
return;
}
StaticTile[] statics = Map.Tiles.GetStaticTiles( point.X, point.Y, true );
if ( landTile.ID == 0x244 && statics.Length == 0 ) // 0x244 = invalid land tile
{
bool empty = true;
IPooledEnumerable<Item> eable = Map.GetItemsInRange( point, 0 );
foreach ( Item item in eable )
{
if ( item != this )
{
empty = false;
break;
}
}
eable.Free();
if ( empty )
{
HitObject( point, landTop, 0 );
return;
}
}
for ( int j = 0; j < statics.Length; j++ )
{
StaticTile t = statics[j];
ItemData id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
height = id.CalcHeight;
if ( t.Z <= point.Z && t.Z + height >= point.Z && ( id.Flags & ( TileFlag.Impassable | TileFlag.Wall | TileFlag.NoShoot ) ) != 0 )
{
if ( i > m_PathIdx )
point = m_Path[i-1];
else
point = GetWorldLocation();
HitObject( point, t.Z, height );
return;
}
}
}
Rectangle2D rect = new Rectangle2D( pTop.X, pTop.Y, ( pBottom.X - pTop.X ) + 1, ( pBottom.Y - pTop.Y ) + 1 );
IPooledEnumerable<Item> area = Map.GetItemsInBounds( rect );
foreach ( Item i in area )
{
if ( i == this || i.ItemID >= 0x4000 )
continue;
if ( i is BRGoal )
{
height = 17;
}
else if ( i is Blocker )
{
height = 20;
}
else
{
ItemData id = i.ItemData;
if ( ( id.Flags & (TileFlag.Impassable | TileFlag.Wall | TileFlag.NoShoot)) == 0 )
continue;
height = id.CalcHeight;
}
Point3D point = i.Location;
Point3D loc = i.Location;
for ( int j = m_PathIdx; j < pathCheckEnd; j++ )
{
point = m_Path[j];
if ( loc.X == point.X && loc.Y == point.Y &&
( (i is Blocker ) || ( loc.Z <= point.Z && loc.Z + height >= point.Z ) ) )
{
found = true;
if ( j > m_PathIdx )
point = m_Path[j-1];
else
point = GetWorldLocation();
break;
}
}
if ( !found )
continue;
area.Free();
if ( i is BRGoal goal )
{
Point3D oldLoc = new Point3D( GetWorldLocation() );
if ( CheckScore( goal, m_Thrower, 3 ) )
DoAnim( oldLoc, point, Map );
else
HitObject( point, loc.Z, height );
}
else
{
HitObject( point, loc.Z, height );
}
return;
}
area.Free();
IPooledEnumerable<NetState> clients = Map.GetClientsInBounds( rect );
foreach ( NetState ns in clients)
{
Mobile m = ns.Mobile;
if ( m == null || m == m_Thrower )
continue;
Point3D point;
Point3D loc = m.Location;
for ( int j = m_PathIdx; j < pathCheckEnd && !found; j++ )
{
point = m_Path[j];
if ( loc.X == point.X && loc.Y == point.Y &&
loc.Z <= point.Z && loc.Z + 16 >= point.Z )
{
found = CheckCatch( m, point );
}
}
if ( !found )
continue;
clients.Free();
// TODO: probably need to change this a lot...
DoCatch( m );
return;
}
clients.Free();
m_PathIdx = pathCheckEnd;
if ( m_PathIdx > 0 && m_PathIdx - 1 < m_Path.Count )
DoAnim( GetWorldLocation(), m_Path[m_PathIdx - 1], Map );
Timer.DelayCall( TimeSpan.FromSeconds( 0.1 ), new TimerCallback( ContinueFlight ) ).Start();
}
else
{
if ( m_PathIdx > 0 && m_PathIdx - 1 < m_Path.Count )
MoveToWorld( m_Path[m_PathIdx - 1] );
else if ( m_Path.Count > 0 )
MoveToWorld( m_Path.Last );
int myZ = Map.GetAverageZ( X, Y );
StaticTile[] statics = Map.Tiles.GetStaticTiles( X, Y, true );
for ( int j = 0; j < statics.Length; j++ )
{
StaticTile t = statics[j];
ItemData id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
height = id.CalcHeight;
if ( t.Z + height > myZ && t.Z + height <= Z )
myZ = t.Z + height;
}
IPooledEnumerable<Item> eable = GetItemsInRange( 0 );
foreach ( Item item in eable )
{
if ( item.Visible && item != this )
{
height = item.ItemData.CalcHeight;
if ( item.Z + height > myZ && item.Z + height <= Z )
myZ = item.Z + height;
}
}
eable.Free();
Z = myZ;
m_Flying = false;
Visible = true;
m_Path.Clear();
m_PathIdx = 0;
}
}
public Mobile Thrower => m_Thrower;
public bool CheckScore( BRGoal goal, Mobile m, int points )
{
if ( m_Game == null || m == null || goal == null )
return false;
BRTeamInfo team = m_Game.GetTeamInfo( m );
if ( team == null || goal.Team == null || team == goal.Team )
return false;
if ( points > 3 )
m_Game.Alert( "Touchdown {0} ({1})!", team.Name, m.Name );
else
m_Game.Alert( "Field goal {0} ({1})!", team.Name, m.Name );
for (int i = m_Helpers.Count - 1; i >= 0; i--)
{
Mobile mob = (Mobile)m_Helpers[i];
BRPlayerInfo pi = team[mob];
if ( pi != null )
{
if ( mob == m )
pi.Captures += points;
pi.Score += points + 1;
points /= 2;
}
}
m_Game.ReturnBomb();
m_Flying = false;
Visible = true;
m_Path.Clear();
m_PathIdx = 0;
Target.Cancel( m );
return true;
}
private bool TakeBomb( Mobile m, BRTeamInfo team, string verb )
{
if ( !m.Player || !m.Alive || m.NetState == null )
return false;
if ( m.PlaceInBackpack( this ) )
{
m.RevealingAction();
m.LocalOverheadMessage( MessageType.Regular, 0x59, false, "You got the bomb!" );
m_Game.Alert( "{1} ({2}) {0} the bomb!", verb, m.Name, team.Name );
m.Target = new BombTarget( this, m );
if ( m_Helpers.Contains( m ) )
m_Helpers.Remove( m );
if ( m_Helpers.Count > 0 )
{
Mobile last = (Mobile)m_Helpers[0];
if ( m_Game.GetTeamInfo( last ) != team )
m_Helpers.Clear();
}
m_Helpers.Add( m );
return true;
}
else
{
return false;
}
}
}
public class BRGoal : BaseAddon
{
private bool m_North = false;
[CommandProperty( AccessLevel.GameMaster )]
public bool North { get => m_North;
set { m_North = value; Remake(); } }
private static AddonComponent SetHue( AddonComponent ac, int hue )
{
ac.Hue = hue;
return ac;
}
private void Remake()
{
foreach ( AddonComponent ac in Components )
{
ac.Addon = null;
ac.Delete();
}
Components.Clear();
// stairs
AddComponent( new AddonComponent( 0x74D ), -1, +1, -5 );
AddComponent( new AddonComponent( 0x71F ), 0, +1, -5 );
AddComponent( new AddonComponent( 0x74B ), +1, +1, -5 );
AddComponent( new AddonComponent( 0x736 ), +1, 0, -5 );
AddComponent( new AddonComponent( 0x74C ), +1, -1, -5 );
AddComponent( new AddonComponent( 0x737 ), 0, -1, -5 );
AddComponent( new AddonComponent( 0x74A ), -1, -1, -5 );
AddComponent( new AddonComponent( 0x749 ), -1, 0, -5 );
// Center Sparkle
AddComponent( new AddonComponent( 0x375A ), 0, 0, -1 );
if ( !m_North )
{
// Pillars
AddComponent( new AddonComponent( 0x0CE ), 0, +1, -2 );
AddComponent( new AddonComponent( 0x0CC ), 0, -1, -2 );
AddComponent( new AddonComponent( 0x0D0 ), 0, 0, -2 );
// Yellow parts
AddComponent( SetHue( new AddonComponent( 0x0DF ), 0x499 ), 0, +1, 7 );
AddComponent( SetHue( new AddonComponent( 0x0DF ), 0x499 ), 0, 0, 16 );
AddComponent( SetHue( new AddonComponent( 0x0DF ), 0x499 ), 0, -1, 7 );
// Blue Sparkles
AddComponent( SetHue( new AddonComponent( 0x377A ), 0x84C ), 0, +1, 12 );
AddComponent( SetHue( new AddonComponent( 0x377A ), 0x84C ), 0, +1, -1 );
AddComponent( SetHue( new AddonComponent( 0x377A ), 0x84C ), 0, -1, 12 );
AddComponent( SetHue( new AddonComponent( 0x377A ), 0x84C ), 0, -1, -1 );
}
else
{
// Pillars
AddComponent( new AddonComponent( 0x0CF ), +1, 0, -2 );
AddComponent( new AddonComponent( 0x0CC ), -1, 0, -2 );
AddComponent( new AddonComponent( 0x0D1 ), 0, 0, -2 );
// Yellow parts
AddComponent( SetHue( new AddonComponent( 0x0DF ), 0x499 ), +1, 0, 7 );
AddComponent( SetHue( new AddonComponent( 0x0DF ), 0x499 ), 0, 0, 16 );
AddComponent( SetHue( new AddonComponent( 0x0DF ), 0x499 ), -1, 0, 7 );
// Blue Sparkles
AddComponent( SetHue( new AddonComponent( 0x377A ), 0x84C ), +1, 0, 12 );
AddComponent( SetHue( new AddonComponent( 0x377A ), 0x84C ), +1, 0, -1 );
AddComponent( SetHue( new AddonComponent( 0x377A ), 0x84C ), -1, 0, 12 );
AddComponent( SetHue( new AddonComponent( 0x377A ), 0x84C ), -1, 0, -1 );
}
}
public override string DefaultName => "Bombing Run Goal";
[Constructible]
public BRGoal()
{
ItemID = 0x51D;
Hue = 0x84C;
Visible = true;
Remake();
}
public BRGoal( Serial serial ) : base( serial )
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize (writer);
writer.Write( (int)1 ); // version
writer.Write( m_North );
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize (reader);
int version = reader.ReadInt();
switch ( version )
{
case 1:
{
m_North = reader.ReadBool();
goto case 0;
}
case 0:
{
break;
}
}
Hue = 0x84C;
}
public override bool ShareHue => false;
public BRTeamInfo Team
{
get => m_Team;
set
{
m_Team = value;
if ( m_Team != null && m_Team.Color != 0 )
Hue = m_Team.Color;
else
Hue = 0x84C;
}
}
private BRTeamInfo m_Team;
public override bool OnMoveOver( Mobile m )
{
if ( !Visible )
return true;
if ( m == null || !m.Player || !m.Alive || m.Backpack == null || m_Team?.Game == null )
return true;
if ( !base.OnMoveOver( m ) )
return false;
if ( m_Team != null && m_Team.Color != 0 )
Hue = m_Team.Color;
else
Hue = 0x84C;
if ( m.Backpack.FindItemByType( typeof( BRBomb ), true ) is BRBomb b )
b.CheckScore( this, m, 7 );
return true;
}
}
public sealed class BRBoard : Item
{
public override string DefaultName => "Scoreboard";
public BRTeamInfo m_TeamInfo;
[Constructible]
public BRBoard()
: base( 7774 )
{
Movable = false;
}
public override void OnDoubleClick( Mobile from )
{
if ( m_TeamInfo?.Game != null )
{
from.CloseGump( typeof( BRBoardGump ) );
from.SendGump( new BRBoardGump( from, m_TeamInfo.Game ) );
}
}
public BRBoard( 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 class BRBoardGump : Gump
{
public string Center( string text )
{
return $"<CENTER>{text}</CENTER>";
}
public string Color( string text, int color )
{
return $"<BASEFONT COLOR=#{color:X6}>{text}</BASEFONT>";
}
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 );
}
private const int LabelColor32 = 0xFFFFFF;
private const int BlackColor32 = 0x000000;
private BRGame m_Game;
public BRBoardGump( Mobile mob, BRGame game )
: this( mob, game, null )
{
}
public BRBoardGump( Mobile mob, BRGame game, BRTeamInfo section )
: base( 60, 60 )
{
m_Game = game;
BRTeamInfo ourTeam = game.GetTeamInfo( mob );
ArrayList entries = new ArrayList();
if ( section == null )
{
for ( int i = 0; i < game.Context.Participants.Count; ++i )
{
BRTeamInfo teamInfo = game.Controller.TeamInfo[i % game.Controller.TeamInfo.Length];
if ( teamInfo == null )
continue;
entries.Add( teamInfo );
}
}
else
{
foreach ( BRPlayerInfo player in section.Players.Values )
{
if ( player.Score > 0 )
entries.Add( player );
}
}
entries.Sort();
/*
delegate( IRankedCTF a, IRankedCTF b )
{
return b.Score - a.Score;
} );*/
int height = 0;
if ( section == null )
height = 73 + ( entries.Count * 75 ) + 28;
Closable = false;
AddPage( 0 );
AddBackground( 1, 1, 398, height, 3600 );
AddImageTiled( 16, 15, 369, height - 29, 3604 );
for ( int i = 0; i < entries.Count; i += 1 )
AddImageTiled( 22, 58 + ( i * 75 ), 357, 70, 0x2430 );
AddAlphaRegion( 16, 15, 369, height - 29 );
AddImage( 215, -45, 0xEE40 );
//AddImage( 330, 141, 0x8BA );
AddBorderedText( 22, 22, 294, 20, Center( "BR Scoreboard" ), LabelColor32, BlackColor32 );
AddImageTiled( 32, 50, 264, 1, 9107 );
AddImageTiled( 42, 52, 264, 1, 9157 );
if ( section == null )
{
for ( int i = 0; i < entries.Count; ++i )
{
BRTeamInfo teamInfo = entries[i] as BRTeamInfo;
AddImage( 30, 70 + ( i * 75 ), 10152 );
AddImage( 30, 85 + ( i * 75 ), 10151 );
AddImage( 30, 100 + ( i * 75 ), 10151 );
AddImage( 30, 106 + ( i * 75 ), 10154 );
AddImage( 24, 60 + ( i * 75 ), teamInfo == ourTeam ? 9730 : 9727, teamInfo.Color - 1 );
int nameColor = LabelColor32;
int borderColor = BlackColor32;
switch ( teamInfo.Color )
{
case 0x47E:
nameColor = 0xFFFFFF;
break;
case 0x4F2:
nameColor = 0x3399FF;
break;
case 0x4F7:
nameColor = 0x33FF33;
break;
case 0x4FC:
nameColor = 0xFF00FF;
break;
case 0x021:
nameColor = 0xFF3333;
break;
case 0x01A:
nameColor = 0xFF66FF;
break;
case 0x455:
nameColor = 0x333333;
borderColor = 0xFFFFFF;
break;
}
AddBorderedText( 60, 65 + ( i * 75 ), 250, 20, $"{LadderGump.Rank(1 + i)}: {teamInfo.Name}", nameColor, borderColor );
AddBorderedText( 50 + 10, 85 + ( i * 75 ), 100, 20, "Score:", 0xFFC000, BlackColor32 );
AddBorderedText( 50 + 15, 105 + ( i * 75 ), 100, 20, teamInfo.Score.ToString( "N0" ), 0xFFC000, BlackColor32 );
AddBorderedText( 110 + 10, 85 + ( i * 75 ), 100, 20, "Kills:", 0xFFC000, BlackColor32 );
AddBorderedText( 110 + 15, 105 + ( i * 75 ), 100, 20, teamInfo.Kills.ToString( "N0" ), 0xFFC000, BlackColor32 );
AddBorderedText( 160 + 10, 85 + ( i * 75 ), 100, 20, "Points:", 0xFFC000, BlackColor32 );
AddBorderedText( 160 + 15, 105 + ( i * 75 ), 100, 20, teamInfo.Captures.ToString( "N0" ), 0xFFC000, BlackColor32 );
BRPlayerInfo pl = teamInfo.Leader;
AddBorderedText( 235 + 10, 85 + ( i * 75 ), 250, 20, "Leader:", 0xFFC000, BlackColor32 );
if ( pl != null )
AddBorderedText( 235 + 15, 105 + ( i * 75 ), 250, 20, pl.Player.Name, 0xFFC000, BlackColor32 );
}
}
else
{
}
AddButton( 314, height - 42, 247, 248, 1, GumpButtonType.Reply, 0 );
}
}
public sealed class BRPlayerInfo : IRankedCTF, IComparable
{
private BRTeamInfo m_TeamInfo;
private Mobile m_Player;
private int m_Kills;
private int m_Captures;
private int m_Score;
public Mobile Player => m_Player;
public int CompareTo( object obj )
{
BRPlayerInfo pi = (BRPlayerInfo)obj;
int res = pi.Captures.CompareTo( Captures );
if ( res == 0 )
{
res = pi.Score.CompareTo( Score );
if ( res == 0 )
res = pi.Kills.CompareTo( Kills );
}
return res;
}
public string Name => m_Player.Name;
public int Kills
{
get => m_Kills;
set
{
m_TeamInfo.Kills += ( value - m_Kills );
m_Kills = value;
}
}
public int Captures
{
get => m_Captures;
set
{
m_TeamInfo.Captures += ( value - m_Captures );
m_Captures = value;
}
}
public int Score
{
get => m_Score;
set
{
m_TeamInfo.Score += ( value - m_Score );
m_Score = value;
if ( m_TeamInfo.Leader == null || m_Score > m_TeamInfo.Leader.Score )
m_TeamInfo.Leader = this;
}
}
public BRPlayerInfo( BRTeamInfo teamInfo, Mobile player )
{
m_TeamInfo = teamInfo;
m_Player = player;
}
}
[PropertyObject]
public sealed class BRTeamInfo : IRankedCTF, IComparable
{
private BRGame m_Game;
private int m_TeamID;
private int m_Color;
private string m_Name;
private BRBoard m_Board;
private BRGoal m_Goal;
private int m_Kills;
private int m_Captures;
private int m_Score;
private Hashtable m_Players;
public int CompareTo( object obj )
{
BRTeamInfo ti = (BRTeamInfo)obj;
int res = ti.Captures.CompareTo( Captures );
if ( res == 0 )
{
res = ti.Score.CompareTo( Score );
if ( res == 0 )
res = ti.Kills.CompareTo( Kills );
}
return res;
}
public string Name => $"{m_Name} Team";
public BRGame Game { get => m_Game;
set => m_Game = value;
}
public int TeamID => m_TeamID;
public int Kills { get => m_Kills;
set => m_Kills = value;
}
public int Captures { get => m_Captures;
set => m_Captures = value;
}
public int Score { get => m_Score;
set => m_Score = value;
}
private BRPlayerInfo m_Leader;
public BRPlayerInfo Leader
{
get => m_Leader;
set => m_Leader = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public BRBoard Board
{
get => m_Board;
set => m_Board = value;
}
public Hashtable Players => m_Players;
public BRPlayerInfo this[Mobile mob]
{
get
{
if ( mob == null )
return null;
if ( !(m_Players[mob] is BRPlayerInfo val) )
m_Players[mob] = val = new BRPlayerInfo( this, mob );
return val;
}
}
[CommandProperty( AccessLevel.GameMaster )]
public int Color
{
get => m_Color;
set => m_Color = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public string TeamName
{
get => m_Name;
set => m_Name = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public BRGoal Goal
{
get => m_Goal;
set
{
m_Goal = value;
if ( m_Goal != null )
m_Goal.Team = this;
}
}
public BRTeamInfo( int teamID )
{
m_TeamID = teamID;
m_Players = new Hashtable();
}
public void Reset()
{
m_Kills = 0;
m_Captures = 0;
m_Score = 0;
m_Leader = null;
m_Players.Clear();
if ( m_Board != null )
m_Board.m_TeamInfo = this;
if ( m_Goal != null )
m_Goal.Team = this;
}
public BRTeamInfo( int teamID, GenericReader ip )
{
m_TeamID = teamID;
m_Players = new Hashtable();
int version = ip.ReadEncodedInt();
switch ( version )
{
case 0:
{
m_Board = ip.ReadItem() as BRBoard;
m_Name = ip.ReadString();
m_Color = ip.ReadEncodedInt();
m_Goal = ip.ReadItem() as BRGoal;
break;
}
}
}
public void Serialize( GenericWriter op )
{
op.WriteEncodedInt( 0 ); // version
op.Write( m_Board );
op.Write( m_Name );
op.WriteEncodedInt( m_Color );
op.Write( m_Goal );
}
public override string ToString()
{
if ( m_Name != null )
return $"({Name}) ...";
else
return "...";
}
}
public sealed class BRController : EventController
{
private BRTeamInfo[] m_TeamInfo;
private TimeSpan m_Duration;
private Point3D m_BombHome;
public BRTeamInfo[] TeamInfo => m_TeamInfo;
[CommandProperty( AccessLevel.GameMaster )]
public BRTeamInfo Team1 { get => m_TeamInfo[0];
set { } }
[CommandProperty( AccessLevel.GameMaster )]
public BRTeamInfo Team2 { get => m_TeamInfo[1];
set { } }
[CommandProperty( AccessLevel.GameMaster )]
public BRTeamInfo Team3 { get => m_TeamInfo[2];
set { } }
[CommandProperty( AccessLevel.GameMaster )]
public BRTeamInfo Team4 { get => m_TeamInfo[3];
set { } }
[CommandProperty( AccessLevel.GameMaster )]
public TimeSpan Duration
{
get => m_Duration;
set => m_Duration = value;
}
[CommandProperty( AccessLevel.GameMaster )]
public Point3D BombHome
{
get => m_BombHome;
set => m_BombHome = value;
}
public override string Title => "Bombing Run";
public override string DefaultName => "Bombing Run Controller";
public override string GetTeamName( int teamID )
{
return m_TeamInfo[teamID % m_TeamInfo.Length].Name;
}
public override EventGame Construct( DuelContext context )
{
return new BRGame( this, context );
}
[Constructible]
public BRController()
{
Visible = false;
Movable = false;
m_Duration = TimeSpan.FromMinutes( 30.0 );
m_BombHome = Point3D.Zero;
m_TeamInfo = new BRTeamInfo[4];
for ( int i = 0; i < m_TeamInfo.Length; ++i )
m_TeamInfo[i] = new BRTeamInfo( i );
}
public BRController( Serial serial )
: base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 0 );
writer.Write( m_BombHome );
writer.Write( m_Duration );
writer.WriteEncodedInt( m_TeamInfo.Length );
for ( int i = 0; i < m_TeamInfo.Length; ++i )
m_TeamInfo[i].Serialize( writer );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
switch ( version )
{
case 0:
{
m_BombHome = reader.ReadPoint3D();
m_Duration = reader.ReadTimeSpan();
m_TeamInfo = new BRTeamInfo[reader.ReadEncodedInt()];
for ( int i = 0; i < m_TeamInfo.Length; ++i )
m_TeamInfo[i] = new BRTeamInfo( i, reader );
break;
}
}
}
}
public sealed class BRGame : EventGame
{
public override bool CantDoAnything( Mobile mob )
{
if ( mob?.Backpack == null || GetTeamInfo( mob ) == null )
return false;
Item bomb = mob.Backpack.FindItemByType( typeof( BRBomb ), true );
if ( bomb != null )
return true;
return false;
}
private BRBomb m_Bomb;
private TimerCallback m_UnhideCallback = null;
public void ReturnBomb()
{
if ( m_Bomb != null && m_Controller != null )
{
if ( m_UnhideCallback == null )
m_UnhideCallback = new TimerCallback( UnhideBomb );
m_Bomb.Visible = false;
m_Bomb.MoveToWorld( m_Controller.BombHome, m_Controller.Map );
Timer.DelayCall( TimeSpan.FromSeconds( Utility.RandomMinMax( 5, 15 ) ), m_UnhideCallback );
}
}
private void UnhideBomb()
{
if ( m_Bomb != null )
{
m_Bomb.Visible = true;
m_Bomb.OnLocationChange( m_Bomb.Location );
}
}
private BRController m_Controller;
public BRController Controller => m_Controller;
public void Alert( string text )
{
if ( m_Context.m_Tournament != null )
m_Context.m_Tournament.Alert( text );
for ( int i = 0; i < m_Context.Participants.Count; ++i )
{
Participant p = m_Context.Participants[i] as Participant;
for ( int j = 0; j < p.Players.Length; ++j )
{
if ( p.Players[j] != null )
p.Players[j].Mobile.SendMessage( 0x35, text );
}
}
}
public void Alert( string format, params object[] args )
{
Alert( string.Format( format, args ) );
}
public BRGame( BRController controller, DuelContext context ) : base( context )
{
m_Controller = controller;
}
public Map Facet
{
get
{
if ( m_Context.Arena != null )
return m_Context.Arena.Facet;
return m_Controller.Map;
}
}
public BRTeamInfo GetTeamInfo( Mobile mob )
{
int teamID = GetTeamID( mob );
if ( teamID >= 0 )
return m_Controller.TeamInfo[teamID % m_Controller.TeamInfo.Length];
return null;
}
public int GetTeamID( Mobile mob )
{
if ( !(mob is PlayerMobile pm) )
return mob is BaseCreature creature ? creature.Team - 1 : -1;
if ( pm.DuelContext == null || pm.DuelContext != m_Context )
return -1;
if ( pm.DuelPlayer == null || pm.DuelPlayer.Eliminated )
return -1;
return pm.DuelContext.Participants.IndexOf( pm.DuelPlayer.Participant );
}
public int GetColor( Mobile mob )
{
return GetTeamInfo( mob )?.Color ?? -1;
}
private void ApplyHues( Participant p, int hueOverride )
{
for ( int i = 0; i < p.Players.Length; ++i )
{
if ( p.Players[i] != null )
p.Players[i].Mobile.SolidHueOverride = hueOverride;
}
}
public void DelayBounce( TimeSpan ts, Mobile mob, Container corpse )
{
Timer.DelayCall( ts, new TimerStateCallback( DelayBounce_Callback ), new object[] { mob, corpse } );
}
private void DelayBounce_Callback( object state )
{
object[] states = (object[]) state;
Mobile mob = (Mobile) states[0];
Container corpse = (Container) states[1];
DuelPlayer dp = null;
if ( mob is PlayerMobile mobile )
dp = mobile.DuelPlayer;
m_Context.RemoveAggressions( mob );
if ( dp != null && !dp.Eliminated )
mob.MoveToWorld( m_Context.Arena.GetBaseStartPoint( GetTeamID( mob ) ), Facet );
else
m_Context.SendOutside( mob );
m_Context.Refresh( mob, corpse );
DuelContext.Debuff( mob );
DuelContext.CancelSpell( mob );
mob.Frozen = false;
}
public override bool OnDeath( Mobile mob, Container corpse )
{
Mobile killer = mob.FindMostRecentDamager( false );
bool hadBomb = false;
Item[] bombs = corpse.FindItemsByType( typeof( BRBomb ), false );
for ( int i = 0; i < bombs.Length; ++i )
( bombs[i] as BRBomb ).DropTo( mob, killer );
hadBomb = ( hadBomb || bombs.Length > 0 );
if ( mob.Backpack != null )
{
bombs = mob.Backpack.FindItemsByType( typeof( BRBomb ), false );
for ( int i = 0; i < bombs.Length; ++i )
( bombs[i] as BRBomb ).DropTo( mob, killer );
hadBomb = ( hadBomb || bombs.Length > 0 );
}
if ( killer != null && killer.Player )
{
BRTeamInfo teamInfo = GetTeamInfo( killer );
BRTeamInfo victInfo = GetTeamInfo( mob );
if ( teamInfo != null && teamInfo != victInfo )
{
BRPlayerInfo playerInfo = teamInfo[killer];
if ( playerInfo != null )
{
playerInfo.Kills += 1;
playerInfo.Score += 1; // base frag
if ( hadBomb )
playerInfo.Score += 4; // fragged bomb carrier
}
}
}
mob.CloseGump( typeof( BRBoardGump ) );
mob.SendGump( new BRBoardGump( mob, this ) );
m_Context.Requip( mob, corpse );
DelayBounce( TimeSpan.FromSeconds( 30.0 ), mob, corpse );
return false;
}
private Timer m_FinishTimer;
public override void OnStart()
{
for ( int i = 0; i < m_Controller.TeamInfo.Length; ++i )
{
BRTeamInfo teamInfo = m_Controller.TeamInfo[i];
teamInfo.Game = this;
teamInfo.Reset();
}
for ( int i = 0; i < m_Context.Participants.Count; ++i )
ApplyHues( m_Context.Participants[i] as Participant, m_Controller.TeamInfo[i % m_Controller.TeamInfo.Length].Color );
if ( m_FinishTimer != null )
m_FinishTimer.Stop();
m_Bomb = new BRBomb( this );
ReturnBomb();
m_FinishTimer = Timer.DelayCall( m_Controller.Duration, new TimerCallback( Finish_Callback ) );
}
private void Finish_Callback()
{
ArrayList teams = new ArrayList();
for ( int i = 0; i < m_Context.Participants.Count; ++i )
{
BRTeamInfo teamInfo = m_Controller.TeamInfo[i % m_Controller.TeamInfo.Length];
if ( teamInfo == null )
continue;
teams.Add( teamInfo );
}
teams.Sort();
Tournament tourny = m_Context.m_Tournament;
StringBuilder sb = new StringBuilder();
if ( tourny != null && tourny.TournyType == TournyType.FreeForAll )
{
sb.Append( m_Context.Participants.Count * tourny.PlayersPerParticipant );
sb.Append( "-man FFA" );
}
else if ( tourny != null && tourny.TournyType == TournyType.RandomTeam )
{
sb.Append( tourny.ParticipantsPerMatch );
sb.Append( "-team" );
}
else if ( tourny != null && tourny.TournyType == TournyType.RedVsBlue )
{
sb.Append( "Red v Blue" );
}
else if ( tourny != null && tourny.TournyType == TournyType.Faction )
{
sb.Append( tourny.ParticipantsPerMatch );
sb.Append( "-team Faction" );
}
else if ( tourny != null )
{
for ( int i = 0; i < tourny.ParticipantsPerMatch; ++i )
{
if ( sb.Length > 0 )
sb.Append( 'v' );
sb.Append( tourny.PlayersPerParticipant );
}
}
if ( m_Controller != null )
sb.Append( ' ' ).Append( m_Controller.Title );
string title = sb.ToString();
BRTeamInfo winner = (BRTeamInfo)( teams.Count > 0 ? teams[0] : null );
for ( int i = 0; i < teams.Count; ++i )
{
TrophyRank rank = TrophyRank.Bronze;
if ( i == 0 )
rank = TrophyRank.Gold;
else if ( i == 1 )
rank = TrophyRank.Silver;
BRPlayerInfo leader = ((BRTeamInfo)teams[i]).Leader;
foreach ( BRPlayerInfo pl in ((BRTeamInfo)teams[i]).Players.Values )
{
Mobile mob = pl.Player;
if ( mob == null )
continue;
sb = new StringBuilder();
sb.Append( title );
if ( pl == leader )
sb.Append( " Leader" );
if ( pl.Score > 0 )
{
sb.Append( ": " );
//sb.Append( pl.Score.ToString( "N0" ) );
//sb.Append( pl.Score == 1 ? " point" : " points" );
sb.Append( pl.Kills.ToString( "N0" ) );
sb.Append( pl.Kills == 1 ? " kill" : " kills" );
if ( pl.Captures > 0 )
{
sb.Append( ", " );
sb.Append( pl.Captures.ToString( "N0" ) );
sb.Append( pl.Captures == 1 ? " point" : " points" );
}
}
Item item = new Trophy( sb.ToString(), rank );
if ( pl == leader )
item.ItemID = 4810;
item.Name = $"{item.Name}, {((BRTeamInfo) teams[i]).Name.ToLower()} team";
if ( !mob.PlaceInBackpack( item ) )
mob.BankBox.DropItem( item );
int cash = pl.Score * 250;
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() );
}
}
}
for ( int i = 0; i < m_Context.Participants.Count; ++i )
{
if ( !(m_Context.Participants[i] is Participant p) || p.Players == null )
continue;
for ( int j = 0; j < p.Players.Length; ++j )
{
DuelPlayer dp = p.Players[j];
if ( dp?.Mobile != null )
{
dp.Mobile.CloseGump( typeof( BRBoardGump ) );
dp.Mobile.SendGump( new BRBoardGump( dp.Mobile, this ) );
}
}
if ( i == winner.TeamID )
continue;
if ( p.Players != null )
{
for ( int j = 0; j < p.Players.Length; ++j )
{
if ( p.Players[j] != null )
p.Players[j].Eliminated = true;
}
}
}
m_Context.Finish( m_Context.Participants[winner.TeamID] as Participant );
}
public override void OnStop()
{
for ( int i = 0; i < m_Controller.TeamInfo.Length; ++i )
{
BRTeamInfo teamInfo = m_Controller.TeamInfo[i];
if ( teamInfo.Board != null )
teamInfo.Board.m_TeamInfo = null;
teamInfo.Game = null;
}
ReturnBomb();
m_Bomb?.Delete();
for ( int i = 0; i < m_Context.Participants.Count; ++i )
ApplyHues( m_Context.Participants[i] as Participant, -1 );
m_FinishTimer?.Stop();
m_FinishTimer = null;
}
}
}