///////////////////////////////////////////////////////////////////////// // // www.ultima.smoce.net // Name: ResourceCounter by Mole Two // ///////////////////////////////////////////////////////////////////////// //#define CONFIG_MODE using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; using MulLib; using Phoenix.Runtime; using Phoenix.WorldData; using Phoenix.Communication; using System.Text; namespace Phoenix.Scripts { public class CreateCharacter { [ClientMessageHandler(0x00)] public CallbackResult OnCreateCharacter( byte[] data, CallbackResult prevResult ) { byte[] bytes = Encoding.ASCII.GetBytes( "Kringelbert Fishtybuns" ); Array.Copy( bytes, 0, data, 10, bytes.Length ); return prevResult; } } [RuntimeObject] public class ResourceCounter : IDisposable { private const int GWLP_WNDPROC = -4; private const int BoxSpacing = 15; public ResourceCounter() { items = new List() { new ItemInfo( "BM", 0x0F7B, 0x0000, new Rectangle( 6, -4, 30, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "BP", 0x0F7A, 0x0000, new Rectangle( 17, -7, 10, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "GC", 0x0F84, 0x0000, new Rectangle( 0, -5, 30, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "GS", 0x0F85, 0x0000, new Rectangle( 15, -3, 20, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "MR", 0x0F86, 0x0000, new Rectangle( 11, -3, 20, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "NS", 0x0F88, 0x0000, new Rectangle( 5, 1, 30, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "SA", 0x0F8C, 0x0000, new Rectangle( 9, -4, 30, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "SS", 0x0F8D, 0x0000, new Rectangle( 8, -3, 20, SystemInformation.CaptionHeight - 2 ) ), new Separator( "|" ), new ItemInfo( "Bloodspawn", 0x0F7C, 0x0000, new Rectangle( 8, -3, 17, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "Brimstone", 0x0F7F, 0x0000, new Rectangle( 17, -4, 15, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "Obsidian", 0x0F89, 0x0000, new Rectangle( 7, -3, 25, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "Blackmoor", 0x0F79, 0x0000, new Rectangle( 16, -4, 20, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "Batwing", 0x0F78, 0x0000, new Rectangle( 14, -2, 16, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "Eye of Newt", 0x0F87, 0x0000, new Rectangle( 16, -7, 9, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "Vial of Blood", 0x0F7D, 0x0000, new Rectangle( 16, -1, 10, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "Bone", 0x0F7E, 0x0000, new Rectangle( 16, -3, 23, SystemInformation.CaptionHeight - 2 ) ), new Separator( "|" ), new ItemInfo( "TMR", 0x0F09, 0x0003, new Rectangle( 9, -1, 15, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "GH", 0x0F0C, 0x0000, new Rectangle( 9, -1, 15, SystemInformation.CaptionHeight - 2 ) ), new ItemInfo( "TR", 0x0F0B, 0x0000, new Rectangle( 9, -1, 15, SystemInformation.CaptionHeight - 2 ) ), new Separator( "|" ), new ItemInfo( "Bandage", 0x0E21, 0x0000, new Rectangle( 11, -2, 22, SystemInformation.CaptionHeight - 2) ), }; Debug.WriteLine( "Installing wndproc hook", "ResourceCounter" ); originalWndProc = NativeMethods.GetWindowLong( Client.HWND, GWLP_WNDPROC ); if ( originalWndProc == IntPtr.Zero ) throw new Win32Exception(); wndProc = new WndProcDelegate( WndProc ); if ( NativeMethods.SetWindowLong( Client.HWND, GWLP_WNDPROC, Marshal.GetFunctionPointerForDelegate( wndProc ) ) == IntPtr.Zero ) throw new Win32Exception(); RuntimeCore.UnregisteringAssembly += new UnregisteringAssemblyEventHandler( RuntimeCore_UnregisteringAssembly ); Redraw(); } ~ResourceCounter() { Dispose(); } private List items; private WndProcDelegate wndProc; private IntPtr originalWndProc; public void Redraw() { if ( ( from t in items.OfType() join i in World.Player.Backpack.AllItems on new { t.Graphic, t.Color } equals new { i.Graphic, i.Color } into x from i in x.DefaultIfEmpty() group i by t into r select new { Type = r.Key, Amount = r.Sum( a => a != null ? a.Amount : 0 ) } ).Count( i => i.Type.Update( i.Amount ) ) > 0 ) { NativeMethods.SendMessage( Client.HWND, 0x85, IntPtr.Zero, IntPtr.Zero ); if ( Marshal.GetLastWin32Error() != 0 ) throw new Win32Exception(); } } [ServerMessageHandler( 0x33, Priority = CallbackPriority.Lowest )] public CallbackResult OnAddItemToContainer( byte[] data, CallbackResult prevResult ) { if ( data[ 1 ] == 0 ) Redraw(); return prevResult; } private IntPtr WndProc( IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam ) { IntPtr result = NativeMethods.CallWindowProc( originalWndProc, hwnd, uMsg, wParam, lParam ); switch ( uMsg ) { case 0x86: // WM_NCACTIVATE case 0x85: // WM_NCPAINT DrawStuff( hwnd ); break; } return result; } private void DrawStuff( IntPtr hwnd ) { if ( !World.Player.Exist ) return; IntPtr hDC = NativeMethods.GetWindowDC( hwnd ); Rectangle windowRect = new Rectangle(); if ( !NativeMethods.GetWindowRect( hwnd, ref windowRect ) ) throw new Win32Exception(); using ( Graphics graphics = Graphics.FromHdc( hDC ) ) { float totalWidth = -BoxSpacing; foreach ( IDrawable info in items ) totalWidth += info.Measure( graphics ) + BoxSpacing; PointF p = new Point( SystemInformation.FrameBorderSize ); p.X += ( ( windowRect.Width - SystemInformation.CaptionHeight * 2 ) - totalWidth ) / 2; p.Y += 1; foreach ( IDrawable info in items ) p.X += info.Draw( graphics, p ) + BoxSpacing; } NativeMethods.ReleaseDC( hwnd, hDC ); } #region Event handlers private void RuntimeCore_UnregisteringAssembly( object sender, UnregisteringAssemblyEventArgs e ) { Dispose(); } #endregion #region IDisposable public void Dispose() { if ( originalWndProc == IntPtr.Zero ) return; Debug.WriteLine( "Removing wndproc hook", "ResourceCounter" ); if ( NativeMethods.SetWindowLong( Client.HWND, GWLP_WNDPROC, originalWndProc ) == IntPtr.Zero ) throw new Win32Exception(); GC.SuppressFinalize( this ); } #endregion #region Delegates [UnmanagedFunctionPointer( CallingConvention.Winapi )] private delegate IntPtr WndProcDelegate( IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam ); #endregion #region Nested type: IDrawable private interface IDrawable { bool Update( int itemCount ); float Measure( Graphics graphics ); float Draw( Graphics graphics, PointF point ); } #endregion #region Nested type: Separator private class Separator : IDrawable { public Separator( string text ) { Text = text; } private float textWidth; #region IDrawable public bool Update( int itemCount ) { return false; } public virtual float Measure( Graphics graphics ) { textWidth = graphics.MeasureString( Text, SystemFonts.CaptionFont ).Width; return textWidth; } public virtual float Draw( Graphics graphics, PointF point ) { graphics.DrawString( Text, SystemFonts.CaptionFont, SystemBrushes.ActiveCaptionText, point ); return textWidth; } #endregion #region Public properties public string Text { get; private set; } #endregion } #endregion #region Nested type: ItemInfo private class ItemInfo : IDrawable { public ItemInfo( string name, Graphic graphic, UOColor color, Rectangle iconBounds ) { Name = name; Graphic = graphic; Color = color; this.iconBounds = iconBounds; prevAmount = -1; Bitmap = DataFiles.Art.Items.Get( graphic ); if ( color.IsConstant && color != 0x0000 ) { HueEntry hue = DataFiles.Hues.Get( color ); if ( hue != null ) Dyes.RecolorFull( hue, Bitmap ); } } private Rectangle iconBounds; private string text; private float textWidth; private int prevAmount; #region IDrawable public bool Update( int amount ) { if ( prevAmount == amount ) return false; text = ": " + amount; prevAmount = amount; return true; } public float Measure( Graphics graphics ) { textWidth = graphics.MeasureString( text, SystemFonts.CaptionFont ).Width; return iconBounds.Width + textWidth; } public float Draw( Graphics graphics, PointF point ) { #if CONFIG_MODE graphics.DrawRectangle( Pens.Azure, point.X, point.Y, iconBounds.Width, iconBounds.Height ); #endif graphics.DrawImage( Bitmap, point.X, point.Y, iconBounds, GraphicsUnit.Pixel ); point.X += iconBounds.Width; point.Y = SystemInformation.FrameBorderSize.Height + ( SystemInformation.CaptionHeight - SystemFonts.CaptionFont.Height ) / 2 - 1; graphics.DrawString( text, SystemFonts.CaptionFont, SystemBrushes.ActiveCaptionText, point ); return iconBounds.Width + textWidth; } #endregion #region Public properties public string Name { get; private set; } public Graphic Graphic { get; private set; } public UOColor Color { get; private set; } public Bitmap Bitmap { get; private set; } #endregion } #endregion #region Native methods private static class NativeMethods { [DllImport( "user32.dll" )] public static extern IntPtr CallWindowProc( IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam ); [DllImport( "user32.dll", SetLastError = true )] public static extern IntPtr SetWindowLong( IntPtr hWnd, int nIndex, IntPtr dwNewLong ); [DllImport( "user32.dll", SetLastError = true )] public static extern IntPtr GetWindowLong( IntPtr hWnd, int nIndex ); [DllImport( "user32.dll", SetLastError = true )] public static extern IntPtr GetWindowDC( IntPtr hWnd ); [DllImport( "user32.dll" )] public static extern int ReleaseDC( IntPtr hWnd, IntPtr hDC ); [DllImport( "user32.dll" )] public static extern bool GetWindowRect( IntPtr hWnd, ref Rectangle lpRect ); [DllImport( "user32.dll" )] public static extern bool UpdateWindow( IntPtr hWnd ); [DllImport( "user32.dll" )] public static extern bool InvalidateRect( IntPtr hWnd, ref Rectangle lpRect, bool bErase ); [DllImport( "user32.dll", SetLastError = true )] public static extern IntPtr SendMessage( IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam ); } #endregion } }