DEADSOFTWARE

net: game: other: hash database and resource downloader converted to new dirsys
[d2df-sdl.git] / src / game / g_main.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_main;
18 interface
20 uses Utils;
22 procedure Main ();
23 procedure Init ();
24 procedure Release ();
25 procedure Update ();
26 procedure Draw ();
27 procedure KeyPress (K: Word);
28 procedure CharPress (C: AnsiChar);
30 var
31 {--- TO REMOVE ---}
32 //GameDir: string;
33 {-----------------}
35 {--- Read-only dirs ---}
36 GameWAD: string;
37 DataDirs: SSArray;
38 ModelDirs: SSArray;
39 MegawadDirs: SSArray;
40 MapDirs: SSArray;
41 WadDirs: SSArray;
42 AllMapDirs: SSArray; // Maps + Megawads
44 {--- Read-Write dirs ---}
45 LogFileName: string;
46 LogDirs: SSArray;
47 SaveDirs: SSArray;
48 CacheDirs: SSArray;
49 ConfigDirs: SSArray;
50 ScreenshotDirs: SSArray;
51 MapDownloadDirs: SSArray;
52 WadDownloadDirs: SSArray;
54 implementation
56 uses
57 {$INCLUDE ../nogl/noGLuses.inc}
58 {$IFDEF ENABLE_HOLMES}
59 g_holmes, sdlcarcass, fui_ctls, fui_wadread, fui_style, fui_gfx_gl,
60 {$ENDIF}
61 wadreader, e_log, g_window,
62 e_graphics, e_input, g_game, g_console, g_gui,
63 e_sound, g_options, g_sound, g_player, g_basic,
64 g_weapons, SysUtils, g_triggers, MAPDEF, g_map, e_res,
65 g_menu, g_language, g_net, g_touch, g_system, g_res_downloader,
66 conbuf, envvars,
67 xparser;
70 var
71 charbuff: packed array [0..15] of AnsiChar;
73 procedure InitPath;
74 var i: Integer; rwdir, rodir: AnsiString;
76 procedure AddPath (var arr: SSArray; str: AnsiString);
77 begin
78 SetLength(arr, Length(arr) + 1);
79 arr[High(arr)] := ExpandFileName(str)
80 end;
82 procedure AddDef (var arr: SSArray; str: AnsiString);
83 begin
84 if arr = nil then
85 AddPath(arr, str)
86 end;
88 begin
89 //GetDir(0, GameDir);
91 i := 1;
92 while i < ParamCount do
93 begin
94 case ParamStr(i) of
95 '--rw-dir':
96 begin
97 Inc(i);
98 rwdir := ParamStr(i);
99 (* RW *)
100 AddPath(LogDirs, e_CatPath(rwdir, ''));
101 AddPath(SaveDirs, e_CatPath(rwdir, 'data'));
102 AddPath(CacheDirs, e_CatPath(rwdir, 'data/cache'));
103 AddPath(ConfigDirs, e_CatPath(rwdir, ''));
104 AddPath(MapDownloadDirs, e_CatPath(rwdir, 'maps/downloads'));
105 AddPath(WadDownloadDirs, e_CatPath(rwdir, 'wads/downloads'));
106 AddPath(ScreenshotDirs, e_CatPath(rwdir, 'screenshots'));
107 (* RO *)
108 AddPath(DataDirs, e_CatPath(rwdir, 'data'));
109 AddPath(ModelDirs, e_CatPath(rwdir, 'data/models'));
110 AddPath(MegawadDirs, e_CatPath(rwdir, 'maps/megawads'));
111 AddPath(MapDirs, e_CatPath(rwdir, 'maps'));
112 AddPath(WadDirs, e_CatPath(rwdir, 'wads'));
113 end;
114 '--ro-dir':
115 begin
116 Inc(i);
117 rodir := ParamStr(i);
118 (* RO *)
119 AddPath(DataDirs, e_CatPath(rodir, 'data'));
120 AddPath(ModelDirs, e_CatPath(rodir, 'data/models'));
121 AddPath(MegawadDirs, e_CatPath(rodir, 'maps/megawads'));
122 AddPath(MapDirs, e_CatPath(rodir, 'maps'));
123 AddPath(WadDirs, e_CatPath(rodir, 'wads'));
124 end;
125 end;
126 Inc(i)
127 end;
129 (* RO *)
130 AddDef(DataDirs, 'data');
131 AddDef(ModelDirs, 'data/models');
132 AddDef(MegawadDirs, 'maps/megawads');
133 AddDef(MapDirs, 'maps');
134 AddDef(WadDirs, 'wads');
135 (* RW *)
136 AddDef(LogDirs, '.');
137 AddDef(SaveDirs, 'data');
138 AddDef(CacheDirs, 'data/cache');
139 AddDef(ConfigDirs, '.');
140 AddDef(MapDownloadDirs, 'maps/downloads');
141 AddDef(WadDownloadDirs, 'wads/downloads');
142 AddDef(ScreenshotDirs, 'screenshots');
144 for i := 0 to High(MapDirs) do
145 AddPath(AllMapDirs, MapDirs[i]);
146 for i := 0 to High(MegawadDirs) do
147 AddPath(AllMapDirs, MegawadDirs[i]);
149 if LogFileName = '' then
150 begin
151 rwdir := e_GetWriteableDir(LogDirs, false);
152 if rwdir <> '' then
153 begin
154 {$IFDEF HEADLESS}
155 LogFileName := e_CatPath(rwdir, 'Doom2DF_H.log');
156 {$ELSE}
157 LogFileName := e_Catpath(rwdir, 'Doom2DF.log');
158 {$ENDIF}
159 end
160 end
161 end;
163 procedure Main();
164 {$IFDEF ENABLE_HOLMES}
165 var flexloaded: Boolean;
166 {$ENDIF}
167 var s: AnsiString;
168 begin
169 InitPath;
170 if LogFileName <> '' then
171 e_InitLog(LogFileName, TWriteMode.WM_NEWFILE);
172 e_InitWritelnDriver();
174 GameWAD := e_FindWad(DataDirs, 'GAME');
175 assert(GameWad <> '', 'GAME.WAD not installed?');
178 // e_InitLog(GameDir + '/' + LogFileName, TWriteMode.WM_NEWFILE);
180 e_WriteLog(
181 'Doom 2D: Forever version ' + GAME_VERSION +
182 ' proto ' + IntToStr(NET_PROTOCOL_VER),
183 TMsgType.Notify
184 );
185 e_WriteLog(
186 'Build date: ' + GAME_BUILDDATE + ' ' + GAME_BUILDTIME,
187 TMsgType.Notify
188 );
190 {$IFDEF HEADLESS}
191 conbufDumpToStdOut := true;
192 {$ENDIF}
193 e_WriteToStdOut := False; //{$IFDEF HEADLESS}True;{$ELSE}False;{$ENDIF}
195 e_InitInput;
197 sys_Init;
199 s := CONFIG_FILENAME;
200 if e_FindResource(ConfigDirs, s) = true then
201 begin
202 g_Options_Read(s)
203 end
204 else
205 begin
206 g_Options_SetDefault;
207 g_Options_SetDefaultVideo
208 end;
209 if sys_SetDisplayMode(gScreenWidth, gScreenHeight, gBPP, gFullScreen) = False then
210 raise Exception.Create('Failed to set videomode on startup.');
212 g_Console_SysInit;
213 e_WriteLog(gLanguage, TMsgType.Notify);
214 g_Language_Set(gLanguage);
216 {$IF not DEFINED(HEADLESS) and DEFINED(ENABLE_HOLMES)}
217 flexloaded := true;
218 if not fuiAddWad('flexui.wad') then
219 begin
220 if not fuiAddWad('./data/flexui.wad') then fuiAddWad('./flexui.wad');
221 end;
222 try
223 fuiGfxLoadFont('win8', 'flexui/fonts/win8.fuifont');
224 fuiGfxLoadFont('win14', 'flexui/fonts/win14.fuifont');
225 fuiGfxLoadFont('win16', 'flexui/fonts/win16.fuifont');
226 fuiGfxLoadFont('dos8', 'flexui/fonts/dos8.fuifont');
227 fuiGfxLoadFont('msx6', 'flexui/fonts/msx6.fuifont');
228 except on e: Exception do
229 begin
230 writeln('ERROR loading FlexUI fonts');
231 flexloaded := false;
232 //raise;
233 end;
234 else
235 begin
236 flexloaded := false;
237 //raise;
238 end;
239 end;
240 if (flexloaded) then
241 begin
242 try
243 e_LogWriteln('FlexUI: loading stylesheet...');
244 uiLoadStyles('flexui/widgets.wgs');
245 except on e: TParserException do
246 begin
247 writeln('ERROR at (', e.tokLine, ',', e.tokCol, '): ', e.message);
248 //raise;
249 flexloaded := false;
250 end;
251 else
252 begin
253 //raise;
254 flexloaded := false;
255 end;
256 end;
257 end;
258 g_holmes_imfunctional := not flexloaded;
260 if (not g_holmes_imfunctional) then
261 begin
262 uiInitialize();
263 uiContext.font := 'win14';
264 end;
266 if assigned(oglInitCB) then oglInitCB;
267 {$ENDIF}
269 //g_Res_CreateDatabases(true); // it will be done before connecting to the server for the first time
271 e_WriteLog('Entering SDLMain', TMsgType.Notify);
273 {$WARNINGS OFF}
274 SDLMain();
275 {$WARNINGS ON}
277 {$IFDEF ENABLE_HOLMES}
278 if assigned(oglDeinitCB) then oglDeinitCB;
279 {$ENDIF}
281 sys_Final;
282 end;
284 procedure Init();
285 var
286 NoSound: Boolean;
287 begin
288 Randomize;
290 {$IFDEF HEADLESS}
291 {$IFDEF USE_SDLMIXER}
292 NoSound := False; // hope env has set SDL_AUDIODRIVER to dummy
293 {$ELSE}
294 NoSound := True; // FMOD backend will sort it out
295 {$ENDIF}
296 {$ELSE}
297 NoSound := False;
298 {$ENDIF}
300 g_Touch_Init;
302 (*
303 if (e_JoysticksAvailable > 0) then
304 e_WriteLog('Input: Joysticks available.', TMsgType.Notify)
305 else
306 e_WriteLog('Input: No Joysticks.', TMsgType.Notify);
307 *)
309 if (not gNoSound) then
310 begin
311 e_WriteLog('Initializing sound system', TMsgType.Notify);
312 e_InitSoundSystem(NoSound);
313 end;
315 e_WriteLog('Init game', TMsgType.Notify);
316 g_Game_Init();
318 FillChar(charbuff, sizeof(charbuff), ' ');
319 end;
322 procedure Release();
323 begin
324 e_WriteLog('Releasing engine', TMsgType.Notify);
325 e_ReleaseEngine();
327 e_WriteLog('Releasing input', TMsgType.Notify);
328 e_ReleaseInput();
330 if not gNoSound then
331 begin
332 e_WriteLog('Releasing sound', TMsgType.Notify);
333 e_ReleaseSoundSystem();
334 end;
335 end;
338 procedure Update ();
339 begin
340 g_Game_Update();
341 end;
344 procedure Draw ();
345 begin
346 g_Game_Draw();
347 end;
350 function Translit (const S: AnsiString): AnsiString;
351 var
352 i: Integer;
353 begin
354 Result := S;
355 for i := 1 to Length(Result) do
356 begin
357 case Result[i] of
358 'É': Result[i] := 'Q';
359 'Ö': Result[i] := 'W';
360 'Ó': Result[i] := 'E';
361 'Ê': Result[i] := 'R';
362 'Å': Result[i] := 'T';
363 'Í': Result[i] := 'Y';
364 'Ã': Result[i] := 'U';
365 'Ø': Result[i] := 'I';
366 'Ù': Result[i] := 'O';
367 'Ç': Result[i] := 'P';
368 'Õ': Result[i] := '['; //Chr(219);
369 'Ú': Result[i] := ']'; //Chr(221);
370 'Ô': Result[i] := 'A';
371 'Û': Result[i] := 'S';
372 'Â': Result[i] := 'D';
373 'À': Result[i] := 'F';
374 'Ï': Result[i] := 'G';
375 'Ð': Result[i] := 'H';
376 'Î': Result[i] := 'J';
377 'Ë': Result[i] := 'K';
378 'Ä': Result[i] := 'L';
379 'Æ': Result[i] := ';'; //Chr(186);
380 'Ý': Result[i] := #39; //Chr(222);
381 'ß': Result[i] := 'Z';
382 '×': Result[i] := 'X';
383 'Ñ': Result[i] := 'C';
384 'Ì': Result[i] := 'V';
385 'È': Result[i] := 'B';
386 'Ò': Result[i] := 'N';
387 'Ü': Result[i] := 'M';
388 'Á': Result[i] := ','; //Chr(188);
389 'Þ': Result[i] := '.'; //Chr(190);
390 end;
391 end;
392 end;
395 function CheckCheat (ct: TStrings_Locale; eofs: Integer=0): Boolean;
396 var
397 ls1, ls2: string;
398 begin
399 ls1 := CheatEng[ct];
400 ls2 := Translit(CheatRus[ct]);
401 if length(ls1) = 0 then ls1 := '~';
402 if length(ls2) = 0 then ls2 := '~';
403 result :=
404 (Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1)) = ls1) or
405 (Translit(Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1))) = ls1) or
406 (Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2)) = ls2) or
407 (Translit(Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2))) = ls2);
409 if ct = I_GAME_CHEAT_JETPACK then
410 begin
411 e_WriteLog('ls1: ['+ls1+']', MSG_NOTIFY);
412 e_WriteLog('ls2: ['+ls2+']', MSG_NOTIFY);
413 e_WriteLog('bf0: ['+Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1))+']', MSG_NOTIFY);
414 e_WriteLog('bf1: ['+Translit(Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1)))+']', MSG_NOTIFY);
415 e_WriteLog('bf2: ['+Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2))+']', MSG_NOTIFY);
416 e_WriteLog('bf3: ['+Translit(Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2)))+']', MSG_NOTIFY);
417 end;
419 end;
422 procedure Cheat ();
423 const
424 CHEAT_DAMAGE = 500;
425 label
426 Cheated;
427 var
428 s, s2: string;
429 c: ShortString;
430 a: Integer;
431 begin
433 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
434 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode))
435 or g_Game_IsNet then Exit;
437 if not gGameOn then exit;
438 if not conIsCheatsEnabled then exit;
440 s := 'SOUND_GAME_RADIO';
442 //
443 if CheckCheat(I_GAME_CHEAT_GODMODE) then
444 begin
445 if gPlayer1 <> nil then gPlayer1.GodMode := not gPlayer1.GodMode;
446 if gPlayer2 <> nil then gPlayer2.GodMode := not gPlayer2.GodMode;
447 goto Cheated;
448 end;
449 // RAMBO
450 if CheckCheat(I_GAME_CHEAT_WEAPONS) then
451 begin
452 if gPlayer1 <> nil then gPlayer1.AllRulez(False);
453 if gPlayer2 <> nil then gPlayer2.AllRulez(False);
454 goto Cheated;
455 end;
456 // TANK
457 if CheckCheat(I_GAME_CHEAT_HEALTH) then
458 begin
459 if gPlayer1 <> nil then gPlayer1.AllRulez(True);
460 if gPlayer2 <> nil then gPlayer2.AllRulez(True);
461 goto Cheated;
462 end;
463 // IDDQD
464 if CheckCheat(I_GAME_CHEAT_DEATH) then
465 begin
466 if gPlayer1 <> nil then gPlayer1.Damage(CHEAT_DAMAGE, 0, 0, 0, HIT_TRAP);
467 if gPlayer2 <> nil then gPlayer2.Damage(CHEAT_DAMAGE, 0, 0, 0, HIT_TRAP);
468 s := 'SOUND_MONSTER_HAHA';
469 goto Cheated;
470 end;
471 //
472 if CheckCheat(I_GAME_CHEAT_DOORS) then
473 begin
474 g_Triggers_OpenAll();
475 goto Cheated;
476 end;
477 // GOODBYE
478 if CheckCheat(I_GAME_CHEAT_NEXTMAP) then
479 begin
480 if gTriggers <> nil then
481 for a := 0 to High(gTriggers) do
482 if gTriggers[a].TriggerType = TRIGGER_EXIT then
483 begin
484 gExitByTrigger := True;
485 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
486 g_Game_ExitLevel(gTriggers[a].tgcMap);
487 Break;
488 end;
489 goto Cheated;
490 end;
491 //
492 s2 := Copy(charbuff, 15, 2);
493 if CheckCheat(I_GAME_CHEAT_CHANGEMAP, 2) and (s2[1] >= '0') and (s2[1] <= '9') and (s2[2] >= '0') and (s2[2] <= '9') then
494 begin
495 if g_Map_Exist(gGameSettings.WAD + ':\MAP' + s2) then
496 begin
497 c := 'MAP' + s2;
498 g_Game_ExitLevel(c);
499 end;
500 goto Cheated;
501 end;
502 //
503 if CheckCheat(I_GAME_CHEAT_FLY) then
504 begin
505 gFly := not gFly;
506 goto Cheated;
507 end;
508 // BULLFROG
509 if CheckCheat(I_GAME_CHEAT_JUMPS) then
510 begin
511 VEL_JUMP := 30-VEL_JUMP;
512 goto Cheated;
513 end;
514 // FORMULA1
515 if CheckCheat(I_GAME_CHEAT_SPEED) then
516 begin
517 MAX_RUNVEL := 32-MAX_RUNVEL;
518 goto Cheated;
519 end;
520 // CONDOM
521 if CheckCheat(I_GAME_CHEAT_SUIT) then
522 begin
523 if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_SUIT);
524 if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_SUIT);
525 goto Cheated;
526 end;
527 //
528 if CheckCheat(I_GAME_CHEAT_AIR) then
529 begin
530 if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_OXYGEN);
531 if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_OXYGEN);
532 goto Cheated;
533 end;
534 // PURELOVE
535 if CheckCheat(I_GAME_CHEAT_BERSERK) then
536 begin
537 if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_MEDKIT_BLACK);
538 if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_MEDKIT_BLACK);
539 goto Cheated;
540 end;
541 //
542 if CheckCheat(I_GAME_CHEAT_JETPACK) then
543 begin
544 if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_JETPACK);
545 if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_JETPACK);
546 goto Cheated;
547 end;
548 // CASPER
549 if CheckCheat(I_GAME_CHEAT_NOCLIP) then
550 begin
551 if gPlayer1 <> nil then gPlayer1.SwitchNoClip;
552 if gPlayer2 <> nil then gPlayer2.SwitchNoClip;
553 goto Cheated;
554 end;
555 //
556 if CheckCheat(I_GAME_CHEAT_NOTARGET) then
557 begin
558 if gPlayer1 <> nil then gPlayer1.NoTarget := not gPlayer1.NoTarget;
559 if gPlayer2 <> nil then gPlayer2.NoTarget := not gPlayer2.NoTarget;
560 goto Cheated;
561 end;
562 // INFERNO
563 if CheckCheat(I_GAME_CHEAT_NORELOAD) then
564 begin
565 if gPlayer1 <> nil then gPlayer1.NoReload := not gPlayer1.NoReload;
566 if gPlayer2 <> nil then gPlayer2.NoReload := not gPlayer2.NoReload;
567 goto Cheated;
568 end;
569 if CheckCheat(I_GAME_CHEAT_AIMLINE) then
570 begin
571 gAimLine := not gAimLine;
572 goto Cheated;
573 end;
574 if CheckCheat(I_GAME_CHEAT_AUTOMAP) then
575 begin
576 gShowMap := not gShowMap;
577 goto Cheated;
578 end;
579 Exit;
581 Cheated:
582 g_Sound_PlayEx(s);
583 end;
586 procedure KeyPress (K: Word);
587 {$IFNDEF HEADLESS}
588 var
589 Msg: g_gui.TMessage;
590 {$ENDIF}
591 begin
592 {$IFNDEF HEADLESS}
593 case K of
594 VK_ESCAPE: // <Esc>:
595 begin
596 if (g_ActiveWindow <> nil) then
597 begin
598 Msg.Msg := WM_KEYDOWN;
599 Msg.WParam := VK_ESCAPE;
600 g_ActiveWindow.OnMessage(Msg);
601 if (not g_Game_IsNet) and (g_ActiveWindow = nil) then g_Game_Pause(false); //Fn loves to do this
602 end
603 else if (gState <> STATE_FOLD) then
604 begin
605 if gGameOn or (gState = STATE_INTERSINGLE) or (gState = STATE_INTERCUSTOM) then
606 begin
607 g_Game_InGameMenu(True);
608 end
609 else if (gExit = 0) and (gState <> STATE_SLIST) then
610 begin
611 if (gState <> STATE_MENU) then
612 begin
613 if (NetMode <> NET_NONE) then
614 begin
615 g_Game_StopAllSounds(True);
616 g_Game_Free;
617 gState := STATE_MENU;
618 Exit;
619 end;
620 end;
621 g_GUI_ShowWindow('MainMenu');
622 g_Sound_PlayEx('MENU_OPEN');
623 end;
624 end;
625 end;
627 IK_F2, IK_F3, IK_F4, IK_F5, IK_F6, IK_F7, IK_F10:
628 begin // <F2> .. <F6> � <F12>
629 if gGameOn and (not gConsoleShow) and (not gChatShow) then
630 begin
631 while (g_ActiveWindow <> nil) do g_GUI_HideWindow(False);
632 if (not g_Game_IsNet) then g_Game_Pause(True);
633 case K of
634 IK_F2: g_Menu_Show_SaveMenu();
635 IK_F3: g_Menu_Show_LoadMenu();
636 IK_F4: g_Menu_Show_GameSetGame();
637 IK_F5: g_Menu_Show_OptionsVideo();
638 IK_F6: g_Menu_Show_OptionsSound();
639 IK_F7: g_Menu_Show_EndGameMenu();
640 IK_F10: g_Menu_Show_QuitGameMenu();
641 end;
642 end;
643 end;
645 else
646 begin
647 gJustChatted := False;
648 if gConsoleShow or gChatShow then
649 begin
650 g_Console_Control(K);
651 end
652 else if (g_ActiveWindow <> nil) then
653 begin
654 Msg.Msg := WM_KEYDOWN;
655 Msg.WParam := K;
656 g_ActiveWindow.OnMessage(Msg);
657 end
658 else if (gState = STATE_MENU) then
659 begin
660 g_GUI_ShowWindow('MainMenu');
661 g_Sound_PlayEx('MENU_OPEN');
662 end;
663 end;
664 end;
665 {$ENDIF}
666 end;
669 procedure CharPress (C: AnsiChar);
670 var
671 Msg: g_gui.TMessage;
672 a: Integer;
673 begin
674 if gConsoleShow or gChatShow then
675 begin
676 g_Console_Char(C)
677 end
678 else if (g_ActiveWindow <> nil) then
679 begin
680 Msg.Msg := WM_CHAR;
681 Msg.WParam := Ord(C);
682 g_ActiveWindow.OnMessage(Msg);
683 end
684 else
685 begin
686 for a := 0 to 14 do charbuff[a] := charbuff[a+1];
687 charbuff[15] := upcase1251(C);
688 Cheat();
689 end;
690 end;
692 end.