Skip to content

Instantly share code, notes, and snippets.

@mohiji
Created March 19, 2011 03:47
Show Gist options
  • Save mohiji/877204 to your computer and use it in GitHub Desktop.
Save mohiji/877204 to your computer and use it in GitHub Desktop.
Egoboo's main menu implementation from Summer 2005
// menu.c
// Egoboo, Copyright (C) 2000 Aaron Bishop
#include "egoboo.h"
#include "Ui.h"
#include "Menu.h"
#include "Log.h"
// TEMPORARY!
#define NET_DONE_SENDING_FILES 10009
#define NET_NUM_FILES_TO_SEND 10010
//--------------------------------------------------------------------------------------------
// New menu code
//--------------------------------------------------------------------------------------------
enum MenuStates
{
MM_Begin,
MM_Entering,
MM_Running,
MM_Leaving,
MM_Finish,
};
static int selectedPlayer = 0;
static int selectedModule = 0;
/* Copyright text variables. Change these to change how the copyright text appears */
const char copyrightText[] = "Egoboo 2.x\nhttp://www.mohiji.org/projects/egoboo2x";
//const char copyrightText[] =
//"Welcome to Egoboo!\nhttp://home.no.net/egoboo\nhttp://egoboo.taxiwin.com\nVersion 2.3.6";
static int copyrightLeft = 0;
static int copyrightTop = 0;
/* Button labels. Defined here for consistency's sake, rather than leaving them as constants */
const char *mainMenuButtons[] = {
"Single Player",
"Multi-player",
"Options",
"Quit",
""
};
const char *singlePlayerButtons[] = {
"New Player",
"Load Saved Player",
"Back",
""
};
/* Button position for the "easy" menus, like the main one */
static int buttonLeft = 0;
static int buttonTop = 0;
static bool_t startNewPlayer = bfalse;
/* The font used for drawing text. It's smaller than the button font */
Font *menuFont = NULL;
// "Slidy" buttons used in some of the menus. They're shiny.
struct {
float lerp;
int top;
int left;
char **buttons;
}SlidyButtonState;
static void initSlidyButtons(float lerp, const char *buttons[])
{
SlidyButtonState.lerp = lerp;
SlidyButtonState.buttons = (char**)buttons;
}
static void updateSlidyButtons(float deltaTime)
{
SlidyButtonState.lerp += (deltaTime * 1.5f);
}
static void drawSlidyButtons()
{
int i;
for (i = 0; SlidyButtonState.buttons[i][0] != 0; i++)
{
int x = buttonLeft - (360 - i * 35) * SlidyButtonState.lerp;
int y = buttonTop + (i * 35);
ui_doButton(UI_Nothing, SlidyButtonState.buttons[i], x, y, 200, 30);
}
}
/** initMenus
* Loads resources for the menus, and figures out where things should
* be positioned. If we ever allow changing resolution on the fly, this
* function will have to be updated/called more than once.
*/
int initMenus()
{
int i;
menuFont = fnt_loadFont("basicdat/Negatori.ttf", 18);
if (!menuFont)
{
log_error("Could not load the menu font!\n");
return 0;
}
// Figure out where to draw the buttons
buttonLeft = 40;
buttonTop = displaySurface->h - 20;
for (i = 0; mainMenuButtons[i][0] != 0; i++)
{
buttonTop -= 35;
}
// Figure out where to draw the copyright text
copyrightLeft = 0;
copyrightLeft = 0;
fnt_getTextBoxSize(menuFont, copyrightText, 20, &copyrightLeft, &copyrightTop);
// Draw the copyright text to the right of the buttons
copyrightLeft = 280;
// And relative to the bottom of the screen
copyrightTop = displaySurface->h - copyrightTop - 20;
return 1;
}
int doMainMenu(float deltaTime)
{
static int menuState = MM_Begin;
static GLTexture background;
static float lerp;
static int menuChoice = 0;
int result = 0;
switch(menuState)
{
case MM_Begin:
// set up menu variables
GLTexture_Load(&background, "basicdat/menu/menu_advent.bmp");
menuChoice = 0;
menuState = MM_Entering;
initSlidyButtons(1.0f, mainMenuButtons);
// let this fall through into MM_Entering
case MM_Entering:
// do buttons sliding in animation, and background fading in
// background
glColor4f(1, 1, 1, 1 -SlidyButtonState.lerp);
ui_drawImage(0, &background, (displaySurface->w - background.imgWidth), 0, 0, 0);
// "Copyright" text
fnt_drawTextBox(menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20);
drawSlidyButtons();
updateSlidyButtons(-deltaTime);
// Let lerp wind down relative to the time elapsed
if (SlidyButtonState.lerp <= 0.0f)
{
menuState = MM_Running;
}
break;
case MM_Running:
// Do normal run
// Background
glColor4f(1, 1, 1, 1);
ui_drawImage(0, &background, (displaySurface->w - background.imgWidth), 0, 0, 0);
// "Copyright" text
fnt_drawTextBox(menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20);
// Buttons
if(ui_doButton(1, mainMenuButtons[0], buttonLeft, buttonTop, 200, 30) == 1)
{
// begin single player stuff
menuChoice = 1;
}
if(ui_doButton(2, mainMenuButtons[1], buttonLeft, buttonTop + 35, 200, 30) == 1)
{
// begin multi player stuff
menuChoice = 2;
}
if(ui_doButton(3, mainMenuButtons[2], buttonLeft, buttonTop + 35 * 2, 200, 30) == 1)
{
// go to options menu
menuChoice = 3;
}
if(ui_doButton(4, mainMenuButtons[3], buttonLeft, buttonTop + 35 * 3, 200, 30) == 1)
{
// quit game
menuChoice = 4;
}
if(menuChoice != 0)
{
menuState = MM_Leaving;
initSlidyButtons(0.0f, mainMenuButtons);
}
break;
case MM_Leaving:
// Do buttons sliding out and background fading
// Do the same stuff as in MM_Entering, but backwards
glColor4f(1, 1, 1, 1 - SlidyButtonState.lerp);
ui_drawImage(0, &background, (displaySurface->w - background.imgWidth), 0, 0, 0);
// "Copyright" text
fnt_drawTextBox(menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20);
// Buttons
drawSlidyButtons();
updateSlidyButtons(deltaTime);
if(SlidyButtonState.lerp >= 1.0f) {
menuState = MM_Finish;
}
break;
case MM_Finish:
// Free the background texture; don't need to hold onto it
GLTexture_Release(&background);
menuState = MM_Begin; // Make sure this all resets next time doMainMenu is called
// Set the next menu to load
result = menuChoice;
break;
};
return result;
}
int doSinglePlayerMenu(float deltaTime)
{
static int menuState = MM_Begin;
static GLTexture background;
static int menuChoice;
int result = 0;
switch(menuState)
{
case MM_Begin:
// Load resources for this menu
GLTexture_Load(&background, "basicdat/menu/menu_gnome.bmp");
menuChoice = 0;
menuState = MM_Entering;
initSlidyButtons(1.0f, singlePlayerButtons);
// Let this fall through
case MM_Entering:
glColor4f(1, 1, 1, 1 - SlidyButtonState.lerp);
// Draw the background image
ui_drawImage(0, &background, displaySurface->w - background.imgWidth, 0, 0, 0);
// "Copyright" text
fnt_drawTextBox(menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20);
drawSlidyButtons();
updateSlidyButtons(-deltaTime);
if (SlidyButtonState.lerp <= 0.0f)
menuState = MM_Running;
break;
case MM_Running:
// Draw the background image
ui_drawImage(0, &background, displaySurface->w - background.imgWidth, 0, 0, 0);
// "Copyright" text
fnt_drawTextBox(menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20);
// Buttons
if (ui_doButton(1, singlePlayerButtons[0], buttonLeft, buttonTop, 200, 30) == 1)
{
menuChoice = 1;
}
if (ui_doButton(2, singlePlayerButtons[1], buttonLeft, buttonTop + 35, 200, 30) == 1)
{
menuChoice = 2;
}
if (ui_doButton(3, singlePlayerButtons[2], buttonLeft, buttonTop + 35 * 2, 200, 30) == 1)
{
menuChoice = 3;
}
if(menuChoice != 0)
{
menuState = MM_Leaving;
initSlidyButtons(0.0f, singlePlayerButtons);
}
break;
case MM_Leaving:
// Do buttons sliding out and background fading
// Do the same stuff as in MM_Entering, but backwards
glColor4f(1, 1, 1, 1 - SlidyButtonState.lerp);
ui_drawImage(0, &background, displaySurface->w - background.imgWidth, 0, 0, 0);
// "Copyright" text
fnt_drawTextBox(menuFont, copyrightText, copyrightLeft, copyrightTop, 0, 0, 20);
drawSlidyButtons();
updateSlidyButtons(deltaTime);
if(SlidyButtonState.lerp >= 1.0f)
{
menuState = MM_Finish;
}
break;
case MM_Finish:
// Release the background texture
GLTexture_Release(&background);
// Set the next menu to load
result = menuChoice;
// And make sure that if we come back to this menu, it resets
// properly
menuState = MM_Begin;
}
return result;
}
// TODO: I totally fudged the layout of this menu by adding an offset for when
// the game isn't in 640x480. Needs to be fixed.
int doChooseModule(float deltaTime)
{
static int menuState = MM_Begin;
static int startIndex;
static GLTexture background;
static int validModules[MAXMODULE];
static int numValidModules;
static int moduleMenuOffsetX;
static int moduleMenuOffsetY;
int result = 0;
int i, x, y;
char txtBuffer[128];
switch(menuState)
{
case MM_Begin:
// Load font & background
GLTexture_Load(&background, "basicdat/menu/menu_sleepy.bmp");
startIndex = 0;
selectedModule = -1;
// Find the module's that we want to allow loading for. If startNewPlayer
// is true, we want ones that don't allow imports (e.g. starter modules).
// Otherwise, we want modules that allow imports
memset(validModules, 0, sizeof(int) * MAXMODULE);
numValidModules = 0;
for (i = 0;i < globalnummodule; i++)
{
if (modimportamount[i] == 0)
{
if (startNewPlayer)
{
validModules[numValidModules] = i;
numValidModules++;
}
} else
{
if (!startNewPlayer)
{
validModules[numValidModules] = i;
numValidModules++;
}
}
}
// Figure out at what offset we want to draw the module menu.
moduleMenuOffsetX = (displaySurface->w - 640) / 2;
moduleMenuOffsetY = (displaySurface->h - 480) / 2;
menuState = MM_Entering;
// fall through...
case MM_Entering:
menuState = MM_Running;
// fall through for now...
case MM_Running:
// Draw the background
glColor4f(1, 1, 1, 1);
x = (displaySurface->w / 2) - (background.imgWidth / 2);
y = displaySurface->h - background.imgHeight;
ui_drawImage(0, &background, x, y, 0, 0);
// Fudged offset here.. DAMN! Doesn't work, as the mouse tracking gets skewed
// I guess I'll do it the uglier way
//glTranslatef(moduleMenuOffsetX, moduleMenuOffsetY, 0);
// Draw the arrows to pick modules
if(ui_doButton(1051, "<-", moduleMenuOffsetX + 20, moduleMenuOffsetY + 74, 30, 30))
{
startIndex--;
}
if(ui_doButton(1052, "->", moduleMenuOffsetX + 590, moduleMenuOffsetY + 74, 30, 30))
{
startIndex++;
if(startIndex + 3 >= numValidModules)
{
startIndex = numValidModules - 3;
}
}
// Clamp startIndex to 0
startIndex = (startIndex > 0) ? startIndex : 0;
// Draw buttons for the modules that can be selected
x = 93;
y = 20;
for(i = startIndex; i < (startIndex + 3) && i < numValidModules; i++)
{
if(ui_doImageButton(i, &TxTitleImage[validModules[i]],
moduleMenuOffsetX + x, moduleMenuOffsetY + y, 138, 138))
{
selectedModule = i;
}
x += 138 + 20; // Width of the button, and the spacing between buttons
}
// Draw an unused button as the backdrop for the text for now
ui_drawButton(0xFFFFFFFF, moduleMenuOffsetX + 21, moduleMenuOffsetY + 173, 291, 230);
// And draw the next & back buttons
if (ui_doButton(53, "Select Module",
moduleMenuOffsetX + 327, moduleMenuOffsetY + 173, 200, 30))
{
// go to the next menu with this module selected
selectedModule = validModules[selectedModule];
menuState = MM_Leaving;
}
if (ui_doButton(54, "Back", moduleMenuOffsetX + 327, moduleMenuOffsetY + 208, 200, 30))
{
// Signal doMenu to go back to the previous menu
selectedModule = -1;
menuState = MM_Leaving;
}
// Draw the text description of the selected module
if(selectedModule > -1)
{
y = 173 + 5;
x = 21 + 5;
glColor4f(1, 1, 1, 1);
fnt_drawText(menuFont, moduleMenuOffsetX + x, moduleMenuOffsetY + y,
modlongname[validModules[selectedModule]]);
y += 20;
snprintf(txtBuffer, 128, "Difficulty: %s", modrank[validModules[selectedModule]]);
fnt_drawText(menuFont, moduleMenuOffsetX + x, moduleMenuOffsetY + y, txtBuffer);
y += 20;
if(modmaxplayers[validModules[selectedModule]] > 1)
{
if(modminplayers[validModules[selectedModule]] == modmaxplayers[validModules[selectedModule]])
{
snprintf(txtBuffer, 128, "%d Players", modminplayers[validModules[selectedModule]]);
} else
{
snprintf(txtBuffer, 128, "%d - %d Players", modminplayers[validModules[selectedModule]], modmaxplayers[validModules[selectedModule]]);
}
} else
{
snprintf(txtBuffer, 128, "Starter Module");
}
fnt_drawText(menuFont, moduleMenuOffsetX + x, moduleMenuOffsetY + y, txtBuffer);
y += 20;
// And finally, the summary
snprintf(txtBuffer, 128, "modules/%s/gamedat/menu.txt", modloadname[validModules[selectedModule]]);
get_module_summary(txtBuffer);
for(i = 0;i < SUMMARYLINES;i++)
{
fnt_drawText(menuFont, moduleMenuOffsetX + x, moduleMenuOffsetY + y, modsummary[i]);
y += 20;
}
}
break;
case MM_Leaving:
menuState = MM_Finish;
// fall through for now
case MM_Finish:
GLTexture_Release(&background);
menuState = MM_Begin;
if(selectedModule == -1)
{
result = -1;
} else
{
// Save the name of the module that we've picked
strncpy(pickedmodule, modloadname[selectedModule], 64);
// If the module allows imports, return 1. Else, return 2
if(modimportamount[selectedModule] > 0)
{
importvalid = btrue;
importamount = modimportamount[selectedModule];
result = 1;
}
else
{
importvalid = bfalse;
result = 2;
}
exportvalid = modallowexport[selectedModule];
playeramount = modmaxplayers[selectedModule];
respawnvalid = bfalse;
respawnanytime = bfalse;
if(modrespawnvalid[selectedModule]) respawnvalid = btrue;
if(modrespawnvalid[selectedModule] == ANYTIME) respawnanytime = btrue;
rtscontrol = bfalse;
}
break;
}
return result;
}
int doChoosePlayer(float deltaTime)
{
static int menuState = MM_Begin;
static GLTexture background;
int result = 0;
int numVertical, numHorizontal;
int i, j, x, y;
int player;
char srcDir[64], destDir[64];
switch(menuState)
{
case MM_Begin:
selectedPlayer = 0;
GLTexture_Load(&background, "basicdat/menu/menu_sleepy.bmp");
// load information for all the players that could be imported
check_player_import("players");
menuState = MM_Entering;
// fall through
case MM_Entering:
menuState = MM_Running;
// fall through
case MM_Running:
// Figure out how many players we can show without scrolling
numVertical = 6;
numHorizontal = 2;
// Draw the player selection buttons
// I'm being tricky, and drawing two buttons right next to each other
// for each player: one with the icon, and another with the name. I'm
// given those buttons the same ID, so they'll act like the same button.
player = 0;
x = 20;
for(j = 0;j < numHorizontal && player < numloadplayer;j++)
{
y = 20;
for(i = 0;i < numVertical && player < numloadplayer;i++)
{
if(ui_doImageButtonWithText(player, &TxIcon[player], loadplayername[player], x, y, 175, 42))
{
selectedPlayer = player;
}
player++;
y += 47;
}
x += 180;
}
// Draw the background
x = (displaySurface->w / 2) - (background.imgWidth / 2);
y = displaySurface->h - background.imgHeight;
ui_drawImage(0, &background, x, y, 0, 0);
// Buttons for going ahead
if (ui_doButton(100, "Play!", 40, 350, 200, 30))
{
menuState = MM_Leaving;
}
if (ui_doButton(101, "Back", 40, 385, 200, 30))
{
selectedPlayer = -1;
menuState = MM_Leaving;
}
break;
case MM_Leaving:
menuState = MM_Finish;
// fall through
case MM_Finish:
GLTexture_Release(&background);
menuState = MM_Begin;
if(selectedPlayer == -1) result = -1;
else
{
// Build the import directory
// I'm just allowing 1 player for now...
empty_import_directory();
fs_createDirectory("import");
localcontrol[0] = INPUTKEY | INPUTMOUSE | INPUTJOYA;
localslot[0] = localmachine * 9;
// Copy the character to the import directory
sprintf(srcDir, "players/%s", loadplayerdir[selectedPlayer]);
sprintf(destDir, "import/temp%04d.obj", localslot[0]);
fs_copyDirectory(srcDir, destDir);
// Copy all of the character's items to the import directory
for(i = 0;i < 8;i++)
{
sprintf(srcDir, "players/%s/%d.obj", loadplayerdir[selectedPlayer], i);
sprintf(destDir, "import/temp%04d.obj", localslot[0] + i + 1);
fs_copyDirectory(srcDir, destDir);
}
numimport = 1;
result = 1;
}
break;
}
return result;
}
int doShowMenuResults(float deltaTime)
{
int x, y;
char text[128];
Font *font;
SDL_Surface *screen = SDL_GetVideoSurface();
font = ui_getFont();
ui_drawButton(0xFFFFFFFF, 30, 30, screen->w - 60, screen->h - 65);
x = 35;
y = 35;
glColor4f(1, 1, 1, 1);
snprintf(text, 128, "Module selected: %s", modloadname[selectedModule]);
fnt_drawText(font, x, y, text);
y += 35;
if(importvalid)
{
snprintf(text, 128, "Player selected: %s", loadplayername[selectedPlayer]);
} else
{
snprintf(text, 128, "Starting a new player.");
}
fnt_drawText(font, x, y, text);
return 1;
}
int doNotImplemented(float deltaTime)
{
int x, y;
int w, h;
char notImplementedMessage[] = "Not implemented yet! Check back soon!";
fnt_getTextSize(ui_getFont(), notImplementedMessage, &w, &h);
w += 50; // add some space on the sides
x = displaySurface->w / 2 - w / 2;
y = displaySurface->h / 2 - 17;
if(ui_doButton(1, notImplementedMessage, x, y, w, 30) == 1)
{
return 1;
}
return 0;
}
// All the different menus. yay!
enum
{
MainMenu,
SinglePlayer,
MultiPlayer,
ChooseModule,
ChoosePlayer,
TestResults,
Options,
VideoOptions,
AudioOptions,
InputOptions,
NewPlayer,
LoadPlayer,
HostGame,
JoinGame,
};
int doMenu(float deltaTime)
{
static int whichMenu = MainMenu;
static int lastMenu = MainMenu;
int result = 0;
switch(whichMenu)
{
case MainMenu:
result = doMainMenu(deltaTime);
if(result != 0)
{
lastMenu = MainMenu;
if(result == 1) whichMenu = SinglePlayer;
else if(result == 2) whichMenu = MultiPlayer;
else if(result == 3) whichMenu = Options;
else if(result == 4) return -1; // need to request a quit somehow
}
break;
case SinglePlayer:
result = doSinglePlayerMenu(deltaTime);
if(result != 0)
{
lastMenu = SinglePlayer;
if(result == 1)
{
whichMenu = ChooseModule;
startNewPlayer = btrue;
} else if(result == 2)
{
whichMenu = ChooseModule;
startNewPlayer = bfalse;
}
else if(result == 3) whichMenu = MainMenu;
else whichMenu = NewPlayer;
}
break;
case ChooseModule:
result = doChooseModule(deltaTime);
if(result == -1) whichMenu = lastMenu;
else if(result == 1) whichMenu = ChoosePlayer;
else if(result == 2) whichMenu = TestResults;
break;
case ChoosePlayer:
result = doChoosePlayer(deltaTime);
if(result == -1) whichMenu = ChooseModule;
else if(result == 1) whichMenu = TestResults;
break;
case TestResults:
result = doShowMenuResults(deltaTime);
if(result != 0)
{
whichMenu = MainMenu;
return 1;
}
break;
default:
result = doNotImplemented(deltaTime);
if(result != 0)
{
whichMenu = lastMenu;
}
}
return 0;
}
// Egoboo - Ui.c
#include "Ui.h"
#include <string.h>
#include <SDL_opengl.h>
struct UiContext
{
// Tracking control focus stuff
UI_ID active;
UI_ID hot;
// Basic mouse state
int mouseX, mouseY;
int mouseReleased;
int mousePressed;
Font *defaultFont;
Font *activeFont;
};
static struct UiContext ui_context;
//--------------------------------------------------------------------------------------------
// Core functions
int ui_initialize(const char *default_font, int default_font_size)
{
memset(&ui_context, 0, sizeof(ui_context));
ui_context.active = ui_context.hot = UI_Nothing;
ui_context.defaultFont = fnt_loadFont(default_font, default_font_size);
return 1;
}
void ui_shutdown()
{
if(ui_context.defaultFont)
{
fnt_freeFont(ui_context.defaultFont);
}
memset(&ui_context, 0, sizeof(ui_context));
}
void ui_handleSDLEvent(SDL_Event *evt)
{
if(evt)
{
switch(evt->type)
{
case SDL_MOUSEBUTTONDOWN:
ui_context.mouseReleased = 0;
ui_context.mousePressed = 1;
break;
case SDL_MOUSEBUTTONUP:
ui_context.mousePressed = 0;
ui_context.mouseReleased = 1;
break;
case SDL_MOUSEMOTION:
ui_context.mouseX = evt->motion.x;
ui_context.mouseY = evt->motion.y;
break;
}
}
}
void ui_beginFrame(float deltaTime)
{
SDL_Surface *screen;
screen = SDL_GetVideoSurface();
glPushAttrib(GL_ENABLE_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, screen->w, screen->h);
// Set up an ortho projection for the gui to use. Controls are free to modify this
// later, but most of them will need this, so it's done by default at the beginning
// of a frame
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, screen->w, screen->h, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// hotness gets reset at the start of each frame
ui_context.hot = UI_Nothing;
}
void ui_endFrame()
{
// Restore the OpenGL matrices to what they were
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Re-enable any states disabled by gui_beginFrame
glPopAttrib();
// Clear input states at the end of the frame
ui_context.mousePressed = ui_context.mouseReleased = 0;
}
//--------------------------------------------------------------------------------------------
// Utility functions
int ui_mouseInside(int x, int y, int width, int height)
{
int right, bottom;
right = x + width;
bottom = y + height;
if(x <= ui_context.mouseX && y <= ui_context.mouseY && ui_context.mouseX <= right && ui_context.mouseY <= bottom)
{
return 1;
}
return 0;
}
void ui_setactive(UI_ID id)
{
ui_context.active = id;
}
void ui_sethot(UI_ID id)
{
// Only allow hotness to be set if this control, or no control is active
if(ui_context.active == id || ui_context.active == UI_Nothing)
{
ui_context.hot = id;
}
}
Font* ui_getFont()
{
return (ui_context.activeFont != NULL) ? ui_context.activeFont : ui_context.defaultFont;
}
//--------------------------------------------------------------------------------------------
// Behaviors
int ui_buttonBehavior(UI_ID id, int x, int y, int width, int height)
{
int result = 0;
// If the mouse is over the button, try and set hotness so that it can be clicked
if(ui_mouseInside(x, y, width, height))
{
ui_sethot(id);
}
// Check to see if the button gets clicked on
if(ui_context.active == id)
{
if(ui_context.mouseReleased == 1)
{
if(ui_context.hot == id) result = 1;
ui_setactive(UI_Nothing);
}
} else if(ui_context.hot == id)
{
if(ui_context.mousePressed == 1)
{
ui_setactive(id);
}
}
return result;
}
//--------------------------------------------------------------------------------------------
// Drawing
void ui_drawButton(UI_ID id, int x, int y, int width, int height)
{
// Draw the button
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
if(ui_context.active != UI_Nothing && ui_context.active == id && ui_context.hot == id)
glColor4f(0, 0, 0.9f, 0.6f);
else if(ui_context.hot != UI_Nothing && ui_context.hot == id)
glColor4f(0.9f, 0, 0, 0.6f);
else
//glColor4f(0.6f, 0, 0, 0.6f);
glColor4f(0.4f, 0, 0, 1.0f);
glVertex2i(x, y);
glVertex2i(x, y + height);
glVertex2i(x + width, y + height);
glVertex2i(x + width, y);
glEnd();
glEnable(GL_TEXTURE_2D);
}
void ui_drawImage(UI_ID id, GLTexture *img, int x, int y, int width, int height)
{
int w, h;
float x1, y1;
if(img)
{
if(width == 0 || height == 0)
{
w = img->imgWidth;
h = img->imgHeight;
} else
{
w = width;
h = height;
}
x1 = (float)img->imgWidth / img->txDimensions;
y1 = (float)img->imgHeight / img->txDimensions;
// Draw the image
glBindTexture(GL_TEXTURE_2D, img->textureID);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0, 0); glVertex2i(x, y);
glTexCoord2f(x1, 0); glVertex2i(x + w, y);
glTexCoord2f(0, y1); glVertex2i(x, y + h);
glTexCoord2f(x1, y1); glVertex2i(x + w, y + h);
glEnd();
}
}
/** ui_drawTextBox
* Draws a text string into a box, splitting it into lines according to newlines in the string.
* NOTE: Doesn't pay attention to the width/height arguments yet.
*
* text - The text to draw
* x - The x position to start drawing at
* y - The y position to start drawing at
* width - Maximum width of the box (not implemented)
* height - Maximum height of the box (not implemented)
* spacing - Amount of space to move down between lines. (usually close to your font size)
*/
void ui_drawTextBox(const char *text, int x, int y, int width, int height, int spacing)
{
Font *font = ui_getFont();
fnt_drawTextBox(font, text, x, y, width, height, spacing);
}
//--------------------------------------------------------------------------------------------
// Controls
int ui_doButton(UI_ID id, const char *text, int x, int y, int width, int height)
{
int result;
int text_w, text_h;
int text_x, text_y;
Font *font;
// Do all the logic type work for the button
result = ui_buttonBehavior(id, x, y, width, height);
// Draw the button part of the button
ui_drawButton(id, x, y, width, height);
// And then draw the text that goes on top of the button
font = ui_getFont();
if(font)
{
// find the width & height of the text to be drawn, so that it can be centered inside
// the button
fnt_getTextSize(font, text, &text_w, &text_h);
text_x = (width - text_w) / 2 + x;
text_y = (height - text_h) / 2 + y;
glColor3f(1, 1, 1);
fnt_drawText(font, text_x, text_y, text);
}
return result;
}
int ui_doImageButton(UI_ID id, GLTexture *img, int x, int y, int width, int height)
{
int result;
// Do all the logic type work for the button
result = ui_buttonBehavior(id, x, y, width, height);
// Draw the button part of the button
ui_drawButton(id, x, y, width, height);
// And then draw the image on top of it
glColor3f(1, 1, 1);
ui_drawImage(id, img, x + 5, y + 5, width - 10, height - 10);
return result;
}
int ui_doImageButtonWithText(UI_ID id, GLTexture *img, const char *text, int x, int y, int width, int height)
{
int result;
Font *font;
int text_x, text_y;
int text_w, text_h;
// Do all the logic type work for the button
result = ui_buttonBehavior(id, x, y, width, height);
// Draw the button part of the button
ui_drawButton(id, x, y, width, height);
// Draw the image part
glColor3f(1, 1, 1);
ui_drawImage(id, img, x + 5, y + 5, 0, 0);
// And draw the text next to the image
// And then draw the text that goes on top of the button
font = ui_getFont();
if(font)
{
// find the width & height of the text to be drawn, so that it can be centered inside
// the button
fnt_getTextSize(font, text, &text_w, &text_h);
text_x = img->imgWidth + 10 + x;
text_y = (height - text_h) / 2 + y;
glColor3f(1, 1, 1);
fnt_drawText(font, text_x, text_y, text);
}
return result;
}
// Egoboo - Ui.h
// The immediate-mode gui system again, refactored a bit. I'm removing the ability
// to have more than one UI context, as there isn't any point for the game to have it,
// and instead offering a global interface to one ui. Switching between fonts is
// also now an option.
#ifndef egoboo_ui_h
#define egoboo_ui_h
#include "Font.h"
#include "GLTexture.h"
#include <SDL.h>
typedef unsigned int UI_ID;
#define UI_Nothing (UI_ID)(-1)
// Initialize or shut down the ui system
int ui_initialize();
void ui_shutdown();
// Pass input data from SDL to the ui
void ui_handleSDLEvent(SDL_Event *evt);
// Allow the ui to do work that needs to be done before and after each frame
void ui_beginFrame(float deltaTime);
void ui_endFrame();
// UI controls
int ui_doButton(UI_ID id, const char *text, int x, int y, int width, int height);
int ui_doImageButton(UI_ID id, GLTexture *img, int x, int y, int width, int height);
int ui_doImageButtonWithText(UI_ID id, GLTexture *img, const char *text, int x, int y, int width, int height);
//int ui_doTextBox(UI_ID id, const char *text, int x, int y, int width, int height);
// Utility functions
int ui_mouseInside(int x, int y, int width, int height);
void ui_setActive(UI_ID id);
void ui_setHot(UI_ID id);
Font* ui_getFont();
/*****************************************************************************/
// Most users won't need to worry about stuff below here; it's mostly for
// implementing new controls.
/*****************************************************************************/
// Behaviors
int ui_buttonBehavior(UI_ID id, int x, int y, int width, int height);
// Drawing
void ui_drawButton(UI_ID id, int x, int y, int width, int height);
void ui_drawImage(UI_ID id, GLTexture *img, int x, int y, int width, int height);
void ui_drawTextBox(const char *text, int x, int y, int width, int height, int spacing);
#endif // include guard
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment