(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
TreeMenu headerCode: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)); } };
ImplentationCode:#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;
};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; }


Reply With Quote



