DEADSOFTWARE

network: code uglification; fixed bug with weapon switching (i hope)
[d2df-sdl.git] / src / game / g_console.pas
index e0f401deeade15964e5c93c103ecebfa6065088c..f91bc624495dbbf29073904eb7ede60a3be7d8b7 100644 (file)
@@ -1,4 +1,4 @@
-(* 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
@@ -19,7 +19,7 @@ unit g_console;
 interface
 
 uses
-  wadreader; // for SArray
+  utils; // for SSArray
 
 procedure g_Console_Init ();
 procedure g_Console_Update ();
@@ -36,12 +36,16 @@ procedure conwriteln (const s: AnsiString; show: Boolean=false);
 procedure conwritefln (const s: AnsiString; args: array of const; show: Boolean=false);
 
 // <0: no arg; 0/1: true/false
-function conGetBoolArg (p: SArray; idx: Integer): Integer;
+function conGetBoolArg (p: SSArray; idx: Integer): Integer;
 
 procedure g_Console_Chat_Switch (team: Boolean=false);
 
-procedure conRegVar (const conname: AnsiString; pvar: PBoolean; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false); overload;
-procedure conRegVar (const conname: AnsiString; pvar: PSingle; amin, amax: Single; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false); overload;
+procedure conRegVar (const conname: AnsiString; pvar: PBoolean; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
+procedure conRegVar (const conname: AnsiString; pvar: PSingle; amin, amax: Single; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
+procedure conRegVar (const conname: AnsiString; pvar: PInteger; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
+
+// poor man's floating literal parser; i'm sorry, but `StrToFloat()` sux cocks
+function conParseFloat (var res: Single; const s: AnsiString): Boolean;
 
 
 var
@@ -57,15 +61,15 @@ implementation
 
 uses
   g_textures, g_main, e_graphics, e_input, g_game,
-  SysUtils, g_basic, g_options, Math,
-  g_menu, g_language, g_net, g_netmsg, e_log, conbuf, utils;
+  SysUtils, g_basic, g_options, Math, g_touch,
+  g_menu, g_language, g_net, g_netmsg, e_log, conbuf;
 
 
 type
   PCommand = ^TCommand;
 
-  TCmdProc = procedure (p: SArray);
-  TCmdProcEx = procedure (me: PCommand; p: SArray);
+  TCmdProc = procedure (p: SSArray);
+  TCmdProcEx = procedure (me: PCommand; p: SSArray);
 
   TCommand = record
     cmd: AnsiString;
@@ -80,7 +84,7 @@ type
 
   TAlias = record
     name: AnsiString;
-    commands: SArray;
+    commands: SSArray;
   end;
 
 
@@ -100,9 +104,9 @@ var
   Cons_Shown: Boolean; // Ðèñîâàòü ëè êîíñîëü?
   Line: AnsiString;
   CPos: Word;
-  //ConsoleHistory: SArray;
-  CommandHistory: SArray;
-  Whitelist: SArray;
+  //ConsoleHistory: SSArray;
+  CommandHistory: SSArray;
+  Whitelist: SSArray;
   commands: Array of TCommand = nil;
   Aliases: Array of TAlias = nil;
   CmdIndex: Word;
@@ -113,9 +117,48 @@ var
                             end;
 
 
+// poor man's floating literal parser; i'm sorry, but `StrToFloat()` sux cocks
+function conParseFloat (var res: Single; const s: AnsiString): Boolean;
+var
+  pos: Integer = 1;
+  frac: Single = 1;
+  slen: Integer;
+begin
+  result := false;
+  res := 0;
+  slen := Length(s);
+  while (slen > 0) and (s[slen] <= ' ') do Dec(slen);
+  while (pos <= slen) and (s[pos] <= ' ') do Inc(pos);
+  if (pos > slen) then exit;
+  if (slen-pos = 1) and (s[pos] = '.') then exit; // single dot
+  // integral part
+  while (pos <= slen) do
+  begin
+    if (s[pos] < '0') or (s[pos] > '9') then break;
+    res := res*10+Byte(s[pos])-48;
+    Inc(pos);
+  end;
+  if (pos <= slen) then
+  begin
+    // must be a dot
+    if (s[pos] <> '.') then exit;
+    Inc(pos);
+    while (pos <= slen) do
+    begin
+      if (s[pos] < '0') or (s[pos] > '9') then break;
+      frac := frac/10;
+      res += frac*(Byte(s[pos])-48);
+      Inc(pos);
+    end;
+  end;
+  if (pos <= slen) then exit; // oops
+  result := true;
+end;
+
+
 // ////////////////////////////////////////////////////////////////////////// //
 // <0: no arg; 0/1: true/false; 666: toggle
-function conGetBoolArg (p: SArray; idx: Integer): Integer;
+function conGetBoolArg (p: SSArray; idx: Integer): Integer;
 begin
   if (idx < 0) or (idx > High(p)) then begin result := -1; exit; end;
   result := 0;
@@ -126,7 +169,7 @@ begin
 end;
 
 
-procedure boolVarHandler (me: PCommand; p: SArray);
+procedure boolVarHandler (me: PCommand; p: SSArray);
   procedure binaryFlag (var flag: Boolean; msg: AnsiString);
   begin
     if (Length(p) > 2) then
@@ -137,11 +180,12 @@ procedure boolVarHandler (me: PCommand; p: SArray);
     begin
       case conGetBoolArg(p, 1) of
         -1: begin end;
-         0: if conIsCheatsEnabled then flag := false else begin conwriteln('not available'); exit; end;
-         1: if conIsCheatsEnabled then flag := true else begin conwriteln('not available'); exit; end;
-         666: if conIsCheatsEnabled then flag := not flag else begin conwriteln('not available'); exit; end;
+         0: if not me.cheat or conIsCheatsEnabled then flag := false else begin conwriteln('not available'); exit; end;
+         1: if not me.cheat or conIsCheatsEnabled then flag := true else begin conwriteln('not available'); exit; end;
+         666: if not me.cheat or conIsCheatsEnabled then flag := not flag else begin conwriteln('not available'); exit; end;
       end;
-      if flag then conwritefln('%s: tan', [msg]) else conwritefln('%s: ona', [msg]);
+      if (Length(msg) = 0) then msg := p[0] else msg += ':';
+      if flag then conwritefln('%s tan', [msg]) else conwritefln('%s ona', [msg]);
     end;
   end;
 begin
@@ -149,7 +193,42 @@ begin
 end;
 
 
-procedure conRegVar (const conname: AnsiString; pvar: PBoolean; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false); overload;
+procedure intVarHandler (me: PCommand; p: SSArray);
+  procedure binaryFlag (var flag: Boolean; msg: AnsiString);
+  begin
+    if (Length(p) > 2) then
+    begin
+      conwritefln('too many arguments to ''%s''', [p[0]]);
+    end
+    else
+    begin
+      case conGetBoolArg(p, 1) of
+        -1: begin end;
+         0: if not me.cheat or conIsCheatsEnabled then flag := false else begin conwriteln('not available'); exit; end;
+         1: if not me.cheat or conIsCheatsEnabled then flag := true else begin conwriteln('not available'); exit; end;
+         666: if not me.cheat or conIsCheatsEnabled then flag := not flag else begin conwriteln('not available'); exit; end;
+      end;
+      if (Length(msg) = 0) then msg := p[0] else msg += ':';
+      if flag then conwritefln('%s tan', [msg]) else conwritefln('%s ona', [msg]);
+    end;
+  end;
+begin
+  if (Length(p) <> 2) then
+  begin
+    conwritefln('%s %d', [me.cmd, PInteger(me.ptr)^]);
+  end
+  else
+  begin
+    try
+      PInteger(me.ptr)^ := StrToInt(p[1]);
+    except
+      conwritefln('invalid integer value: "%s"', [p[1]]);
+    end;
+  end;
+end;
+
+
+procedure conRegVar (const conname: AnsiString; pvar: PBoolean; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
 var
   f: Integer;
   cp: PCommand;
@@ -161,7 +240,26 @@ begin
   cp.proc := nil;
   cp.procEx := boolVarHandler;
   cp.help := ahelp;
-  cp.hidden := false;
+  cp.hidden := ahidden;
+  cp.ptr := pvar;
+  cp.msg := amsg;
+  cp.cheat := acheat;
+end;
+
+
+procedure conRegVar (const conname: AnsiString; pvar: PInteger; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
+var
+  f: Integer;
+  cp: PCommand;
+begin
+  f := Length(commands);
+  SetLength(commands, f+1);
+  cp := @commands[f];
+  cp.cmd := LowerCase(conname);
+  cp.proc := nil;
+  cp.procEx := intVarHandler;
+  cp.help := ahelp;
+  cp.hidden := ahidden;
   cp.ptr := pvar;
   cp.msg := amsg;
   cp.cheat := acheat;
@@ -177,44 +275,7 @@ type
   end;
 
 
-procedure singleVarHandler (me: PCommand; p: SArray);
-  // poor man's floating literal parser; i'm sorry, but `StrToFloat()` sux cocks
-  function parseFloat (var res: Single; const s: AnsiString): Boolean;
-  var
-    pos: Integer = 1;
-    frac: Single = 1;
-    slen: Integer;
-  begin
-    result := false;
-    res := 0;
-    slen := Length(s);
-    while (slen > 0) and (s[slen] <= ' ') do Dec(slen);
-    while (pos <= slen) and (s[pos] <= ' ') do Inc(pos);
-    if (pos > slen) then exit;
-    if (slen-pos = 1) and (s[pos] = '.') then exit; // single dot
-    // integral part
-    while (pos <= slen) do
-    begin
-      if (s[pos] < '0') or (s[pos] > '9') then break;
-      res := res*10+Byte(s[pos])-48;
-      Inc(pos);
-    end;
-    if (pos <= slen) then
-    begin
-      // must be a dot
-      if (s[pos] <> '.') then exit;
-      Inc(pos);
-      while (pos <= slen) do
-      begin
-        if (s[pos] < '0') or (s[pos] > '9') then break;
-        frac := frac/10;
-        res += frac*(Byte(s[pos])-48);
-        Inc(pos);
-      end;
-    end;
-    if (pos <= slen) then exit; // oops
-    result := true;
-  end;
+procedure singleVarHandler (me: PCommand; p: SSArray);
 var
   pv: PVarSingle;
   nv: Single;
@@ -222,13 +283,13 @@ var
 begin
   if (Length(p) > 2) then
   begin
-    conwritefln('too many arguments to ''%s''', [p[0]]);
+    conwritefln('too many arguments to ''%s''', [me.cmd]);
     exit;
   end;
   pv := PVarSingle(me.ptr);
   if (Length(p) = 2) then
   begin
-    if not conIsCheatsEnabled then begin conwriteln('not available'); exit; end;
+    if me.cheat and (not conIsCheatsEnabled) then begin conwriteln('not available'); exit; end;
     if (CompareText(p[1], 'default') = 0) or (CompareText(p[1], 'def') = 0) or
        (CompareText(p[1], 'd') = 0) or (CompareText(p[1], 'off') = 0) or
        (CompareText(p[1], 'ona') = 0) then
@@ -237,9 +298,9 @@ begin
     end
     else
     begin
-      if not parseFloat(nv, p[1]) then
+      if not conParseFloat(nv, p[1]) then
       begin
-        conwritefln('%s: ''%s'' doesn''t look like a floating number', [p[0], p[1]]);
+        conwritefln('%s: ''%s'' doesn''t look like a floating number', [me.cmd, p[1]]);
         exit;
       end;
       if (nv < pv.min) then nv := pv.min;
@@ -248,18 +309,18 @@ begin
     end;
   end;
   msg := me.msg;
-  if (Length(msg) = 0) then msg := p[0] else msg += ':';
+  if (Length(msg) = 0) then msg := me.cmd else msg += ':';
   conwritefln('%s %s', [msg, pv.val^]);
 end;
 
 
-procedure conRegVar (const conname: AnsiString; pvar: PSingle; amin, amax: Single; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false); overload;
+procedure conRegVar (const conname: AnsiString; pvar: PSingle; amin, amax: Single; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
 var
   f: Integer;
   cp: PCommand;
   pv: PVarSingle;
 begin
-  GetMem(pv, sizeof(pv^));
+  GetMem(pv, sizeof(TVarSingle));
   pv.val := pvar;
   pv.min := amin;
   pv.max := amax;
@@ -271,7 +332,7 @@ begin
   cp.proc := nil;
   cp.procEx := singleVarHandler;
   cp.help := ahelp;
-  cp.hidden := false;
+  cp.hidden := ahidden;
   cp.ptr := pv;
   cp.msg := amsg;
   cp.cheat := acheat;
@@ -294,7 +355,7 @@ begin
     end;
 end;
 
-function ParseAlias(Str: AnsiString): SArray;
+function ParseAlias(Str: AnsiString): SSArray;
 begin
   Result := nil;
 
@@ -310,7 +371,7 @@ begin
   end;
 end;
 
-procedure ConsoleCommands(p: SArray);
+procedure ConsoleCommands(p: SSArray);
 var
   cmd, s: AnsiString;
   a, b: Integer;
@@ -463,6 +524,13 @@ begin
       g_Console_Add('exec <script file>');
   end;
 
+  if (cmd = 'ver') or (cmd = 'version') then
+  begin
+    conwriteln('Doom 2D: Forever v. ' + GAME_VERSION);
+    conwritefln('Net protocol v. %d', [NET_PROTOCOL_VER]);
+    conwritefln('Build date: %s at %s', [GAME_BUILDDATE, GAME_BUILDTIME]);
+  end;
+
   if cmd = 'alias' then
   begin
     // alias [alias_name] [commands]
@@ -556,6 +624,15 @@ begin
   cp.cheat := acheat;
 end;
 
+
+procedure segfault (p: SSArray);
+var
+  pp: PByte = nil;
+begin
+  pp^ := 0;
+end;
+
+
 procedure g_Console_Init();
 var
   a: Integer;
@@ -574,6 +651,8 @@ begin
       Time := 0;
     end;
 
+  AddCommand('segfault', segfault, 'make segfault');
+
   AddCommand('clear', ConsoleCommands, 'clear console');
   AddCommand('clearhistory', ConsoleCommands);
   AddCommand('showhistory', ConsoleCommands);
@@ -585,6 +664,8 @@ begin
   AddCommand('exec', ConsoleCommands);
   AddCommand('alias', ConsoleCommands);
   AddCommand('call', ConsoleCommands);
+  AddCommand('ver', ConsoleCommands);
+  AddCommand('version', ConsoleCommands);
 
   AddCommand('d_window', DebugCommands);
   AddCommand('d_sounds', DebugCommands);
@@ -596,6 +677,7 @@ begin
   AddCommand('d_health', DebugCommands);
   AddCommand('d_player', DebugCommands);
   AddCommand('d_joy', DebugCommands);
+  AddCommand('d_mem', DebugCommands);
 
   AddCommand('p1_name', GameCVars);
   AddCommand('p2_name', GameCVars);
@@ -899,6 +981,7 @@ begin
   if gChatShow then Exit;
   gConsoleShow := not gConsoleShow;
   Cons_Shown := True;
+  g_Touch_ShowKeyboard(gConsoleShow or gChatShow);
 end;
 
 procedure g_Console_Chat_Switch(Team: Boolean = False);
@@ -911,6 +994,7 @@ begin
     gChatEnter := False;
   Line := '';
   CPos := 1;
+  g_Touch_ShowKeyboard(gConsoleShow or gChatShow);
 end;
 
 procedure g_Console_Char(C: AnsiChar);
@@ -934,19 +1018,19 @@ var
 begin
   if (Length(Line) = 0) then
   begin
+    if (g_Game_IsNet and g_Game_IsServer) or (g_Game_IsClient) then exit;
     g_Console_Add('');
     for i := 0 to High(commands) do
     begin
-      if not commands[i].hidden then
+      // hidden commands are hidden when cheats aren't enabled
+      if commands[i].hidden and not conIsCheatsEnabled then continue;
+      if (Length(commands[i].help) > 0) then
       begin
-        if (Length(commands[i].help) > 0) then
-        begin
-          g_Console_Add('  '+commands[i].cmd+' -- '+commands[i].help);
-        end
-        else
-        begin
-          g_Console_Add('  '+commands[i].cmd);
-        end;
+        g_Console_Add('  '+commands[i].cmd+' -- '+commands[i].help);
+      end
+      else
+      begin
+        g_Console_Add('  '+commands[i].cmd);
       end;
     end;
     exit;
@@ -960,7 +1044,8 @@ begin
     ll := Copy(ll, 0, Length(ll)-1);
     for i := 0 to High(commands) do
     begin
-      if commands[i].hidden then continue;
+      // hidden commands are hidden when cheats aren't enabled
+      if commands[i].hidden and not conIsCheatsEnabled then continue;
       if (commands[i].cmd = ll) then
       begin
         if (Length(commands[i].help) > 0) then
@@ -976,7 +1061,8 @@ begin
   tused := 0;
   for i := 0 to High(commands) do
   begin
-    if commands[i].hidden then continue;
+    // hidden commands are hidden when cheats aren't enabled
+    if commands[i].hidden and not conIsCheatsEnabled then continue;
     cmd := commands[i].cmd;
     if (Length(cmd) >= Length(ll)) and (ll = Copy(cmd, 0, Length(ll))) then
     begin
@@ -1049,13 +1135,13 @@ begin
     IK_DELETE:
       if (Length(Line) > 0) and (CPos <= Length(Line)) then
         Delete(Line, CPos, 1);
-    IK_LEFT, IK_KPLEFT:
+    IK_LEFT, IK_KPLEFT, VK_LEFT:
       if CPos > 1 then
         CPos := CPos - 1;
-    IK_RIGHT, IK_KPRIGHT:
+    IK_RIGHT, IK_KPRIGHT, VK_RIGHT:
       if CPos <= Length(Line) then
         CPos := CPos + 1;
-    IK_RETURN, IK_KPRETURN:
+    IK_RETURN, IK_KPRETURN, VK_OPEN, VK_FIRE:
     begin
       if Cons_Shown then
         g_Console_Process(Line)
@@ -1086,12 +1172,13 @@ begin
           CPos := 1;
           gChatShow := False;
           gJustChatted := True;
+          g_Touch_ShowKeyboard(gConsoleShow or gChatShow);
         end;
     end;
     IK_TAB:
       if not gChatShow then
         Complete();
-    IK_DOWN, IK_KPDOWN:
+    IK_DOWN, IK_KPDOWN, VK_DOWN:
       if not gChatShow then
         if (CommandHistory <> nil) and
            (CmdIndex < Length(CommandHistory)) then
@@ -1101,7 +1188,7 @@ begin
           Line := CommandHistory[CmdIndex];
           CPos := Length(Line) + 1;
         end;
-    IK_UP, IK_KPUP:
+    IK_UP, IK_KPUP, VK_UP:
       if not gChatShow then
         if (CommandHistory <> nil) and
            (CmdIndex <= Length(CommandHistory)) then
@@ -1111,9 +1198,9 @@ begin
           Line := CommandHistory[CmdIndex];
           Cpos := Length(Line) + 1;
         end;
-    IK_PAGEUP, IK_KPPAGEUP: // PgUp
+    IK_PAGEUP, IK_KPPAGEUP, VK_PREV: // PgUp
       if not gChatShow then Inc(conSkipLines);
-    IK_PAGEDN, IK_KPPAGEDN: // PgDown
+    IK_PAGEDN, IK_KPPAGEDN, VK_NEXT: // PgDown
       if not gChatShow and (conSkipLines > 0) then Dec(conSkipLines);
     IK_HOME, IK_KPHOME:
       CPos := 1;
@@ -1149,7 +1236,7 @@ begin
     end;
 end;
 
-function ParseString(Str: AnsiString): SArray;
+function ParseString(Str: AnsiString): SSArray;
 begin
   Result := nil;
 
@@ -1300,7 +1387,7 @@ end;
 
 function g_Console_CommandBlacklisted(C: AnsiString): Boolean;
 var
-  Arr: SArray;
+  Arr: SSArray;
   i: Integer;
 begin
   Result := True;
@@ -1321,7 +1408,7 @@ end;
 
 procedure g_Console_Process(L: AnsiString; quiet: Boolean = False);
 var
-  Arr: SArray;
+  Arr: SSArray;
   i: Integer;
 begin
   Arr := nil;
@@ -1377,4 +1464,5 @@ begin
   g_Console_Add(Format(_lc[I_CONSOLE_UNKNOWN], [Arr[0]]));
 end;
 
+
 end.