DEADSOFTWARE

changed license to GPLv3 only; sorry, no trust to FSF anymore
[d2df-sdl.git] / src / game / g_console.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_console;
18 interface
20 uses
21 utils; // for SSArray
23 const
24 ACTION_JUMP = 0;
25 ACTION_MOVELEFT = 1;
26 ACTION_MOVERIGHT = 2;
27 ACTION_LOOKDOWN = 3;
28 ACTION_LOOKUP = 4;
29 ACTION_ATTACK = 5;
30 ACTION_SCORES = 6;
31 ACTION_ACTIVATE = 7;
32 ACTION_STRAFE = 8;
33 ACTION_WEAPNEXT = 9;
34 ACTION_WEAPPREV = 10;
36 FIRST_ACTION = ACTION_JUMP;
37 LAST_ACTION = ACTION_WEAPPREV;
39 procedure g_Console_Init;
40 procedure g_Console_Update;
41 procedure g_Console_Draw (MessagesOnly: Boolean = False);
42 procedure g_Console_Char (C: AnsiChar);
43 procedure g_Console_Control (K: Word);
44 procedure g_Console_Process (L: AnsiString; quiet: Boolean=false);
45 procedure g_Console_Add (L: AnsiString; show: Boolean=false);
46 procedure g_Console_Clear;
47 function g_Console_CommandBlacklisted (C: AnsiString): Boolean;
48 procedure g_Console_ReadConfig (filename: String);
49 procedure g_Console_WriteConfig (filename: String);
50 procedure g_Console_WriteGameConfig;
52 function g_Console_Interactive: Boolean;
53 function g_Console_Action (action: Integer): Boolean;
54 function g_Console_MatchBind (key: Integer; down: AnsiString; up: AnsiString = ''): Boolean;
55 function g_Console_FindBind (n: Integer; down: AnsiString; up: AnsiString = ''): Integer;
56 procedure g_Console_BindKey (key: Integer; down: AnsiString; up: AnsiString = '');
57 procedure g_Console_ProcessBind (key: Integer; down: Boolean);
58 procedure g_Console_ResetBinds;
60 procedure conwriteln (const s: AnsiString; show: Boolean=false);
61 procedure conwritefln (const s: AnsiString; args: array of const; show: Boolean=false);
63 procedure conRegVar (const conname: AnsiString; pvar: PBoolean; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
64 procedure conRegVar (const conname: AnsiString; pvar: PSingle; amin, amax: Single; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
65 procedure conRegVar (const conname: AnsiString; pvar: PInteger; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
66 procedure conRegVar (const conname: AnsiString; pvar: PAnsiString; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
68 // <0: no arg; 0/1: true/false
69 function conGetBoolArg (p: SSArray; idx: Integer): Integer;
71 // poor man's floating literal parser; i'm sorry, but `StrToFloat()` sux cocks
72 function conParseFloat (var res: Single; const s: AnsiString): Boolean;
75 var
76 gConsoleShow: Boolean = false; // True - êîíñîëü îòêðûòà èëè îòêðûâàåòñÿ
77 gChatShow: Boolean = false;
78 gChatTeam: Boolean = false;
79 gAllowConsoleMessages: Boolean = true;
80 gJustChatted: Boolean = false; // ÷òîáû àäìèí â èíòåðå ÷àòÿñü íå ïðîìàòûâàë ñòàòèñòèêó
81 gParsingBinds: Boolean = true; // íå ïåðåñîõðàíÿòü êîíôèã âî âðåìÿ ïàðñèíãà
82 gPlayerAction: Array [0..1, 0..LAST_ACTION] of Boolean; // [player, action]
84 implementation
86 uses
87 g_textures, g_main, e_graphics, e_input, g_game,
88 SysUtils, g_basic, g_options, Math, g_touch,
89 g_menu, g_gui, g_language, g_net, g_netmsg, e_log, conbuf;
92 type
93 PCommand = ^TCommand;
95 TCmdProc = procedure (p: SSArray);
96 TCmdProcEx = procedure (me: PCommand; p: SSArray);
98 TCommand = record
99 cmd: AnsiString;
100 proc: TCmdProc;
101 procEx: TCmdProcEx;
102 help: AnsiString;
103 hidden: Boolean;
104 ptr: Pointer; // various data
105 msg: AnsiString; // message for var changes
106 cheat: Boolean;
107 action: Integer; // >= 0 for action commands
108 player: Integer; // used for action commands
109 end;
111 TAlias = record
112 name: AnsiString;
113 commands: SSArray;
114 end;
117 const
118 MsgTime = 144;
119 MaxScriptRecursion = 16;
121 DEBUG_STRING = 'DEBUG MODE';
123 var
124 ID: DWORD;
125 RecursionDepth: Word = 0;
126 RecursionLimitHit: Boolean = False;
127 Cons_Y: SmallInt;
128 ConsoleHeight: Single;
129 Cons_Shown: Boolean; // draw console
130 InputReady: Boolean; // allow text input in console/chat
131 Line: AnsiString;
132 CPos: Word;
133 //ConsoleHistory: SSArray;
134 CommandHistory: SSArray;
135 Whitelist: SSArray;
136 commands: Array of TCommand = nil;
137 Aliases: Array of TAlias = nil;
138 CmdIndex: Word;
139 conSkipLines: Integer = 0;
140 MsgArray: Array [0..4] of record
141 Msg: AnsiString;
142 Time: Word;
143 end;
145 gInputBinds: Array [0..e_MaxInputKeys - 1] of record
146 down, up: SSArray;
147 end;
148 menu_toggled: BOOLEAN; (* hack for menu controls *)
149 ChatTop: BOOLEAN;
150 ConsoleStep: Single;
151 ConsoleTrans: Single;
154 procedure g_Console_Switch;
155 begin
156 Cons_Y := Min(0, Max(Cons_Y, -Floor(gScreenHeight * ConsoleHeight)));
157 if Cons_Shown = False then
158 Cons_Y := -Floor(gScreenHeight * ConsoleHeight);
159 gChatShow := False;
160 gConsoleShow := not gConsoleShow;
161 Cons_Shown := True;
162 InputReady := False;
163 g_Touch_ShowKeyboard(gConsoleShow or gChatShow);
164 end;
166 procedure g_Console_Chat_Switch (Team: Boolean = False);
167 begin
168 if not g_Game_IsNet then Exit;
169 Cons_Y := Min(0, Max(Cons_Y, -Floor(gScreenHeight * ConsoleHeight)));
170 if Cons_Shown = False then
171 Cons_Y := -Floor(gScreenHeight * ConsoleHeight);
172 gConsoleShow := False;
173 gChatShow := not gChatShow;
174 gChatTeam := Team;
175 Cons_Shown := True;
176 InputReady := False;
177 Line := '';
178 CPos := 1;
179 g_Touch_ShowKeyboard(gConsoleShow or gChatShow);
180 end;
182 // poor man's floating literal parser; i'm sorry, but `StrToFloat()` sux cocks
183 function conParseFloat (var res: Single; const s: AnsiString): Boolean;
184 var
185 pos: Integer = 1;
186 frac: Single = 1;
187 slen: Integer;
188 begin
189 result := false;
190 res := 0;
191 slen := Length(s);
192 while (slen > 0) and (s[slen] <= ' ') do Dec(slen);
193 while (pos <= slen) and (s[pos] <= ' ') do Inc(pos);
194 if (pos > slen) then exit;
195 if (slen-pos = 1) and (s[pos] = '.') then exit; // single dot
196 // integral part
197 while (pos <= slen) do
198 begin
199 if (s[pos] < '0') or (s[pos] > '9') then break;
200 res := res*10+Byte(s[pos])-48;
201 Inc(pos);
202 end;
203 if (pos <= slen) then
204 begin
205 // must be a dot
206 if (s[pos] <> '.') then exit;
207 Inc(pos);
208 while (pos <= slen) do
209 begin
210 if (s[pos] < '0') or (s[pos] > '9') then break;
211 frac := frac/10;
212 res += frac*(Byte(s[pos])-48);
213 Inc(pos);
214 end;
215 end;
216 if (pos <= slen) then exit; // oops
217 result := true;
218 end;
221 // ////////////////////////////////////////////////////////////////////////// //
222 // <0: no arg; 0/1: true/false; 666: toggle
223 function conGetBoolArg (p: SSArray; idx: Integer): Integer;
224 begin
225 if (idx < 0) or (idx > High(p)) then begin result := -1; exit; end;
226 result := 0;
227 if (p[idx] = '1') or (CompareText(p[idx], 'on') = 0) or (CompareText(p[idx], 'true') = 0) or
228 (CompareText(p[idx], 'tan') = 0) or (CompareText(p[idx], 'yes') = 0) then result := 1
229 else if (CompareText(p[idx], 'toggle') = 0) or (CompareText(p[idx], 'switch') = 0) or
230 (CompareText(p[idx], 't') = 0) then result := 666;
231 end;
234 procedure boolVarHandler (me: PCommand; p: SSArray);
235 procedure binaryFlag (var flag: Boolean; msg: AnsiString);
236 var
237 old: Boolean;
238 begin
239 if (Length(p) > 2) then
240 begin
241 conwritefln('too many arguments to ''%s''', [p[0]]);
242 end
243 else
244 begin
245 old := flag;
246 case conGetBoolArg(p, 1) of
247 -1: begin end;
248 0: if not me.cheat or conIsCheatsEnabled then flag := false else begin conwriteln('not available'); exit; end;
249 1: if not me.cheat or conIsCheatsEnabled then flag := true else begin conwriteln('not available'); exit; end;
250 666: if not me.cheat or conIsCheatsEnabled then flag := not flag else begin conwriteln('not available'); exit; end;
251 end;
252 if flag <> old then
253 g_Console_WriteGameConfig();
254 if (Length(msg) = 0) then msg := p[0] else msg += ':';
255 if flag then conwritefln('%s tan', [msg]) else conwritefln('%s ona', [msg]);
256 end;
257 end;
258 begin
259 binaryFlag(PBoolean(me.ptr)^, me.msg);
260 end;
263 procedure intVarHandler (me: PCommand; p: SSArray);
264 var
265 old: Integer;
266 begin
267 if (Length(p) <> 2) then
268 begin
269 conwritefln('%s %d', [me.cmd, PInteger(me.ptr)^]);
270 end
271 else
272 begin
273 try
274 old := PInteger(me.ptr)^;
275 PInteger(me.ptr)^ := StrToInt(p[1]);
276 if PInteger(me.ptr)^ <> old then
277 g_Console_WriteGameConfig();
278 except
279 conwritefln('invalid integer value: "%s"', [p[1]]);
280 end;
281 end;
282 end;
285 procedure strVarHandler (me: PCommand; p: SSArray);
286 var
287 old: AnsiString;
288 begin
289 if (Length(p) <> 2) then
290 begin
291 conwritefln('%s %s', [me.cmd, QuoteStr(PAnsiString(me.ptr)^)]);
292 end
293 else
294 begin
295 old := PAnsiString(me.ptr)^;
296 PAnsiString(me.ptr)^ := p[1];
297 if PAnsiString(me.ptr)^ <> old then
298 g_Console_WriteGameConfig();
299 end;
300 end;
303 procedure conRegVar (const conname: AnsiString; pvar: PBoolean; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
304 var
305 f: Integer;
306 cp: PCommand;
307 begin
308 f := Length(commands);
309 SetLength(commands, f+1);
310 cp := @commands[f];
311 cp.cmd := LowerCase(conname);
312 cp.proc := nil;
313 cp.procEx := boolVarHandler;
314 cp.help := ahelp;
315 cp.hidden := ahidden;
316 cp.ptr := pvar;
317 cp.msg := amsg;
318 cp.cheat := acheat;
319 cp.action := -1;
320 cp.player := -1;
321 end;
324 procedure conRegVar (const conname: AnsiString; pvar: PInteger; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
325 var
326 f: Integer;
327 cp: PCommand;
328 begin
329 f := Length(commands);
330 SetLength(commands, f+1);
331 cp := @commands[f];
332 cp.cmd := LowerCase(conname);
333 cp.proc := nil;
334 cp.procEx := intVarHandler;
335 cp.help := ahelp;
336 cp.hidden := ahidden;
337 cp.ptr := pvar;
338 cp.msg := amsg;
339 cp.cheat := acheat;
340 cp.action := -1;
341 cp.player := -1;
342 end;
345 procedure conRegVar (const conname: AnsiString; pvar: PAnsiString; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
346 var
347 f: Integer;
348 cp: PCommand;
349 begin
350 f := Length(commands);
351 SetLength(commands, f+1);
352 cp := @commands[f];
353 cp.cmd := LowerCase(conname);
354 cp.proc := nil;
355 cp.procEx := strVarHandler;
356 cp.help := ahelp;
357 cp.hidden := ahidden;
358 cp.ptr := pvar;
359 cp.msg := amsg;
360 cp.cheat := acheat;
361 cp.action := -1;
362 cp.player := -1;
363 end;
365 // ////////////////////////////////////////////////////////////////////////// //
366 type
367 PVarSingle = ^TVarSingle;
368 TVarSingle = record
369 val: PSingle;
370 min, max, def: Single; // default will be starting value
371 end;
374 procedure singleVarHandler (me: PCommand; p: SSArray);
375 var
376 pv: PVarSingle;
377 nv, old: Single;
378 msg: AnsiString;
379 begin
380 if (Length(p) > 2) then
381 begin
382 conwritefln('too many arguments to ''%s''', [me.cmd]);
383 exit;
384 end;
385 pv := PVarSingle(me.ptr);
386 old := pv.val^;
387 if (Length(p) = 2) then
388 begin
389 if me.cheat and (not conIsCheatsEnabled) then begin conwriteln('not available'); exit; end;
390 if (CompareText(p[1], 'default') = 0) or (CompareText(p[1], 'def') = 0) or
391 (CompareText(p[1], 'd') = 0) or (CompareText(p[1], 'off') = 0) or
392 (CompareText(p[1], 'ona') = 0) then
393 begin
394 pv.val^ := pv.def;
395 end
396 else
397 begin
398 if not conParseFloat(nv, p[1]) then
399 begin
400 conwritefln('%s: ''%s'' doesn''t look like a floating number', [me.cmd, p[1]]);
401 exit;
402 end;
403 if (nv < pv.min) then nv := pv.min;
404 if (nv > pv.max) then nv := pv.max;
405 pv.val^ := nv;
406 end;
407 end;
408 if pv.val^ <> old then
409 g_Console_WriteGameConfig();
410 msg := me.msg;
411 if (Length(msg) = 0) then msg := me.cmd else msg += ':';
412 conwritefln('%s %s', [msg, pv.val^]);
413 end;
416 procedure conRegVar (const conname: AnsiString; pvar: PSingle; amin, amax: Single; const ahelp: AnsiString; const amsg: AnsiString; acheat: Boolean=false; ahidden: Boolean=false); overload;
417 var
418 f: Integer;
419 cp: PCommand;
420 pv: PVarSingle;
421 begin
422 GetMem(pv, sizeof(TVarSingle));
423 pv.val := pvar;
424 pv.min := amin;
425 pv.max := amax;
426 pv.def := pvar^;
427 f := Length(commands);
428 SetLength(commands, f+1);
429 cp := @commands[f];
430 cp.cmd := LowerCase(conname);
431 cp.proc := nil;
432 cp.procEx := singleVarHandler;
433 cp.help := ahelp;
434 cp.hidden := ahidden;
435 cp.ptr := pv;
436 cp.msg := amsg;
437 cp.cheat := acheat;
438 cp.action := -1;
439 cp.player := -1;
440 end;
443 // ////////////////////////////////////////////////////////////////////////// //
444 function GetStrACmd(var Str: AnsiString): AnsiString;
445 var
446 a: Integer;
447 begin
448 Result := '';
449 for a := 1 to Length(Str) do
450 if (a = Length(Str)) or (Str[a+1] = ';') then
451 begin
452 Result := Copy(Str, 1, a);
453 Delete(Str, 1, a+1);
454 Str := Trim(Str);
455 Exit;
456 end;
457 end;
459 function ParseAlias(Str: AnsiString): SSArray;
460 begin
461 Result := nil;
463 Str := Trim(Str);
465 if Str = '' then
466 Exit;
468 while Str <> '' do
469 begin
470 SetLength(Result, Length(Result)+1);
471 Result[High(Result)] := GetStrACmd(Str);
472 end;
473 end;
475 procedure ConsoleCommands(p: SSArray);
476 var
477 cmd, s: AnsiString;
478 a, b: Integer;
479 (* F: TextFile; *)
480 begin
481 cmd := LowerCase(p[0]);
482 s := '';
484 if cmd = 'clear' then
485 begin
486 //ConsoleHistory := nil;
487 cbufClear();
488 conSkipLines := 0;
490 for a := 0 to High(MsgArray) do
491 with MsgArray[a] do
492 begin
493 Msg := '';
494 Time := 0;
495 end;
496 end;
498 if cmd = 'clearhistory' then
499 CommandHistory := nil;
501 if cmd = 'showhistory' then
502 if CommandHistory <> nil then
503 begin
504 g_Console_Add('');
505 for a := 0 to High(CommandHistory) do
506 g_Console_Add(' '+CommandHistory[a]);
507 end;
509 if cmd = 'commands' then
510 begin
511 g_Console_Add('');
512 g_Console_Add('commands list:');
513 for a := High(commands) downto 0 do
514 begin
515 if (Length(commands[a].help) > 0) then
516 begin
517 g_Console_Add(' '+commands[a].cmd+' -- '+commands[a].help);
518 end
519 else
520 begin
521 g_Console_Add(' '+commands[a].cmd);
522 end;
523 end;
524 end;
526 if cmd = 'time' then
527 g_Console_Add(TimeToStr(Now), True);
529 if cmd = 'date' then
530 g_Console_Add(DateToStr(Now), True);
532 if cmd = 'echo' then
533 if Length(p) > 1 then
534 begin
535 if p[1] = 'ololo' then
536 gCheats := True
537 else
538 begin
539 s := '';
540 for a := 1 to High(p) do
541 s := s + p[a] + ' ';
542 g_Console_Add(b_Text_Format(s), True);
543 end;
544 end
545 else
546 g_Console_Add('');
548 if cmd = 'dump' then
549 begin
550 (*
551 if ConsoleHistory <> nil then
552 begin
553 if Length(P) > 1 then
554 s := P[1]
555 else
556 s := GameDir+'/console.txt';
558 {$I-}
559 AssignFile(F, s);
560 Rewrite(F);
561 if IOResult <> 0 then
562 begin
563 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [s]));
564 CloseFile(F);
565 Exit;
566 end;
568 for a := 0 to High(ConsoleHistory) do
569 WriteLn(F, ConsoleHistory[a]);
571 CloseFile(F);
572 g_Console_Add(Format(_lc[I_CONSOLE_DUMPED], [s]));
573 {$I+}
574 end;
575 *)
576 end;
578 if cmd = 'exec' then
579 begin
580 // exec <filename>
581 if Length(p) = 2 then
582 g_Console_ReadConfig(GameDir + '/' + p[1])
583 else
584 g_Console_Add('exec <script file>');
585 end;
587 if cmd = 'writeconfig' then
588 begin
589 // writeconfig <filename>
590 if Length(p) = 2 then
591 g_Console_WriteConfig(GameDir + '/' + p[1])
592 else
593 g_Console_Add('writeconfig <file>');
594 end;
596 if (cmd = 'ver') or (cmd = 'version') then
597 begin
598 conwriteln('Doom 2D: Forever v. ' + GAME_VERSION);
599 conwritefln('Net protocol v. %d', [NET_PROTOCOL_VER]);
600 conwritefln('Build date: %s at %s', [GAME_BUILDDATE, GAME_BUILDTIME]);
601 end;
603 if cmd = 'alias' then
604 begin
605 // alias [alias_name] [commands]
606 if Length(p) > 1 then
607 begin
608 for a := 0 to High(Aliases) do
609 if Aliases[a].name = p[1] then
610 begin
611 if Length(p) > 2 then
612 Aliases[a].commands := ParseAlias(p[2])
613 else
614 for b := 0 to High(Aliases[a].commands) do
615 g_Console_Add(Aliases[a].commands[b]);
616 Exit;
617 end;
618 SetLength(Aliases, Length(Aliases)+1);
619 a := High(Aliases);
620 Aliases[a].name := p[1];
621 if Length(p) > 2 then
622 Aliases[a].commands := ParseAlias(p[2])
623 else
624 for b := 0 to High(Aliases[a].commands) do
625 g_Console_Add(Aliases[a].commands[b]);
626 end else
627 for a := 0 to High(Aliases) do
628 if Aliases[a].commands <> nil then
629 g_Console_Add(Aliases[a].name);
630 end;
632 if cmd = 'call' then
633 begin
634 // call <alias_name>
635 if Length(p) > 1 then
636 begin
637 if Aliases = nil then
638 Exit;
639 for a := 0 to High(Aliases) do
640 if Aliases[a].name = p[1] then
641 begin
642 if Aliases[a].commands <> nil then
643 begin
644 // with this system proper endless loop detection seems either impossible
645 // or very dirty to implement, so let's have this instead
646 // prevents endless loops
647 for b := 0 to High(Aliases[a].commands) do
648 begin
649 Inc(RecursionDepth);
650 RecursionLimitHit := (RecursionDepth > MaxScriptRecursion) or RecursionLimitHit;
651 if not RecursionLimitHit then
652 g_Console_Process(Aliases[a].commands[b], True);
653 Dec(RecursionDepth);
654 end;
655 if (RecursionDepth = 0) and RecursionLimitHit then
656 begin
657 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_CALL], [s]));
658 RecursionLimitHit := False;
659 end;
660 end;
661 Exit;
662 end;
663 end
664 else
665 g_Console_Add('call <alias name>');
666 end;
667 end;
669 procedure WhitelistCommand(cmd: AnsiString);
670 var
671 a: Integer;
672 begin
673 SetLength(Whitelist, Length(Whitelist)+1);
674 a := High(Whitelist);
675 Whitelist[a] := LowerCase(cmd);
676 end;
678 procedure segfault (p: SSArray);
679 var
680 pp: PByte = nil;
681 begin
682 pp^ := 0;
683 end;
685 function GetCommandString (p: SSArray): AnsiString;
686 var i: Integer;
687 begin
688 result := '';
689 if Length(p) >= 1 then
690 begin
691 result := p[0];
692 for i := 1 to High(p) do
693 result := result + '; ' + p[i]
694 end
695 end;
697 function QuoteStr(str: String): String;
698 begin
699 if Pos(' ', str) > 0 then
700 Result := '"' + str + '"'
701 else
702 Result := str;
703 end;
705 procedure BindCommands (p: SSArray);
706 var cmd, key: AnsiString; i: Integer;
707 begin
708 cmd := LowerCase(p[0]);
709 case cmd of
710 'bind':
711 // bind <key> [down [up]]
712 if (Length(p) >= 2) and (Length(p) <= 4) then
713 begin
714 i := 0;
715 key := LowerCase(p[1]);
716 while (i < e_MaxInputKeys) and (key <> LowerCase(e_KeyNames[i])) do inc(i);
717 if i < e_MaxInputKeys then
718 begin
719 if Length(p) = 2 then
720 g_Console_Add(QuoteStr(e_KeyNames[i]) + ' = ' + QuoteStr(GetCommandString(gInputBinds[i].down)) + ' ' + QuoteStr(GetCommandString(gInputBinds[i].up)))
721 else if Length(p) = 3 then
722 g_Console_BindKey(i, p[2], '')
723 else (* len = 4 *)
724 g_Console_BindKey(i, p[2], p[3])
725 end
726 else
727 g_Console_Add('bind: "' + p[1] + '" is not a key')
728 end
729 else
730 begin
731 g_Console_Add('bind <key> <down action> [up action]')
732 end;
733 'bindlist':
734 for i := 0 to e_MaxInputKeys - 1 do
735 if (gInputBinds[i].down <> nil) or (gInputBinds[i].up <> nil) then
736 g_Console_Add(e_KeyNames[i] + ' ' + QuoteStr(GetCommandString(gInputBinds[i].down)) + ' ' + QuoteStr(GetCommandString(gInputBinds[i].up)));
737 'unbind':
738 // unbind <key>
739 if Length(p) = 2 then
740 begin
741 key := LowerCase(p[1]);
742 i := 0;
743 while (i < e_MaxInputKeys) and (key <> LowerCase(e_KeyNames[i])) do inc(i);
744 if i < e_MaxInputKeys then
745 g_Console_BindKey(i, '')
746 else
747 g_Console_Add('unbind: "' + p[1] + '" is not a key')
748 end
749 else
750 g_Console_Add('unbind <key>');
751 'unbindall':
752 for i := 0 to e_MaxInputKeys - 1 do
753 g_Console_BindKey(i, '');
754 'showkeyboard':
755 g_Touch_ShowKeyboard(True);
756 'hidekeyboard':
757 g_Touch_ShowKeyboard(False);
758 'togglemenu':
759 begin
760 if gConsoleShow then
761 g_Console_Switch
762 else if gChatShow then
763 g_Console_Chat_Switch
764 else
765 KeyPress(VK_ESCAPE);
766 menu_toggled := True
767 end;
768 'toggleconsole':
769 g_Console_Switch;
770 'togglechat':
771 g_Console_Chat_Switch;
772 'toggleteamchat':
773 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
774 g_Console_Chat_Switch(True);
775 end
776 end;
778 procedure AddCommand(cmd: AnsiString; proc: TCmdProc; ahelp: AnsiString=''; ahidden: Boolean=false; acheat: Boolean=false);
779 var
780 a: Integer;
781 cp: PCommand;
782 begin
783 SetLength(commands, Length(commands)+1);
784 a := High(commands);
785 cp := @commands[a];
786 cp.cmd := LowerCase(cmd);
787 cp.proc := proc;
788 cp.procEx := nil;
789 cp.help := ahelp;
790 cp.hidden := ahidden;
791 cp.ptr := nil;
792 cp.msg := '';
793 cp.cheat := acheat;
794 cp.action := -1;
795 cp.player := -1;
796 end;
798 procedure AddAction (cmd: AnsiString; action: Integer; help: AnsiString = ''; hidden: Boolean = False; cheat: Boolean = False);
799 const
800 PrefixList: array [0..1] of AnsiString = ('+', '-');
801 PlayerList: array [0..1] of Integer = (1, 2);
802 var
803 s: AnsiString;
804 i: Integer;
806 procedure NewAction (cmd: AnsiString; player: Integer);
807 var cp: PCommand;
808 begin
809 SetLength(commands, Length(commands) + 1);
810 cp := @commands[High(commands)];
811 cp.cmd := LowerCase(cmd);
812 cp.proc := nil;
813 cp.procEx := nil;
814 cp.help := help;
815 cp.hidden := hidden;
816 cp.ptr := nil;
817 cp.msg := '';
818 cp.cheat := cheat;
819 cp.action := action;
820 cp.player := player;
821 end;
823 begin
824 ASSERT(action >= FIRST_ACTION);
825 ASSERT(action <= LAST_ACTION);
826 for s in PrefixList do
827 begin
828 NewAction(s + cmd, 0);
829 for i in PlayerList do
830 NewAction(s + 'p' + IntToStr(i) + '_' + cmd, i - 1)
831 end
832 end;
834 procedure g_Console_Init();
835 var
836 a: Integer;
837 begin
838 g_Texture_CreateWAD(ID, GameWAD+':TEXTURES\CONSOLE');
839 Cons_Y := -Floor(gScreenHeight * ConsoleHeight);
840 gConsoleShow := False;
841 gChatShow := False;
842 Cons_Shown := False;
843 InputReady := False;
844 CPos := 1;
846 for a := 0 to High(MsgArray) do
847 with MsgArray[a] do
848 begin
849 Msg := '';
850 Time := 0;
851 end;
853 AddCommand('segfault', segfault, 'make segfault');
855 AddCommand('bind', BindCommands);
856 AddCommand('bindlist', BindCommands);
857 AddCommand('unbind', BindCommands);
858 AddCommand('unbindall', BindCommands);
859 AddCommand('showkeyboard', BindCommands);
860 AddCommand('hidekeyboard', BindCommands);
861 AddCommand('togglemenu', BindCommands);
862 AddCommand('toggleconsole', BindCommands);
863 AddCommand('togglechat', BindCommands);
864 AddCommand('toggleteamchat', BindCommands);
866 AddCommand('clear', ConsoleCommands, 'clear console');
867 AddCommand('clearhistory', ConsoleCommands);
868 AddCommand('showhistory', ConsoleCommands);
869 AddCommand('commands', ConsoleCommands);
870 AddCommand('time', ConsoleCommands);
871 AddCommand('date', ConsoleCommands);
872 AddCommand('echo', ConsoleCommands);
873 AddCommand('dump', ConsoleCommands);
874 AddCommand('exec', ConsoleCommands);
875 AddCommand('writeconfig', ConsoleCommands);
876 AddCommand('alias', ConsoleCommands);
877 AddCommand('call', ConsoleCommands);
878 AddCommand('ver', ConsoleCommands);
879 AddCommand('version', ConsoleCommands);
881 AddCommand('d_window', DebugCommands);
882 AddCommand('d_sounds', DebugCommands);
883 AddCommand('d_frames', DebugCommands);
884 AddCommand('d_winmsg', DebugCommands);
885 AddCommand('d_monoff', DebugCommands);
886 AddCommand('d_botoff', DebugCommands);
887 AddCommand('d_monster', DebugCommands);
888 AddCommand('d_health', DebugCommands);
889 AddCommand('d_player', DebugCommands);
890 AddCommand('d_joy', DebugCommands);
891 AddCommand('d_mem', DebugCommands);
893 AddCommand('p1_name', GameCVars);
894 AddCommand('p2_name', GameCVars);
895 AddCommand('p1_color', GameCVars);
896 AddCommand('p2_color', GameCVars);
897 AddCommand('r_showscore', GameCVars);
898 AddCommand('r_showlives', GameCVars);
899 AddCommand('r_showstat', GameCVars);
900 AddCommand('r_showkillmsg', GameCVars);
901 AddCommand('r_showspect', GameCVars);
902 AddCommand('r_showping', GameCVars);
903 AddCommand('g_gamemode', GameCVars);
904 AddCommand('g_friendlyfire', GameCVars);
905 AddCommand('g_weaponstay', GameCVars);
906 AddCommand('g_allow_exit', GameCVars);
907 AddCommand('g_allow_monsters', GameCVars);
908 AddCommand('g_bot_vsmonsters', GameCVars);
909 AddCommand('g_bot_vsplayers', GameCVars);
910 AddCommand('g_scorelimit', GameCVars);
911 AddCommand('g_timelimit', GameCVars);
912 AddCommand('g_maxlives', GameCVars);
913 AddCommand('g_warmuptime', GameCVars);
914 AddCommand('net_interp', GameCVars);
915 AddCommand('net_forceplayerupdate', GameCVars);
916 AddCommand('net_predictself', GameCVars);
917 AddCommand('sv_name', GameCVars);
918 AddCommand('sv_passwd', GameCVars);
919 AddCommand('sv_maxplrs', GameCVars);
920 AddCommand('sv_public', GameCVars);
921 AddCommand('sv_intertime', GameCVars);
923 AddCommand('quit', GameCommands);
924 AddCommand('exit', GameCommands);
925 AddCommand('pause', GameCommands);
926 AddCommand('endgame', GameCommands);
927 AddCommand('restart', GameCommands);
928 AddCommand('addbot', GameCommands);
929 AddCommand('bot_add', GameCommands);
930 AddCommand('bot_addlist', GameCommands);
931 AddCommand('bot_addred', GameCommands);
932 AddCommand('bot_addblue', GameCommands);
933 AddCommand('bot_removeall', GameCommands);
934 AddCommand('chat', GameCommands);
935 AddCommand('teamchat', GameCommands);
936 AddCommand('game', GameCommands);
937 AddCommand('host', GameCommands);
938 AddCommand('map', GameCommands);
939 AddCommand('nextmap', GameCommands);
940 AddCommand('endmap', GameCommands);
941 AddCommand('goodbye', GameCommands);
942 AddCommand('suicide', GameCommands);
943 AddCommand('spectate', GameCommands);
944 AddCommand('ready', GameCommands);
945 AddCommand('kick', GameCommands);
946 AddCommand('kick_id', GameCommands);
947 AddCommand('ban', GameCommands);
948 AddCommand('permban', GameCommands);
949 AddCommand('ban_id', GameCommands);
950 AddCommand('permban_id', GameCommands);
951 AddCommand('unban', GameCommands);
952 AddCommand('connect', GameCommands);
953 AddCommand('disconnect', GameCommands);
954 AddCommand('reconnect', GameCommands);
955 AddCommand('say', GameCommands);
956 AddCommand('tell', GameCommands);
957 AddCommand('overtime', GameCommands);
958 AddCommand('rcon_password', GameCommands);
959 AddCommand('rcon', GameCommands);
960 AddCommand('callvote', GameCommands);
961 AddCommand('vote', GameCommands);
962 AddCommand('clientlist', GameCommands);
963 AddCommand('event', GameCommands);
964 AddCommand('screenshot', GameCommands);
965 AddCommand('weapon', GameCommands);
966 AddCommand('p1_weapon', GameCommands);
967 AddCommand('p2_weapon', GameCommands);
969 AddCommand('god', GameCheats);
970 AddCommand('notarget', GameCheats);
971 AddCommand('give', GameCheats); // "exit" too ;-)
972 AddCommand('open', GameCheats);
973 AddCommand('fly', GameCheats);
974 AddCommand('noclip', GameCheats);
975 AddCommand('speedy', GameCheats);
976 AddCommand('jumpy', GameCheats);
977 AddCommand('noreload', GameCheats);
978 AddCommand('aimline', GameCheats);
979 AddCommand('automap', GameCheats);
981 AddAction('jump', ACTION_JUMP);
982 AddAction('moveleft', ACTION_MOVELEFT);
983 AddAction('moveright', ACTION_MOVERIGHT);
984 AddAction('lookup', ACTION_LOOKUP);
985 AddAction('lookdown', ACTION_LOOKDOWN);
986 AddAction('attack', ACTION_ATTACK);
987 AddAction('scores', ACTION_SCORES);
988 AddAction('activate', ACTION_ACTIVATE);
989 AddAction('strafe', ACTION_STRAFE);
990 AddAction('weapnext', ACTION_WEAPNEXT);
991 AddAction('weapprev', ACTION_WEAPPREV);
993 WhitelistCommand('say');
994 WhitelistCommand('tell');
995 WhitelistCommand('overtime');
996 WhitelistCommand('ready');
997 WhitelistCommand('map');
998 WhitelistCommand('nextmap');
999 WhitelistCommand('endmap');
1000 WhitelistCommand('restart');
1001 WhitelistCommand('kick');
1002 WhitelistCommand('ban');
1004 WhitelistCommand('addbot');
1005 WhitelistCommand('bot_add');
1006 WhitelistCommand('bot_addred');
1007 WhitelistCommand('bot_addblue');
1008 WhitelistCommand('bot_removeall');
1010 WhitelistCommand('g_gamemode');
1011 WhitelistCommand('g_friendlyfire');
1012 WhitelistCommand('g_weaponstay');
1013 WhitelistCommand('g_allow_exit');
1014 WhitelistCommand('g_allow_monsters');
1015 WhitelistCommand('g_scorelimit');
1016 WhitelistCommand('g_timelimit');
1018 g_Console_ResetBinds;
1019 g_Console_ReadConfig(GameDir + '/dfconfig.cfg');
1020 g_Console_ReadConfig(GameDir + '/autoexec.cfg');
1021 gParsingBinds := False;
1023 g_Console_Add(Format(_lc[I_CONSOLE_WELCOME], [GAME_VERSION]));
1024 g_Console_Add('');
1025 end;
1027 procedure g_Console_Update;
1028 var
1029 a, b, Step: Integer;
1030 begin
1031 if Cons_Shown then
1032 begin
1033 Step := Max(1, Round(Floor(gScreenHeight * ConsoleHeight) * ConsoleStep));
1034 if gConsoleShow then
1035 begin
1036 (* Open animation *)
1037 Cons_Y := Min(Cons_Y + Step, 0);
1038 InputReady := True
1039 end
1040 else
1041 begin
1042 (* Close animation *)
1043 Cons_Y := Max(Cons_Y - Step, -Floor(gScreenHeight * ConsoleHeight));
1044 Cons_Shown := Cons_Y > -Floor(gScreenHeight * ConsoleHeight);
1045 InputReady := False
1046 end;
1048 if gChatShow then
1049 InputReady := True
1050 end;
1052 a := 0;
1053 while a <= High(MsgArray) do
1054 begin
1055 if MsgArray[a].Time > 0 then
1056 begin
1057 if MsgArray[a].Time = 1 then
1058 begin
1059 if a < High(MsgArray) then
1060 begin
1061 for b := a to High(MsgArray)-1 do
1062 MsgArray[b] := MsgArray[b+1];
1064 MsgArray[High(MsgArray)].Time := 0;
1066 a := a - 1;
1067 end;
1068 end
1069 else
1070 Dec(MsgArray[a].Time);
1071 end;
1073 a := a + 1;
1074 end;
1075 end;
1078 procedure drawConsoleText ();
1079 var
1080 CWidth, CHeight: Byte;
1081 ty: Integer;
1082 sp, ep: LongWord;
1083 skip: Integer;
1085 procedure putLine (sp, ep: LongWord);
1086 var
1087 p: LongWord;
1088 wdt, cw: Integer;
1089 begin
1090 p := sp;
1091 wdt := 0;
1092 while p <> ep do
1093 begin
1094 cw := e_TextureFontCharWidth(cbufAt(p), gStdFont);
1095 if wdt+cw > gScreenWidth-8 then break;
1096 //e_TextureFontPrintChar(X, Y: Integer; Ch: Char; FontID: DWORD; Shadow: Boolean = False);
1097 Inc(wdt, cw);
1098 cbufNext(p);
1099 end;
1100 if p <> ep then putLine(p, ep); // do rest of the line first
1101 // now print our part
1102 if skip = 0 then
1103 begin
1104 ep := p;
1105 p := sp;
1106 wdt := 2;
1107 while p <> ep do
1108 begin
1109 cw := e_TextureFontCharWidth(cbufAt(p), gStdFont);
1110 e_TextureFontPrintCharEx(wdt, ty, cbufAt(p), gStdFont);
1111 Inc(wdt, cw);
1112 cbufNext(p);
1113 end;
1114 Dec(ty, CHeight);
1115 end
1116 else
1117 begin
1118 Dec(skip);
1119 end;
1120 end;
1122 begin
1123 e_TextureFontGetSize(gStdFont, CWidth, CHeight);
1124 ty := Floor(gScreenHeight * ConsoleHeight) - 4 - 2 * CHeight - Abs(Cons_Y);
1125 skip := conSkipLines;
1126 cbufLastLine(sp, ep);
1127 repeat
1128 putLine(sp, ep);
1129 if ty+CHeight <= 0 then break;
1130 until not cbufLineUp(sp, ep);
1131 end;
1133 procedure g_Console_Draw(MessagesOnly: Boolean = False);
1134 var
1135 CWidth, CHeight: Byte;
1136 mfW, mfH: Word;
1137 a, b, offset_y: Integer;
1138 begin
1139 e_TextureFontGetSize(gStdFont, CWidth, CHeight);
1141 if ChatTop and gChatShow then
1142 offset_y := CHeight
1143 else
1144 offset_y := 0;
1146 for a := 0 to High(MsgArray) do
1147 if MsgArray[a].Time > 0 then
1148 e_TextureFontPrintFmt(0, offset_y + CHeight * a, MsgArray[a].Msg, gStdFont, True);
1150 if MessagesOnly then Exit;
1152 if gChatShow then
1153 begin
1154 if ChatTop then
1155 offset_y := 0
1156 else
1157 offset_y := gScreenHeight - CHeight - 1;
1158 if gChatTeam then
1159 begin
1160 e_TextureFontPrintEx(0, offset_y, 'say team> ' + Line, gStdFont, 255, 255, 255, 1, True);
1161 e_TextureFontPrintEx((CPos + 9) * CWidth, offset_y, '_', gStdFont, 255, 255, 255, 1, True);
1162 end
1163 else
1164 begin
1165 e_TextureFontPrintEx(0, offset_y, 'say> ' + Line, gStdFont, 255, 255, 255, 1, True);
1166 e_TextureFontPrintEx((CPos + 4) * CWidth, offset_y, '_', gStdFont, 255, 255, 255, 1, True);
1167 end
1168 end;
1170 if not Cons_Shown then
1171 Exit;
1173 if gDebugMode then
1174 begin
1175 e_CharFont_GetSize(gMenuFont, DEBUG_STRING, mfW, mfH);
1176 a := (gScreenWidth - 2*mfW) div 2;
1177 b := Cons_Y + (Floor(gScreenHeight * ConsoleHeight) - 2 * mfH) div 2;
1178 e_CharFont_PrintEx(gMenuFont, a div 2, b div 2, DEBUG_STRING,
1179 _RGB(128, 0, 0), 2.0);
1180 end;
1182 e_DrawSize(ID, 0, Cons_Y, Round(ConsoleTrans * 255), False, False, gScreenWidth, Floor(gScreenHeight * ConsoleHeight));
1183 e_TextureFontPrint(0, Cons_Y + Floor(gScreenHeight * ConsoleHeight) - CHeight - 4, '> ' + Line, gStdFont);
1185 drawConsoleText();
1186 (*
1187 if ConsoleHistory <> nil then
1188 begin
1189 b := 0;
1190 if CHeight > 0 then
1191 if Length(ConsoleHistory) > (Floor(gScreenHeight * ConsoleHeight) div CHeight) - 1 then
1192 b := Length(ConsoleHistory) - (Floor(gScreenHeight * ConsoleHeight) div CHeight) + 1;
1194 b := Max(b-Offset, 0);
1195 d := Max(High(ConsoleHistory)-Offset, 0);
1197 c := 2;
1198 for a := d downto b do
1199 begin
1200 e_TextureFontPrintFmt(0, Floor(gScreenHeight * ConsoleHeight) - 4 - c * CHeight - Abs(Cons_Y), ConsoleHistory[a], gStdFont, True);
1201 c := c + 1;
1202 end;
1203 end;
1204 *)
1206 e_TextureFontPrint((CPos + 1) * CWidth, Cons_Y + Floor(gScreenHeight * ConsoleHeight) - 21, '_', gStdFont);
1207 end;
1209 procedure g_Console_Char(C: AnsiChar);
1210 begin
1211 if InputReady and (gConsoleShow or gChatShow) then
1212 begin
1213 Insert(C, Line, CPos);
1214 CPos := CPos + 1;
1215 end
1216 end;
1219 var
1220 tcomplist: array of AnsiString = nil;
1221 tcompidx: array of Integer = nil;
1223 procedure Complete ();
1224 var
1225 i, c: Integer;
1226 tused: Integer;
1227 ll, lpfx, cmd: AnsiString;
1228 begin
1229 if (Length(Line) = 0) then
1230 begin
1231 g_Console_Add('');
1232 for i := 0 to High(commands) do
1233 begin
1234 // hidden commands are hidden when cheats aren't enabled
1235 if commands[i].hidden and not conIsCheatsEnabled then continue;
1236 if (Length(commands[i].help) > 0) then
1237 begin
1238 g_Console_Add(' '+commands[i].cmd+' -- '+commands[i].help);
1239 end
1240 else
1241 begin
1242 g_Console_Add(' '+commands[i].cmd);
1243 end;
1244 end;
1245 exit;
1246 end;
1248 ll := LowerCase(Line);
1249 lpfx := '';
1251 if (Length(ll) > 1) and (ll[Length(ll)] = ' ') then
1252 begin
1253 ll := Copy(ll, 0, Length(ll)-1);
1254 for i := 0 to High(commands) do
1255 begin
1256 // hidden commands are hidden when cheats aren't enabled
1257 if commands[i].hidden and not conIsCheatsEnabled then continue;
1258 if (commands[i].cmd = ll) then
1259 begin
1260 if (Length(commands[i].help) > 0) then
1261 begin
1262 g_Console_Add(' '+commands[i].cmd+' -- '+commands[i].help);
1263 end;
1264 end;
1265 end;
1266 exit;
1267 end;
1269 // build completion list
1270 tused := 0;
1271 for i := 0 to High(commands) do
1272 begin
1273 // hidden commands are hidden when cheats aren't enabled
1274 if commands[i].hidden and not conIsCheatsEnabled then continue;
1275 cmd := commands[i].cmd;
1276 if (Length(cmd) >= Length(ll)) and (ll = Copy(cmd, 0, Length(ll))) then
1277 begin
1278 if (tused = Length(tcomplist)) then
1279 begin
1280 SetLength(tcomplist, Length(tcomplist)+128);
1281 SetLength(tcompidx, Length(tcompidx)+128);
1282 end;
1283 tcomplist[tused] := cmd;
1284 tcompidx[tused] := i;
1285 Inc(tused);
1286 if (Length(cmd) > Length(lpfx)) then lpfx := cmd;
1287 end;
1288 end;
1290 // get longest prefix
1291 for i := 0 to tused-1 do
1292 begin
1293 cmd := tcomplist[i];
1294 for c := 1 to Length(lpfx) do
1295 begin
1296 if (c > Length(cmd)) then break;
1297 if (cmd[c] <> lpfx[c]) then begin lpfx := Copy(lpfx, 0, c-1); break; end;
1298 end;
1299 end;
1301 if (tused = 0) then exit;
1303 if (tused = 1) then
1304 begin
1305 Line := tcomplist[0]+' ';
1306 CPos := Length(Line)+1;
1307 end
1308 else
1309 begin
1310 // has longest prefix?
1311 if (Length(lpfx) > Length(ll)) then
1312 begin
1313 Line := lpfx;
1314 CPos:= Length(Line)+1;
1315 end
1316 else
1317 begin
1318 g_Console_Add('');
1319 for i := 0 to tused-1 do
1320 begin
1321 if (Length(commands[tcompidx[i]].help) > 0) then
1322 begin
1323 g_Console_Add(' '+tcomplist[i]+' -- '+commands[tcompidx[i]].help);
1324 end
1325 else
1326 begin
1327 g_Console_Add(' '+tcomplist[i]);
1328 end;
1329 end;
1330 end;
1331 end;
1332 end;
1335 procedure g_Console_Control(K: Word);
1336 begin
1337 case K of
1338 IK_BACKSPACE:
1339 if (Length(Line) > 0) and (CPos > 1) then
1340 begin
1341 Delete(Line, CPos-1, 1);
1342 CPos := CPos-1;
1343 end;
1344 IK_DELETE:
1345 if (Length(Line) > 0) and (CPos <= Length(Line)) then
1346 Delete(Line, CPos, 1);
1347 IK_LEFT, IK_KPLEFT, VK_LEFT, JOY0_LEFT, JOY1_LEFT, JOY2_LEFT, JOY3_LEFT:
1348 if CPos > 1 then
1349 CPos := CPos - 1;
1350 IK_RIGHT, IK_KPRIGHT, VK_RIGHT, JOY0_RIGHT, JOY1_RIGHT, JOY2_RIGHT, JOY3_RIGHT:
1351 if CPos <= Length(Line) then
1352 CPos := CPos + 1;
1353 IK_RETURN, IK_KPRETURN, VK_OPEN, VK_FIRE, JOY0_ATTACK, JOY1_ATTACK, JOY2_ATTACK, JOY3_ATTACK:
1354 begin
1355 if gConsoleShow then
1356 g_Console_Process(Line)
1357 else
1358 if gChatShow then
1359 begin
1360 if (Length(Line) > 0) and g_Game_IsNet then
1361 begin
1362 if gChatTeam then
1363 begin
1364 if g_Game_IsClient then
1365 MC_SEND_Chat(b_Text_Format(Line), NET_CHAT_TEAM)
1366 else
1367 MH_SEND_Chat('[' + gPlayer1Settings.name + ']: ' + b_Text_Format(Line),
1368 NET_CHAT_TEAM, gPlayer1Settings.Team);
1369 end
1370 else
1371 begin
1372 if g_Game_IsClient then
1373 MC_SEND_Chat(b_Text_Format(Line), NET_CHAT_PLAYER)
1374 else
1375 MH_SEND_Chat('[' + gPlayer1Settings.name + ']: ' + b_Text_Format(Line),
1376 NET_CHAT_PLAYER);
1377 end;
1378 end;
1380 Line := '';
1381 CPos := 1;
1382 gJustChatted := True;
1383 g_Console_Chat_Switch;
1384 InputReady := False;
1385 end;
1386 end;
1387 IK_TAB:
1388 if not gChatShow then
1389 Complete();
1390 IK_DOWN, IK_KPDOWN, VK_DOWN, JOY0_DOWN, JOY1_DOWN, JOY2_DOWN, JOY3_DOWN:
1391 if not gChatShow then
1392 if (CommandHistory <> nil) and
1393 (CmdIndex < Length(CommandHistory)) then
1394 begin
1395 if CmdIndex < Length(CommandHistory)-1 then
1396 CmdIndex := CmdIndex + 1;
1397 Line := CommandHistory[CmdIndex];
1398 CPos := Length(Line) + 1;
1399 end;
1400 IK_UP, IK_KPUP, VK_UP, JOY0_UP, JOY1_UP, JOY2_UP, JOY3_UP:
1401 if not gChatShow then
1402 if (CommandHistory <> nil) and
1403 (CmdIndex <= Length(CommandHistory)) then
1404 begin
1405 if CmdIndex > 0 then
1406 CmdIndex := CmdIndex - 1;
1407 Line := CommandHistory[CmdIndex];
1408 Cpos := Length(Line) + 1;
1409 end;
1410 IK_PAGEUP, IK_KPPAGEUP, VK_PREV, JOY0_PREV, JOY1_PREV, JOY2_PREV, JOY3_PREV: // PgUp
1411 if not gChatShow then Inc(conSkipLines);
1412 IK_PAGEDN, IK_KPPAGEDN, VK_NEXT, JOY0_NEXT, JOY1_NEXT, JOY2_NEXT, JOY3_NEXT: // PgDown
1413 if not gChatShow and (conSkipLines > 0) then Dec(conSkipLines);
1414 IK_HOME, IK_KPHOME:
1415 CPos := 1;
1416 IK_END, IK_KPEND:
1417 CPos := Length(Line) + 1;
1418 IK_A..IK_Z, IK_SPACE, IK_SHIFT, IK_RSHIFT, IK_CAPSLOCK, IK_LBRACKET, IK_RBRACKET,
1419 IK_SEMICOLON, IK_QUOTE, IK_BACKSLASH, IK_SLASH, IK_COMMA, IK_DOT, IK_EQUALS,
1420 IK_0, IK_1, IK_2, IK_3, IK_4, IK_5, IK_6, IK_7, IK_8, IK_9, IK_MINUS, IK_EQUALS:
1421 (* see TEXTINPUT event *)
1422 end
1423 end;
1425 function GetStr(var Str: AnsiString): AnsiString;
1426 var
1427 a, b: Integer;
1428 begin
1429 Result := '';
1430 if Str[1] = '"' then
1431 begin
1432 for b := 1 to Length(Str) do
1433 if (b = Length(Str)) or (Str[b+1] = '"') then
1434 begin
1435 Result := Copy(Str, 2, b-1);
1436 Delete(Str, 1, b+1);
1437 Str := Trim(Str);
1438 Exit;
1439 end;
1440 end;
1442 for a := 1 to Length(Str) do
1443 if (a = Length(Str)) or (Str[a+1] = ' ') then
1444 begin
1445 Result := Copy(Str, 1, a);
1446 Delete(Str, 1, a+1);
1447 Str := Trim(Str);
1448 Exit;
1449 end;
1450 end;
1452 function ParseString(Str: AnsiString): SSArray;
1453 begin
1454 Result := nil;
1456 Str := Trim(Str);
1458 if Str = '' then
1459 Exit;
1461 while Str <> '' do
1462 begin
1463 SetLength(Result, Length(Result)+1);
1464 Result[High(Result)] := GetStr(Str);
1465 end;
1466 end;
1468 procedure g_Console_Add (L: AnsiString; show: Boolean=false);
1470 procedure conmsg (s: AnsiString);
1471 var
1472 a: Integer;
1473 begin
1474 if length(s) = 0 then exit;
1475 for a := 0 to High(MsgArray) do
1476 begin
1477 with MsgArray[a] do
1478 begin
1479 if Time = 0 then
1480 begin
1481 Msg := s;
1482 Time := MsgTime;
1483 exit;
1484 end;
1485 end;
1486 end;
1487 for a := 0 to High(MsgArray)-1 do MsgArray[a] := MsgArray[a+1];
1488 with MsgArray[High(MsgArray)] do
1489 begin
1490 Msg := L;
1491 Time := MsgTime;
1492 end;
1493 end;
1495 var
1496 f: Integer;
1497 begin
1498 // put it to console
1499 cbufPut(L);
1500 if (length(L) = 0) or ((L[length(L)] <> #10) and (L[length(L)] <> #13)) then cbufPut(#10);
1502 // now show 'em out of console too
1503 show := show and gAllowConsoleMessages;
1504 if show and gShowMessages then
1505 begin
1506 // Âûâîä ñòðîê ñ ïåðåíîñàìè ïî î÷åðåäè
1507 while length(L) > 0 do
1508 begin
1509 f := Pos(#10, L);
1510 if f <= 0 then f := length(L)+1;
1511 conmsg(Copy(L, 1, f-1));
1512 Delete(L, 1, f);
1513 end;
1514 end;
1516 //SetLength(ConsoleHistory, Length(ConsoleHistory)+1);
1517 //ConsoleHistory[High(ConsoleHistory)] := L;
1519 (*
1520 {$IFDEF HEADLESS}
1521 e_WriteLog('CON: ' + L, MSG_NOTIFY);
1522 {$ENDIF}
1523 *)
1524 end;
1527 var
1528 consolewriterLastWasEOL: Boolean = false;
1530 procedure consolewriter (constref buf; len: SizeUInt);
1531 var
1532 b: PByte;
1533 begin
1534 if (len < 1) then exit;
1535 b := PByte(@buf);
1536 consolewriterLastWasEOL := (b[len-1] = 13) or (b[len-1] = 10);
1537 while (len > 0) do
1538 begin
1539 if (b[0] <> 13) and (b[0] <> 10) then
1540 begin
1541 cbufPut(AnsiChar(b[0]));
1542 end
1543 else
1544 begin
1545 if (len > 1) and (b[0] = 13) then begin len -= 1; b += 1; end;
1546 cbufPut(#10);
1547 end;
1548 len -= 1;
1549 b += 1;
1550 end;
1551 end;
1554 // returns formatted string if `writerCB` is `nil`, empty string otherwise
1555 //function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1556 //TFormatStrFCallback = procedure (constref buf; len: SizeUInt);
1557 procedure conwriteln (const s: AnsiString; show: Boolean=false);
1558 begin
1559 g_Console_Add(s, show);
1560 end;
1563 procedure conwritefln (const s: AnsiString; args: array of const; show: Boolean=false);
1564 begin
1565 if show then
1566 begin
1567 g_Console_Add(formatstrf(s, args), true);
1568 end
1569 else
1570 begin
1571 consolewriterLastWasEOL := false;
1572 formatstrf(s, args, consolewriter);
1573 if not consolewriterLastWasEOL then cbufPut(#10);
1574 end;
1575 end;
1578 procedure g_Console_Clear();
1579 begin
1580 //ConsoleHistory := nil;
1581 cbufClear();
1582 conSkipLines := 0;
1583 end;
1585 procedure AddToHistory(L: AnsiString);
1586 var
1587 len: Integer;
1588 begin
1589 len := Length(CommandHistory);
1591 if (len = 0) or
1592 (LowerCase(CommandHistory[len-1]) <> LowerCase(L)) then
1593 begin
1594 SetLength(CommandHistory, len+1);
1595 CommandHistory[len] := L;
1596 end;
1598 CmdIndex := Length(CommandHistory);
1599 end;
1601 function g_Console_CommandBlacklisted(C: AnsiString): Boolean;
1602 var
1603 Arr: SSArray;
1604 i: Integer;
1605 begin
1606 Result := True;
1608 Arr := nil;
1610 if Trim(C) = '' then
1611 Exit;
1613 Arr := ParseString(C);
1614 if Arr = nil then
1615 Exit;
1617 for i := 0 to High(Whitelist) do
1618 if Whitelist[i] = LowerCase(Arr[0]) then
1619 Result := False;
1620 end;
1622 procedure g_Console_Process(L: AnsiString; quiet: Boolean = False);
1623 var
1624 Arr: SSArray;
1625 i: Integer;
1626 begin
1627 Arr := nil;
1629 if Trim(L) = '' then
1630 Exit;
1632 conSkipLines := 0; // "unscroll"
1634 if L = 'goobers' then
1635 begin
1636 Line := '';
1637 CPos := 1;
1638 gCheats := true;
1639 g_Console_Add('Your memory serves you well.');
1640 exit;
1641 end;
1643 if not quiet then
1644 begin
1645 g_Console_Add('> '+L);
1646 Line := '';
1647 CPos := 1;
1648 end;
1650 Arr := ParseString(L);
1651 if Arr = nil then
1652 Exit;
1654 if commands = nil then
1655 Exit;
1657 if not quiet then
1658 AddToHistory(L);
1660 for i := 0 to High(commands) do
1661 begin
1662 if commands[i].cmd = LowerCase(Arr[0]) then
1663 begin
1664 if commands[i].action >= 0 then
1665 begin
1666 gPlayerAction[commands[i].player, commands[i].action] := commands[i].cmd[1] = '+';
1667 exit
1668 end;
1669 if assigned(commands[i].procEx) then
1670 begin
1671 commands[i].procEx(@commands[i], Arr);
1672 exit
1673 end;
1674 if assigned(commands[i].proc) then
1675 begin
1676 commands[i].proc(Arr);
1677 exit
1678 end
1679 end
1680 end;
1682 g_Console_Add(Format(_lc[I_CONSOLE_UNKNOWN], [Arr[0]]));
1683 end;
1686 function g_Console_Interactive: Boolean;
1687 begin
1688 Result := gConsoleShow
1689 end;
1691 procedure g_Console_BindKey (key: Integer; down: AnsiString; up: AnsiString = '');
1692 begin
1693 //e_LogWritefln('bind "%s" "%s" <%s>', [LowerCase(e_KeyNames[key]), cmd, key]);
1694 ASSERT(key >= 0);
1695 ASSERT(key < e_MaxInputKeys);
1696 if key > 0 then
1697 begin
1698 gInputBinds[key].down := ParseAlias(down);
1699 gInputBinds[key].up := ParseAlias(up);
1700 end;
1701 g_Console_WriteGameConfig();
1702 end;
1704 function g_Console_MatchBind (key: Integer; down: AnsiString; up: AnsiString = ''): Boolean;
1706 function EqualsCommandLists (a, b: SSArray): Boolean;
1707 var i, len: Integer;
1708 begin
1709 result := False;
1710 len := Length(a);
1711 if len = Length(b) then
1712 begin
1713 i := 0;
1714 while (i < len) and (a[i] = b[i]) do inc(i);
1715 if i >= len then
1716 result := True
1717 end
1718 end;
1720 begin
1721 ASSERT(key >= 0);
1722 ASSERT(key < e_MaxInputKeys);
1723 result := EqualsCommandLists(ParseAlias(down), gInputBinds[key].down) and EqualsCommandLists(ParseAlias(up), gInputBinds[key].up)
1724 end;
1726 function g_Console_FindBind (n: Integer; down: AnsiString; up: AnsiString = ''): Integer;
1727 var i: Integer;
1728 begin
1729 ASSERT(n >= 1);
1730 result := 0;
1731 if commands = nil then Exit;
1732 i := 0;
1733 while (n >= 1) and (i < e_MaxInputKeys) do
1734 begin
1735 if g_Console_MatchBind(i, down, up) then
1736 begin
1737 result := i;
1738 dec(n)
1739 end;
1740 inc(i)
1741 end;
1742 if n >= 1 then
1743 result := 0
1744 end;
1746 function g_Console_Action (action: Integer): Boolean;
1747 var i, len: Integer;
1748 begin
1749 ASSERT(action >= FIRST_ACTION);
1750 ASSERT(action <= LAST_ACTION);
1751 i := 0;
1752 len := Length(gPlayerAction);
1753 while (i < len) and (not gPlayerAction[i, action]) do inc(i);
1754 Result := i < len
1755 end;
1757 function BindsAllowed (key: Integer): Boolean;
1758 begin
1759 Result := False;
1760 if (not g_GUIGrabInput) and (key >= 0) and (key < e_MaxInputKeys) and ((gInputBinds[key].down <> nil) or (gInputBinds[key].up <> nil)) then
1761 begin
1762 if gChatShow then
1763 Result := g_Console_MatchBind(key, 'togglemenu') or
1764 g_Console_MatchBind(key, 'showkeyboard') or
1765 g_Console_MatchBind(key, 'hidekeyboard')
1766 else if gConsoleShow or (g_ActiveWindow <> nil) or (gGameSettings.GameType = GT_NONE) then
1767 Result := g_Console_MatchBind(key, 'togglemenu') or
1768 g_Console_MatchBind(key, 'toggleconsole') or
1769 g_Console_MatchBind(key, 'showkeyboard') or
1770 g_Console_MatchBind(key, 'hidekeyboard')
1771 else (* in game *)
1772 Result := True
1773 end
1774 end;
1776 procedure g_Console_ProcessBind (key: Integer; down: Boolean);
1777 var i: Integer;
1778 begin
1779 if BindsAllowed(key) then
1780 begin
1781 if down then
1782 for i := 0 to High(gInputBinds[key].down) do
1783 g_Console_Process(gInputBinds[key].down[i], True)
1784 else
1785 for i := 0 to High(gInputBinds[key].up) do
1786 g_Console_Process(gInputBinds[key].up[i], True)
1787 end;
1788 if down and not menu_toggled then
1789 KeyPress(key);
1790 menu_toggled := False
1791 end;
1793 procedure g_Console_ResetBinds;
1794 var i: Integer;
1795 begin
1796 for i := 0 to e_MaxInputKeys - 1 do
1797 g_Console_BindKey(i, '', '');
1799 g_Console_BindKey(IK_GRAVE, 'toggleconsole');
1800 g_Console_BindKey(IK_ESCAPE, 'togglemenu');
1801 g_Console_BindKey(IK_A, '+p1_moveleft', '-p1_moveleft');
1802 g_Console_BindKey(IK_D, '+p1_moveright', '-p1_moveright');
1803 g_Console_BindKey(IK_W, '+p1_lookup', '-p1_lookup');
1804 g_Console_BindKey(IK_S, '+p1_lookdown', '-p1_lookdown');
1805 g_Console_BindKey(IK_SPACE, '+p1_jump', '-p1_jump');
1806 g_Console_BindKey(IK_H, '+p1_attack', '-p1_attack');
1807 g_Console_BindKey(IK_J, '+p1_activate', '-p1_activate');
1808 g_Console_BindKey(IK_E, '+p1_weapnext', '-p1_weapnext');
1809 g_Console_BindKey(IK_Q, '+p1_weapprev', '-p1_weapprev');
1810 g_Console_BindKey(IK_ALT, '+p1_strafe', '-p1_strafe');
1811 g_Console_BindKey(IK_1, 'p1_weapon 1');
1812 g_Console_BindKey(IK_2, 'p1_weapon 2');
1813 g_Console_BindKey(IK_3, 'p1_weapon 3');
1814 g_Console_BindKey(IK_4, 'p1_weapon 4');
1815 g_Console_BindKey(IK_5, 'p1_weapon 5');
1816 g_Console_BindKey(IK_6, 'p1_weapon 6');
1817 g_Console_BindKey(IK_7, 'p1_weapon 7');
1818 g_Console_BindKey(IK_8, 'p1_weapon 8');
1819 g_Console_BindKey(IK_9, 'p1_weapon 9');
1820 g_Console_BindKey(IK_0, 'p1_weapon 10');
1821 g_Console_BindKey(IK_MINUS, 'p1_weapon 11');
1822 g_Console_BindKey(IK_T, 'togglechat');
1823 g_Console_BindKey(IK_Y, 'toggleteamchat');
1824 g_Console_BindKey(IK_F11, 'screenshot');
1825 g_Console_BindKey(IK_TAB, '+p1_scores', '-p1_scores');
1826 g_Console_BindKey(IK_PAUSE, 'pause');
1827 g_Console_BindKey(IK_F1, 'vote');
1829 (* for i := 0 to e_MaxJoys - 1 do *)
1830 for i := 0 to 1 do
1831 begin
1832 g_Console_BindKey(e_JoyHatToKey(i, 0, HAT_LEFT), '+p' + IntToStr(i mod 2 + 1) + '_moveleft', '-p' + IntToStr(i mod 2 + 1) + '_moveleft');
1833 g_Console_BindKey(e_JoyHatToKey(i, 0, HAT_RIGHT), '+p' + IntToStr(i mod 2 + 1) + '_moveright', '-p' + IntToStr(i mod 2 + 1) + '_moveright');
1834 g_Console_BindKey(e_JoyHatToKey(i, 0, HAT_UP), '+p' + IntToStr(i mod 2 + 1) + '_lookup', '-p' + IntToStr(i mod 2 + 1) + '_lookup');
1835 g_Console_BindKey(e_JoyHatToKey(i, 0, HAT_DOWN), '+p' + IntToStr(i mod 2 + 1) + '_lookdown', '-p' + IntToStr(i mod 2 + 1) + '_lookdown');
1836 g_Console_BindKey(e_JoyButtonToKey(i, 2), '+p' + IntToStr(i mod 2 + 1) + '_jump', '-p' + IntToStr(i mod 2 + 1) + '_jump');
1837 g_Console_BindKey(e_JoyButtonToKey(i, 0), '+p' + IntToStr(i mod 2 + 1) + '_attack', '-p' + IntToStr(i mod 2 + 1) + '_attack');
1838 g_Console_BindKey(e_JoyButtonToKey(i, 3), '+p' + IntToStr(i mod 2 + 1) + '_activate', '-p' + IntToStr(i mod 2 + 1) + '_activate');
1839 g_Console_BindKey(e_JoyButtonToKey(i, 1), '+p' + IntToStr(i mod 2 + 1) + '_weapnext', '-p' + IntToStr(i mod 2 + 1) + '_weapnext');
1840 g_Console_BindKey(e_JoyButtonToKey(i, 4), '+p' + IntToStr(i mod 2 + 1) + '_weapprev', '-p' + IntToStr(i mod 2 + 1) + '_weapprev');
1841 g_Console_BindKey(e_JoyButtonToKey(i, 7), '+p' + IntToStr(i mod 2 + 1) + '_strafe', '-p' + IntToStr(i mod 2 + 1) + '_strafe');
1842 g_Console_BindKey(e_JoyButtonToKey(i, 10), 'togglemenu');
1843 end;
1845 g_Console_BindKey(VK_ESCAPE, 'togglemenu');
1846 g_Console_BindKey(VK_LSTRAFE, '+moveleft; +strafe', '-moveleft; -strafe');
1847 g_Console_BindKey(VK_RSTRAFE, '+moveright; +strafe', '-moveright; -strafe');
1848 g_Console_BindKey(VK_LEFT, '+moveleft', '-moveleft');
1849 g_Console_BindKey(VK_RIGHT, '+moveright', '-moveright');
1850 g_Console_BindKey(VK_UP, '+lookup', '-lookup');
1851 g_Console_BindKey(VK_DOWN, '+lookdown', '-lookdown');
1852 g_Console_BindKey(VK_JUMP, '+jump', '-jump');
1853 g_Console_BindKey(VK_FIRE, '+attack', '-attack');
1854 g_Console_BindKey(VK_OPEN, '+activate', '-activate');
1855 g_Console_BindKey(VK_NEXT, '+weapnext', '-weapnext');
1856 g_Console_BindKey(VK_PREV, '+weapprev', '-weapprev');
1857 g_Console_BindKey(VK_STRAFE, '+strafe', '-strafe');
1858 g_Console_BindKey(VK_0, 'weapon 1');
1859 g_Console_BindKey(VK_1, 'weapon 2');
1860 g_Console_BindKey(VK_2, 'weapon 3');
1861 g_Console_BindKey(VK_3, 'weapon 4');
1862 g_Console_BindKey(VK_4, 'weapon 5');
1863 g_Console_BindKey(VK_5, 'weapon 6');
1864 g_Console_BindKey(VK_6, 'weapon 7');
1865 g_Console_BindKey(VK_7, 'weapon 8');
1866 g_Console_BindKey(VK_8, 'weapon 9');
1867 g_Console_BindKey(VK_9, 'weapon 10');
1868 g_Console_BindKey(VK_A, 'weapon 11');
1869 g_Console_BindKey(VK_CHAT, 'togglechat');
1870 g_Console_BindKey(VK_TEAM, 'toggleteamchat');
1871 g_Console_BindKey(VK_CONSOLE, 'toggleconsole');
1872 g_Console_BindKey(VK_PRINTSCR, 'screenshot');
1873 g_Console_BindKey(VK_STATUS, '+scores', '-scores');
1874 g_Console_BindKey(VK_SHOWKBD, 'showkeyboard');
1875 g_Console_BindKey(VK_HIDEKBD, 'hidekeyboard');
1876 end;
1878 procedure g_Console_ReadConfig (filename: String);
1879 var f: TextFile; s: AnsiString; i, len: Integer;
1880 begin
1881 if FileExists(filename) then
1882 begin
1883 AssignFile(f, filename);
1884 Reset(f);
1885 while not EOF(f) do
1886 begin
1887 ReadLn(f, s);
1888 len := Length(s);
1889 if len > 0 then
1890 begin
1891 i := 1;
1892 (* skip spaces *)
1893 while (i <= len) and (s[i] <= ' ') do inc(i);
1894 (* skip comments *)
1895 if (i <= len) and ((s[i] <> '#') and ((i + 1 > len) or (s[i] <> '/') or (s[i + 1] <> '/'))) then
1896 g_Console_Process(s, True);
1897 end
1898 end;
1899 CloseFile(f);
1900 end
1901 end;
1903 procedure g_Console_WriteConfig (filename: String);
1904 var f: TextFile; i, j: Integer;
1905 begin
1906 AssignFile(f, filename);
1907 Rewrite(f);
1908 WriteLn(f, '// generated by doom2d, do not modify');
1909 WriteLn(f, 'unbindall');
1910 for i := 0 to e_MaxInputKeys - 1 do
1911 if (Length(gInputBinds[i].down) > 0) or (Length(gInputBinds[i].up) > 0) then
1912 begin
1913 Write(f, 'bind ', e_KeyNames[i], ' ', QuoteStr(GetCommandString(gInputBinds[i].down)));
1914 if Length(gInputBinds[i].down) = 0 then
1915 Write(f, '""');
1916 if Length(gInputBinds[i].up) > 0 then
1917 Write(f, ' ', QuoteStr(GetCommandString(gInputBinds[i].up)));
1918 WriteLn(f, '');
1919 end;
1920 for i := 0 to High(commands) do
1921 begin
1922 if not commands[i].cheat then
1923 begin
1924 if @commands[i].procEx = @boolVarHandler then
1925 begin
1926 if PBoolean(commands[i].ptr)^ then j := 1 else j := 0;
1927 WriteLn(f, commands[i].cmd, ' ', j)
1928 end
1929 else if @commands[i].procEx = @intVarHandler then
1930 begin
1931 WriteLn(f, commands[i].cmd, ' ', PInteger(commands[i].ptr)^)
1932 end
1933 else if @commands[i].procEx = @singleVarHandler then
1934 begin
1935 WriteLn(f, commands[i].cmd, ' ', PVarSingle(commands[i].ptr).val^:0:6)
1936 end
1937 else if @commands[i].procEx = @strVarHandler then
1938 begin
1939 if Length(PAnsiString(commands[i].ptr)^) = 0 then
1940 WriteLn(f, commands[i].cmd, ' ""')
1941 else
1942 WriteLn(f, commands[i].cmd, ' ', QuoteStr(PAnsiString(commands[i].ptr)^))
1943 end
1944 end
1945 end;
1946 CloseFile(f)
1947 end;
1949 procedure g_Console_WriteGameConfig;
1950 begin
1951 if gParsingBinds then
1952 Exit;
1953 g_Console_WriteConfig(GameDir + '/dfconfig.cfg');
1954 end;
1956 initialization
1957 conRegVar('chat_at_top', @ChatTop, 'draw chat at top border', 'draw chat at top border');
1958 conRegVar('console_height', @ConsoleHeight, 0.0, 1.0, 'set console size', 'set console size');
1959 conRegVar('console_trans', @ConsoleTrans, 0.0, 1.0, 'set console transparency', 'set console transparency');
1960 conRegVar('console_step', @ConsoleStep, 0.0, 1.0, 'set console animation speed', 'set console animation speed');
1961 {$IFDEF ANDROID}
1962 ChatTop := True;
1963 ConsoleHeight := 0.35;
1964 {$ELSE}
1965 ChatTop := False;
1966 ConsoleHeight := 0.5;
1967 {$ENDIF}
1968 ConsoleTrans := 0.1;
1969 ConsoleStep := 0.07;
1970 end.