Page 1 of 2 12 LastLast
Results 1 to 15 of 24

  Click here to go to the first staff post in this thread.   Thread: finally a menu class that doesn't suck

  1. #1
    Join Date
    Sep 2004
    Location
    Capital, Ism
    Posts
    321
    Thanks
    3
    Thanked 49 Times in 9 Posts
    Rep Power
    101

    finally a menu class that doesn't suck

    (cross posting with UC)

    so over the weekend i decided it was about time to drop a menu into my hack, only to find that basically every menu was something like http://www.uc-forum.com/forum/c-and-...-dx-8-9-a.html which is to say, gigantic kludgy mess with no abstraction and everything hardcoded in. on top of that, ugly too.

    Here is an elegant tree-recursive implementation that is very lightweight, infinitely extensible and looks good ( http://fiveload.com/images/bfbc2game2.png ) to boot. I have supplied implementations for boolean and float leaf nodes only.



    Limitations: keyboard only, if you want mouse support you will have to add cell dimension tracking which is fairly straightforward but definitely not worth the effort to me.

    Usage
    Code:
    class Vars
    {
    public:
            bool nospread, norecoil, noshake, nosky, playerESP, boundingBoxes;
            float aimFOV;
            Vars(void)
            {
                    nospread = norecoil = noshake = playerESP = boundingBoxes = true;
                    nosky = false;
                    aimFOV = 10.0f;
            }
    
            void initMenu(TreeMenu *menu)
            {
                    menu->setName("The Gustav");
                    menu->node("aimbot")->node(new BoolLeaf("no spread", &nospread));
                    menu->node("aimbot")->node(new BoolLeaf("no recoil", &norecoil));
                    menu->node("aimbot")->node(new BoolLeaf("no shake", &noshake));
                    menu->node("aimbot")->node(new FloatLeaf("FOV", &aimFOV, 0, 180, 5));
                    menu->node("visuals")->node(new BoolLeaf("no sky/fog", &nosky));
                    menu->node("visuals")->node(new BoolLeaf("player ESP", &playerESP));
                    menu->node("visuals")->node(new BoolLeaf("vehicle ESP", &boundingBoxes));
            }
    };
    TreeMenu header
    Code:
    #pragma once
    #include <map>
    #include <string>
    #include "D3DRenderer.h"
    
    using namespace std;
    
    #define MENU_ROW_WIDTH        200
    #define MENU_ROW_HEIGHT        40
    #define MENU_ROW_PADLEFT    20
    #define MENU_ROW_PADTOP        20
    #define MENU_CELL_MARGIN    4
    #define MENU_BACKGROUND        D3DCOLOR_ARGB(125, 50, 50, 50)
    #define MENU_SELECTED        D3DCOLOR_ARGB(150, 200, 200, 200)
    #define NODE_NAME_WIDTH        140
    #define NODE_VALUE_WIDTH    MENU_ROW_WIDTH - NODE_NAME_WIDTH - MENU_CELL_MARGIN
    
    #define MENU_KEY_LEFT        0
    #define MENU_KEY_RIGHT        1
    #define MENU_KEY_UP            2
    #define MENU_KEY_DOWN        3
    #define MENU_ACTIVATE        4
    
    #define menuIter map<string, TreeMenu*>::iterator
    
    class TreeMenu
    {
    public:
        TreeMenu();
        TreeMenu(string _name);
        ~TreeMenu(void);
        void doInput();
        void setName(string name);
        void renderFull(int x, int y);
    
        TreeMenu* node(string name);
        TreeMenu* node(TreeMenu *node);
    
    protected:
        string name;
        map<string, TreeMenu*> children;
        bool active;
    
    private:
        virtual bool selectable();
        virtual void handleKey(int keyCode);
        virtual void render(int x, int y, bool selected);
        
        static void renderDefaultCell(int x, int y, string text, bool selected = false);
    
        static bool keys[5];
    
        menuIter selectedItem;
    };
    
    class Leaf : public TreeMenu
    {
    public:
        Leaf(string name) : TreeMenu(name) {}
    private:
        virtual void handleKey(int keyCode);
        virtual void render(int x, int y, bool selected);
        virtual string valueToString() { return ""; }
        virtual void incrementValue() {}
        virtual void decrementValue() {}
        bool selectable() { return true; }
    };
    
    class BoolLeaf : public Leaf
    {
    public:
        BoolLeaf(string name, bool* addr) : Leaf(name), val(addr) { }
        virtual string valueToString();
        virtual void incrementValue();
        virtual void decrementValue();
    private:
        bool *val;
    };
    
    class FloatLeaf : public Leaf
    {
    public:
        FloatLeaf(string name, float* addr, float _min = 0, float _max = 1, float _increment = 0.1)
            : Leaf(name), val(addr), minVal(_min), maxVal(_max), increment(_increment) { }
        virtual string valueToString();
        virtual void incrementValue();
        virtual void decrementValue();
    private:
        float *val, minVal, maxVal, increment;
    Implentation
    Code:
    #include "TreeMenu.h"
    #include "Loads.h"
    
    bool TreeMenu::keys[5];
    
    TreeMenu::TreeMenu()
    {
        TreeMenu("");
    }
    
    TreeMenu::TreeMenu(string _name) : name(_name)
    {
        memset( TreeMenu::keys, false, sizeof(bool) * 5 );
        selectedItem = children.begin();
        active = false;
    }
    
    void TreeMenu::setName(string name)
    {
        this->name = name;
    }
    
    TreeMenu::~TreeMenu(void)
    {
    }
    
    void TreeMenu::doInput()
    {
        bool curKeyState[5];
        curKeyState[MENU_KEY_LEFT]    = (GetAsyncKeyState(VK_LEFT) & 0x8000) != 0;
        curKeyState[MENU_KEY_RIGHT]    = (GetAsyncKeyState(VK_RIGHT) & 0x8000) != 0;
        curKeyState[MENU_KEY_UP]    = (GetAsyncKeyState(VK_UP) & 0x8000) != 0;
        curKeyState[MENU_KEY_DOWN]    = (GetAsyncKeyState(VK_DOWN) & 0x8000) != 0;
        curKeyState[MENU_ACTIVATE]    = (GetAsyncKeyState(VK_HOME) & 0x8000) != 0;
    
        for (int key = MENU_KEY_LEFT; key <= MENU_ACTIVATE; key++)
        {
            if (TreeMenu::keys[key] && !curKeyState[key]) handleKey(key);
            TreeMenu::keys[key] = curKeyState[key];
        }
    }
    
    void TreeMenu::handleKey(int keyCode)
    {
        // Handle activate at root only
        if (keyCode ==     MENU_ACTIVATE) {
            active = !active;
            return;
        }
        if (selectedItem->second->active)
            return selectedItem->second->handleKey(keyCode);
        switch (keyCode)
        {
        case MENU_KEY_LEFT:
            active = false;
            break;
        case MENU_KEY_RIGHT:
            if (selectedItem->second->selectable())
                selectedItem->second->active = true;
            break;
        case MENU_KEY_UP:
            if (selectedItem == children.begin())
                selectedItem = children.end();
            selectedItem--;
            break;
        case MENU_KEY_DOWN:
            if (++selectedItem == children.end())
                selectedItem = children.begin();
            break;
        }
    }
    
    TreeMenu* TreeMenu::node(string name)
    {
        if (children.find(name) != children.end())
            return children[name];
        return node(new TreeMenu(name));
    }
    
    TreeMenu* TreeMenu::node(TreeMenu *node)
    {
        if (children.find(node->name) != children.end())
            return children[node->name];
        children[node->name] = node;
        selectedItem = children.begin();
        return node;
    }
    
    bool TreeMenu::selectable()
    {
        return children.begin() != children.end();
    }
    
    void TreeMenu::renderDefaultCell(int x, int y, string text, bool selected)
    {
        g_pD3DRenderer->DrawRect(x, y, x + MENU_ROW_WIDTH, y + MENU_ROW_HEIGHT,
                selected ? MENU_SELECTED : MENU_BACKGROUND);
        g_pD3DRenderer->PrintStringAligned(x + MENU_ROW_PADLEFT, y + MENU_ROW_HEIGHT/2, tWhite, DT_VCENTER, text.c_str());
    }
    
    void TreeMenu::renderFull(int x, int y)
    {
        if (!active) return;
        renderDefaultCell(x, y, name);
        
        for (menuIter it = children.begin(); it != children.end(); it++)
        {
            y += MENU_ROW_HEIGHT + MENU_CELL_MARGIN;
            it->second->render(x, y, it == selectedItem);
        }
    }
    
    void TreeMenu::render(int x, int y, bool selected)
    {
        renderDefaultCell(x, y, name, selected);
        
        if (active) {
            x += MENU_ROW_WIDTH + MENU_CELL_MARGIN;
            for (menuIter it = children.begin(); it != children.end(); it++)
            {
                it->second->render(x, y, it == selectedItem);
                y += MENU_ROW_HEIGHT + MENU_CELL_MARGIN;
            }
        }
    }
    void Leaf::handleKey(int keyCode)
    {
        switch (keyCode)
        {
        case MENU_KEY_LEFT:
            active = false;
            break;
        case MENU_KEY_UP:
            incrementValue();
            break;
        case MENU_KEY_DOWN:
            decrementValue();
            break;
        }
    }
    
    void Leaf::render(int x, int y, bool selected)
    {
        g_pD3DRenderer->DrawRect(x, y, x + NODE_NAME_WIDTH, y + MENU_ROW_HEIGHT,
                selected ? MENU_SELECTED : MENU_BACKGROUND);
        g_pD3DRenderer->PrintStringAligned(x + MENU_ROW_PADLEFT, y + MENU_ROW_HEIGHT/2, tWhite, DT_VCENTER, name.c_str());
    
        x += NODE_NAME_WIDTH + MENU_CELL_MARGIN;
    
        g_pD3DRenderer->DrawRect(x, y, x + NODE_VALUE_WIDTH, y + MENU_ROW_HEIGHT,
                active ? MENU_SELECTED : MENU_BACKGROUND);
    
        g_pD3DRenderer->PrintStringAligned(x + MENU_ROW_PADLEFT/2, y + MENU_ROW_HEIGHT/2, tWhite, DT_VCENTER,
                valueToString().c_str());
    }
    
    string BoolLeaf::valueToString()
    {
        if (*val) return "On";
        return "Off";
    }
    void BoolLeaf::incrementValue()
    {
        *val = !*val;
    }
    void BoolLeaf::decrementValue()
    {
        *val = !*val;
    }
    
    string FloatLeaf::valueToString()
    {
        char buf[16];
        sprintf_s(buf, "%.2f", *val);
        return buf;
    }
    
    void FloatLeaf::incrementValue()
    {
        *val += increment;
        if (*val > maxVal) *val = minVal;
    }
    void FloatLeaf::decrementValue()
    {
        *val -= increment;
        if (*val < minVal) *val = maxVal;
    }
    };

  2. #2
    Join Date
    Jul 2009
    Posts
    112
    Thanks
    13
    Thanked 19 Times in 12 Posts
    Rep Power
    0

    Re: finally a menu class that doesn't suck

    The code is very good, but I do not like the simple design, but that's my opinion.

  3. #3
    Join Date
    Sep 2004
    Location
    Capital, Ism
    Posts
    321
    Thanks
    3
    Thanked 49 Times in 9 Posts
    Rep Power
    101

    Re: finally a menu class that doesn't suck

    override render and you can have any design you want!

  4. #4
    Join Date
    Jul 2009
    Posts
    112
    Thanks
    13
    Thanked 19 Times in 12 Posts
    Rep Power
    0

    Re: finally a menu class that doesn't suck

    Quote Originally Posted by AkaneTendo View Post
    override render and you can have any design you want!
    Thats true.

  5. #5
    Join Date
    Feb 2005
    Location
    OoOoOoooo0oOO
    Posts
    745
    Thanks
    18
    Thanked 16 Times in 8 Posts
    Rep Power
    118

    Re: finally a menu class that doesn't suck

    You alloc memory for every node, but it is never freed. You should consider using a shared_ptr for your nodes to ensure you don't leak memory.

  6. #6
    Join Date
    Jan 2008
    Location
    Kynox's sister's bedroom
    Posts
    910
    Thanks
    6
    Thanked 42 Times in 29 Posts
    Rep Power
    104

    Re: finally a menu class that doesn't suck

    Lolz.

  7. The Following User Says Thank You to Chazwazza For This Useful Post:

    Absolution (12-14-2010)

  8. #7
    Join Date
    Sep 2004
    Location
    Capital, Ism
    Posts
    321
    Thanks
    3
    Thanked 49 Times in 9 Posts
    Rep Power
    101

    Re: finally a menu class that doesn't suck

    pffff memory leaks? who does that. anyway you can do some interesting things with templating:
    Code:
    template <typename T> class EnumLeaf : public Leaf
    {
    public:
    	EnumLeaf(string name, map<string, T> *_enumMap, T *_val);
    	virtual string valueToString();
    	virtual void incrementValue();
    	virtual void decrementValue();
    	virtual void renderValue(int x, int y, bool active);
    private:
    	map<string, T> *enumMap;
    	typename map<string, T>::iterator selected;
    	T *val;
    };


    edit: fine, fine. if you insist:

    Code:
    TreeMenu::~TreeMenu(void)
    {
    	for (menuIter it = children.begin(); it != children.end(); it++)
    		delete *it;
    }

  9. #8
    Join Date
    Aug 2007
    Posts
    245
    Thanks
    13
    Thanked 5 Times in 4 Posts
    Rep Power
    68

    Re: finally a menu class that doesn't suck

    lol, yay for templates, told ya was good idea to look into those, Nice menu bro.

  10. #9
    Join Date
    Sep 2006
    Location
    UK
    Posts
    1,044
    Thanks
    69
    Thanked 296 Times in 126 Posts
    Rep Power
    130

    Re: finally a menu class that doesn't suck

    Instead of having a child class for each data type (bool,float etc etc), you should template it so it can use any type without having to write out the entire class again

    Code:
    template <typename TY>
    class Leaf : public TreeMenu
    {
    public:
        Leaf(string name) : TreeMenu(name) {}
    private:
        virtual void handleKey(int keyCode);
        virtual void render(int x, int y, bool selected);
        virtual string valueToString() { return ""; }
        virtual void incrementValue() {}
        virtual void decrementValue() {}
        bool selectable() { return true; }
    
    private:
    TY m_Value;
    };

  11. #10
    Join Date
    Sep 2004
    Location
    Capital, Ism
    Posts
    321
    Thanks
    3
    Thanked 49 Times in 9 Posts
    Rep Power
    101

    Re: finally a menu class that doesn't suck

    You can't without defining what increment and decrement mean for each individual primitive.

    edit: it almost works for bools, but if you want to make increment and decrement general purpose, you will have problems with bool val += val, since operator+(bool, ...) is not strictly speaking defined and your compiler will complain. i'm not sure if that is a good idea but it is certainly possible

    edit2: you can get around this with partial sepcialization of templates, which I suppose is still better than subclassing. this is what I did:

    Code:
    template <typename T> class ValueLeaf : public Leaf
    {
    public:
    	ValueLeaf(string name, T* addr, T _min = 0, T _max = 1, T _increment = 1 )
    		: Leaf(name), val(addr), minVal(_min), maxVal(_max), increment(_increment) { }
    	virtual string valueToString();
    	virtual void incrementValue();
    	virtual void decrementValue();
    private:
    	T *val, minVal, maxVal, increment;
    };
    Code:
    template class ValueLeaf<bool>;
    template class ValueLeaf<short>;
    template class ValueLeaf<int>;
    template class ValueLeaf<float>;
    template class ValueLeaf<double>;
    template <> void ValueLeaf<bool>::incrementValue()
    {
    	*val = !val;
    }
    template <> void ValueLeaf<bool>::decrementValue()
    {
    	*val = !val;
    }
    template <> string ValueLeaf<bool>::valueToString()
    {
    	return *val ? "On" : "Off";
    }
    template <> string ValueLeaf<short>::valueToString()
    {
    	char buf[16];
    	sprintf_s(buf, "%hd", *val);
    	return buf;
    }
    template <> string ValueLeaf<int>::valueToString()
    {
    	char buf[16];
    	sprintf_s(buf, "%d", *val);
    	return buf;
    }
    Code:
    		menu->node("aimbot")->node(new ValueLeaf<bool>("no shake", &noshake));
    		menu->node("aimbot")->node(new ValueLeaf<float>("FOV", &aimFOV, 0, 180, 1));

  12. #11
    Join Date
    Sep 2006
    Location
    UK
    Posts
    1,044
    Thanks
    69
    Thanked 296 Times in 126 Posts
    Rep Power
    130

    Re: finally a menu class that doesn't suck

    Lol yes you can, just define some other member variables such as TY m_Increment, m_Max, m_Min. The compiler knows how to handle different types and how they are manipulated so you don't need to worry about overloading operators (assuming you are casting them correctly in the first place).

    Here's some code I used in SquareSpace. Stopped using it now though because using maps for cvars is too slow but the concept is what I think you were aiming for.

    Code:
    template< typename T >
    class CVar
    {
    	friend class CVarMgr;
    public:
    	CVar( PCHAR name, T value, T max, T min, T step )
    	{
    		m_Value = value;
    		m_Max = max;
    		m_Min = min;
    		m_Step = step;
    		m_pszName = name;
    	}
    	~CVar( );
    
    	PCHAR m_pszName;
    	T m_Max, m_Min, m_Value, m_Step;
    };
    
    class CVarMgr
    {
    public:
    	CVarMgr( );
    	~CVarMgr( );
    
    public:
    	void Initialize( VOID );
    
    	template<typename T>VOID Add( PCHAR name, FLOAT value, FLOAT max, FLOAT min, FLOAT step )
    	{
    		CVar<T> * pVar = new CVar<T>( name, value, max, min, step );
    
    		if( !pVar )
    		{
    			WriteLog( "CVarMgr::Add() Could not create new CVar named \"%s\"", name );
    			return;
    		}
    
    		m_mVarMap[ string( name ) ] = pVar;
    	}
    
    	template<typename T>CVar<T> * GetVar( PCHAR name )
    	{
    		return (CVar<T>*)( m_mVarMap[ string( name ) ] );
    	}
    
    	bool IsInList( PCHAR name )
    	{
    		return ( m_mVarMap[ string( name ) ] != NULL );
    	}
    
    	template<typename T> T GetValue( PCHAR name )
    	{
    		CVar<T> * pVar = (CVar<T>*)( m_mVarMap[ string( name ) ] );
    
    		if( !pVar )
    		{
    			gConsole.Print( "^rBad pointer for var name \"^w%s^r\"", name );
    			pVar = (CVar<T>*)( m_mVarMap[ "NULLVAR" ] );
    		}
    
    		return ( T )( pVar->m_Value );
    	}
    
    	void Shutdown( void );
    
    private:
    	map< string, void* > m_mVarMap;
    }
    So you would use that like:

    Add<FLOAT>( "aimbot", 1, 1, 0, 1 );

    Then

    gVarMgr.GetValue<FLOAT>( "aimbot" );

  13. #12
    Join Date
    Jan 2008
    Location
    Kynox's sister's bedroom
    Posts
    910
    Thanks
    6
    Thanked 42 Times in 29 Posts
    Rep Power
    104

    Re: finally a menu class that doesn't suck

    I <3 watching C programmers attempt to use C++. Always entertaining.

    *grabs popcorn*

  14. #13
    Join Date
    Sep 2006
    Location
    UK
    Posts
    1,044
    Thanks
    69
    Thanked 296 Times in 126 Posts
    Rep Power
    130

    Re: finally a menu class that doesn't suck

    Yawn change the record cypher

  15. #14
    Join Date
    Sep 2004
    Location
    Capital, Ism
    Posts
    321
    Thanks
    3
    Thanked 49 Times in 9 Posts
    Rep Power
    101

    Re: finally a menu class that doesn't suck

    bool + bool is not defined

  16. #15
    Join Date
    Jan 2008
    Location
    Kynox's sister's bedroom
    Posts
    910
    Thanks
    6
    Thanked 42 Times in 29 Posts
    Rep Power
    104

    Re: finally a menu class that doesn't suck

    Quote Originally Posted by Chod View Post
    Yawn change the record cypher
    http://www.youtube.com/watch?v=WNKk0dJUk8U

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Similar Threads

  1. Basic Menu Class
    By Vrillon in forum DirectX/D3D
    Replies: 22
    Last Post: 05-07-2011, 01:31 PM
  2. Release Simple D3D9 Menu Class
    By plux in forum DirectX/D3D
    Replies: 5
    Last Post: 06-28-2010, 11:19 AM
  3. Trouble with cLTFX menu class
    By Sucrose in forum DirectX/D3D
    Replies: 1
    Last Post: 01-25-2009, 06:10 PM
  4. (help)cLTFX menu class
    By Knuckles in forum DirectX/D3D
    Replies: 3
    Last Post: 11-26-2008, 02:50 AM
  5. Menu class
    By WinBoot32 in forum Miscellaneous
    Replies: 13
    Last Post: 12-23-2006, 04:29 AM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •