/* * Alizarin Tetris * A generic way to display menus. * * Copyright 2000, Kiri Wagstaff & Westley Weimer */ #include /* go autoconf! */ #include #include "atris.h" #include "display.h" #include "menu.h" /*************************************************************************** * menu() * Create a new menu and return it. *********************************************************************PROTO*/ ATMenu* menu(int nButtons, char** strings, int defaultchoice, Uint32 face_color0, Uint32 text_color0, Uint32 face_color1, Uint32 text_color1, int x, int y) { int i, yp; ATMenu* am = (ATMenu*)malloc(sizeof(ATMenu)); assert(am); /* Create the buttons */ am->buttons = (ATButton**)malloc(nButtons*sizeof(ATButton*)); assert(am->buttons); am->clicked = (char*)malloc(nButtons*sizeof(char)); assert(am->clicked); am->nButtons = nButtons; am->x = x; am->y = y; am->defaultchoice = defaultchoice; yp = y; Assert(nButtons >= 0); for (i=0; ibuttons[i] = button(strings[i], face_color0, text_color0, face_color1, text_color1, x, yp); yp += am->buttons[i]->area.h; /* add a separating space? */ am->clicked[i] = FALSE; } am->w = am->buttons[0]->area.w; am->h = (am->buttons[nButtons-1]->area.y - am->buttons[0]->area.y) + am->buttons[nButtons-1]->area.h; /* Set the default one */ if (defaultchoice >= 0) am->clicked[defaultchoice] = TRUE; return am; } /*************************************************************************** * show_menu() * Displays the menu. *********************************************************************PROTO*/ void show_menu(ATMenu* am) { int i; for (i=0; inButtons; i++) show_button(am->buttons[i], am->clicked[i]); } /*************************************************************************** * check_menu() * Returns the index of the button that was clicked (-1 if none). *********************************************************************PROTO*/ int check_menu(ATMenu* am, int x, int y) { int i, j; for (i=0; inButtons; i++) if (check_button(am->buttons[i], x, y)) { /* invert its clicked state */ if (am->clicked[i]) { am->clicked[i] = FALSE; /* reset the default */ am->clicked[am->defaultchoice] = TRUE; show_button(am->buttons[am->defaultchoice], am->clicked[am->defaultchoice]); } else { /* blank all others */ for (j=0; jnButtons; j++) if (i!=j && am->clicked[j]) { am->clicked[j] = FALSE; show_button(am->buttons[j], am->clicked[j]); } am->clicked[i] = TRUE; } /* redraw it */ show_button(am->buttons[i], am->clicked[i]); Debug("Button %d was clicked.\n", i); return i; } return -1; } /*************************************************************************** * delete_menu() * Deletes the menu and all of its buttons. *********************************************************************PROTO*/ void delete_menu(ATMenu* am) { int i; for (i=0; inButtons; i++) Free(am->buttons[i]); Free(am); } #define BORDER_X 8 #define BORDER_Y 8 #define BUTTON_X_SPACE 4 #define RADIO_HEIGHT 12 /*************************************************************************** * setup_radio() * Prepared a WalkRadio button for display. *********************************************************************PROTO*/ void setup_radio(WalkRadio *wr) { int i; int our_height; wr->face_color[0] = int_button_face0; wr->face_color[1] = int_button_face1; wr->text_color[0] = int_button_text0; wr->text_color[1] = int_button_text1; wr->border_color[0] = int_button_border0; wr->border_color[1] = int_button_border1; wr->bitmap0 = malloc(sizeof(SDL_Surface *) * wr->n); Assert(wr->bitmap0); wr->bitmap1 = malloc(sizeof(SDL_Surface *) * wr->n); Assert(wr->bitmap1); wr->w = 0; wr->h = 0; for (i=0; in; i++) { SDL_Color sc; SDL_GetRGB(wr->text_color[0], screen->format, &sc.r, &sc.g, &sc.b); wr->bitmap0[i] = TTF_RenderText_Blended(sfont, wr->label[i], sc); Assert(wr->bitmap0[i]); SDL_GetRGB(wr->text_color[1], screen->format, &sc.r, &sc.g, &sc.b); wr->bitmap1[i] = TTF_RenderText_Blended(sfont, wr->label[i], sc); Assert(wr->bitmap1[i]); if (wr->bitmap0[i]->w + BUTTON_X_SPACE > wr->w) wr->w = wr->bitmap0[i]->w + BUTTON_X_SPACE; if (wr->bitmap0[i]->h > wr->h) wr->h = wr->bitmap0[i]->h; } wr->w += 5 ; wr->h += 10 ; wr->area.w = wr->w + BORDER_X; our_height = RADIO_HEIGHT; if (wr->n < our_height) our_height = wr->n; wr->area.h = wr->h * our_height + BORDER_Y; return; } /*************************************************************************** * check_radio() * Determines if we have clicked in a radio button. *********************************************************************PROTO*/ int check_radio(WalkRadio *wr, int x, int y) { if (wr->inactive) return 0; if ( x >= wr->area.x && x <= wr->area.x + wr->area.w && y >= wr->area.y && y <= wr->area.y + wr->area.h ) { int start, stop; int c; if (wr->n < RADIO_HEIGHT) { /* draw them all */ start = 0; stop = wr->n; } else { /* at least four */ start = wr->defaultchoice - (RADIO_HEIGHT/2); if (start < 0) start = 0; stop = start + RADIO_HEIGHT; if (stop > wr->n) { /* we're at the bottom */ start -= (stop - wr->n); stop = wr->n; } } c = (y - wr->area.y) / wr->h; c += start; if (c < 0) c = 0; if (c >= wr->n) c = wr->n - 1; wr->defaultchoice = c; return 1; } return 0; } /*************************************************************************** * clear_radio() * Clears away a WalkRadio button. Works even if I am inactive. *********************************************************************PROTO*/ void clear_radio(WalkRadio *wr) { SDL_FillRect(widget_layer, &wr->area, int_black); SDL_FillRect(screen, &wr->area, int_black); SDL_BlitSurface(flame_layer, &wr->area, screen, &wr->area); SDL_BlitSurface(widget_layer, &wr->area, screen, &wr->area); SDL_UpdateSafe(screen, 1, &wr->area); return; } /*************************************************************************** * draw_radio() * Draws a WalkRadio button. * * Only draws me if I am not inactive. *********************************************************************PROTO*/ void draw_radio(WalkRadio *wr, int state) { int start, stop, i; SDL_Rect draw; if (wr->inactive) return; wr->area.x = wr->x; wr->area.y = wr->y; SDL_FillRect(widget_layer, &wr->area, wr->border_color[state]); if (wr->n < RADIO_HEIGHT) { /* draw them all */ start = 0; stop = wr->n; } else { /* at least four */ start = wr->defaultchoice - (RADIO_HEIGHT/2); if (start < 0) start = 0; stop = start + RADIO_HEIGHT; if (stop > wr->n) { /* we're at the bottom */ start -= (stop - wr->n); stop = wr->n; } } draw.x = wr->x + BORDER_X/2; draw.y = wr->y + BORDER_Y/2; draw.w = wr->w; draw.h = wr->h; for (i=start; idefaultchoice == i); SDL_Rect text; SDL_FillRect(widget_layer, &draw, wr->text_color[on]); draw.x += 2; draw.y += 2; draw.w -= 4; draw.h -= 4; if (state || on) SDL_FillRect(widget_layer, &draw, wr->face_color[on]); else SDL_FillRect(widget_layer, &draw, wr->border_color[on]); draw.x += 3; draw.y += 3; draw.w -= 6; draw.h -= 6; text.x = draw.x; text.y = draw.y; text.w = wr->bitmap0[i]->w; text.h = wr->bitmap0[i]->h; text.x += (draw.w - text.w) / 2; if (on) { SDL_BlitSafe(wr->bitmap1[i], NULL, widget_layer, &text); } else { SDL_BlitSafe(wr->bitmap0[i], NULL, widget_layer, &text); } draw.x -= 5; draw.y -= 5; draw.w += 10; draw.h += 10; draw.y += draw.h; /* move down! */ } SDL_BlitSurface(flame_layer, &wr->area, screen, &wr->area); SDL_BlitSurface(widget_layer, &wr->area, screen, &wr->area); SDL_UpdateSafe(screen, 1, &wr->area); } /*************************************************************************** * create_single_wrg() * Creates a walk-radio-group with a single walk-radio button with N * choices. *********************************************************************PROTO*/ WalkRadioGroup * create_single_wrg(int n) { WalkRadioGroup *wrg; Calloc(wrg, WalkRadioGroup *, 1 * sizeof(*wrg)); wrg->n = 1; Calloc(wrg->wr, WalkRadio *, wrg->n * sizeof(*wrg->wr)); wrg->wr[0].n = n; Calloc(wrg->wr[0].label, char **, wrg->wr[0].n * sizeof(*wrg->wr[0].label)); return wrg; } /*************************************************************************** * handle_radio_event() * Tries to handle the given event with respect to the given set of * WalkRadio's. Returns non-zero if an action said that we are done. *********************************************************************PROTO*/ int handle_radio_event(WalkRadioGroup *wrg, const SDL_Event *ev) { int i, ret; if (ev->type == SDL_MOUSEBUTTONDOWN) { for (i=0; in; i++) { if (check_radio(&wrg->wr[i], ev->button.x, ev->button.y)) { if (i != wrg->cur) { draw_radio(&wrg->wr[wrg->cur], 0); } draw_radio(&wrg->wr[i], 1); if (wrg->wr[i].action) { ret = wrg->wr[i].action(&wrg->wr[i]); return ret; } else return wrg->wr[i].defaultchoice; } } } else if (ev->type == SDL_KEYDOWN) { switch (ev->key.keysym.sym) { case SDLK_RETURN: if (wrg->wr[wrg->cur].action) { ret = wrg->wr[wrg->cur].action(&wrg->wr[wrg->cur]); return ret; } else return wrg->wr[wrg->cur].defaultchoice; return 1; case SDLK_UP: wrg->wr[wrg->cur].defaultchoice --; if (wrg->wr[wrg->cur].defaultchoice < 0) wrg->wr[wrg->cur].defaultchoice = wrg->wr[wrg->cur].n - 1; draw_radio(&wrg->wr[wrg->cur], 1); return -1; break; case SDLK_DOWN: wrg->wr[wrg->cur].defaultchoice ++; if (wrg->wr[wrg->cur].defaultchoice >= wrg->wr[wrg->cur].n) wrg->wr[wrg->cur].defaultchoice = 0; draw_radio(&wrg->wr[wrg->cur], 1); return -1; case SDLK_LEFT: draw_radio(&wrg->wr[wrg->cur], 0); do { wrg->cur--; if (wrg->cur < 0) wrg->cur = wrg->n - 1; } while (wrg->wr[wrg->cur].inactive); draw_radio(&wrg->wr[wrg->cur], 1); return -1; case SDLK_RIGHT: draw_radio(&wrg->wr[wrg->cur], 0); do { wrg->cur++; if (wrg->cur >= wrg->n) wrg->cur = 0; } while (wrg->wr[wrg->cur].inactive); draw_radio(&wrg->wr[wrg->cur], 1); return -1; default: return -1; } } return -1; } /* * $Log: menu.c,v $ * Revision 1.17 2000/11/06 01:22:40 wkiri * Updated menu system. * * Revision 1.16 2000/11/06 00:24:01 weimer * add WalkRadioGroup modifications (inactive field for Kiri) and some support * for special pieces * * Revision 1.15 2000/11/02 03:06:20 weimer * better interface for walk-radio menus: we are now ready for Kiri to change * them to add run-time options ... * * Revision 1.14 2000/10/29 21:23:28 weimer * One last round of header-file changes to reflect my newest and greatest * knowledge of autoconf/automake. Now if you fail to have some bizarro * function, we try to go on anyway unless it is vastly needed. * * Revision 1.13 2000/10/29 17:23:13 weimer * incorporate "xflame" flaming background for added spiffiness ... * * Revision 1.12 2000/10/21 01:22:44 weimer * prevent segfault from out-of-range values * * Revision 1.11 2000/10/21 01:14:43 weimer * massic autoconf/automake restructure ... * * Revision 1.10 2000/10/19 22:06:51 weimer * minor changes ... * * Revision 1.9 2000/10/18 23:57:49 weimer * general fixup, color changes, display changes. * Notable: "Safe" Blits and Updates now perform "clipping". No more X errors, * we hope! * * Revision 1.8 2000/10/13 02:26:54 weimer * rudimentary identity functions, including adding new players * * Revision 1.7 2000/10/12 22:21:25 weimer * display changes, more multi-local-play threading (e.g., myScore -> * Score[0]), that sort of thing ... * * Revision 1.6 2000/10/12 19:17:08 weimer * Further support for AI players and multiple game types. * * Revision 1.5 2000/10/12 00:49:08 weimer * added "AI" support and walking radio menus for initial game configuration * * Revision 1.4 2000/09/09 17:05:35 wkiri * Hideous log changes (Wes: how dare you include a comment character!) * * Revision 1.3 2000/09/09 16:58:27 weimer * Sweeping Change of Ultimate Mastery. Main loop restructuring to clean up * main(), isolate the behavior of the three game types. Move graphic files * into graphics/-, style files into styles/-, remove some unused files, * add game flow support (breaks between games, remembering your level within * this game), multiplayer support for the event loop, some global variable * cleanup. All that and a bag of chips! * * Revision 1.2 2000/09/04 22:49:51 wkiri * gamemenu now uses the new nifty menus. Also, added delete_menu. * * Revision 1.1 2000/09/04 19:54:26 wkiri * Added menus (menu.[ch]). */