runuo/Scripts/Items/Misc/PowerGenerator.cs
Mark Sturgill d2c670f5c3 Implement Core.TickCount
Convert movement, actions (lift/use), combat, and spells to use Core.TickCount and avoid DateTime caveats (performance, system time dependency, etc)
Refactor DateTime.Now to DateTime.UtcNow
Add LOS check for Iron Maiden addon
2013-10-06 03:21:18 -07:00

571 lines
No EOL
13 KiB
C#

using System;
using System.Collections;
using Server;
using Server.Gumps;
using Server.Network;
namespace Server.Items
{
public class PowerGenerator : BaseAddon
{
[Constructable]
public PowerGenerator() : this( Utility.RandomMinMax( 3, 6 ) )
{
}
[Constructable]
public PowerGenerator( int sideLength )
{
AddGeneratorComponent( 0x4FA1, 0, 0, 0 );
AddGeneratorComponent( 0x76, -1, 0, 0 );
AddGeneratorComponent( 0x75, 0, -1, 0 );
AddGeneratorComponent( 0x37F4, 0, 0, 13 );
AddComponent( new ControlPanel( sideLength ), 1, 0, -2 );
}
public override bool ShareHue{ get{ return false; } }
private void AddGeneratorComponent( int itemID, int x, int y, int z )
{
AddonComponent component = new AddonComponent( itemID );
component.Name = "a power generator";
component.Hue = 0x451;
AddComponent( component, x, y, z );
}
public PowerGenerator( Serial serial ) : base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.WriteEncodedInt( (int) 0 ); // version
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadEncodedInt();
}
}
public class ControlPanel : AddonComponent
{
private static readonly TimeSpan m_UseTimeout = TimeSpan.FromMinutes( 2.0 );
public struct Node
{
private int m_X;
private int m_Y;
public int X{ get{ return m_X; } set{ m_X = value; } }
public int Y{ get{ return m_Y; } set{ m_Y = value; } }
public Node( int x, int y )
{
m_X = x;
m_Y = y;
}
}
private int m_SideLength;
private Node[] m_Path;
[CommandProperty( AccessLevel.GameMaster )]
public int SideLength
{
get{ return m_SideLength; }
set
{
if ( value < 3 )
value = 3;
else if ( value > 6 )
value = 6;
if ( m_SideLength != value )
{
m_SideLength = value;
InitPath();
}
}
}
public Node[] Path{ get{ return m_Path; } }
public override string DefaultName
{
get { return "a control panel"; }
}
public ControlPanel( int sideLength ) : base( 0xBDC )
{
Hue = 0x835;
SideLength = sideLength;
}
private enum PathDirection
{
Left,
Up,
Right,
Down
}
public void InitPath()
{
// Depth-First Search algorithm
int totalNodes = SideLength * SideLength;
Node[] stack = new Node[totalNodes];
Node current = stack[0] = new Node( 0, 0 );
int stackSize = 1;
bool[,] visited = new bool[SideLength, SideLength];
visited[0, 0] = true;
while ( true )
{
PathDirection[] choices = new PathDirection[4];
int count = 0;
if ( current.X > 0 && !visited[current.X - 1, current.Y] )
choices[count++] = PathDirection.Left;
if ( current.Y > 0 && !visited[current.X, current.Y - 1] )
choices[count++] = PathDirection.Up;
if ( current.X < SideLength - 1 && !visited[current.X + 1, current.Y] )
choices[count++] = PathDirection.Right;
if ( current.Y < SideLength - 1 && !visited[current.X, current.Y + 1] )
choices[count++] = PathDirection.Down;
if ( count > 0 )
{
PathDirection dir = choices[Utility.Random( count )];
switch ( dir )
{
case PathDirection.Left:
current = new Node( current.X - 1, current.Y );
break;
case PathDirection.Up:
current = new Node( current.X, current.Y - 1 );
break;
case PathDirection.Right:
current = new Node( current.X + 1, current.Y );
break;
default:
current = new Node( current.X, current.Y + 1 );
break;
}
stack[stackSize++] = current;
if ( current.X == SideLength - 1 && current.Y == SideLength - 1 )
break;
visited[current.X, current.Y] = true;
}
else
{
current = stack[--stackSize - 1];
}
}
m_Path = new Node[stackSize];
for ( int i = 0; i < stackSize; i++ )
{
m_Path[i] = stack[i];
}
if ( m_User != null )
{
m_User.CloseGump( typeof( GameGump ) );
m_User = null;
}
}
private Mobile m_User;
private DateTime m_LastUse;
public override void OnDoubleClick( Mobile from )
{
if ( !from.InRange( this, 3 ) )
{
from.SendLocalizedMessage( 500446 ); // That is too far away.
return;
}
if ( m_User != null )
{
if ( m_User == from )
return;
if ( m_User.Deleted || m_User.Map != Map || !m_User.InRange( this, 3 )
|| m_User.NetState == null || DateTime.UtcNow - m_LastUse >= m_UseTimeout )
{
m_User.CloseGump( typeof( GameGump ) );
}
else
{
from.SendMessage( "Someone is currently using the control panel." );
return;
}
}
m_User = from;
m_LastUse = DateTime.UtcNow;
from.SendGump( new GameGump( this, from, 0, false ) );
}
private class GameGump : Gump
{
private enum NodeHue
{
Gray,
Blue,
Red
}
private ControlPanel m_Panel;
private Mobile m_From;
private int m_Step;
public GameGump( ControlPanel panel, Mobile from, int step, bool hint ) : base( 5, 30 )
{
m_Panel = panel;
m_From = from;
m_Step = step;
int sideLength = panel.SideLength;
AddBackground( 50, 0, 530, 410, 0xA28 );
AddImage( 0, 0, 0x28C8 );
AddImage( 547, 0, 0x28C9 );
AddBackground( 95, 20, 442, 90, 0xA28 );
AddHtml( 229, 35, 300, 45, "GENERATOR CONTROL PANEL", false, false );
AddHtml( 223, 60, 300, 70, "Use the Directional Controls to", false, false );
AddHtml( 253, 75, 300, 85, "Close the Grid Circuit", false, false );
AddImage( 140, 40, 0x28D3 );
AddImage( 420, 40, 0x28D3 );
AddBackground( 365, 120, 178, 210, 0x1400 );
AddImage( 365, 115, 0x28D4 );
AddImage( 365, 288, 0x28D4 );
AddImage( 414, 189, 0x589 );
AddImage( 435, 210, 0xA52 );
AddButton( 408, 222, 0x29EA, 0x29EC, 1, GumpButtonType.Reply, 0 ); // Left
AddButton( 448, 185, 0x29CC, 0x29CE, 2, GumpButtonType.Reply, 0 ); // Up
AddButton( 473, 222, 0x29D6, 0x29D8, 3, GumpButtonType.Reply, 0 ); // Right
AddButton( 448, 243, 0x29E0, 0x29E2, 4, GumpButtonType.Reply, 0 ); // Down
AddBackground( 90, 115, 30 + 40 * sideLength, 30 + 40 * sideLength, 0xA28 );
AddBackground( 100, 125, 10 + 40 * sideLength, 10 + 40 * sideLength, 0x1400 );
for ( int i = 0; i < sideLength; i++ )
{
for ( int j = 0; j < sideLength - 1; j++ )
{
AddImage( 120 + 40 * i, 162 + 40 * j, 0x13F9 );
}
}
for ( int i = 0; i < sideLength - 1; i++ )
{
for ( int j = 0; j < sideLength; j++ )
{
AddImage( 138 + 40 * i, 147 + 40 * j, 0x13FD );
}
}
Node[] path = panel.Path;
NodeHue[,] hues = new NodeHue[sideLength, sideLength];
for ( int i = 0; i <= step; i++ )
{
Node n = path[i];
hues[n.X, n.Y] = NodeHue.Blue;
}
Node lastNode = path[path.Length - 1];
hues[lastNode.X, lastNode.Y] = NodeHue.Red;
for ( int i = 0; i < sideLength; i++ )
{
for ( int j = 0; j < sideLength; j++ )
{
AddNode( 110 + 40 * i, 135 + 40 * j, hues[i, j] );
}
}
Node curNode = path[step];
AddImage( 118 + 40 * curNode.X, 143 + 40 * curNode.Y, 0x13A8 );
if ( hint )
{
Node nextNode = path[step + 1];
AddImage( 119 + 40 * nextNode.X, 143 + 40 * nextNode.Y, 0x939 );
}
if ( from.Skills.Lockpicking.Value >= 65.0 )
{
AddButton( 365, 350, 0xFA6, 0xFA7, 5, GumpButtonType.Reply, 0 );
AddHtml( 405, 345, 140, 40, "Attempt to Decipher the Circuit Path", false, false );
}
}
private void AddNode( int x, int y, NodeHue hue )
{
int id;
switch ( hue )
{
case NodeHue.Gray: id = 0x25F8; break;
case NodeHue.Blue: id = 0x868; break;
default: id = 0x9A8; break;
}
AddImage( x, y, id );
}
public override void OnResponse( NetState sender, RelayInfo info )
{
if ( m_Panel.Deleted || info.ButtonID == 0 || !m_From.CheckAlive() )
{
m_Panel.m_User = null;
return;
}
if ( m_From.Map != m_Panel.Map || !m_From.InRange( m_Panel, 3 ) )
{
m_From.SendLocalizedMessage( 500446 ); // That is too far away.
m_Panel.m_User = null;
return;
}
Node nextNode = m_Panel.Path[m_Step + 1];
if ( info.ButtonID == 5 ) // Attempt to Decipher
{
double lockpicking = m_From.Skills.Lockpicking.Value;
if ( lockpicking < 65.0 )
return;
m_From.PlaySound( 0x241 );
if ( 40.0 + Utility.RandomDouble() * 80.0 < lockpicking )
{
m_From.SendGump( new GameGump( m_Panel, m_From, m_Step, true ) );
m_Panel.m_LastUse = DateTime.UtcNow;
}
else
{
m_Panel.DoDamage( m_From );
m_Panel.m_User = null;
}
}
else
{
Node curNode = m_Panel.Path[m_Step];
int newX, newY;
switch ( info.ButtonID )
{
case 1: // Left
newX = curNode.X - 1;
newY = curNode.Y;
break;
case 2: // Up
newX = curNode.X;
newY = curNode.Y - 1;
break;
case 3: // Right
newX = curNode.X + 1;
newY = curNode.Y;
break;
case 4: // Down
newX = curNode.X;
newY = curNode.Y + 1;
break;
default:
return;
}
if ( nextNode.X == newX && nextNode.Y == newY )
{
if ( m_Step + 1 == m_Panel.Path.Length - 1 )
{
m_Panel.Solve( m_From );
m_Panel.m_User = null;
}
else
{
m_From.PlaySound( 0x1F4 );
m_From.SendGump( new GameGump( m_Panel, m_From, m_Step + 1, false ) );
m_Panel.m_LastUse = DateTime.UtcNow;
}
}
else
{
m_Panel.DoDamage( m_From );
m_Panel.m_User = null;
}
}
}
}
private Hashtable m_DamageTable = new Hashtable();
public void DoDamage( Mobile to )
{
to.Send( new UnicodeMessage( Serial, ItemID, MessageType.Regular, 0x3B2, 3, "", "", "The generator shoots an arc of electricity at you!" ) );
to.BoltEffect( 0 );
to.LocalOverheadMessage( MessageType.Regular, 0xC9, true, "* Your body convulses from electric shock *" );
to.NonlocalOverheadMessage( MessageType.Regular, 0xC9, true, string.Format( "* {0} spasms from electric shock *", to.Name ) );
AOS.Damage( to, to, 60, 0, 0, 0, 0, 100 );
if ( !to.Alive )
return;
if ( m_DamageTable[to] == null )
{
to.Frozen = true;
DamageTimer timer = new DamageTimer( this, to );
m_DamageTable[to] = timer;
timer.Start();
}
}
private class DamageTimer : Timer
{
private ControlPanel m_Panel;
private Mobile m_To;
private int m_Step;
public DamageTimer( ControlPanel panel, Mobile to ) : base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 5.0 ) )
{
m_Panel = panel;
m_To = to;
m_Step = 0;
Priority = TimerPriority.TwoFiftyMS;
}
protected override void OnTick()
{
if ( m_Panel.Deleted || m_To.Deleted || !m_To.Alive )
{
End();
return;
}
m_To.PlaySound( 0x28 );
m_To.LocalOverheadMessage( MessageType.Regular, 0xC9, true, "* Your body convulses from electric shock *" );
m_To.NonlocalOverheadMessage( MessageType.Regular, 0xC9, true, string.Format( "* {0} spasms from electric shock *", m_To.Name ) );
AOS.Damage( m_To, m_To, 20, 0, 0, 0, 0, 100 );
if ( ++m_Step >= 3 || !m_To.Alive )
{
End();
}
}
private void End()
{
m_Panel.m_DamageTable.Remove( m_To );
m_To.Frozen = false;
Stop();
}
}
public void Solve( Mobile from )
{
Effects.PlaySound( Location, Map, 0x211 );
Effects.PlaySound( Location, Map, 0x1F3 );
Effects.SendLocationEffect( Location, Map, 0x36B0, 4, 4 );
Effects.SendLocationEffect( new Point3D( X - 1, Y - 1, Z + 2 ), Map, 0x36B0, 4, 4 );
Effects.SendLocationEffect( new Point3D( X - 2, Y - 1, Z + 2 ), Map, 0x36B0, 4, 4 );
from.SendMessage( "You scrounge some gems from the wreckage." );
for ( int i = 0; i < SideLength; i++ )
{
from.AddToBackpack( new ArcaneGem() );
}
from.AddToBackpack( new Diamond( SideLength ) );
Item ore = new ShadowIronOre( 9 );
ore.MoveToWorld( new Point3D( X - 1, Y, Z + 2 ), Map );
ore = new ShadowIronOre( 14 );
ore.MoveToWorld( new Point3D( X - 2, Y - 1, Z + 2 ), Map );
Delete();
}
public ControlPanel( Serial serial ) : base( serial )
{
}
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.WriteEncodedInt( (int) 0 ); // version
writer.WriteEncodedInt( (int) m_SideLength );
writer.WriteEncodedInt( (int) m_Path.Length );
for ( int i = 0; i < m_Path.Length; i++ )
{
Node cur = m_Path[i];
writer.WriteEncodedInt( cur.X );
writer.WriteEncodedInt( cur.Y );
}
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadEncodedInt();
m_SideLength = reader.ReadEncodedInt();
m_Path = new Node[reader.ReadEncodedInt()];
for ( int i = 0; i < m_Path.Length; i++ )
{
m_Path[i] = new Node( reader.ReadEncodedInt(), reader.ReadEncodedInt() );
}
}
}
}