##########################################################################
# This file is part of Vacuum Magic
# Copyright (C) 2008 by UPi <upi at sourceforge.net>
##########################################################################

use strict;

package main;

our ($App, $BrandyFont, @BossRegistry, @Cos, $DataDir, $DifficultySetting, @DifficultySettingNames, %Events, $FullScreen,
$Game, %GameEvents, @GameObjects, $LastUnicodeKey, %MenuEvents, $MenuFont, $MusicEnabled, $NumGuys,
$PhysicalScreenWidth, $PhysicalScreenHeight, @Players, $ScreenWidth, $ScreenHeight, @Sin, $SoundEnabled,
%Textures, $TutorialPlayed, $UnicodeMode, $Version);

sub DoMenu {
  $Game = new Menu;
  my $retval = $Game->Run();
  &SaveConfig();
  return $retval;
}


##########################################################################
package MenuItem;
##########################################################################

@MenuItem::ISA = qw(GameObject);
use vars qw($Gravity $NormalColor $SelectedColor);
$Gravity = 0.2;
$NormalColor = [0.8,0.8,0.8,1];
$SelectedColor = [1,1,0,1];

sub new {
  my ($class, $x, $y, $text, %options) = @_;
  my ($self);

  $self = new GameObject;
  %{$self} = ( %$self,
    'targetX' => $x,
    'targetY' => $y,
    'h' => 40,
    'selected' => 0,
    'filled' => 0,
    'parameter' => 0,
    'color' => $NormalColor,
  );
  %{$self} = ( %$self, %options )  if %options;
  
  bless $self, $class;
  $self->SetText($text);
  $self->SetInitialSpeed();
  return $self;
}

sub Center {
  my ($self, $center) = @_;
  
  my $oldTargetX = $self->{targetX};
  $center = $ScreenWidth / 2  unless $center;
  $self->{targetX} = $center - $self->{w} / 2;
  $self->{x} += $self->{targetX} - $oldTargetX;
  $self->{centered} = 1;
}

sub Show {
  my $self = shift;
  return if $self->CanSelect();
  $self->SetInitialSpeed();
}

sub Hide {
  my $self = shift;
  return  if $self->{state} eq 'leaving';
  $self->{state} = 'leaving';
  $self->{speedY} = 0;
  $self->{speedX} = rand(10) - 5;
}

sub HideAndDelete {
  my $self = shift;
  $self->Hide();
  $self->{deleteAfterHiding} = 1;
}

sub Delete {
  my $self = shift;
  $self->{selected} = $self->{filled} = 0;
  $self->SUPER::Delete();
}

sub ApproachingSpeed {
  my ($position, $speed, $target) = @_;
    
  if ($position + $speed * abs($speed / $Gravity) / 2 > $target) {
    return $speed - $Gravity;
  } else {
    return $speed + $Gravity;
  }
}

sub Advance {
  my $self = shift;
  
  if ('entering' eq $self->{state}) {
    $self->{x} += $self->{speedX};
    $self->{y} += $self->{speedY};
    $self->{speedX} = &ApproachingSpeed($self->{x}, $self->{speedX}, $self->{targetX});
    $self->{speedY} = &ApproachingSpeed($self->{y}, $self->{speedY}, $self->{targetY});
    if ( abs($self->{x} - $self->{targetX}) + abs($self->{y} - $self->{targetY}) < 2 ) {
      $self->{x} = $self->{targetX};
      $self->{y} = $self->{targetY};
      $self->{state} = 'shown';
    }
  } elsif ('leaving' eq $self->{state}) {
    $self->{x} += $self->{speedX};
    $self->{y} += $self->{speedY};
    $self->{speedY} -= $Gravity;
    if ($self->{y} < - $self->{h}) {
      $self->{state} = 'hidden';
      $self->Delete() if $self->{deleteAfterHiding}
    }
  }
}

sub Draw {
  my $self = shift;
  my ($scale, $x, $factor);

  return if $self->{state} eq 'hidden';
  $x = $self->{x};
  if ($self->{selected}) {
    $factor = 20 / $self->{w};
    $factor = 0.1  if $factor > 0.1;
    $scale = 1.0 + $Sin[$Game->{anim}*7 % 800] * $factor;
    $x -= $self->{w} * ($scale - 1.0) / 2;
  }
  $MenuFont->pre_output();
  $MenuFont->output($x, $self->{y}, $self->{text}, $self->{selected} ? $SelectedColor : $self->{color}, undef, $scale);
  $MenuFont->post_output();
  ::glColor(1,1,1);
}

sub SetInitialSpeed {
  my $self = shift;
  
  $self->{x} = $self->{targetX} + rand(500) - 250;
  $self->{y} = -$self->{h};
  $self->{speedY} = sqrt( 2 * $Gravity * ($self->{targetY} - $self->{y}) );
  $self->{speedX} = 0;
  $self->{state} = 'entering';
}

sub InternalSetText {
  my ($self, $text) = @_;
  
  $self->{text} = $text;
  $self->{w} = $MenuFont->TextWidth($text);
  $self->Center()  if $self->{centered};
}

sub SetText {
  my ($self, $text) = @_;

  $self->{parameter} = '';
  $self->{basetext} = $text;
  $self->InternalSetText($text);
}

sub SetParameter {
  my ($self, $parameter) = @_;
  
  $self->{parameter} = $parameter;
  $self->InternalSetText($self->{basetext} . ' ' . $parameter);
}

sub Select {
  my ($self) = @_;

  foreach my $item (@GameObjects) {
    $item->{selected} = 0 if ref $item eq 'MenuItem';
  }
  $self->{selected} = 1;
  $Game->ShowTooltip( $self->{tooltip} );
}

sub CanSelect {
  my ($self) = @_;
  
  return $self->{state} =~ /(?:entering|shown)/;
}

sub InputText {
  my ($self, $maxLength, $filter) = @_;
  my ($text, $char);
  
  $maxLength = $maxLength || 20;
  $text = $self->{parameter} . '|';
  $self->SetParameter($text);
  SDL::EnableUnicode(1); $UnicodeMode = 1;
  while (1) {
    $LastUnicodeKey = 0;
    $Game->MenuAdvance();
    last  if $Game->{abortgame};
    if (%Events) {
      my ($key) = %Events;
      if ($key == ::SDLK_BACKSPACE) {
        substr($text, -2, 1, '');        # Remove next to last char
        $self->SetParameter($text);
      } elsif ($key == ::SDLK_RETURN) {
        last;
      } elsif (length($text) < $maxLength) {
        $char = chr($LastUnicodeKey);
        next  if $filter and not ($char =~ /$filter/);
        substr($text, -1, 0, chr($LastUnicodeKey));   # Insert before last char
        $self->SetParameter($text);
      }
    }
  }
  $text =~ s/\|$//;
  $self->SetParameter($text);
  
  SDL::EnableUnicode(0); $UnicodeMode = 0;
  if ($Game->{abortgame}) {
    return undef;
    $Game->{abortgame} = 0;
  }
  return $self->{parameter};
}

sub SetChoices {
  my ($self, $initialChoiceIndex, $choiceValues, $choiceLabels) = @_;
  
  $self->{choiceValues} = $choiceValues;
  $self->{choiceLabels} = $choiceLabels;
  $self->SetCurrentChoice($initialChoiceIndex);
  $self->{left} = \&PreviousChoice  unless $self->{left};
  $self->{right} = \&NextChoice  unless $self->{right};
}

sub SetChoicesByValue {
  my ($self, $initialChoiceValue, $choiceValues, $choiceLabels) = @_;
  my ($i);
  
  for ($i = 0; $i < @$choiceValues; ++$i) {
    if ($choiceValues->[$i] eq $initialChoiceValue) {
      return $self->SetChoices($i, $choiceValues, $choiceLabels);
    }
  }
  warn "SetChoicesByValue: initial value '$initialChoiceValue' not found in: " . join(':', @$choiceValues);
  return $self->SetChoices(0, $choiceValues, $choiceLabels);
}

sub SetCurrentChoice {
  my ($self, $currentChoiceIndex) = @_;
  my ($choices, $prefix, $postfix);
  
  $choices = $self->{choiceValues};
  return  unless $choices;
  $currentChoiceIndex = 0  if $currentChoiceIndex < 0;
  $currentChoiceIndex = scalar(@$choices)-1  if $currentChoiceIndex >= scalar(@$choices);
  $self->{currentChoiceIndex} = $currentChoiceIndex;
  $self->{currentChoiceValue} = $choices->[$currentChoiceIndex];
  $prefix = $currentChoiceIndex ? '< ' : '  ';
  $postfix = $currentChoiceIndex == scalar(@$choices)-1 ? '  ' : ' >';
  $self->SetText($prefix . ($self->{choiceLabels}->[$currentChoiceIndex] || $self->{currentChoiceValue}) . $postfix);
  $self->{onChoiceChanged}->($currentChoiceIndex, $self->{currentChoiceValue})  if $self->{onChoiceChanged};
}

sub NextChoice {
  my ($self) = @_;
  
  $self->SetCurrentChoice($self->{currentChoiceIndex} + 1);
}

sub PreviousChoice {
  my ($self) = @_;
  
  $self->SetCurrentChoice($self->{currentChoiceIndex} - 1);
}



##########################################################################
package Menu;
##########################################################################

@Menu::ISA = qw(GameBase);
use vars qw(@syms $LastGameChoice $LastBossChoice $LastNormalGameChoice $LastMiniGameChoice);
@syms = qw(UNKNOWN FIRST BACKSPACE TAB CLEAR RETURN PAUSE ESCAPE SPACE EXCLAIM QUOTEDBL HASH DOLLAR AMPERSAND QUOTE LEFTPAREN RIGHTPAREN ASTERISK PLUS COMMA MINUS PERIOD SLASH 0 1 2 3 4 5 6 7 8 9 COLON SEMICOLON LESS EQUALS GREATER QUESTION AT LEFTBRACKET BACKSLASH RIGHTBRACKET CARET UNDERSCORE BACKQUOTE a b c d e f g h i j k l m n o p q r s t u v w x y z DELETE WORLD_0 WORLD_1 WORLD_2 WORLD_3 WORLD_4 WORLD_5 WORLD_6 WORLD_7 WORLD_8 WORLD_9 WORLD_10 WORLD_11 WORLD_12 WORLD_13 WORLD_14 WORLD_15 WORLD_16 WORLD_17 WORLD_18 WORLD_19 WORLD_20 WORLD_21 WORLD_22 WORLD_23 WORLD_24 WORLD_25 WORLD_26 WORLD_27 WORLD_28 WORLD_29 WORLD_30 WORLD_31 WORLD_32 WORLD_33 WORLD_34 WORLD_35 WORLD_36 WORLD_37 WORLD_38 WORLD_39 WORLD_40 WORLD_41 WORLD_42 WORLD_43 WORLD_44 WORLD_45 WORLD_46 WORLD_47 WORLD_48 WORLD_49 WORLD_50 WORLD_51 WORLD_52 WORLD_53 WORLD_54 WORLD_55 WORLD_56 WORLD_57 WORLD_58 WORLD_59 WORLD_60 WORLD_61 WORLD_62 WORLD_63 WORLD_64 WORLD_65 WORLD_66 WORLD_67 WORLD_68 WORLD_69 WORLD_70 WORLD_71 WORLD_72 WORLD_73 WORLD_74 WORLD_75 WORLD_76 WORLD_77 WORLD_78 WORLD_79 WORLD_80 WORLD_81 WORLD_82 WORLD_83 WORLD_84 WORLD_85 WORLD_86 WORLD_87 WORLD_88 WORLD_89 WORLD_90 WORLD_91 WORLD_92 WORLD_93 WORLD_94 WORLD_95 KP0 KP1 KP2 KP3 KP4 KP5 KP6 KP7 KP8 KP9 KP_PERIOD KP_DIVIDE KP_MULTIPLY KP_MINUS KP_PLUS KP_ENTER KP_EQUALS UP DOWN RIGHT LEFT INSERT HOME END PAGEUP PAGEDOWN F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 NUMLOCK CAPSLOCK SCROLLOCK RSHIFT LSHIFT RCTRL LCTRL RALT LALT RMETA LMETA LSUPER RSUPER MODE COMPOSE HELP PRINT SYSREQ BREAK MENU POWER EURO UNDO LAST );

$LastGameChoice = 1;
$LastMiniGameChoice = 1;
$LastBossChoice = 0;
$LastNormalGameChoice = 0;

sub Exit {
  my $self = shift;
  
  &::SaveConfig();
  $self->SUPER::Exit();
}

sub ResetGame {
  my ($self) = @_;
  
  $self->SUPER::ResetGame();
  $self->{playback} = &::CreateGamePlayback('silent');
  unless ($self->{playback}) {
    $self->{playback} = &::LoadRecord("$DataDir/demo.txt", 'silent');
    unless (ref $self->{playback}) {
      delete $self->{playback};
      foreach (1..10) { new Ball; }
      new SuperBall;
    }
  }
  my $logo = new GameObject(
    'scale' => 0,
    'scale2' => 0,
    'rotationSpeed' => -0.05 * 200,
    'rotate' => 0.05 / 2 * 200 * 200,
    'rotate2' => 0,
    'y' => 0,
    'glitter' => 0,
    'draw' => sub {
      my ($self) = @_;
      my $x = 30; my $y = 410 + $self->{y};
      my $texture = $Textures{logo};
      my $scale = $self->{scale} * $self->{scale2};
      ::glLoadIdentity();
      ::glTranslate($x+371, $y+80, 0);
      ::glScale($scale, $scale, 1);
      ::glRotate($self->{rotate} + $self->{rotate2}, 0, 0, 1);
      $texture->Blit( 0-371, -80, 256, 160, 0, 0 );
      $texture->Blit( 256-371, -80, 256, 160, 0, 170 );
      $texture->Blit( 511-371, -80, 230, 160, 0, 340 );
      if ($self->{glitter}) {
        ::glBlendFunc(::GL_SRC_ALPHA,::GL_ONE);
        ::glColor(1,1,1,$self->{glitter} / 2);
        $texture->Blit( 0-371, -80, 256, 160, 0, 0 );
        $texture->Blit( 256-371, -80, 256, 160, 0, 170 );
        $texture->Blit( 511-371, -80, 230, 160, 0, 340 );
        ::glColor(1,1,1,1);
        ::glBlendFunc(::GL_SRC_ALPHA,::GL_ONE_MINUS_SRC_ALPHA);
      }
      ::glLoadIdentity();
    },
    'advance' => sub {
      my ($self) = @_;
      my $anim = $Game->{anim};
      if ($anim % 1000 >= 32) {
        $self->{glitter} = 0;
      } else {
        $self->{glitter} = 1 - $Cos[$anim % 1000 * 25];
      }
      $self->{scale2} = 1 + $Sin[$anim * 1.5 % 800] * 0.02;
      $self->{rotate2} = $Sin[$anim * 2 % 800] * 1.5;
      $self->{y} = $Cos[$anim * 4 % 800] * 15;
      if ($self->{scale} < 1) {
        $self->{scale} += 1/200;
      } else {
        $self->{scale} = 1;
      }
      if ($self->{rotate} < 1) {
        $self->{rotate} = 0;
      } else {
        $self->{rotate} += $self->{rotationSpeed};
        $self->{rotationSpeed} += 0.05;
      }
    },
  );
}

sub StopPlayback {
  my $self = shift;
  
  return  unless $self->{playback};
  $self->{playback}->Delete();
  $self->{playback} = undef;
  foreach (1..10) { new Ball; }
  new SuperBall;
}

sub DrawScoreBoard {
  my $self = shift;
  my ($tt, @tt, $x, $y);
  
  $tt = $self->{tooltip};
  @tt = ref $tt ? @$tt : ($tt);
  return  unless @tt;
  $y = 5;
  
  $MenuFont->pre_output;
  foreach (reverse @tt) {
    $x = ($ScreenWidth - $MenuFont->TextWidth($_)) / 2;
    $MenuFont->output( $x, $y, $_ );
    $y += 35;
  }
  $MenuFont->post_output;
}

sub ShowTooltip {
  my ($self, $tooltip) = @_;
  
  $tooltip = [::Ts('Vacuum Magic %s (C) 2008 by UPi (upi@apocalypse.rulez.org)', $Version),
    ::T("Use cursor keys to navigate menu, Enter to select"),
    ::T("P pauses the game, Esc quits")] unless $tooltip;
  $self->{tooltip} = $tooltip;
}

sub MenuAdvance {
  my $self = shift;

  my $advance = $self->CalculateAdvances();
  %Events = %MenuEvents = ();
  %GameEvents = ();
  &::HandleEvents();
  while ($advance--) {
    &::AdvanceBackground()  unless $self->{silent};
    $self->AdvanceGameObjects();
  }
  $self->DrawGame();
  $App->sync();
}

sub SetCurrentItemIndex {
  my ($self, $index) = @_;

  return if ($index < 0 or $index >= scalar @{$self->{menuItems}} or not $self->{menuItems}->[$index]->CanSelect());
  $self->{currentItemIndex} = $index;
  $self->{currentItem} = $self->{menuItems}->[$index];
  $self->{currentItem}->Select();
}

sub EnterSubMenu {
  my $self = shift;
  my ($recall, $menuItem);
  
  $recall->{oldItems} = $self->{menuItems};
  $recall->{oldCurrentItemIndex} = $self->{currentItemIndex};
  foreach $menuItem (@{$self->{menuItems}}) { $menuItem->Hide(); }
  $self->{menuItems} = [];
  
  return $recall;
}

sub LeaveSubMenu {
  my ($self, $recall) = @_;
  my ($menuItem);

  foreach $menuItem (@{$self->{menuItems}}) { $menuItem->HideAndDelete(); }
  $self->{menuItems} = $recall->{oldItems};
  foreach $menuItem (@{$self->{menuItems}}) { $menuItem->Show(); }
  $self->SetCurrentItemIndex($recall->{oldCurrentItemIndex});
  $self->{abortgame} = 0;
}

sub HandleUpDownKeys {
  my $self = shift;

  if ($MenuEvents{DOWN}) {
    $self->SetCurrentItemIndex( $self->{currentItemIndex} + 1 );
  }
  if ($MenuEvents{UP}) {
    $self->SetCurrentItemIndex( $self->{currentItemIndex} - 1 );
  }
}

sub KeyToText {
  my ($key) = @_;
  return "???"  unless $key;
  eval("::SDLK_$_() eq $key") and return ucfirst(lc($_)) foreach @syms;
  warn "No match for $key\n";
  return "???";
}

sub KeysToText {
  my $keys = shift;
  my ($retval);
  if ( $keys->[0] =~ /^[LRB](\d)+$/ ) {
    return "Joystick $1";
  }
  return join(' / ', map { &KeyToText($_) } @$keys );
}

sub MakeDifficultyItem {
  my ($x, $y) = @_;
  my ($menuItem);
  
  $menuItem = new MenuItem( $x, $y, ::T('Game Difficulty:'),
    tooltip => [ ::T("Select your desired difficulty level.") ],
    onChoiceChanged => \&OnDifficultyChanged,
  );
  $menuItem->SetChoices( $DifficultySetting,
    [0 .. $#DifficultySettingNames],
    [ map { ::T('Game Difficulty:') . ' ' . $_ } @DifficultySettingNames]
  );
  return $menuItem;
}

sub OnDifficultyChanged {
  my ($difficultySettingValue) = @_;
  $DifficultySetting = $difficultySettingValue;
}

sub MakeResolutionItem {
  my ($x, $y) = @_;
  my ($menuItem, @resolutions);
  
  @resolutions = (400, 480, 640, 800, 1024, 1280, 1600);
  ::RemoveFromList @resolutions, $PhysicalScreenWidth;
  @resolutions = sort { $a <=> $b } (@resolutions, $PhysicalScreenWidth);
  $menuItem = new MenuItem( $x, $y, ::T('Screen Resolution:'),
    tooltip => [ ::T("Set the screen size at which you want the game to run."), ::T("The larger the window, the slower the game runs."), ::T("You need to restart the game to apply this setting.") ],
    onChoiceChanged => \&OnResolutionChanged,
  );
  $menuItem->SetChoicesByValue( $PhysicalScreenWidth,
    \@resolutions,
    [ map { ::T('Screen Resolution:') . " $_ x " . $_ * 0.75 } @resolutions]
  );
  return $menuItem;
}

sub OnResolutionChanged {
  my ($currentChoiceIndex, $currentChoiceValue) = @_;
  &::ChangeResolution($currentChoiceValue);
}

sub UpdateSubMenu {
  my $self = shift;
  my $item;
  
  foreach $item (@{$self->{menuItems}}) {
    $item->{'update'}->($item)  if $item->{'update'};
  }
}

sub CenterSubMenu {
  my $self = shift;
  my $item;
  
  foreach $item (@{$self->{menuItems}}) {
    $item->Center();
  }
}

sub RunSubMenu {
  my ($self, $initialItem) = @_;
  my ($item, $action);
  
  $initialItem = $initialItem || 0;
  $self->SetCurrentItemIndex($initialItem);
  $self->UpdateSubMenu();
  while (1) {
    last  if $self->{abortgame} or $self->{result};
    $self->MenuAdvance();
    $self->HandleUpDownKeys();
    $item = $self->{currentItem};

    foreach $action ('left', 'right', 'button') {
      if ($MenuEvents{uc($action)}) {
        if ($item->{$action}) {
          $item->{$action}->($item);
          $self->UpdateSubMenu();
        }
      }
    }
  }
  return $self->{result};
}

sub Run {
  my $self = shift;
  
  $MenuFont = $BrandyFont;
  $self->ResetGame();
  my ($y, $yinc) = (400, -40);
  $self->{menuItems} = [
    new MenuItem( 100, $y += $yinc, ::T("Start Game"),
      'button' => sub { $self->DoStartGameMenu(); } ),
    new MenuItem( 100, $y += $yinc, ::T("Flight Recorder"),
      'tooltip' => [ ::T("You can view and save your games here."), ::T("You can even post your best flights on the Internet!")],
      'button' => sub { $self->DoRecorderMenu(); } ),
    new MenuItem( 100, $y += $yinc, ::T("Instructions"),
      'tooltip' => ::T("Need a little help?"),
      'button' => sub { $self->DoInstructionsMenu(); } ),
    new MenuItem( 100, $y += $yinc, ::T("Options"),
      'tooltip' => ::T("Various game settings"),
      'button' => sub { $self->DoOptionsMenu(); } ),
    new MenuItem( 100, $y += $yinc, ::T("Setup players"),
      'tooltip' => ::T("Set the number of players, setup keys and joysticks"),
      'button' => sub { $self->DoControlsMenu(); }, ),
    new MenuItem( 100, $y += $yinc, ::T("Exit Game"),
      'tooltip' => ::T("Press Enter to exit the game"),
      'button' => sub { $self->Exit() }, ),
  ];
  $self->CenterSubMenu();
  &GameTimer::ResetTimer();
  return $self->RunSubMenu();
}

sub DoStartGameMenu {
  my $self = shift;
  my $recall = $self->EnterSubMenu();
  my ($y, $yinc) = (400, -40);
  my ($normalGameMenu, $bossMenu);
  
  push @{$self->{menuItems}}, (
    new MenuItem( 100, $y += $yinc, ::T("Back to main menu"),
      'button' => sub { $self->{abortgame} = 1; } ),
    $normalGameMenu = new MenuItem( 100, $y += $yinc * 1.5, ::T("Normal Game"),
      'tooltip' => [ ::T("Regular gameplay, starting easy"),  ::T("and getting more difficult gradually."), ::T("Use the left and right keys to start from a higher level.") ],
      'button' => sub {
                    $self->CheckTutorial($normalGameMenu->{currentChoiceValue});
                    $LastNormalGameChoice = $normalGameMenu->{currentChoiceIndex} ;
                  },
    ),
    $bossMenu = new MenuItem( 100, $y += $yinc, ::T("Boss Battle"),
      'tooltip' => [ ::T("Only the boss levels?"), ::T("Enjoy!"), ::T("Also, use the left and right keys to choose a specific boss.") ],
      'button' => sub { 
                    $self->CheckTutorial($bossMenu->{currentChoiceValue});
                    $LastBossChoice = $bossMenu->{currentChoiceIndex} 
                  },
    ),
    new MenuItem( 100, $y += $yinc, ::T("Mini Games..."),
      'tooltip' => [ ::T("Enter this submenu for some more game modes."), ::T("These are less complex than the Normal Game"), ::T("Still fun, though") ],
      'button' => sub { $self->DoMiniGameMenu(); } ),
    &MakeDifficultyItem( 100, $y += $yinc ),
  );
  
  $bossMenu->SetChoices( $LastBossChoice,
    ['Bosses', map {"Bosses/$_"} @BossRegistry],
    [::T('Boss Battle'), map { eval "\$${_}::LevelName" } @BossRegistry] );
  $normalGameMenu->SetChoices( $LastNormalGameChoice,
    ['NormalGame', map {"NormalGame/$_"} (11, 21, 31, 41, 51, 61, 71, 81, 91)],
    [::T('Normal Game'), map {::Ts('Normal Game from level %s', $_)} (11, 21, 31, 41, 51, 61, 71, 81, 91)], );

  $self->CenterSubMenu();
  $self->RunSubMenu($LastGameChoice);
  $LastGameChoice = $self->{currentItemIndex};
  $self->LeaveSubMenu($recall);
}

sub DoMiniGameMenu {
  my $self = shift;
  my $recall = $self->EnterSubMenu();
  my ($y, $yinc) = (400, -40);
  
  push @{$self->{menuItems}}, (
    new MenuItem( 100, $y += $yinc, ::T("Back to main menu"),
      'button' => sub { $self->{abortgame} = 1; } ),
    new MenuItem( 100, $y += $yinc * 1.5, ::T("Target Practice"),
      'tooltip' => [ ::T("Just practice shooting at targets."), ::T("Easy!") ],
      'button' => sub { $self->{result} = 'TargetPractice'; } ),
    new MenuItem( 100, $y += $yinc, ::T("Nightmare Flight"),
      'tooltip' => [ ::T("This game starts at Incredibly Difficult,"), ::T("and then gets harder..") ],
      'button' => sub { $self->{result} = 'NightmareFlight'; } ),
    new MenuItem( 100, $y += $yinc, ::T("Asteroids"),
      'tooltip' => [ ::T("A version of the old space shooter, Asteroids..."), ::T("Vacuum Magic style!") ],
      'button' => sub { $self->{result} = 'AsteroidsGame'; } ),
    new MenuItem( 100, $y += $yinc, ::T("Pang Zero"),
      'tooltip' => [ ::T("An extended Pang Zero mix."), ::T("Incidentally, Pang Zero is a great game!!"), ::T("I wrote it myself! :)")  ],
      'button' => sub { $self->{result} = 'PangZeroGame'; } ),
  );
  
  $self->CenterSubMenu();
  $self->RunSubMenu($LastMiniGameChoice);
  $LastMiniGameChoice = $self->{currentItemIndex};
  $self->LeaveSubMenu($recall);
}

sub CheckTutorial {
  my ($self, $gameMode) = @_;
  
  return $self->{result} = $gameMode  if $TutorialPlayed;
  my $recall = $self->EnterSubMenu();
  my ($y, $yinc) = (400, -40);
  
  push @{$self->{menuItems}}, (
    new MenuItem( 100, $y += $yinc, ::T("Play The Tutorial First"),
      'tooltip' => [ ::T("Watch and learn the basics of the game."), ::T("You'll be ready to fly in no time!") ],
      'button' => sub { $self->{result} = 'Tutorial'; $TutorialPlayed = 1; } ),
    new MenuItem( 100, $y += $yinc, ::T("Skip The Tutorial"),
      'tooltip' => [ ::T("Just start the game already!"), ],
      'button' => sub { $self->{result} = $gameMode; $TutorialPlayed = 1 } ),
  );
  
  $self->CenterSubMenu();
  $self->RunSubMenu();
  $self->LeaveSubMenu($recall);
}

sub DoInstructionsMenu {
  my $self = shift;
  my $recall = $self->EnterSubMenu();
  my ($y, $yinc) = (400, -40);
  
  push @{$self->{menuItems}}, (
    new MenuItem( 100, $y += $yinc, ::T("Back to main menu"),
      'button' => sub { $self->{abortgame} = 1; } ),
    new MenuItem( 100, $y += $yinc * 1.5, ::T("Tutorial"),
      'tooltip' => [ ::T("Watch and learn the basics of the game."), ::T("You'll be ready to fly in no time!") ],
      'button' => sub { $self->{result} = 'Tutorial'; $TutorialPlayed = 1; } ),
    new MenuItem( 100, $y += $yinc, ::T("Know Your Enemies"),
      'tooltip' => ::T("Specificly, how tasty they are."),
      'button' => sub { $self->DoInstructions(); } ),
  );
  
  $self->RunSubMenu(1);
  $self->LeaveSubMenu($recall);
}

sub DoOptionsMenu {
  my $self = shift;
  my $recall = $self->EnterSubMenu();
  my ($y, $yinc) = (400, -40);
  push @{$self->{menuItems}}, (
    new MenuItem( 100, $y += $yinc, ::T("Back to main menu"),
      'button' => sub { $self->{abortgame} = 1; } ),
    new MenuItem( 100, $y += $yinc * 1.5, ::T("Sound:"),
      'tooltip' => ::T("Press Enter to turn sound effects on/off."),
      'update' => sub { my $item = shift; $item->SetParameter($SoundEnabled ? ::T('on') : ::T('off')); },
      'button' => sub { $SoundEnabled = 1 - $SoundEnabled; }, ),
    new MenuItem( 100, $y += $yinc, ::T("Music:"),
      'tooltip' => ::T("Press Enter to turn the background music on/off."),
      'update' => sub { my $item = shift; $item->SetParameter($::MusicEnabled ? ::T('on') : ::T('off')); },
      'button' => sub { &::SetMusicEnabled(1 - $MusicEnabled); }, ),
    new MenuItem( 100, $y += $yinc, ::T("Fullscreen:"),
      'tooltip' => [::T("Press Enter to toggle fullscreen mode on/off."), ::T("You may have to restart the game."), ::T("(Thank you, Microsoft.)")],
      'update' => sub { my $item = shift; $item->SetParameter($::FullScreen? ::T('on') : ::T('windowed')); },
      'button' => sub { $App->fullscreen; $FullScreen = $FullScreen ? 0 : 1; }
       ),
    &MakeResolutionItem( 68, $y += $yinc ),
    &MakeDifficultyItem( 68, $y += $yinc ),
#     new MenuItem( 100, $y += $yinc, "Show website at exit: ", 
#       'tooltip' => ["Should Pang Zero take you to our web site at exit?", "True enlightenment awaits you online!"], ),
  );

  $self->RunSubMenu();
  $self->LeaveSubMenu($recall);
}

sub DoControlsMenu {
  my $self = shift;
  my ($y, $yinc, $i, $menuItem, $recall, @keysAsText, @yPositions);
  
  $recall = $self->EnterSubMenu();
  ($y, $yinc) = (420, -38);
  push @{$self->{menuItems}}, (
    new MenuItem( 50, $y += $yinc, ::T("Back to main menu"),
      'button' => sub { $self->{abortgame} = 1; }, ),
    new MenuItem( 18, $y += $yinc, '< ' . ::T('Number of Players:'),
      'tooltip' => [::T("Use left and right key to set the number of players here."), ::T("The more the merrier!"), ::T("Don't forget to set their keys below.")],
      'update' => sub { my $item = shift; $item->SetParameter( $NumGuys . " >" ); },
      'left' => sub { --$NumGuys  if $NumGuys > 1; },
      'right' => sub { ++$NumGuys  if $NumGuys < scalar @Players; },
    ),
  );
  $self->CenterSubMenu();
  for ($i = 0; $i < scalar @Players; ++$i) {
    $yPositions[$i] = $y - 40 + $i * $yinc;
    push @keysAsText, new MenuItem( 220, $yPositions[$i], &KeysToText($Players[$i]->{keys}) );
    push @{$self->{menuItems}}, new MenuItem( 50, $yPositions[$i], ::Ts('Player %s', ($i+1)),
      'number' => $i,
      'player' => $Players[$i],
      'keysastext' => $keysAsText[$i],
      'tooltip' => ::T("Press Enter to change the keys for this player"),
      'update' => sub { my $item = shift; 
        if ($item->{number} < scalar $NumGuys) { 
          $item->Show(); $item->{keysastext}->Show();
        } else { 
          $item->Hide(); $item->{keysastext}->Hide(); };
      },
      'button' => sub { my $item = shift; $Game->InputPlayerKeys($item->{number}); }
    );
  }
  $self->{keysAsText} = \@keysAsText;
  $self->{yPositions} = \@yPositions;
  
  $self->SetCurrentItemIndex(1);
  $self->RunSubMenu();
  $self->LeaveSubMenu($recall);
  foreach (@keysAsText) { $_->HideAndDelete(); }
  delete $self->{yPositions};
  delete $self->{keysAsText};
}

sub InputPlayerKeys {
  my ($self, $playerNumber) = @_;
  my ($player, $key, $keysAsText, @prompts, $keyMenuItem);
  
  $player = $Players[$playerNumber];
  $key = 0;
  $keysAsText = $self->{keysAsText}->[$playerNumber];
  $self->{currentItem}->Hide();
  $keysAsText->Hide();
  @prompts = (::T("Press 'LEFT' key or joystick button"), ::T("Press 'RIGHT' key"), ::T("Press 'UP' key"), ::T("Press 'DOWN' key"), ::T("Press 'FIRE' key"));
  $keyMenuItem = new MenuItem( 100, $self->{yPositions}->[$playerNumber], $prompts[0] );
  $keyMenuItem->Select();
  
  while (1) {
    $self->MenuAdvance();
    if ($self->{abortgame}) {
      $self->{abortgame} = 0;
      goto endOfKeyEntry;
    }
    if (%Events) {
      my ($event) = %Events;
      if ($event =~ /^B(\d+)$/) {
        $player->{keys} = ["L$1", "R$1", "U$1", "D$1", "B$1"];
        last;
      }
      $player->{keys}->[$key] = $event;
      ++$key;
      last if $key >= scalar @prompts;
      $keyMenuItem->SetText($prompts[$key]);
    }
  }
  
  endOfKeyEntry:
  $self->{currentItem}->Show();
  $self->{currentItem}->Select();
  $keysAsText->SetText(&KeysToText($player->{keys}));
  $keysAsText->Show();
  $keyMenuItem->HideAndDelete();
}


sub DoInstructions {
  my $self = shift;
  my ($recall, @enemyList, @enemies, $enemyClass, $row, $col, $menuItem, $scoreMenuItem, $enemyDesc, $enemy);
  
  $self->StopPlayback();
  $recall = $self->EnterSubMenu();
  $self->ShowTooltip(' ');
  @enemyList = ( 'Ball', 'Bee', 'PangGuy', 'Witch', 'Dougar', 'CloudKill', 'Bat', 'FireBat', 'BouncyMoon', 'FlameElemental', 'Ghost', 'SeekerBot' );
  $row = $col = 0;
  foreach $enemyClass (@enemyList) {
    $enemy = eval "new $enemyClass";  die $@ if $@;
    $enemy->{speed} = 0;
    $menuItem = new MenuItem($col * 200,  330 - $row * 150, $enemy->GetName());
    $menuItem->Center( $col * 200 + 100 );
    $menuItem->{color} = [1,1,1,1];
    $scoreMenuItem = undef;
    $scoreMenuItem = new MenuItem($col * 200,  300 - $row * 150, $enemy->{score} . ' points')  if $enemy->{score};
    $scoreMenuItem->Center( $col * 200 + 100 )  if $scoreMenuItem;
    push @enemies, [ $enemy, $menuItem, $scoreMenuItem, $col, $row ];
    if (++$col >= 4) {
      $col = 0; ++$row;
    }
  }
  
  while (1) {
    my $advance = $self->CalculateAdvances();
    %Events = %MenuEvents = %GameEvents = ();
    &::HandleEvents();
    while ($advance--) {
      &::AdvanceBackground();
      $self->AdvanceGameObjects();
    }
    foreach $enemyDesc (@enemies) {
      $enemy = $enemyDesc->[0];
      $enemy->{x} = $enemyDesc->[3] * 200 + 100 - $enemy->{w}/2;
      $enemy->{y} = 370 - $enemyDesc->[4] * 150;
    }
    last  if $self->{abortgame} or $self->{result};
    $self->DrawGame();
    $App->sync();
  }

  foreach $enemyDesc (@enemies) {
    $enemyDesc->[0]->Delete();
    $enemyDesc->[1]->HideAndDelete();
    $enemyDesc->[2]->HideAndDelete()  if $enemyDesc->[2];
  }

  $self->LeaveSubMenu($recall);
}

1;
