DEADSOFTWARE

"strife" key for gaypad keyset
[d2df-sdl.git] / src / game / g_game.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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
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.
12 *
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/>.
15 *)
16 {$MODE DELPHI}
17 unit g_game;
19 interface
21 uses
22 g_basic, g_player, e_graphics, Classes, g_res_downloader,
23 SysUtils, g_sound, g_gui, g_scripts, MAPSTRUCT, wadreader, md5;
25 type
26 TGameSettings = record
27 GameType: Byte;
28 GameMode: Byte;
29 TimeLimit: Word;
30 GoalLimit: Word;
31 WarmupTime: Word;
32 MaxLives: Byte;
33 Options: LongWord;
34 WAD: String;
35 end;
37 TGameEvent = record
38 Name: String;
39 Command: String;
40 end;
42 TDelayedEvent = record
43 Pending: Boolean;
44 Time: LongWord;
45 DEType: Byte;
46 DENum: Integer;
47 DEStr: String;
48 end;
50 TPlayerSettings = record
51 Name: String;
52 Model: String;
53 Color: TRGB;
54 Team: Byte;
55 end;
57 TMegaWADInfo = record
58 Name: String;
59 Description: String;
60 Author: String;
61 Pic: String;
62 end;
64 THearPoint = record
65 Active: Boolean;
66 Coords: TPoint;
67 end;
69 function g_Game_IsNet(): Boolean;
70 function g_Game_IsServer(): Boolean;
71 function g_Game_IsClient(): Boolean;
72 procedure g_Game_Init();
73 procedure g_Game_Free();
74 procedure g_Game_LoadData();
75 procedure g_Game_FreeData();
76 procedure g_Game_Update();
77 procedure g_Game_Draw();
78 procedure g_Game_Quit();
79 procedure g_Game_SetupScreenSize();
80 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
81 function g_Game_ModeToText(Mode: Byte): string;
82 function g_Game_TextToMode(Mode: string): Byte;
83 procedure g_Game_ExecuteEvent(Name: String);
84 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
85 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
86 procedure g_Game_RemovePlayer();
87 procedure g_Game_Spectate();
88 procedure g_Game_SpectateCenterView();
89 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
90 procedure g_Game_StartCustom(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte);
91 procedure g_Game_StartServer(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte; IPAddr: LongWord; Port: Word);
92 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
93 procedure g_Game_Restart();
94 procedure g_Game_RestartLevel();
95 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
96 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
97 procedure g_Game_SaveOptions();
98 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
99 procedure g_Game_ChangeMap(MapPath: String);
100 procedure g_Game_ExitLevel(Map: Char16);
101 function g_Game_GetFirstMap(WAD: String): String;
102 function g_Game_GetNextMap(): String;
103 procedure g_Game_NextLevel();
104 procedure g_Game_Pause(Enable: Boolean);
105 procedure g_Game_InGameMenu(Show: Boolean);
106 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
107 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
108 procedure g_Game_Message(Msg: String; Time: Word);
109 procedure g_Game_LoadMapList(FileName: String);
110 procedure g_Game_PauseAllSounds(Enable: Boolean);
111 procedure g_Game_StopAllSounds(all: Boolean);
112 procedure g_Game_UpdateTriggerSounds();
113 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
114 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
115 procedure g_Game_Announce_KillCombo(Param: Integer);
116 procedure g_Game_StartVote(Command, Initiator: string);
117 procedure g_Game_CheckVote;
118 procedure g_TakeScreenShot();
119 procedure g_FatalError(Text: String);
120 procedure g_SimpleError(Text: String);
121 function g_Game_IsTestMap(): Boolean;
122 procedure g_Game_DeleteTestMap();
123 procedure GameCVars(P: SArray);
124 procedure GameCommands(P: SArray);
125 procedure GameCheats(P: SArray);
126 procedure DebugCommands(P: SArray);
127 procedure g_Game_Process_Params;
128 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
129 procedure g_Game_StepLoading();
130 procedure g_Game_ClearLoading();
131 procedure g_Game_SetDebugMode();
132 procedure DrawLoadingStat();
134 { procedure SetWinPause(Enable: Boolean); }
136 const
137 GAME_TICK = 28;
139 LOADING_SHOW_STEP = 100;
140 LOADING_INTERLINE = 20;
142 GT_NONE = 0;
143 GT_SINGLE = 1;
144 GT_CUSTOM = 2;
145 GT_SERVER = 3;
146 GT_CLIENT = 4;
148 GM_NONE = 0;
149 GM_DM = 1;
150 GM_TDM = 2;
151 GM_CTF = 3;
152 GM_COOP = 4;
153 GM_SINGLE = 5;
155 MESSAGE_DIKEY = WM_USER + 1;
157 EXIT_QUIT = 1;
158 EXIT_SIMPLE = 2;
159 EXIT_RESTART = 3;
160 EXIT_ENDLEVELSINGLE = 4;
161 EXIT_ENDLEVELCUSTOM = 5;
163 GAME_OPTION_RESERVED = 1;
164 GAME_OPTION_TEAMDAMAGE = 2;
165 GAME_OPTION_ALLOWEXIT = 4;
166 GAME_OPTION_WEAPONSTAY = 8;
167 GAME_OPTION_MONSTERS = 16;
168 GAME_OPTION_BOTVSPLAYER = 32;
169 GAME_OPTION_BOTVSMONSTER = 64;
171 STATE_NONE = 0;
172 STATE_MENU = 1;
173 STATE_FOLD = 2;
174 STATE_INTERCUSTOM = 3;
175 STATE_INTERSINGLE = 4;
176 STATE_INTERTEXT = 5;
177 STATE_INTERPIC = 6;
178 STATE_ENDPIC = 7;
179 STATE_SLIST = 8;
181 LMS_RESPAWN_NONE = 0;
182 LMS_RESPAWN_WARMUP = 1;
183 LMS_RESPAWN_FINAL = 2;
185 SPECT_NONE = 0;
186 SPECT_STATS = 1;
187 SPECT_MAPVIEW = 2;
188 SPECT_PLAYERS = 3;
190 DE_GLOBEVENT = 0;
191 DE_BFGHIT = 1;
192 DE_KILLCOMBO = 2;
194 ANNOUNCE_NONE = 0;
195 ANNOUNCE_ME = 1;
196 ANNOUNCE_MEPLUS = 2;
197 ANNOUNCE_ALL = 3;
199 CONFIG_FILENAME = 'Doom2DF.cfg';
200 LOG_FILENAME = 'Doom2DF.log';
202 TEST_MAP_NAME = '$$$_TEST_$$$';
204 STD_PLAYER_MODEL = 'Doomer';
206 var
207 gStdFont: DWORD;
208 gGameSettings: TGameSettings;
209 gPlayer1Settings: TPlayerSettings;
210 gPlayer2Settings: TPlayerSettings;
211 gGameOn: Boolean;
212 gPlayerScreenSize: TPoint;
213 gPlayer1ScreenCoord: TPoint;
214 gPlayer2ScreenCoord: TPoint;
215 gPlayer1: TPlayer = nil;
216 gPlayer2: TPlayer = nil;
217 gPlayerDrawn: TPlayer = nil;
218 gTime: LongWord;
219 gSwitchGameMode: Byte = GM_DM;
220 gHearPoint1, gHearPoint2: THearPoint;
221 gSoundEffectsDF: Boolean = False;
222 gSoundTriggerTime: Word = 0;
223 gAnnouncer: Byte = ANNOUNCE_NONE;
224 goodsnd: array[0..3] of TPlayableSound;
225 killsnd: array[0..3] of TPlayableSound;
226 gDefInterTime: ShortInt = -1;
227 gInterEndTime: LongWord = 0;
228 gInterTime: LongWord = 0;
229 gServInterTime: Byte = 0;
230 gGameStartTime: LongWord = 0;
231 gTotalMonsters: Integer = 0;
232 gPause: Boolean;
233 gShowTime: Boolean = True;
234 gShowFPS: Boolean = False;
235 gShowGoals: Boolean = True;
236 gShowStat: Boolean = True;
237 gShowKillMsg: Boolean = True;
238 gShowLives: Boolean = True;
239 gShowPing: Boolean = False;
240 gShowMap: Boolean = False;
241 gExit: Byte = 0;
242 gState: Byte = STATE_NONE;
243 sX, sY: Integer;
244 sWidth, sHeight: Word;
245 gSpectMode: Byte = SPECT_NONE;
246 gSpectHUD: Boolean = True;
247 gSpectKeyPress: Boolean = False;
248 gSpectX: Integer = 0;
249 gSpectY: Integer = 0;
250 gSpectStep: Byte = 8;
251 gSpectViewTwo: Boolean = False;
252 gSpectPID1: Integer = -1;
253 gSpectPID2: Integer = -1;
254 gMusic: TMusic = nil;
255 gLoadGameMode: Boolean;
256 gCheats: Boolean = False;
257 gMapOnce: Boolean = False;
258 gMapToDelete: String;
259 gTempDelete: Boolean = False;
260 gLastMap: Boolean = False;
261 gWinPosX, gWinPosY: Integer;
262 gWinSizeX, gWinSizeY: Integer;
263 gWinFrameX, gWinFrameY, gWinCaption: Integer;
264 gWinActive: Boolean = True; // by default window is active, lol
265 gResolutionChange: Boolean = False;
266 gRC_Width, gRC_Height: Word;
267 gRC_FullScreen, gRC_Maximized: Boolean;
268 gLanguageChange: Boolean = False;
269 gDebugMode: Boolean = False;
270 g_debug_Sounds: Boolean = False;
271 g_debug_Frames: Boolean = False;
272 g_debug_WinMsgs: Boolean = False;
273 g_debug_MonsterOff: Boolean = False;
274 g_debug_BotAIOff: Byte = 0;
275 g_debug_HealthBar: Boolean = False;
276 g_Debug_Player: Boolean = False;
277 gCoopMonstersKilled: Word = 0;
278 gCoopSecretsFound: Word = 0;
279 gCoopTotalMonstersKilled: Word = 0;
280 gCoopTotalSecretsFound: Word = 0;
281 gCoopTotalMonsters: Word = 0;
282 gCoopTotalSecrets: Word = 0;
283 gStatsOff: Boolean = False;
284 gStatsPressed: Boolean = False;
285 gExitByTrigger: Boolean = False;
286 gNextMap: String = '';
287 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
288 gLMSRespawnTime: Cardinal = 0;
289 gLMSSoftSpawn: Boolean = False;
290 gMissionFailed: Boolean = False;
291 gVoteInProgress: Boolean = False;
292 gVotePassed: Boolean = False;
293 gVoteCommand: string = '';
294 gVoteTimer: Cardinal = 0;
295 gVoteCmdTimer: Cardinal = 0;
296 gVoteCount: Integer = 0;
297 gVoteTimeout: Cardinal = 30;
298 gVoted: Boolean = False;
299 gVotesEnabled: Boolean = True;
300 gEvents: Array of TGameEvent;
301 gDelayedEvents: Array of TDelayedEvent;
303 // move button values:
304 // bits 0-1: l/r state:
305 // 0: neither left, nor right pressed
306 // 1: left pressed
307 // 2: right pressed
308 // bits 4-5: l/r state when strafe was pressed
309 P1MoveButton: Byte = 0;
310 P2MoveButton: Byte = 0;
312 implementation
314 uses
315 g_textures, g_main, g_window, g_menu,
316 e_input, e_log, g_console, g_items, g_map,
317 g_playermodel, g_gfx, g_options, g_weapons, Math,
318 g_triggers, MAPDEF, g_monsters, e_sound, CONFIG,
319 BinEditor, g_language, g_net, SDL,
320 ENet, e_fixedbuffer, g_netmsg, g_netmaster, GL, GLExt,
321 utils, sfs;
323 type
324 TEndCustomGameStat = record
325 PlayerStat: TPlayerStatArray;
326 TeamStat: TTeamStat;
327 GameTime: LongWord;
328 GameMode: Byte;
329 Map, MapName: String;
330 end;
332 TEndSingleGameStat = record
333 PlayerStat: Array [0..1] of record
334 Kills: Integer;
335 Secrets: Integer;
336 end;
337 GameTime: LongWord;
338 TwoPlayers: Boolean;
339 TotalSecrets: Integer;
340 end;
342 TLoadingStat = record
343 CurValue: Integer;
344 MaxValue: Integer;
345 ShowCount: Integer;
346 Msgs: Array of String;
347 NextMsg: Word;
348 end;
350 TParamStrValue = record
351 Name: String;
352 Value: String;
353 end;
355 TParamStrValues = Array of TParamStrValue;
357 const
358 INTER_ACTION_TEXT = 1;
359 INTER_ACTION_PIC = 2;
360 INTER_ACTION_MUSIC = 3;
362 var
363 FPS, UPS: Word;
364 FPSCounter, UPSCounter: Word;
365 FPSTime, UPSTime: LongWord;
366 DataLoaded: Boolean = False;
367 LastScreenShot: Int64;
368 IsDrawStat: Boolean = False;
369 CustomStat: TEndCustomGameStat;
370 SingleStat: TEndSingleGameStat;
371 LoadingStat: TLoadingStat;
372 EndingGameCounter: Byte = 0;
373 MessageText: String;
374 MessageTime: Word;
375 MapList: SArray = nil;
376 MapIndex: Integer = -1;
377 MegaWAD: record
378 info: TMegaWADInfo;
379 endpic: String;
380 endmus: String;
381 res: record
382 text: Array of ShortString;
383 anim: Array of ShortString;
384 pic: Array of ShortString;
385 mus: Array of ShortString;
386 end;
387 triggers: Array of record
388 event: ShortString;
389 actions: Array of record
390 action, p1, p2: Integer;
391 end;
392 end;
393 cur_trigger: Integer;
394 cur_action: Integer;
395 end;
396 //InterPic: String;
397 InterText: record
398 lines: SArray;
399 img: String;
400 cur_line: Integer;
401 cur_char: Integer;
402 counter: Integer;
403 endtext: Boolean;
404 end;
406 function Compare(a, b: TPlayerStat): Integer;
407 begin
408 if a.Spectator then Result := 1
409 else if b.Spectator then Result := -1
410 else if a.Frags < b.Frags then Result := 1
411 else if a.Frags > b.Frags then Result := -1
412 else if a.Deaths < b.Deaths then Result := -1
413 else if a.Deaths > b.Deaths then Result := 1
414 else if a.Kills < b.Kills then Result := -1
415 else Result := 1;
416 end;
418 procedure SortGameStat(var stat: TPlayerStatArray);
419 var
420 I, J: Integer;
421 T: TPlayerStat;
422 begin
423 if stat = nil then Exit;
425 for I := High(stat) downto Low(stat) do
426 for J := Low(stat) to High(stat) - 1 do
427 if Compare(stat[J], stat[J + 1]) = 1 then
428 begin
429 T := stat[J];
430 stat[J] := stat[J + 1];
431 stat[J + 1] := T;
432 end;
433 end;
435 function g_Game_ModeToText(Mode: Byte): string;
436 begin
437 Result := '';
438 case Mode of
439 GM_DM: Result := _lc[I_MENU_GAME_TYPE_DM];
440 GM_TDM: Result := _lc[I_MENU_GAME_TYPE_TDM];
441 GM_CTF: Result := _lc[I_MENU_GAME_TYPE_CTF];
442 GM_COOP: Result := _lc[I_MENU_GAME_TYPE_COOP];
443 GM_SINGLE: Result := _lc[I_MENU_GAME_TYPE_SINGLE];
444 end;
445 end;
447 function g_Game_TextToMode(Mode: string): Byte;
448 begin
449 Result := GM_NONE;
450 Mode := UpperCase(Mode);
451 if Mode = _lc[I_MENU_GAME_TYPE_DM] then
452 begin
453 Result := GM_DM;
454 Exit;
455 end;
456 if Mode = _lc[I_MENU_GAME_TYPE_TDM] then
457 begin
458 Result := GM_TDM;
459 Exit;
460 end;
461 if Mode = _lc[I_MENU_GAME_TYPE_CTF] then
462 begin
463 Result := GM_CTF;
464 Exit;
465 end;
466 if Mode = _lc[I_MENU_GAME_TYPE_COOP] then
467 begin
468 Result := GM_COOP;
469 Exit;
470 end;
471 if Mode = _lc[I_MENU_GAME_TYPE_SINGLE] then
472 begin
473 Result := GM_SINGLE;
474 Exit;
475 end;
476 end;
478 function g_Game_IsNet(): Boolean;
479 begin
480 Result := (gGameSettings.GameType in [GT_SERVER, GT_CLIENT]);
481 end;
483 function g_Game_IsServer(): Boolean;
484 begin
485 Result := (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM, GT_SERVER]);
486 end;
488 function g_Game_IsClient(): Boolean;
489 begin
490 Result := (gGameSettings.GameType = GT_CLIENT);
491 end;
493 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
494 var
495 w: TWADFile;
496 cfg: TConfig;
497 p: Pointer;
498 len: Integer;
499 begin
500 Result.name := ExtractFileName(WAD);
501 Result.description := '';
502 Result.author := '';
504 w := TWADFile.Create();
505 w.ReadFile(WAD);
507 if not w.GetResource('INTERSCRIPT', p, len) then
508 begin
509 w.Free();
510 Exit;
511 end;
513 cfg := TConfig.CreateMem(p, len);
514 Result.name := cfg.ReadStr('megawad', 'name', ExtractFileName(WAD));
515 Result.description := cfg.ReadStr('megawad', 'description', '');
516 Result.author := cfg.ReadStr('megawad', 'author', '');
517 Result.pic := cfg.ReadStr('megawad', 'pic', '');
518 cfg.Free();
520 FreeMem(p);
521 end;
523 procedure g_Game_FreeWAD();
524 var
525 a: Integer;
526 begin
527 for a := 0 to High(MegaWAD.res.pic) do
528 if MegaWAD.res.pic[a] <> '' then
529 g_Texture_Delete(MegaWAD.res.pic[a]);
531 for a := 0 to High(MegaWAD.res.mus) do
532 if MegaWAD.res.mus[a] <> '' then
533 g_Sound_Delete(MegaWAD.res.mus[a]);
535 MegaWAD.res.pic := nil;
536 MegaWAD.res.text := nil;
537 MegaWAD.res.anim := nil;
538 MegaWAD.res.mus := nil;
539 MegaWAD.triggers := nil;
541 g_Texture_Delete('TEXTURE_endpic');
542 g_Sound_Delete('MUSIC_endmus');
544 ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
545 gGameSettings.WAD := '';
546 end;
548 procedure g_Game_LoadWAD(WAD: string);
549 var
550 w: TWADFile;
551 cfg: TConfig;
552 p: Pointer;
553 {b, }len: Integer;
554 s: string;
555 begin
556 g_Game_FreeWAD();
557 gGameSettings.WAD := WAD;
558 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
559 Exit;
561 MegaWAD.info := g_Game_GetMegaWADInfo(MapsDir + WAD);
563 w := TWADFile.Create();
564 w.ReadFile(MapsDir + WAD);
566 if not w.GetResource('INTERSCRIPT', p, len) then
567 begin
568 w.Free();
569 Exit;
570 end;
572 cfg := TConfig.CreateMem(p, len);
574 {b := 1;
575 while True do
576 begin
577 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
578 if s = '' then Break;
579 b := b+1;
581 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
582 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
584 g_Texture_CreateWADEx(s, s);
585 end;
587 b := 1;
588 while True do
589 begin
590 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
591 if s = '' then Break;
592 b := b+1;
594 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
595 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
597 g_Music_CreateWADEx(s, s);
598 end;}
600 MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
601 if MegaWAD.endpic <> '' then
602 begin
603 s := g_ExtractWadName(MegaWAD.endpic);
604 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
605 g_Texture_CreateWADEx('TEXTURE_endpic', s+MegaWAD.endpic);
606 end;
607 MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
608 if MegaWAD.endmus <> '' then
609 begin
610 s := g_ExtractWadName(MegaWAD.endmus);
611 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
612 g_Sound_CreateWADEx('MUSIC_endmus', s+MegaWAD.endmus, True);
613 end;
615 cfg.Free();
616 FreeMem(p);
617 w.Free();
618 end;
620 {procedure start_trigger(t: string);
621 begin
622 end;
624 function next_trigger(): Boolean;
625 begin
626 end;}
628 procedure DisableCheats();
629 begin
630 MAX_RUNVEL := 8;
631 VEL_JUMP := 10;
632 gFly := False;
634 if gPlayer1 <> nil then gPlayer1.GodMode := False;
635 if gPlayer2 <> nil then gPlayer2.GodMode := False;
636 if gPlayer1 <> nil then gPlayer1.NoTarget := False;
637 if gPlayer2 <> nil then gPlayer2.NoTarget := False;
638 end;
640 procedure g_Game_ExecuteEvent(Name: String);
641 var
642 a: Integer;
643 begin
644 if Name = '' then
645 Exit;
646 if gEvents = nil then
647 Exit;
648 for a := 0 to High(gEvents) do
649 if gEvents[a].Name = Name then
650 begin
651 if gEvents[a].Command <> '' then
652 g_Console_Process(gEvents[a].Command, True);
653 break;
654 end;
655 end;
657 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
658 var
659 a, n: Integer;
660 begin
661 n := -1;
662 if gDelayedEvents <> nil then
663 for a := 0 to High(gDelayedEvents) do
664 if not gDelayedEvents[a].Pending then
665 begin
666 n := a;
667 break;
668 end;
669 if n = -1 then
670 begin
671 SetLength(gDelayedEvents, Length(gDelayedEvents) + 1);
672 n := High(gDelayedEvents);
673 end;
674 gDelayedEvents[n].Pending := True;
675 gDelayedEvents[n].DEType := DEType;
676 gDelayedEvents[n].DENum := Num;
677 gDelayedEvents[n].DEStr := Str;
678 if DEType = DE_GLOBEVENT then
679 gDelayedEvents[n].Time := (GetTimer() {div 1000}) + Time
680 else
681 gDelayedEvents[n].Time := gTime + Time;
682 Result := n;
683 end;
685 procedure EndGame();
686 var
687 a: Integer;
688 FileName: string;
689 begin
690 if g_Game_IsNet and g_Game_IsServer then
691 MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed));
693 // Ñòîï èãðà:
694 gPause := False;
695 gGameOn := False;
697 g_Game_StopAllSounds(False);
699 MessageTime := 0;
700 MessageText := '';
702 EndingGameCounter := 0;
703 g_ActiveWindow := nil;
705 gLMSRespawn := LMS_RESPAWN_NONE;
706 gLMSRespawnTime := 0;
708 case gExit of
709 EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
710 begin
711 g_Game_Free();
713 if gMapOnce then
714 begin // Ýòî áûë òåñò
715 g_Game_Quit();
716 end
717 else
718 begin // Âûõîä â ãëàâíîå ìåíþ
719 gMusic.SetByName('MUSIC_MENU');
720 gMusic.Play();
721 if gState <> STATE_SLIST then
722 begin
723 g_GUI_ShowWindow('MainMenu');
724 gState := STATE_MENU;
725 end else
726 begin
727 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
728 slReturnPressed := True;
729 if g_Net_Slist_Fetch(slCurrent) then
730 begin
731 if slCurrent = nil then
732 slWaitStr := _lc[I_NET_SLIST_NOSERVERS];
733 end
734 else
735 slWaitStr := _lc[I_NET_SLIST_ERROR];
736 end;
738 g_Game_ExecuteEvent('ongameend');
739 end;
740 end;
742 EXIT_RESTART: // Íà÷àòü óðîâåíü ñíà÷àëà
743 begin
744 if not g_Game_IsClient then g_Game_Restart();
745 end;
747 EXIT_ENDLEVELCUSTOM: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
748 begin
749 // Ñòàòèñòèêà Ñâîåé èãðû:
750 FileName := g_ExtractWadName(gMapInfo.Map);
752 CustomStat.GameTime := gTime;
753 CustomStat.Map := ExtractFileName(FileName)+':'+g_ExtractFileName(gMapInfo.Map); //ResName;
754 CustomStat.MapName := gMapInfo.Name;
755 CustomStat.GameMode := gGameSettings.GameMode;
756 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
757 CustomStat.TeamStat := gTeamStat;
759 CustomStat.PlayerStat := nil;
761 // Ñòàòèñòèêà èãðîêîâ:
762 if gPlayers <> nil then
763 begin
764 for a := 0 to High(gPlayers) do
765 if gPlayers[a] <> nil then
766 begin
767 SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
768 with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
769 begin
770 Name := gPlayers[a].Name;
771 Frags := gPlayers[a].Frags;
772 Deaths := gPlayers[a].Death;
773 Kills := gPlayers[a].Kills;
774 Team := gPlayers[a].Team;
775 Color := gPlayers[a].Model.Color;
776 Spectator := gPlayers[a].FSpectator;
777 end;
778 end;
780 SortGameStat(CustomStat.PlayerStat);
781 end;
783 g_Game_ExecuteEvent('onmapend');
785 // Çàòóõàþùèé ýêðàí:
786 EndingGameCounter := 255;
787 gState := STATE_FOLD;
788 gInterTime := 0;
789 if gDefInterTime < 0 then
790 gInterEndTime := IfThen((gGameSettings.GameType = GT_SERVER) and (gPlayer1 = nil), 15000, 25000)
791 else
792 gInterEndTime := gDefInterTime * 1000;
793 end;
795 EXIT_ENDLEVELSINGLE: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
796 begin
797 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
798 SingleStat.GameTime := gTime;
799 SingleStat.TwoPlayers := gPlayer2 <> nil;
800 SingleStat.TotalSecrets := gSecretsCount;
801 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
802 SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills;
803 SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets;
804 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
805 if SingleStat.TwoPlayers then
806 begin
807 SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills;
808 SingleStat.PlayerStat[1].Secrets := gPlayer2.Secrets;
809 end;
811 g_Game_ExecuteEvent('onmapend');
813 // Åñòü åùå êàðòû:
814 if gNextMap <> '' then
815 begin
816 gMusic.SetByName('MUSIC_INTERMUS');
817 gMusic.Play();
818 gState := STATE_INTERSINGLE;
820 g_Game_ExecuteEvent('oninter');
821 end
822 else // Áîëüøå íåò êàðò
823 begin
824 // Çàòóõàþùèé ýêðàí:
825 EndingGameCounter := 255;
826 gState := STATE_FOLD;
827 end;
828 end;
829 end;
831 // Îêîí÷àíèå îáðàáîòàíî:
832 if gExit <> EXIT_QUIT then
833 gExit := 0;
834 end;
836 procedure DrawStat();
837 var
838 pc, x, y, w, h: Integer;
839 w1, w2, w3, w4: Integer;
840 a, aa: Integer;
841 cw, ch, r, g, b, rr, gg, bb: Byte;
842 s1, s2, s3: String;
843 _y: Integer;
844 stat: TPlayerStatArray;
845 wad, map: string;
846 mapstr: string;
847 begin
848 s1 := '';
849 s2 := '';
850 s3 := '';
851 pc := g_Player_GetCount;
852 e_TextureFontGetSize(gStdFont, cw, ch);
854 w := gScreenWidth-(gScreenWidth div 5);
855 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
856 h := 32+ch*(11+pc)
857 else
858 h := 40+ch*5+(ch+8)*pc;
859 x := (gScreenWidth div 2)-(w div 2);
860 y := (gScreenHeight div 2)-(h div 2);
862 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
863 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
865 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
866 map := g_ExtractFileName(gMapInfo.Map);
867 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
869 case gGameSettings.GameMode of
870 GM_DM:
871 begin
872 if gGameSettings.MaxLives = 0 then
873 s1 := _lc[I_GAME_DM]
874 else
875 s1 := _lc[I_GAME_LMS];
876 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
877 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
878 end;
880 GM_TDM:
881 begin
882 if gGameSettings.MaxLives = 0 then
883 s1 := _lc[I_GAME_TDM]
884 else
885 s1 := _lc[I_GAME_TLMS];
886 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
887 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
888 end;
890 GM_CTF:
891 begin
892 s1 := _lc[I_GAME_CTF];
893 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]);
894 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
895 end;
897 GM_COOP:
898 begin
899 if gGameSettings.MaxLives = 0 then
900 s1 := _lc[I_GAME_COOP]
901 else
902 s1 := _lc[I_GAME_SURV];
903 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
904 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
905 end;
907 else
908 begin
909 s1 := '';
910 s2 := '';
911 end;
912 end;
914 _y := y+8;
915 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
916 _y := _y+ch+8;
917 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
918 _y := _y+ch+8;
919 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
921 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3,
922 gStdFont, 200, 200, 200, 1);
924 if NetMode = NET_SERVER then
925 e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1)
926 else
927 if NetMode = NET_CLIENT then
928 e_TextureFontPrintEx(x+8, y + 8,
929 NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1);
931 if pc = 0 then
932 Exit;
933 stat := g_Player_GetStats();
934 SortGameStat(stat);
936 w2 := (w-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
937 w3 := (w-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
938 w4 := w3;
939 w1 := w-16-w2-w3-w4; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
941 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
942 begin
943 _y := _y+ch+ch;
945 for a := TEAM_RED to TEAM_BLUE do
946 begin
947 if a = TEAM_RED then
948 begin
949 s1 := _lc[I_GAME_TEAM_RED];
950 r := 255;
951 g := 0;
952 b := 0;
953 end
954 else
955 begin
956 s1 := _lc[I_GAME_TEAM_BLUE];
957 r := 0;
958 g := 0;
959 b := 255;
960 end;
962 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
963 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals),
964 gStdFont, r, g, b, 1);
966 _y := _y+ch+(ch div 4);
967 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
968 _y := _y+(ch div 4);
970 for aa := 0 to High(stat) do
971 if stat[aa].Team = a then
972 with stat[aa] do
973 begin
974 if Spectator then
975 begin
976 rr := r div 2;
977 gg := g div 2;
978 bb := b div 2;
979 end
980 else
981 begin
982 rr := r;
983 gg := g;
984 bb := b;
985 end;
986 // Èìÿ
987 e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
988 // Ïèíã/ïîòåðè
989 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
990 // Ôðàãè
991 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
992 // Ñìåðòè
993 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
994 _y := _y+ch;
995 end;
997 _y := _y+ch;
998 end;
999 end
1000 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
1001 begin
1002 _y := _y+ch+ch;
1003 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
1004 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
1005 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
1006 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
1008 _y := _y+ch+8;
1009 for aa := 0 to High(stat) do
1010 with stat[aa] do
1011 begin
1012 if Spectator then
1013 begin
1014 r := 127;
1015 g := 64;
1016 end
1017 else
1018 begin
1019 r := 255;
1020 g := 127;
1021 end;
1022 // Öâåò èãðîêà
1023 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
1024 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
1025 // Èìÿ
1026 e_TextureFontPrintEx(x+16+16+8, _y+4, Name, gStdFont, r, g, 0, 1);
1027 // Ïèíã/ïîòåðè
1028 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
1029 // Ôðàãè
1030 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
1031 // Ñìåðòè
1032 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
1033 _y := _y+ch+8;
1034 end;
1035 end
1036 end;
1038 procedure g_Game_Init();
1039 var
1040 SR: TSearchRec;
1041 begin
1042 gExit := 0;
1043 gMapToDelete := '';
1044 gTempDelete := False;
1046 sfsGCDisable(); // temporary disable removing of temporary volumes
1048 try
1049 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
1050 g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
1051 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
1052 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
1054 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
1055 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
1056 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
1058 g_Game_ClearLoading();
1059 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
1060 g_Game_SetLoadingText('', 0, False);
1062 g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
1063 g_Console_Init();
1065 g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
1066 g_PlayerModel_LoadData();
1068 if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then
1069 repeat
1070 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1071 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1072 until FindNext(SR) <> 0;
1073 FindClose(SR);
1075 if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then
1076 repeat
1077 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1078 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1079 until FindNext(SR) <> 0;
1080 FindClose(SR);
1082 if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then
1083 repeat
1084 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1085 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1086 until FindNext(SR) <> 0;
1087 FindClose(SR);
1089 gGameOn := False;
1090 gPause := False;
1091 gTime := 0;
1092 LastScreenShot := 0;
1094 {e_MouseInfo.Accel := 1.0;}
1096 g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
1097 g_Game_LoadData();
1099 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1100 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
1101 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
1102 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True);
1103 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
1105 g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
1106 g_Menu_Init();
1108 g_Scripts_Init();
1109 g_Scripts_Load('game.conprint("Scripts Init")');
1111 gMusic := TMusic.Create();
1112 gMusic.SetByName('MUSIC_MENU');
1113 gMusic.Play();
1115 gGameSettings.WarmupTime := 30;
1117 gState := STATE_MENU;
1119 SetLength(gEvents, 6);
1120 gEvents[0].Name := 'ongamestart';
1121 gEvents[1].Name := 'ongameend';
1122 gEvents[2].Name := 'onmapstart';
1123 gEvents[3].Name := 'onmapend';
1124 gEvents[4].Name := 'oninter';
1125 gEvents[5].Name := 'onwadend';
1126 finally
1127 sfsGCEnable(); // enable releasing unused volumes
1128 end;
1129 end;
1131 procedure g_Game_Free();
1132 begin
1133 if NetMode = NET_CLIENT then g_Net_Disconnect();
1134 if NetMode = NET_SERVER then g_Net_Host_Die();
1136 g_Map_Free();
1137 g_Player_Free();
1138 g_Player_RemoveAllCorpses();
1140 gGameSettings.GameType := GT_NONE;
1141 if gGameSettings.GameMode = GM_SINGLE then
1142 gGameSettings.GameMode := GM_DM;
1143 gSwitchGameMode := gGameSettings.GameMode;
1145 gChatShow := False;
1146 gExitByTrigger := False;
1147 end;
1149 function IsActivePlayer(p: TPlayer): Boolean;
1150 begin
1151 Result := False;
1152 if p = nil then
1153 Exit;
1154 Result := (not p.FDummy) and (not p.FSpectator);
1155 end;
1157 function GetActivePlayer_ByID(ID: Integer): TPlayer;
1158 var
1159 a: Integer;
1160 begin
1161 Result := nil;
1162 if ID < 0 then
1163 Exit;
1164 if gPlayers = nil then
1165 Exit;
1166 for a := Low(gPlayers) to High(gPlayers) do
1167 if IsActivePlayer(gPlayers[a]) then
1168 begin
1169 if gPlayers[a].UID <> ID then
1170 continue;
1171 Result := gPlayers[a];
1172 break;
1173 end;
1174 end;
1176 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1177 var
1178 a, idx: Integer;
1179 ids: Array of Word;
1180 begin
1181 Result := -1;
1182 if gPlayers = nil then
1183 Exit;
1184 SetLength(ids, 0);
1185 idx := -1;
1186 for a := Low(gPlayers) to High(gPlayers) do
1187 if IsActivePlayer(gPlayers[a]) then
1188 begin
1189 SetLength(ids, Length(ids) + 1);
1190 ids[High(ids)] := gPlayers[a].UID;
1191 if gPlayers[a].UID = Skip then
1192 idx := High(ids);
1193 end;
1194 if Length(ids) = 0 then
1195 Exit;
1196 if idx = -1 then
1197 Result := ids[0]
1198 else
1199 Result := ids[(idx + 1) mod Length(ids)];
1200 end;
1202 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1203 var
1204 a, idx: Integer;
1205 ids: Array of Word;
1206 begin
1207 Result := -1;
1208 if gPlayers = nil then
1209 Exit;
1210 SetLength(ids, 0);
1211 idx := -1;
1212 for a := Low(gPlayers) to High(gPlayers) do
1213 if IsActivePlayer(gPlayers[a]) then
1214 begin
1215 SetLength(ids, Length(ids) + 1);
1216 ids[High(ids)] := gPlayers[a].UID;
1217 if gPlayers[a].UID = Skip then
1218 idx := High(ids);
1219 end;
1220 if Length(ids) = 0 then
1221 Exit;
1222 if idx = -1 then
1223 Result := ids[Length(ids) - 1]
1224 else
1225 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1226 end;
1228 function isKeyPressed (key1: Word; key2: Word): Boolean;
1229 begin
1230 if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
1231 if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
1232 result := false;
1233 end;
1235 procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false);
1236 var
1237 time: Word;
1238 strafeDir: Byte;
1239 begin
1240 if (plr = nil) then exit;
1241 if (p2hack) then time := 1000 else time := 1;
1242 strafeDir := MoveButton shr 4;
1243 MoveButton := MoveButton and $0F;
1244 with ctrl do
1245 begin
1246 if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1247 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1248 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1250 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1251 if MoveButton = 1 then plr.PressKey(KEY_LEFT, time)
1252 else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time);
1254 // if we have "strafe" key, turn off old strafe mechanics
1255 if isKeyPressed(KeyStrafe, KeyStrafe2) then
1256 begin
1257 // new strafe mechanics
1258 if (strafeDir = 0) then strafeDir := MoveButton; // start strafing
1259 // now set direction according to strafe
1260 if (strafeDir = 1) then plr.SetDirection(D_LEFT)
1261 else if (strafeDir = 2) then plr.SetDirection(D_RIGHT)
1262 else plr.SetDirection(TDirection(MoveButton-1));
1263 end
1264 else
1265 begin
1266 strafeDir := 0; // not strafing anymore
1267 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1268 if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(D_LEFT)
1269 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1270 else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(D_RIGHT)
1271 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1272 else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1));
1273 end;
1275 // fix movebutton state
1276 MoveButton := MoveButton or (strafeDir shl 4);
1278 // Îñòàëüíûå êëàâèøè:
1279 if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time);
1280 if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time);
1281 if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time);
1282 if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE);
1283 if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON);
1284 if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON);
1285 if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN);
1286 end;
1287 end;
1289 procedure g_Game_Update();
1290 var
1291 Msg: g_gui.TMessage;
1292 Time: Int64;
1293 a: Byte;
1294 w: Word;
1295 i, b: Integer;
1296 begin
1297 // Ïîðà âûêëþ÷àòü èãðó:
1298 if gExit = EXIT_QUIT then
1299 Exit;
1300 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1301 if gExit <> 0 then
1302 begin
1303 EndGame();
1304 if gExit = EXIT_QUIT then
1305 Exit;
1306 end;
1308 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1309 e_PollInput();
1311 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1312 g_Console_Update();
1314 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1315 begin
1316 gExit := EXIT_SIMPLE;
1317 EndGame();
1318 Exit;
1319 end;
1321 case gState of
1322 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1323 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1324 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1325 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1326 begin
1327 if g_Game_IsNet and g_Game_IsServer then
1328 begin
1329 gInterTime := gInterTime + GAME_TICK;
1330 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1331 if a <> gServInterTime then
1332 begin
1333 gServInterTime := a;
1334 MH_SEND_TimeSync(gServInterTime);
1335 end;
1336 end;
1338 if (not g_Game_IsClient) and
1341 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1342 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1343 and (g_ActiveWindow = nil)
1345 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1347 then
1348 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1349 g_Game_StopAllSounds(True);
1351 if gMapOnce then // Ýòî áûë òåñò
1352 gExit := EXIT_SIMPLE
1353 else
1354 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1355 g_Game_ChangeMap(gNextMap)
1356 else // Ñëåäóþùåé êàðòû íåò
1357 begin
1358 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1359 begin
1360 // Âûõîä â ãëàâíîå ìåíþ:
1361 g_Game_Free;
1362 g_GUI_ShowWindow('MainMenu');
1363 gMusic.SetByName('MUSIC_MENU');
1364 gMusic.Play();
1365 gState := STATE_MENU;
1366 end else
1367 begin
1368 // Ôèíàëüíàÿ êàðòèíêà:
1369 g_Game_ExecuteEvent('onwadend');
1370 g_Game_Free();
1371 if not gMusic.SetByName('MUSIC_endmus') then
1372 gMusic.SetByName('MUSIC_STDENDMUS');
1373 gMusic.Play();
1374 gState := STATE_ENDPIC;
1375 end;
1376 g_Game_ExecuteEvent('ongameend');
1377 end;
1379 Exit;
1380 end;
1382 if gState = STATE_INTERTEXT then
1383 if InterText.counter > 0 then
1384 InterText.counter := InterText.counter - 1;
1385 end;
1387 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1388 begin
1389 if EndingGameCounter = 0 then
1390 begin
1391 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1392 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1393 begin
1394 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1395 begin
1396 g_Game_ExecuteEvent('onwadend');
1397 if not gMusic.SetByName('MUSIC_endmus') then
1398 gMusic.SetByName('MUSIC_STDENDMUS');
1399 end
1400 else
1401 gMusic.SetByName('MUSIC_ROUNDMUS');
1403 gMusic.Play();
1404 gState := STATE_INTERCUSTOM;
1405 end
1406 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1407 begin
1408 gMusic.SetByName('MUSIC_INTERMUS');
1409 gMusic.Play();
1410 gState := STATE_INTERSINGLE;
1411 end;
1412 g_Game_ExecuteEvent('oninter');
1413 end
1414 else
1415 DecMin(EndingGameCounter, 6, 0);
1416 end;
1418 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1419 begin
1420 if gMapOnce then // Ýòî áûë òåñò
1421 begin
1422 gExit := EXIT_SIMPLE;
1423 Exit;
1424 end;
1425 end;
1427 STATE_SLIST:
1428 g_Serverlist_Control(slCurrent);
1429 end;
1431 if g_Game_IsNet then
1432 if not gConsoleShow then
1433 if not gChatShow then
1434 begin
1435 if g_ActiveWindow = nil then
1436 begin
1437 if e_KeyPressed(gGameControls.GameControls.Chat) then
1438 g_Console_Chat_Switch(False)
1439 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1440 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1441 g_Console_Chat_Switch(True);
1442 end;
1443 end else
1444 if not gChatEnter then
1445 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1446 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1447 gChatEnter := True;
1449 // Ñòàòèñòèêà ïî Tab:
1450 if gGameOn then
1451 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1452 (gGameSettings.GameType <> GT_SINGLE) and
1453 e_KeyPressed(gGameControls.GameControls.Stat);
1455 // Èãðà èäåò:
1456 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1457 begin
1458 // Âðåìÿ += 28 ìèëëèñåêóíä:
1459 gTime := gTime + GAME_TICK;
1461 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1462 if MessageTime = 0 then
1463 MessageText := '';
1464 if MessageTime > 0 then
1465 MessageTime := MessageTime - 1;
1467 if (g_Game_IsServer) then
1468 begin
1469 // Áûë çàäàí ëèìèò âðåìåíè:
1470 if (gGameSettings.TimeLimit > 0) then
1471 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1472 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1473 g_Game_NextLevel();
1474 Exit;
1475 end;
1477 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1478 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1479 g_Game_RestartRound(gLMSSoftSpawn);
1481 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1482 if gVoteInProgress and (gVoteTimer < gTime) then
1483 g_Game_CheckVote
1484 else if gVotePassed and (gVoteCmdTimer < gTime) then
1485 begin
1486 g_Console_Process(gVoteCommand);
1487 gVoteCommand := '';
1488 gVotePassed := False;
1489 end;
1491 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1492 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1493 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1494 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1495 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1497 // Áûë çàäàí ëèìèò ïîáåä:
1498 if (gGameSettings.GoalLimit > 0) then
1499 begin
1500 b := 0;
1502 if gGameSettings.GameMode = GM_DM then
1503 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1504 for i := 0 to High(gPlayers) do
1505 if gPlayers[i] <> nil then
1506 if gPlayers[i].Frags > b then
1507 b := gPlayers[i].Frags;
1508 end
1509 else
1510 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1511 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1512 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1513 end;
1515 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1516 if b >= gGameSettings.GoalLimit then
1517 begin
1518 g_Game_NextLevel();
1519 Exit;
1520 end;
1521 end;
1523 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1524 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1525 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1526 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1527 begin
1528 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1529 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1530 end // if not console
1531 else
1532 begin
1533 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1534 end;
1535 end; // if server
1537 // Íàáëþäàòåëü
1538 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1539 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1540 begin
1541 if not gSpectKeyPress then
1542 begin
1543 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1544 begin
1545 // switch spect mode
1546 case gSpectMode of
1547 SPECT_NONE: ; // not spectator
1548 SPECT_STATS,
1549 SPECT_MAPVIEW: Inc(gSpectMode);
1550 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1551 end;
1552 gSpectKeyPress := True;
1553 end;
1554 if gSpectMode = SPECT_MAPVIEW then
1555 begin
1556 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1557 gSpectX := Max(gSpectX - gSpectStep, 0);
1558 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1559 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1560 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1561 gSpectY := Max(gSpectY - gSpectStep, 0);
1562 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1563 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1564 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1565 begin
1566 // decrease step
1567 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1568 gSpectKeyPress := True;
1569 end;
1570 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1571 begin
1572 // increase step
1573 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1574 gSpectKeyPress := True;
1575 end;
1576 end;
1577 if gSpectMode = SPECT_PLAYERS then
1578 begin
1579 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1580 begin
1581 // add second view
1582 gSpectViewTwo := True;
1583 gSpectKeyPress := True;
1584 end;
1585 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1586 begin
1587 // remove second view
1588 gSpectViewTwo := False;
1589 gSpectKeyPress := True;
1590 end;
1591 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1592 begin
1593 // prev player (view 1)
1594 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1595 gSpectKeyPress := True;
1596 end;
1597 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1598 begin
1599 // next player (view 1)
1600 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1601 gSpectKeyPress := True;
1602 end;
1603 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1604 begin
1605 // prev player (view 2)
1606 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1607 gSpectKeyPress := True;
1608 end;
1609 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1610 begin
1611 // next player (view 2)
1612 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1613 gSpectKeyPress := True;
1614 end;
1615 end;
1616 end
1617 else
1618 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1619 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1620 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1621 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1622 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1623 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1624 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1625 gSpectKeyPress := False;
1626 end;
1628 // Îáíîâëÿåì âñå îñòàëüíîå:
1629 g_Map_Update();
1630 g_Items_Update();
1631 g_Triggers_Update();
1632 g_Weapon_Update();
1633 g_Monsters_Update();
1634 g_GFX_Update();
1635 g_Player_UpdateAll();
1636 g_Player_UpdatePhysicalObjects();
1637 if gGameSettings.GameType = GT_SERVER then
1638 if Length(gMonstersSpawned) > 0 then
1639 begin
1640 for I := 0 to High(gMonstersSpawned) do
1641 MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1642 SetLength(gMonstersSpawned, 0);
1643 end;
1645 if (gSoundTriggerTime > 8) then
1646 begin
1647 g_Game_UpdateTriggerSounds();
1648 gSoundTriggerTime := 0;
1649 end
1650 else
1651 Inc(gSoundTriggerTime);
1653 if (NetMode = NET_SERVER) then
1654 begin
1655 Inc(NetTimeToUpdate);
1656 Inc(NetTimeToReliable);
1657 if NetTimeToReliable >= NetRelupdRate then
1658 begin
1659 for I := 0 to High(gPlayers) do
1660 if gPlayers[I] <> nil then
1661 MH_SEND_PlayerPos(True, gPlayers[I].UID);
1663 if gMonsters <> nil then
1664 for I := 0 to High(gMonsters) do
1665 if gMonsters[I] <> nil then
1666 begin
1667 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1668 begin
1669 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1670 MH_SEND_MonsterPos(gMonsters[I].UID);
1671 end
1672 else
1673 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1674 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1675 (gMonsters[I].GameVelX <> 0) or
1676 (gMonsters[I].GameVelY <> 0) then
1677 MH_SEND_MonsterPos(gMonsters[I].UID);
1678 end;
1680 NetTimeToReliable := 0;
1681 NetTimeToUpdate := NetUpdateRate;
1682 end
1683 else if NetTimeToUpdate >= NetUpdateRate then
1684 begin
1685 if gPlayers <> nil then
1686 for I := 0 to High(gPlayers) do
1687 if gPlayers[I] <> nil then
1688 MH_SEND_PlayerPos(False, gPlayers[I].UID);
1690 if gMonsters <> nil then
1691 for I := 0 to High(gMonsters) do
1692 if gMonsters[I] <> nil then
1693 begin
1694 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1695 begin
1696 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1697 MH_SEND_MonsterPos(gMonsters[I].UID);
1698 end
1699 else
1700 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1701 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1702 (gMonsters[I].GameVelX <> 0) or
1703 (gMonsters[I].GameVelY <> 0) then
1704 MH_SEND_MonsterPos(gMonsters[I].UID);
1705 end;
1707 NetTimeToUpdate := 0;
1708 end;
1710 if NetUseMaster then
1711 if gTime >= NetTimeToMaster then
1712 begin
1713 if (NetMHost = nil) or (NetMPeer = nil) then
1714 if not g_Net_Slist_Connect then
1715 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1717 g_Net_Slist_Update;
1718 NetTimeToMaster := gTime + NetMasterRate;
1719 end;
1720 end
1721 else
1722 if NetMode = NET_CLIENT then
1723 MC_SEND_PlayerPos();
1724 end; // if gameOn ...
1726 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1727 if g_ActiveWindow <> nil then
1728 begin
1729 w := e_GetFirstKeyPressed();
1731 if (w <> IK_INVALID) then
1732 begin
1733 Msg.Msg := MESSAGE_DIKEY;
1734 Msg.wParam := w;
1735 g_ActiveWindow.OnMessage(Msg);
1736 end;
1738 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1739 if g_ActiveWindow <> nil then
1740 g_ActiveWindow.Update();
1742 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1743 if gResolutionChange then
1744 begin
1745 e_WriteLog('Changing resolution', MSG_NOTIFY);
1746 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1747 gResolutionChange := False;
1748 end;
1750 // Íóæíî ñìåíèòü ÿçûê:
1751 if gLanguageChange then
1752 begin
1753 //e_WriteLog('Read language file', MSG_NOTIFY);
1754 //g_Language_Load(DataDir + gLanguage + '.txt');
1755 g_Language_Set(gLanguage);
1756 g_Menu_Reset();
1757 gLanguageChange := False;
1758 end;
1759 end;
1761 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
1762 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
1763 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
1764 begin
1765 g_TakeScreenShot();
1766 LastScreenShot := GetTimer();
1767 end;
1769 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
1770 if e_KeyPressed(IK_F10) and
1771 gGameOn and
1772 (not gConsoleShow) and
1773 (g_ActiveWindow = nil) then
1774 begin
1775 KeyPress(IK_F10);
1776 end;
1778 Time := GetTimer() {div 1000};
1780 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
1781 if gDelayedEvents <> nil then
1782 for a := 0 to High(gDelayedEvents) do
1783 if gDelayedEvents[a].Pending and
1785 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
1786 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
1787 ) then
1788 begin
1789 case gDelayedEvents[a].DEType of
1790 DE_GLOBEVENT:
1791 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
1792 DE_BFGHIT:
1793 if gGameOn then
1794 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
1795 DE_KILLCOMBO:
1796 if gGameOn then
1797 begin
1798 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
1799 if g_Game_IsNet and g_Game_IsServer then
1800 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
1801 end;
1802 end;
1803 gDelayedEvents[a].Pending := False;
1804 end;
1806 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
1807 UPSCounter := UPSCounter + 1;
1808 if Time - UPSTime >= 1000 then
1809 begin
1810 UPS := UPSCounter;
1811 UPSCounter := 0;
1812 UPSTime := Time;
1813 end;
1814 end;
1816 procedure g_Game_LoadData();
1817 begin
1818 if DataLoaded then Exit;
1820 e_WriteLog('Loading game data...', MSG_NOTIFY);
1822 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
1823 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
1824 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
1825 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
1826 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
1827 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
1828 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB');
1829 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS');
1830 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD');
1831 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB');
1832 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS');
1833 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD');
1834 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
1835 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
1836 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
1837 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
1838 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
1839 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
1840 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
1841 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
1842 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
1843 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
1844 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
1845 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
1846 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
1847 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
1848 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
1849 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
1850 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
1851 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
1852 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
1853 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
1854 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
1855 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
1857 goodsnd[0] := TPlayableSound.Create();
1858 goodsnd[1] := TPlayableSound.Create();
1859 goodsnd[2] := TPlayableSound.Create();
1860 goodsnd[3] := TPlayableSound.Create();
1862 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
1863 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
1864 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
1865 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
1867 killsnd[0] := TPlayableSound.Create();
1868 killsnd[1] := TPlayableSound.Create();
1869 killsnd[2] := TPlayableSound.Create();
1870 killsnd[3] := TPlayableSound.Create();
1872 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
1873 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
1874 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
1875 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
1877 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
1878 g_Items_LoadData();
1880 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
1881 g_Weapon_LoadData();
1883 g_Monsters_LoadData();
1885 DataLoaded := True;
1886 end;
1888 procedure g_Game_FreeData();
1889 begin
1890 if not DataLoaded then Exit;
1892 g_Items_FreeData();
1893 g_Weapon_FreeData();
1894 g_Monsters_FreeData();
1896 e_WriteLog('Releasing game data...', MSG_NOTIFY);
1898 g_Texture_Delete('NOTEXTURE');
1899 g_Texture_Delete('TEXTURE_PLAYER_HUD');
1900 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
1901 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
1902 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
1903 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
1904 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
1905 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
1906 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
1907 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
1908 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
1909 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
1910 g_Frames_DeleteByName('FRAMES_TELEPORT');
1911 g_Sound_Delete('SOUND_GAME_TELEPORT');
1912 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
1913 g_Sound_Delete('SOUND_GAME_DOOROPEN');
1914 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
1915 g_Sound_Delete('SOUND_GAME_BULK1');
1916 g_Sound_Delete('SOUND_GAME_BULK2');
1917 g_Sound_Delete('SOUND_GAME_BUBBLE1');
1918 g_Sound_Delete('SOUND_GAME_BUBBLE2');
1919 g_Sound_Delete('SOUND_GAME_SWITCH1');
1920 g_Sound_Delete('SOUND_GAME_SWITCH0');
1922 goodsnd[0].Free();
1923 goodsnd[1].Free();
1924 goodsnd[2].Free();
1925 goodsnd[3].Free();
1927 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
1928 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
1929 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
1930 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
1932 killsnd[0].Free();
1933 killsnd[1].Free();
1934 killsnd[2].Free();
1935 killsnd[3].Free();
1937 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
1938 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
1939 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
1940 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
1942 DataLoaded := False;
1943 end;
1945 procedure DrawCustomStat();
1946 var
1947 pc, x, y, w, _y,
1948 w1, w2, w3,
1949 t, p, m: Integer;
1950 ww1, hh1: Word;
1951 ww2, hh2, r, g, b, rr, gg, bb: Byte;
1952 s1, s2, topstr: String;
1953 begin
1954 e_TextureFontGetSize(gStdFont, ww2, hh2);
1956 e_PollInput();
1957 if e_KeyPressed(IK_TAB) then
1958 begin
1959 if not gStatsPressed then
1960 begin
1961 gStatsOff := not gStatsOff;
1962 gStatsPressed := True;
1963 end;
1964 end
1965 else
1966 gStatsPressed := False;
1968 if gStatsOff then
1969 begin
1970 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
1971 w := (Length(s1) * ww2) div 2;
1972 x := gScreenWidth div 2 - w;
1973 y := 8;
1974 e_TextureFontPrint(x, y, s1, gStdFont);
1975 Exit;
1976 end;
1978 if (gGameSettings.GameMode = GM_COOP) then
1979 begin
1980 if gMissionFailed then
1981 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
1982 else
1983 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
1984 end
1985 else
1986 topstr := _lc[I_MENU_INTER_ROUND_OVER];
1988 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
1989 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
1991 if g_Game_IsNet then
1992 begin
1993 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
1994 if not gChatShow then
1995 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
1996 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
1997 end;
1999 if g_Game_IsClient then
2000 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2001 else
2002 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2003 if not gChatShow then
2004 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2005 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2007 x := 32;
2008 y := 16+hh1+16;
2010 w := gScreenWidth-x*2;
2012 w2 := (w-16) div 6;
2013 w3 := w2;
2014 w1 := w-16-w2-w3;
2016 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2017 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2019 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2021 case CustomStat.GameMode of
2022 GM_DM:
2023 begin
2024 if gGameSettings.MaxLives = 0 then
2025 s1 := _lc[I_GAME_DM]
2026 else
2027 s1 := _lc[I_GAME_LMS];
2028 end;
2029 GM_TDM:
2030 begin
2031 if gGameSettings.MaxLives = 0 then
2032 s1 := _lc[I_GAME_TDM]
2033 else
2034 s1 := _lc[I_GAME_TLMS];
2035 end;
2036 GM_CTF: s1 := _lc[I_GAME_CTF];
2037 GM_COOP:
2038 begin
2039 if gGameSettings.MaxLives = 0 then
2040 s1 := _lc[I_GAME_COOP]
2041 else
2042 s1 := _lc[I_GAME_SURV];
2043 end;
2044 else s1 := '';
2045 end;
2047 _y := y+16;
2048 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2049 _y := _y+8;
2051 _y := _y+16;
2052 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2053 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2055 _y := _y+16;
2056 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2057 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2058 (CustomStat.GameTime div 1000 div 60) mod 60,
2059 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2061 pc := Length(CustomStat.PlayerStat);
2062 if pc = 0 then Exit;
2064 if CustomStat.GameMode = GM_COOP then
2065 begin
2066 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2067 _y := _y+32;
2068 s2 := _lc[I_GAME_MONSTERS];
2069 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2070 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2071 _y := _y+16;
2072 s2 := _lc[I_GAME_SECRETS];
2073 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2074 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2075 if gLastMap then
2076 begin
2077 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2078 _y := _y-16;
2079 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2080 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2081 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2082 _y := _y+16;
2083 s2 := _lc[I_GAME_SECRETS_TOTAL];
2084 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2085 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2086 end;
2087 end;
2089 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2090 begin
2091 _y := _y+16+16;
2093 with CustomStat do
2094 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2095 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2096 else s1 := _lc[I_GAME_WIN_DRAW];
2098 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2099 _y := _y+40;
2101 for t := TEAM_RED to TEAM_BLUE do
2102 begin
2103 if t = TEAM_RED then
2104 begin
2105 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2106 gStdFont, 255, 0, 0, 1);
2107 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2108 gStdFont, 255, 0, 0, 1);
2109 r := 255;
2110 g := 0;
2111 b := 0;
2112 end
2113 else
2114 begin
2115 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2116 gStdFont, 0, 0, 255, 1);
2117 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2118 gStdFont, 0, 0, 255, 1);
2119 r := 0;
2120 g := 0;
2121 b := 255;
2122 end;
2124 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2125 _y := _y+24;
2127 for p := 0 to High(CustomStat.PlayerStat) do
2128 if CustomStat.PlayerStat[p].Team = t then
2129 with CustomStat.PlayerStat[p] do
2130 begin
2131 if Spectator then
2132 begin
2133 rr := r div 2;
2134 gg := g div 2;
2135 bb := b div 2;
2136 end
2137 else
2138 begin
2139 rr := r;
2140 gg := g;
2141 bb := b;
2142 end;
2143 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2144 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2145 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2146 _y := _y+24;
2147 end;
2149 _y := _y+16+16;
2150 end;
2151 end
2152 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2153 begin
2154 _y := _y+40;
2155 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2156 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2157 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2159 _y := _y+24;
2160 for p := 0 to High(CustomStat.PlayerStat) do
2161 with CustomStat.PlayerStat[p] do
2162 begin
2163 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2165 if Spectator then
2166 r := 127
2167 else
2168 r := 255;
2170 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2171 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2172 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2173 _y := _y+24;
2174 end;
2175 end;
2176 end;
2178 procedure DrawSingleStat();
2179 var
2180 tm, key_x, val_x, y: Integer;
2181 w1, w2, h: Word;
2182 s1, s2: String;
2184 procedure player_stat(n: Integer);
2185 var
2186 kpm: Real;
2188 begin
2189 // "Kills: # / #":
2190 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2191 s2 := Format(' %d', [gTotalMonsters]);
2193 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2194 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2195 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2196 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2197 s1 := s1 + '/';
2198 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2199 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2201 // "Kills-per-minute: ##.#":
2202 s1 := _lc[I_MENU_INTER_KPM];
2203 if tm > 0 then
2204 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2205 else
2206 kpm := SingleStat.PlayerStat[n].Kills;
2207 s2 := Format(' %.1f', [kpm]);
2209 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2210 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2212 // "Secrets found: # / #":
2213 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2214 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2216 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2217 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2218 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2219 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2220 s1 := s1 + '/';
2221 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2222 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2223 end;
2225 begin
2226 // "Level Complete":
2227 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2228 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2230 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2231 s1 := _lc[I_MENU_INTER_KPM];
2232 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2233 Inc(w1, 16);
2234 s1 := ' 9999.9';
2235 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2237 key_x := (gScreenWidth-w1-w2) div 2;
2238 val_x := key_x + w1;
2240 // "Time: #:##:##":
2241 tm := SingleStat.GameTime div 1000;
2242 s1 := _lc[I_MENU_INTER_TIME];
2243 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2245 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2246 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2248 if SingleStat.TwoPlayers then
2249 begin
2250 // "Player 1":
2251 s1 := _lc[I_MENU_PLAYER_1];
2252 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2253 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2255 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2256 y := 176;
2257 player_stat(0);
2259 // "Player 2":
2260 s1 := _lc[I_MENU_PLAYER_2];
2261 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2262 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2264 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2265 y := 336;
2266 player_stat(1);
2267 end
2268 else
2269 begin
2270 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2271 y := 128;
2272 player_stat(0);
2273 end;
2274 end;
2276 procedure DrawLoadingStat();
2277 var
2278 ww, hh: Word;
2279 xx, yy, i: Integer;
2280 s: String;
2281 begin
2282 if Length(LoadingStat.Msgs) = 0 then
2283 Exit;
2285 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2286 yy := (gScreenHeight div 3);
2287 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2288 xx := (gScreenWidth div 3);
2290 with LoadingStat do
2291 for i := 0 to NextMsg-1 do
2292 begin
2293 if (i = (NextMsg-1)) and (MaxValue > 0) then
2294 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2295 else
2296 s := Msgs[i];
2298 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2299 yy := yy + LOADING_INTERLINE;
2300 end;
2301 end;
2303 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2304 var
2305 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2306 begin
2307 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2308 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2309 begin
2310 Scale := 1;
2311 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2312 ScaleSz := 16 div Scale;
2313 // Ðàçìåðû ìèíè-êàðòû:
2314 aX := max(gMapInfo.Width div ScaleSz, 1);
2315 aY := max(gMapInfo.Height div ScaleSz, 1);
2316 // Ðàìêà êàðòû:
2317 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2319 if gWalls <> nil then
2320 begin
2321 // Ðèñóåì ñòåíû:
2322 for a := 0 to High(gWalls) do
2323 with gWalls[a] do
2324 if PanelType <> 0 then
2325 begin
2326 // Ëåâûé âåðõíèé óãîë:
2327 aX := X div ScaleSz;
2328 aY := Y div ScaleSz;
2329 // Ðàçìåðû:
2330 aX2 := max(Width div ScaleSz, 1);
2331 aY2 := max(Height div ScaleSz, 1);
2332 // Ïðàâûé íèæíèé óãîë:
2333 aX2 := aX + aX2 - 1;
2334 aY2 := aY + aY2 - 1;
2336 case PanelType of
2337 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2338 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2339 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2340 end;
2341 end;
2342 end;
2343 if gSteps <> nil then
2344 begin
2345 // Ðèñóåì ñòóïåíè:
2346 for a := 0 to High(gSteps) do
2347 with gSteps[a] do
2348 if PanelType <> 0 then
2349 begin
2350 // Ëåâûé âåðõíèé óãîë:
2351 aX := X div ScaleSz;
2352 aY := Y div ScaleSz;
2353 // Ðàçìåðû:
2354 aX2 := max(Width div ScaleSz, 1);
2355 aY2 := max(Height div ScaleSz, 1);
2356 // Ïðàâûé íèæíèé óãîë:
2357 aX2 := aX + aX2 - 1;
2358 aY2 := aY + aY2 - 1;
2360 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2361 end;
2362 end;
2363 if gLifts <> nil then
2364 begin
2365 // Ðèñóåì ëèôòû:
2366 for a := 0 to High(gLifts) do
2367 with gLifts[a] do
2368 if PanelType <> 0 then
2369 begin
2370 // Ëåâûé âåðõíèé óãîë:
2371 aX := X div ScaleSz;
2372 aY := Y div ScaleSz;
2373 // Ðàçìåðû:
2374 aX2 := max(Width div ScaleSz, 1);
2375 aY2 := max(Height div ScaleSz, 1);
2376 // Ïðàâûé íèæíèé óãîë:
2377 aX2 := aX + aX2 - 1;
2378 aY2 := aY + aY2 - 1;
2380 case LiftType of
2381 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2382 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2383 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2384 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2385 end;
2386 end;
2387 end;
2388 if gWater <> nil then
2389 begin
2390 // Ðèñóåì âîäó:
2391 for a := 0 to High(gWater) do
2392 with gWater[a] do
2393 if PanelType <> 0 then
2394 begin
2395 // Ëåâûé âåðõíèé óãîë:
2396 aX := X div ScaleSz;
2397 aY := Y div ScaleSz;
2398 // Ðàçìåðû:
2399 aX2 := max(Width div ScaleSz, 1);
2400 aY2 := max(Height div ScaleSz, 1);
2401 // Ïðàâûé íèæíèé óãîë:
2402 aX2 := aX + aX2 - 1;
2403 aY2 := aY + aY2 - 1;
2405 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2406 end;
2407 end;
2408 if gAcid1 <> nil then
2409 begin
2410 // Ðèñóåì êèñëîòó 1:
2411 for a := 0 to High(gAcid1) do
2412 with gAcid1[a] do
2413 if PanelType <> 0 then
2414 begin
2415 // Ëåâûé âåðõíèé óãîë:
2416 aX := X div ScaleSz;
2417 aY := Y div ScaleSz;
2418 // Ðàçìåðû:
2419 aX2 := max(Width div ScaleSz, 1);
2420 aY2 := max(Height div ScaleSz, 1);
2421 // Ïðàâûé íèæíèé óãîë:
2422 aX2 := aX + aX2 - 1;
2423 aY2 := aY + aY2 - 1;
2425 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2426 end;
2427 end;
2428 if gAcid2 <> nil then
2429 begin
2430 // Ðèñóåì êèñëîòó 2:
2431 for a := 0 to High(gAcid2) do
2432 with gAcid2[a] do
2433 if PanelType <> 0 then
2434 begin
2435 // Ëåâûé âåðõíèé óãîë:
2436 aX := X div ScaleSz;
2437 aY := Y div ScaleSz;
2438 // Ðàçìåðû:
2439 aX2 := max(Width div ScaleSz, 1);
2440 aY2 := max(Height div ScaleSz, 1);
2441 // Ïðàâûé íèæíèé óãîë:
2442 aX2 := aX + aX2 - 1;
2443 aY2 := aY + aY2 - 1;
2445 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2446 end;
2447 end;
2448 if gPlayers <> nil then
2449 begin
2450 // Ðèñóåì èãðîêîâ:
2451 for a := 0 to High(gPlayers) do
2452 if gPlayers[a] <> nil then with gPlayers[a] do
2453 if Live then begin
2454 // Ëåâûé âåðõíèé óãîë:
2455 aX := Obj.X div ScaleSz + 1;
2456 aY := Obj.Y div ScaleSz + 1;
2457 // Ðàçìåðû:
2458 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2459 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2460 // Ïðàâûé íèæíèé óãîë:
2461 aX2 := aX + aX2 - 1;
2462 aY2 := aY + aY2 - 1;
2464 if gPlayers[a] = p then
2465 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2466 else
2467 case Team of
2468 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2469 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2470 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2471 end;
2472 end;
2473 end;
2474 if gMonsters <> nil then
2475 begin
2476 // Ðèñóåì ìîíñòðîâ:
2477 for a := 0 to High(gMonsters) do
2478 if gMonsters[a] <> nil then with gMonsters[a] do
2479 if Live then begin
2480 // Ëåâûé âåðõíèé óãîë:
2481 aX := Obj.X div ScaleSz + 1;
2482 aY := Obj.Y div ScaleSz + 1;
2483 // Ðàçìåðû:
2484 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2485 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2486 // Ïðàâûé íèæíèé óãîë:
2487 aX2 := aX + aX2 - 1;
2488 aY2 := aY + aY2 - 1;
2490 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2491 end;
2492 end;
2493 end;
2494 end;
2496 procedure DrawMapView(x, y, w, h: Integer);
2497 var
2498 bx, by: Integer;
2499 begin
2500 glPushMatrix();
2502 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2503 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2504 g_Map_DrawBack(-bx, -by);
2506 sX := x;
2507 sY := y;
2508 sWidth := w;
2509 sHeight := h;
2511 glTranslatef(-x, -y, 0);
2513 g_Map_DrawPanels(PANEL_BACK);
2514 g_Map_DrawPanels(PANEL_STEP);
2515 g_Items_Draw();
2516 g_Weapon_Draw();
2517 g_Player_DrawShells();
2518 g_Player_DrawAll();
2519 g_Player_DrawCorpses();
2520 g_Map_DrawPanels(PANEL_WALL);
2521 g_Monsters_Draw();
2522 g_Map_DrawPanels(PANEL_CLOSEDOOR);
2523 g_GFX_Draw();
2524 g_Map_DrawFlags();
2525 g_Map_DrawPanels(PANEL_ACID1);
2526 g_Map_DrawPanels(PANEL_ACID2);
2527 g_Map_DrawPanels(PANEL_WATER);
2528 g_Map_DrawPanels(PANEL_FORE);
2529 if g_debug_HealthBar then
2530 begin
2531 g_Monsters_DrawHealth();
2532 g_Player_DrawHealth();
2533 end;
2535 glPopMatrix();
2536 end;
2538 procedure DrawPlayer(p: TPlayer);
2539 var
2540 px, py, a, b, c, d: Integer;
2541 //R: TRect;
2542 begin
2543 if (p = nil) or (p.FDummy) then
2544 begin
2545 glPushMatrix();
2546 g_Map_DrawBack(0, 0);
2547 glPopMatrix();
2548 Exit;
2549 end;
2551 gPlayerDrawn := p;
2553 glPushMatrix();
2555 px := p.GameX + PLAYER_RECT_CX;
2556 py := p.GameY + PLAYER_RECT_CY;
2558 if px > (gPlayerScreenSize.X div 2) then
2559 a := -px + (gPlayerScreenSize.X div 2)
2560 else
2561 a := 0;
2562 if py > (gPlayerScreenSize.Y div 2) then
2563 b := -py + (gPlayerScreenSize.Y div 2)
2564 else
2565 b := 0;
2566 if px > (gMapInfo.Width - (gPlayerScreenSize.X div 2)) then
2567 a := -gMapInfo.Width + gPlayerScreenSize.X;
2568 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2569 b := -gMapInfo.Height + gPlayerScreenSize.Y;
2570 if gMapInfo.Width <= gPlayerScreenSize.X then
2571 a := 0;
2572 if gMapInfo.Height <= gPlayerScreenSize.Y then
2573 b := 0;
2575 if p.IncCam <> 0 then
2576 begin
2577 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2578 begin
2579 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2580 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2581 end;
2583 if py < (gPlayerScreenSize.Y div 2) then
2584 begin
2585 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2586 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2587 end;
2589 if p.IncCam < 0 then
2590 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and
2591 (p.IncCam < 0) do
2592 p.IncCam := p.IncCam + 1;
2594 if p.IncCam > 0 then
2595 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and
2596 (p.IncCam > 0) do
2597 p.IncCam := p.IncCam - 1;
2598 end;
2600 if (px< gPlayerScreenSize.X div 2) or
2601 (gMapInfo.Width-gPlayerScreenSize.X <= 256) then
2602 c := 0
2603 else
2604 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then
2605 c := gBackSize.X - gPlayerScreenSize.X
2606 else
2607 c := Round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2609 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or
2610 (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then
2611 d := 0
2612 else
2613 if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then
2614 d := gBackSize.Y - gPlayerScreenSize.Y
2615 else
2616 d := Round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
2618 g_Map_DrawBack(-c, -d);
2620 sX := -a;
2621 sY := -(b+p.IncCam);
2622 sWidth := gPlayerScreenSize.X;
2623 sHeight := gPlayerScreenSize.Y;
2625 glTranslatef(a, b+p.IncCam, 0);
2627 g_Map_DrawPanels(PANEL_BACK);
2628 g_Map_DrawPanels(PANEL_STEP);
2629 g_Items_Draw();
2630 g_Weapon_Draw();
2631 g_Player_DrawShells();
2632 g_Player_DrawAll();
2633 g_Player_DrawCorpses();
2634 g_Map_DrawPanels(PANEL_WALL);
2635 g_Monsters_Draw();
2636 g_Map_DrawPanels(PANEL_CLOSEDOOR);
2637 g_GFX_Draw();
2638 g_Map_DrawFlags();
2639 g_Map_DrawPanels(PANEL_ACID1);
2640 g_Map_DrawPanels(PANEL_ACID2);
2641 g_Map_DrawPanels(PANEL_WATER);
2642 g_Map_DrawPanels(PANEL_FORE);
2643 if g_debug_HealthBar then
2644 begin
2645 g_Monsters_DrawHealth();
2646 g_Player_DrawHealth();
2647 end;
2649 if p.FSpectator then
2650 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2651 p.GameY + PLAYER_RECT_CY - 4,
2652 'X', gStdFont, 255, 255, 255, 1, True);
2654 for a := 0 to High(gCollideMap) do
2655 for b := 0 to High(gCollideMap[a]) do
2656 begin
2657 d := 0;
2658 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2659 d := d + 1;
2660 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2661 d := d + 2;
2663 case d of
2664 1: e_DrawPoint(1, b, a, 200, 200, 200);
2665 2: e_DrawPoint(1, b, a, 64, 64, 255);
2666 3: e_DrawPoint(1, b, a, 255, 0, 255);
2667 end;
2668 end;
2671 glPopMatrix();
2673 p.DrawPain();
2674 p.DrawPickup();
2675 p.DrawRulez();
2676 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
2677 if g_Debug_Player then
2678 g_Player_DrawDebug(p);
2679 p.DrawGUI();
2680 end;
2682 procedure g_Game_Draw();
2683 var
2684 ID: DWORD;
2685 w, h: Word;
2686 ww, hh: Byte;
2687 Time: Int64;
2688 back: string;
2689 plView1, plView2: TPlayer;
2690 Split: Boolean;
2691 begin
2692 if gExit = EXIT_QUIT then Exit;
2694 Time := GetTimer() {div 1000};
2695 FPSCounter := FPSCounter+1;
2696 if Time - FPSTime >= 1000 then
2697 begin
2698 FPS := FPSCounter;
2699 FPSCounter := 0;
2700 FPSTime := Time;
2701 end;
2703 if gGameOn or (gState = STATE_FOLD) then
2704 begin
2705 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
2706 begin
2707 gSpectMode := SPECT_NONE;
2708 if not gRevertPlayers then
2709 begin
2710 plView1 := gPlayer1;
2711 plView2 := gPlayer2;
2712 end
2713 else
2714 begin
2715 plView1 := gPlayer2;
2716 plView2 := gPlayer1;
2717 end;
2718 end
2719 else
2720 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
2721 begin
2722 gSpectMode := SPECT_NONE;
2723 if gPlayer2 = nil then
2724 plView1 := gPlayer1
2725 else
2726 plView1 := gPlayer2;
2727 plView2 := nil;
2728 end
2729 else
2730 begin
2731 plView1 := nil;
2732 plView2 := nil;
2733 end;
2735 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
2736 gSpectMode := SPECT_STATS;
2738 if gSpectMode = SPECT_PLAYERS then
2739 if gPlayers <> nil then
2740 begin
2741 plView1 := GetActivePlayer_ByID(gSpectPID1);
2742 if plView1 = nil then
2743 begin
2744 gSpectPID1 := GetActivePlayerID_Next();
2745 plView1 := GetActivePlayer_ByID(gSpectPID1);
2746 end;
2747 if gSpectViewTwo then
2748 begin
2749 plView2 := GetActivePlayer_ByID(gSpectPID2);
2750 if plView2 = nil then
2751 begin
2752 gSpectPID2 := GetActivePlayerID_Next();
2753 plView2 := GetActivePlayer_ByID(gSpectPID2);
2754 end;
2755 end;
2756 end;
2758 if gSpectMode = SPECT_MAPVIEW then
2759 begin
2760 // Ðåæèì ïðîñìîòðà êàðòû
2761 Split := False;
2762 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
2763 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
2764 gHearPoint1.Active := True;
2765 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
2766 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
2767 gHearPoint2.Active := False;
2768 end
2769 else
2770 begin
2771 Split := (plView1 <> nil) and (plView2 <> nil);
2773 // Òî÷êè ñëóõà èãðîêîâ
2774 if plView1 <> nil then
2775 begin
2776 gHearPoint1.Active := True;
2777 gHearPoint1.Coords.X := plView1.GameX;
2778 gHearPoint1.Coords.Y := plView1.GameY;
2779 end else
2780 gHearPoint1.Active := False;
2781 if plView2 <> nil then
2782 begin
2783 gHearPoint2.Active := True;
2784 gHearPoint2.Coords.X := plView2.GameX;
2785 gHearPoint2.Coords.Y := plView2.GameY;
2786 end else
2787 gHearPoint2.Active := False;
2789 // Ðàçìåð ýêðàíîâ èãðîêîâ:
2790 gPlayerScreenSize.X := gScreenWidth-196;
2791 if Split then
2792 begin
2793 gPlayerScreenSize.Y := gScreenHeight div 2;
2794 if gScreenHeight mod 2 = 0 then
2795 Dec(gPlayerScreenSize.Y);
2796 end
2797 else
2798 gPlayerScreenSize.Y := gScreenHeight;
2800 if Split then
2801 if gScreenHeight mod 2 = 0 then
2802 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
2803 else
2804 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
2806 DrawPlayer(plView1);
2807 gPlayer1ScreenCoord.X := sX;
2808 gPlayer1ScreenCoord.Y := sY;
2810 if Split then
2811 begin
2812 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
2814 DrawPlayer(plView2);
2815 gPlayer2ScreenCoord.X := sX;
2816 gPlayer2ScreenCoord.Y := sY;
2817 end;
2819 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
2821 if Split then
2822 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
2823 end;
2825 if MessageText <> '' then
2826 begin
2827 w := 0;
2828 h := 0;
2829 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
2830 if Split then
2831 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
2832 (gScreenHeight div 2)-(h div 2), MessageText)
2833 else
2834 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
2835 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
2836 end;
2838 if IsDrawStat or (gSpectMode = 1) then DrawStat();
2840 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
2841 begin
2842 // Draw spectator GUI
2843 ww := 0;
2844 hh := 0;
2845 e_TextureFontGetSize(gStdFont, ww, hh);
2846 case gSpectMode of
2847 SPECT_STATS:
2848 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
2849 SPECT_MAPVIEW:
2850 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
2851 SPECT_PLAYERS:
2852 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
2853 end;
2854 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
2855 if gSpectMode = SPECT_MAPVIEW then
2856 begin
2857 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
2858 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
2859 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
2860 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
2861 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
2862 end;
2863 if gSpectMode = SPECT_PLAYERS then
2864 begin
2865 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
2866 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
2867 if gSpectViewTwo then
2868 begin
2869 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
2870 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
2871 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
2872 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
2873 end
2874 else
2875 begin
2876 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
2877 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
2878 end;
2879 end;
2880 end;
2881 end;
2883 if gPause and gGameOn and (g_ActiveWindow = nil) then
2884 begin
2885 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2887 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
2888 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
2889 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
2890 end;
2892 if not gGameOn then
2893 begin
2894 if (gState = STATE_MENU) then
2895 begin
2896 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
2897 begin
2898 if g_Texture_Get('MENU_BACKGROUND', ID) then
2899 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
2900 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2901 end;
2902 if g_ActiveWindow <> nil then
2903 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2904 end;
2906 if gState = STATE_FOLD then
2907 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
2909 if gState = STATE_INTERCUSTOM then
2910 begin
2911 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
2912 begin
2913 back := 'TEXTURE_endpic';
2914 if not g_Texture_Get(back, ID) then
2915 back := _lc[I_TEXTURE_ENDPIC];
2916 end
2917 else
2918 back := 'INTER';
2920 if g_Texture_Get(back, ID) then
2921 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
2922 else
2923 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2925 DrawCustomStat();
2927 if g_ActiveWindow <> nil then
2928 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2929 end;
2931 if gState = STATE_INTERSINGLE then
2932 begin
2933 if EndingGameCounter > 0 then
2934 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter)
2935 else
2936 begin
2937 back := 'INTER';
2939 if g_Texture_Get(back, ID) then
2940 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
2941 else
2942 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2944 DrawSingleStat();
2946 if g_ActiveWindow <> nil then
2947 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2948 end;
2949 end;
2951 if gState = STATE_ENDPIC then
2952 begin
2953 ID := DWORD(-1);
2954 if not g_Texture_Get('TEXTURE_endpic', ID) then
2955 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
2957 if ID <> DWORD(-1) then
2958 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
2959 else
2960 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2962 if g_ActiveWindow <> nil then
2963 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2964 end;
2966 if gState = STATE_SLIST then
2967 begin
2968 if g_Texture_Get('MENU_BACKGROUND', ID) then
2969 begin
2970 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
2971 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2972 end;
2973 g_Serverlist_Draw(slCurrent);
2974 end;
2975 end;
2977 if g_ActiveWindow <> nil then
2978 begin
2979 if gGameOn then
2980 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2981 g_ActiveWindow.Draw();
2982 end;
2984 g_Console_Draw();
2986 if g_debug_Sounds and gGameOn then
2987 begin
2988 for w := 0 to High(e_SoundsArray) do
2989 for h := 0 to e_SoundsArray[w].nRefs do
2990 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
2991 end;
2993 if gShowFPS then
2994 begin
2995 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
2996 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
2997 end;
2999 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3000 e_TextureFontPrint(gScreenWidth-72, 0,
3001 Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]),
3002 gStdFont);
3003 end;
3005 procedure g_Game_Quit();
3006 begin
3007 g_Game_StopAllSounds(True);
3008 gMusic.Free();
3009 g_Game_SaveOptions();
3010 g_Game_FreeData();
3011 g_PlayerModel_FreeData();
3012 g_Texture_DeleteAll();
3013 g_Frames_DeleteAll();
3014 g_Menu_Free();
3016 if NetInitDone then g_Net_Free;
3018 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3019 if gMapToDelete <> '' then
3020 g_Game_DeleteTestMap();
3022 gExit := EXIT_QUIT;
3023 PushExitEvent();
3024 end;
3026 procedure g_FatalError(Text: String);
3027 begin
3028 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3029 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3031 gExit := EXIT_SIMPLE;
3032 end;
3034 procedure g_SimpleError(Text: String);
3035 begin
3036 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3037 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3038 end;
3040 procedure g_Game_SetupScreenSize();
3041 var
3042 d: Single;
3043 begin
3044 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3045 gPlayerScreenSize.X := gScreenWidth-196;
3046 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3047 gPlayerScreenSize.Y := gScreenHeight div 2
3048 else
3049 gPlayerScreenSize.Y := gScreenHeight;
3051 // Ðàçìåð çàäíåãî ïëàíà:
3052 if BackID <> DWORD(-1) then
3053 begin
3054 d := SKY_STRETCH;
3056 if (gScreenWidth*d > gMapInfo.Width) or
3057 (gScreenHeight*d > gMapInfo.Height) then
3058 d := 1.0;
3060 gBackSize.X := Round(gScreenWidth*d);
3061 gBackSize.Y := Round(gScreenHeight*d);
3062 end;
3063 end;
3065 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3066 begin
3067 g_Window_SetSize(newWidth, newHeight, nowFull);
3068 end;
3070 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3071 begin
3072 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3073 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3074 Exit;
3075 if gPlayer1 = nil then
3076 begin
3077 if g_Game_IsClient then
3078 begin
3079 if NetPlrUID1 > -1 then
3080 begin
3081 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3082 gPlayer1 := g_Player_Get(NetPlrUID1);
3083 end;
3084 Exit;
3085 end;
3087 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3088 Team := gPlayer1Settings.Team;
3090 // Ñîçäàíèå ïåðâîãî èãðîêà:
3091 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3092 gPlayer1Settings.Color,
3093 Team, False));
3094 if gPlayer1 = nil then
3095 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3096 else
3097 begin
3098 gPlayer1.Name := gPlayer1Settings.Name;
3099 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3100 if g_Game_IsServer and g_Game_IsNet then
3101 MH_SEND_PlayerCreate(gPlayer1.UID);
3102 gPlayer1.Respawn(False, True);
3104 if g_Game_IsNet and NetUseMaster then
3105 g_Net_Slist_Update;
3106 end;
3108 Exit;
3109 end;
3110 if gPlayer2 = nil then
3111 begin
3112 if g_Game_IsClient then
3113 begin
3114 if NetPlrUID2 > -1 then
3115 gPlayer2 := g_Player_Get(NetPlrUID2);
3116 Exit;
3117 end;
3119 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3120 Team := gPlayer2Settings.Team;
3122 // Ñîçäàíèå âòîðîãî èãðîêà:
3123 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3124 gPlayer2Settings.Color,
3125 Team, False));
3126 if gPlayer2 = nil then
3127 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3128 else
3129 begin
3130 gPlayer2.Name := gPlayer2Settings.Name;
3131 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3132 if g_Game_IsServer and g_Game_IsNet then
3133 MH_SEND_PlayerCreate(gPlayer2.UID);
3134 gPlayer2.Respawn(False, True);
3136 if g_Game_IsNet and NetUseMaster then
3137 g_Net_Slist_Update;
3138 end;
3140 Exit;
3141 end;
3142 end;
3144 procedure g_Game_RemovePlayer();
3145 var
3146 Pl: TPlayer;
3147 begin
3148 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3149 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3150 Exit;
3151 Pl := gPlayer2;
3152 if Pl <> nil then
3153 begin
3154 if g_Game_IsServer then
3155 begin
3156 Pl.Lives := 0;
3157 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3158 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3159 g_Player_Remove(Pl.UID);
3161 if g_Game_IsNet and NetUseMaster then
3162 g_Net_Slist_Update;
3163 end else
3164 gPlayer2 := nil;
3165 Exit;
3166 end;
3167 Pl := gPlayer1;
3168 if Pl <> nil then
3169 begin
3170 if g_Game_IsServer then
3171 begin
3172 Pl.Lives := 0;
3173 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3174 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3175 g_Player_Remove(Pl.UID);
3177 if g_Game_IsNet and NetUseMaster then
3178 g_Net_Slist_Update;
3179 end else
3180 begin
3181 gPlayer1 := nil;
3182 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3183 end;
3184 Exit;
3185 end;
3186 end;
3188 procedure g_Game_Spectate();
3189 begin
3190 g_Game_RemovePlayer();
3191 if gPlayer1 <> nil then
3192 g_Game_RemovePlayer();
3193 end;
3195 procedure g_Game_SpectateCenterView();
3196 begin
3197 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3198 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3199 end;
3201 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3202 var
3203 i, nPl: Integer;
3204 begin
3205 g_Game_Free();
3207 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3209 g_Game_ClearLoading();
3211 // Íàñòðîéêè èãðû:
3212 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3213 gAimLine := False;
3214 gShowMap := False;
3215 gGameSettings.GameType := GT_SINGLE;
3216 gGameSettings.MaxLives := 0;
3217 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3218 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3219 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3220 gSwitchGameMode := GM_SINGLE;
3222 g_Game_ExecuteEvent('ongamestart');
3224 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3225 g_Game_SetupScreenSize();
3227 // Ñîçäàíèå ïåðâîãî èãðîêà:
3228 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3229 gPlayer1Settings.Color,
3230 gPlayer1Settings.Team, False));
3231 if gPlayer1 = nil then
3232 begin
3233 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3234 Exit;
3235 end;
3237 gPlayer1.Name := gPlayer1Settings.Name;
3238 nPl := 1;
3240 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3241 if TwoPlayers then
3242 begin
3243 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3244 gPlayer2Settings.Color,
3245 gPlayer2Settings.Team, False));
3246 if gPlayer2 = nil then
3247 begin
3248 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3249 Exit;
3250 end;
3252 gPlayer2.Name := gPlayer2Settings.Name;
3253 Inc(nPl);
3254 end;
3256 // Çàãðóçêà è çàïóñê êàðòû:
3257 if not g_Game_StartMap(MAP, True) then
3258 begin
3259 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3260 Exit;
3261 end;
3263 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3264 g_Player_Init();
3266 // Ñîçäàåì áîòîâ:
3267 for i := nPl+1 to nPlayers do
3268 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3269 end;
3271 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3272 TimeLimit, GoalLimit: Word;
3273 MaxLives: Byte;
3274 Options: LongWord; nPlayers: Byte);
3275 var
3276 i, nPl: Integer;
3277 begin
3278 g_Game_Free();
3280 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3282 g_Game_ClearLoading();
3284 // Íàñòðîéêè èãðû:
3285 gGameSettings.GameType := GT_CUSTOM;
3286 gGameSettings.GameMode := GameMode;
3287 gSwitchGameMode := GameMode;
3288 gGameSettings.TimeLimit := TimeLimit;
3289 gGameSettings.GoalLimit := GoalLimit;
3290 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3291 gGameSettings.Options := Options;
3293 gCoopTotalMonstersKilled := 0;
3294 gCoopTotalSecretsFound := 0;
3295 gCoopTotalMonsters := 0;
3296 gCoopTotalSecrets := 0;
3297 gAimLine := False;
3298 gShowMap := False;
3300 g_Game_ExecuteEvent('ongamestart');
3302 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3303 g_Game_SetupScreenSize();
3305 // Ðåæèì íàáëþäàòåëÿ:
3306 if nPlayers = 0 then
3307 begin
3308 gPlayer1 := nil;
3309 gPlayer2 := nil;
3310 end;
3312 nPl := 0;
3313 if nPlayers >= 1 then
3314 begin
3315 // Ñîçäàíèå ïåðâîãî èãðîêà:
3316 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3317 gPlayer1Settings.Color,
3318 gPlayer1Settings.Team, False));
3319 if gPlayer1 = nil then
3320 begin
3321 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3322 Exit;
3323 end;
3325 gPlayer1.Name := gPlayer1Settings.Name;
3326 Inc(nPl);
3327 end;
3329 if nPlayers >= 2 then
3330 begin
3331 // Ñîçäàíèå âòîðîãî èãðîêà:
3332 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3333 gPlayer2Settings.Color,
3334 gPlayer2Settings.Team, False));
3335 if gPlayer2 = nil then
3336 begin
3337 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3338 Exit;
3339 end;
3341 gPlayer2.Name := gPlayer2Settings.Name;
3342 Inc(nPl);
3343 end;
3345 // Çàãðóçêà è çàïóñê êàðòû:
3346 if not g_Game_StartMap(Map, True) then
3347 begin
3348 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3349 Exit;
3350 end;
3352 // Íåò òî÷åê ïîÿâëåíèÿ:
3353 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3354 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3355 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3356 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3357 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3358 begin
3359 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3360 Exit;
3361 end;
3363 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3364 g_Player_Init();
3366 // Ñîçäàåì áîòîâ:
3367 for i := nPl+1 to nPlayers do
3368 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3369 end;
3371 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3372 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3373 Options: LongWord; nPlayers: Byte;
3374 IPAddr: LongWord; Port: Word);
3375 begin
3376 g_Game_Free();
3378 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3380 g_Game_ClearLoading();
3382 // Íàñòðîéêè èãðû:
3383 gGameSettings.GameType := GT_SERVER;
3384 gGameSettings.GameMode := GameMode;
3385 gSwitchGameMode := GameMode;
3386 gGameSettings.TimeLimit := TimeLimit;
3387 gGameSettings.GoalLimit := GoalLimit;
3388 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3389 gGameSettings.Options := Options;
3391 gCoopTotalMonstersKilled := 0;
3392 gCoopTotalSecretsFound := 0;
3393 gCoopTotalMonsters := 0;
3394 gCoopTotalSecrets := 0;
3395 gAimLine := False;
3396 gShowMap := False;
3398 g_Game_ExecuteEvent('ongamestart');
3400 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3401 g_Game_SetupScreenSize();
3403 // Ðåæèì íàáëþäàòåëÿ:
3404 if nPlayers = 0 then
3405 begin
3406 gPlayer1 := nil;
3407 gPlayer2 := nil;
3408 end;
3410 if nPlayers >= 1 then
3411 begin
3412 // Ñîçäàíèå ïåðâîãî èãðîêà:
3413 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3414 gPlayer1Settings.Color,
3415 gPlayer1Settings.Team, False));
3416 if gPlayer1 = nil then
3417 begin
3418 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3419 Exit;
3420 end;
3422 gPlayer1.Name := gPlayer1Settings.Name;
3423 end;
3425 if nPlayers >= 2 then
3426 begin
3427 // Ñîçäàíèå âòîðîãî èãðîêà:
3428 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3429 gPlayer2Settings.Color,
3430 gPlayer2Settings.Team, False));
3431 if gPlayer2 = nil then
3432 begin
3433 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3434 Exit;
3435 end;
3437 gPlayer2.Name := gPlayer2Settings.Name;
3438 end;
3440 // Ñòàðòóåì ñåðâåð
3441 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3442 begin
3443 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3444 Exit;
3445 end;
3447 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3449 // Çàãðóçêà è çàïóñê êàðòû:
3450 if not g_Game_StartMap(Map, True) then
3451 begin
3452 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3453 Exit;
3454 end;
3456 // Íåò òî÷åê ïîÿâëåíèÿ:
3457 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3458 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3459 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3460 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3461 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3462 begin
3463 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3464 Exit;
3465 end;
3467 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3468 g_Player_Init();
3470 NetState := NET_STATE_GAME;
3471 end;
3473 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3474 var
3475 Map: String;
3476 WadName: string;
3477 Ptr: Pointer;
3478 T: Cardinal;
3479 MID: Byte;
3480 State: Byte;
3481 OuterLoop: Boolean;
3482 newResPath: string;
3483 begin
3484 g_Game_Free();
3486 State := 0;
3487 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3488 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3490 g_Game_ClearLoading();
3492 // Íàñòðîéêè èãðû:
3493 gGameSettings.GameType := GT_CLIENT;
3495 gCoopTotalMonstersKilled := 0;
3496 gCoopTotalSecretsFound := 0;
3497 gCoopTotalMonsters := 0;
3498 gCoopTotalSecrets := 0;
3499 gAimLine := False;
3500 gShowMap := False;
3502 g_Game_ExecuteEvent('ongamestart');
3504 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3505 g_Game_SetupScreenSize();
3507 NetState := NET_STATE_AUTH;
3509 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3510 // Ñòàðòóåì êëèåíò
3511 if not g_Net_Connect(Addr, Port) then
3512 begin
3513 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3514 NetState := NET_STATE_NONE;
3515 Exit;
3516 end;
3518 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3519 MC_SEND_Info(PW);
3520 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3522 OuterLoop := True;
3523 while OuterLoop do
3524 begin
3525 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3526 begin
3527 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3528 begin
3529 Ptr := NetEvent.packet^.data;
3530 e_Raw_Seek(0);
3532 MID := e_Raw_Read_Byte(Ptr);
3534 if (MID = NET_MSG_INFO) and (State = 0) then
3535 begin
3536 NetMyID := e_Raw_Read_Byte(Ptr);
3537 NetPlrUID1 := e_Raw_Read_Word(Ptr);
3539 WadName := e_Raw_Read_String(Ptr);
3540 Map := e_Raw_Read_String(Ptr);
3542 gWADHash := e_Raw_Read_MD5(Ptr);
3544 gGameSettings.GameMode := e_Raw_Read_Byte(Ptr);
3545 gSwitchGameMode := gGameSettings.GameMode;
3546 gGameSettings.GoalLimit := e_Raw_Read_Word(Ptr);
3547 gGameSettings.TimeLimit := e_Raw_Read_Word(Ptr);
3548 gGameSettings.MaxLives := e_Raw_Read_Byte(Ptr);
3549 gGameSettings.Options := e_Raw_Read_LongWord(Ptr);
3550 T := e_Raw_Read_LongWord(Ptr);
3552 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3553 if newResPath = '' then
3554 begin
3555 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3556 newResPath := g_Res_DownloadWAD(WadName);
3557 if newResPath = '' then
3558 begin
3559 g_FatalError(_lc[I_NET_ERR_HASH]);
3560 enet_packet_destroy(NetEvent.packet);
3561 NetState := NET_STATE_NONE;
3562 Exit;
3563 end;
3564 end;
3565 newResPath := ExtractRelativePath(MapsDir, newResPath);
3567 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3568 gPlayer1Settings.Color,
3569 gPlayer1Settings.Team, False));
3571 if gPlayer1 = nil then
3572 begin
3573 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3575 enet_packet_destroy(NetEvent.packet);
3576 NetState := NET_STATE_NONE;
3577 Exit;
3578 end;
3580 gPlayer1.Name := gPlayer1Settings.Name;
3581 gPlayer1.UID := NetPlrUID1;
3582 gPlayer1.Reset(True);
3584 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3585 begin
3586 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3588 enet_packet_destroy(NetEvent.packet);
3589 NetState := NET_STATE_NONE;
3590 Exit;
3591 end;
3593 gTime := T;
3595 State := 1;
3596 OuterLoop := False;
3597 enet_packet_destroy(NetEvent.packet);
3598 break;
3599 end
3600 else
3601 enet_packet_destroy(NetEvent.packet);
3602 end
3603 else
3604 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3605 begin
3606 State := 0;
3607 if (NetEvent.data <= NET_DISC_MAX) then
3608 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3609 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3610 OuterLoop := False;
3611 Break;
3612 end;
3613 end;
3615 ProcessLoading();
3617 e_PollInput();
3619 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3620 begin
3621 State := 0;
3622 break;
3623 end;
3624 end;
3626 if State <> 1 then
3627 begin
3628 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3629 NetState := NET_STATE_NONE;
3630 Exit;
3631 end;
3633 gLMSRespawn := LMS_RESPAWN_NONE;
3634 gLMSRespawnTime := 0;
3636 g_Player_Init();
3637 NetState := NET_STATE_GAME;
3638 MC_SEND_FullStateRequest;
3639 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3640 end;
3642 procedure g_Game_SaveOptions();
3643 begin
3644 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3645 end;
3647 procedure g_Game_ChangeMap(MapPath: String);
3648 var
3649 Force: Boolean;
3650 begin
3651 g_Game_ClearLoading();
3653 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3654 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3655 if gExitByTrigger then
3656 begin
3657 Force := False;
3658 gExitByTrigger := False;
3659 end;
3660 if not g_Game_StartMap(MapPath, Force) then
3661 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3662 end;
3664 procedure g_Game_Restart();
3665 var
3666 Map: string;
3667 begin
3668 if g_Game_IsClient then
3669 Exit;
3670 map := g_ExtractFileName(gMapInfo.Map);
3672 MessageTime := 0;
3673 gGameOn := False;
3674 g_Game_ClearLoading();
3675 g_Game_StartMap(Map, True);
3676 end;
3678 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
3679 var
3680 NewWAD, ResName: String;
3681 I: Integer;
3682 begin
3683 g_Map_Free();
3684 g_Player_RemoveAllCorpses();
3686 if (not g_Game_IsClient) and
3687 (gSwitchGameMode <> gGameSettings.GameMode) and
3688 (gGameSettings.GameMode <> GM_SINGLE) then
3689 begin
3690 if gSwitchGameMode = GM_CTF then
3691 gGameSettings.MaxLives := 0;
3692 gGameSettings.GameMode := gSwitchGameMode;
3693 Force := True;
3694 end else
3695 gSwitchGameMode := gGameSettings.GameMode;
3697 g_Player_ResetTeams();
3699 if Pos(':\', Map) > 0 then
3700 begin
3701 NewWAD := g_ExtractWadName(Map);
3702 ResName := g_ExtractFileName(Map);
3703 if g_Game_IsServer then
3704 begin
3705 gWADHash := MD5File(MapsDir + NewWAD);
3706 g_Game_LoadWAD(NewWAD);
3707 end else
3708 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
3709 g_Game_ClientWAD(NewWAD, gWADHash);
3710 end else
3711 ResName := Map;
3713 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
3714 if Result then
3715 begin
3716 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
3718 gState := STATE_NONE;
3719 g_ActiveWindow := nil;
3720 gGameOn := True;
3722 DisableCheats();
3723 ResetTimer();
3725 if gGameSettings.GameMode = GM_CTF then
3726 begin
3727 g_Map_ResetFlag(FLAG_RED);
3728 g_Map_ResetFlag(FLAG_BLUE);
3729 // CTF, à ôëàãîâ íåò:
3730 if not g_Map_HaveFlagPoints() then
3731 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
3732 end;
3733 end
3734 else
3735 begin
3736 gState := STATE_MENU;
3737 gGameOn := False;
3738 end;
3740 gExit := 0;
3741 gPause := False;
3742 gTime := 0;
3743 NetTimeToUpdate := 1;
3744 NetTimeToReliable := 0;
3745 NetTimeToMaster := NetMasterRate;
3746 gLMSRespawn := LMS_RESPAWN_NONE;
3747 gLMSRespawnTime := 0;
3748 gMissionFailed := False;
3749 gNextMap := '';
3751 gCoopMonstersKilled := 0;
3752 gCoopSecretsFound := 0;
3754 gVoteInProgress := False;
3755 gVotePassed := False;
3756 gVoteCount := 0;
3757 gVoted := False;
3759 gStatsOff := False;
3761 if not gGameOn then Exit;
3763 g_Game_SpectateCenterView();
3765 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
3766 begin
3767 gLMSRespawn := LMS_RESPAWN_WARMUP;
3768 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
3769 gLMSSoftSpawn := True;
3770 if NetMode = NET_SERVER then
3771 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
3772 else
3773 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
3774 end;
3776 if NetMode = NET_SERVER then
3777 begin
3778 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
3780 // Ìàñòåðñåðâåð
3781 if NetUseMaster then
3782 begin
3783 if (NetMHost = nil) or (NetMPeer = nil) then
3784 if not g_Net_Slist_Connect then
3785 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
3787 g_Net_Slist_Update;
3788 end;
3790 if NetClients <> nil then
3791 for I := 0 to High(NetClients) do
3792 if NetClients[I].Used then
3793 begin
3794 NetClients[I].Voted := False;
3795 if NetClients[I].RequestedFullUpdate then
3796 begin
3797 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
3798 NetClients[I].RequestedFullUpdate := False;
3799 end;
3800 end;
3802 g_Net_UnbanNonPermHosts();
3803 end;
3805 if gLastMap then
3806 begin
3807 gCoopTotalMonstersKilled := 0;
3808 gCoopTotalSecretsFound := 0;
3809 gCoopTotalMonsters := 0;
3810 gCoopTotalSecrets := 0;
3811 gLastMap := False;
3812 end;
3814 g_Game_ExecuteEvent('onmapstart');
3815 end;
3817 procedure SetFirstLevel();
3818 begin
3819 gNextMap := '';
3821 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
3822 if MapList = nil then
3823 Exit;
3825 SortSArray(MapList);
3826 gNextMap := MapList[Low(MapList)];
3828 MapList := nil;
3829 end;
3831 procedure g_Game_ExitLevel(Map: Char16);
3832 begin
3833 gNextMap := Map;
3835 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
3836 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
3837 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
3838 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
3840 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
3841 if gGameSettings.GameType = GT_SINGLE then
3842 gExit := EXIT_ENDLEVELSINGLE
3843 else // Âûøëè â âûõîä â Ñâîåé èãðå
3844 begin
3845 gExit := EXIT_ENDLEVELCUSTOM;
3846 if gGameSettings.GameMode = GM_COOP then
3847 g_Player_RememberAll;
3849 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
3850 begin
3851 gLastMap := True;
3852 if gGameSettings.GameMode = GM_COOP then
3853 gStatsOff := True;
3855 gStatsPressed := True;
3856 gNextMap := 'MAP01';
3858 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
3859 g_Game_NextLevel;
3861 if g_Game_IsNet then
3862 begin
3863 MH_SEND_GameStats();
3864 MH_SEND_CoopStats();
3865 end;
3866 end;
3867 end;
3868 end;
3870 procedure g_Game_RestartLevel();
3871 var
3872 Map: string;
3873 begin
3874 if gGameSettings.GameMode = GM_SINGLE then
3875 begin
3876 g_Game_Restart();
3877 Exit;
3878 end;
3879 gExit := EXIT_ENDLEVELCUSTOM;
3880 Map := g_ExtractFileName(gMapInfo.Map);
3881 gNextMap := Map;
3882 end;
3884 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
3885 var
3886 gWAD: String;
3887 begin
3888 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
3889 Exit;
3890 if not g_Game_IsClient then
3891 Exit;
3892 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
3893 if gWAD = '' then
3894 begin
3895 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3896 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
3897 if gWAD = '' then
3898 begin
3899 g_Game_Free();
3900 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
3901 Exit;
3902 end;
3903 end;
3904 NewWAD := ExtractRelativePath(MapsDir, gWAD);
3905 g_Game_LoadWAD(NewWAD);
3906 end;
3908 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
3909 var
3910 i, n, nb, nr: Integer;
3911 begin
3912 if not g_Game_IsServer then Exit;
3913 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
3914 gLMSRespawn := LMS_RESPAWN_NONE;
3915 gLMSRespawnTime := 0;
3916 MessageTime := 0;
3918 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
3919 begin
3920 gMissionFailed := True;
3921 g_Game_RestartLevel;
3922 Exit;
3923 end;
3925 n := 0; nb := 0; nr := 0;
3926 for i := Low(gPlayers) to High(gPlayers) do
3927 if (gPlayers[i] <> nil) and
3928 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
3929 (gPlayers[i] is TBot)) then
3930 begin
3931 Inc(n);
3932 if gPlayers[i].Team = TEAM_RED then Inc(nr)
3933 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
3934 end;
3936 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
3937 begin
3938 // wait a second until the fuckers finally decide to join
3939 gLMSRespawn := LMS_RESPAWN_WARMUP;
3940 gLMSRespawnTime := gTime + 1000;
3941 gLMSSoftSpawn := NoMapRestart;
3942 Exit;
3943 end;
3945 g_Player_RemoveAllCorpses;
3946 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
3947 if g_Game_IsNet then
3948 MH_SEND_GameEvent(NET_EV_LMS_START);
3950 for i := Low(gPlayers) to High(gPlayers) do
3951 begin
3952 if gPlayers[i] = nil then continue;
3953 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
3954 // don't touch normal spectators
3955 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
3956 begin
3957 gPlayers[i].FNoRespawn := True;
3958 gPlayers[i].Lives := 0;
3959 if g_Game_IsNet then
3960 MH_SEND_PlayerStats(gPlayers[I].UID);
3961 continue;
3962 end;
3963 gPlayers[i].FNoRespawn := False;
3964 gPlayers[i].Lives := gGameSettings.MaxLives;
3965 gPlayers[i].Respawn(False, True);
3966 if gGameSettings.GameMode = GM_COOP then
3967 begin
3968 gPlayers[i].Frags := 0;
3969 gPlayers[i].RecallState;
3970 end;
3971 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
3972 gPlayer1 := g_Player_Get(gLMSPID1);
3973 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
3974 gPlayer2 := g_Player_Get(gLMSPID2);
3975 end;
3977 for i := Low(gItems) to High(gItems) do
3978 begin
3979 if gItems[i].Respawnable then
3980 begin
3981 gItems[i].QuietRespawn := True;
3982 gItems[i].RespawnTime := 0;
3983 end
3984 else
3985 begin
3986 g_Items_Remove(i);
3987 if g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
3988 end;
3989 end;
3991 for i := Low(gMonsters) to High(gMonsters) do
3992 begin
3993 if (gMonsters[i] <> nil) and not gMonsters[i].FNoRespawn then
3994 gMonsters[i].Respawn;
3995 end;
3997 gLMSSoftSpawn := False;
3998 end;
4000 function g_Game_GetFirstMap(WAD: String): String;
4001 begin
4002 Result := '';
4004 MapList := g_Map_GetMapsList(WAD);
4005 if MapList = nil then
4006 Exit;
4008 SortSArray(MapList);
4009 Result := MapList[Low(MapList)];
4011 if not g_Map_Exist(WAD + ':\' + Result) then
4012 Result := '';
4014 MapList := nil;
4015 end;
4017 function g_Game_GetNextMap(): String;
4018 var
4019 I: Integer;
4020 Map: string;
4021 begin
4022 Result := '';
4024 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4025 if MapList = nil then
4026 Exit;
4028 Map := g_ExtractFileName(gMapInfo.Map);
4030 SortSArray(MapList);
4031 MapIndex := -255;
4032 for I := Low(MapList) to High(MapList) do
4033 if Map = MapList[I] then
4034 begin
4035 MapIndex := I;
4036 Break;
4037 end;
4039 if MapIndex <> -255 then
4040 begin
4041 if MapIndex = High(MapList) then
4042 Result := MapList[Low(MapList)]
4043 else
4044 Result := MapList[MapIndex + 1];
4046 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4047 end;
4049 MapList := nil;
4050 end;
4052 procedure g_Game_NextLevel();
4053 begin
4054 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4055 gExit := EXIT_ENDLEVELCUSTOM
4056 else
4057 begin
4058 gExit := EXIT_ENDLEVELSINGLE;
4059 Exit;
4060 end;
4062 if gNextMap <> '' then Exit;
4063 gNextMap := g_Game_GetNextMap();
4064 end;
4066 function g_Game_IsTestMap(): Boolean;
4067 begin
4068 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4069 end;
4071 procedure g_Game_DeleteTestMap();
4072 var
4073 a: Integer;
4074 MapName: Char16;
4075 WadName: string;
4077 WAD: TWADFile;
4078 MapList: SArray;
4079 time: Integer;
4081 begin
4082 a := Pos('.wad:\', gMapToDelete);
4083 if a = 0 then
4084 Exit;
4086 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4087 WadName := Copy(gMapToDelete, 1, a + 3);
4088 Delete(gMapToDelete, 1, a + 5);
4089 gMapToDelete := UpperCase(gMapToDelete);
4090 MapName := '';
4091 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4094 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4095 if MapName <> TEST_MAP_NAME then
4096 Exit;
4098 if not gTempDelete then
4099 begin
4100 time := g_GetFileTime(WadName);
4101 WAD := TWADFile.Create();
4103 // ×èòàåì Wad-ôàéë:
4104 if not WAD.ReadFile(WadName) then
4105 begin // Íåò òàêîãî WAD-ôàéëà
4106 WAD.Free();
4107 Exit;
4108 end;
4110 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4111 WAD.CreateImage();
4112 MapList := WAD.GetResourcesList('');
4114 if MapList <> nil then
4115 for a := 0 to High(MapList) do
4116 if MapList[a] = MapName then
4117 begin
4118 // Óäàëÿåì è ñîõðàíÿåì:
4119 WAD.RemoveResource('', MapName);
4120 WAD.SaveTo(WadName);
4121 Break;
4122 end;
4124 WAD.Free();
4125 g_SetFileTime(WadName, time);
4126 end else
4128 if gTempDelete then DeleteFile(WadName);
4129 end;
4131 procedure GameCVars(P: SArray);
4132 var
4133 a, b: Integer;
4134 stat: TPlayerStatArray;
4135 cmd, s: string;
4136 config: TConfig;
4137 begin
4138 stat := nil;
4139 cmd := LowerCase(P[0]);
4140 if cmd = 'r_showfps' then
4141 begin
4142 if (Length(P) > 1) and
4143 ((P[1] = '1') or (P[1] = '0')) then
4144 gShowFPS := (P[1][1] = '1');
4146 if gShowFPS then
4147 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4148 else
4149 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4150 end
4151 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4152 begin
4153 with gGameSettings do
4154 begin
4155 if (Length(P) > 1) and
4156 ((P[1] = '1') or (P[1] = '0')) then
4157 begin
4158 if (P[1][1] = '1') then
4159 Options := Options or GAME_OPTION_TEAMDAMAGE
4160 else
4161 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4162 end;
4164 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4165 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4166 else
4167 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4169 if g_Game_IsNet then MH_SEND_GameSettings;
4170 end;
4171 end
4172 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4173 begin
4174 with gGameSettings do
4175 begin
4176 if (Length(P) > 1) and
4177 ((P[1] = '1') or (P[1] = '0')) then
4178 begin
4179 if (P[1][1] = '1') then
4180 Options := Options or GAME_OPTION_WEAPONSTAY
4181 else
4182 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4183 end;
4185 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4186 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4187 else
4188 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4190 if g_Game_IsNet then MH_SEND_GameSettings;
4191 end;
4192 end
4193 else if cmd = 'g_gamemode' then
4194 begin
4195 a := g_Game_TextToMode(P[1]);
4196 if a = GM_SINGLE then a := GM_COOP;
4197 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4198 begin
4199 gSwitchGameMode := a;
4200 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4201 (gState = STATE_INTERSINGLE) then
4202 gSwitchGameMode := GM_SINGLE;
4203 if not gGameOn then
4204 gGameSettings.GameMode := gSwitchGameMode;
4205 end;
4206 if gSwitchGameMode = gGameSettings.GameMode then
4207 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4208 [g_Game_ModeToText(gGameSettings.GameMode)]))
4209 else
4210 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4211 [g_Game_ModeToText(gGameSettings.GameMode),
4212 g_Game_ModeToText(gSwitchGameMode)]));
4213 end
4214 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4215 begin
4216 with gGameSettings do
4217 begin
4218 if (Length(P) > 1) and
4219 ((P[1] = '1') or (P[1] = '0')) then
4220 begin
4221 if (P[1][1] = '1') then
4222 Options := Options or GAME_OPTION_ALLOWEXIT
4223 else
4224 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4225 end;
4227 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4228 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4229 else
4230 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4231 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4233 if g_Game_IsNet then MH_SEND_GameSettings;
4234 end;
4235 end
4236 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4237 begin
4238 with gGameSettings do
4239 begin
4240 if (Length(P) > 1) and
4241 ((P[1] = '1') or (P[1] = '0')) then
4242 begin
4243 if (P[1][1] = '1') then
4244 Options := Options or GAME_OPTION_MONSTERS
4245 else
4246 Options := Options and (not GAME_OPTION_MONSTERS);
4247 end;
4249 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4250 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4251 else
4252 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4253 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4255 if g_Game_IsNet then MH_SEND_GameSettings;
4256 end;
4257 end
4258 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4259 begin
4260 with gGameSettings do
4261 begin
4262 if (Length(P) > 1) and
4263 ((P[1] = '1') or (P[1] = '0')) then
4264 begin
4265 if (P[1][1] = '1') then
4266 Options := Options or GAME_OPTION_BOTVSPLAYER
4267 else
4268 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4269 end;
4271 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4272 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4273 else
4274 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4276 if g_Game_IsNet then MH_SEND_GameSettings;
4277 end;
4278 end
4279 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4280 begin
4281 with gGameSettings do
4282 begin
4283 if (Length(P) > 1) and
4284 ((P[1] = '1') or (P[1] = '0')) then
4285 begin
4286 if (P[1][1] = '1') then
4287 Options := Options or GAME_OPTION_BOTVSMONSTER
4288 else
4289 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4290 end;
4292 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4293 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4294 else
4295 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4297 if g_Game_IsNet then MH_SEND_GameSettings;
4298 end;
4299 end
4300 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4301 begin
4302 if Length(P) > 1 then
4303 begin
4304 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4305 gGameSettings.WarmupTime := 30
4306 else
4307 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4308 end;
4310 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4311 [gGameSettings.WarmupTime]));
4312 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4313 end
4314 else if cmd = 'net_interp' then
4315 begin
4316 if (Length(P) > 1) then
4317 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4319 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4320 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4321 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4322 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4323 config.Free();
4324 end
4325 else if cmd = 'net_forceplayerupdate' then
4326 begin
4327 if (Length(P) > 1) and
4328 ((P[1] = '1') or (P[1] = '0')) then
4329 NetForcePlayerUpdate := (P[1][1] = '1');
4331 if NetForcePlayerUpdate then
4332 g_Console_Add('net_forceplayerupdate = 1')
4333 else
4334 g_Console_Add('net_forceplayerupdate = 0');
4335 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4336 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4337 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4338 config.Free();
4339 end
4340 else if cmd = 'net_predictself' then
4341 begin
4342 if (Length(P) > 1) and
4343 ((P[1] = '1') or (P[1] = '0')) then
4344 NetPredictSelf := (P[1][1] = '1');
4346 if NetPredictSelf then
4347 g_Console_Add('net_predictself = 1')
4348 else
4349 g_Console_Add('net_predictself = 0');
4350 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4351 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4352 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4353 config.Free();
4354 end
4355 else if cmd = 'sv_name' then
4356 begin
4357 if (Length(P) > 1) and (Length(P[1]) > 0) then
4358 begin
4359 NetServerName := P[1];
4360 if Length(NetServerName) > 64 then
4361 SetLength(NetServerName, 64);
4362 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4363 g_Net_Slist_Update;
4364 end;
4366 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4367 end
4368 else if cmd = 'sv_passwd' then
4369 begin
4370 if (Length(P) > 1) and (Length(P[1]) > 0) then
4371 begin
4372 NetPassword := P[1];
4373 if Length(NetPassword) > 24 then
4374 SetLength(NetPassword, 24);
4375 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4376 g_Net_Slist_Update;
4377 end;
4379 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4380 end
4381 else if cmd = 'sv_maxplrs' then
4382 begin
4383 if (Length(P) > 1) then
4384 begin
4385 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4386 if g_Game_IsServer and g_Game_IsNet then
4387 begin
4388 b := 0;
4389 for a := 0 to High(NetClients) do
4390 if NetClients[a].Used then
4391 begin
4392 Inc(b);
4393 if b > NetMaxClients then
4394 begin
4395 s := g_Player_Get(NetClients[a].Player).Name;
4396 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4397 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4398 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4399 end;
4400 end;
4401 if NetUseMaster then
4402 g_Net_Slist_Update;
4403 end;
4404 end;
4406 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4407 end
4408 else if cmd = 'sv_public' then
4409 begin
4410 if (Length(P) > 1) then
4411 begin
4412 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4413 if g_Game_IsServer and g_Game_IsNet then
4414 if NetUseMaster then
4415 begin
4416 if NetMPeer = nil then
4417 if not g_Net_Slist_Connect() then
4418 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4419 g_Net_Slist_Update();
4420 end
4421 else
4422 if NetMPeer <> nil then
4423 g_Net_Slist_Disconnect();
4424 end;
4426 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4427 end
4428 else if cmd = 'sv_intertime' then
4429 begin
4430 if (Length(P) > 1) then
4431 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4433 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4434 end
4435 else if cmd = 'p1_name' then
4436 begin
4437 if (Length(P) > 1) and gGameOn then
4438 begin
4439 if g_Game_IsClient then
4440 begin
4441 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4442 MC_SEND_PlayerSettings;
4443 end
4444 else
4445 if gPlayer1 <> nil then
4446 begin
4447 gPlayer1.Name := b_Text_Unformat(P[1]);
4448 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4449 end
4450 else
4451 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4452 end;
4453 end
4454 else if cmd = 'p2_name' then
4455 begin
4456 if (Length(P) > 1) and gGameOn then
4457 begin
4458 if g_Game_IsClient then
4459 begin
4460 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4461 MC_SEND_PlayerSettings;
4462 end
4463 else
4464 if gPlayer2 <> nil then
4465 begin
4466 gPlayer2.Name := b_Text_Unformat(P[1]);
4467 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4468 end
4469 else
4470 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4471 end;
4472 end
4473 else if cmd = 'p1_color' then
4474 begin
4475 if Length(P) > 3 then
4476 if g_Game_IsClient then
4477 begin
4478 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4479 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4480 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4481 MC_SEND_PlayerSettings;
4482 end
4483 else
4484 if gPlayer1 <> nil then
4485 begin
4486 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4487 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4488 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4489 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4490 end
4491 else
4492 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4493 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4494 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4495 end
4496 else if (cmd = 'p2_color') and not g_Game_IsNet then
4497 begin
4498 if Length(P) > 3 then
4499 if g_Game_IsClient then
4500 begin
4501 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4502 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4503 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4504 MC_SEND_PlayerSettings;
4505 end
4506 else
4507 if gPlayer2 <> nil then
4508 begin
4509 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4510 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4511 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4512 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4513 end
4514 else
4515 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4516 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4517 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4518 end
4519 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4520 begin
4521 if cmd = 'r_showtime' then
4522 begin
4523 if (Length(P) > 1) and
4524 ((P[1] = '1') or (P[1] = '0')) then
4525 gShowTime := (P[1][1] = '1');
4527 if gShowTime then
4528 g_Console_Add(_lc[I_MSG_TIME_ON])
4529 else
4530 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4531 end
4532 else if cmd = 'r_showscore' then
4533 begin
4534 if (Length(P) > 1) and
4535 ((P[1] = '1') or (P[1] = '0')) then
4536 gShowGoals := (P[1][1] = '1');
4538 if gShowGoals then
4539 g_Console_Add(_lc[I_MSG_SCORE_ON])
4540 else
4541 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4542 end
4543 else if cmd = 'r_showstat' then
4544 begin
4545 if (Length(P) > 1) and
4546 ((P[1] = '1') or (P[1] = '0')) then
4547 gShowStat := (P[1][1] = '1');
4549 if gShowStat then
4550 g_Console_Add(_lc[I_MSG_STATS_ON])
4551 else
4552 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4553 end
4554 else if cmd = 'r_showkillmsg' then
4555 begin
4556 if (Length(P) > 1) and
4557 ((P[1] = '1') or (P[1] = '0')) then
4558 gShowKillMsg := (P[1][1] = '1');
4560 if gShowKillMsg then
4561 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4562 else
4563 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4564 end
4565 else if cmd = 'r_showlives' then
4566 begin
4567 if (Length(P) > 1) and
4568 ((P[1] = '1') or (P[1] = '0')) then
4569 gShowLives := (P[1][1] = '1');
4571 if gShowLives then
4572 g_Console_Add(_lc[I_MSG_LIVES_ON])
4573 else
4574 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4575 end
4576 else if cmd = 'r_showspect' then
4577 begin
4578 if (Length(P) > 1) and
4579 ((P[1] = '1') or (P[1] = '0')) then
4580 gSpectHUD := (P[1][1] = '1');
4582 if gSpectHUD then
4583 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4584 else
4585 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4586 end
4587 else if cmd = 'r_showping' then
4588 begin
4589 if (Length(P) > 1) and
4590 ((P[1] = '1') or (P[1] = '0')) then
4591 gShowPing := (P[1][1] = '1');
4593 if gShowPing then
4594 g_Console_Add(_lc[I_MSG_PING_ON])
4595 else
4596 g_Console_Add(_lc[I_MSG_PING_OFF]);
4597 end
4598 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4599 begin
4600 if Length(P) > 1 then
4601 begin
4602 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4603 gGameSettings.GoalLimit := 0
4604 else
4605 begin
4606 b := 0;
4608 if gGameSettings.GameMode = GM_DM then
4609 begin // DM
4610 stat := g_Player_GetStats();
4611 if stat <> nil then
4612 for a := 0 to High(stat) do
4613 if stat[a].Frags > b then
4614 b := stat[a].Frags;
4615 end
4616 else // TDM/CTF
4617 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4619 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4620 end;
4622 if g_Game_IsNet then MH_SEND_GameSettings;
4623 end;
4625 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4626 end
4627 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4628 begin
4629 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4630 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4632 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4633 [gGameSettings.TimeLimit div 3600,
4634 (gGameSettings.TimeLimit div 60) mod 60,
4635 gGameSettings.TimeLimit mod 60]));
4636 if g_Game_IsNet then MH_SEND_GameSettings;
4637 end
4638 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4639 begin
4640 if Length(P) > 1 then
4641 begin
4642 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4643 gGameSettings.MaxLives := 0
4644 else
4645 begin
4646 b := 0;
4647 stat := g_Player_GetStats();
4648 if stat <> nil then
4649 for a := 0 to High(stat) do
4650 if stat[a].Lives > b then
4651 b := stat[a].Lives;
4652 gGameSettings.MaxLives :=
4653 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4654 end;
4655 end;
4657 g_Console_Add(Format(_lc[I_MSG_LIVES],
4658 [gGameSettings.MaxLives]));
4659 if g_Game_IsNet then MH_SEND_GameSettings;
4660 end;
4661 end;
4662 end;
4664 procedure DebugCommands(P: SArray);
4665 var
4666 a, b: Integer;
4667 cmd: string;
4668 //pt: TPoint;
4669 begin
4670 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
4671 if gDebugMode then
4672 begin
4673 cmd := LowerCase(P[0]);
4674 if cmd = 'd_window' then
4675 begin
4676 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
4677 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
4678 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
4679 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
4680 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
4681 end
4682 else if cmd = 'd_sounds' then
4683 begin
4684 if (Length(P) > 1) and
4685 ((P[1] = '1') or (P[1] = '0')) then
4686 g_Debug_Sounds := (P[1][1] = '1');
4688 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
4689 end
4690 else if cmd = 'd_frames' then
4691 begin
4692 if (Length(P) > 1) and
4693 ((P[1] = '1') or (P[1] = '0')) then
4694 g_Debug_Frames := (P[1][1] = '1');
4696 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
4697 end
4698 else if cmd = 'd_winmsg' then
4699 begin
4700 if (Length(P) > 1) and
4701 ((P[1] = '1') or (P[1] = '0')) then
4702 g_Debug_WinMsgs := (P[1][1] = '1');
4704 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
4705 end
4706 else if (cmd = 'd_monoff') and not g_Game_IsNet then
4707 begin
4708 if (Length(P) > 1) and
4709 ((P[1] = '1') or (P[1] = '0')) then
4710 g_Debug_MonsterOff := (P[1][1] = '1');
4712 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
4713 end
4714 else if (cmd = 'd_botoff') and not g_Game_IsNet then
4715 begin
4716 if Length(P) > 1 then
4717 case P[1][1] of
4718 '0': g_debug_BotAIOff := 0;
4719 '1': g_debug_BotAIOff := 1;
4720 '2': g_debug_BotAIOff := 2;
4721 '3': g_debug_BotAIOff := 3;
4722 end;
4724 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
4725 end
4726 else if cmd = 'd_monster' then
4727 begin
4728 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
4729 if Length(P) < 2 then
4730 begin
4731 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
4732 g_Console_Add('ID | Name');
4733 for b := MONSTER_DEMON to MONSTER_MAN do
4734 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
4735 end else
4736 begin
4737 a := StrToIntDef(P[1], 0);
4738 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4739 a := g_Monsters_GetIDByName(P[1]);
4741 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4742 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
4743 else
4744 begin
4745 with gPlayer1.Obj do
4746 b := g_Monsters_Create(a,
4747 X + Rect.X + (Rect.Width div 2),
4748 Y + Rect.Y + Rect.Height,
4749 gPlayer1.Direction, True);
4750 if (Length(P) > 2) and (b >= 0) then
4751 gMonsters[b].MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
4752 end;
4753 end;
4754 end
4755 else if (cmd = 'd_health') then
4756 begin
4757 if (Length(P) > 1) and
4758 ((P[1] = '1') or (P[1] = '0')) then
4759 g_debug_HealthBar := (P[1][1] = '1');
4761 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
4762 end
4763 else if (cmd = 'd_player') then
4764 begin
4765 if (Length(P) > 1) and
4766 ((P[1] = '1') or (P[1] = '0')) then
4767 g_debug_Player := (P[1][1] = '1');
4769 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
4770 end
4771 else if (cmd = 'd_joy') then
4772 begin
4773 for a := 1 to 8 do
4774 g_Console_Add(e_JoystickStateToString(a));
4775 end;
4776 end
4777 else
4778 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
4779 end;
4782 procedure GameCheats(P: SArray);
4783 var
4784 cmd: string;
4785 f, a: Integer;
4786 plr: TPlayer;
4787 begin
4788 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
4789 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
4790 begin
4791 g_Console_Add('not available');
4792 exit;
4793 end;
4794 plr := gPlayer1;
4795 if plr = nil then
4796 begin
4797 g_Console_Add('where is the player?!');
4798 exit;
4799 end;
4800 cmd := LowerCase(P[0]);
4801 // god
4802 if cmd = 'god' then
4803 begin
4804 plr.GodMode := not plr.GodMode;
4805 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
4806 exit;
4807 end;
4808 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
4809 if cmd = 'give' then
4810 begin
4811 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
4812 for f := 1 to High(P) do
4813 begin
4814 cmd := LowerCase(P[f]);
4815 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
4816 if (cmd = 'all') or (cmd = 'weapons') then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
4817 if cmd = 'exit' then
4818 begin
4819 if gTriggers <> nil then
4820 begin
4821 for a := 0 to High(gTriggers) do
4822 begin
4823 if gTriggers[a].TriggerType = TRIGGER_EXIT then
4824 begin
4825 g_Console_Add('player left the map');
4826 gExitByTrigger := True;
4827 g_Game_ExitLevel(gTriggers[a].Data.MapName);
4828 break;
4829 end;
4830 end;
4831 end;
4832 continue;
4833 end;
4834 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
4835 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got jetpack'); continue; end;
4836 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got envirosuit'); continue; end;
4837 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got berserk pack'); continue; end;
4838 g_Console_Add('i don''t know how to give '''+cmd+'''!');
4839 end;
4840 exit;
4841 end;
4842 // open
4843 if cmd = 'open' then
4844 begin
4845 g_Console_Add('player activated sesame');
4846 g_Triggers_OpenAll();
4847 exit;
4848 end;
4849 // fly
4850 if cmd = 'fly' then
4851 begin
4852 gFly := not gFly;
4853 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
4854 exit;
4855 end;
4856 // noclip
4857 if cmd = 'noclip' then
4858 begin
4859 plr.SwitchNoClip;
4860 g_Console_Add('wall hardeness adjusted');
4861 exit;
4862 end;
4863 // notarget
4864 if cmd = 'notarget' then
4865 begin
4866 plr.NoTarget := not plr.NoTarget;
4867 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
4868 exit;
4869 end;
4870 // noreload
4871 if cmd = 'noreload' then
4872 begin
4873 plr.NoReload := not plr.NoReload;
4874 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
4875 exit;
4876 end;
4877 // speedy
4878 if cmd = 'speedy' then
4879 begin
4880 MAX_RUNVEL := 32-MAX_RUNVEL;
4881 g_Console_Add('speed adjusted');
4882 exit;
4883 end;
4884 // jumpy
4885 if cmd = 'jumpy' then
4886 begin
4887 VEL_JUMP := 30-VEL_JUMP;
4888 g_Console_Add('jump height adjusted');
4889 exit;
4890 end;
4891 // automap
4892 if cmd = 'automap' then
4893 begin
4894 gShowMap := not gShowMap;
4895 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
4896 exit;
4897 end;
4898 // aimline
4899 if cmd = 'aimline' then
4900 begin
4901 gAimLine := not gAimLine;
4902 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
4903 exit;
4904 end;
4905 end;
4907 procedure GameCommands(P: SArray);
4908 var
4909 a, b: Integer;
4910 s, pw: String;
4911 chstr: string;
4912 cmd: string;
4913 pl: pTNetClient = nil;
4914 plr: TPlayer;
4915 prt: Word;
4916 nm: Boolean;
4917 listen: LongWord;
4918 begin
4919 // Îáùèå êîìàíäû:
4920 cmd := LowerCase(P[0]);
4921 chstr := '';
4922 if (cmd = 'quit') or
4923 (cmd = 'exit') then
4924 begin
4925 g_Game_Free();
4926 g_Game_Quit();
4927 Exit;
4928 end
4929 else if cmd = 'pause' then
4930 begin
4931 if (g_ActiveWindow = nil) then
4932 g_Game_Pause(not gPause);
4933 end
4934 else if cmd = 'endgame' then
4935 gExit := EXIT_SIMPLE
4936 else if cmd = 'restart' then
4937 begin
4938 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
4939 begin
4940 if g_Game_IsClient then
4941 begin
4942 g_Console_Add(_lc[I_MSG_SERVERONLY]);
4943 Exit;
4944 end;
4945 g_Game_Restart();
4946 end else
4947 g_Console_Add(_lc[I_MSG_NOT_GAME]);
4948 end
4949 else if cmd = 'kick' then
4950 begin
4951 if g_Game_IsServer then
4952 begin
4953 if Length(P) < 2 then
4954 begin
4955 g_Console_Add('kick <name>');
4956 Exit;
4957 end;
4958 if P[1] = '' then
4959 begin
4960 g_Console_Add('kick <name>');
4961 Exit;
4962 end;
4964 if g_Game_IsNet then
4965 pl := g_Net_Client_ByName(P[1]);
4966 if (pl <> nil) then
4967 begin
4968 s := g_Net_ClientName_ByID(pl^.ID);
4969 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
4970 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4971 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4972 if NetUseMaster then
4973 g_Net_Slist_Update;
4974 end else if gPlayers <> nil then
4975 for a := Low(gPlayers) to High(gPlayers) do
4976 if gPlayers[a] <> nil then
4977 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
4978 begin
4979 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
4980 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
4981 continue;
4982 gPlayers[a].Lives := 0;
4983 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4984 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
4985 g_Player_Remove(gPlayers[a].UID);
4986 if NetUseMaster then
4987 g_Net_Slist_Update;
4988 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
4989 g_Bot_MixNames();
4990 end;
4991 end else
4992 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
4993 end
4994 else if cmd = 'kick_id' then
4995 begin
4996 if g_Game_IsServer and g_Game_IsNet then
4997 begin
4998 if Length(P) < 2 then
4999 begin
5000 g_Console_Add('kick_id <client ID>');
5001 Exit;
5002 end;
5003 if P[1] = '' then
5004 begin
5005 g_Console_Add('kick_id <client ID>');
5006 Exit;
5007 end;
5009 a := StrToIntDef(P[1], 0);
5010 if (NetClients <> nil) and (a <= High(NetClients)) then
5011 begin
5012 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5013 begin
5014 s := g_Net_ClientName_ByID(NetClients[a].ID);
5015 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5016 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5017 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5018 if NetUseMaster then
5019 g_Net_Slist_Update;
5020 end;
5021 end;
5022 end else
5023 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5024 end
5025 else if cmd = 'ban' then
5026 begin
5027 if g_Game_IsServer and g_Game_IsNet then
5028 begin
5029 if Length(P) < 2 then
5030 begin
5031 g_Console_Add('ban <name>');
5032 Exit;
5033 end;
5034 if P[1] = '' then
5035 begin
5036 g_Console_Add('ban <name>');
5037 Exit;
5038 end;
5040 pl := g_Net_Client_ByName(P[1]);
5041 if (pl <> nil) then
5042 begin
5043 s := g_Net_ClientName_ByID(pl^.ID);
5044 g_Net_BanHost(pl^.Peer^.address.host, False);
5045 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5046 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5047 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5048 if NetUseMaster then
5049 g_Net_Slist_Update;
5050 end else
5051 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5052 end else
5053 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5054 end
5055 else if cmd = 'ban_id' then
5056 begin
5057 if g_Game_IsServer and g_Game_IsNet then
5058 begin
5059 if Length(P) < 2 then
5060 begin
5061 g_Console_Add('ban_id <client ID>');
5062 Exit;
5063 end;
5064 if P[1] = '' then
5065 begin
5066 g_Console_Add('ban_id <client ID>');
5067 Exit;
5068 end;
5070 a := StrToIntDef(P[1], 0);
5071 if (NetClients <> nil) and (a <= High(NetClients)) then
5072 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5073 begin
5074 s := g_Net_ClientName_ByID(NetClients[a].ID);
5075 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5076 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5077 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5078 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5079 if NetUseMaster then
5080 g_Net_Slist_Update;
5081 end;
5082 end else
5083 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5084 end
5085 else if cmd = 'permban' then
5086 begin
5087 if g_Game_IsServer and g_Game_IsNet then
5088 begin
5089 if Length(P) < 2 then
5090 begin
5091 g_Console_Add('permban <name>');
5092 Exit;
5093 end;
5094 if P[1] = '' then
5095 begin
5096 g_Console_Add('permban <name>');
5097 Exit;
5098 end;
5100 pl := g_Net_Client_ByName(P[1]);
5101 if (pl <> nil) then
5102 begin
5103 s := g_Net_ClientName_ByID(pl^.ID);
5104 g_Net_BanHost(pl^.Peer^.address.host);
5105 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5106 g_Net_SaveBanList();
5107 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5108 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5109 if NetUseMaster then
5110 g_Net_Slist_Update;
5111 end else
5112 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5113 end else
5114 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5115 end
5116 else if cmd = 'permban_id' then
5117 begin
5118 if g_Game_IsServer and g_Game_IsNet then
5119 begin
5120 if Length(P) < 2 then
5121 begin
5122 g_Console_Add('permban_id <client ID>');
5123 Exit;
5124 end;
5125 if P[1] = '' then
5126 begin
5127 g_Console_Add('permban_id <client ID>');
5128 Exit;
5129 end;
5131 a := StrToIntDef(P[1], 0);
5132 if (NetClients <> nil) and (a <= High(NetClients)) then
5133 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5134 begin
5135 s := g_Net_ClientName_ByID(NetClients[a].ID);
5136 g_Net_BanHost(NetClients[a].Peer^.address.host);
5137 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5138 g_Net_SaveBanList();
5139 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5140 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5141 if NetUseMaster then
5142 g_Net_Slist_Update;
5143 end;
5144 end else
5145 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5146 end
5147 else if cmd = 'unban' then
5148 begin
5149 if g_Game_IsServer and g_Game_IsNet then
5150 begin
5151 if Length(P) < 2 then
5152 begin
5153 g_Console_Add('unban <IP Address>');
5154 Exit;
5155 end;
5156 if P[1] = '' then
5157 begin
5158 g_Console_Add('unban <IP Address>');
5159 Exit;
5160 end;
5162 if g_Net_UnbanHost(P[1]) then
5163 begin
5164 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5165 g_Net_SaveBanList();
5166 end else
5167 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5168 end else
5169 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5170 end
5171 else if cmd = 'clientlist' then
5172 begin
5173 if g_Game_IsServer and g_Game_IsNet then
5174 begin
5175 b := 0;
5176 if NetClients <> nil then
5177 for a := Low(NetClients) to High(NetClients) do
5178 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5179 begin
5180 plr := g_Player_Get(NetClients[a].Player);
5181 if plr = nil then continue;
5182 Inc(b);
5183 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5184 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5185 end;
5186 if b = 0 then
5187 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5188 end else
5189 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5190 end
5191 else if cmd = 'connect' then
5192 begin
5193 if (NetMode = NET_NONE) then
5194 begin
5195 if Length(P) < 2 then
5196 begin
5197 g_Console_Add('connect <IP> [port] [password]');
5198 Exit;
5199 end;
5200 if P[1] = '' then
5201 begin
5202 g_Console_Add('connect <IP> [port] [password]');
5203 Exit;
5204 end;
5206 if Length(P) > 2 then
5207 prt := StrToIntDef(P[2], 25666)
5208 else
5209 prt := 25666;
5211 if Length(P) > 3 then
5212 pw := P[3]
5213 else
5214 pw := '';
5216 g_Game_StartClient(P[1], prt, pw);
5217 end;
5218 end
5219 else if cmd = 'disconnect' then
5220 begin
5221 if (NetMode = NET_CLIENT) then
5222 g_Net_Disconnect();
5223 end
5224 else if cmd = 'reconnect' then
5225 begin
5226 if (NetMode = NET_SERVER) then
5227 Exit;
5229 if (NetMode = NET_CLIENT) then
5230 begin
5231 g_Net_Disconnect();
5232 gExit := EXIT_SIMPLE;
5233 EndGame;
5234 end;
5236 //TODO: Use last successful password to reconnect, instead of ''
5237 g_Game_StartClient(NetClientIP, NetClientPort, '');
5238 end
5239 else if (cmd = 'addbot') or
5240 (cmd = 'bot_add') then
5241 begin
5242 if Length(P) > 1 then
5243 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5244 else
5245 g_Bot_Add(TEAM_NONE, 2);
5246 end
5247 else if cmd = 'bot_addlist' then
5248 begin
5249 if Length(P) > 1 then
5250 if Length(P) = 2 then
5251 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5252 else
5253 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5254 end
5255 else if cmd = 'bot_removeall' then
5256 g_Bot_RemoveAll()
5257 else if cmd = 'chat' then
5258 begin
5259 if g_Game_IsNet then
5260 begin
5261 if Length(P) > 1 then
5262 begin
5263 for a := 1 to High(P) do
5264 chstr := chstr + P[a] + ' ';
5266 if Length(chstr) > 200 then SetLength(chstr, 200);
5268 if Length(chstr) < 1 then
5269 begin
5270 g_Console_Add('chat <text>');
5271 Exit;
5272 end;
5274 chstr := b_Text_Format(chstr);
5275 if g_Game_IsClient then
5276 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5277 else
5278 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5279 end
5280 else
5281 g_Console_Add('chat <text>');
5282 end else
5283 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5284 end
5285 else if cmd = 'teamchat' then
5286 begin
5287 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5288 begin
5289 if Length(P) > 1 then
5290 begin
5291 for a := 1 to High(P) do
5292 chstr := chstr + P[a] + ' ';
5294 if Length(chstr) > 200 then SetLength(chstr, 200);
5296 if Length(chstr) < 1 then
5297 begin
5298 g_Console_Add('teamchat <text>');
5299 Exit;
5300 end;
5302 chstr := b_Text_Format(chstr);
5303 if g_Game_IsClient then
5304 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5305 else
5306 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5307 gPlayer1Settings.Team);
5308 end
5309 else
5310 g_Console_Add('teamchat <text>');
5311 end else
5312 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5313 end
5314 else if cmd = 'game' then
5315 begin
5316 if gGameSettings.GameType <> GT_NONE then
5317 begin
5318 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5319 Exit;
5320 end;
5321 if Length(P) = 1 then
5322 begin
5323 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5324 Exit;
5325 end;
5326 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5327 P[1] := addWadExtension(P[1]);
5328 if FileExists(MapsDir + P[1]) then
5329 begin
5330 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5331 if Length(P) < 3 then
5332 begin
5333 SetLength(P, 3);
5334 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5335 end;
5337 s := P[1] + ':\' + UpperCase(P[2]);
5339 if g_Map_Exist(MapsDir + s) then
5340 begin
5341 // Çàïóñêàåì ñâîþ èãðó
5342 g_Game_Free();
5343 with gGameSettings do
5344 begin
5345 GameMode := g_Game_TextToMode(gcGameMode);
5346 if gSwitchGameMode <> GM_NONE then
5347 GameMode := gSwitchGameMode;
5348 if GameMode = GM_NONE then GameMode := GM_DM;
5349 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5350 b := 1;
5351 if Length(P) >= 4 then
5352 b := StrToIntDef(P[3], 1);
5353 g_Game_StartCustom(s, GameMode, TimeLimit,
5354 GoalLimit, MaxLives, Options, b);
5355 end;
5356 end
5357 else
5358 if P[2] = '' then
5359 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5360 else
5361 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5362 end else
5363 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5364 end
5365 else if cmd = 'host' then
5366 begin
5367 if gGameSettings.GameType <> GT_NONE then
5368 begin
5369 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5370 Exit;
5371 end;
5372 if Length(P) < 4 then
5373 begin
5374 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5375 Exit;
5376 end;
5377 if not StrToIp(P[1], listen) then
5378 Exit;
5379 prt := StrToIntDef(P[2], 25666);
5381 P[3] := addWadExtension(P[3]);
5382 if FileExists(MapsDir + P[3]) then
5383 begin
5384 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5385 if Length(P) < 5 then
5386 begin
5387 SetLength(P, 5);
5388 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5389 end;
5391 s := P[3] + ':\' + UpperCase(P[4]);
5393 if g_Map_Exist(MapsDir + s) then
5394 begin
5395 // Çàïóñêàåì ñâîþ èãðó
5396 g_Game_Free();
5397 with gGameSettings do
5398 begin
5399 GameMode := g_Game_TextToMode(gcGameMode);
5400 if gSwitchGameMode <> GM_NONE then
5401 GameMode := gSwitchGameMode;
5402 if GameMode = GM_NONE then GameMode := GM_DM;
5403 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5404 b := 0;
5405 if Length(P) >= 6 then
5406 b := StrToIntDef(P[5], 0);
5407 g_Game_StartServer(s, GameMode, TimeLimit,
5408 GoalLimit, MaxLives, Options, b, listen, prt);
5409 end;
5410 end
5411 else
5412 if P[4] = '' then
5413 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5414 else
5415 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5416 end else
5417 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5418 end
5419 else if cmd = 'map' then
5420 begin
5421 if Length(P) = 1 then
5422 begin
5423 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5424 begin
5425 g_Console_Add(cmd + ' <MAP>');
5426 g_Console_Add(cmd + ' <WAD> [MAP]');
5427 end else
5428 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5429 end else
5430 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5431 begin
5432 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5433 if Length(P) < 3 then
5434 begin
5435 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5436 s := UpperCase(P[1]);
5437 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5438 begin // Êàðòà íàøëàñü
5439 gExitByTrigger := False;
5440 if gGameOn then
5441 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5442 gNextMap := s;
5443 gExit := EXIT_ENDLEVELCUSTOM;
5444 end
5445 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5446 g_Game_ChangeMap(s);
5447 end else
5448 begin
5449 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5450 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5451 P[1] := addWadExtension(P[1]);
5452 if FileExists(MapsDir + P[1]) then
5453 begin
5454 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5455 SetLength(P, 3);
5456 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5458 s := P[1] + ':\' + P[2];
5460 if g_Map_Exist(MapsDir + s) then
5461 begin
5462 gExitByTrigger := False;
5463 if gGameOn then
5464 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5465 gNextMap := s;
5466 gExit := EXIT_ENDLEVELCUSTOM;
5467 end
5468 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5469 g_Game_ChangeMap(s);
5470 end else
5471 if P[2] = '' then
5472 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5473 else
5474 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5475 end else
5476 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5477 end;
5478 end else
5479 begin
5480 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5481 P[1] := addWadExtension(P[1]);
5482 if FileExists(MapsDir + P[1]) then
5483 begin
5484 // Íàøëè WAD ôàéë
5485 P[2] := UpperCase(P[2]);
5486 s := P[1] + ':\' + P[2];
5488 if g_Map_Exist(MapsDir + s) then
5489 begin // Íàøëè êàðòó
5490 gExitByTrigger := False;
5491 if gGameOn then
5492 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5493 gNextMap := s;
5494 gExit := EXIT_ENDLEVELCUSTOM;
5495 end
5496 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5497 g_Game_ChangeMap(s);
5498 end else
5499 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5500 end else
5501 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5502 end;
5503 end else
5504 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5505 end
5506 else if cmd = 'nextmap' then
5507 begin
5508 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5509 g_Console_Add(_lc[I_MSG_NOT_GAME])
5510 else begin
5511 nm := True;
5512 if Length(P) = 1 then
5513 begin
5514 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5515 begin
5516 g_Console_Add(cmd + ' <MAP>');
5517 g_Console_Add(cmd + ' <WAD> [MAP]');
5518 end else begin
5519 nm := False;
5520 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5521 end;
5522 end else
5523 begin
5524 nm := False;
5525 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5526 begin
5527 if Length(P) < 3 then
5528 begin
5529 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5530 s := UpperCase(P[1]);
5531 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5532 begin // Êàðòà íàøëàñü
5533 gExitByTrigger := False;
5534 gNextMap := s;
5535 nm := True;
5536 end else
5537 begin
5538 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5539 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5540 P[1] := addWadExtension(P[1]);
5541 if FileExists(MapsDir + P[1]) then
5542 begin
5543 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5544 SetLength(P, 3);
5545 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5547 s := P[1] + ':\' + P[2];
5549 if g_Map_Exist(MapsDir + s) then
5550 begin // Óñòàíàâëèâàåì êàðòó
5551 gExitByTrigger := False;
5552 gNextMap := s;
5553 nm := True;
5554 end else
5555 if P[2] = '' then
5556 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5557 else
5558 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5559 end else
5560 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5561 end;
5562 end else
5563 begin
5564 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5565 P[1] := addWadExtension(P[1]);
5566 if FileExists(MapsDir + P[1]) then
5567 begin
5568 // Íàøëè WAD ôàéë
5569 P[2] := UpperCase(P[2]);
5570 s := P[1] + ':\' + P[2];
5572 if g_Map_Exist(MapsDir + s) then
5573 begin // Íàøëè êàðòó
5574 gExitByTrigger := False;
5575 gNextMap := s;
5576 nm := True;
5577 end else
5578 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5579 end else
5580 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5581 end;
5582 end else
5583 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5584 end;
5585 if nm then
5586 if gNextMap = '' then
5587 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
5588 else
5589 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
5590 end;
5591 end
5592 else if (cmd = 'endmap') or (cmd = 'goodbye') then
5593 begin
5594 if not gGameOn then
5595 g_Console_Add(_lc[I_MSG_NOT_GAME])
5596 else
5597 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5598 begin
5599 gExitByTrigger := False;
5600 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
5601 if (gNextMap = '') and (gTriggers <> nil) then
5602 for a := 0 to High(gTriggers) do
5603 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5604 begin
5605 gExitByTrigger := True;
5606 gNextMap := gTriggers[a].Data.MapName;
5607 Break;
5608 end;
5609 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
5610 if gNextMap = '' then
5611 gNextMap := g_Game_GetNextMap();
5612 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
5613 if Pos(':\', gNextMap) = 0 then
5614 s := gGameSettings.WAD + ':\' + gNextMap
5615 else
5616 s := gNextMap;
5617 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
5618 if g_Map_Exist(MapsDir + s) then
5619 gExit := EXIT_ENDLEVELCUSTOM
5620 else
5621 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
5622 end else
5623 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5624 end
5625 else if (cmd = 'event') then
5626 begin
5627 if (Length(P) <= 1) then
5628 begin
5629 for a := 0 to High(gEvents) do
5630 if gEvents[a].Command = '' then
5631 g_Console_Add(gEvents[a].Name + ' <none>')
5632 else
5633 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5634 Exit;
5635 end;
5636 if (Length(P) = 2) then
5637 begin
5638 for a := 0 to High(gEvents) do
5639 if gEvents[a].Name = P[1] then
5640 if gEvents[a].Command = '' then
5641 g_Console_Add(gEvents[a].Name + ' <none>')
5642 else
5643 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5644 Exit;
5645 end;
5646 for a := 0 to High(gEvents) do
5647 if gEvents[a].Name = P[1] then
5648 begin
5649 gEvents[a].Command := '';
5650 for b := 2 to High(P) do
5651 if Pos(' ', P[b]) = 0 then
5652 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
5653 else
5654 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
5655 gEvents[a].Command := Trim(gEvents[a].Command);
5656 Exit;
5657 end;
5658 end
5659 // Êîìàíäû Ñâîåé èãðû:
5660 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5661 begin
5662 if cmd = 'bot_addred' then
5663 begin
5664 if Length(P) > 1 then
5665 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
5666 else
5667 g_Bot_Add(TEAM_RED, 2);
5668 end
5669 else if cmd = 'bot_addblue' then
5670 begin
5671 if Length(P) > 1 then
5672 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
5673 else
5674 g_Bot_Add(TEAM_BLUE, 2);
5675 end
5676 else if cmd = 'suicide' then
5677 begin
5678 if gGameOn then
5679 begin
5680 if g_Game_IsClient then
5681 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
5682 else
5683 begin
5684 if gPlayer1 <> nil then
5685 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
5686 if gPlayer2 <> nil then
5687 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
5688 end;
5689 end;
5690 end
5691 else if cmd = 'spectate' then
5692 begin
5693 if not gGameOn then
5694 Exit;
5695 g_Game_Spectate();
5696 end
5697 else if cmd = 'say' then
5698 begin
5699 if g_Game_IsServer and g_Game_IsNet then
5700 begin
5701 if Length(P) > 1 then
5702 begin
5703 chstr := '';
5704 for a := 1 to High(P) do
5705 chstr := chstr + P[a] + ' ';
5707 if Length(chstr) > 200 then SetLength(chstr, 200);
5709 if Length(chstr) < 1 then
5710 begin
5711 g_Console_Add('say <text>');
5712 Exit;
5713 end;
5715 chstr := b_Text_Format(chstr);
5716 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
5717 end
5718 else g_Console_Add('say <text>');
5719 end else
5720 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5721 end
5722 else if cmd = 'tell' then
5723 begin
5724 if g_Game_IsServer and g_Game_IsNet then
5725 begin
5726 if (Length(P) > 2) and (P[1] <> '') then
5727 begin
5728 chstr := '';
5729 for a := 2 to High(P) do
5730 chstr := chstr + P[a] + ' ';
5732 if Length(chstr) > 200 then SetLength(chstr, 200);
5734 if Length(chstr) < 1 then
5735 begin
5736 g_Console_Add('tell <playername> <text>');
5737 Exit;
5738 end;
5740 pl := g_Net_Client_ByName(P[1]);
5741 if pl <> nil then
5742 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
5743 else
5744 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5745 end
5746 else g_Console_Add('tell <playername> <text>');
5747 end else
5748 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5749 end
5750 else if (cmd = 'overtime') and not g_Game_IsClient then
5751 begin
5752 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
5753 Exit;
5754 // Äîïîëíèòåëüíîå âðåìÿ:
5755 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
5757 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5758 [gGameSettings.TimeLimit div 3600,
5759 (gGameSettings.TimeLimit div 60) mod 60,
5760 gGameSettings.TimeLimit mod 60]));
5761 if g_Game_IsNet then MH_SEND_GameSettings;
5762 end
5763 else if (cmd = 'rcon_password') and g_Game_IsClient then
5764 begin
5765 if (Length(P) <= 1) then
5766 g_Console_Add('rcon_password <password>')
5767 else
5768 MC_SEND_RCONPassword(P[1]);
5769 end
5770 else if cmd = 'rcon' then
5771 begin
5772 if g_Game_IsClient then
5773 begin
5774 if Length(P) > 1 then
5775 begin
5776 chstr := '';
5777 for a := 1 to High(P) do
5778 chstr := chstr + P[a] + ' ';
5780 if Length(chstr) > 200 then SetLength(chstr, 200);
5782 if Length(chstr) < 1 then
5783 begin
5784 g_Console_Add('rcon <command>');
5785 Exit;
5786 end;
5788 MC_SEND_RCONCommand(chstr);
5789 end
5790 else g_Console_Add('rcon <command>');
5791 end;
5792 end
5793 else if cmd = 'ready' then
5794 begin
5795 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
5796 gLMSRespawnTime := gTime + 100;
5797 end
5798 else if (cmd = 'callvote') and g_Game_IsNet then
5799 begin
5800 if Length(P) > 1 then
5801 begin
5802 chstr := '';
5803 for a := 1 to High(P) do begin
5804 if a > 1 then chstr := chstr + ' ';
5805 chstr := chstr + P[a];
5806 end;
5808 if Length(chstr) > 200 then SetLength(chstr, 200);
5810 if Length(chstr) < 1 then
5811 begin
5812 g_Console_Add('callvote <command>');
5813 Exit;
5814 end;
5816 if g_Game_IsClient then
5817 MC_SEND_Vote(True, chstr)
5818 else
5819 g_Game_StartVote(chstr, gPlayer1Settings.Name);
5820 g_Console_Process('vote', True);
5821 end
5822 else
5823 g_Console_Add('callvote <command>');
5824 end
5825 else if (cmd = 'vote') and g_Game_IsNet then
5826 begin
5827 if g_Game_IsClient then
5828 MC_SEND_Vote(False)
5829 else if gVoteInProgress then
5830 begin
5831 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
5832 a := Floor((NetClientCount+1)/2.0) + 1
5833 else
5834 a := Floor(NetClientCount/2.0) + 1;
5835 if gVoted then
5836 begin
5837 Dec(gVoteCount);
5838 gVoted := False;
5839 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
5840 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
5841 end
5842 else
5843 begin
5844 Inc(gVoteCount);
5845 gVoted := True;
5846 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
5847 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
5848 g_Game_CheckVote;
5849 end;
5850 end;
5851 end
5852 end;
5853 end;
5855 procedure g_TakeScreenShot();
5856 var
5857 a: Word;
5858 FileName: string;
5859 ssdir, t: string;
5860 st: TStream;
5861 ok: Boolean;
5862 begin
5863 if e_NoGraphics then Exit;
5864 ssdir := GameDir+'/screenshots';
5865 if not findFileCI(ssdir, true) then
5866 begin
5867 // try to create dir
5868 try
5869 CreateDir(ssdir);
5870 except
5871 end;
5872 if not findFileCI(ssdir, true) then exit; // alas
5873 end;
5874 try
5875 for a := 1 to High(Word) do
5876 begin
5877 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
5878 t := FileName;
5879 if findFileCI(t, true) then continue;
5880 if not findFileCI(FileName) then
5881 begin
5882 ok := false;
5883 st := createDiskFile(FileName);
5884 try
5885 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
5886 ok := true;
5887 finally
5888 st.Free();
5889 end;
5890 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
5891 break;
5892 end;
5893 end;
5894 except
5895 end;
5896 end;
5898 procedure g_Game_InGameMenu(Show: Boolean);
5899 begin
5900 if (g_ActiveWindow = nil) and Show then
5901 begin
5902 if gGameSettings.GameType = GT_SINGLE then
5903 g_GUI_ShowWindow('GameSingleMenu')
5904 else
5905 begin
5906 if g_Game_IsClient then
5907 g_GUI_ShowWindow('GameClientMenu')
5908 else
5909 if g_Game_IsNet then
5910 g_GUI_ShowWindow('GameServerMenu')
5911 else
5912 g_GUI_ShowWindow('GameCustomMenu');
5913 end;
5914 g_Sound_PlayEx('MENU_OPEN');
5916 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
5917 if (not g_Game_IsNet) then
5918 g_Game_Pause(True);
5919 end
5920 else
5921 if (g_ActiveWindow <> nil) and (not Show) then
5922 begin
5923 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
5924 if (not g_Game_IsNet) then
5925 g_Game_Pause(False);
5926 end;
5927 end;
5929 procedure g_Game_Pause(Enable: Boolean);
5930 begin
5931 if not gGameOn then
5932 Exit;
5934 if gPause = Enable then
5935 Exit;
5937 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
5938 Exit;
5940 gPause := Enable;
5941 g_Game_PauseAllSounds(Enable);
5942 end;
5944 procedure g_Game_PauseAllSounds(Enable: Boolean);
5945 var
5946 i: Integer;
5947 begin
5948 // Òðèããåðû:
5949 if gTriggers <> nil then
5950 for i := 0 to High(gTriggers) do
5951 with gTriggers[i] do
5952 if (TriggerType = TRIGGER_SOUND) and
5953 (Sound <> nil) and
5954 Sound.IsPlaying() then
5955 begin
5956 Sound.Pause(Enable);
5957 end;
5959 // Çâóêè èãðîêîâ:
5960 if gPlayers <> nil then
5961 for i := 0 to High(gPlayers) do
5962 if gPlayers[i] <> nil then
5963 gPlayers[i].PauseSounds(Enable);
5965 // Ìóçûêà:
5966 if gMusic <> nil then
5967 gMusic.Pause(Enable);
5968 end;
5970 procedure g_Game_StopAllSounds(all: Boolean);
5971 var
5972 i: Integer;
5973 begin
5974 if gTriggers <> nil then
5975 for i := 0 to High(gTriggers) do
5976 with gTriggers[i] do
5977 if (TriggerType = TRIGGER_SOUND) and
5978 (Sound <> nil) then
5979 Sound.Stop();
5981 if gMusic <> nil then
5982 gMusic.Stop();
5984 if all then
5985 e_StopChannels();
5986 end;
5988 procedure g_Game_UpdateTriggerSounds();
5989 var
5990 i: Integer;
5991 begin
5992 if gTriggers <> nil then
5993 for i := 0 to High(gTriggers) do
5994 with gTriggers[i] do
5995 if (TriggerType = TRIGGER_SOUND) and
5996 (Sound <> nil) and
5997 (Data.Local) and
5998 Sound.IsPlaying() then
5999 begin
6000 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6001 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6002 begin
6003 Sound.SetPan(0.5 - Data.Pan/255.0);
6004 Sound.SetVolume(Data.Volume/255.0);
6005 end
6006 else
6007 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6008 end;
6009 end;
6011 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6012 begin
6013 Result := False;
6014 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6015 begin
6016 Result := True;
6017 Exit;
6018 end;
6019 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6020 begin
6021 Result := True;
6022 Exit;
6023 end;
6024 if gSpectMode <> SPECT_PLAYERS then
6025 Exit;
6026 if gSpectPID1 = UID then
6027 begin
6028 Result := True;
6029 Exit;
6030 end;
6031 if gSpectViewTwo and (gSpectPID2 = UID) then
6032 begin
6033 Result := True;
6034 Exit;
6035 end;
6036 end;
6038 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6039 var
6040 Pl: TPlayer;
6041 begin
6042 Result := False;
6043 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6044 begin
6045 Result := True;
6046 Exit;
6047 end;
6048 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6049 begin
6050 Result := True;
6051 Exit;
6052 end;
6053 if gSpectMode <> SPECT_PLAYERS then
6054 Exit;
6055 Pl := g_Player_Get(gSpectPID1);
6056 if (Pl <> nil) and (Pl.Team = Team) then
6057 begin
6058 Result := True;
6059 Exit;
6060 end;
6061 if gSpectViewTwo then
6062 begin
6063 Pl := g_Player_Get(gSpectPID2);
6064 if (Pl <> nil) and (Pl.Team = Team) then
6065 begin
6066 Result := True;
6067 Exit;
6068 end;
6069 end;
6070 end;
6072 procedure g_Game_Message(Msg: string; Time: Word);
6073 begin
6074 MessageText := b_Text_Format(Msg);
6075 MessageTime := Time;
6076 end;
6078 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6079 var
6080 a: Integer;
6081 begin
6082 case gAnnouncer of
6083 ANNOUNCE_NONE:
6084 Exit;
6085 ANNOUNCE_ME,
6086 ANNOUNCE_MEPLUS:
6087 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6088 Exit;
6089 end;
6090 for a := 0 to 3 do
6091 if goodsnd[a].IsPlaying() then
6092 Exit;
6094 goodsnd[Random(4)].Play();
6095 end;
6097 procedure g_Game_Announce_KillCombo(Param: Integer);
6098 var
6099 UID: Word;
6100 c, n: Byte;
6101 Pl: TPlayer;
6102 Name: String;
6103 begin
6104 UID := Param and $FFFF;
6105 c := Param shr 16;
6106 if c < 2 then
6107 Exit;
6109 Pl := g_Player_Get(UID);
6110 if Pl = nil then
6111 Name := '?'
6112 else
6113 Name := Pl.Name;
6115 case c of
6116 2: begin
6117 n := 0;
6118 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6119 end;
6120 3: begin
6121 n := 1;
6122 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6123 end;
6124 4: begin
6125 n := 2;
6126 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6127 end;
6128 else begin
6129 n := 3;
6130 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6131 end;
6132 end;
6134 case gAnnouncer of
6135 ANNOUNCE_NONE:
6136 Exit;
6137 ANNOUNCE_ME:
6138 if not g_Game_IsWatchedPlayer(UID) then
6139 Exit;
6140 ANNOUNCE_MEPLUS:
6141 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6142 Exit;
6143 end;
6145 if killsnd[n].IsPlaying() then
6146 killsnd[n].Stop();
6147 killsnd[n].Play();
6148 end;
6150 procedure g_Game_StartVote(Command, Initiator: string);
6151 var
6152 Need: Integer;
6153 begin
6154 if not gVotesEnabled then Exit;
6155 if gGameSettings.GameType <> GT_SERVER then Exit;
6156 if gVoteInProgress or gVotePassed then
6157 begin
6158 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6159 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6160 Exit;
6161 end;
6162 gVoteInProgress := True;
6163 gVotePassed := False;
6164 gVoteTimer := gTime + gVoteTimeout * 1000;
6165 gVoteCount := 0;
6166 gVoted := False;
6167 gVoteCommand := Command;
6169 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6170 Need := Floor((NetClientCount+1)/2.0)+1
6171 else
6172 Need := Floor(NetClientCount/2.0)+1;
6173 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6174 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6175 end;
6177 procedure g_Game_CheckVote;
6178 var
6179 I, Need: Integer;
6180 begin
6181 if gGameSettings.GameType <> GT_SERVER then Exit;
6182 if not gVoteInProgress then Exit;
6184 if (gTime >= gVoteTimer) then
6185 begin
6186 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6187 Need := Floor((NetClientCount+1)/2.0) + 1
6188 else
6189 Need := Floor(NetClientCount/2.0) + 1;
6190 if gVoteCount >= Need then
6191 begin
6192 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6193 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6194 gVotePassed := True;
6195 gVoteCmdTimer := gTime + 5000;
6196 end
6197 else
6198 begin
6199 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6200 MH_SEND_VoteEvent(NET_VE_FAILED);
6201 end;
6202 if NetClients <> nil then
6203 for I := Low(NetClients) to High(NetClients) do
6204 if NetClients[i].Used then
6205 NetClients[i].Voted := False;
6206 gVoteInProgress := False;
6207 gVoted := False;
6208 gVoteCount := 0;
6209 end
6210 else
6211 begin
6212 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6213 Need := Floor((NetClientCount+1)/2.0) + 1
6214 else
6215 Need := Floor(NetClientCount/2.0) + 1;
6216 if gVoteCount >= Need then
6217 begin
6218 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6219 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6220 gVoteInProgress := False;
6221 gVotePassed := True;
6222 gVoteCmdTimer := gTime + 5000;
6223 gVoted := False;
6224 gVoteCount := 0;
6225 if NetClients <> nil then
6226 for I := Low(NetClients) to High(NetClients) do
6227 if NetClients[i].Used then
6228 NetClients[i].Voted := False;
6229 end;
6230 end;
6231 end;
6233 procedure g_Game_LoadMapList(FileName: string);
6234 var
6235 ListFile: TextFile;
6236 s: string;
6237 begin
6238 MapList := nil;
6239 MapIndex := -1;
6241 if not FileExists(FileName) then Exit;
6243 AssignFile(ListFile, FileName);
6244 Reset(ListFile);
6245 while not EOF(ListFile) do
6246 begin
6247 ReadLn(ListFile, s);
6249 s := Trim(s);
6250 if s = '' then Continue;
6252 SetLength(MapList, Length(MapList)+1);
6253 MapList[High(MapList)] := s;
6254 end;
6255 CloseFile(ListFile);
6256 end;
6258 procedure g_Game_SetDebugMode();
6259 begin
6260 gDebugMode := True;
6261 // ×èòû (äàæå â ñâîåé èãðå):
6262 gCheats := True;
6263 end;
6265 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6266 var
6267 i: Word;
6268 begin
6269 if Length(LoadingStat.Msgs) = 0 then
6270 Exit;
6272 with LoadingStat do
6273 begin
6274 if not reWrite then
6275 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6276 if NextMsg = Length(Msgs) then
6277 begin // scroll
6278 for i := 0 to High(Msgs)-1 do
6279 Msgs[i] := Msgs[i+1];
6280 end
6281 else
6282 Inc(NextMsg);
6283 end else
6284 if NextMsg = 0 then
6285 Inc(NextMsg);
6287 Msgs[NextMsg-1] := Text;
6288 CurValue := 0;
6289 MaxValue := Max;
6290 ShowCount := 0;
6291 end;
6293 g_ActiveWindow := nil;
6295 ProcessLoading;
6296 end;
6298 procedure g_Game_StepLoading();
6299 begin
6300 with LoadingStat do
6301 begin
6302 Inc(CurValue);
6303 Inc(ShowCount);
6304 if (ShowCount > LOADING_SHOW_STEP) then
6305 begin
6306 ShowCount := 0;
6307 ProcessLoading;
6308 end;
6309 end;
6310 end;
6312 procedure g_Game_ClearLoading();
6313 var
6314 len: Word;
6315 begin
6316 with LoadingStat do
6317 begin
6318 CurValue := 0;
6319 MaxValue := 0;
6320 ShowCount := 0;
6321 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6322 if len < 1 then len := 1;
6323 SetLength(Msgs, len);
6324 for len := Low(Msgs) to High(Msgs) do
6325 Msgs[len] := '';
6326 NextMsg := 0;
6327 end;
6328 end;
6330 procedure Parse_Params(var pars: TParamStrValues);
6331 var
6332 i: Integer;
6333 s: String;
6334 begin
6335 SetLength(pars, 0);
6336 i := 1;
6337 while i <= ParamCount do
6338 begin
6339 s := ParamStr(i);
6340 if (s[1] = '-') and (Length(s) > 1) then
6341 begin
6342 if (s[2] = '-') and (Length(s) > 2) then
6343 begin // Îäèíî÷íûé ïàðàìåòð
6344 SetLength(pars, Length(pars) + 1);
6345 with pars[High(pars)] do
6346 begin
6347 Name := LowerCase(s);
6348 Value := '+';
6349 end;
6350 end
6351 else
6352 if (i < ParamCount) then
6353 begin // Ïàðàìåòð ñî çíà÷åíèåì
6354 Inc(i);
6355 SetLength(pars, Length(pars) + 1);
6356 with pars[High(pars)] do
6357 begin
6358 Name := LowerCase(s);
6359 Value := LowerCase(ParamStr(i));
6360 end;
6361 end;
6362 end;
6364 Inc(i);
6365 end;
6366 end;
6368 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6369 var
6370 i: Integer;
6371 begin
6372 Result := '';
6373 for i := 0 to High(pars) do
6374 if pars[i].Name = aName then
6375 begin
6376 Result := pars[i].Value;
6377 Break;
6378 end;
6379 end;
6381 procedure g_Game_Process_Params();
6382 var
6383 pars: TParamStrValues;
6384 map: String;
6385 GMode, n: Byte;
6386 LimT, LimS: Integer;
6387 Opt: LongWord;
6388 Lives: Integer;
6389 s: String;
6390 Port: Integer;
6391 ip: String;
6392 F: TextFile;
6393 begin
6394 Parse_Params(pars);
6396 // Debug mode:
6397 s := Find_Param_Value(pars, '--debug');
6398 if (s <> '') then
6399 g_Game_SetDebugMode();
6401 // Connect when game loads
6402 ip := Find_Param_Value(pars, '-connect');
6404 if ip <> '' then
6405 begin
6406 s := Find_Param_Value(pars, '-port');
6407 if (s = '') or not TryStrToInt(s, Port) then
6408 Port := 25666;
6410 s := Find_Param_Value(pars, '-pw');
6412 g_Game_StartClient(ip, Port, s);
6413 Exit;
6414 end;
6416 // Start map when game loads:
6417 map := LowerCase(Find_Param_Value(pars, '-map'));
6418 if isWadPath(map) then
6419 begin
6420 // Game mode:
6421 s := Find_Param_Value(pars, '-gm');
6422 GMode := g_Game_TextToMode(s);
6423 if GMode = GM_NONE then GMode := GM_DM;
6424 if GMode = GM_SINGLE then GMode := GM_COOP;
6426 // Time limit:
6427 s := Find_Param_Value(pars, '-limt');
6428 if (s = '') or (not TryStrToInt(s, LimT)) then
6429 LimT := 0;
6430 if LimT < 0 then
6431 LimT := 0;
6433 // Goal limit:
6434 s := Find_Param_Value(pars, '-lims');
6435 if (s = '') or (not TryStrToInt(s, LimS)) then
6436 LimS := 0;
6437 if LimS < 0 then
6438 LimS := 0;
6440 // Lives limit:
6441 s := Find_Param_Value(pars, '-lives');
6442 if (s = '') or (not TryStrToInt(s, Lives)) then
6443 Lives := 0;
6444 if Lives < 0 then
6445 Lives := 0;
6447 // Options:
6448 s := Find_Param_Value(pars, '-opt');
6449 if (s = '') then
6450 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6451 else
6452 Opt := StrToIntDef(s, 0);
6453 if Opt = 0 then
6454 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6456 // Close after map:
6457 s := Find_Param_Value(pars, '--close');
6458 if (s <> '') then
6459 gMapOnce := True;
6461 // Delete test map after play:
6462 s := Find_Param_Value(pars, '--testdelete');
6463 if (s <> '') then
6464 begin
6465 gMapToDelete := MapsDir + map;
6466 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6467 Halt(1);
6468 end;
6470 // Delete temporary WAD after play:
6471 s := Find_Param_Value(pars, '--tempdelete');
6472 if (s <> '') then
6473 begin
6474 gMapToDelete := MapsDir + map;
6475 gTempDelete := True;
6476 end;
6478 // Number of players:
6479 s := Find_Param_Value(pars, '-pl');
6480 if (s = '') then
6481 n := 1
6482 else
6483 n := StrToIntDef(s, 1);
6485 // Start:
6486 s := Find_Param_Value(pars, '-port');
6487 if (s = '') or not TryStrToInt(s, Port) then
6488 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6489 else
6490 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6491 end;
6493 // Execute script when game loads:
6494 s := Find_Param_Value(pars, '-exec');
6495 if s <> '' then
6496 begin
6497 if Pos(':\', s) = 0 then
6498 s := GameDir + '/' + s;
6500 {$I-}
6501 AssignFile(F, s);
6502 Reset(F);
6503 if IOResult <> 0 then
6504 begin
6505 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6506 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6507 CloseFile(F);
6508 Exit;
6509 end;
6510 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6511 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6513 while not EOF(F) do
6514 begin
6515 ReadLn(F, s);
6516 if IOResult <> 0 then
6517 begin
6518 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6519 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6520 CloseFile(F);
6521 Exit;
6522 end;
6523 if Pos('#', s) <> 1 then // script comment
6524 g_Console_Process(s, True);
6525 end;
6527 CloseFile(F);
6528 {$I+}
6529 end;
6531 SetLength(pars, 0);
6532 end;
6534 end.