huge CVS merge, see ChangeLog for details.
[supertux.git] / src / menu.c
index 2ac0fe3..e398cfd 100644 (file)
@@ -7,7 +7,7 @@
   tobi.web@gmx.de
   http://www.newbreedsoftware.com/supertux/
   
-  December 20, 2003 - December 29, 2003
+  December 20, 2003 - December 30, 2003
 */
 
 #ifdef LINUX
 #include <ctype.h>
 #endif
 
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
 #include "defines.h"
 #include "globals.h"
 #include "menu.h"
 #include "screen.h"
 #include "setup.h"
 #include "sound.h"
+#include "scene.h"
 #include "leveleditor.h"
-#include "gameloop.h"
+#include "timer.h"
+#include "high_scores.h"
 
-/* Set defaults */
-void initmenu(void)
-{
-  menu_change = NO;
-  show_menu = NO;
-  menuitem = 0;
-  menumenu = 0;
-  menuaction = -1;
-}
+/* (global) menu variables */
+int menuaction;
+int show_menu;
+int menu_change;
+texture_type checkbox, checkbox_checked;
+
+menu_type main_menu, game_menu, options_menu, leveleditor_menu, highscore_menu;
+menu_type* current_menu, * last_menu;
 
-/* ---- Menu Options - Item Sound On/off ----*/
-void menu_option_sound()
+/* input implementation variables */
+int delete_character;
+char mn_input_char;
+
+/* Set the current menu */
+void menu_set_current(menu_type* pmenu)
 {
-  if (audio_device == YES)
+  if(pmenu != current_menu)
     {
-      if(menuitem == 1)
-        {
-          if(use_sound == YES)
-            {
-              drawcenteredtext("Sound ON", 224, letters_red, NO_UPDATE, 2);
-            }
-          else
-            {
-              drawcenteredtext("Sound OFF", 224, letters_red, NO_UPDATE, 2);
-            }
-
-          if(menuaction == MN_HIT)
-            { /* Disable/Enable sound */
-              if(use_sound == YES)
-                {
-                  use_sound = NO;
-                }
-              else
-                {
-                  use_sound = YES;
-                }
-              menu_change = YES;
-            }
-        }
-      else
-        {
-          if(use_sound == YES)
-            drawcenteredtext("Sound ON", 224, letters_blue, NO_UPDATE, 2);
-          else
-            drawcenteredtext("Sound OFF", 224, letters_blue, NO_UPDATE, 2);
-        }
+      menu_change = YES;
+      last_menu = current_menu;
+      current_menu = pmenu;
     }
+}
+
+/* Return a pointer to a new menu item */
+menu_item_type* menu_item_create(int kind, char *text, int init_toggle, void* target_menu)
+{
+  menu_item_type *pnew_item = (menu_item_type*) malloc(sizeof(menu_item_type));
+  pnew_item->kind = kind;
+  pnew_item->text = (char*) malloc(sizeof(char) * (strlen(text) + 1));
+  strcpy(pnew_item->text,text);
+  if(kind == MN_TOGGLE)
+    pnew_item->toggled = init_toggle;
   else
-    { /* if audio_device != YES */
-      /* let the user move over the deactivated option */
-      if (menuitem == 1)
-        {
-          drawcenteredtext("Sound OFF", 224, letters_red, NO_UPDATE, 2);
-        }
-      else
+    pnew_item->toggled = NO;
+  pnew_item->target_menu = target_menu;
+  pnew_item->input = NULL;
+  return pnew_item;
+}
+
+/* Free a menu and all its items */
+void menu_free(menu_type* pmenu)
+{
+  int i;
+  if(pmenu->num_items != 0 && pmenu->item != NULL)
+    {
+      for(i = 0; i < pmenu->num_items; ++i)
         {
-          drawcenteredtext("Sound OFF", 224, letters_black, NO_UPDATE, 2);
+          free(pmenu->item[i].text);
+          free(pmenu->item[i].input);
         }
+      free(pmenu->item);
     }
 }
 
+/* Initialize a menu */
+void menu_init(menu_type* pmenu)
+{
+  pmenu->num_items = 0;
+  pmenu->active_item = 0;
+  pmenu->item = NULL;
+}
+
+/* Add an item to a menu */
+void menu_additem(menu_type* pmenu, menu_item_type* pmenu_item)
+{
+  ++pmenu->num_items;
+  pmenu->item = (menu_item_type*) realloc(pmenu->item, sizeof(menu_item_type) * pmenu->num_items);
+  memcpy(&pmenu->item[pmenu->num_items-1],pmenu_item,sizeof(menu_item_type));
+}
 
-/* ---- Menu Options - Item Music On/off ----*/
-void menu_option_music()
+/* Process actions done on the menu */
+void menu_action(menu_type* pmenu)
 {
-  if (audio_device == YES)
+  int i;
+
+  if(pmenu->num_items != 0 && pmenu->item != NULL)
     {
-      if(menuitem == 2)
+      switch(menuaction)
         {
-          if(use_music == YES)
+        case MN_UP:
+          if(pmenu->active_item > 0)
+            --pmenu->active_item;
+          else
+            pmenu->active_item = pmenu->num_items-1;
+          break;
+        case MN_DOWN:
+          if(pmenu->active_item < pmenu->num_items-1)
+            ++pmenu->active_item;
+          else
+            pmenu->active_item = 0;
+          break;
+        case MN_HIT:
+          if(pmenu->item[pmenu->active_item].kind == MN_GOTO && pmenu->item[pmenu->active_item].target_menu != NULL)
+            menu_set_current((menu_type*)pmenu->item[pmenu->active_item].target_menu);
+          else if(pmenu->item[pmenu->active_item].kind == MN_TOGGLE)
             {
-              drawcenteredtext("Music ON", 256, letters_red, NO_UPDATE, 2);
+              pmenu->item[pmenu->active_item].toggled = !pmenu->item[pmenu->active_item].toggled;
+              menu_change = YES;
             }
-          else
+          else if(pmenu->item[pmenu->active_item].kind == MN_ACTION || pmenu->item[pmenu->active_item].kind == MN_TEXTFIELD)
             {
-              drawcenteredtext("Music OFF", 256, letters_red, NO_UPDATE, 2);
+              pmenu->item[pmenu->active_item].toggled = YES;
             }
-          if(menuaction == MN_HIT)
-            { /* Disable/Enable music */
-              if(use_music == YES)
-                {  /* In the menu no music is played, so we have to check only use_music */
-                  if(playing_music())
-                    halt_music();
-                  use_music = NO;
-                }
-              else
+          else if(pmenu->item[pmenu->active_item].kind == MN_BACK)
+            {
+              if(last_menu != NULL)
+                menu_set_current(last_menu);
+            }
+          break;
+        case MN_REMOVE:
+          if(pmenu->item[pmenu->active_item].kind == MN_TEXTFIELD)
+            {
+              if(pmenu->item[pmenu->active_item].input != NULL)
                 {
-                  use_music = YES;
-                  if (!playing_music())
+                  i = strlen(pmenu->item[pmenu->active_item].input);
+
+                  while(delete_character > 0)  /* remove charactes */
                     {
-                      switch (current_music)
-                        {
-                        case LEVEL_MUSIC:
-                          play_music(level_song, 2);
-                          break;
-                        case HERRING_MUSIC:
-                          play_music(herring_song, 2);
-                          break;
-                        case HURRYUP_MUSIC: // keep the compiler happy
-                        case NO_MUSIC:      // keep the compiler happy for the moment :-)
-                        {}
-                          /*default:*/
-                        }
+                      pmenu->item[pmenu->active_item].input[i-1] = '\0';
+                      delete_character--;
                     }
                 }
-              menu_change = YES;
             }
-        } /* if menuitem != 2 : print normal blue font */
-      else
-        {
-          if(use_music == YES)
-            {
-              drawcenteredtext("Music ON", 256, letters_blue, NO_UPDATE, 2);
-            }
-          else
+          break;
+        case MN_INPUT:
+          if(pmenu->item[pmenu->active_item].kind == MN_TEXTFIELD)
             {
-              drawcenteredtext("Music OFF", 256, letters_blue, NO_UPDATE, 2);
+              if(pmenu->item[pmenu->active_item].input != NULL)
+                {      
+                  i = strlen(pmenu->item[pmenu->active_item].input);
+                  pmenu->item[pmenu->active_item].input = (char*) realloc(pmenu->item[pmenu->active_item].input,sizeof(char)*(i + 2));
+                  pmenu->item[pmenu->active_item].input[i] = mn_input_char;
+                  pmenu->item[pmenu->active_item].input[i+1] = '\0';
+                }
+              else
+                {
+                  pmenu->item[pmenu->active_item].input = (char*) malloc(2*sizeof(char));
+                  pmenu->item[pmenu->active_item].input[0] = mn_input_char;
+                  pmenu->item[pmenu->active_item].input[1] = '\0';
+                }
             }
+          break;
         }
     }
-  else
-    { /* if audio_device != YES */
-      /* let the user move over the deactivated option */
-      if (menuitem == 2)
-        {
-          drawcenteredtext("Music OFF", 256, letters_red, NO_UPDATE, 2);
-        }
-      else
-        {
-          drawcenteredtext("Music OFF", 256, letters_black, NO_UPDATE, 2);
-        }
-    }
+    
+    menuaction = -1;
+
+  if(pmenu->item[pmenu->active_item].kind == MN_DEACTIVE)
+    menu_action(pmenu);
 }
 
-int menu_main(void)
+/* Check, if the value of the active menu item has changed. */
+int menu_check(menu_type* pmenu)
 {
-  /* Does the menu item exist? If not, we reset to the most down item */
-  if(menuitem > MENU_MAIN_ITEM_MAX)
-    menuitem =0;
-  else if(menuitem < 0)
-    menuitem = MENU_MAIN_ITEM_MAX;
-
-  /*The menu looks different, when the game is started */
-  if(menuitem == 0)
+  if(pmenu->num_items != 0 && pmenu->item != NULL)
     {
-      drawcenteredtext("Start Game", 192, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* we are ready to start the game, if this item got hit */
+      if((pmenu->item[pmenu->active_item].kind == MN_ACTION || pmenu->item[pmenu->active_item].kind == MN_TEXTFIELD) && pmenu->item[pmenu->active_item].toggled == YES)
         {
-          game_started = 1;
+          pmenu->item[pmenu->active_item].toggled = NO;
           show_menu = 0;
+          return pmenu->active_item;
         }
-    }
-  else
-    drawcenteredtext("Start Game", 192, letters_blue, NO_UPDATE, 2);
-
-  if(menuitem == 1)
-    {
-      drawcenteredtext("Options", 224, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Switch to the 'Options' menu */
+      else if(pmenu->item[pmenu->active_item].kind == MN_TOGGLE)
         {
-          menumenu = MENU_OPTIONS;
-          menu_change = YES;
-        }
-    }
-  else
-    drawcenteredtext("Options", 224, letters_blue, NO_UPDATE, 2);
-
-  if(menuitem == 2)
-    {
-      drawcenteredtext("Level editor", 256, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Set variables, so that the level editor is executed */
-        {
-          level_editor_started = YES;
-          show_menu = 0;
-        }
-    }
-  else
-    drawcenteredtext("Level editor", 256, letters_blue, NO_UPDATE, 2);
-
-  if(menuitem == 3)
-    {
-      drawcenteredtext("Quit", 288, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Quit a running game or the application */
-        {
-          return 1;
+          return pmenu->active_item;
         }
+      else
+        return -1;
     }
   else
-    {
-      drawcenteredtext("Quit", 288, letters_blue, NO_UPDATE, 2);
-    }
-
-  return 0;
+    return -1;
 }
 
-int menu_game(void)
+/* Draw the current menu. */
+void menu_draw(menu_type* pmenu)
 {
-  /* Does the menu item exist? If not, we reset to the most down item */
-  if(menuitem > MENU_GAME_ITEM_MAX)
-    menuitem = 0;
-  else if(menuitem < 0)
-    menuitem = MENU_GAME_ITEM_MAX;
-
-  /*The menu looks different, when the game is started */
-  if(menuitem == 0)
-    {
-      drawcenteredtext("Return To Game", 192, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Don't show the menu anymore, if this item got hit */
-        show_menu = 0;
-    }
-  else
-    drawcenteredtext("Return To Game", 192, letters_blue, NO_UPDATE, 2);
-    
-  if(menuitem == 1)
-    {
-      drawcenteredtext("Save Game", 224, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Don't show the menu anymore, if this item got hit */
-      {
-        show_menu = 0;
-       savegame();
-      }
-    }
-  else
-    drawcenteredtext("Save Game", 224, letters_blue, NO_UPDATE, 2);
-    
-  if(menuitem == 2)
-    {
-      drawcenteredtext("Load Game", 256, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Don't show the menu anymore, if this item got hit */
-      {
-        show_menu = 0;
-       char *x = NULL;
-       loadgame(x);
-      }
-    }
-  else
-    drawcenteredtext("Load Game", 256, letters_blue, NO_UPDATE, 2);
-    
-  if(menuitem == 3)
+  int i, y, menu_height, menu_width;
+
+  /* The width of the menu has to be more than the width of the text with the most characters */
+  menu_width = 0;
+  for(i = 0; i < pmenu->num_items; ++i)
     {
-      drawcenteredtext("Options", 288, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Switch to the 'Options' menu */
+    y = strlen(pmenu->item[i].text) + (pmenu->item[i].input ? strlen(pmenu->item[i].input) : 0);
+      if( y > menu_width )
         {
-          menumenu = MENU_OPTIONS;
-          menu_change = YES;
+          menu_width = y;
+          if( pmenu->item[i].kind == MN_TOGGLE)
+            menu_width += 2;
         }
     }
-  else
-    drawcenteredtext("Options", 288, letters_blue, NO_UPDATE, 2);
-
-  if(menuitem == 4)
-    {
-      drawcenteredtext("Quit Game", 320, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Quit a running game */
-        return 1;
-    }
-  else
-    {
-      drawcenteredtext("Quit Game", 320, letters_blue, NO_UPDATE, 2);
-    }
-
-  return 0;
-}
-
-int menu_options(void)
-{
-  if(menuitem > MENU_OPTIONS_ITEM_MAX )
-    menuitem = 0;
-  else if(menuitem < 0)
-    menuitem = MENU_OPTIONS_ITEM_MAX;
+  menu_width = menu_width * 16 + 48;
+  menu_height = (pmenu->num_items) * 24;
+  
+  /* Draw a transparent background */
+  fillrect(screen->w/2 - menu_width/2,screen->h/2-(((pmenu->num_items)*24)/2),menu_width,menu_height,150,150,150,100);
 
-  if(menuitem == 0)
+  for(i = 0; i < pmenu->num_items; ++i)
     {
-      if(use_fullscreen)
-        drawcenteredtext("Fullscreen ON", 192, letters_red, NO_UPDATE, 2);
-      else
-        drawcenteredtext("Fullscreen OFF", 192, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Disable/Enable fullscreen */
+      if(pmenu->item[i].kind == MN_DEACTIVE)
         {
-          if(use_fullscreen)
-            use_fullscreen = 0;
-          else
-            use_fullscreen = 1;
-          st_video_setup();
-          menu_change = YES;
+          text_drawf(&black_text,pmenu->item[i].text,0,(i)*24 - menu_height/2 + 10,A_HMIDDLE, A_VMIDDLE,2,NO_UPDATE);
         }
-    }
-  else
-    {
-      if(use_fullscreen)
-        drawcenteredtext("Fullscreen ON", 192, letters_blue, NO_UPDATE, 2);
-      else
-        drawcenteredtext("Fullscreen OFF", 192, letters_blue, NO_UPDATE, 2);
-    }
-
-  /* handle menu sound on/off option */
-  menu_option_sound();
-
-  /* handle menu music on/off option */
-  menu_option_music();
-
-  if(menuitem == 3)
-    {
-      drawcenteredtext("Back", 288, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Go back to main menu. */
+      else if(pmenu->item[i].kind == MN_TEXTFIELD)
         {
-          if(game_started)
-            menumenu = MENU_GAME;
+          text_drawf(&gold_text,pmenu->item[i].input,(strlen(pmenu->item[i].text) * 16)/2,(i)*24 - menu_height/2 + 10,A_HMIDDLE, A_VMIDDLE,2,NO_UPDATE);
+          if(i == pmenu->active_item)
+            {
+              text_drawf(&blue_text,pmenu->item[i].text,-((strlen(pmenu->item[i].input) * 16)/2),(i)*24 - menu_height/2 + 10,A_HMIDDLE, A_VMIDDLE,3,NO_UPDATE);
+            }
           else
-            menumenu = MENU_MAIN;
-          menu_change = YES;
+            {
+              text_drawf(&white_text,pmenu->item[i].text,-((strlen(pmenu->item[i].input) * 16)/2),(i)*24 - menu_height/2 +10,A_HMIDDLE, A_VMIDDLE,2,NO_UPDATE);
+            }
         }
-    }
-  else
-    drawcenteredtext("Back", 288, letters_blue, NO_UPDATE, 2);
-
-  return 0;
-}
-
-/* Menu LevelEditor */
-int menu_leveleditor(void)
-{
-  if(menuitem > MENU_LEVELEDITOR_ITEM_MAX )
-    menuitem = 0;
-  else if(menuitem < 0)
-    menuitem = MENU_LEVELEDITOR_ITEM_MAX;
-
-  if(menuitem == 0)
-    {
-      drawcenteredtext("Return To Level Editor", 192, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Don't show the menu anymore, if this item got hit */
-        show_menu = 0;
-    }
-  else
-    drawcenteredtext("Return To Level Editor", 192, letters_blue, NO_UPDATE, 2);
-
-  if(menuitem == 1)
-    {
-      drawcenteredtext("New Level", 224, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Don't show the menu anymore, if this item got hit */
+      else if(i == pmenu->active_item)
         {
-          show_menu = 0;
-          newlevel();
+          text_drawf(&blue_text,pmenu->item[i].text,0,(i)*24 - menu_height/2 + 10 ,A_HMIDDLE, A_VMIDDLE,3,NO_UPDATE);
         }
-    }
-  else
-    drawcenteredtext("New Level", 224, letters_blue, NO_UPDATE, 2);
-  if(menuitem == 2)
-    {
-      drawcenteredtext("Load Level", 256, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Create a new Level and load it into the level-editor. */
+      else
         {
-          show_menu = 0;
-          selectlevel();
+          text_drawf(&white_text,pmenu->item[i].text,0,(i)*24 - menu_height/2 + 10,A_HMIDDLE, A_VMIDDLE,2,NO_UPDATE);
         }
-    }
-  else
-    drawcenteredtext("Load Level", 256, letters_blue, NO_UPDATE, 2);
-  if(menuitem == 3)
-    {
-      drawcenteredtext("Save Level", 288, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Save the current level in the level-editor. */
+      if(pmenu->item[i].kind == MN_TOGGLE)
         {
-          show_menu = 0;
-          savelevel();
-        }
-    }
-  else
-    drawcenteredtext("Save Level", 288, letters_blue, NO_UPDATE, 2);
 
-  if(menuitem == 4)
-    {
-      drawcenteredtext("Quit Level Editor", 320, letters_red, NO_UPDATE, 2);
-      if(menuaction == MN_HIT) /* Quit the level-editor. (to the main-menu) */
-        {
-          return 1;
+          if(pmenu->item[i].toggled == YES)
+            texture_draw(&checkbox_checked,screen->w / 2 + (strlen(pmenu->item[i].text) * 16)/2  + 16,(i)*24 - menu_height/2 + 10 + screen->h / 2 -8,NO_UPDATE);
+          else
+            texture_draw(&checkbox,screen->w / 2 + (strlen(pmenu->item[i].text) * 16)/2 + 16,(i)*24 - menu_height/2 + 10 + screen->h / 2 - 8,NO_UPDATE);
         }
     }
-  else
-    drawcenteredtext("Quit Level Editor", 320, letters_blue, NO_UPDATE, 2);
-
-  return 0;
 }
 
-/* --- MENU --- */
-/* Draw the menu and execute the (menu)events */
-int drawmenu(void)
+/* Reset/Set global defaults */
+void menu_reset(void)
 {
-  int quit = 0;
-
   menu_change = NO;
+  show_menu = NO;
+  menuaction = -1;
+  current_menu = NULL;
+  last_menu = NULL;
 
+  delete_character = 0;
+  mn_input_char = '\0';
+}
 
-  if(menuaction == MN_UP)
-    {
-      /* Go one menu-item up */
-      --menuitem;
-    }
-  else if(menuaction == MN_DOWN)
-    ++menuitem; /* Go one menu-item down */
-
+/* --- MENU --- */
+/* Draw the current menu and execute the (menu)events */
+void menu_process_current(void)
+{
+  menu_change = NO;
 
-  if(menumenu == MENU_MAIN)
-    {
-      quit = menu_main();
-    }
-  else if(menumenu == MENU_GAME)
-    {
-      quit = menu_game();
-    }
-  else if(menumenu == MENU_OPTIONS)
-    {
-      quit = menu_options();
-    }
-  else if(menumenu == MENU_LEVELEDITOR)
+  if(current_menu != NULL)
     {
-      quit = menu_leveleditor();
+      menu_action(current_menu);
+      menu_draw(current_menu);
     }
 
   menuaction = -1;
-
-  return quit;
 }
 
 /* Check for menu event */
-void menu_event(SDLKey key)
+void menu_event(SDL_keysym* keysym)
 {
-
-
-  if (key == SDLK_UP)
+  SDLKey key = keysym->sym;
+  SDLMod keymod;
+  keymod = SDL_GetModState();
+  char ch[2];
+
+  /* If the current unicode character is an ASCII character,
+     assign it to ch. */
+  if ( (keysym->unicode & 0xFF80) == 0 )
     {
-      /* Menu Up */
-
-      menuaction = MN_UP;
-      menu_change = YES;
+      ch[0] = keysym->unicode & 0x7F;
+      ch[1] = '\0';
     }
-  else if (key == SDLK_DOWN)
+  else
     {
-      /* Menu Down */
+      /* An International Character. */
+    }
 
+  switch(key)
+    {
+    case SDLK_UP:              /* Menu Up */
+      menuaction = MN_UP;
+      menu_change = YES;
+      break;
+    case SDLK_DOWN:            /* Menu Down */
       menuaction = MN_DOWN;
       menu_change = YES;
-    }
-  else if (key == SDLK_SPACE || key == SDLK_RETURN)
-    {
-      /* Menu Hit */
-
+      break;
+    case SDLK_SPACE:           /* Menu Hit */
+    case SDLK_RETURN:
       menuaction = MN_HIT;
       menu_change = YES;
+      break;
+    case SDLK_DELETE:
+    case SDLK_BACKSPACE:
+      menuaction = MN_REMOVE;
+      menu_change = YES;
+      delete_character++;
+      break;
+    default:
+      if( key >= SDLK_0 && key <= SDLK_9)
+        {
+          menuaction = MN_INPUT;
+          menu_change = YES;
+          mn_input_char = *ch;
+        }
+      else if( key >= SDLK_a && key <= SDLK_z )
+        {
+          menuaction = MN_INPUT;
+          menu_change = YES;
+          mn_input_char = *ch;
+        }
+      else
+        {
+        mn_input_char = '\0';
+       }
+      break;
     }
 
+
   /* FIXME: NO JOYSTICK SUPPORT */
   /*#ifdef JOY_YES
   else if (event.type == SDL_JOYBUTTONDOWN)
    {
-      Joystick button: Continue: 
-     
+      Joystick button: Continue:
+
      done = 1;
    }
   #endif*/
-
 }