DEADSOFTWARE

GUI: play scroll sound when using switches
[d2df-sdl.git] / src / game / g_gui.pas
index f1d86ae37244c61a559e996d4b124a7efb93ba0b..3181e6969102e55d77b1be621b7e46ab32158a79 100644 (file)
@@ -1,9 +1,8 @@
-(* Copyright (C)  DooM 2D:Forever Developers
+(* Copyright (C)  Doom 2D: Forever Developers
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *)
-{$MODE DELPHI}
+{$INCLUDE ../shared/a_modes.inc}
 unit g_gui;
 
 interface
 
 uses
-  e_graphics, e_input, e_log, g_playermodel, g_basic, MAPSTRUCT, wadreader;
+  {$IFDEF USE_MEMPOOL}mempool,{$ENDIF}
+  e_graphics, e_input, e_log, g_playermodel, g_basic, g_touch, MAPDEF, utils;
 
 const
   MAINMENU_HEADER_COLOR: TRGB = (R:255; G:255; B:255);
@@ -82,9 +82,9 @@ type
     lParam: LongInt;
   end;
 
-  TFontType = (FONT_TEXTURE, FONT_CHAR);
+  TFontType = (Texture, Character);
 
-  TFont = class(TObject)
+  TFont = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
   private
     ID: DWORD;
     FScale: Single;
@@ -108,27 +108,32 @@ type
   TOnChangeEvent = procedure(Sender: TGUIControl);
   TOnEnterEvent = procedure(Sender: TGUIControl);
 
-  TGUIControl = class
+  TGUIControl = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
   private
     FX, FY: Integer;
     FEnabled: Boolean;
     FWindow : TGUIWindow;
     FName: string;
     FUserData: Pointer;
+    FRightAlign: Boolean; //HACK! this works only for "normal" menus, only for menu text labels, and generally sux. sorry.
+    FMaxWidth: Integer; //HACK! used for right-aligning labels
   public
     constructor Create;
     procedure OnMessage(var Msg: TMessage); virtual;
     procedure Update; virtual;
     procedure Draw; virtual;
+    function GetWidth(): Integer; virtual;
+    function GetHeight(): Integer; virtual;
     function WantActivationKey (key: LongInt): Boolean; virtual;
     property X: Integer read FX write FX;
     property Y: Integer read FY write FY;
     property Enabled: Boolean read FEnabled write FEnabled;
     property Name: string read FName write FName;
     property UserData: Pointer read FUserData write FUserData;
+    property RightAlign: Boolean read FRightAlign write FRightAlign; // for menu
   end;
 
-  TGUIWindow = class
+  TGUIWindow = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
   private
     FActiveControl: TGUIControl;
     FDefControl: string;
@@ -172,13 +177,13 @@ type
   public
     Proc: procedure;
     ProcEx: procedure (sender: TGUITextButton);
-    constructor Create(Proc: Pointer; FontID: DWORD; Text: string);
+    constructor Create(aProc: Pointer; FontID: DWORD; Text: string);
     destructor Destroy(); override;
     procedure OnMessage(var Msg: TMessage); override;
     procedure Update(); override;
     procedure Draw(); override;
-    function GetWidth(): Integer;
-    function GetHeight(): Integer;
+    function GetWidth(): Integer; override;
+    function GetHeight(): Integer; override;
     procedure Click(Silent: Boolean = False);
     property Caption: string read FText write FText;
     property Color: TRGB read FColor write FColor;
@@ -197,8 +202,8 @@ type
     constructor Create(Text: string; FontID: DWORD);
     procedure OnMessage(var Msg: TMessage); override;
     procedure Draw; override;
-    function GetWidth: Integer;
-    function GetHeight: Integer;
+    function GetWidth: Integer; override;
+    function GetHeight: Integer; override;
     property OnClick: TOnClickEvent read FOnClickEvent write FOnClickEvent;
     property FixedLength: Word read FFixedLen write FFixedLen;
     property Text: string read FText write FText;
@@ -221,7 +226,7 @@ type
     procedure OnMessage(var Msg: TMessage); override;
     procedure Update; override;
     procedure Draw; override;
-    function GetWidth(): Word;
+    function GetWidth(): Integer; override;
     property OnChange: TOnChangeEvent read FOnChangeEvent write FOnChangeEvent;
     property Max: Word read FMax write FMax;
     property Value: Integer read FValue write FSetValue;
@@ -240,7 +245,7 @@ type
     procedure AddItem(Item: string);
     procedure Update; override;
     procedure Draw; override;
-    function GetWidth(): Word;
+    function GetWidth(): Integer; override;
     function GetText: string;
     property ItemIndex: Integer read FIndex write FIndex;
     property Color: TRGB read FColor write FColor;
@@ -262,13 +267,14 @@ type
     FMiddleID: DWORD;
     FOnChangeEvent: TOnChangeEvent;
     FOnEnterEvent: TOnEnterEvent;
+    FInvalid: Boolean;
     procedure SetText(Text: string);
   public
     constructor Create(FontID: DWORD);
     procedure OnMessage(var Msg: TMessage); override;
     procedure Update; override;
     procedure Draw; override;
-    function GetWidth(): Word;
+    function GetWidth(): Integer; override;
     property OnChange: TOnChangeEvent read FOnChangeEvent write FOnChangeEvent;
     property OnEnter: TOnEnterEvent read FOnEnterEvent write FOnEnterEvent;
     property Width: Word read FWidth write FWidth;
@@ -277,6 +283,7 @@ type
     property Text: string read FText write SetText;
     property Color: TRGB read FColor write FColor;
     property Font: TFont read FFont write FFont;
+    property Invalid: Boolean read FInvalid write FInvalid;
   end;
 
   TGUIKeyRead = class(TGUIControl)
@@ -289,7 +296,7 @@ type
     constructor Create(FontID: DWORD);
     procedure OnMessage(var Msg: TMessage); override;
     procedure Draw; override;
-    function GetWidth(): Word;
+    function GetWidth(): Integer; override;
     function WantActivationKey (key: LongInt): Boolean; override;
     property Key: Word read FKey write FKey;
     property Color: TRGB read FColor write FColor;
@@ -310,7 +317,7 @@ type
     constructor Create(FontID: DWORD);
     procedure OnMessage(var Msg: TMessage); override;
     procedure Draw; override;
-    function GetWidth(): Word;
+    function GetWidth(): Integer; override;
     function WantActivationKey (key: LongInt): Boolean; override;
     property Key0: Word read FKey0 write FKey0;
     property Key1: Word read FKey1 write FKey1;
@@ -343,7 +350,7 @@ type
   TGUIMapPreview = class(TGUIControl)
   private
     FMapData: array of TPreviewPanel;
-    FMapSize: TPoint;
+    FMapSize: TDFPoint;
     FScale: Single;
   public
     constructor Create();
@@ -373,7 +380,7 @@ type
 
   TGUIListBox = class(TGUIControl)
   private
-    FItems: SArray;
+    FItems: SSArray;
     FActiveColor: TRGB;
     FUnActiveColor: TRGB;
     FFont: TFont;
@@ -386,7 +393,7 @@ type
     FDrawScroll: Boolean;
     FOnChangeEvent: TOnChangeEvent;
 
-    procedure FSetItems(Items: SArray);
+    procedure FSetItems(Items: SSArray);
     procedure FSetIndex(aIndex: Integer);
 
   public
@@ -394,16 +401,17 @@ type
     procedure OnMessage(var Msg: TMessage); override;
     procedure Draw(); override;
     procedure AddItem(Item: String);
+    function ItemExists (item: String): Boolean;
     procedure SelectItem(Item: String);
     procedure Clear();
-    function  GetWidth(): Word;
-    function  GetHeight(): Word;
+    function  GetWidth(): Integer; override;
+    function  GetHeight(): Integer; override;
     function  SelectedItem(): String;
 
     property OnChange: TOnChangeEvent read FOnChangeEvent write FOnChangeEvent;
     property Sort: Boolean read FSort write FSort;
     property ItemIndex: Integer read FIndex write FSetIndex;
-    property Items: SArray read FItems write FSetItems;
+    property Items: SSArray read FItems write FSetItems;
     property DrawBack: Boolean read FDrawBack write FDrawBack;
     property DrawScrollBar: Boolean read FDrawScroll write FDrawScroll;
     property ActiveColor: TRGB read FActiveColor write FActiveColor;
@@ -411,29 +419,28 @@ type
     property Font: TFont read FFont write FFont;
   end;
 
-  TGUIFileListBox = class (TGUIListBox)
+  TGUIFileListBox = class(TGUIListBox)
   private
-    FBasePath: String;
-    FPath: String;
+    FSubPath: String;
     FFileMask: String;
     FDirs: Boolean;
+    FBaseList: SSArray; // highter index have highter priority
 
-    procedure OpenDir(path: String);
+    procedure ScanDirs;
 
   public
-    procedure OnMessage(var Msg: TMessage); override;
-    procedure SetBase(path: String);
+    procedure OnMessage (var Msg: TMessage); override;
+    procedure SetBase (dirs: SSArray; path: String = '');
     function  SelectedItem(): String;
-    procedure UpdateFileList();
+    procedure UpdateFileList;
 
     property Dirs: Boolean read FDirs write FDirs;
     property FileMask: String read FFileMask write FFileMask;
-    property Path: String read FPath;
   end;
 
   TGUIMemo = class(TGUIControl)
   private
-    FLines: SArray;
+    FLines: SSArray;
     FFont: TFont;
     FStartLine: Integer;
     FWidth: Word;
@@ -446,8 +453,8 @@ type
     procedure OnMessage(var Msg: TMessage); override;
     procedure Draw; override;
     procedure Clear;
-    function GetWidth(): Word;
-    function GetHeight(): Word;
+    function GetWidth(): Integer; override;
+    function GetHeight(): Integer; override;
     procedure SetText(Text: string);
     property DrawBack: Boolean read FDrawBack write FDrawBack;
     property DrawScrollBar: Boolean read FDrawScroll write FDrawScroll;
@@ -459,18 +466,19 @@ type
   private
     FButtons: array of TGUITextButton;
     FHeader: TGUILabel;
+    FLogo: DWord;
     FIndex: Integer;
     FFontID: DWORD;
     FCounter: Byte;
     FMarkerID1: DWORD;
     FMarkerID2: DWORD;
   public
-    constructor Create(FontID: DWORD; Header: string);
+    constructor Create(FontID: DWORD; Logo, Header: string);
     destructor Destroy; override;
     procedure OnMessage(var Msg: TMessage); override;
     function AddButton(fProc: Pointer; Caption: string; ShowWindow: string = ''): TGUITextButton;
-    function GetButton(Name: string): TGUITextButton;
-    procedure EnableButton(Name: string; e: Boolean);
+    function GetButton(aName: string): TGUITextButton;
+    procedure EnableButton(aName: string; e: Boolean);
     procedure AddSpace();
     procedure Update; override;
     procedure Draw; override;
@@ -514,8 +522,8 @@ type
     function AddFileList(fText: string; Width, Height: Word): TGUIFileListBox;
     function AddMemo(fText: string; Width, Height: Word): TGUIMemo;
     procedure ReAlign();
-    function GetControl(Name: string): TGUIControl;
-    function GetControlsText(Name: string): TGUILabel;
+    function GetControl(aName: string): TGUIControl;
+    function GetControlsText(aName: string): TGUILabel;
     procedure Draw; override;
     procedure Update; override;
     procedure UpdateIndex();
@@ -527,6 +535,7 @@ type
 var
   g_GUIWindows: array of TGUIWindow;
   g_ActiveWindow: TGUIWindow = nil;
+  g_GUIGrabInput: Boolean = False;
 
 procedure g_GUI_Init();
 function  g_GUI_AddWindow(Window: TGUIWindow): TGUIWindow;
@@ -537,16 +546,20 @@ function  g_GUI_Destroy(): Boolean;
 procedure g_GUI_SaveMenuPos();
 procedure g_GUI_LoadMenuPos();
 
+
 implementation
 
 uses
-  GL, GLExt, g_textures, g_sound, SysUtils,
-  g_game, Math, StrUtils, g_player, g_options, MAPREADER,
-  g_map, MAPDEF, g_weapons;
+  {$INCLUDE ../nogl/noGLuses.inc}
+  g_textures, g_sound, SysUtils, e_res,
+  g_game, Math, StrUtils, g_player, g_options,
+  g_map, g_weapons, xdynrec, wadreader;
+
 
 var
   Box: Array [0..8] of DWORD;
-  Saved_Windows: SArray;
+  Saved_Windows: SSArray;
+
 
 procedure g_GUI_Init();
 begin
@@ -801,13 +814,26 @@ procedure TGUIWindow.Draw;
 var
   i: Integer;
   ID: DWORD;
+  tw, th: Word;
 begin
-  if FBackTexture <> '' then
+  if FBackTexture <> '' then  // Here goes code duplication from g_game.pas:DrawMenuBackground()
     if g_Texture_Get(FBackTexture, ID) then
-      e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
+    begin
+      e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
+      e_GetTextureSize(ID, @tw, @th);
+      if tw = th then
+        tw := round(tw * 1.333 * (gScreenHeight / th))
+      else
+        tw := trunc(tw * (gScreenHeight / th));
+      e_DrawSize(ID, (gScreenWidth - tw) div 2, 0, 0, False, False, tw, gScreenHeight);
+    end
     else
       e_Clear(GL_COLOR_BUFFER_BIT, 0.5, 0.5, 0.5);
 
+  // small hack here
+  if FName = 'AuthorsMenu' then
+    e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
+
   for i := 0 to High(Childs) do
     if Childs[i] <> nil then Childs[i].Draw;
 end;
@@ -819,11 +845,15 @@ begin
   if @FOnKeyDownEx <> nil then FOnKeyDownEx(self, Msg.wParam);
 
   if Msg.Msg = WM_KEYDOWN then
-    if Msg.wParam = IK_ESCAPE then
-    begin
-      g_GUI_HideWindow;
-      Exit;
-    end;
+  begin
+    case Msg.wParam of
+      VK_ESCAPE:
+        begin
+          g_GUI_HideWindow;
+          Exit
+        end
+    end
+  end
 end;
 
 procedure TGUIWindow.SetActive(Control: TGUIControl);
@@ -857,6 +887,8 @@ begin
   FY := 0;
 
   FEnabled := True;
+  FRightAlign := false;
+  FMaxWidth := -1;
 end;
 
 procedure TGUIControl.OnMessage(var Msg: TMessage);
@@ -878,6 +910,16 @@ begin
   result := false;
 end;
 
+function TGUIControl.GetWidth(): Integer;
+begin
+  result := 0;
+end;
+
+function TGUIControl.GetHeight(): Integer;
+begin
+  result := 0;
+end;
+
 { TGUITextButton }
 
 procedure TGUITextButton.Click(Silent: Boolean = False);
@@ -890,14 +932,14 @@ begin
   if FShowWindow <> '' then g_GUI_ShowWindow(FShowWindow);
 end;
 
-constructor TGUITextButton.Create(Proc: Pointer; FontID: DWORD; Text: string);
+constructor TGUITextButton.Create(aProc: Pointer; FontID: DWORD; Text: string);
 begin
   inherited Create();
 
-  Self.Proc := Proc;
+  Self.Proc := aProc;
   ProcEx := nil;
 
-  FFont := TFont.Create(FontID, FONT_CHAR);
+  FFont := TFont.Create(FontID, TFontType.Character);
 
   FText := Text;
 end;
@@ -938,7 +980,7 @@ begin
   case Msg.Msg of
     WM_KEYDOWN:
       case Msg.wParam of
-        IK_RETURN, IK_KPRETURN: Click();
+        IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK: Click();
       end;
   end;
 end;
@@ -966,7 +1008,7 @@ end;
 
 procedure TFont.Draw(X, Y: Integer; Text: string; R, G, B: Byte);
 begin
-  if FFontType = FONT_CHAR then e_CharFont_PrintEx(ID, X, Y, Text, _RGB(R, G, B), FScale)
+  if FFontType = TFontType.Character then e_CharFont_PrintEx(ID, X, Y, Text, _RGB(R, G, B), FScale)
   else e_TextureFontPrintEx(X, Y, Text, ID, R, G, B, FScale);
 end;
 
@@ -974,7 +1016,7 @@ procedure TFont.GetTextSize(Text: string; var w, h: Word);
 var
   cw, ch: Byte;
 begin
-  if FFontType = FONT_CHAR then e_CharFont_GetSize(ID, Text, w, h)
+  if FFontType = TFontType.Character then e_CharFont_GetSize(ID, Text, w, h)
   else
   begin
     e_TextureFontGetSize(ID, cw, ch);
@@ -992,6 +1034,7 @@ function TGUIMainMenu.AddButton(fProc: Pointer; Caption: string; ShowWindow: str
 var
   a, _x: Integer;
   h, hh: Word;
+  lh: Word = 0;
 begin
   FIndex := 0;
 
@@ -1011,18 +1054,21 @@ begin
     if FButtons[a] <> nil then
       _x := Min(_x, (gScreenWidth div 2)-(FButtons[a].GetWidth div 2));
 
-  hh := FHeader.GetHeight;
+  if FLogo <> 0 then e_GetTextureSize(FLogo, nil, @lh);
+  hh := FButtons[High(FButtons)].GetHeight;
 
-  h := hh*(2+Length(FButtons))+MAINMENU_SPACE*(Length(FButtons)-1);
-  h := (gScreenHeight div 2)-(h div 2);
+  if FLogo <> 0 then h := lh + hh * (1 + Length(FButtons)) + MAINMENU_SPACE * (Length(FButtons) - 1)
+  else h := hh * (2 + Length(FButtons)) + MAINMENU_SPACE * (Length(FButtons) - 1);
+  h := (gScreenHeight div 2) - (h div 2);
 
-  with FHeader do
+  if FHeader <> nil then with FHeader do
   begin
     FX := _x;
     FY := h;
   end;
 
-  Inc(h, hh*2);
+  if FLogo <> 0 then Inc(h, lh)
+  else Inc(h, hh*2);
 
   for a := 0 to High(FButtons) do
   begin
@@ -1045,7 +1091,7 @@ begin
   FButtons[High(FButtons)] := nil;
 end;
 
-constructor TGUIMainMenu.Create(FontID: DWORD; Header: string);
+constructor TGUIMainMenu.Create(FontID: DWORD; Logo, Header: string);
 begin
   inherited Create();
 
@@ -1056,12 +1102,15 @@ begin
   g_Texture_Get(MAINMENU_MARKER1, FMarkerID1);
   g_Texture_Get(MAINMENU_MARKER2, FMarkerID2);
 
-  FHeader := TGUILabel.Create(Header, FFontID);
-  with FHeader do
+  if not g_Texture_Get(Logo, FLogo) then
   begin
-    FColor := MAINMENU_HEADER_COLOR;
-    FX := (gScreenWidth div 2)-(GetWidth div 2);
-    FY := (gScreenHeight div 2)-(GetHeight div 2);
+    FHeader := TGUILabel.Create(Header, FFontID);
+    with FHeader do
+    begin
+      FColor := MAINMENU_HEADER_COLOR;
+      FX := (gScreenWidth div 2)-(GetWidth div 2);
+      FY := (gScreenHeight div 2)-(GetHeight div 2);
+    end;
   end;
 end;
 
@@ -1081,10 +1130,16 @@ end;
 procedure TGUIMainMenu.Draw;
 var
   a: Integer;
+  w, h: Word;
+
 begin
   inherited;
 
-  FHeader.Draw;
+  if FHeader <> nil then FHeader.Draw
+  else begin
+    e_GetTextureSize(FLogo, @w, @h);
+    e_Draw(FLogo, ((gScreenWidth div 2) - (w div 2)), FButtons[0].FY - FButtons[0].GetHeight - h, 0, True, False);
+  end;
 
   if FButtons <> nil then
   begin
@@ -1096,14 +1151,14 @@ begin
   end;
 end;
 
-procedure TGUIMainMenu.EnableButton(Name: string; e: Boolean);
+procedure TGUIMainMenu.EnableButton(aName: string; e: Boolean);
 var
   a: Integer;
 begin
   if FButtons = nil then Exit;
 
   for a := 0 to High(FButtons) do
-    if (FButtons[a] <> nil) and (FButtons[a].Name = Name) then
+    if (FButtons[a] <> nil) and (FButtons[a].Name = aName) then
     begin
       if e then FButtons[a].FColor := MAINMENU_ITEMS_COLOR
       else FButtons[a].FColor := MAINMENU_UNACTIVEITEMS_COLOR;
@@ -1112,7 +1167,7 @@ begin
     end;
 end;
 
-function TGUIMainMenu.GetButton(Name: string): TGUITextButton;
+function TGUIMainMenu.GetButton(aName: string): TGUITextButton;
 var
   a: Integer;
 begin
@@ -1121,7 +1176,7 @@ begin
   if FButtons = nil then Exit;
 
   for a := 0 to High(FButtons) do
-    if (FButtons[a] <> nil) and (FButtons[a].Name = Name) then
+    if (FButtons[a] <> nil) and (FButtons[a].Name = aName) then
     begin
       Result := FButtons[a];
       Break;
@@ -1152,7 +1207,7 @@ begin
   case Msg.Msg of
     WM_KEYDOWN:
       case Msg.wParam of
-        IK_UP, IK_KPUP:
+        IK_UP, IK_KPUP, VK_UP, JOY0_UP, JOY1_UP, JOY2_UP, JOY3_UP:
         begin
           repeat
             Dec(FIndex);
@@ -1161,7 +1216,7 @@ begin
 
           g_Sound_PlayEx(MENU_CHANGESOUND);
         end;
-        IK_DOWN, IK_KPDOWN:
+        IK_DOWN, IK_KPDOWN, VK_DOWN, JOY0_DOWN, JOY1_DOWN, JOY2_DOWN, JOY3_DOWN:
         begin
           repeat
             Inc(FIndex);
@@ -1170,7 +1225,7 @@ begin
 
           g_Sound_PlayEx(MENU_CHANGESOUND);
         end;
-        IK_RETURN, IK_KPRETURN: if (FIndex <> -1) and FButtons[FIndex].FEnabled then FButtons[FIndex].Click;
+        IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK: if (FIndex <> -1) and FButtons[FIndex].FEnabled then FButtons[FIndex].Click;
       end;
   end;
 end;
@@ -1197,7 +1252,7 @@ constructor TGUILabel.Create(Text: string; FontID: DWORD);
 begin
   inherited Create();
 
-  FFont := TFont.Create(FontID, FONT_CHAR);
+  FFont := TFont.Create(FontID, TFontType.Character);
 
   FText := Text;
   FFixedLen := 0;
@@ -1205,8 +1260,18 @@ begin
 end;
 
 procedure TGUILabel.Draw;
+var
+  w, h: Word;
 begin
-  FFont.Draw(FX, FY, FText, FColor.R, FColor.G, FColor.B);
+  if RightAlign then
+  begin
+    FFont.GetTextSize(FText, w, h);
+    FFont.Draw(FX+FMaxWidth-w, FY, FText, FColor.R, FColor.G, FColor.B);
+  end
+  else
+  begin
+    FFont.Draw(FX, FY, FText, FColor.R, FColor.G, FColor.B);
+  end;
 end;
 
 function TGUILabel.GetHeight: Integer;
@@ -1237,7 +1302,7 @@ begin
   case Msg.Msg of
     WM_KEYDOWN:
       case Msg.wParam of
-        IK_RETURN, IK_KPRETURN: if @FOnClickEvent <> nil then FOnClickEvent();
+        IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK: if @FOnClickEvent <> nil then FOnClickEvent();
       end;
   end;
 end;
@@ -1291,7 +1356,7 @@ end;
 procedure TGUIMenu.AddText(fText: string; MaxWidth: Word);
 var
   a, i: Integer;
-  l: SArray;
+  l: SSArray;
 begin
   l := GetLines(fText, FFontID, MaxWidth);
 
@@ -1374,7 +1439,7 @@ end;
 
 procedure TGUIMenu.Draw;
 var
-  a, x, y: Integer;
+  a, locx, locy: Integer;
 begin
   inherited;
 
@@ -1389,27 +1454,32 @@ begin
 
   if (FIndex <> -1) and (FCounter > MENU_MARKERDELAY div 2) then
   begin
-    x := 0;
-    y := 0;
+    locx := 0;
+    locy := 0;
 
     if FItems[FIndex].Text <> nil then
     begin
-      x := FItems[FIndex].Text.FX;
-      y := FItems[FIndex].Text.FY;
+      locx := FItems[FIndex].Text.FX;
+      locy := FItems[FIndex].Text.FY;
+      //HACK!
+      if FItems[FIndex].Text.RightAlign then
+      begin
+        locx := locx+FItems[FIndex].Text.FMaxWidth-FItems[FIndex].Text.GetWidth;
+      end;
     end
     else if FItems[FIndex].Control <> nil then
     begin
-      x := FItems[FIndex].Control.FX;
-      y := FItems[FIndex].Control.FY;
+      locx := FItems[FIndex].Control.FX;
+      locy := FItems[FIndex].Control.FY;
     end;
 
-    x := x-e_CharFont_GetMaxWidth(FFontID);
+    locx := locx-e_CharFont_GetMaxWidth(FFontID);
 
-    e_CharFont_PrintEx(FFontID, x, y, #16, _RGB(255, 0, 0));
+    e_CharFont_PrintEx(FFontID, locx, locy, #16, _RGB(255, 0, 0));
   end;
 end;
 
-function TGUIMenu.GetControl(Name: String): TGUIControl;
+function TGUIMenu.GetControl(aName: String): TGUIControl;
 var
   a: Integer;
 begin
@@ -1418,16 +1488,16 @@ begin
   if FItems <> nil then
     for a := 0 to High(FItems) do
       if FItems[a].Control <> nil then
-        if LowerCase(FItems[a].Control.Name) = LowerCase(Name) then
+        if LowerCase(FItems[a].Control.Name) = LowerCase(aName) then
         begin
           Result := FItems[a].Control;
           Break;
         end;
 
-  Assert(Result <> nil, 'GUI control "'+Name+'" not found!');
+  Assert(Result <> nil, 'GUI control "'+aName+'" not found!');
 end;
 
-function TGUIMenu.GetControlsText(Name: String): TGUILabel;
+function TGUIMenu.GetControlsText(aName: String): TGUILabel;
 var
   a: Integer;
 begin
@@ -1436,13 +1506,13 @@ begin
   if FItems <> nil then
     for a := 0 to High(FItems) do
       if FItems[a].Control <> nil then
-        if LowerCase(FItems[a].Control.Name) = LowerCase(Name) then
+        if LowerCase(FItems[a].Control.Name) = LowerCase(aName) then
         begin
           Result := FItems[a].Text;
           Break;
         end;
 
-  Assert(Result <> nil, 'GUI control''s text "'+Name+'" not found!');
+  Assert(Result <> nil, 'GUI control''s text "'+aName+'" not found!');
 end;
 
 function TGUIMenu.NewItem: Integer;
@@ -1484,7 +1554,7 @@ begin
     WM_KEYDOWN:
     begin
       case Msg.wParam of
-        IK_UP, IK_KPUP:
+        IK_UP, IK_KPUP, VK_UP,JOY0_UP, JOY1_UP, JOY2_UP, JOY3_UP:
         begin
           c := 0;
           repeat
@@ -1505,7 +1575,7 @@ begin
           g_Sound_PlayEx(MENU_CHANGESOUND);
         end;
 
-        IK_DOWN, IK_KPDOWN:
+        IK_DOWN, IK_KPDOWN, VK_DOWN, JOY0_DOWN, JOY1_DOWN, JOY2_DOWN, JOY3_DOWN:
         begin
           c := 0;
           repeat
@@ -1526,13 +1596,15 @@ begin
           g_Sound_PlayEx(MENU_CHANGESOUND);
         end;
 
-        IK_LEFT, IK_RIGHT, IK_KPLEFT, IK_KPRIGHT:
+        IK_LEFT, IK_RIGHT, IK_KPLEFT, IK_KPRIGHT, VK_LEFT, VK_RIGHT,
+        JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT,
+       JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT:
         begin
           if FIndex <> -1 then
             if FItems[FIndex].Control <> nil then
               FItems[FIndex].Control.OnMessage(Msg);
         end;
-        IK_RETURN, IK_KPRETURN:
+        IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK:
         begin
           if FIndex <> -1 then
           begin
@@ -1563,10 +1635,27 @@ end;
 procedure TGUIMenu.ReAlign();
 var
   a, tx, cx, w, h: Integer;
+  cww: array of Integer; // cached widths
+  maxcww: Integer;
 begin
   if FItems = nil then Exit;
 
-  if not FAlign then tx := FLeft else
+  SetLength(cww, length(FItems));
+  maxcww := 0;
+  for a := 0 to High(FItems) do
+  begin
+    if FItems[a].Text <> nil then
+    begin
+      cww[a] := FItems[a].Text.GetWidth;
+      if maxcww < cww[a] then maxcww := cww[a];
+    end;
+  end;
+
+  if not FAlign then
+  begin
+    tx := FLeft;
+  end
+  else
   begin
     tx := gScreenWidth;
     for a := 0 to High(FItems) do
@@ -1576,29 +1665,17 @@ begin
       if FItems[a].Control <> nil then
       begin
         w := w+MENU_HSPACE;
-
-        if FItems[a].ControlType = TGUILabel then
-          w := w+(FItems[a].Control as TGUILabel).GetWidth
-        else if FItems[a].ControlType = TGUITextButton then
-          w := w+(FItems[a].Control as TGUITextButton).GetWidth
-        else if FItems[a].ControlType = TGUIScroll then
-          w := w+(FItems[a].Control as TGUIScroll).GetWidth
-        else if FItems[a].ControlType = TGUISwitch then
-          w := w+(FItems[a].Control as TGUISwitch).GetWidth
-        else if FItems[a].ControlType = TGUIEdit then
-          w := w+(FItems[a].Control as TGUIEdit).GetWidth
-        else if FItems[a].ControlType = TGUIKeyRead then
-          w := w+(FItems[a].Control as TGUIKeyRead).GetWidth
-        else if FItems[a].ControlType = TGUIKeyRead2 then
-          w := w+(FItems[a].Control as TGUIKeyRead2).GetWidth
-        else if (FItems[a].ControlType = TGUIListBox) then
-          w := w+(FItems[a].Control as TGUIListBox).GetWidth
-        else if (FItems[a].ControlType = TGUIFileListBox) then
-          w := w+(FItems[a].Control as TGUIFileListBox).GetWidth
-        else if FItems[a].ControlType = TGUIMemo then
-          w := w+(FItems[a].Control as TGUIMemo).GetWidth;
+             if FItems[a].ControlType = TGUILabel then w := w+(FItems[a].Control as TGUILabel).GetWidth
+        else if FItems[a].ControlType = TGUITextButton then w := w+(FItems[a].Control as TGUITextButton).GetWidth
+        else if FItems[a].ControlType = TGUIScroll then w := w+(FItems[a].Control as TGUIScroll).GetWidth
+        else if FItems[a].ControlType = TGUISwitch then w := w+(FItems[a].Control as TGUISwitch).GetWidth
+        else if FItems[a].ControlType = TGUIEdit then w := w+(FItems[a].Control as TGUIEdit).GetWidth
+        else if FItems[a].ControlType = TGUIKeyRead then w := w+(FItems[a].Control as TGUIKeyRead).GetWidth
+        else if FItems[a].ControlType = TGUIKeyRead2 then w := w+(FItems[a].Control as TGUIKeyRead2).GetWidth
+        else if FItems[a].ControlType = TGUIListBox then w := w+(FItems[a].Control as TGUIListBox).GetWidth
+        else if FItems[a].ControlType = TGUIFileListBox then w := w+(FItems[a].Control as TGUIFileListBox).GetWidth
+        else if FItems[a].ControlType = TGUIMemo then w := w+(FItems[a].Control as TGUIMemo).GetWidth;
       end;
-
       tx := Min(tx, (gScreenWidth div 2)-(w div 2));
     end;
   end;
@@ -1641,35 +1718,46 @@ begin
   end;
 
   for a := 0 to High(FItems) do
+  begin
     with FItems[a] do
     begin
       if Text <> nil then
+      begin
         with Text do
         begin
           FX := tx;
           FY := h;
         end;
+        //HACK!
+        if Text.RightAlign and (length(cww) > a) then
+        begin
+          //Text.FX := Text.FX+maxcww;
+          Text.FMaxWidth := maxcww;
+        end;
+      end;
 
-        if Control <> nil then
-          with Control do
-            if Text <> nil then
-            begin
-              FX := cx;
-              FY := h;
-            end
-            else
-            begin
-              FX := tx;
-              FY := h;
-            end;
+      if Control <> nil then
+      begin
+        with Control do
+        begin
+          if Text <> nil then
+          begin
+            FX := cx;
+            FY := h;
+          end
+          else
+          begin
+            FX := tx;
+            FY := h;
+          end;
+        end;
+      end;
 
-        if (ControlType = TGUIListBox) or (ControlType = TGUIFileListBox) then
-          Inc(h, (Control as TGUIListBox).GetHeight+MENU_VSPACE)
-        else if ControlType = TGUIMemo then
-          Inc(h, (Control as TGUIMemo).GetHeight+MENU_VSPACE)
-        else
-          Inc(h, e_CharFont_GetMaxHeight(FFontID)+MENU_VSPACE);
+           if (ControlType = TGUIListBox) or (ControlType = TGUIFileListBox) then Inc(h, (Control as TGUIListBox).GetHeight+MENU_VSPACE)
+      else if ControlType = TGUIMemo then Inc(h, (Control as TGUIMemo).GetHeight+MENU_VSPACE)
+      else Inc(h, e_CharFont_GetMaxHeight(FFontID)+MENU_VSPACE);
     end;
+  end;
 
   // another ugly hack
   if FYesNo and (length(FItems) > 1) then
@@ -1838,7 +1926,8 @@ begin
     Text := TGUILabel.Create(fText, FFontID);
     with Text do
     begin
-      FColor := MENU_ITEMSTEXT_COLOR;
+      FColor := MENU_ITEMSCTRL_COLOR; //MENU_ITEMSTEXT_COLOR;
+      RightAlign := true;
     end;
 
     ControlType := TGUIKeyRead2;
@@ -2029,7 +2118,7 @@ begin
   if a > FMax then FValue := FMax else FValue := a;
 end;
 
-function TGUIScroll.GetWidth: Word;
+function TGUIScroll.GetWidth: Integer;
 begin
   Result := 16+(FMax+1)*8;
 end;
@@ -2044,14 +2133,14 @@ begin
     WM_KEYDOWN:
     begin
       case Msg.wParam of
-        IK_LEFT, IK_KPLEFT:
+        IK_LEFT, IK_KPLEFT, VK_LEFT, JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT:
           if FValue > 0 then
           begin
             Dec(FValue);
             g_Sound_PlayEx(SCROLL_SUBSOUND);
             if @FOnChangeEvent <> nil then FOnChangeEvent(Self);
           end;
-        IK_RIGHT, IK_KPRIGHT:
+        IK_RIGHT, IK_KPRIGHT, VK_RIGHT, JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT:
           if FValue < FMax then
           begin
             Inc(FValue);
@@ -2085,7 +2174,7 @@ begin
 
   FIndex := -1;
 
-  FFont := TFont.Create(FontID, FONT_CHAR);
+  FFont := TFont.Create(FontID, TFontType.Character);
 end;
 
 procedure TGUISwitch.Draw;
@@ -2101,7 +2190,7 @@ begin
   else Result := '';
 end;
 
-function TGUISwitch.GetWidth: Word;
+function TGUISwitch.GetWidth: Integer;
 var
   a: Integer;
   w, h: Word;
@@ -2128,27 +2217,34 @@ begin
   case Msg.Msg of
     WM_KEYDOWN:
       case Msg.wParam of
-        IK_RETURN, IK_RIGHT, IK_KPRETURN, IK_KPRIGHT:
+        IK_RETURN, IK_RIGHT, IK_KPRETURN, IK_KPRIGHT, VK_FIRE, VK_OPEN, VK_RIGHT,
+        JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT,
+        JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK:
         begin
           if FIndex < High(FItems) then
             Inc(FIndex)
           else
             FIndex := 0;
 
+          g_Sound_PlayEx(SCROLL_ADDSOUND);
+
           if @FOnChangeEvent <> nil then
             FOnChangeEvent(Self);
         end;
 
-    IK_LEFT, IK_KPLEFT:
-      begin
-        if FIndex > 0 then
-          Dec(FIndex)
-        else
-          FIndex := High(FItems);
+      IK_LEFT, IK_KPLEFT, VK_LEFT,
+      JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT:
+        begin
+          if FIndex > 0 then
+            Dec(FIndex)
+          else
+            FIndex := High(FItems);
 
-        if @FOnChangeEvent <> nil then
-          FOnChangeEvent(Self);
-      end;
+          g_Sound_PlayEx(SCROLL_SUBSOUND);
+
+          if @FOnChangeEvent <> nil then
+            FOnChangeEvent(Self);
+        end;
     end;
   end;
 end;
@@ -2165,10 +2261,11 @@ constructor TGUIEdit.Create(FontID: DWORD);
 begin
   inherited Create();
 
-  FFont := TFont.Create(FontID, FONT_CHAR);
+  FFont := TFont.Create(FontID, TFontType.Character);
 
   FMaxLength := 0;
   FWidth := 0;
+  FInvalid := false;
 
   g_Texture_Get(EDIT_LEFT, FLeftID);
   g_Texture_Get(EDIT_RIGHT, FRightID);
@@ -2178,6 +2275,7 @@ end;
 procedure TGUIEdit.Draw;
 var
   c, w, h: Word;
+  r, g, b: Byte;
 begin
   inherited;
 
@@ -2187,9 +2285,13 @@ begin
   for c := 0 to FWidth-1 do
     e_Draw(FMiddleID, FX+8+c*16, FY, 0, True, False);
 
-  FFont.Draw(FX+8, FY, FText, FColor.R, FColor.G, FColor.B);
+  r := FColor.R;
+  g := FColor.G;
+  b := FColor.B;
+  if FInvalid and (FWindow.FActiveControl <> self) then begin r := 128; g := 128; b := 128; end;
+  FFont.Draw(FX+8, FY, FText, r, g, b);
 
-  if FWindow.FActiveControl = Self then
+  if (FWindow.FActiveControl = self) then
   begin
     FFont.GetTextSize(Copy(FText, 1, FCaretPos), w, h);
     h := e_CharFont_GetMaxHeight(FFont.ID);
@@ -2198,7 +2300,7 @@ begin
   end;
 end;
 
-function TGUIEdit.GetWidth: Word;
+function TGUIEdit.GetWidth: Integer;
 begin
   Result := 16+FWidth*16;
 end;
@@ -2240,9 +2342,9 @@ begin
           IK_DELETE: Delete(FText, FCaretPos + 1, 1);
           IK_END, IK_KPEND: FCaretPos := Length(FText);
           IK_HOME, IK_KPHOME: FCaretPos := 0;
-          IK_LEFT, IK_KPLEFT: if FCaretPos > 0 then Dec(FCaretPos);
-          IK_RIGHT, IK_KPRIGHT: if FCaretPos < Length(FText) then Inc(FCaretPos);
-          IK_RETURN, IK_KPRETURN:
+          IK_LEFT, IK_KPLEFT, VK_LEFT, JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT: if FCaretPos > 0 then Dec(FCaretPos);
+          IK_RIGHT, IK_KPRIGHT, VK_RIGHT, JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT: if FCaretPos < Length(FText) then Inc(FCaretPos);
+          IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK:
             with FWindow do
             begin
               if FActiveControl <> Self then
@@ -2259,6 +2361,9 @@ begin
             end;
         end;
     end;
+
+  g_GUIGrabInput := (@FOnEnterEvent = nil) and (FWindow.FActiveControl = Self);
+  g_Touch_ShowKeyboard(g_GUIGrabInput)
 end;
 
 procedure TGUIEdit.SetText(Text: string);
@@ -2281,7 +2386,7 @@ begin
   FKey := 0;
   FIsQuery := false;
 
-  FFont := TFont.Create(FontID, FONT_CHAR);
+  FFont := TFont.Create(FontID, TFontType.Character);
 end;
 
 procedure TGUIKeyRead.Draw;
@@ -2292,7 +2397,7 @@ begin
              FColor.R, FColor.G, FColor.B);
 end;
 
-function TGUIKeyRead.GetWidth: Word;
+function TGUIKeyRead.GetWidth: Integer;
 var
   a: Byte;
   w, h: Word;
@@ -2339,12 +2444,12 @@ begin
     case Msg of
       WM_KEYDOWN:
         case wParam of
-          IK_ESCAPE:
+          VK_ESCAPE:
             begin
               if FIsQuery then actDefCtl();
               FIsQuery := False;
             end;
-          IK_RETURN, IK_KPRETURN:
+          IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK:
             begin
               if not FIsQuery then
                 begin
@@ -2354,9 +2459,10 @@ begin
 
                   FIsQuery := True;
                 end
-              else
+              else if (wParam < VK_FIRSTKEY) and (wParam > VK_LASTKEY) then
                 begin
-                  FKey := IK_ENTER; // <Enter>
+                  // FKey := IK_ENTER; // <Enter>
+                  FKey := wParam;
                   FIsQuery := False;
                   actDefCtl();
                 end;
@@ -2378,15 +2484,21 @@ begin
             FKey := 0;
             actDefCtl();
           end
-          else if FIsQuery and (wParam <> IK_ENTER) and (wParam <> IK_KPRETURN) then // Not <Enter
+          else if FIsQuery then
           begin
-            if e_KeyNames[wParam] <> '' then
-              FKey := wParam;
-            FIsQuery := False;
-            actDefCtl();
+            case wParam of
+              IK_ENTER, IK_KPRETURN, VK_FIRSTKEY..VK_LASTKEY (*, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK*): // Not <Enter
+            else
+              if e_KeyNames[wParam] <> '' then
+                FKey := wParam;
+              FIsQuery := False;
+              actDefCtl();
+            end
           end;
         end;
     end;
+
+  g_GUIGrabInput := FIsQuery
 end;
 
 { TGUIKeyRead2 }
@@ -2404,7 +2516,7 @@ begin
   FIsQuery := False;
 
   FFontID := FontID;
-  FFont := TFont.Create(FontID, FONT_CHAR);
+  FFont := TFont.Create(FontID, TFontType.Character);
 
   FMaxKeyNameWdt := 0;
   for a := 0 to 255 do
@@ -2413,6 +2525,8 @@ begin
     FMaxKeyNameWdt := Max(FMaxKeyNameWdt, w);
   end;
 
+  FMaxKeyNameWdt := FMaxKeyNameWdt-(FMaxKeyNameWdt div 3);
+
   FFont.GetTextSize(KEYREAD_QUERY, w, h);
   if w > FMaxKeyNameWdt then FMaxKeyNameWdt := w;
 
@@ -2433,7 +2547,7 @@ procedure TGUIKeyRead2.Draw;
     r := 255;
     g := 0;
     b := 0;
-    if FKeyIdx = idx then g := 127;
+    if FKeyIdx = idx then begin r := 255; g := 255; b := 255; end;
     if FIsQuery and (FKeyIdx = idx) then
       FFont.Draw(x, y, KEYREAD_QUERY, r, g, b)
     else
@@ -2449,18 +2563,21 @@ begin
   drawText(1);
 end;
 
-function TGUIKeyRead2.GetWidth: Word;
+function TGUIKeyRead2.GetWidth: Integer;
 begin
   Result := FMaxKeyNameWdt*2+8+8+16;
 end;
 
 function TGUIKeyRead2.WantActivationKey (key: LongInt): Boolean;
 begin
-  result :=
-    (key = IK_BACKSPACE) or
-    (key = IK_LEFT) or (key = IK_RIGHT) or
-    (key = IK_KPLEFT) or (key = IK_KPRIGHT) or
-    false; // oops
+  case key of
+    IK_BACKSPACE, IK_LEFT, IK_RIGHT, IK_KPLEFT, IK_KPRIGHT, VK_LEFT, VK_RIGHT,
+    JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT,
+    JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT:
+      result := True
+  else
+      result := False
+  end
 end;
 
 procedure TGUIKeyRead2.OnMessage(var Msg: TMessage);
@@ -2483,12 +2600,12 @@ begin
     case Msg of
       WM_KEYDOWN:
         case wParam of
-          IK_ESCAPE:
+          VK_ESCAPE:
             begin
               if FIsQuery then actDefCtl();
               FIsQuery := False;
             end;
-          IK_RETURN, IK_KPRETURN:
+          IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK:
             begin
               if not FIsQuery then
                 begin
@@ -2498,9 +2615,10 @@ begin
 
                   FIsQuery := True;
                 end
-              else
+              else if (wParam < VK_FIRSTKEY) and (wParam > VK_LASTKEY) then
                 begin
-                  if (FKeyIdx = 0) then FKey0 := IK_ENTER else FKey1 := IK_ENTER; // <Enter>
+                  // if (FKeyIdx = 0) then FKey0 := IK_ENTER else FKey1 := IK_ENTER; // <Enter>
+                  if (FKeyIdx = 0) then FKey0 := wParam else FKey1 := wParam;
                   FIsQuery := False;
                   actDefCtl();
                 end;
@@ -2513,13 +2631,13 @@ begin
                 actDefCtl();
               end;
             end;
-          IK_LEFT, IK_KPLEFT:
+          IK_LEFT, IK_KPLEFT, VK_LEFT, JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT:
             if not FIsQuery then
             begin
               FKeyIdx := 0;
               actDefCtl();
             end;
-          IK_RIGHT, IK_KPRIGHT:
+          IK_RIGHT, IK_KPRIGHT, VK_RIGHT, JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT:
             if not FIsQuery then
             begin
               FKeyIdx := 1;
@@ -2534,17 +2652,23 @@ begin
             if (FKeyIdx = 0) then FKey0 := 0 else FKey1 := 0;
             actDefCtl();
           end
-          else if FIsQuery and (wParam <> IK_ENTER) and (wParam <> IK_KPRETURN) then // Not <Enter
+          else if FIsQuery then
           begin
-            if e_KeyNames[wParam] <> '' then
-            begin
-              if (FKeyIdx = 0) then FKey0 := wParam else FKey1 := wParam;
-            end;
-            FIsQuery := False;
-            actDefCtl();
+            case wParam of
+              IK_ENTER, IK_KPRETURN, VK_FIRSTKEY..VK_LASTKEY (*, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK*): // Not <Enter
+            else
+              if e_KeyNames[wParam] <> '' then
+              begin
+                if (FKeyIdx = 0) then FKey0 := wParam else FKey1 := wParam;
+              end;
+              FIsQuery := False;
+              actDefCtl()
+            end
           end;
         end;
     end;
+
+  g_GUIGrabInput := FIsQuery
 end;
 
 
@@ -2589,7 +2713,7 @@ begin
   if FModel = nil then
     Exit;
 
-  if FModel.Weapon < WEAPON_SUPERPULEMET then
+  if FModel.Weapon < WP_LAST then
     FModel.SetWeapon(FModel.Weapon+1)
   else
     FModel.SetWeapon(WEAPON_KASTET);
@@ -2724,15 +2848,20 @@ end;
 procedure TGUIMapPreview.SetMap(Res: string);
 var
   WAD: TWADFile;
-  MapReader: TMapReader_1;
-  panels: TPanelsRec1Array;
-  header: TMapHeaderRec_1;
-  a: Integer;
+  panlist: TDynField;
+  pan: TDynRecord;
+  //header: TMapHeaderRec_1;
   FileName: string;
   Data: Pointer;
   Len: Integer;
   rX, rY: Single;
+  map: TDynRecord = nil;
 begin
+  FMapSize.X := 0;
+  FMapSize.Y := 0;
+  FScale := 0.0;
+  FMapData := nil;
+
   FileName := g_ExtractWadName(Res);
 
   WAD := TWADFile.Create();
@@ -2751,68 +2880,71 @@ begin
 
   WAD.Free();
 
-  MapReader := TMapReader_1.Create();
-
-  if not MapReader.LoadMap(Data) then
-  begin
+  try
+    map := g_Map_ParseMap(Data, Len);
+  except
     FreeMem(Data);
-    MapReader.Free();
-    FMapSize.X := 0;
-    FMapSize.Y := 0;
-    FScale := 0.0;
-    FMapData := nil;
-    Exit;
+    map.Free();
+    //raise;
+    exit;
   end;
 
   FreeMem(Data);
 
-  panels := MapReader.GetPanels();
-  header := MapReader.GetMapHeader();
+  if (map = nil) then exit;
 
-  FMapSize.X := header.Width div 16;
-  FMapSize.Y := header.Height div 16;
+  try
+    panlist := map.field['panel'];
+    //header := GetMapHeader(map);
 
-  rX := Ceil(header.Width / (MAPPREVIEW_WIDTH*256.0));
-  rY := Ceil(header.Height / (MAPPREVIEW_HEIGHT*256.0));
-  FScale := max(rX, rY);
+    FMapSize.X := map.Width div 16;
+    FMapSize.Y := map.Height div 16;
 
-  FMapData := nil;
+    rX := Ceil(map.Width / (MAPPREVIEW_WIDTH*256.0));
+    rY := Ceil(map.Height / (MAPPREVIEW_HEIGHT*256.0));
+    FScale := max(rX, rY);
+
+    FMapData := nil;
 
-  if panels <> nil then
-    for a := 0 to High(panels) do
-      if WordBool(panels[a].PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or
-                                           PANEL_STEP or PANEL_WATER or
-                                           PANEL_ACID1 or PANEL_ACID2)) then
+    if (panlist <> nil) then
+    begin
+      for pan in panlist do
       begin
-        SetLength(FMapData, Length(FMapData)+1);
-        with FMapData[High(FMapData)] do
+        if (pan.PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or
+                                             PANEL_STEP or PANEL_WATER or
+                                             PANEL_ACID1 or PANEL_ACID2)) <> 0 then
         begin
-          X1 := panels[a].X div 16;
-          Y1 := panels[a].Y div 16;
+          SetLength(FMapData, Length(FMapData)+1);
+          with FMapData[High(FMapData)] do
+          begin
+            X1 := pan.X div 16;
+            Y1 := pan.Y div 16;
 
-          X2 := (panels[a].X + panels[a].Width) div 16;
-          Y2 := (panels[a].Y + panels[a].Height) div 16;
+            X2 := (pan.X + pan.Width) div 16;
+            Y2 := (pan.Y + pan.Height) div 16;
 
-          X1 := Trunc(X1/FScale + 0.5);
-          Y1 := Trunc(Y1/FScale + 0.5);
-          X2 := Trunc(X2/FScale + 0.5);
-          Y2 := Trunc(Y2/FScale + 0.5);
+            X1 := Trunc(X1/FScale + 0.5);
+            Y1 := Trunc(Y1/FScale + 0.5);
+            X2 := Trunc(X2/FScale + 0.5);
+            Y2 := Trunc(Y2/FScale + 0.5);
 
-          if (X1 <> X2) or (Y1 <> Y2) then
-          begin
-            if X1 = X2 then
-              X2 := X2 + 1;
-            if Y1 = Y2 then
-              Y2 := Y2 + 1;
-          end;
+            if (X1 <> X2) or (Y1 <> Y2) then
+            begin
+              if X1 = X2 then
+                X2 := X2 + 1;
+              if Y1 = Y2 then
+                Y2 := Y2 + 1;
+            end;
 
-          PanelType := panels[a].PanelType;
+            PanelType := pan.PanelType;
+          end;
+        end;
       end;
-   end;
-
-  panels := nil;
-
-  MapReader.Free();
+    end;
+  finally
+    //writeln('freeing map');
+    map.Free();
+  end;
 end;
 
 procedure TGUIMapPreview.ClearMap();
@@ -2855,7 +2987,15 @@ begin
   if FSort then g_Basic.Sort(FItems);
 end;
 
-procedure TGUIListBox.Clear();
+function TGUIListBox.ItemExists (item: String): Boolean;
+  var i: Integer;
+begin
+  i := 0;
+  while (i <= High(FItems)) and (FItems[i] <> item) do Inc(i);
+  result := i <= High(FItems)
+end;
+
+procedure TGUIListBox.Clear;
 begin
   FItems := nil;
 
@@ -2867,7 +3007,7 @@ constructor TGUIListBox.Create(FontID: DWORD; Width, Height: Word);
 begin
   inherited Create();
 
-  FFont := TFont.Create(FontID, FONT_CHAR);
+  FFont := TFont.Create(FontID, TFontType.Character);
 
   FWidth := Width;
   FHeight := Height;
@@ -2909,12 +3049,12 @@ begin
     end;
 end;
 
-function TGUIListBox.GetHeight: Word;
+function TGUIListBox.GetHeight: Integer;
 begin
   Result := 8+FHeight*16;
 end;
 
-function TGUIListBox.GetWidth: Word;
+function TGUIListBox.GetWidth: Integer;
 begin
   Result := 8+(FWidth+1)*16;
 end;
@@ -2943,21 +3083,21 @@ begin
             FIndex := High(FItems);
             FStartLine := Max(High(FItems)-FHeight+1, 0);
           end;
-          IK_UP, IK_LEFT, IK_KPUP, IK_KPLEFT:
+          IK_UP, IK_LEFT, IK_KPUP, IK_KPLEFT, VK_LEFT, JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT:
             if FIndex > 0 then
             begin
               Dec(FIndex);
               if FIndex < FStartLine then Dec(FStartLine);
               if @FOnChangeEvent <> nil then FOnChangeEvent(Self);
             end;
-          IK_DOWN, IK_RIGHT, IK_KPDOWN, IK_KPRIGHT:
+          IK_DOWN, IK_RIGHT, IK_KPDOWN, IK_KPRIGHT, VK_RIGHT, JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT:
             if FIndex < High(FItems) then
             begin
               Inc(FIndex);
               if FIndex > FStartLine+FHeight-1 then Inc(FStartLine);
               if @FOnChangeEvent <> nil then FOnChangeEvent(Self);
             end;
-          IK_RETURN, IK_KPRETURN:
+          IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK:
             with FWindow do
             begin
               if FActiveControl <> Self then SetActive(Self)
@@ -2989,7 +3129,7 @@ begin
   Result := FItems[FIndex];
 end;
 
-procedure TGUIListBox.FSetItems(Items: SArray);
+procedure TGUIListBox.FSetItems(Items: SSArray);
 begin
   if FItems <> nil then
     FItems := nil;
@@ -3045,7 +3185,7 @@ end;
 
 procedure TGUIFileListBox.OnMessage(var Msg: TMessage);
 var
-  a: Integer;
+  a, b: Integer; s: AnsiString;
 begin
   if not FEnabled then
     Exit;
@@ -3099,7 +3239,7 @@ begin
                 FStartLine := High(FItems)-FHeight+1;
             end;
 
-          IK_UP, IK_LEFT, IK_KPUP, IK_KPLEFT:
+          IK_UP, IK_LEFT, IK_KPUP, IK_KPLEFT, VK_UP, VK_LEFT, JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT:
             if FIndex > 0 then
             begin
               Dec(FIndex);
@@ -3109,7 +3249,7 @@ begin
                 FOnChangeEvent(Self);
             end;
 
-          IK_DOWN, IK_RIGHT, IK_KPDOWN, IK_KPRIGHT:
+          IK_DOWN, IK_RIGHT, IK_KPDOWN, IK_KPRIGHT, VK_DOWN, VK_RIGHT, JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT:
             if FIndex < High(FItems) then
             begin
               Inc(FIndex);
@@ -3119,7 +3259,7 @@ begin
                 FOnChangeEvent(Self);
             end;
 
-          IK_RETURN, IK_KPRETURN:
+          IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK:
             with FWindow do
             begin
               if FActiveControl <> Self then
@@ -3128,7 +3268,18 @@ begin
                 begin
                   if FItems[FIndex][1] = #29 then // Ïàïêà
                   begin
-                    OpenDir(FPath+Copy(FItems[FIndex], 2, 255));
+                    if FItems[FIndex] = #29 + '..' then
+                    begin
+                      e_LogWritefln('TGUIFileListBox: Upper dir "%s" -> "%s"', [FSubPath, e_UpperDir(FSubPath)]);
+                      FSubPath := e_UpperDir(FSubPath)
+                    end
+                    else
+                    begin
+                      s := Copy(AnsiString(FItems[FIndex]), 2);
+                      e_LogWritefln('TGUIFileListBox: Enter dir "%s" -> "%s"', [FSubPath, e_CatPath(FSubPath, s)]);
+                      FSubPath := e_CatPath(FSubPath, s);
+                    end;
+                    ScanDirs;
                     FIndex := 0;
                     Exit;
                   end;
@@ -3142,7 +3293,9 @@ begin
         end;
 
       WM_CHAR:
-        for a := 0 to High(FItems) do
+        for b := FIndex + 1 to High(FItems) + FIndex do
+        begin
+          a := b mod Length(FItems);
           if ( (Length(FItems[a]) > 0) and
                (LowerCase(FItems[a][1]) = LowerCase(Chr(wParam))) ) or
              ( (Length(FItems[a]) > 1) and
@@ -3155,73 +3308,82 @@ begin
               FOnChangeEvent(Self);
             Break;
           end;
+        end;
     end;
 end;
 
-procedure TGUIFileListBox.OpenDir(path: String);
-var
-  SR: TSearchRec;
-  i: Integer;
-  sm, sc: string;
+procedure TGUIFileListBox.ScanDirs;
+  var i, j: Integer; path: AnsiString; SR: TSearchRec; sm, sc: String;
 begin
-  Clear();
+  Clear;
 
-  path := IncludeTrailingPathDelimiter(path);
-  path := ExpandFileName(path);
-
-  // Êàòàëîãè:
-  if FDirs then
+  i := High(FBaseList);
+  while i >= 0 do
   begin
-    if FindFirst(path+'*', faDirectory, SR) = 0 then
-    repeat
-      if not LongBool(SR.Attr and faDirectory) then
-        Continue;
-      if (SR.Name = '.') or
-         ((SR.Name = '..') and (path = ExpandFileName(FBasePath))) then
-        Continue;
-
-      AddItem(#1 + SR.Name);
-    until FindNext(SR) <> 0;
-
-    FindClose(SR);
+    path := e_CatPath(FBaseList[i], FSubPath);
+    if FDirs then
+    begin
+      if FindFirst(path + '/' + '*', faDirectory, SR) = 0 then
+      begin
+        repeat
+          if LongBool(SR.Attr and faDirectory) then
+            if (SR.Name <> '.') and ((FSubPath <> '') or (SR.Name <> '..')) then
+              if Self.ItemExists(#1 + SR.Name) = false then
+                Self.AddItem(#1 + SR.Name)
+        until FindNext(SR) <> 0
+      end;
+      FindClose(SR)
+    end;
+    Dec(i)
   end;
 
-  // Ôàéëû:
-  sm := FFileMask;
-  while sm <> '' do
+  i := High(FBaseList);
+  while i >= 0 do
   begin
-    i := Pos('|', sm);
-    if i = 0 then i := length(sm)+1;
-    sc := Copy(sm, 1, i-1);
-    Delete(sm, 1, i);
-    if FindFirst(path+sc, faAnyFile, SR) = 0 then repeat AddItem(SR.Name); until FindNext(SR) <> 0;
-    FindClose(SR);
+    path := e_CatPath(FBaseList[i], FSubPath);
+    sm := FFileMask;
+    while sm <> '' do
+    begin
+      j := Pos('|', sm);
+      if j = 0 then
+        j := length(sm) + 1;
+      sc := Copy(sm, 1, j - 1);
+      Delete(sm, 1, j);
+      if FindFirst(path + '/' + sc, faAnyFile, SR) = 0 then
+      begin
+        repeat
+          if Self.ItemExists(SR.Name) = false then
+            AddItem(SR.Name)
+        until FindNext(SR) <> 0
+      end;
+      FindClose(SR)
+    end;
+    Dec(i)
   end;
 
   for i := 0 to High(FItems) do
     if FItems[i][1] = #1 then
       FItems[i][1] := #29;
-
-  FPath := path;
 end;
 
-procedure TGUIFileListBox.SetBase(path: String);
+procedure TGUIFileListBox.SetBase (dirs: SSArray; path: String = '');
 begin
-  FBasePath := path;
-  OpenDir(FBasePath);
+  FBaseList := dirs;
+  FSubPath := path;
+  ScanDirs
 end;
 
-function TGUIFileListBox.SelectedItem(): String;
+function TGUIFileListBox.SelectedItem (): String;
+  var s: AnsiString;
 begin
-  Result := '';
-
-  if (FIndex = -1) or (FItems = nil) or
-     (FIndex > High(FItems)) or
-     (FItems[FIndex][1] = '/') or
-     (FItems[FIndex][1] = '\') then
-    Exit;
-
-  Result := FPath + FItems[FIndex];
+  result := '';
+  if (FIndex >= 0) and (FIndex <= High(FItems)) and (FItems[FIndex][1] <> '/') and (FItems[FIndex][1] <> '\') then
+  begin
+    s := e_CatPath(FSubPath, FItems[FIndex]);
+    if e_FindResource(FBaseList, s) = true then
+      result := ExpandFileName(s)
+  end;
+  e_LogWritefln('TGUIFileListBox.SelectedItem -> "%s"', [result]);
 end;
 
 procedure TGUIFileListBox.UpdateFileList();
@@ -3236,7 +3398,8 @@ begin
   else
     fn := FItems[FIndex];
 
-  OpenDir(FPath);
+//  OpenDir(FPath);
+  ScanDirs;
 
   if fn <> '' then
     SelectItem(fn);
@@ -3254,7 +3417,7 @@ constructor TGUIMemo.Create(FontID: DWORD; Width, Height: Word);
 begin
   inherited Create();
 
-  FFont := TFont.Create(FontID, FONT_CHAR);
+  FFont := TFont.Create(FontID, TFontType.Character);
 
   FWidth := Width;
   FHeight := Height;
@@ -3278,12 +3441,12 @@ begin
       FFont.Draw(FX+4, FY+4+(a-FStartLine)*16, FLines[a], FColor.R, FColor.G, FColor.B);
 end;
 
-function TGUIMemo.GetHeight: Word;
+function TGUIMemo.GetHeight: Integer;
 begin
   Result := 8+FHeight*16;
 end;
 
-function TGUIMemo.GetWidth: Word;
+function TGUIMemo.GetWidth: Integer;
 begin
   Result := 8+(FWidth+1)*16;
 end;
@@ -3300,13 +3463,13 @@ begin
     case Msg of
       WM_KEYDOWN:
         case wParam of
-          IK_UP, IK_LEFT, IK_KPUP, IK_KPLEFT:
+          IK_UP, IK_LEFT, IK_KPUP, IK_KPLEFT, VK_UP, VK_LEFT, JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT:
             if FStartLine > 0 then
               Dec(FStartLine);
-          IK_DOWN, IK_RIGHT, IK_KPDOWN, IK_KPRIGHT:
+          IK_DOWN, IK_RIGHT, IK_KPDOWN, IK_KPRIGHT, VK_DOWN, VK_RIGHT, JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT:
             if FStartLine < Length(FLines)-FHeight then
               Inc(FStartLine);
-          IK_RETURN, IK_KPRETURN:
+          IK_RETURN, IK_KPRETURN, VK_FIRE, VK_OPEN, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK:
             with FWindow do
             begin
               if FActiveControl <> Self then