using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using ClassicAssist.Data; using ClassicAssist.Data.Skills; using ClassicAssist.Data.Vendors; using ClassicAssist.Misc; using ClassicAssist.Shared.Resources; using ClassicAssist.UI.ViewModels; using ClassicAssist.UO; using ClassicAssist.UO.Data; using ClassicAssist.UO.Network; using ClassicAssist.UO.Network.PacketFilter; using ClassicAssist.UO.Network.Packets; using ClassicAssist.UO.Objects; using ClassicAssist.UO.Objects.Gumps; namespace ClassicAssist.Shared.UO { public class Commands { private static readonly object _dragDropLock = new object(); public static void DragItem( int serial, int amount ) { Engine.SendPacketToServer( new DragItem( serial, amount ) ); } public static void DropItem( int serial, int containerSerial, int x = -1, int y = -1, int z = 0 ) { Engine.SendPacketToServer( new DropItem( serial, containerSerial, x, y, z ) ); } public static async Task DragDropAsync( int serial, int amount, int containerSerial, int x = -1, int y = -1, int z = 0 ) { if ( amount == -1 ) { Item i = Engine.Items.GetItem( serial ); amount = i?.Count ?? 1; } lock ( _dragDropLock ) { DragItem( serial, amount ); Thread.Sleep( Options.CurrentOptions.ActionDelayMS ); DropItem( serial, containerSerial, x, y, z ); } await Task.CompletedTask; } public static Task EquipType( int id, Layer layer ) { if ( id <= -1 ) { SystemMessage( Strings.Invalid_type___ ); return Task.CompletedTask; } int containerSerial = Engine.Player?.Serial ?? 0; if ( containerSerial == 0 || containerSerial == -1 ) { return Task.CompletedTask; } Item backpack = Engine.Player?.Backpack; Item item = backpack?.Container?.SelectEntity( i => i.ID == id ); if ( item == null ) { return Task.CompletedTask; } if ( layer == Layer.Invalid ) { StaticTile tileData = TileData.GetStaticTile( item.ID ); layer = (Layer) tileData.Quality; } if ( layer == Layer.Invalid ) { throw new ArgumentException( "EquipItem: Layer is invalid" ); } return ActionPacketQueue.EnqueueActionPackets( new BasePacket[] { new DragItem( item.Serial, 1 ), new EquipRequest( item.Serial, layer, containerSerial ) }, QueuePriority.Medium ); } public static Task EquipItem( Item item, Layer layer ) { int containerSerial = Engine.Player?.Serial ?? 0; if ( containerSerial == 0 || containerSerial == -1 ) { return Task.CompletedTask; } if ( layer == Layer.Invalid ) { StaticTile tileData = TileData.GetStaticTile( item.ID ); layer = (Layer) tileData.Quality; } if ( layer == Layer.Invalid ) { throw new ArgumentException( "EquipItem: Layer is invalid" ); } return ActionPacketQueue.EnqueueActionPackets( new BasePacket[] { new DragItem( item.Serial, 1 ), new EquipRequest( item.Serial, layer, containerSerial ) }, QueuePriority.Medium ); } public static void SystemMessage( string text, int hue = 0x03b2 ) { //TODO byte[] textBytes = Encoding.BigEndianUnicode.GetBytes( text + '\0' ); int length = 48 + textBytes.Length; PacketWriter pw = new PacketWriter( length ); pw.Write( (byte) 0xAE ); pw.Write( (short) length ); pw.Write( 0xFFFFFFFF ); pw.Write( (ushort) 0xFFFF ); pw.Write( (byte) 0 ); pw.Write( (short) hue ); pw.Write( (short) 0x03 ); pw.WriteAsciiFixed( Strings.UO_LOCALE, 4 ); pw.WriteAsciiFixed( "System\0", 30 ); pw.Write( textBytes, 0, textBytes.Length ); Engine.SendPacketToClient( pw ); } public static void MobileQuery( int serial, MobileQueryType queryType = MobileQueryType.StatsRequest ) { Engine.SendPacketToServer( new MobileQuery( serial, queryType ) ); } public static async Task GetTargeSerialAsync( string message = "", int timeout = 30000 ) { if ( string.IsNullOrEmpty( message ) ) { message = Strings.Target_object___; } SystemMessage( message ); Random random = new Random(); return await Task.Run( () => { bool wasTargetting = Engine.TargetExists; uint value = (uint) random.Next( 1, int.MaxValue ); //TODO PacketWriter pw = new PacketWriter( 19 ); pw.Write( (byte) 0x6C ); pw.Write( (byte) 0 ); pw.Write( value ); pw.Write( (byte) 0 ); pw.Fill(); AutoResetEvent are = new AutoResetEvent( false ); int serial = -1; PacketFilterInfo pfi = new PacketFilterInfo( 0x6C, new[] { PacketFilterConditions.UIntAtPositionCondition( value, 2 ) }, ( packet, info ) => { serial = ( packet[7] << 24 ) | ( packet[8] << 16 ) | ( packet[9] << 8 ) | packet[10]; are.Set(); } ); try { Engine.AddSendPreFilter( pfi ); Engine.SendPacketToClient( pw ); bool result = are.WaitOne( timeout ); if ( result ) { return serial; } Engine.SendPacketToClient( new CancelTargetCursor( value ) ); SystemMessage( Strings.Timeout___ ); return serial; } finally { Engine.RemoveSendPreFilter( pfi ); if ( wasTargetting ) { ResendTargetToClient(); } } } ); } public static async Task<(TargetType, TargetFlags, int, int, int, int, int)> GetTargeInfoAsync( string message = "", int timeout = 30000 ) { if ( string.IsNullOrEmpty( message ) ) { message = Strings.Target_object___; } SystemMessage( message ); Random random = new Random(); return await Task.Run( () => { bool wasTargetting = Engine.TargetExists; uint value = (uint) random.Next( 1, int.MaxValue ); //TODO PacketWriter pw = new PacketWriter( 19 ); pw.Write( (byte) 0x6C ); pw.Write( (byte) 1 ); pw.Write( value ); pw.Write( (byte) 0 ); pw.Fill(); AutoResetEvent are = new AutoResetEvent( false ); TargetType targetType = TargetType.Object; TargetFlags targetFlags = TargetFlags.None; int serial = 0; int x = 0; int y = 0; int z = 0; int itemID = 0; PacketFilterInfo pfi = new PacketFilterInfo( 0x6C, new[] { PacketFilterConditions.UIntAtPositionCondition( value, 2 ) }, ( packet, info ) => { targetType = (TargetType) packet[1]; targetFlags = (TargetFlags) packet[6]; serial = ( packet[7] << 24 ) | ( packet[8] << 16 ) | ( packet[9] << 8 ) | packet[10]; x = ( packet[11] << 8 ) | packet[12]; y = ( packet[13] << 8 ) | packet[14]; z = ( packet[15] << 8 ) | packet[16]; itemID = ( packet[17] << 8 ) | packet[18]; are.Set(); } ); try { Engine.AddSendPreFilter( pfi ); Engine.SendPacketToClient( pw ); bool result = are.WaitOne( timeout ); if ( result ) { return ( targetType, targetFlags, serial, x, y, z, itemID ); } Engine.SendPacketToClient( new CancelTargetCursor( value ) ); SystemMessage( Strings.Timeout___ ); return ( targetType, targetFlags, serial, x, y, z, itemID ); } finally { Engine.RemoveSendPreFilter( pfi ); if ( wasTargetting ) { ResendTargetToClient(); } } } ); } public static bool GumpButtonClick( uint gumpID, int buttonID, int[] switches = null ) { if ( !Engine.GumpList.TryGetValue( gumpID, out int serial ) ) { return false; } Engine.SendPacketToServer( new GumpButtonClick( (int) gumpID, serial, buttonID, switches ) ); Engine.GumpList.TryRemove( gumpID, out _ ); CloseClientGump( gumpID ); return true; } public static void CloseClientGump( Type gumpType ) { Engine.Gumps.GetGumps( out Gump[] gumps ); IEnumerable closeGumps = gumps.Where( t => t.GetType() == gumpType ); foreach ( Gump closeGump in closeGumps ) { CloseClientGump( closeGump.ID ); } } public static void CloseClientGump( uint gumpID ) { Engine.Gumps.Remove( gumpID ); Engine.SendPacketToClient( new CloseClientGump( gumpID ) ); } public static bool WaitForMenu( int gumpId, int timeout = 30000 ) { PacketFilterInfo pfi = new PacketFilterInfo( 0x7C ); if ( gumpId != 0 ) { pfi = new PacketFilterInfo( 0x7C, new[] { PacketFilterConditions.ShortAtPositionCondition( gumpId, 7 ) } ); } PacketWaitEntry packetWaitEntry = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming, true ); try { bool result = packetWaitEntry.Lock.WaitOne( timeout ); return result; } finally { Engine.PacketWaitEntries.Remove( packetWaitEntry ); } } public static bool WaitForGump( uint gumpId, int timeout = 30000 ) { PacketFilterInfo pfi = new PacketFilterInfo( 0xDD ); if ( gumpId != 0 ) { pfi = new PacketFilterInfo( 0xDD, new[] { PacketFilterConditions.UIntAtPositionCondition( gumpId, 7 ) } ); } PacketWaitEntry packetWaitEntry = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming, true ); try { bool result = packetWaitEntry.Lock.WaitOne( timeout ); return result; } finally { Engine.PacketWaitEntries.Remove( packetWaitEntry ); } } public static void ChangeSkillLock( SkillEntry skill, LockStatus lockStatus, bool requery = true ) { Engine.SendPacketToServer( new ChangeSkillLock( skill, lockStatus ) ); if ( requery ) { Engine.SendPacketToServer( new MobileQuery( Engine.Player?.Serial ?? 0, MobileQueryType.SkillsRequest ) ); } } public static void ChangeStatLock( StatType stat, LockStatus lockStatus ) { Engine.SendPacketToServer( new ChangeStatLock( stat, lockStatus ) ); } public static (int, bool) WaitForTargetOrFizzle( int timeout ) { PacketWaitEntry targetWe = CreateWaitEntry( new PacketFilterInfo( 0x6C ) ); PacketWaitEntry fizzWe = CreateWaitEntry( new PacketFilterInfo( 0xC0, new[] { PacketFilterConditions.IntAtPositionCondition( Engine.Player.Serial, 2 ), PacketFilterConditions.ShortAtPositionCondition( 0x3735, 10 ) } ) ); PacketWaitEntry fizzMessageWe = CreateWaitEntry( new PacketFilterInfo( 0xC1, new[] { PacketFilterConditions.IntAtPositionCondition( 502632, 14 ) /* The spell fizzles. */ } ) ); PacketWaitEntry recoveredMessageWe = CreateWaitEntry( new PacketFilterInfo( 0xC1, new[] { PacketFilterConditions.IntAtPositionCondition( 502644, 14 ) /* You have not yet recovered from casting a spell. */ } ) ); PacketWaitEntry alreadyCastingWe = CreateWaitEntry( new PacketFilterInfo( 0xC1, new[] { PacketFilterConditions.IntAtPositionCondition( 502642, 14 ) /* You are already casting a spell. */ } ) ); PacketWaitEntry alreadyCasting2We = CreateWaitEntry( new PacketFilterInfo( 0xC1, new[] { PacketFilterConditions.IntAtPositionCondition( 502645, 14 ) /* You are already casting a spell. */ } ) ); PacketWaitEntry concentrationWe = CreateWaitEntry( new PacketFilterInfo( 0xC1, new[] { PacketFilterConditions.IntAtPositionCondition( 500641, 14 ) /* Your concentration is disturbed, thus ruining thy spell. */ } ) ); PacketWaitEntry noManaWe = CreateWaitEntry( new PacketFilterInfo( 0xC1, new[] { PacketFilterConditions.IntAtPositionCondition( 502625, 14 ) /* Insufficient mana etc... */ } ) ); PacketWaitEntry fizzChivWe = CreateWaitEntry( new PacketFilterInfo( 0x54, new[] { PacketFilterConditions.ShortAtPositionCondition( 0x1D6, 2 ), PacketFilterConditions.ShortAtPositionCondition( Engine.Player.X, 6 ), PacketFilterConditions.ShortAtPositionCondition( Engine.Player.Y, 8 ), PacketFilterConditions.ShortAtPositionCondition( Engine.Player.Z, 10 ) } ) ); Engine.WaitingForTarget = true; List tasks = new List(); try { Task targetTask = Task.Run( () => { do { bool result = targetWe.Lock.WaitOne( timeout ); if ( !result ) { return false; } if ( targetWe.Packet[6] == 0x03 ) { continue; } return true; } while ( true ); } ); Task fizzTask = Task.Factory.StartNew( () => fizzWe.Lock.WaitOne( timeout ), TaskCreationOptions.LongRunning ); Task fizzMessageTask = Task.Factory.StartNew( () => fizzMessageWe.Lock.WaitOne( timeout + 100 ), TaskCreationOptions.LongRunning ); Task recoveredMessageTask = Task.Factory.StartNew( () => recoveredMessageWe.Lock.WaitOne( timeout + 100 ), TaskCreationOptions.LongRunning ); Task alreadyCastingTask = Task.Factory.StartNew( () => alreadyCastingWe.Lock.WaitOne( timeout + 100 ), TaskCreationOptions.LongRunning ); Task alreadyCasting2Task = Task.Factory.StartNew( () => alreadyCasting2We.Lock.WaitOne( timeout + 100 ), TaskCreationOptions.LongRunning ); Task concentrationTask = Task.Factory.StartNew( () => concentrationWe.Lock.WaitOne( timeout + 100 ), TaskCreationOptions.LongRunning ); Task noManaTask = Task.Factory.StartNew( () => noManaWe.Lock.WaitOne( timeout + 100 ), TaskCreationOptions.LongRunning ); Task fizzChivTask = Task.Factory.StartNew( () => fizzChivWe.Lock.WaitOne( timeout + 100 ), TaskCreationOptions.LongRunning ); int index; tasks.AddRange( new[] { targetTask, fizzTask, fizzMessageTask, recoveredMessageTask, alreadyCastingTask, alreadyCasting2Task, concentrationTask, noManaTask, fizzChivTask } ); try { index = Task.WaitAny( tasks.ToArray() ); } catch ( OperationCanceledException ) { return ( -1, false ); } catch ( ThreadInterruptedException ) { return ( -1, false ); } return ( index, index == 0 && targetTask.Result ); } finally { Engine.PacketWaitEntries.Remove( targetWe ); Engine.PacketWaitEntries.Remove( fizzWe ); Engine.PacketWaitEntries.Remove( fizzMessageWe ); Engine.PacketWaitEntries.Remove( recoveredMessageWe ); Engine.PacketWaitEntries.Remove( alreadyCastingWe ); Engine.PacketWaitEntries.Remove( alreadyCasting2We ); Engine.PacketWaitEntries.Remove( concentrationWe ); Engine.PacketWaitEntries.Remove( noManaWe ); Engine.PacketWaitEntries.Remove( fizzChivWe ); Engine.WaitingForTarget = false; } } private static PacketWaitEntry CreateWaitEntry( PacketFilterInfo packetFilterInfo, PacketDirection direction = PacketDirection.Incoming ) { return Engine.PacketWaitEntries.Add( packetFilterInfo, direction ); } public static bool WaitForTarget( int timeout ) { PacketFilterInfo pfi = new PacketFilterInfo( 0x6C ); Engine.WaitingForTarget = true; PacketWaitEntry we = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming ); try { do { bool result = we.Lock.WaitOne( timeout ); if ( !result ) { return false; } if ( we.Packet[6] == 0x03 ) { continue; } return true; } while ( true ); } finally { Engine.PacketWaitEntries.Remove( we ); Engine.WaitingForTarget = false; } } public static async Task WaitForIncomingPacketFilterAsync( PacketFilterInfo pfi, int timeout, bool invokeHandler = true, bool fixedSize = false ) { AutoResetEvent are = new AutoResetEvent( false ); byte[] packet = null; Engine.AddReceiveFilter( new PacketFilterInfo( pfi.PacketID, pfi.GetConditions(), ( data, info ) => { packet = data; are.Set(); } ) ); Task filterTask = are.ToTask(); if ( await Task.WhenAny( filterTask, Task.Delay( timeout ) ) == filterTask ) { if ( invokeHandler ) { PacketHandler handler = IncomingPacketHandlers.GetHandler( packet[0] ); handler?.OnReceive( new PacketReader( packet, packet.Length, fixedSize ) ); } } Engine.RemoveReceiveFilter( pfi ); return packet != null; } public static void Resync() { Engine.SendPacketToServer( new Resync() ); } public static void ToggleGargoyleFlying() { Engine.SendPacketToServer( new ToggleGargoyleFlying() ); } public static bool WaitForIncomingPacket( PacketFilterInfo pfi, int timeout, Action beforeWait = null ) { PacketWaitEntry we = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming, true ); bool result; beforeWait?.Invoke(); try { result = we.Lock.WaitOne( timeout ); } finally { Engine.PacketWaitEntries.Remove( we ); } return result; } public static void SetWeaponAbility( int abilityIndex ) { Engine.SendPacketToServer( new SetWeaponAbility( abilityIndex ) ); } public static void ClearWeaponAbility() { Engine.SendPacketToServer( new ClearWeaponAbility() ); } public static void RenameRequest( int serial, string name ) { Engine.SendPacketToServer( new RenameRequest( serial, name ) ); } public static bool WaitForContainerContents( int serial, int timeout ) { PacketFilterInfo pfi = new PacketFilterInfo( 0x3C, new[] { PacketFilterConditions.IntAtPositionCondition( serial, 19 ) } ); PacketWaitEntry we = Engine.PacketWaitEntries.Add( pfi, PacketDirection.Incoming, true ); Engine.SendPacketToServer( new UseObject( serial ) ); try { bool result = we.Lock.WaitOne( timeout ); return result; } finally { Engine.PacketWaitEntries.Remove( we ); } } private static byte[] GetKeywordBytes( IReadOnlyList keywords ) { int length = keywords.Count; List codeBytes = new List { (byte) ( length >> 4 ) }; int num3 = length & 15; bool flag = false; int index = 0; while ( index < length ) { int keywordID = keywords[index]; if ( flag ) { codeBytes.Add( (byte) ( keywordID >> 4 ) ); num3 = keywordID & 15; } else { codeBytes.Add( (byte) ( ( num3 << 4 ) | ( ( keywordID >> 8 ) & 15 ) ) ); codeBytes.Add( (byte) keywordID ); } index++; flag = !flag; } if ( !flag ) { codeBytes.Add( (byte) ( num3 << 4 ) ); } return codeBytes.ToArray(); } public static void Speak( string text, int hue = 34, JournalSpeech speechType = JournalSpeech.Say ) { int[] keywords = new int[0]; if ( speechType == JournalSpeech.Say ) { keywords = Speech.GetKeywords( text.ToLower() ); } PacketWriter writer = new PacketWriter(); writer.Write( (byte) 0xAD ); writer.Write( (short) 0 ); // len; writer.Write( keywords.Length > 0 ? (byte) 0xC0 : (byte) speechType ); writer.Write( (short) hue ); writer.Write( (short) 0x3 ); writer.WriteAsciiFixed( Strings.UO_LOCALE, 4 ); if ( keywords.Length > 0 ) { byte[] bytes = GetKeywordBytes( keywords ); writer.Write( bytes, 0, bytes.Length ); writer.WriteAsciiNull( text ); } else { writer.WriteBigUniNull( text ); } byte[] packet = writer.ToArray(); packet[1] = (byte) ( packet.Length << 8 ); packet[2] = (byte) packet.Length; Engine.SendPacketToServer( packet, packet.Length ); } public static void PartyMessage( string message ) { int len = 6 + ( message.Length + 1 ) * 2; PacketWriter writer = new PacketWriter( len ); writer.Write( (byte) 0xBF ); writer.Write( (short) len ); writer.Write( (short) 6 ); writer.Write( (byte) 4 ); writer.WriteBigUniNull( message ); Engine.SendPacketToServer( writer ); } public static void OverheadMessage( string message, int hue, int serial ) { byte[] textBytes = Encoding.BigEndianUnicode.GetBytes( message + '\0' ); int length = 48 + textBytes.Length; Entity entity = (Entity) Engine.Mobiles.GetMobile( serial ) ?? Engine.Items.GetItem( serial ); if ( entity == null ) { return; } PacketWriter pw = new PacketWriter( length ); pw.Write( (byte) 0xAE ); pw.Write( (short) length ); pw.Write( entity.Serial ); pw.Write( (ushort) entity.ID ); pw.Write( (byte) JournalSpeech.Say ); pw.Write( (short) hue ); pw.Write( (short) 0x03 ); pw.WriteAsciiFixed( Strings.UO_LOCALE, 4 ); pw.WriteAsciiFixed( entity.Name ?? "Unknown", 30 ); pw.Write( textBytes, 0, textBytes.Length ); Engine.SendPacketToClient( pw ); } public static void ResendTargetToClient() { PacketWriter pw = new PacketWriter( 19 ); pw.Write( (byte) 0x6C ); pw.Write( (byte) TargetType.Object ); pw.Write( Engine.TargetSerial ); pw.Write( (byte) Engine.TargetFlags ); pw.Write( 0 ); pw.Write( (short) 0 ); pw.Write( (short) 0 ); pw.Write( (short) 0 ); pw.Write( (short) 0 ); Engine.TargetExists = true; Engine.SendPacketToClient( pw ); } public static void VendorBuy( int serial, ShopListEntry[] shopListEntries ) { int len = 8 + shopListEntries.Length * 7; PacketWriter pw = new PacketWriter( len ); pw.Write( (byte) 0x3B ); pw.Write( (short) len ); // length pw.Write( serial ); pw.Write( (byte) 2 ); //item list foreach ( ShopListEntry entry in shopListEntries ) { if ( entry == null ) { continue; } pw.Write( (byte) Layer.ShopBuy ); pw.Write( entry.Item.Serial ); pw.Write( (short) entry.Amount ); } Engine.SendPacketToServer( pw ); } public static void SetForceWalk( bool force ) { PacketWriter pw = new PacketWriter( 6 ); pw.Write( (byte) 0xBF ); pw.Write( (short) 0x06 ); pw.Write( (short) 0x26 ); pw.Write( (byte) ( force ? 2 : 0 ) ); Engine.SendPacketToClient( pw ); } public static void UO3DEquipItems( int[] serials ) { if ( serials == null || serials.Length == 0 ) { return; } int len = 4 + serials.Length * 4; PacketWriter pw = new PacketWriter( len ); pw.Write( (byte) 0xEC ); pw.Write( (short) len ); //size pw.Write( (byte) serials.Length ); foreach ( int serial in serials ) { pw.Write( serial ); } Engine.SendPacketToServer( pw ); } public static void UO3DUnequipItems( int[] layers ) { if ( layers == null || layers.Length == 0 ) { return; } PacketWriter pw = new PacketWriter( 4 + layers.Length * 2 ); pw.Write( (byte) 0xED ); pw.Write( (short) ( 4 + layers.Length * 2 ) ); pw.Write( (byte) layers.Length ); foreach ( int layer in layers ) { pw.Write( (short) layer ); } Engine.SendPacketToServer( pw ); } public static void ChatMsg( string text ) { PacketWriter pw = new PacketWriter( 11 + text.Length * 2 ); pw.Write( (byte) 0xB3 ); pw.Write( (short) ( 11 + text.Length * 2 ) ); pw.WriteAsciiFixed( Strings.UO_LOCALE, 4 ); pw.Write( (short) 0x61 ); pw.WriteBigUniNull( text ); Engine.SendPacketToServer( pw ); } public static void JoinChatChannel( string channel ) { /* * Text 0x62 (Join Conference): BYTE[2] Holder (0x0022) BYTE[?] Unicode conference name BYTE[2] Holder (0x0022) BYTE[2] Holder (0x0020) BYTE[?] Unicode password if pass req'd BYTE[2] Null Teriminator (0x0000) */ PacketWriter pw = new PacketWriter(); pw.Write( (byte) 0xB3 ); pw.Write( (short) ( 17 + channel.Length * 2 ) ); pw.WriteAsciiFixed( Strings.UO_LOCALE, 4 ); pw.Write( (short) 0x62 ); pw.WriteBigUniNull( channel ); pw.Write( (short) 0x22 ); pw.Write( (short) 0x20 ); pw.Write( (short) 0 ); Engine.SendPacketToServer( pw ); } public static void RemoveObject( int serial ) { Engine.SendPacketToClient( new RemoveObject( serial ) ); } public static async Task InspectObjectAsync() { ( TargetType targetType, TargetFlags _, int serial, int x, int y, int z, int itemID ) = await GetTargeInfoAsync( Strings.Target_object___ ); if ( targetType == TargetType.Object && serial != 0 ) { Entity entity = UOMath.IsMobile( serial ) ? (Entity) Engine.Mobiles.GetMobile( serial ) : Engine.Items.GetItem( serial ); if ( entity == null ) { return; } Engine.UIInvoker.Invoke( "ObjectInspectorWindow", null, typeof( ObjectInspectorViewModel ), new[] { entity } ); } else { if ( itemID == 0 ) { if ( x == 65535 && y == 65535 ) { return; } LandTile landTile = MapInfo.GetLandTile( (int) Engine.Player.Map, x, y ); Engine.UIInvoker.Invoke( "ObjectInspectorWindow", null, typeof( ObjectInspectorViewModel ), new[] { (object) landTile } ); } else { StaticTile[] statics = Statics.GetStatics( (int) Engine.Player.Map, x, y ); if ( statics == null ) { return; } StaticTile selectedStatic = statics.FirstOrDefault( i => i.ID == itemID ); if ( selectedStatic.ID == 0 ) { selectedStatic = TileData.GetStaticTile( itemID ); selectedStatic.X = x; selectedStatic.Y = y; selectedStatic.Z = z; } Engine.UIInvoker.Invoke( "ObjectInspectorWindow", null, typeof( ObjectInspectorViewModel ), new[] { (object) selectedStatic } ); } } } } }