+ boolean position_set_by_scrollbar = (dx == 999);
+
+ if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
+ num_page_entries = num_entries;
+ else
+ num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
+
+ game_status = last_game_status; /* restore current game status */
+
+ if (button == MB_MENU_INITIALIZE)
+ {
+ int num_entries = numTreeInfoInGroup(ti);
+ int entry_pos = posTreeInfo(ti);
+
+ if (ti->cl_first == -1)
+ {
+ /* only on initialization */
+ ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
+ ti->cl_cursor = entry_pos - ti->cl_first;
+ }
+ else if (ti->cl_cursor >= num_page_entries ||
+ (num_entries > num_page_entries &&
+ num_entries - ti->cl_first < num_page_entries))
+ {
+ /* only after change of list size (by custom graphic configuration) */
+ ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
+ ti->cl_cursor = entry_pos - ti->cl_first;
+ }
+
+ if (position_set_by_scrollbar)
+ ti->cl_first = dy;
+ else
+ AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
+ ti->cl_first, ti);
+
+ drawChooseTreeList(ti->cl_first, num_page_entries, ti);
+ drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
+ drawChooseTreeCursor(ti->cl_cursor, TRUE);
+
+ return;
+ }
+ else if (button == MB_MENU_LEAVE)
+ {
+ FadeSetLeaveMenu();
+
+ PlaySound(SND_MENU_ITEM_SELECTING);
+
+ if (ti->node_parent)
+ {
+ *ti_ptr = ti->node_parent;
+ DrawChooseTree(ti_ptr);
+ }
+ else if (game_status == GAME_MODE_SETUP)
+ {
+ if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
+ setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
+ setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
+ execSetupGame();
+ else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE ||
+ setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
+ setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE)
+ execSetupGraphics();
+ else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
+ setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
+ setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
+ execSetupSound();
+ else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
+ setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
+ setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
+ execSetupTouch();
+ else
+ execSetupArtwork();
+ }
+ else
+ {
+ if (game_status == GAME_MODE_LEVELNR)
+ {
+ int new_level_nr = atoi(level_number_current->identifier);
+
+ HandleMainMenu_SelectLevel(0, 0, new_level_nr);
+ }
+
+ game_status = GAME_MODE_MAIN;
+
+ DrawMainMenu();
+ }
+
+ return;
+ }
+
+ if (mx || my) /* mouse input */
+ {
+ int last_game_status = game_status; /* save current game status */
+
+ x = (mx - mSX) / 32;
+ y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
+
+ game_status = last_game_status; /* restore current game status */
+ }
+ else if (dx || dy) /* keyboard or scrollbar/scrollbutton input */
+ {
+ /* move cursor instead of scrolling when already at start/end of list */
+ if (dy == -1 * SCROLL_LINE && ti->cl_first == 0)
+ dy = -1;
+ else if (dy == +1 * SCROLL_LINE &&
+ ti->cl_first + num_page_entries == num_entries)
+ dy = 1;
+
+ /* handle scrolling screen one line or page */
+ if (ti->cl_cursor + dy < 0 ||
+ ti->cl_cursor + dy > num_page_entries - 1)
+ {
+ boolean redraw = FALSE;
+
+ if (ABS(dy) == SCROLL_PAGE)
+ step = num_page_entries - 1;
+
+ if (dy < 0 && ti->cl_first > 0)
+ {
+ /* scroll page/line up */
+
+ ti->cl_first -= step;
+ if (ti->cl_first < 0)
+ ti->cl_first = 0;
+
+ redraw = TRUE;
+ }
+ else if (dy > 0 && ti->cl_first + num_page_entries < num_entries)
+ {
+ /* scroll page/line down */
+
+ ti->cl_first += step;
+ if (ti->cl_first + num_page_entries > num_entries)
+ ti->cl_first = MAX(0, num_entries - num_page_entries);
+
+ redraw = TRUE;
+ }
+
+ if (redraw)
+ {
+ drawChooseTreeList(ti->cl_first, num_page_entries, ti);
+ drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
+ drawChooseTreeCursor(ti->cl_cursor, TRUE);
+
+ AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
+ ti->cl_first, ti);
+ }
+
+ return;
+ }
+
+ /* handle moving cursor one line */
+ y = ti->cl_cursor + dy;
+ }
+
+ if (dx == 1)
+ {
+ TreeInfo *node_first, *node_cursor;
+ int entry_pos = ti->cl_first + y;
+
+ node_first = getTreeInfoFirstGroupEntry(ti);
+ node_cursor = getTreeInfoFromPos(node_first, entry_pos);
+
+ if (node_cursor->node_group)
+ {
+ FadeSetEnterMenu();
+
+ PlaySound(SND_MENU_ITEM_SELECTING);
+
+ node_cursor->cl_first = ti->cl_first;
+ node_cursor->cl_cursor = ti->cl_cursor;
+ *ti_ptr = node_cursor->node_group;
+ DrawChooseTree(ti_ptr);
+
+ return;
+ }
+ }
+ else if (dx == -1 && ti->node_parent)
+ {
+ FadeSetLeaveMenu();
+
+ PlaySound(SND_MENU_ITEM_SELECTING);
+
+ *ti_ptr = ti->node_parent;
+ DrawChooseTree(ti_ptr);
+
+ return;
+ }
+
+ if (!anyScrollbarGadgetActive() &&
+ IN_VIS_MENU(x, y) &&
+ mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
+ y >= 0 && y < num_page_entries)
+ {
+ if (button)
+ {
+ if (y != ti->cl_cursor)
+ {
+ PlaySound(SND_MENU_ITEM_ACTIVATING);
+
+ drawChooseTreeCursor(ti->cl_cursor, FALSE);
+ drawChooseTreeCursor(y, TRUE);
+ drawChooseTreeInfo(ti->cl_first + y, ti);
+
+ ti->cl_cursor = y;
+ }
+ }
+ else
+ {
+ TreeInfo *node_first, *node_cursor;
+ int entry_pos = ti->cl_first + y;
+
+ PlaySound(SND_MENU_ITEM_SELECTING);
+
+ node_first = getTreeInfoFirstGroupEntry(ti);
+ node_cursor = getTreeInfoFromPos(node_first, entry_pos);
+
+ if (node_cursor->node_group)
+ {
+ FadeSetEnterMenu();
+
+ node_cursor->cl_first = ti->cl_first;
+ node_cursor->cl_cursor = ti->cl_cursor;
+ *ti_ptr = node_cursor->node_group;
+ DrawChooseTree(ti_ptr);
+ }
+ else if (node_cursor->parent_link)
+ {
+ FadeSetLeaveMenu();
+
+ *ti_ptr = node_cursor->node_parent;
+ DrawChooseTree(ti_ptr);
+ }
+ else
+ {
+ FadeSetEnterMenu();
+
+ node_cursor->cl_first = ti->cl_first;
+ node_cursor->cl_cursor = ti->cl_cursor;
+ *ti_ptr = node_cursor;
+
+ if (ti->type == TREE_TYPE_LEVEL_DIR)
+ {
+ LoadLevelSetup_SeriesInfo();
+
+ SaveLevelSetup_LastSeries();
+ SaveLevelSetup_SeriesInfo();
+ TapeErase();
+ }
+
+ if (game_status == GAME_MODE_SETUP)
+ {
+ if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED ||
+ setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY ||
+ setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE)
+ execSetupGame();
+ else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE ||
+ setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE ||
+ setup_mode == SETUP_MODE_CHOOSE_SCALING_TYPE)
+ execSetupGraphics();
+ else if (setup_mode == SETUP_MODE_CHOOSE_VOLUME_SIMPLE ||
+ setup_mode == SETUP_MODE_CHOOSE_VOLUME_LOOPS ||
+ setup_mode == SETUP_MODE_CHOOSE_VOLUME_MUSIC)
+ execSetupSound();
+ else if (setup_mode == SETUP_MODE_CHOOSE_TOUCH_CONTROL ||
+ setup_mode == SETUP_MODE_CHOOSE_MOVE_DISTANCE ||
+ setup_mode == SETUP_MODE_CHOOSE_DROP_DISTANCE)
+ execSetupTouch();
+ else
+ execSetupArtwork();
+ }
+ else
+ {
+ if (game_status == GAME_MODE_LEVELNR)
+ {
+ int new_level_nr = atoi(level_number_current->identifier);
+
+ HandleMainMenu_SelectLevel(0, 0, new_level_nr);
+ }
+
+ game_status = GAME_MODE_MAIN;
+
+ DrawMainMenu();
+ }
+ }
+ }
+ }
+}
+
+void DrawChooseLevelSet()
+{
+ SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
+
+ DrawChooseTree(&leveldir_current);
+
+ PlayMenuSound();
+ PlayMenuMusic();
+}
+
+void HandleChooseLevelSet(int mx, int my, int dx, int dy, int button)
+{
+ HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
+}
+
+void DrawChooseLevelNr()
+{
+ int i;
+
+ if (level_number != NULL)
+ {
+ freeTreeInfo(level_number);
+
+ level_number = NULL;
+ }
+
+ for (i = leveldir_current->first_level; i <= leveldir_current->last_level;i++)
+ {
+ TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_LEVEL_NR);
+ char identifier[32], name[32];
+ int value = i;
+
+ /* temporarily load level info to get level name */
+ LoadLevelInfoOnly(i);
+
+ ti->node_top = &level_number;
+ ti->sort_priority = 10000 + value;
+ ti->color = (level.no_valid_file ? FC_BLUE :
+ LevelStats_getSolved(i) ? FC_GREEN :
+ LevelStats_getPlayed(i) ? FC_YELLOW : FC_RED);
+
+ sprintf(identifier, "%d", value);
+ sprintf(name, "%03d: %s", value,
+ (level.no_valid_file ? "(no file)" : level.name));
+
+ setString(&ti->identifier, identifier);
+ setString(&ti->name, name);
+ setString(&ti->name_sorting, name);
+
+ pushTreeInfo(&level_number, ti);
+ }
+
+ /* sort level number values to start with lowest level number */
+ sortTreeInfo(&level_number);
+
+ /* set current level number to current level number */
+ level_number_current =
+ getTreeInfoFromIdentifier(level_number, i_to_a(level_nr));
+
+ /* if that also fails, set current level number to first available level */
+ if (level_number_current == NULL)
+ level_number_current = level_number;
+
+ SetMainBackgroundImage(IMG_BACKGROUND_LEVELNR);
+
+ DrawChooseTree(&level_number_current);
+
+ PlayMenuSound();
+ PlayMenuMusic();
+}
+
+void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button)
+{
+ HandleChooseTree(mx, my, dx, dy, button, &level_number_current);
+}
+
+void DrawHallOfFame(int highlight_position)
+{
+ int fade_mask = REDRAW_FIELD;
+
+ /* required before door position may be changed in next step */
+ CloseDoor(DOOR_CLOSE_ALL);
+
+ /* needed if different viewport properties defined for scores */
+ ChangeViewportPropertiesIfNeeded();
+
+ if (CheckIfGlobalBorderHasChanged())
+ fade_mask = REDRAW_ALL;
+
+ UnmapAllGadgets();
+ FadeSoundsAndMusic();
+
+ /* (this is needed when called from GameEnd() after winning a game) */
+ KeyboardAutoRepeatOn();
+ ActivateJoystick();
+
+ /* (this is needed when called from GameEnd() after winning a game) */
+ SetDrawDeactivationMask(REDRAW_NONE);
+ SetDrawBackgroundMask(REDRAW_FIELD);
+
+ if (highlight_position < 0)
+ LoadScore(level_nr);
+
+ FadeSetEnterScreen();
+
+ FadeOut(fade_mask);
+
+ InitAnimation();
+
+ PlayMenuSound();
+ PlayMenuMusic();
+
+ OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
+
+ HandleHallOfFame(highlight_position, 0, 0, 0, MB_MENU_INITIALIZE);
+
+ DrawMaskedBorder(fade_mask);
+
+ FadeIn(fade_mask);
+}
+
+static void drawHallOfFameList(int first_entry, int highlight_position)
+{
+ int i, j;
+
+ SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
+ ClearField();
+
+ DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame");
+ DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2,
+ "HighScores of Level %d", level_nr);
+
+ for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
+ {
+ int entry = first_entry + i;
+ boolean active = (entry == highlight_position);
+ int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
+ int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
+ int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
+ int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
+ int dx1 = 3 * getFontWidth(font_nr1);
+ int dx2 = dx1 + getFontWidth(font_nr1);
+ int dx3 = SXSIZE - 2 * (mSX - SX) - 5 * getFontWidth(font_nr4);
+ int num_dots = (dx3 - dx2) / getFontWidth(font_nr3);
+ int sy = mSY + 64 + i * 32;
+
+ DrawText(mSX, sy, int2str(entry + 1, 3), font_nr1);
+ DrawText(mSX + dx1, sy, ".", font_nr1);
+
+ for (j = 0; j < num_dots; j++)
+ DrawText(mSX + dx2 + j * getFontWidth(font_nr3), sy, ".", font_nr3);
+
+ if (!strEqual(highscore[entry].Name, EMPTY_PLAYER_NAME))
+ DrawText(mSX + dx2, sy, highscore[entry].Name, font_nr2);
+
+ DrawText(mSX + dx3, sy, int2str(highscore[entry].Score, 5), font_nr4);
+ }
+
+ redraw_mask |= REDRAW_FIELD;
+}
+
+void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
+{
+ static int first_entry = 0;
+ static int highlight_position = 0;
+ int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
+
+ if (button == MB_MENU_INITIALIZE)
+ {
+ first_entry = 0;
+ highlight_position = mx;
+ drawHallOfFameList(first_entry, highlight_position);
+
+ return;
+ }
+
+ if (ABS(dy) == SCROLL_PAGE) /* handle scrolling one page */
+ step = NUM_MENU_ENTRIES_ON_SCREEN - 1;
+
+ if (dy < 0)
+ {
+ if (first_entry > 0)
+ {
+ first_entry -= step;
+ if (first_entry < 0)
+ first_entry = 0;
+
+ drawHallOfFameList(first_entry, highlight_position);
+ }
+ }
+ else if (dy > 0)
+ {
+ if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
+ {
+ first_entry += step;
+ if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
+ first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
+
+ drawHallOfFameList(first_entry, highlight_position);
+ }
+ }
+ else if (button == MB_MENU_LEAVE)
+ {
+ PlaySound(SND_MENU_ITEM_SELECTING);
+
+ FadeSound(SND_BACKGROUND_SCORES);
+
+ game_status = GAME_MODE_MAIN;
+
+ DrawMainMenu();
+ }
+ else if (button == MB_MENU_CHOICE)
+ {
+ PlaySound(SND_MENU_ITEM_SELECTING);
+
+ FadeSound(SND_BACKGROUND_SCORES);
+
+ game_status = GAME_MODE_MAIN;
+
+ DrawMainMenu();
+ }
+
+ if (game_status == GAME_MODE_SCORES)
+ PlayMenuSoundIfLoop();
+}
+
+
+/* ========================================================================= */
+/* setup screen functions */
+/* ========================================================================= */
+
+static struct TokenInfo *setup_info;
+static int num_setup_info; /* number of setup entries shown on screen */
+static int max_setup_info; /* total number of setup entries in list */
+
+static char *screen_mode_text;
+static char *window_size_text;
+static char *scaling_type_text;
+static char *scroll_delay_text;
+static char *snapshot_mode_text;
+static char *game_speed_text;
+static char *graphics_set_name;
+static char *sounds_set_name;
+static char *music_set_name;
+static char *volume_simple_text;
+static char *volume_loops_text;
+static char *volume_music_text;
+static char *touch_controls_text;
+static char *move_distance_text;
+static char *drop_distance_text;
+
+static void execSetupMain()
+{
+ setup_mode = SETUP_MODE_MAIN;
+
+ DrawSetupScreen();
+}
+
+static void execSetupGame_setGameSpeeds()
+{
+ if (game_speeds == NULL)
+ {
+ int i;
+
+ for (i = 0; game_speeds_list[i].value != -1; i++)
+ {
+ TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
+ char identifier[32], name[32];
+ int value = game_speeds_list[i].value;
+ char *text = game_speeds_list[i].text;
+
+ ti->node_top = &game_speeds;
+ ti->sort_priority = 10000 - value;
+
+ sprintf(identifier, "%d", value);
+ sprintf(name, "%s", text);
+
+ setString(&ti->identifier, identifier);
+ setString(&ti->name, name);
+ setString(&ti->name_sorting, name);
+ setString(&ti->infotext, "Game Speed");
+
+ pushTreeInfo(&game_speeds, ti);
+ }
+
+ /* sort game speed values to start with slowest game speed */
+ sortTreeInfo(&game_speeds);
+
+ /* set current game speed to configured game speed value */
+ game_speed_current =
+ getTreeInfoFromIdentifier(game_speeds, i_to_a(setup.game_frame_delay));
+
+ /* if that fails, set current game speed to reliable default value */
+ if (game_speed_current == NULL)
+ game_speed_current =
+ getTreeInfoFromIdentifier(game_speeds, i_to_a(GAME_FRAME_DELAY));
+
+ /* if that also fails, set current game speed to first available speed */
+ if (game_speed_current == NULL)
+ game_speed_current = game_speeds;
+ }
+
+ setup.game_frame_delay = atoi(game_speed_current->identifier);
+
+ /* needed for displaying game speed text instead of identifier */
+ game_speed_text = game_speed_current->name;
+}
+
+static void execSetupGame_setScrollDelays()
+{
+ if (scroll_delays == NULL)
+ {
+ int i;
+
+ for (i = 0; scroll_delays_list[i].value != -1; i++)
+ {
+ TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
+ char identifier[32], name[32];
+ int value = scroll_delays_list[i].value;
+ char *text = scroll_delays_list[i].text;
+
+ ti->node_top = &scroll_delays;
+ ti->sort_priority = value;
+
+ sprintf(identifier, "%d", value);
+ sprintf(name, "%s", text);
+
+ setString(&ti->identifier, identifier);
+ setString(&ti->name, name);
+ setString(&ti->name_sorting, name);
+ setString(&ti->infotext, "Scaling Type");