1 (* Copyright (C) Doom 2D: Forever Developers
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, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE ../shared/a_modes.inc}
24 procedure g_Console_Init ();
25 procedure g_Console_Update ();
26 procedure g_Console_Draw ();
27 procedure g_Console_Switch ();
28 procedure g_Console_Char (C
: AnsiChar);
29 procedure g_Console_Control (K
: Word);
30 procedure g_Console_Process (L
: AnsiString; quiet
: Boolean=false);
31 procedure g_Console_Add (L
: AnsiString; show
: Boolean=false);
32 procedure g_Console_Clear ();
33 function g_Console_CommandBlacklisted (C
: AnsiString): Boolean;
35 procedure conwriteln (const s
: AnsiString; show
: Boolean=false);
36 procedure conwritefln (const s
: AnsiString; args
: array of const; show
: Boolean=false);
38 // <0: no arg; 0/1: true/false
39 function conGetBoolArg (p
: SSArray
; idx
: Integer): Integer;
41 procedure g_Console_Chat_Switch (team
: Boolean=false);
43 procedure conRegVar (const conname
: AnsiString; pvar
: PBoolean; const ahelp
: AnsiString; const amsg
: AnsiString; acheat
: Boolean=false; ahidden
: Boolean=false); overload
;
44 procedure conRegVar (const conname
: AnsiString; pvar
: PSingle; amin
, amax
: Single; const ahelp
: AnsiString; const amsg
: AnsiString; acheat
: Boolean=false; ahidden
: Boolean=false); overload
;
45 procedure conRegVar (const conname
: AnsiString; pvar
: PInteger; const ahelp
: AnsiString; const amsg
: AnsiString; acheat
: Boolean=false; ahidden
: Boolean=false); overload
;
47 // poor man's floating literal parser; i'm sorry, but `StrToFloat()` sux cocks
48 function conParseFloat (var res
: Single; const s
: AnsiString): Boolean;
52 gConsoleShow
: Boolean = false; // True - êîíñîëü îòêðûòà èëè îòêðûâàåòñÿ
53 gChatShow
: Boolean = false;
54 gChatTeam
: Boolean = false;
55 gAllowConsoleMessages
: Boolean = true;
56 gChatEnter
: Boolean = true;
57 gJustChatted
: Boolean = false; // ÷òîáû àäìèí â èíòåðå ÷àòÿñü íå ïðîìàòûâàë ñòàòèñòèêó
63 g_textures
, g_main
, e_graphics
, e_input
, g_game
,
64 SysUtils
, g_basic
, g_options
, Math
, g_touch
,
65 g_menu
, g_language
, g_net
, g_netmsg
, e_log
, conbuf
;
71 TCmdProc
= procedure (p
: SSArray
);
72 TCmdProcEx
= procedure (me
: PCommand
; p
: SSArray
);
80 ptr
: Pointer; // various data
81 msg
: AnsiString; // message for var changes
95 MaxScriptRecursion
= 16;
97 DEBUG_STRING
= 'DEBUG MODE';
101 RecursionDepth
: Word = 0;
102 RecursionLimitHit
: Boolean = False;
104 Cons_Shown
: Boolean; // Ðèñîâàòü ëè êîíñîëü?
107 //ConsoleHistory: SSArray;
108 CommandHistory
: SSArray
;
110 commands
: Array of TCommand
= nil;
111 Aliases
: Array of TAlias
= nil;
113 conSkipLines
: Integer = 0;
114 MsgArray
: Array [0..4] of record
120 // poor man's floating literal parser; i'm sorry, but `StrToFloat()` sux cocks
121 function conParseFloat (var res
: Single; const s
: AnsiString): Boolean;
130 while (slen
> 0) and (s
[slen
] <= ' ') do Dec(slen
);
131 while (pos
<= slen
) and (s
[pos
] <= ' ') do Inc(pos
);
132 if (pos
> slen
) then exit
;
133 if (slen
-pos
= 1) and (s
[pos
] = '.') then exit
; // single dot
135 while (pos
<= slen
) do
137 if (s
[pos
] < '0') or (s
[pos
] > '9') then break
;
138 res
:= res
*10+Byte(s
[pos
])-48;
141 if (pos
<= slen
) then
144 if (s
[pos
] <> '.') then exit
;
146 while (pos
<= slen
) do
148 if (s
[pos
] < '0') or (s
[pos
] > '9') then break
;
150 res
+= frac
*(Byte(s
[pos
])-48);
154 if (pos
<= slen
) then exit
; // oops
159 // ////////////////////////////////////////////////////////////////////////// //
160 // <0: no arg; 0/1: true/false; 666: toggle
161 function conGetBoolArg (p
: SSArray
; idx
: Integer): Integer;
163 if (idx
< 0) or (idx
> High(p
)) then begin result
:= -1; exit
; end;
165 if (p
[idx
] = '1') or (CompareText(p
[idx
], 'on') = 0) or (CompareText(p
[idx
], 'true') = 0) or
166 (CompareText(p
[idx
], 'tan') = 0) or (CompareText(p
[idx
], 'yes') = 0) then result
:= 1
167 else if (CompareText(p
[idx
], 'toggle') = 0) or (CompareText(p
[idx
], 'switch') = 0) or
168 (CompareText(p
[idx
], 't') = 0) then result
:= 666;
172 procedure boolVarHandler (me
: PCommand
; p
: SSArray
);
173 procedure binaryFlag (var flag
: Boolean; msg
: AnsiString);
175 if (Length(p
) > 2) then
177 conwritefln('too many arguments to ''%s''', [p
[0]]);
181 case conGetBoolArg(p
, 1) of
183 0: if not me
.cheat
or conIsCheatsEnabled
then flag
:= false else begin conwriteln('not available'); exit
; end;
184 1: if not me
.cheat
or conIsCheatsEnabled
then flag
:= true else begin conwriteln('not available'); exit
; end;
185 666: if not me
.cheat
or conIsCheatsEnabled
then flag
:= not flag
else begin conwriteln('not available'); exit
; end;
187 if (Length(msg
) = 0) then msg
:= p
[0] else msg
+= ':';
188 if flag
then conwritefln('%s tan', [msg
]) else conwritefln('%s ona', [msg
]);
192 binaryFlag(PBoolean(me
.ptr
)^, me
.msg
);
196 procedure intVarHandler (me
: PCommand
; p
: SSArray
);
197 procedure binaryFlag (var flag
: Boolean; msg
: AnsiString);
199 if (Length(p
) > 2) then
201 conwritefln('too many arguments to ''%s''', [p
[0]]);
205 case conGetBoolArg(p
, 1) of
207 0: if not me
.cheat
or conIsCheatsEnabled
then flag
:= false else begin conwriteln('not available'); exit
; end;
208 1: if not me
.cheat
or conIsCheatsEnabled
then flag
:= true else begin conwriteln('not available'); exit
; end;
209 666: if not me
.cheat
or conIsCheatsEnabled
then flag
:= not flag
else begin conwriteln('not available'); exit
; end;
211 if (Length(msg
) = 0) then msg
:= p
[0] else msg
+= ':';
212 if flag
then conwritefln('%s tan', [msg
]) else conwritefln('%s ona', [msg
]);
216 if (Length(p
) <> 2) then
218 conwritefln('%s %d', [me
.cmd
, PInteger(me
.ptr
)^]);
223 PInteger(me
.ptr
)^ := StrToInt(p
[1]);
225 conwritefln('invalid integer value: "%s"', [p
[1]]);
231 procedure conRegVar (const conname
: AnsiString; pvar
: PBoolean; const ahelp
: AnsiString; const amsg
: AnsiString; acheat
: Boolean=false; ahidden
: Boolean=false); overload
;
236 f
:= Length(commands
);
237 SetLength(commands
, f
+1);
239 cp
.cmd
:= LowerCase(conname
);
241 cp
.procEx
:= boolVarHandler
;
243 cp
.hidden
:= ahidden
;
250 procedure conRegVar (const conname
: AnsiString; pvar
: PInteger; const ahelp
: AnsiString; const amsg
: AnsiString; acheat
: Boolean=false; ahidden
: Boolean=false); overload
;
255 f
:= Length(commands
);
256 SetLength(commands
, f
+1);
258 cp
.cmd
:= LowerCase(conname
);
260 cp
.procEx
:= intVarHandler
;
262 cp
.hidden
:= ahidden
;
269 // ////////////////////////////////////////////////////////////////////////// //
271 PVarSingle
= ^TVarSingle
;
274 min
, max
, def
: Single; // default will be starting value
278 procedure singleVarHandler (me
: PCommand
; p
: SSArray
);
284 if (Length(p
) > 2) then
286 conwritefln('too many arguments to ''%s''', [me
.cmd
]);
289 pv
:= PVarSingle(me
.ptr
);
290 if (Length(p
) = 2) then
292 if me
.cheat
and (not conIsCheatsEnabled
) then begin conwriteln('not available'); exit
; end;
293 if (CompareText(p
[1], 'default') = 0) or (CompareText(p
[1], 'def') = 0) or
294 (CompareText(p
[1], 'd') = 0) or (CompareText(p
[1], 'off') = 0) or
295 (CompareText(p
[1], 'ona') = 0) then
301 if not conParseFloat(nv
, p
[1]) then
303 conwritefln('%s: ''%s'' doesn''t look like a floating number', [me
.cmd
, p
[1]]);
306 if (nv
< pv
.min
) then nv
:= pv
.min
;
307 if (nv
> pv
.max
) then nv
:= pv
.max
;
312 if (Length(msg
) = 0) then msg
:= me
.cmd
else msg
+= ':';
313 conwritefln('%s %s', [msg
, pv
.val
^]);
317 procedure conRegVar (const conname
: AnsiString; pvar
: PSingle; amin
, amax
: Single; const ahelp
: AnsiString; const amsg
: AnsiString; acheat
: Boolean=false; ahidden
: Boolean=false); overload
;
323 GetMem(pv
, sizeof(TVarSingle
));
328 f
:= Length(commands
);
329 SetLength(commands
, f
+1);
331 cp
.cmd
:= LowerCase(conname
);
333 cp
.procEx
:= singleVarHandler
;
335 cp
.hidden
:= ahidden
;
342 // ////////////////////////////////////////////////////////////////////////// //
343 function GetStrACmd(var Str
: AnsiString): AnsiString;
348 for a
:= 1 to Length(Str
) do
349 if (a
= Length(Str
)) or (Str
[a
+1] = ';') then
351 Result
:= Copy(Str
, 1, a
);
358 function ParseAlias(Str
: AnsiString): SSArray
;
369 SetLength(Result
, Length(Result
)+1);
370 Result
[High(Result
)] := GetStrACmd(Str
);
374 procedure ConsoleCommands(p
: SSArray
);
380 cmd
:= LowerCase(p
[0]);
383 if cmd
= 'clear' then
385 //ConsoleHistory := nil;
389 for a
:= 0 to High(MsgArray
) do
397 if cmd
= 'clearhistory' then
398 CommandHistory
:= nil;
400 if cmd
= 'showhistory' then
401 if CommandHistory
<> nil then
404 for a
:= 0 to High(CommandHistory
) do
405 g_Console_Add(' '+CommandHistory
[a
]);
408 if cmd
= 'commands' then
411 g_Console_Add('commands list:');
412 for a
:= High(commands
) downto 0 do
414 if (Length(commands
[a
].help
) > 0) then
416 g_Console_Add(' '+commands
[a
].cmd
+' -- '+commands
[a
].help
);
420 g_Console_Add(' '+commands
[a
].cmd
);
426 g_Console_Add(TimeToStr(Now
), True);
429 g_Console_Add(DateToStr(Now
), True);
432 if Length(p
) > 1 then
434 if p
[1] = 'ololo' then
439 for a
:= 1 to High(p
) do
441 g_Console_Add(b_Text_Format(s
), True);
450 if ConsoleHistory <> nil then
452 if Length(P) > 1 then
455 s := GameDir+'/console.txt';
460 if IOResult <> 0 then
462 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [s]));
467 for a := 0 to High(ConsoleHistory) do
468 WriteLn(F, ConsoleHistory[a]);
471 g_Console_Add(Format(_lc[I_CONSOLE_DUMPED], [s]));
480 if Length(p
) > 1 then
482 s
:= GameDir
+'/'+p
[1];
487 if IOResult
<> 0 then
489 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_READ
], [s
]));
493 g_Console_Add(Format(_lc
[I_CONSOLE_EXEC
], [s
]));
498 if IOResult
<> 0 then
500 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_READ
], [s
]));
504 if Pos('#', s
) <> 1 then // script comment
506 // prevents endless loops
508 RecursionLimitHit
:= (RecursionDepth
> MaxScriptRecursion
) or RecursionLimitHit
;
509 if not RecursionLimitHit
then
510 g_Console_Process(s
, True);
514 if (RecursionDepth
= 0) and RecursionLimitHit
then
516 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_CALL
], [s
]));
517 RecursionLimitHit
:= False;
524 g_Console_Add('exec <script file>');
527 if (cmd
= 'ver') or (cmd
= 'version') then
529 conwriteln('Doom 2D: Forever v. ' + GAME_VERSION
);
530 conwritefln('Net protocol v. %d', [NET_PROTOCOL_VER
]);
531 conwritefln('Build date: %s at %s', [GAME_BUILDDATE
, GAME_BUILDTIME
]);
534 if cmd
= 'alias' then
536 // alias [alias_name] [commands]
537 if Length(p
) > 1 then
539 for a
:= 0 to High(Aliases
) do
540 if Aliases
[a
].name
= p
[1] then
542 if Length(p
) > 2 then
543 Aliases
[a
].commands
:= ParseAlias(p
[2])
545 for b
:= 0 to High(Aliases
[a
].commands
) do
546 g_Console_Add(Aliases
[a
].commands
[b
]);
549 SetLength(Aliases
, Length(Aliases
)+1);
551 Aliases
[a
].name
:= p
[1];
552 if Length(p
) > 2 then
553 Aliases
[a
].commands
:= ParseAlias(p
[2])
555 for b
:= 0 to High(Aliases
[a
].commands
) do
556 g_Console_Add(Aliases
[a
].commands
[b
]);
558 for a
:= 0 to High(Aliases
) do
559 if Aliases
[a
].commands
<> nil then
560 g_Console_Add(Aliases
[a
].name
);
566 if Length(p
) > 1 then
568 if Aliases
= nil then
570 for a
:= 0 to High(Aliases
) do
571 if Aliases
[a
].name
= p
[1] then
573 if Aliases
[a
].commands
<> nil then
575 // with this system proper endless loop detection seems either impossible
576 // or very dirty to implement, so let's have this instead
577 // prevents endless loops
578 for b
:= 0 to High(Aliases
[a
].commands
) do
581 RecursionLimitHit
:= (RecursionDepth
> MaxScriptRecursion
) or RecursionLimitHit
;
582 if not RecursionLimitHit
then
583 g_Console_Process(Aliases
[a
].commands
[b
], True);
586 if (RecursionDepth
= 0) and RecursionLimitHit
then
588 g_Console_Add(Format(_lc
[I_CONSOLE_ERROR_CALL
], [s
]));
589 RecursionLimitHit
:= False;
596 g_Console_Add('call <alias name>');
600 procedure WhitelistCommand(cmd
: AnsiString);
604 SetLength(Whitelist
, Length(Whitelist
)+1);
605 a
:= High(Whitelist
);
606 Whitelist
[a
] := LowerCase(cmd
);
609 procedure AddCommand(cmd
: AnsiString; proc
: TCmdProc
; ahelp
: AnsiString=''; ahidden
: Boolean=false; acheat
: Boolean=false);
614 SetLength(commands
, Length(commands
)+1);
617 cp
.cmd
:= LowerCase(cmd
);
621 cp
.hidden
:= ahidden
;
628 procedure segfault (p
: SSArray
);
636 procedure g_Console_Init();
640 g_Texture_CreateWAD(ID
, GameWAD
+':TEXTURES\CONSOLE');
641 Cons_Y
:= -(gScreenHeight
div 2);
642 gConsoleShow
:= False;
647 for a
:= 0 to High(MsgArray
) do
654 AddCommand('segfault', segfault
, 'make segfault');
656 AddCommand('clear', ConsoleCommands
, 'clear console');
657 AddCommand('clearhistory', ConsoleCommands
);
658 AddCommand('showhistory', ConsoleCommands
);
659 AddCommand('commands', ConsoleCommands
);
660 AddCommand('time', ConsoleCommands
);
661 AddCommand('date', ConsoleCommands
);
662 AddCommand('echo', ConsoleCommands
);
663 AddCommand('dump', ConsoleCommands
);
664 AddCommand('exec', ConsoleCommands
);
665 AddCommand('alias', ConsoleCommands
);
666 AddCommand('call', ConsoleCommands
);
667 AddCommand('ver', ConsoleCommands
);
668 AddCommand('version', ConsoleCommands
);
670 AddCommand('d_window', DebugCommands
);
671 AddCommand('d_sounds', DebugCommands
);
672 AddCommand('d_frames', DebugCommands
);
673 AddCommand('d_winmsg', DebugCommands
);
674 AddCommand('d_monoff', DebugCommands
);
675 AddCommand('d_botoff', DebugCommands
);
676 AddCommand('d_monster', DebugCommands
);
677 AddCommand('d_health', DebugCommands
);
678 AddCommand('d_player', DebugCommands
);
679 AddCommand('d_joy', DebugCommands
);
680 AddCommand('d_mem', DebugCommands
);
682 AddCommand('p1_name', GameCVars
);
683 AddCommand('p2_name', GameCVars
);
684 AddCommand('p1_color', GameCVars
);
685 AddCommand('p2_color', GameCVars
);
686 AddCommand('r_showfps', GameCVars
);
687 AddCommand('r_showtime', GameCVars
);
688 AddCommand('r_showscore', GameCVars
);
689 AddCommand('r_showlives', GameCVars
);
690 AddCommand('r_showstat', GameCVars
);
691 AddCommand('r_showkillmsg', GameCVars
);
692 AddCommand('r_showspect', GameCVars
);
693 AddCommand('r_showping', GameCVars
);
694 AddCommand('g_gamemode', GameCVars
);
695 AddCommand('g_friendlyfire', GameCVars
);
696 AddCommand('g_weaponstay', GameCVars
);
697 AddCommand('g_allow_exit', GameCVars
);
698 AddCommand('g_allow_monsters', GameCVars
);
699 AddCommand('g_bot_vsmonsters', GameCVars
);
700 AddCommand('g_bot_vsplayers', GameCVars
);
701 AddCommand('g_scorelimit', GameCVars
);
702 AddCommand('g_timelimit', GameCVars
);
703 AddCommand('g_maxlives', GameCVars
);
704 AddCommand('g_warmuptime', GameCVars
);
705 AddCommand('net_interp', GameCVars
);
706 AddCommand('net_forceplayerupdate', GameCVars
);
707 AddCommand('net_predictself', GameCVars
);
708 AddCommand('sv_name', GameCVars
);
709 AddCommand('sv_passwd', GameCVars
);
710 AddCommand('sv_maxplrs', GameCVars
);
711 AddCommand('sv_public', GameCVars
);
712 AddCommand('sv_intertime', GameCVars
);
714 AddCommand('quit', GameCommands
);
715 AddCommand('exit', GameCommands
);
716 AddCommand('pause', GameCommands
);
717 AddCommand('endgame', GameCommands
);
718 AddCommand('restart', GameCommands
);
719 AddCommand('addbot', GameCommands
);
720 AddCommand('bot_add', GameCommands
);
721 AddCommand('bot_addlist', GameCommands
);
722 AddCommand('bot_addred', GameCommands
);
723 AddCommand('bot_addblue', GameCommands
);
724 AddCommand('bot_removeall', GameCommands
);
725 AddCommand('chat', GameCommands
);
726 AddCommand('teamchat', GameCommands
);
727 AddCommand('game', GameCommands
);
728 AddCommand('host', GameCommands
);
729 AddCommand('map', GameCommands
);
730 AddCommand('nextmap', GameCommands
);
731 AddCommand('endmap', GameCommands
);
732 AddCommand('goodbye', GameCommands
);
733 AddCommand('suicide', GameCommands
);
734 AddCommand('spectate', GameCommands
);
735 AddCommand('ready', GameCommands
);
736 AddCommand('kick', GameCommands
);
737 AddCommand('kick_id', GameCommands
);
738 AddCommand('ban', GameCommands
);
739 AddCommand('permban', GameCommands
);
740 AddCommand('ban_id', GameCommands
);
741 AddCommand('permban_id', GameCommands
);
742 AddCommand('unban', GameCommands
);
743 AddCommand('connect', GameCommands
);
744 AddCommand('disconnect', GameCommands
);
745 AddCommand('reconnect', GameCommands
);
746 AddCommand('say', GameCommands
);
747 AddCommand('tell', GameCommands
);
748 AddCommand('overtime', GameCommands
);
749 AddCommand('rcon_password', GameCommands
);
750 AddCommand('rcon', GameCommands
);
751 AddCommand('callvote', GameCommands
);
752 AddCommand('vote', GameCommands
);
753 AddCommand('clientlist', GameCommands
);
754 AddCommand('event', GameCommands
);
756 AddCommand('god', GameCheats
);
757 AddCommand('notarget', GameCheats
);
758 AddCommand('give', GameCheats
); // "exit" too ;-)
759 AddCommand('open', GameCheats
);
760 AddCommand('fly', GameCheats
);
761 AddCommand('noclip', GameCheats
);
762 AddCommand('speedy', GameCheats
);
763 AddCommand('jumpy', GameCheats
);
764 AddCommand('noreload', GameCheats
);
765 AddCommand('aimline', GameCheats
);
766 AddCommand('automap', GameCheats
);
768 WhitelistCommand('say');
769 WhitelistCommand('tell');
770 WhitelistCommand('overtime');
771 WhitelistCommand('ready');
772 WhitelistCommand('map');
773 WhitelistCommand('nextmap');
774 WhitelistCommand('endmap');
775 WhitelistCommand('restart');
776 WhitelistCommand('kick');
777 WhitelistCommand('ban');
779 WhitelistCommand('addbot');
780 WhitelistCommand('bot_add');
781 WhitelistCommand('bot_addred');
782 WhitelistCommand('bot_addblue');
783 WhitelistCommand('bot_removeall');
785 WhitelistCommand('g_gamemode');
786 WhitelistCommand('g_friendlyfire');
787 WhitelistCommand('g_weaponstay');
788 WhitelistCommand('g_allow_exit');
789 WhitelistCommand('g_allow_monsters');
790 WhitelistCommand('g_scorelimit');
791 WhitelistCommand('g_timelimit');
793 g_Console_Add(Format(_lc
[I_CONSOLE_WELCOME
], [GAME_VERSION
]));
797 procedure g_Console_Update();
803 // Â ïðîöåññå îòêðûòèÿ:
804 if gConsoleShow
and (Cons_Y
< 0) then
806 Cons_Y
:= Cons_Y
+Step
;
809 // Â ïðîöåññå çàêðûòèÿ:
810 if (not gConsoleShow
) and
811 (Cons_Y
> -(gScreenHeight
div 2)) then
812 Cons_Y
:= Cons_Y
-Step
;
814 // Îêîí÷àòåëüíî îòêðûëàñü:
818 // Îêîí÷àòåëüíî çàêðûëàñü:
819 if Cons_Y
<= (-(gScreenHeight
div 2)) then
821 Cons_Y
:= -(gScreenHeight
div 2);
827 while a
<= High(MsgArray
) do
829 if MsgArray
[a
].Time
> 0 then
831 if MsgArray
[a
].Time
= 1 then
833 if a
< High(MsgArray
) then
835 for b
:= a
to High(MsgArray
)-1 do
836 MsgArray
[b
] := MsgArray
[b
+1];
838 MsgArray
[High(MsgArray
)].Time
:= 0;
844 Dec(MsgArray
[a
].Time
);
852 procedure drawConsoleText ();
854 CWidth
, CHeight
: Byte;
859 procedure putLine (sp
, ep
: LongWord);
868 cw
:= e_TextureFontCharWidth(cbufAt(p
), gStdFont
);
869 if wdt
+cw
> gScreenWidth
-8 then break
;
870 //e_TextureFontPrintChar(X, Y: Integer; Ch: Char; FontID: DWORD; Shadow: Boolean = False);
874 if p
<> ep
then putLine(p
, ep
); // do rest of the line first
875 // now print our part
883 cw
:= e_TextureFontCharWidth(cbufAt(p
), gStdFont
);
884 e_TextureFontPrintCharEx(wdt
, ty
, cbufAt(p
), gStdFont
);
897 e_TextureFontGetSize(gStdFont
, CWidth
, CHeight
);
898 ty
:= (gScreenHeight
div 2)-4-2*CHeight
-Abs(Cons_Y
);
899 skip
:= conSkipLines
;
900 cbufLastLine(sp
, ep
);
903 if ty
+CHeight
<= 0 then break
;
904 until not cbufLineUp(sp
, ep
);
907 procedure g_Console_Draw();
909 CWidth
, CHeight
: Byte;
913 e_TextureFontGetSize(gStdFont
, CWidth
, CHeight
);
915 for a
:= 0 to High(MsgArray
) do
916 if MsgArray
[a
].Time
> 0 then
917 e_TextureFontPrintFmt(0, CHeight
*a
, MsgArray
[a
].Msg
,
920 if not Cons_Shown
then
926 e_TextureFontPrintEx(0, gScreenHeight
- CHeight
- 1, 'say team> ' + Line
,
927 gStdFont
, 255, 255, 255, 1, True);
928 e_TextureFontPrintEx((CPos
+ 9)*CWidth
, gScreenHeight
- CHeight
- 1, '_',
929 gStdFont
, 255, 255, 255, 1, True);
933 e_TextureFontPrintEx(0, gScreenHeight
- CHeight
- 1, 'say> ' + Line
,
934 gStdFont
, 255, 255, 255, 1, True);
935 e_TextureFontPrintEx((CPos
+ 4)*CWidth
, gScreenHeight
- CHeight
- 1, '_',
936 gStdFont
, 255, 255, 255, 1, True);
944 e_CharFont_GetSize(gMenuFont
, DEBUG_STRING
, mfW
, mfH
);
945 a
:= (gScreenWidth
- 2*mfW
) div 2;
946 b
:= Cons_Y
+ ((gScreenHeight
div 2) - 2*mfH
) div 2;
947 e_CharFont_PrintEx(gMenuFont
, a
div 2, b
div 2, DEBUG_STRING
,
948 _RGB(128, 0, 0), 2.0);
951 e_DrawSize(ID
, 0, Cons_Y
, Alpha
, False, False, gScreenWidth
, gScreenHeight
div 2);
952 e_TextureFontPrint(0, Cons_Y
+(gScreenHeight
div 2)-CHeight
-4, '> '+Line
, gStdFont
);
956 if ConsoleHistory <> nil then
960 if Length(ConsoleHistory) > ((gScreenHeight div 2) div CHeight)-1 then
961 b := Length(ConsoleHistory)-((gScreenHeight div 2) div CHeight)+1;
963 b := Max(b-Offset, 0);
964 d := Max(High(ConsoleHistory)-Offset, 0);
967 for a := d downto b do
969 e_TextureFontPrintFmt(0, (gScreenHeight div 2)-4-c*CHeight-Abs(Cons_Y), ConsoleHistory[a],
976 e_TextureFontPrint((CPos
+1)*CWidth
, Cons_Y
+(gScreenHeight
div 2)-21, '_', gStdFont
);
979 procedure g_Console_Switch();
981 if gChatShow
then Exit
;
982 gConsoleShow
:= not gConsoleShow
;
984 g_Touch_ShowKeyboard(gConsoleShow
or gChatShow
);
987 procedure g_Console_Chat_Switch(Team
: Boolean = False);
989 if gConsoleShow
then Exit
;
990 if not g_Game_IsNet
then Exit
;
991 gChatShow
:= not gChatShow
;
997 g_Touch_ShowKeyboard(gConsoleShow
or gChatShow
);
1000 procedure g_Console_Char(C
: AnsiChar);
1002 if gChatShow
and (not gChatEnter
) then
1004 Insert(C
, Line
, CPos
);
1010 tcomplist
: array of AnsiString = nil;
1011 tcompidx
: array of Integer = nil;
1013 procedure Complete ();
1017 ll
, lpfx
, cmd
: AnsiString;
1019 if (Length(Line
) = 0) then
1022 for i
:= 0 to High(commands
) do
1024 // hidden commands are hidden when cheats aren't enabled
1025 if commands
[i
].hidden
and not conIsCheatsEnabled
then continue
;
1026 if (Length(commands
[i
].help
) > 0) then
1028 g_Console_Add(' '+commands
[i
].cmd
+' -- '+commands
[i
].help
);
1032 g_Console_Add(' '+commands
[i
].cmd
);
1038 ll
:= LowerCase(Line
);
1041 if (Length(ll
) > 1) and (ll
[Length(ll
)] = ' ') then
1043 ll
:= Copy(ll
, 0, Length(ll
)-1);
1044 for i
:= 0 to High(commands
) do
1046 // hidden commands are hidden when cheats aren't enabled
1047 if commands
[i
].hidden
and not conIsCheatsEnabled
then continue
;
1048 if (commands
[i
].cmd
= ll
) then
1050 if (Length(commands
[i
].help
) > 0) then
1052 g_Console_Add(' '+commands
[i
].cmd
+' -- '+commands
[i
].help
);
1059 // build completion list
1061 for i
:= 0 to High(commands
) do
1063 // hidden commands are hidden when cheats aren't enabled
1064 if commands
[i
].hidden
and not conIsCheatsEnabled
then continue
;
1065 cmd
:= commands
[i
].cmd
;
1066 if (Length(cmd
) >= Length(ll
)) and (ll
= Copy(cmd
, 0, Length(ll
))) then
1068 if (tused
= Length(tcomplist
)) then
1070 SetLength(tcomplist
, Length(tcomplist
)+128);
1071 SetLength(tcompidx
, Length(tcompidx
)+128);
1073 tcomplist
[tused
] := cmd
;
1074 tcompidx
[tused
] := i
;
1076 if (Length(cmd
) > Length(lpfx
)) then lpfx
:= cmd
;
1080 // get longest prefix
1081 for i
:= 0 to tused
-1 do
1083 cmd
:= tcomplist
[i
];
1084 for c
:= 1 to Length(lpfx
) do
1086 if (c
> Length(cmd
)) then break
;
1087 if (cmd
[c
] <> lpfx
[c
]) then begin lpfx
:= Copy(lpfx
, 0, c
-1); break
; end;
1091 if (tused
= 0) then exit
;
1095 Line
:= tcomplist
[0]+' ';
1096 CPos
:= Length(Line
)+1;
1100 // has longest prefix?
1101 if (Length(lpfx
) > Length(ll
)) then
1104 CPos
:= Length(Line
)+1;
1109 for i
:= 0 to tused
-1 do
1111 if (Length(commands
[tcompidx
[i
]].help
) > 0) then
1113 g_Console_Add(' '+tcomplist
[i
]+' -- '+commands
[tcompidx
[i
]].help
);
1117 g_Console_Add(' '+tcomplist
[i
]);
1125 procedure g_Console_Control(K
: Word);
1129 if (Length(Line
) > 0) and (CPos
> 1) then
1131 Delete(Line
, CPos
-1, 1);
1135 if (Length(Line
) > 0) and (CPos
<= Length(Line
)) then
1136 Delete(Line
, CPos
, 1);
1137 IK_LEFT
, IK_KPLEFT
, VK_LEFT
:
1140 IK_RIGHT
, IK_KPRIGHT
, VK_RIGHT
:
1141 if CPos
<= Length(Line
) then
1143 IK_RETURN
, IK_KPRETURN
, VK_OPEN
, VK_FIRE
:
1146 g_Console_Process(Line
)
1150 if (Length(Line
) > 0) and g_Game_IsNet
then
1154 if g_Game_IsClient
then
1155 MC_SEND_Chat(b_Text_Format(Line
), NET_CHAT_TEAM
)
1157 MH_SEND_Chat('[' + gPlayer1Settings
.name
+ ']: ' + b_Text_Format(Line
),
1158 NET_CHAT_TEAM
, gPlayer1Settings
.Team
);
1162 if g_Game_IsClient
then
1163 MC_SEND_Chat(b_Text_Format(Line
), NET_CHAT_PLAYER
)
1165 MH_SEND_Chat('[' + gPlayer1Settings
.name
+ ']: ' + b_Text_Format(Line
),
1173 gJustChatted
:= True;
1174 g_Touch_ShowKeyboard(gConsoleShow
or gChatShow
);
1178 if not gChatShow
then
1180 IK_DOWN
, IK_KPDOWN
, VK_DOWN
:
1181 if not gChatShow
then
1182 if (CommandHistory
<> nil) and
1183 (CmdIndex
< Length(CommandHistory
)) then
1185 if CmdIndex
< Length(CommandHistory
)-1 then
1186 CmdIndex
:= CmdIndex
+ 1;
1187 Line
:= CommandHistory
[CmdIndex
];
1188 CPos
:= Length(Line
) + 1;
1190 IK_UP
, IK_KPUP
, VK_UP
:
1191 if not gChatShow
then
1192 if (CommandHistory
<> nil) and
1193 (CmdIndex
<= Length(CommandHistory
)) then
1195 if CmdIndex
> 0 then
1196 CmdIndex
:= CmdIndex
- 1;
1197 Line
:= CommandHistory
[CmdIndex
];
1198 Cpos
:= Length(Line
) + 1;
1200 IK_PAGEUP
, IK_KPPAGEUP
, VK_PREV
: // PgUp
1201 if not gChatShow
then Inc(conSkipLines
);
1202 IK_PAGEDN
, IK_KPPAGEDN
, VK_NEXT
: // PgDown
1203 if not gChatShow
and (conSkipLines
> 0) then Dec(conSkipLines
);
1207 CPos
:= Length(Line
) + 1;
1211 function GetStr(var Str
: AnsiString): AnsiString;
1216 if Str
[1] = '"' then
1218 for b
:= 1 to Length(Str
) do
1219 if (b
= Length(Str
)) or (Str
[b
+1] = '"') then
1221 Result
:= Copy(Str
, 2, b
-1);
1222 Delete(Str
, 1, b
+1);
1228 for a
:= 1 to Length(Str
) do
1229 if (a
= Length(Str
)) or (Str
[a
+1] = ' ') then
1231 Result
:= Copy(Str
, 1, a
);
1232 Delete(Str
, 1, a
+1);
1238 function ParseString(Str
: AnsiString): SSArray
;
1249 SetLength(Result
, Length(Result
)+1);
1250 Result
[High(Result
)] := GetStr(Str
);
1254 procedure g_Console_Add (L
: AnsiString; show
: Boolean=false);
1256 procedure conmsg (s
: AnsiString);
1260 if length(s
) = 0 then exit
;
1261 for a
:= 0 to High(MsgArray
) do
1273 for a
:= 0 to High(MsgArray
)-1 do MsgArray
[a
] := MsgArray
[a
+1];
1274 with MsgArray
[High(MsgArray
)] do
1284 // put it to console
1286 if (length(L
) = 0) or ((L
[length(L
)] <> #10) and (L
[length(L
)] <> #13)) then cbufPut(#10);
1288 // now show 'em out of console too
1289 show
:= show
and gAllowConsoleMessages
;
1290 if show
and gShowMessages
then
1292 // Âûâîä ñòðîê ñ ïåðåíîñàìè ïî î÷åðåäè
1293 while length(L
) > 0 do
1296 if f
<= 0 then f
:= length(L
)+1;
1297 conmsg(Copy(L
, 1, f
-1));
1302 //SetLength(ConsoleHistory, Length(ConsoleHistory)+1);
1303 //ConsoleHistory[High(ConsoleHistory)] := L;
1307 e_WriteLog('CON: ' + L, MSG_NOTIFY);
1314 consolewriterLastWasEOL
: Boolean = false;
1316 procedure consolewriter (constref buf
; len
: SizeUInt
);
1320 if (len
< 1) then exit
;
1322 consolewriterLastWasEOL
:= (b
[len
-1] = 13) or (b
[len
-1] = 10);
1325 if (b
[0] <> 13) and (b
[0] <> 10) then
1327 cbufPut(AnsiChar(b
[0]));
1331 if (len
> 1) and (b
[0] = 13) then begin len
-= 1; b
+= 1; end;
1340 // returns formatted string if `writerCB` is `nil`, empty string otherwise
1341 //function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1342 //TFormatStrFCallback = procedure (constref buf; len: SizeUInt);
1343 procedure conwriteln (const s
: AnsiString; show
: Boolean=false);
1345 g_Console_Add(s
, show
);
1349 procedure conwritefln (const s
: AnsiString; args
: array of const; show
: Boolean=false);
1353 g_Console_Add(formatstrf(s
, args
), true);
1357 consolewriterLastWasEOL
:= false;
1358 formatstrf(s
, args
, consolewriter
);
1359 if not consolewriterLastWasEOL
then cbufPut(#10);
1364 procedure g_Console_Clear();
1366 //ConsoleHistory := nil;
1371 procedure AddToHistory(L
: AnsiString);
1375 len
:= Length(CommandHistory
);
1378 (LowerCase(CommandHistory
[len
-1]) <> LowerCase(L
)) then
1380 SetLength(CommandHistory
, len
+1);
1381 CommandHistory
[len
] := L
;
1384 CmdIndex
:= Length(CommandHistory
);
1387 function g_Console_CommandBlacklisted(C
: AnsiString): Boolean;
1396 if Trim(C
) = '' then
1399 Arr
:= ParseString(C
);
1403 for i
:= 0 to High(Whitelist
) do
1404 if Whitelist
[i
] = LowerCase(Arr
[0]) then
1408 procedure g_Console_Process(L
: AnsiString; quiet
: Boolean = False);
1415 if Trim(L
) = '' then
1418 conSkipLines
:= 0; // "unscroll"
1420 if L
= 'goobers' then
1425 g_Console_Add('Your memory serves you well.');
1431 g_Console_Add('> '+L
);
1436 Arr
:= ParseString(L
);
1440 if commands
= nil then
1446 for i
:= 0 to High(commands
) do
1448 if commands
[i
].cmd
= LowerCase(Arr
[0]) then
1450 if assigned(commands
[i
].procEx
) then
1452 commands
[i
].procEx(@commands
[i
], Arr
);
1455 if assigned(commands
[i
].proc
) then
1457 commands
[i
].proc(Arr
);
1463 g_Console_Add(Format(_lc
[I_CONSOLE_UNKNOWN
], [Arr
[0]]));