DEADSOFTWARE

add direct weapon switching
[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, 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 gMusic := TMusic.Create();
1109 gMusic.SetByName('MUSIC_MENU');
1110 gMusic.Play();
1112 gGameSettings.WarmupTime := 30;
1114 gState := STATE_MENU;
1116 SetLength(gEvents, 6);
1117 gEvents[0].Name := 'ongamestart';
1118 gEvents[1].Name := 'ongameend';
1119 gEvents[2].Name := 'onmapstart';
1120 gEvents[3].Name := 'onmapend';
1121 gEvents[4].Name := 'oninter';
1122 gEvents[5].Name := 'onwadend';
1123 finally
1124 sfsGCEnable(); // enable releasing unused volumes
1125 end;
1126 end;
1128 procedure g_Game_Free();
1129 begin
1130 if NetMode = NET_CLIENT then g_Net_Disconnect();
1131 if NetMode = NET_SERVER then g_Net_Host_Die();
1133 g_Map_Free();
1134 g_Player_Free();
1135 g_Player_RemoveAllCorpses();
1137 gGameSettings.GameType := GT_NONE;
1138 if gGameSettings.GameMode = GM_SINGLE then
1139 gGameSettings.GameMode := GM_DM;
1140 gSwitchGameMode := gGameSettings.GameMode;
1142 gChatShow := False;
1143 gExitByTrigger := False;
1144 end;
1146 function IsActivePlayer(p: TPlayer): Boolean;
1147 begin
1148 Result := False;
1149 if p = nil then
1150 Exit;
1151 Result := (not p.FDummy) and (not p.FSpectator);
1152 end;
1154 function GetActivePlayer_ByID(ID: Integer): TPlayer;
1155 var
1156 a: Integer;
1157 begin
1158 Result := nil;
1159 if ID < 0 then
1160 Exit;
1161 if gPlayers = nil then
1162 Exit;
1163 for a := Low(gPlayers) to High(gPlayers) do
1164 if IsActivePlayer(gPlayers[a]) then
1165 begin
1166 if gPlayers[a].UID <> ID then
1167 continue;
1168 Result := gPlayers[a];
1169 break;
1170 end;
1171 end;
1173 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1174 var
1175 a, idx: Integer;
1176 ids: Array of Word;
1177 begin
1178 Result := -1;
1179 if gPlayers = nil then
1180 Exit;
1181 SetLength(ids, 0);
1182 idx := -1;
1183 for a := Low(gPlayers) to High(gPlayers) do
1184 if IsActivePlayer(gPlayers[a]) then
1185 begin
1186 SetLength(ids, Length(ids) + 1);
1187 ids[High(ids)] := gPlayers[a].UID;
1188 if gPlayers[a].UID = Skip then
1189 idx := High(ids);
1190 end;
1191 if Length(ids) = 0 then
1192 Exit;
1193 if idx = -1 then
1194 Result := ids[0]
1195 else
1196 Result := ids[(idx + 1) mod Length(ids)];
1197 end;
1199 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1200 var
1201 a, idx: Integer;
1202 ids: Array of Word;
1203 begin
1204 Result := -1;
1205 if gPlayers = nil then
1206 Exit;
1207 SetLength(ids, 0);
1208 idx := -1;
1209 for a := Low(gPlayers) to High(gPlayers) do
1210 if IsActivePlayer(gPlayers[a]) then
1211 begin
1212 SetLength(ids, Length(ids) + 1);
1213 ids[High(ids)] := gPlayers[a].UID;
1214 if gPlayers[a].UID = Skip then
1215 idx := High(ids);
1216 end;
1217 if Length(ids) = 0 then
1218 Exit;
1219 if idx = -1 then
1220 Result := ids[Length(ids) - 1]
1221 else
1222 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1223 end;
1225 function isKeyPressed (key1: Word; key2: Word): Boolean;
1226 begin
1227 if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
1228 if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
1229 result := false;
1230 end;
1232 procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false);
1233 var
1234 time: Word;
1235 strafeDir: Byte;
1236 i: Integer;
1237 begin
1238 if (plr = nil) then exit;
1239 if (p2hack) then time := 1000 else time := 1;
1240 strafeDir := MoveButton shr 4;
1241 MoveButton := MoveButton and $0F;
1242 with ctrl do
1243 begin
1244 if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1245 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1246 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1248 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1249 if MoveButton = 1 then plr.PressKey(KEY_LEFT, time)
1250 else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time);
1252 // if we have "strafe" key, turn off old strafe mechanics
1253 if isKeyPressed(KeyStrafe, KeyStrafe2) then
1254 begin
1255 // new strafe mechanics
1256 if (strafeDir = 0) then strafeDir := MoveButton; // start strafing
1257 // now set direction according to strafe (reversed)
1258 if (strafeDir = 2) then plr.SetDirection(D_LEFT)
1259 else if (strafeDir = 1) then plr.SetDirection(D_RIGHT);
1260 end
1261 else
1262 begin
1263 strafeDir := 0; // not strafing anymore
1264 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1265 if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(D_LEFT)
1266 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1267 else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(D_RIGHT)
1268 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1269 else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1));
1270 end;
1272 // fix movebutton state
1273 MoveButton := MoveButton or (strafeDir shl 4);
1275 // Îñòàëüíûå êëàâèøè:
1276 if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time);
1277 if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time);
1278 if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time);
1279 if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE);
1280 if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON);
1281 if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON);
1282 if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN);
1284 for i := 0 to High(KeyWeapon) do
1285 if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then
1286 begin
1287 plr.ForceWeapon(i);
1288 Break;
1289 end;
1290 end;
1291 end;
1293 procedure g_Game_Update();
1294 var
1295 Msg: g_gui.TMessage;
1296 Time: Int64;
1297 a: Byte;
1298 w: Word;
1299 i, b: Integer;
1300 begin
1301 // Ïîðà âûêëþ÷àòü èãðó:
1302 if gExit = EXIT_QUIT then
1303 Exit;
1304 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1305 if gExit <> 0 then
1306 begin
1307 EndGame();
1308 if gExit = EXIT_QUIT then
1309 Exit;
1310 end;
1312 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1313 e_PollInput();
1315 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1316 g_Console_Update();
1318 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1319 begin
1320 gExit := EXIT_SIMPLE;
1321 EndGame();
1322 Exit;
1323 end;
1325 case gState of
1326 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1327 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1328 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1329 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1330 begin
1331 if g_Game_IsNet and g_Game_IsServer then
1332 begin
1333 gInterTime := gInterTime + GAME_TICK;
1334 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1335 if a <> gServInterTime then
1336 begin
1337 gServInterTime := a;
1338 MH_SEND_TimeSync(gServInterTime);
1339 end;
1340 end;
1342 if (not g_Game_IsClient) and
1345 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1346 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1347 and (g_ActiveWindow = nil)
1349 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1351 then
1352 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1353 g_Game_StopAllSounds(True);
1355 if gMapOnce then // Ýòî áûë òåñò
1356 gExit := EXIT_SIMPLE
1357 else
1358 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1359 g_Game_ChangeMap(gNextMap)
1360 else // Ñëåäóþùåé êàðòû íåò
1361 begin
1362 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1363 begin
1364 // Âûõîä â ãëàâíîå ìåíþ:
1365 g_Game_Free;
1366 g_GUI_ShowWindow('MainMenu');
1367 gMusic.SetByName('MUSIC_MENU');
1368 gMusic.Play();
1369 gState := STATE_MENU;
1370 end else
1371 begin
1372 // Ôèíàëüíàÿ êàðòèíêà:
1373 g_Game_ExecuteEvent('onwadend');
1374 g_Game_Free();
1375 if not gMusic.SetByName('MUSIC_endmus') then
1376 gMusic.SetByName('MUSIC_STDENDMUS');
1377 gMusic.Play();
1378 gState := STATE_ENDPIC;
1379 end;
1380 g_Game_ExecuteEvent('ongameend');
1381 end;
1383 Exit;
1384 end;
1386 if gState = STATE_INTERTEXT then
1387 if InterText.counter > 0 then
1388 InterText.counter := InterText.counter - 1;
1389 end;
1391 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1392 begin
1393 if EndingGameCounter = 0 then
1394 begin
1395 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1396 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1397 begin
1398 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1399 begin
1400 g_Game_ExecuteEvent('onwadend');
1401 if not gMusic.SetByName('MUSIC_endmus') then
1402 gMusic.SetByName('MUSIC_STDENDMUS');
1403 end
1404 else
1405 gMusic.SetByName('MUSIC_ROUNDMUS');
1407 gMusic.Play();
1408 gState := STATE_INTERCUSTOM;
1409 end
1410 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1411 begin
1412 gMusic.SetByName('MUSIC_INTERMUS');
1413 gMusic.Play();
1414 gState := STATE_INTERSINGLE;
1415 end;
1416 g_Game_ExecuteEvent('oninter');
1417 end
1418 else
1419 DecMin(EndingGameCounter, 6, 0);
1420 end;
1422 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1423 begin
1424 if gMapOnce then // Ýòî áûë òåñò
1425 begin
1426 gExit := EXIT_SIMPLE;
1427 Exit;
1428 end;
1429 end;
1431 STATE_SLIST:
1432 g_Serverlist_Control(slCurrent);
1433 end;
1435 if g_Game_IsNet then
1436 if not gConsoleShow then
1437 if not gChatShow then
1438 begin
1439 if g_ActiveWindow = nil then
1440 begin
1441 if e_KeyPressed(gGameControls.GameControls.Chat) then
1442 g_Console_Chat_Switch(False)
1443 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1444 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1445 g_Console_Chat_Switch(True);
1446 end;
1447 end else
1448 if not gChatEnter then
1449 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1450 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1451 gChatEnter := True;
1453 // Ñòàòèñòèêà ïî Tab:
1454 if gGameOn then
1455 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1456 (gGameSettings.GameType <> GT_SINGLE) and
1457 e_KeyPressed(gGameControls.GameControls.Stat);
1459 // Èãðà èäåò:
1460 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1461 begin
1462 // Âðåìÿ += 28 ìèëëèñåêóíä:
1463 gTime := gTime + GAME_TICK;
1465 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1466 if MessageTime = 0 then
1467 MessageText := '';
1468 if MessageTime > 0 then
1469 MessageTime := MessageTime - 1;
1471 if (g_Game_IsServer) then
1472 begin
1473 // Áûë çàäàí ëèìèò âðåìåíè:
1474 if (gGameSettings.TimeLimit > 0) then
1475 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1476 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1477 g_Game_NextLevel();
1478 Exit;
1479 end;
1481 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1482 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1483 g_Game_RestartRound(gLMSSoftSpawn);
1485 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1486 if gVoteInProgress and (gVoteTimer < gTime) then
1487 g_Game_CheckVote
1488 else if gVotePassed and (gVoteCmdTimer < gTime) then
1489 begin
1490 g_Console_Process(gVoteCommand);
1491 gVoteCommand := '';
1492 gVotePassed := False;
1493 end;
1495 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1496 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1497 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1498 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1499 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1501 // Áûë çàäàí ëèìèò ïîáåä:
1502 if (gGameSettings.GoalLimit > 0) then
1503 begin
1504 b := 0;
1506 if gGameSettings.GameMode = GM_DM then
1507 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1508 for i := 0 to High(gPlayers) do
1509 if gPlayers[i] <> nil then
1510 if gPlayers[i].Frags > b then
1511 b := gPlayers[i].Frags;
1512 end
1513 else
1514 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1515 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1516 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1517 end;
1519 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1520 if b >= gGameSettings.GoalLimit then
1521 begin
1522 g_Game_NextLevel();
1523 Exit;
1524 end;
1525 end;
1527 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1528 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1529 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1530 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1531 begin
1532 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1533 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1534 end // if not console
1535 else
1536 begin
1537 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1538 end;
1539 end; // if server
1541 // Íàáëþäàòåëü
1542 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1543 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1544 begin
1545 if not gSpectKeyPress then
1546 begin
1547 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1548 begin
1549 // switch spect mode
1550 case gSpectMode of
1551 SPECT_NONE: ; // not spectator
1552 SPECT_STATS,
1553 SPECT_MAPVIEW: Inc(gSpectMode);
1554 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1555 end;
1556 gSpectKeyPress := True;
1557 end;
1558 if gSpectMode = SPECT_MAPVIEW then
1559 begin
1560 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1561 gSpectX := Max(gSpectX - gSpectStep, 0);
1562 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1563 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1564 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1565 gSpectY := Max(gSpectY - gSpectStep, 0);
1566 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1567 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1568 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1569 begin
1570 // decrease step
1571 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1572 gSpectKeyPress := True;
1573 end;
1574 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1575 begin
1576 // increase step
1577 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1578 gSpectKeyPress := True;
1579 end;
1580 end;
1581 if gSpectMode = SPECT_PLAYERS then
1582 begin
1583 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1584 begin
1585 // add second view
1586 gSpectViewTwo := True;
1587 gSpectKeyPress := True;
1588 end;
1589 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1590 begin
1591 // remove second view
1592 gSpectViewTwo := False;
1593 gSpectKeyPress := True;
1594 end;
1595 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1596 begin
1597 // prev player (view 1)
1598 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1599 gSpectKeyPress := True;
1600 end;
1601 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1602 begin
1603 // next player (view 1)
1604 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1605 gSpectKeyPress := True;
1606 end;
1607 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1608 begin
1609 // prev player (view 2)
1610 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1611 gSpectKeyPress := True;
1612 end;
1613 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1614 begin
1615 // next player (view 2)
1616 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1617 gSpectKeyPress := True;
1618 end;
1619 end;
1620 end
1621 else
1622 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1623 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1624 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1625 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1626 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1627 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1628 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1629 gSpectKeyPress := False;
1630 end;
1632 // Îáíîâëÿåì âñå îñòàëüíîå:
1633 g_Map_Update();
1634 g_Items_Update();
1635 g_Triggers_Update();
1636 g_Weapon_Update();
1637 g_Monsters_Update();
1638 g_GFX_Update();
1639 g_Player_UpdateAll();
1640 g_Player_UpdatePhysicalObjects();
1641 if gGameSettings.GameType = GT_SERVER then
1642 if Length(gMonstersSpawned) > 0 then
1643 begin
1644 for I := 0 to High(gMonstersSpawned) do
1645 MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1646 SetLength(gMonstersSpawned, 0);
1647 end;
1649 if (gSoundTriggerTime > 8) then
1650 begin
1651 g_Game_UpdateTriggerSounds();
1652 gSoundTriggerTime := 0;
1653 end
1654 else
1655 Inc(gSoundTriggerTime);
1657 if (NetMode = NET_SERVER) then
1658 begin
1659 Inc(NetTimeToUpdate);
1660 Inc(NetTimeToReliable);
1661 if NetTimeToReliable >= NetRelupdRate then
1662 begin
1663 for I := 0 to High(gPlayers) do
1664 if gPlayers[I] <> nil then
1665 MH_SEND_PlayerPos(True, gPlayers[I].UID);
1667 if gMonsters <> nil then
1668 for I := 0 to High(gMonsters) do
1669 if gMonsters[I] <> nil then
1670 begin
1671 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1672 begin
1673 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1674 MH_SEND_MonsterPos(gMonsters[I].UID);
1675 end
1676 else
1677 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1678 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1679 (gMonsters[I].GameVelX <> 0) or
1680 (gMonsters[I].GameVelY <> 0) then
1681 MH_SEND_MonsterPos(gMonsters[I].UID);
1682 end;
1684 NetTimeToReliable := 0;
1685 NetTimeToUpdate := NetUpdateRate;
1686 end
1687 else if NetTimeToUpdate >= NetUpdateRate then
1688 begin
1689 if gPlayers <> nil then
1690 for I := 0 to High(gPlayers) do
1691 if gPlayers[I] <> nil then
1692 MH_SEND_PlayerPos(False, gPlayers[I].UID);
1694 if gMonsters <> nil then
1695 for I := 0 to High(gMonsters) do
1696 if gMonsters[I] <> nil then
1697 begin
1698 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1699 begin
1700 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1701 MH_SEND_MonsterPos(gMonsters[I].UID);
1702 end
1703 else
1704 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1705 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1706 (gMonsters[I].GameVelX <> 0) or
1707 (gMonsters[I].GameVelY <> 0) then
1708 MH_SEND_MonsterPos(gMonsters[I].UID);
1709 end;
1711 NetTimeToUpdate := 0;
1712 end;
1714 if NetUseMaster then
1715 if gTime >= NetTimeToMaster then
1716 begin
1717 if (NetMHost = nil) or (NetMPeer = nil) then
1718 if not g_Net_Slist_Connect then
1719 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1721 g_Net_Slist_Update;
1722 NetTimeToMaster := gTime + NetMasterRate;
1723 end;
1724 end
1725 else
1726 if NetMode = NET_CLIENT then
1727 MC_SEND_PlayerPos();
1728 end; // if gameOn ...
1730 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1731 if g_ActiveWindow <> nil then
1732 begin
1733 w := e_GetFirstKeyPressed();
1735 if (w <> IK_INVALID) then
1736 begin
1737 Msg.Msg := MESSAGE_DIKEY;
1738 Msg.wParam := w;
1739 g_ActiveWindow.OnMessage(Msg);
1740 end;
1742 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1743 if g_ActiveWindow <> nil then
1744 g_ActiveWindow.Update();
1746 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1747 if gResolutionChange then
1748 begin
1749 e_WriteLog('Changing resolution', MSG_NOTIFY);
1750 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1751 gResolutionChange := False;
1752 end;
1754 // Íóæíî ñìåíèòü ÿçûê:
1755 if gLanguageChange then
1756 begin
1757 //e_WriteLog('Read language file', MSG_NOTIFY);
1758 //g_Language_Load(DataDir + gLanguage + '.txt');
1759 g_Language_Set(gLanguage);
1760 g_Menu_Reset();
1761 gLanguageChange := False;
1762 end;
1763 end;
1765 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
1766 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
1767 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
1768 begin
1769 g_TakeScreenShot();
1770 LastScreenShot := GetTimer();
1771 end;
1773 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
1774 if e_KeyPressed(IK_F10) and
1775 gGameOn and
1776 (not gConsoleShow) and
1777 (g_ActiveWindow = nil) then
1778 begin
1779 KeyPress(IK_F10);
1780 end;
1782 Time := GetTimer() {div 1000};
1784 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
1785 if gDelayedEvents <> nil then
1786 for a := 0 to High(gDelayedEvents) do
1787 if gDelayedEvents[a].Pending and
1789 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
1790 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
1791 ) then
1792 begin
1793 case gDelayedEvents[a].DEType of
1794 DE_GLOBEVENT:
1795 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
1796 DE_BFGHIT:
1797 if gGameOn then
1798 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
1799 DE_KILLCOMBO:
1800 if gGameOn then
1801 begin
1802 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
1803 if g_Game_IsNet and g_Game_IsServer then
1804 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
1805 end;
1806 end;
1807 gDelayedEvents[a].Pending := False;
1808 end;
1810 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
1811 UPSCounter := UPSCounter + 1;
1812 if Time - UPSTime >= 1000 then
1813 begin
1814 UPS := UPSCounter;
1815 UPSCounter := 0;
1816 UPSTime := Time;
1817 end;
1818 end;
1820 procedure g_Game_LoadData();
1821 begin
1822 if DataLoaded then Exit;
1824 e_WriteLog('Loading game data...', MSG_NOTIFY);
1826 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
1827 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
1828 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
1829 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
1830 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
1831 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
1832 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB');
1833 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS');
1834 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD');
1835 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB');
1836 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS');
1837 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD');
1838 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
1839 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
1840 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
1841 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
1842 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
1843 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
1844 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
1845 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
1846 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
1847 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
1848 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
1849 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
1850 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
1851 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
1852 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
1853 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
1854 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
1855 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
1856 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
1857 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
1858 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
1859 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
1861 goodsnd[0] := TPlayableSound.Create();
1862 goodsnd[1] := TPlayableSound.Create();
1863 goodsnd[2] := TPlayableSound.Create();
1864 goodsnd[3] := TPlayableSound.Create();
1866 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
1867 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
1868 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
1869 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
1871 killsnd[0] := TPlayableSound.Create();
1872 killsnd[1] := TPlayableSound.Create();
1873 killsnd[2] := TPlayableSound.Create();
1874 killsnd[3] := TPlayableSound.Create();
1876 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
1877 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
1878 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
1879 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
1881 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
1882 g_Items_LoadData();
1884 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
1885 g_Weapon_LoadData();
1887 g_Monsters_LoadData();
1889 DataLoaded := True;
1890 end;
1892 procedure g_Game_FreeData();
1893 begin
1894 if not DataLoaded then Exit;
1896 g_Items_FreeData();
1897 g_Weapon_FreeData();
1898 g_Monsters_FreeData();
1900 e_WriteLog('Releasing game data...', MSG_NOTIFY);
1902 g_Texture_Delete('NOTEXTURE');
1903 g_Texture_Delete('TEXTURE_PLAYER_HUD');
1904 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
1905 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
1906 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
1907 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
1908 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
1909 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
1910 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
1911 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
1912 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
1913 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
1914 g_Frames_DeleteByName('FRAMES_TELEPORT');
1915 g_Sound_Delete('SOUND_GAME_TELEPORT');
1916 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
1917 g_Sound_Delete('SOUND_GAME_DOOROPEN');
1918 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
1919 g_Sound_Delete('SOUND_GAME_BULK1');
1920 g_Sound_Delete('SOUND_GAME_BULK2');
1921 g_Sound_Delete('SOUND_GAME_BUBBLE1');
1922 g_Sound_Delete('SOUND_GAME_BUBBLE2');
1923 g_Sound_Delete('SOUND_GAME_SWITCH1');
1924 g_Sound_Delete('SOUND_GAME_SWITCH0');
1926 goodsnd[0].Free();
1927 goodsnd[1].Free();
1928 goodsnd[2].Free();
1929 goodsnd[3].Free();
1931 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
1932 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
1933 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
1934 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
1936 killsnd[0].Free();
1937 killsnd[1].Free();
1938 killsnd[2].Free();
1939 killsnd[3].Free();
1941 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
1942 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
1943 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
1944 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
1946 DataLoaded := False;
1947 end;
1949 procedure DrawCustomStat();
1950 var
1951 pc, x, y, w, _y,
1952 w1, w2, w3,
1953 t, p, m: Integer;
1954 ww1, hh1: Word;
1955 ww2, hh2, r, g, b, rr, gg, bb: Byte;
1956 s1, s2, topstr: String;
1957 begin
1958 e_TextureFontGetSize(gStdFont, ww2, hh2);
1960 e_PollInput();
1961 if e_KeyPressed(IK_TAB) then
1962 begin
1963 if not gStatsPressed then
1964 begin
1965 gStatsOff := not gStatsOff;
1966 gStatsPressed := True;
1967 end;
1968 end
1969 else
1970 gStatsPressed := False;
1972 if gStatsOff then
1973 begin
1974 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
1975 w := (Length(s1) * ww2) div 2;
1976 x := gScreenWidth div 2 - w;
1977 y := 8;
1978 e_TextureFontPrint(x, y, s1, gStdFont);
1979 Exit;
1980 end;
1982 if (gGameSettings.GameMode = GM_COOP) then
1983 begin
1984 if gMissionFailed then
1985 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
1986 else
1987 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
1988 end
1989 else
1990 topstr := _lc[I_MENU_INTER_ROUND_OVER];
1992 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
1993 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
1995 if g_Game_IsNet then
1996 begin
1997 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
1998 if not gChatShow then
1999 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2000 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2001 end;
2003 if g_Game_IsClient then
2004 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2005 else
2006 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2007 if not gChatShow then
2008 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2009 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2011 x := 32;
2012 y := 16+hh1+16;
2014 w := gScreenWidth-x*2;
2016 w2 := (w-16) div 6;
2017 w3 := w2;
2018 w1 := w-16-w2-w3;
2020 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2021 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2023 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2025 case CustomStat.GameMode of
2026 GM_DM:
2027 begin
2028 if gGameSettings.MaxLives = 0 then
2029 s1 := _lc[I_GAME_DM]
2030 else
2031 s1 := _lc[I_GAME_LMS];
2032 end;
2033 GM_TDM:
2034 begin
2035 if gGameSettings.MaxLives = 0 then
2036 s1 := _lc[I_GAME_TDM]
2037 else
2038 s1 := _lc[I_GAME_TLMS];
2039 end;
2040 GM_CTF: s1 := _lc[I_GAME_CTF];
2041 GM_COOP:
2042 begin
2043 if gGameSettings.MaxLives = 0 then
2044 s1 := _lc[I_GAME_COOP]
2045 else
2046 s1 := _lc[I_GAME_SURV];
2047 end;
2048 else s1 := '';
2049 end;
2051 _y := y+16;
2052 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2053 _y := _y+8;
2055 _y := _y+16;
2056 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2057 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2059 _y := _y+16;
2060 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2061 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2062 (CustomStat.GameTime div 1000 div 60) mod 60,
2063 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2065 pc := Length(CustomStat.PlayerStat);
2066 if pc = 0 then Exit;
2068 if CustomStat.GameMode = GM_COOP then
2069 begin
2070 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2071 _y := _y+32;
2072 s2 := _lc[I_GAME_MONSTERS];
2073 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2074 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2075 _y := _y+16;
2076 s2 := _lc[I_GAME_SECRETS];
2077 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2078 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2079 if gLastMap then
2080 begin
2081 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2082 _y := _y-16;
2083 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2084 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2085 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2086 _y := _y+16;
2087 s2 := _lc[I_GAME_SECRETS_TOTAL];
2088 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2089 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2090 end;
2091 end;
2093 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2094 begin
2095 _y := _y+16+16;
2097 with CustomStat do
2098 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2099 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2100 else s1 := _lc[I_GAME_WIN_DRAW];
2102 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2103 _y := _y+40;
2105 for t := TEAM_RED to TEAM_BLUE do
2106 begin
2107 if t = TEAM_RED then
2108 begin
2109 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2110 gStdFont, 255, 0, 0, 1);
2111 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2112 gStdFont, 255, 0, 0, 1);
2113 r := 255;
2114 g := 0;
2115 b := 0;
2116 end
2117 else
2118 begin
2119 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2120 gStdFont, 0, 0, 255, 1);
2121 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2122 gStdFont, 0, 0, 255, 1);
2123 r := 0;
2124 g := 0;
2125 b := 255;
2126 end;
2128 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2129 _y := _y+24;
2131 for p := 0 to High(CustomStat.PlayerStat) do
2132 if CustomStat.PlayerStat[p].Team = t then
2133 with CustomStat.PlayerStat[p] do
2134 begin
2135 if Spectator then
2136 begin
2137 rr := r div 2;
2138 gg := g div 2;
2139 bb := b div 2;
2140 end
2141 else
2142 begin
2143 rr := r;
2144 gg := g;
2145 bb := b;
2146 end;
2147 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2148 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2149 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2150 _y := _y+24;
2151 end;
2153 _y := _y+16+16;
2154 end;
2155 end
2156 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2157 begin
2158 _y := _y+40;
2159 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2160 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2161 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2163 _y := _y+24;
2164 for p := 0 to High(CustomStat.PlayerStat) do
2165 with CustomStat.PlayerStat[p] do
2166 begin
2167 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2169 if Spectator then
2170 r := 127
2171 else
2172 r := 255;
2174 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2175 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2176 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2177 _y := _y+24;
2178 end;
2179 end;
2180 end;
2182 procedure DrawSingleStat();
2183 var
2184 tm, key_x, val_x, y: Integer;
2185 w1, w2, h: Word;
2186 s1, s2: String;
2188 procedure player_stat(n: Integer);
2189 var
2190 kpm: Real;
2192 begin
2193 // "Kills: # / #":
2194 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2195 s2 := Format(' %d', [gTotalMonsters]);
2197 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2198 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2199 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2200 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2201 s1 := s1 + '/';
2202 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2203 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2205 // "Kills-per-minute: ##.#":
2206 s1 := _lc[I_MENU_INTER_KPM];
2207 if tm > 0 then
2208 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2209 else
2210 kpm := SingleStat.PlayerStat[n].Kills;
2211 s2 := Format(' %.1f', [kpm]);
2213 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2214 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2216 // "Secrets found: # / #":
2217 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2218 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2220 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2221 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2222 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2223 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2224 s1 := s1 + '/';
2225 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2226 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2227 end;
2229 begin
2230 // "Level Complete":
2231 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2232 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2234 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2235 s1 := _lc[I_MENU_INTER_KPM];
2236 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2237 Inc(w1, 16);
2238 s1 := ' 9999.9';
2239 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2241 key_x := (gScreenWidth-w1-w2) div 2;
2242 val_x := key_x + w1;
2244 // "Time: #:##:##":
2245 tm := SingleStat.GameTime div 1000;
2246 s1 := _lc[I_MENU_INTER_TIME];
2247 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2249 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2250 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2252 if SingleStat.TwoPlayers then
2253 begin
2254 // "Player 1":
2255 s1 := _lc[I_MENU_PLAYER_1];
2256 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2257 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2259 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2260 y := 176;
2261 player_stat(0);
2263 // "Player 2":
2264 s1 := _lc[I_MENU_PLAYER_2];
2265 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2266 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2268 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2269 y := 336;
2270 player_stat(1);
2271 end
2272 else
2273 begin
2274 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2275 y := 128;
2276 player_stat(0);
2277 end;
2278 end;
2280 procedure DrawLoadingStat();
2281 var
2282 ww, hh: Word;
2283 xx, yy, i: Integer;
2284 s: String;
2285 begin
2286 if Length(LoadingStat.Msgs) = 0 then
2287 Exit;
2289 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2290 yy := (gScreenHeight div 3);
2291 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2292 xx := (gScreenWidth div 3);
2294 with LoadingStat do
2295 for i := 0 to NextMsg-1 do
2296 begin
2297 if (i = (NextMsg-1)) and (MaxValue > 0) then
2298 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2299 else
2300 s := Msgs[i];
2302 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2303 yy := yy + LOADING_INTERLINE;
2304 end;
2305 end;
2307 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2308 var
2309 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2310 begin
2311 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2312 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2313 begin
2314 Scale := 1;
2315 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2316 ScaleSz := 16 div Scale;
2317 // Ðàçìåðû ìèíè-êàðòû:
2318 aX := max(gMapInfo.Width div ScaleSz, 1);
2319 aY := max(gMapInfo.Height div ScaleSz, 1);
2320 // Ðàìêà êàðòû:
2321 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2323 if gWalls <> nil then
2324 begin
2325 // Ðèñóåì ñòåíû:
2326 for a := 0 to High(gWalls) do
2327 with gWalls[a] do
2328 if PanelType <> 0 then
2329 begin
2330 // Ëåâûé âåðõíèé óãîë:
2331 aX := X div ScaleSz;
2332 aY := Y div ScaleSz;
2333 // Ðàçìåðû:
2334 aX2 := max(Width div ScaleSz, 1);
2335 aY2 := max(Height div ScaleSz, 1);
2336 // Ïðàâûé íèæíèé óãîë:
2337 aX2 := aX + aX2 - 1;
2338 aY2 := aY + aY2 - 1;
2340 case PanelType of
2341 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2342 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2343 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2344 end;
2345 end;
2346 end;
2347 if gSteps <> nil then
2348 begin
2349 // Ðèñóåì ñòóïåíè:
2350 for a := 0 to High(gSteps) do
2351 with gSteps[a] do
2352 if PanelType <> 0 then
2353 begin
2354 // Ëåâûé âåðõíèé óãîë:
2355 aX := X div ScaleSz;
2356 aY := Y div ScaleSz;
2357 // Ðàçìåðû:
2358 aX2 := max(Width div ScaleSz, 1);
2359 aY2 := max(Height div ScaleSz, 1);
2360 // Ïðàâûé íèæíèé óãîë:
2361 aX2 := aX + aX2 - 1;
2362 aY2 := aY + aY2 - 1;
2364 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2365 end;
2366 end;
2367 if gLifts <> nil then
2368 begin
2369 // Ðèñóåì ëèôòû:
2370 for a := 0 to High(gLifts) do
2371 with gLifts[a] do
2372 if PanelType <> 0 then
2373 begin
2374 // Ëåâûé âåðõíèé óãîë:
2375 aX := X div ScaleSz;
2376 aY := Y div ScaleSz;
2377 // Ðàçìåðû:
2378 aX2 := max(Width div ScaleSz, 1);
2379 aY2 := max(Height div ScaleSz, 1);
2380 // Ïðàâûé íèæíèé óãîë:
2381 aX2 := aX + aX2 - 1;
2382 aY2 := aY + aY2 - 1;
2384 case LiftType of
2385 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2386 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2387 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2388 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2389 end;
2390 end;
2391 end;
2392 if gWater <> nil then
2393 begin
2394 // Ðèñóåì âîäó:
2395 for a := 0 to High(gWater) do
2396 with gWater[a] do
2397 if PanelType <> 0 then
2398 begin
2399 // Ëåâûé âåðõíèé óãîë:
2400 aX := X div ScaleSz;
2401 aY := Y div ScaleSz;
2402 // Ðàçìåðû:
2403 aX2 := max(Width div ScaleSz, 1);
2404 aY2 := max(Height div ScaleSz, 1);
2405 // Ïðàâûé íèæíèé óãîë:
2406 aX2 := aX + aX2 - 1;
2407 aY2 := aY + aY2 - 1;
2409 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2410 end;
2411 end;
2412 if gAcid1 <> nil then
2413 begin
2414 // Ðèñóåì êèñëîòó 1:
2415 for a := 0 to High(gAcid1) do
2416 with gAcid1[a] do
2417 if PanelType <> 0 then
2418 begin
2419 // Ëåâûé âåðõíèé óãîë:
2420 aX := X div ScaleSz;
2421 aY := Y div ScaleSz;
2422 // Ðàçìåðû:
2423 aX2 := max(Width div ScaleSz, 1);
2424 aY2 := max(Height div ScaleSz, 1);
2425 // Ïðàâûé íèæíèé óãîë:
2426 aX2 := aX + aX2 - 1;
2427 aY2 := aY + aY2 - 1;
2429 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2430 end;
2431 end;
2432 if gAcid2 <> nil then
2433 begin
2434 // Ðèñóåì êèñëîòó 2:
2435 for a := 0 to High(gAcid2) do
2436 with gAcid2[a] do
2437 if PanelType <> 0 then
2438 begin
2439 // Ëåâûé âåðõíèé óãîë:
2440 aX := X div ScaleSz;
2441 aY := Y div ScaleSz;
2442 // Ðàçìåðû:
2443 aX2 := max(Width div ScaleSz, 1);
2444 aY2 := max(Height div ScaleSz, 1);
2445 // Ïðàâûé íèæíèé óãîë:
2446 aX2 := aX + aX2 - 1;
2447 aY2 := aY + aY2 - 1;
2449 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2450 end;
2451 end;
2452 if gPlayers <> nil then
2453 begin
2454 // Ðèñóåì èãðîêîâ:
2455 for a := 0 to High(gPlayers) do
2456 if gPlayers[a] <> nil then with gPlayers[a] do
2457 if Live then begin
2458 // Ëåâûé âåðõíèé óãîë:
2459 aX := Obj.X div ScaleSz + 1;
2460 aY := Obj.Y div ScaleSz + 1;
2461 // Ðàçìåðû:
2462 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2463 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2464 // Ïðàâûé íèæíèé óãîë:
2465 aX2 := aX + aX2 - 1;
2466 aY2 := aY + aY2 - 1;
2468 if gPlayers[a] = p then
2469 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2470 else
2471 case Team of
2472 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2473 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2474 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2475 end;
2476 end;
2477 end;
2478 if gMonsters <> nil then
2479 begin
2480 // Ðèñóåì ìîíñòðîâ:
2481 for a := 0 to High(gMonsters) do
2482 if gMonsters[a] <> nil then with gMonsters[a] do
2483 if Live then begin
2484 // Ëåâûé âåðõíèé óãîë:
2485 aX := Obj.X div ScaleSz + 1;
2486 aY := Obj.Y div ScaleSz + 1;
2487 // Ðàçìåðû:
2488 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2489 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2490 // Ïðàâûé íèæíèé óãîë:
2491 aX2 := aX + aX2 - 1;
2492 aY2 := aY + aY2 - 1;
2494 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2495 end;
2496 end;
2497 end;
2498 end;
2500 procedure DrawMapView(x, y, w, h: Integer);
2501 var
2502 bx, by: Integer;
2503 begin
2504 glPushMatrix();
2506 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2507 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2508 g_Map_DrawBack(-bx, -by);
2510 sX := x;
2511 sY := y;
2512 sWidth := w;
2513 sHeight := h;
2515 glTranslatef(-x, -y, 0);
2517 g_Map_DrawPanels(PANEL_BACK);
2518 g_Map_DrawPanels(PANEL_STEP);
2519 g_Items_Draw();
2520 g_Weapon_Draw();
2521 g_Player_DrawShells();
2522 g_Player_DrawAll();
2523 g_Player_DrawCorpses();
2524 g_Map_DrawPanels(PANEL_WALL);
2525 g_Monsters_Draw();
2526 g_Map_DrawPanels(PANEL_CLOSEDOOR);
2527 g_GFX_Draw();
2528 g_Map_DrawFlags();
2529 g_Map_DrawPanels(PANEL_ACID1);
2530 g_Map_DrawPanels(PANEL_ACID2);
2531 g_Map_DrawPanels(PANEL_WATER);
2532 g_Map_DrawPanels(PANEL_FORE);
2533 if g_debug_HealthBar then
2534 begin
2535 g_Monsters_DrawHealth();
2536 g_Player_DrawHealth();
2537 end;
2539 glPopMatrix();
2540 end;
2542 procedure DrawPlayer(p: TPlayer);
2543 var
2544 px, py, a, b, c, d: Integer;
2545 //R: TRect;
2546 begin
2547 if (p = nil) or (p.FDummy) then
2548 begin
2549 glPushMatrix();
2550 g_Map_DrawBack(0, 0);
2551 glPopMatrix();
2552 Exit;
2553 end;
2555 gPlayerDrawn := p;
2557 glPushMatrix();
2559 px := p.GameX + PLAYER_RECT_CX;
2560 py := p.GameY + PLAYER_RECT_CY;
2562 if px > (gPlayerScreenSize.X div 2) then
2563 a := -px + (gPlayerScreenSize.X div 2)
2564 else
2565 a := 0;
2566 if py > (gPlayerScreenSize.Y div 2) then
2567 b := -py + (gPlayerScreenSize.Y div 2)
2568 else
2569 b := 0;
2570 if px > (gMapInfo.Width - (gPlayerScreenSize.X div 2)) then
2571 a := -gMapInfo.Width + gPlayerScreenSize.X;
2572 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2573 b := -gMapInfo.Height + gPlayerScreenSize.Y;
2574 if gMapInfo.Width <= gPlayerScreenSize.X then
2575 a := 0;
2576 if gMapInfo.Height <= gPlayerScreenSize.Y then
2577 b := 0;
2579 if p.IncCam <> 0 then
2580 begin
2581 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2582 begin
2583 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2584 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2585 end;
2587 if py < (gPlayerScreenSize.Y div 2) then
2588 begin
2589 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2590 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2591 end;
2593 if p.IncCam < 0 then
2594 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and
2595 (p.IncCam < 0) do
2596 p.IncCam := p.IncCam + 1;
2598 if p.IncCam > 0 then
2599 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and
2600 (p.IncCam > 0) do
2601 p.IncCam := p.IncCam - 1;
2602 end;
2604 if (px< gPlayerScreenSize.X div 2) or
2605 (gMapInfo.Width-gPlayerScreenSize.X <= 256) then
2606 c := 0
2607 else
2608 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then
2609 c := gBackSize.X - gPlayerScreenSize.X
2610 else
2611 c := Round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2613 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or
2614 (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then
2615 d := 0
2616 else
2617 if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then
2618 d := gBackSize.Y - gPlayerScreenSize.Y
2619 else
2620 d := Round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
2622 g_Map_DrawBack(-c, -d);
2624 sX := -a;
2625 sY := -(b+p.IncCam);
2626 sWidth := gPlayerScreenSize.X;
2627 sHeight := gPlayerScreenSize.Y;
2629 glTranslatef(a, b+p.IncCam, 0);
2631 g_Map_DrawPanels(PANEL_BACK);
2632 g_Map_DrawPanels(PANEL_STEP);
2633 g_Items_Draw();
2634 g_Weapon_Draw();
2635 g_Player_DrawShells();
2636 g_Player_DrawAll();
2637 g_Player_DrawCorpses();
2638 g_Map_DrawPanels(PANEL_WALL);
2639 g_Monsters_Draw();
2640 g_Map_DrawPanels(PANEL_CLOSEDOOR);
2641 g_GFX_Draw();
2642 g_Map_DrawFlags();
2643 g_Map_DrawPanels(PANEL_ACID1);
2644 g_Map_DrawPanels(PANEL_ACID2);
2645 g_Map_DrawPanels(PANEL_WATER);
2646 g_Map_DrawPanels(PANEL_FORE);
2647 if g_debug_HealthBar then
2648 begin
2649 g_Monsters_DrawHealth();
2650 g_Player_DrawHealth();
2651 end;
2653 if p.FSpectator then
2654 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2655 p.GameY + PLAYER_RECT_CY - 4,
2656 'X', gStdFont, 255, 255, 255, 1, True);
2658 for a := 0 to High(gCollideMap) do
2659 for b := 0 to High(gCollideMap[a]) do
2660 begin
2661 d := 0;
2662 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2663 d := d + 1;
2664 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2665 d := d + 2;
2667 case d of
2668 1: e_DrawPoint(1, b, a, 200, 200, 200);
2669 2: e_DrawPoint(1, b, a, 64, 64, 255);
2670 3: e_DrawPoint(1, b, a, 255, 0, 255);
2671 end;
2672 end;
2675 glPopMatrix();
2677 p.DrawPain();
2678 p.DrawPickup();
2679 p.DrawRulez();
2680 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
2681 if g_Debug_Player then
2682 g_Player_DrawDebug(p);
2683 p.DrawGUI();
2684 end;
2686 procedure g_Game_Draw();
2687 var
2688 ID: DWORD;
2689 w, h: Word;
2690 ww, hh: Byte;
2691 Time: Int64;
2692 back: string;
2693 plView1, plView2: TPlayer;
2694 Split: Boolean;
2695 begin
2696 if gExit = EXIT_QUIT then Exit;
2698 Time := GetTimer() {div 1000};
2699 FPSCounter := FPSCounter+1;
2700 if Time - FPSTime >= 1000 then
2701 begin
2702 FPS := FPSCounter;
2703 FPSCounter := 0;
2704 FPSTime := Time;
2705 end;
2707 if gGameOn or (gState = STATE_FOLD) then
2708 begin
2709 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
2710 begin
2711 gSpectMode := SPECT_NONE;
2712 if not gRevertPlayers then
2713 begin
2714 plView1 := gPlayer1;
2715 plView2 := gPlayer2;
2716 end
2717 else
2718 begin
2719 plView1 := gPlayer2;
2720 plView2 := gPlayer1;
2721 end;
2722 end
2723 else
2724 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
2725 begin
2726 gSpectMode := SPECT_NONE;
2727 if gPlayer2 = nil then
2728 plView1 := gPlayer1
2729 else
2730 plView1 := gPlayer2;
2731 plView2 := nil;
2732 end
2733 else
2734 begin
2735 plView1 := nil;
2736 plView2 := nil;
2737 end;
2739 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
2740 gSpectMode := SPECT_STATS;
2742 if gSpectMode = SPECT_PLAYERS then
2743 if gPlayers <> nil then
2744 begin
2745 plView1 := GetActivePlayer_ByID(gSpectPID1);
2746 if plView1 = nil then
2747 begin
2748 gSpectPID1 := GetActivePlayerID_Next();
2749 plView1 := GetActivePlayer_ByID(gSpectPID1);
2750 end;
2751 if gSpectViewTwo then
2752 begin
2753 plView2 := GetActivePlayer_ByID(gSpectPID2);
2754 if plView2 = nil then
2755 begin
2756 gSpectPID2 := GetActivePlayerID_Next();
2757 plView2 := GetActivePlayer_ByID(gSpectPID2);
2758 end;
2759 end;
2760 end;
2762 if gSpectMode = SPECT_MAPVIEW then
2763 begin
2764 // Ðåæèì ïðîñìîòðà êàðòû
2765 Split := False;
2766 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
2767 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
2768 gHearPoint1.Active := True;
2769 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
2770 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
2771 gHearPoint2.Active := False;
2772 end
2773 else
2774 begin
2775 Split := (plView1 <> nil) and (plView2 <> nil);
2777 // Òî÷êè ñëóõà èãðîêîâ
2778 if plView1 <> nil then
2779 begin
2780 gHearPoint1.Active := True;
2781 gHearPoint1.Coords.X := plView1.GameX;
2782 gHearPoint1.Coords.Y := plView1.GameY;
2783 end else
2784 gHearPoint1.Active := False;
2785 if plView2 <> nil then
2786 begin
2787 gHearPoint2.Active := True;
2788 gHearPoint2.Coords.X := plView2.GameX;
2789 gHearPoint2.Coords.Y := plView2.GameY;
2790 end else
2791 gHearPoint2.Active := False;
2793 // Ðàçìåð ýêðàíîâ èãðîêîâ:
2794 gPlayerScreenSize.X := gScreenWidth-196;
2795 if Split then
2796 begin
2797 gPlayerScreenSize.Y := gScreenHeight div 2;
2798 if gScreenHeight mod 2 = 0 then
2799 Dec(gPlayerScreenSize.Y);
2800 end
2801 else
2802 gPlayerScreenSize.Y := gScreenHeight;
2804 if Split then
2805 if gScreenHeight mod 2 = 0 then
2806 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
2807 else
2808 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
2810 DrawPlayer(plView1);
2811 gPlayer1ScreenCoord.X := sX;
2812 gPlayer1ScreenCoord.Y := sY;
2814 if Split then
2815 begin
2816 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
2818 DrawPlayer(plView2);
2819 gPlayer2ScreenCoord.X := sX;
2820 gPlayer2ScreenCoord.Y := sY;
2821 end;
2823 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
2825 if Split then
2826 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
2827 end;
2829 if MessageText <> '' then
2830 begin
2831 w := 0;
2832 h := 0;
2833 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
2834 if Split then
2835 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
2836 (gScreenHeight div 2)-(h div 2), MessageText)
2837 else
2838 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
2839 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
2840 end;
2842 if IsDrawStat or (gSpectMode = 1) then DrawStat();
2844 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
2845 begin
2846 // Draw spectator GUI
2847 ww := 0;
2848 hh := 0;
2849 e_TextureFontGetSize(gStdFont, ww, hh);
2850 case gSpectMode of
2851 SPECT_STATS:
2852 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
2853 SPECT_MAPVIEW:
2854 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
2855 SPECT_PLAYERS:
2856 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
2857 end;
2858 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
2859 if gSpectMode = SPECT_MAPVIEW then
2860 begin
2861 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
2862 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
2863 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
2864 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
2865 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
2866 end;
2867 if gSpectMode = SPECT_PLAYERS then
2868 begin
2869 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
2870 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
2871 if gSpectViewTwo then
2872 begin
2873 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
2874 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
2875 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
2876 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
2877 end
2878 else
2879 begin
2880 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
2881 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
2882 end;
2883 end;
2884 end;
2885 end;
2887 if gPause and gGameOn and (g_ActiveWindow = nil) then
2888 begin
2889 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2891 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
2892 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
2893 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
2894 end;
2896 if not gGameOn then
2897 begin
2898 if (gState = STATE_MENU) then
2899 begin
2900 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
2901 begin
2902 if g_Texture_Get('MENU_BACKGROUND', ID) then
2903 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
2904 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2905 end;
2906 if g_ActiveWindow <> nil then
2907 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2908 end;
2910 if gState = STATE_FOLD then
2911 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
2913 if gState = STATE_INTERCUSTOM then
2914 begin
2915 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
2916 begin
2917 back := 'TEXTURE_endpic';
2918 if not g_Texture_Get(back, ID) then
2919 back := _lc[I_TEXTURE_ENDPIC];
2920 end
2921 else
2922 back := 'INTER';
2924 if g_Texture_Get(back, ID) then
2925 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
2926 else
2927 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2929 DrawCustomStat();
2931 if g_ActiveWindow <> nil then
2932 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2933 end;
2935 if gState = STATE_INTERSINGLE then
2936 begin
2937 if EndingGameCounter > 0 then
2938 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter)
2939 else
2940 begin
2941 back := 'INTER';
2943 if g_Texture_Get(back, ID) then
2944 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
2945 else
2946 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2948 DrawSingleStat();
2950 if g_ActiveWindow <> nil then
2951 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2952 end;
2953 end;
2955 if gState = STATE_ENDPIC then
2956 begin
2957 ID := DWORD(-1);
2958 if not g_Texture_Get('TEXTURE_endpic', ID) then
2959 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
2961 if ID <> DWORD(-1) then
2962 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
2963 else
2964 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2966 if g_ActiveWindow <> nil then
2967 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2968 end;
2970 if gState = STATE_SLIST then
2971 begin
2972 if g_Texture_Get('MENU_BACKGROUND', ID) then
2973 begin
2974 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
2975 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2976 end;
2977 g_Serverlist_Draw(slCurrent);
2978 end;
2979 end;
2981 if g_ActiveWindow <> nil then
2982 begin
2983 if gGameOn then
2984 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2985 g_ActiveWindow.Draw();
2986 end;
2988 g_Console_Draw();
2990 if g_debug_Sounds and gGameOn then
2991 begin
2992 for w := 0 to High(e_SoundsArray) do
2993 for h := 0 to e_SoundsArray[w].nRefs do
2994 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
2995 end;
2997 if gShowFPS then
2998 begin
2999 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3000 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3001 end;
3003 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3004 e_TextureFontPrint(gScreenWidth-72, 0,
3005 Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]),
3006 gStdFont);
3007 end;
3009 procedure g_Game_Quit();
3010 begin
3011 g_Game_StopAllSounds(True);
3012 gMusic.Free();
3013 g_Game_SaveOptions();
3014 g_Game_FreeData();
3015 g_PlayerModel_FreeData();
3016 g_Texture_DeleteAll();
3017 g_Frames_DeleteAll();
3018 g_Menu_Free();
3020 if NetInitDone then g_Net_Free;
3022 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3023 if gMapToDelete <> '' then
3024 g_Game_DeleteTestMap();
3026 gExit := EXIT_QUIT;
3027 PushExitEvent();
3028 end;
3030 procedure g_FatalError(Text: String);
3031 begin
3032 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3033 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3035 gExit := EXIT_SIMPLE;
3036 end;
3038 procedure g_SimpleError(Text: String);
3039 begin
3040 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3041 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3042 end;
3044 procedure g_Game_SetupScreenSize();
3045 var
3046 d: Single;
3047 begin
3048 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3049 gPlayerScreenSize.X := gScreenWidth-196;
3050 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3051 gPlayerScreenSize.Y := gScreenHeight div 2
3052 else
3053 gPlayerScreenSize.Y := gScreenHeight;
3055 // Ðàçìåð çàäíåãî ïëàíà:
3056 if BackID <> DWORD(-1) then
3057 begin
3058 d := SKY_STRETCH;
3060 if (gScreenWidth*d > gMapInfo.Width) or
3061 (gScreenHeight*d > gMapInfo.Height) then
3062 d := 1.0;
3064 gBackSize.X := Round(gScreenWidth*d);
3065 gBackSize.Y := Round(gScreenHeight*d);
3066 end;
3067 end;
3069 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3070 begin
3071 g_Window_SetSize(newWidth, newHeight, nowFull);
3072 end;
3074 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3075 begin
3076 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3077 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3078 Exit;
3079 if gPlayer1 = nil then
3080 begin
3081 if g_Game_IsClient then
3082 begin
3083 if NetPlrUID1 > -1 then
3084 begin
3085 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3086 gPlayer1 := g_Player_Get(NetPlrUID1);
3087 end;
3088 Exit;
3089 end;
3091 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3092 Team := gPlayer1Settings.Team;
3094 // Ñîçäàíèå ïåðâîãî èãðîêà:
3095 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3096 gPlayer1Settings.Color,
3097 Team, False));
3098 if gPlayer1 = nil then
3099 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3100 else
3101 begin
3102 gPlayer1.Name := gPlayer1Settings.Name;
3103 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3104 if g_Game_IsServer and g_Game_IsNet then
3105 MH_SEND_PlayerCreate(gPlayer1.UID);
3106 gPlayer1.Respawn(False, True);
3108 if g_Game_IsNet and NetUseMaster then
3109 g_Net_Slist_Update;
3110 end;
3112 Exit;
3113 end;
3114 if gPlayer2 = nil then
3115 begin
3116 if g_Game_IsClient then
3117 begin
3118 if NetPlrUID2 > -1 then
3119 gPlayer2 := g_Player_Get(NetPlrUID2);
3120 Exit;
3121 end;
3123 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3124 Team := gPlayer2Settings.Team;
3126 // Ñîçäàíèå âòîðîãî èãðîêà:
3127 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3128 gPlayer2Settings.Color,
3129 Team, False));
3130 if gPlayer2 = nil then
3131 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3132 else
3133 begin
3134 gPlayer2.Name := gPlayer2Settings.Name;
3135 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3136 if g_Game_IsServer and g_Game_IsNet then
3137 MH_SEND_PlayerCreate(gPlayer2.UID);
3138 gPlayer2.Respawn(False, True);
3140 if g_Game_IsNet and NetUseMaster then
3141 g_Net_Slist_Update;
3142 end;
3144 Exit;
3145 end;
3146 end;
3148 procedure g_Game_RemovePlayer();
3149 var
3150 Pl: TPlayer;
3151 begin
3152 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3153 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3154 Exit;
3155 Pl := gPlayer2;
3156 if Pl <> nil then
3157 begin
3158 if g_Game_IsServer then
3159 begin
3160 Pl.Lives := 0;
3161 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3162 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3163 g_Player_Remove(Pl.UID);
3165 if g_Game_IsNet and NetUseMaster then
3166 g_Net_Slist_Update;
3167 end else
3168 gPlayer2 := nil;
3169 Exit;
3170 end;
3171 Pl := gPlayer1;
3172 if Pl <> nil then
3173 begin
3174 if g_Game_IsServer then
3175 begin
3176 Pl.Lives := 0;
3177 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3178 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3179 g_Player_Remove(Pl.UID);
3181 if g_Game_IsNet and NetUseMaster then
3182 g_Net_Slist_Update;
3183 end else
3184 begin
3185 gPlayer1 := nil;
3186 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3187 end;
3188 Exit;
3189 end;
3190 end;
3192 procedure g_Game_Spectate();
3193 begin
3194 g_Game_RemovePlayer();
3195 if gPlayer1 <> nil then
3196 g_Game_RemovePlayer();
3197 end;
3199 procedure g_Game_SpectateCenterView();
3200 begin
3201 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3202 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3203 end;
3205 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3206 var
3207 i, nPl: Integer;
3208 begin
3209 g_Game_Free();
3211 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3213 g_Game_ClearLoading();
3215 // Íàñòðîéêè èãðû:
3216 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3217 gAimLine := False;
3218 gShowMap := False;
3219 gGameSettings.GameType := GT_SINGLE;
3220 gGameSettings.MaxLives := 0;
3221 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3222 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3223 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3224 gSwitchGameMode := GM_SINGLE;
3226 g_Game_ExecuteEvent('ongamestart');
3228 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3229 g_Game_SetupScreenSize();
3231 // Ñîçäàíèå ïåðâîãî èãðîêà:
3232 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3233 gPlayer1Settings.Color,
3234 gPlayer1Settings.Team, False));
3235 if gPlayer1 = nil then
3236 begin
3237 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3238 Exit;
3239 end;
3241 gPlayer1.Name := gPlayer1Settings.Name;
3242 nPl := 1;
3244 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3245 if TwoPlayers then
3246 begin
3247 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3248 gPlayer2Settings.Color,
3249 gPlayer2Settings.Team, False));
3250 if gPlayer2 = nil then
3251 begin
3252 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3253 Exit;
3254 end;
3256 gPlayer2.Name := gPlayer2Settings.Name;
3257 Inc(nPl);
3258 end;
3260 // Çàãðóçêà è çàïóñê êàðòû:
3261 if not g_Game_StartMap(MAP, True) then
3262 begin
3263 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3264 Exit;
3265 end;
3267 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3268 g_Player_Init();
3270 // Ñîçäàåì áîòîâ:
3271 for i := nPl+1 to nPlayers do
3272 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3273 end;
3275 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3276 TimeLimit, GoalLimit: Word;
3277 MaxLives: Byte;
3278 Options: LongWord; nPlayers: Byte);
3279 var
3280 i, nPl: Integer;
3281 begin
3282 g_Game_Free();
3284 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3286 g_Game_ClearLoading();
3288 // Íàñòðîéêè èãðû:
3289 gGameSettings.GameType := GT_CUSTOM;
3290 gGameSettings.GameMode := GameMode;
3291 gSwitchGameMode := GameMode;
3292 gGameSettings.TimeLimit := TimeLimit;
3293 gGameSettings.GoalLimit := GoalLimit;
3294 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3295 gGameSettings.Options := Options;
3297 gCoopTotalMonstersKilled := 0;
3298 gCoopTotalSecretsFound := 0;
3299 gCoopTotalMonsters := 0;
3300 gCoopTotalSecrets := 0;
3301 gAimLine := False;
3302 gShowMap := False;
3304 g_Game_ExecuteEvent('ongamestart');
3306 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3307 g_Game_SetupScreenSize();
3309 // Ðåæèì íàáëþäàòåëÿ:
3310 if nPlayers = 0 then
3311 begin
3312 gPlayer1 := nil;
3313 gPlayer2 := nil;
3314 end;
3316 nPl := 0;
3317 if nPlayers >= 1 then
3318 begin
3319 // Ñîçäàíèå ïåðâîãî èãðîêà:
3320 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3321 gPlayer1Settings.Color,
3322 gPlayer1Settings.Team, False));
3323 if gPlayer1 = nil then
3324 begin
3325 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3326 Exit;
3327 end;
3329 gPlayer1.Name := gPlayer1Settings.Name;
3330 Inc(nPl);
3331 end;
3333 if nPlayers >= 2 then
3334 begin
3335 // Ñîçäàíèå âòîðîãî èãðîêà:
3336 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3337 gPlayer2Settings.Color,
3338 gPlayer2Settings.Team, False));
3339 if gPlayer2 = nil then
3340 begin
3341 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3342 Exit;
3343 end;
3345 gPlayer2.Name := gPlayer2Settings.Name;
3346 Inc(nPl);
3347 end;
3349 // Çàãðóçêà è çàïóñê êàðòû:
3350 if not g_Game_StartMap(Map, True) then
3351 begin
3352 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3353 Exit;
3354 end;
3356 // Íåò òî÷åê ïîÿâëåíèÿ:
3357 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3358 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3359 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3360 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3361 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3362 begin
3363 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3364 Exit;
3365 end;
3367 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3368 g_Player_Init();
3370 // Ñîçäàåì áîòîâ:
3371 for i := nPl+1 to nPlayers do
3372 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3373 end;
3375 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3376 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3377 Options: LongWord; nPlayers: Byte;
3378 IPAddr: LongWord; Port: Word);
3379 begin
3380 g_Game_Free();
3382 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3384 g_Game_ClearLoading();
3386 // Íàñòðîéêè èãðû:
3387 gGameSettings.GameType := GT_SERVER;
3388 gGameSettings.GameMode := GameMode;
3389 gSwitchGameMode := GameMode;
3390 gGameSettings.TimeLimit := TimeLimit;
3391 gGameSettings.GoalLimit := GoalLimit;
3392 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3393 gGameSettings.Options := Options;
3395 gCoopTotalMonstersKilled := 0;
3396 gCoopTotalSecretsFound := 0;
3397 gCoopTotalMonsters := 0;
3398 gCoopTotalSecrets := 0;
3399 gAimLine := False;
3400 gShowMap := False;
3402 g_Game_ExecuteEvent('ongamestart');
3404 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3405 g_Game_SetupScreenSize();
3407 // Ðåæèì íàáëþäàòåëÿ:
3408 if nPlayers = 0 then
3409 begin
3410 gPlayer1 := nil;
3411 gPlayer2 := nil;
3412 end;
3414 if nPlayers >= 1 then
3415 begin
3416 // Ñîçäàíèå ïåðâîãî èãðîêà:
3417 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3418 gPlayer1Settings.Color,
3419 gPlayer1Settings.Team, False));
3420 if gPlayer1 = nil then
3421 begin
3422 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3423 Exit;
3424 end;
3426 gPlayer1.Name := gPlayer1Settings.Name;
3427 end;
3429 if nPlayers >= 2 then
3430 begin
3431 // Ñîçäàíèå âòîðîãî èãðîêà:
3432 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3433 gPlayer2Settings.Color,
3434 gPlayer2Settings.Team, False));
3435 if gPlayer2 = nil then
3436 begin
3437 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3438 Exit;
3439 end;
3441 gPlayer2.Name := gPlayer2Settings.Name;
3442 end;
3444 // Ñòàðòóåì ñåðâåð
3445 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3446 begin
3447 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3448 Exit;
3449 end;
3451 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3453 // Çàãðóçêà è çàïóñê êàðòû:
3454 if not g_Game_StartMap(Map, True) then
3455 begin
3456 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3457 Exit;
3458 end;
3460 // Íåò òî÷åê ïîÿâëåíèÿ:
3461 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3462 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3463 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3464 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3465 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3466 begin
3467 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3468 Exit;
3469 end;
3471 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3472 g_Player_Init();
3474 NetState := NET_STATE_GAME;
3475 end;
3477 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3478 var
3479 Map: String;
3480 WadName: string;
3481 Ptr: Pointer;
3482 T: Cardinal;
3483 MID: Byte;
3484 State: Byte;
3485 OuterLoop: Boolean;
3486 newResPath: string;
3487 begin
3488 g_Game_Free();
3490 State := 0;
3491 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3492 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3494 g_Game_ClearLoading();
3496 // Íàñòðîéêè èãðû:
3497 gGameSettings.GameType := GT_CLIENT;
3499 gCoopTotalMonstersKilled := 0;
3500 gCoopTotalSecretsFound := 0;
3501 gCoopTotalMonsters := 0;
3502 gCoopTotalSecrets := 0;
3503 gAimLine := False;
3504 gShowMap := False;
3506 g_Game_ExecuteEvent('ongamestart');
3508 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3509 g_Game_SetupScreenSize();
3511 NetState := NET_STATE_AUTH;
3513 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3514 // Ñòàðòóåì êëèåíò
3515 if not g_Net_Connect(Addr, Port) then
3516 begin
3517 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3518 NetState := NET_STATE_NONE;
3519 Exit;
3520 end;
3522 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3523 MC_SEND_Info(PW);
3524 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3526 OuterLoop := True;
3527 while OuterLoop do
3528 begin
3529 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3530 begin
3531 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3532 begin
3533 Ptr := NetEvent.packet^.data;
3534 e_Raw_Seek(0);
3536 MID := e_Raw_Read_Byte(Ptr);
3538 if (MID = NET_MSG_INFO) and (State = 0) then
3539 begin
3540 NetMyID := e_Raw_Read_Byte(Ptr);
3541 NetPlrUID1 := e_Raw_Read_Word(Ptr);
3543 WadName := e_Raw_Read_String(Ptr);
3544 Map := e_Raw_Read_String(Ptr);
3546 gWADHash := e_Raw_Read_MD5(Ptr);
3548 gGameSettings.GameMode := e_Raw_Read_Byte(Ptr);
3549 gSwitchGameMode := gGameSettings.GameMode;
3550 gGameSettings.GoalLimit := e_Raw_Read_Word(Ptr);
3551 gGameSettings.TimeLimit := e_Raw_Read_Word(Ptr);
3552 gGameSettings.MaxLives := e_Raw_Read_Byte(Ptr);
3553 gGameSettings.Options := e_Raw_Read_LongWord(Ptr);
3554 T := e_Raw_Read_LongWord(Ptr);
3556 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3557 if newResPath = '' then
3558 begin
3559 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3560 newResPath := g_Res_DownloadWAD(WadName);
3561 if newResPath = '' then
3562 begin
3563 g_FatalError(_lc[I_NET_ERR_HASH]);
3564 enet_packet_destroy(NetEvent.packet);
3565 NetState := NET_STATE_NONE;
3566 Exit;
3567 end;
3568 end;
3569 newResPath := ExtractRelativePath(MapsDir, newResPath);
3571 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3572 gPlayer1Settings.Color,
3573 gPlayer1Settings.Team, False));
3575 if gPlayer1 = nil then
3576 begin
3577 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3579 enet_packet_destroy(NetEvent.packet);
3580 NetState := NET_STATE_NONE;
3581 Exit;
3582 end;
3584 gPlayer1.Name := gPlayer1Settings.Name;
3585 gPlayer1.UID := NetPlrUID1;
3586 gPlayer1.Reset(True);
3588 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3589 begin
3590 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3592 enet_packet_destroy(NetEvent.packet);
3593 NetState := NET_STATE_NONE;
3594 Exit;
3595 end;
3597 gTime := T;
3599 State := 1;
3600 OuterLoop := False;
3601 enet_packet_destroy(NetEvent.packet);
3602 break;
3603 end
3604 else
3605 enet_packet_destroy(NetEvent.packet);
3606 end
3607 else
3608 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3609 begin
3610 State := 0;
3611 if (NetEvent.data <= NET_DISC_MAX) then
3612 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3613 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3614 OuterLoop := False;
3615 Break;
3616 end;
3617 end;
3619 ProcessLoading();
3621 e_PollInput();
3623 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3624 begin
3625 State := 0;
3626 break;
3627 end;
3628 end;
3630 if State <> 1 then
3631 begin
3632 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3633 NetState := NET_STATE_NONE;
3634 Exit;
3635 end;
3637 gLMSRespawn := LMS_RESPAWN_NONE;
3638 gLMSRespawnTime := 0;
3640 g_Player_Init();
3641 NetState := NET_STATE_GAME;
3642 MC_SEND_FullStateRequest;
3643 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3644 end;
3646 procedure g_Game_SaveOptions();
3647 begin
3648 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3649 end;
3651 procedure g_Game_ChangeMap(MapPath: String);
3652 var
3653 Force: Boolean;
3654 begin
3655 g_Game_ClearLoading();
3657 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3658 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3659 if gExitByTrigger then
3660 begin
3661 Force := False;
3662 gExitByTrigger := False;
3663 end;
3664 if not g_Game_StartMap(MapPath, Force) then
3665 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3666 end;
3668 procedure g_Game_Restart();
3669 var
3670 Map: string;
3671 begin
3672 if g_Game_IsClient then
3673 Exit;
3674 map := g_ExtractFileName(gMapInfo.Map);
3676 MessageTime := 0;
3677 gGameOn := False;
3678 g_Game_ClearLoading();
3679 g_Game_StartMap(Map, True);
3680 end;
3682 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
3683 var
3684 NewWAD, ResName: String;
3685 I: Integer;
3686 begin
3687 g_Map_Free();
3688 g_Player_RemoveAllCorpses();
3690 if (not g_Game_IsClient) and
3691 (gSwitchGameMode <> gGameSettings.GameMode) and
3692 (gGameSettings.GameMode <> GM_SINGLE) then
3693 begin
3694 if gSwitchGameMode = GM_CTF then
3695 gGameSettings.MaxLives := 0;
3696 gGameSettings.GameMode := gSwitchGameMode;
3697 Force := True;
3698 end else
3699 gSwitchGameMode := gGameSettings.GameMode;
3701 g_Player_ResetTeams();
3703 if Pos(':\', Map) > 0 then
3704 begin
3705 NewWAD := g_ExtractWadName(Map);
3706 ResName := g_ExtractFileName(Map);
3707 if g_Game_IsServer then
3708 begin
3709 gWADHash := MD5File(MapsDir + NewWAD);
3710 g_Game_LoadWAD(NewWAD);
3711 end else
3712 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
3713 g_Game_ClientWAD(NewWAD, gWADHash);
3714 end else
3715 ResName := Map;
3717 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
3718 if Result then
3719 begin
3720 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
3722 gState := STATE_NONE;
3723 g_ActiveWindow := nil;
3724 gGameOn := True;
3726 DisableCheats();
3727 ResetTimer();
3729 if gGameSettings.GameMode = GM_CTF then
3730 begin
3731 g_Map_ResetFlag(FLAG_RED);
3732 g_Map_ResetFlag(FLAG_BLUE);
3733 // CTF, à ôëàãîâ íåò:
3734 if not g_Map_HaveFlagPoints() then
3735 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
3736 end;
3737 end
3738 else
3739 begin
3740 gState := STATE_MENU;
3741 gGameOn := False;
3742 end;
3744 gExit := 0;
3745 gPause := False;
3746 gTime := 0;
3747 NetTimeToUpdate := 1;
3748 NetTimeToReliable := 0;
3749 NetTimeToMaster := NetMasterRate;
3750 gLMSRespawn := LMS_RESPAWN_NONE;
3751 gLMSRespawnTime := 0;
3752 gMissionFailed := False;
3753 gNextMap := '';
3755 gCoopMonstersKilled := 0;
3756 gCoopSecretsFound := 0;
3758 gVoteInProgress := False;
3759 gVotePassed := False;
3760 gVoteCount := 0;
3761 gVoted := False;
3763 gStatsOff := False;
3765 if not gGameOn then Exit;
3767 g_Game_SpectateCenterView();
3769 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
3770 begin
3771 gLMSRespawn := LMS_RESPAWN_WARMUP;
3772 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
3773 gLMSSoftSpawn := True;
3774 if NetMode = NET_SERVER then
3775 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
3776 else
3777 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
3778 end;
3780 if NetMode = NET_SERVER then
3781 begin
3782 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
3784 // Ìàñòåðñåðâåð
3785 if NetUseMaster then
3786 begin
3787 if (NetMHost = nil) or (NetMPeer = nil) then
3788 if not g_Net_Slist_Connect then
3789 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
3791 g_Net_Slist_Update;
3792 end;
3794 if NetClients <> nil then
3795 for I := 0 to High(NetClients) do
3796 if NetClients[I].Used then
3797 begin
3798 NetClients[I].Voted := False;
3799 if NetClients[I].RequestedFullUpdate then
3800 begin
3801 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
3802 NetClients[I].RequestedFullUpdate := False;
3803 end;
3804 end;
3806 g_Net_UnbanNonPermHosts();
3807 end;
3809 if gLastMap then
3810 begin
3811 gCoopTotalMonstersKilled := 0;
3812 gCoopTotalSecretsFound := 0;
3813 gCoopTotalMonsters := 0;
3814 gCoopTotalSecrets := 0;
3815 gLastMap := False;
3816 end;
3818 g_Game_ExecuteEvent('onmapstart');
3819 end;
3821 procedure SetFirstLevel();
3822 begin
3823 gNextMap := '';
3825 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
3826 if MapList = nil then
3827 Exit;
3829 SortSArray(MapList);
3830 gNextMap := MapList[Low(MapList)];
3832 MapList := nil;
3833 end;
3835 procedure g_Game_ExitLevel(Map: Char16);
3836 begin
3837 gNextMap := Map;
3839 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
3840 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
3841 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
3842 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
3844 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
3845 if gGameSettings.GameType = GT_SINGLE then
3846 gExit := EXIT_ENDLEVELSINGLE
3847 else // Âûøëè â âûõîä â Ñâîåé èãðå
3848 begin
3849 gExit := EXIT_ENDLEVELCUSTOM;
3850 if gGameSettings.GameMode = GM_COOP then
3851 g_Player_RememberAll;
3853 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
3854 begin
3855 gLastMap := True;
3856 if gGameSettings.GameMode = GM_COOP then
3857 gStatsOff := True;
3859 gStatsPressed := True;
3860 gNextMap := 'MAP01';
3862 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
3863 g_Game_NextLevel;
3865 if g_Game_IsNet then
3866 begin
3867 MH_SEND_GameStats();
3868 MH_SEND_CoopStats();
3869 end;
3870 end;
3871 end;
3872 end;
3874 procedure g_Game_RestartLevel();
3875 var
3876 Map: string;
3877 begin
3878 if gGameSettings.GameMode = GM_SINGLE then
3879 begin
3880 g_Game_Restart();
3881 Exit;
3882 end;
3883 gExit := EXIT_ENDLEVELCUSTOM;
3884 Map := g_ExtractFileName(gMapInfo.Map);
3885 gNextMap := Map;
3886 end;
3888 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
3889 var
3890 gWAD: String;
3891 begin
3892 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
3893 Exit;
3894 if not g_Game_IsClient then
3895 Exit;
3896 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
3897 if gWAD = '' then
3898 begin
3899 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3900 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
3901 if gWAD = '' then
3902 begin
3903 g_Game_Free();
3904 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
3905 Exit;
3906 end;
3907 end;
3908 NewWAD := ExtractRelativePath(MapsDir, gWAD);
3909 g_Game_LoadWAD(NewWAD);
3910 end;
3912 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
3913 var
3914 i, n, nb, nr: Integer;
3915 begin
3916 if not g_Game_IsServer then Exit;
3917 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
3918 gLMSRespawn := LMS_RESPAWN_NONE;
3919 gLMSRespawnTime := 0;
3920 MessageTime := 0;
3922 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
3923 begin
3924 gMissionFailed := True;
3925 g_Game_RestartLevel;
3926 Exit;
3927 end;
3929 n := 0; nb := 0; nr := 0;
3930 for i := Low(gPlayers) to High(gPlayers) do
3931 if (gPlayers[i] <> nil) and
3932 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
3933 (gPlayers[i] is TBot)) then
3934 begin
3935 Inc(n);
3936 if gPlayers[i].Team = TEAM_RED then Inc(nr)
3937 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
3938 end;
3940 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
3941 begin
3942 // wait a second until the fuckers finally decide to join
3943 gLMSRespawn := LMS_RESPAWN_WARMUP;
3944 gLMSRespawnTime := gTime + 1000;
3945 gLMSSoftSpawn := NoMapRestart;
3946 Exit;
3947 end;
3949 g_Player_RemoveAllCorpses;
3950 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
3951 if g_Game_IsNet then
3952 MH_SEND_GameEvent(NET_EV_LMS_START);
3954 for i := Low(gPlayers) to High(gPlayers) do
3955 begin
3956 if gPlayers[i] = nil then continue;
3957 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
3958 // don't touch normal spectators
3959 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
3960 begin
3961 gPlayers[i].FNoRespawn := True;
3962 gPlayers[i].Lives := 0;
3963 if g_Game_IsNet then
3964 MH_SEND_PlayerStats(gPlayers[I].UID);
3965 continue;
3966 end;
3967 gPlayers[i].FNoRespawn := False;
3968 gPlayers[i].Lives := gGameSettings.MaxLives;
3969 gPlayers[i].Respawn(False, True);
3970 if gGameSettings.GameMode = GM_COOP then
3971 begin
3972 gPlayers[i].Frags := 0;
3973 gPlayers[i].RecallState;
3974 end;
3975 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
3976 gPlayer1 := g_Player_Get(gLMSPID1);
3977 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
3978 gPlayer2 := g_Player_Get(gLMSPID2);
3979 end;
3981 for i := Low(gItems) to High(gItems) do
3982 begin
3983 if gItems[i].Respawnable then
3984 begin
3985 gItems[i].QuietRespawn := True;
3986 gItems[i].RespawnTime := 0;
3987 end
3988 else
3989 begin
3990 g_Items_Remove(i);
3991 if g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
3992 end;
3993 end;
3995 for i := Low(gMonsters) to High(gMonsters) do
3996 begin
3997 if (gMonsters[i] <> nil) and not gMonsters[i].FNoRespawn then
3998 gMonsters[i].Respawn;
3999 end;
4001 gLMSSoftSpawn := False;
4002 end;
4004 function g_Game_GetFirstMap(WAD: String): String;
4005 begin
4006 Result := '';
4008 MapList := g_Map_GetMapsList(WAD);
4009 if MapList = nil then
4010 Exit;
4012 SortSArray(MapList);
4013 Result := MapList[Low(MapList)];
4015 if not g_Map_Exist(WAD + ':\' + Result) then
4016 Result := '';
4018 MapList := nil;
4019 end;
4021 function g_Game_GetNextMap(): String;
4022 var
4023 I: Integer;
4024 Map: string;
4025 begin
4026 Result := '';
4028 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4029 if MapList = nil then
4030 Exit;
4032 Map := g_ExtractFileName(gMapInfo.Map);
4034 SortSArray(MapList);
4035 MapIndex := -255;
4036 for I := Low(MapList) to High(MapList) do
4037 if Map = MapList[I] then
4038 begin
4039 MapIndex := I;
4040 Break;
4041 end;
4043 if MapIndex <> -255 then
4044 begin
4045 if MapIndex = High(MapList) then
4046 Result := MapList[Low(MapList)]
4047 else
4048 Result := MapList[MapIndex + 1];
4050 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4051 end;
4053 MapList := nil;
4054 end;
4056 procedure g_Game_NextLevel();
4057 begin
4058 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4059 gExit := EXIT_ENDLEVELCUSTOM
4060 else
4061 begin
4062 gExit := EXIT_ENDLEVELSINGLE;
4063 Exit;
4064 end;
4066 if gNextMap <> '' then Exit;
4067 gNextMap := g_Game_GetNextMap();
4068 end;
4070 function g_Game_IsTestMap(): Boolean;
4071 begin
4072 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4073 end;
4075 procedure g_Game_DeleteTestMap();
4076 var
4077 a: Integer;
4078 MapName: Char16;
4079 WadName: string;
4081 WAD: TWADFile;
4082 MapList: SArray;
4083 time: Integer;
4085 begin
4086 a := Pos('.wad:\', gMapToDelete);
4087 if a = 0 then
4088 Exit;
4090 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4091 WadName := Copy(gMapToDelete, 1, a + 3);
4092 Delete(gMapToDelete, 1, a + 5);
4093 gMapToDelete := UpperCase(gMapToDelete);
4094 MapName := '';
4095 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4098 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4099 if MapName <> TEST_MAP_NAME then
4100 Exit;
4102 if not gTempDelete then
4103 begin
4104 time := g_GetFileTime(WadName);
4105 WAD := TWADFile.Create();
4107 // ×èòàåì Wad-ôàéë:
4108 if not WAD.ReadFile(WadName) then
4109 begin // Íåò òàêîãî WAD-ôàéëà
4110 WAD.Free();
4111 Exit;
4112 end;
4114 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4115 WAD.CreateImage();
4116 MapList := WAD.GetResourcesList('');
4118 if MapList <> nil then
4119 for a := 0 to High(MapList) do
4120 if MapList[a] = MapName then
4121 begin
4122 // Óäàëÿåì è ñîõðàíÿåì:
4123 WAD.RemoveResource('', MapName);
4124 WAD.SaveTo(WadName);
4125 Break;
4126 end;
4128 WAD.Free();
4129 g_SetFileTime(WadName, time);
4130 end else
4132 if gTempDelete then DeleteFile(WadName);
4133 end;
4135 procedure GameCVars(P: SArray);
4136 var
4137 a, b: Integer;
4138 stat: TPlayerStatArray;
4139 cmd, s: string;
4140 config: TConfig;
4141 begin
4142 stat := nil;
4143 cmd := LowerCase(P[0]);
4144 if cmd = 'r_showfps' then
4145 begin
4146 if (Length(P) > 1) and
4147 ((P[1] = '1') or (P[1] = '0')) then
4148 gShowFPS := (P[1][1] = '1');
4150 if gShowFPS then
4151 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4152 else
4153 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4154 end
4155 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4156 begin
4157 with gGameSettings do
4158 begin
4159 if (Length(P) > 1) and
4160 ((P[1] = '1') or (P[1] = '0')) then
4161 begin
4162 if (P[1][1] = '1') then
4163 Options := Options or GAME_OPTION_TEAMDAMAGE
4164 else
4165 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4166 end;
4168 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4169 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4170 else
4171 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4173 if g_Game_IsNet then MH_SEND_GameSettings;
4174 end;
4175 end
4176 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4177 begin
4178 with gGameSettings do
4179 begin
4180 if (Length(P) > 1) and
4181 ((P[1] = '1') or (P[1] = '0')) then
4182 begin
4183 if (P[1][1] = '1') then
4184 Options := Options or GAME_OPTION_WEAPONSTAY
4185 else
4186 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4187 end;
4189 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4190 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4191 else
4192 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4194 if g_Game_IsNet then MH_SEND_GameSettings;
4195 end;
4196 end
4197 else if cmd = 'g_gamemode' then
4198 begin
4199 a := g_Game_TextToMode(P[1]);
4200 if a = GM_SINGLE then a := GM_COOP;
4201 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4202 begin
4203 gSwitchGameMode := a;
4204 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4205 (gState = STATE_INTERSINGLE) then
4206 gSwitchGameMode := GM_SINGLE;
4207 if not gGameOn then
4208 gGameSettings.GameMode := gSwitchGameMode;
4209 end;
4210 if gSwitchGameMode = gGameSettings.GameMode then
4211 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4212 [g_Game_ModeToText(gGameSettings.GameMode)]))
4213 else
4214 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4215 [g_Game_ModeToText(gGameSettings.GameMode),
4216 g_Game_ModeToText(gSwitchGameMode)]));
4217 end
4218 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4219 begin
4220 with gGameSettings do
4221 begin
4222 if (Length(P) > 1) and
4223 ((P[1] = '1') or (P[1] = '0')) then
4224 begin
4225 if (P[1][1] = '1') then
4226 Options := Options or GAME_OPTION_ALLOWEXIT
4227 else
4228 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4229 end;
4231 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4232 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4233 else
4234 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4235 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4237 if g_Game_IsNet then MH_SEND_GameSettings;
4238 end;
4239 end
4240 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4241 begin
4242 with gGameSettings do
4243 begin
4244 if (Length(P) > 1) and
4245 ((P[1] = '1') or (P[1] = '0')) then
4246 begin
4247 if (P[1][1] = '1') then
4248 Options := Options or GAME_OPTION_MONSTERS
4249 else
4250 Options := Options and (not GAME_OPTION_MONSTERS);
4251 end;
4253 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4254 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4255 else
4256 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4257 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4259 if g_Game_IsNet then MH_SEND_GameSettings;
4260 end;
4261 end
4262 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4263 begin
4264 with gGameSettings do
4265 begin
4266 if (Length(P) > 1) and
4267 ((P[1] = '1') or (P[1] = '0')) then
4268 begin
4269 if (P[1][1] = '1') then
4270 Options := Options or GAME_OPTION_BOTVSPLAYER
4271 else
4272 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4273 end;
4275 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4276 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4277 else
4278 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4280 if g_Game_IsNet then MH_SEND_GameSettings;
4281 end;
4282 end
4283 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4284 begin
4285 with gGameSettings do
4286 begin
4287 if (Length(P) > 1) and
4288 ((P[1] = '1') or (P[1] = '0')) then
4289 begin
4290 if (P[1][1] = '1') then
4291 Options := Options or GAME_OPTION_BOTVSMONSTER
4292 else
4293 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4294 end;
4296 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4297 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4298 else
4299 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4301 if g_Game_IsNet then MH_SEND_GameSettings;
4302 end;
4303 end
4304 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4305 begin
4306 if Length(P) > 1 then
4307 begin
4308 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4309 gGameSettings.WarmupTime := 30
4310 else
4311 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4312 end;
4314 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4315 [gGameSettings.WarmupTime]));
4316 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4317 end
4318 else if cmd = 'net_interp' then
4319 begin
4320 if (Length(P) > 1) then
4321 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4323 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4324 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4325 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4326 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4327 config.Free();
4328 end
4329 else if cmd = 'net_forceplayerupdate' then
4330 begin
4331 if (Length(P) > 1) and
4332 ((P[1] = '1') or (P[1] = '0')) then
4333 NetForcePlayerUpdate := (P[1][1] = '1');
4335 if NetForcePlayerUpdate then
4336 g_Console_Add('net_forceplayerupdate = 1')
4337 else
4338 g_Console_Add('net_forceplayerupdate = 0');
4339 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4340 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4341 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4342 config.Free();
4343 end
4344 else if cmd = 'net_predictself' then
4345 begin
4346 if (Length(P) > 1) and
4347 ((P[1] = '1') or (P[1] = '0')) then
4348 NetPredictSelf := (P[1][1] = '1');
4350 if NetPredictSelf then
4351 g_Console_Add('net_predictself = 1')
4352 else
4353 g_Console_Add('net_predictself = 0');
4354 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4355 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4356 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4357 config.Free();
4358 end
4359 else if cmd = 'sv_name' then
4360 begin
4361 if (Length(P) > 1) and (Length(P[1]) > 0) then
4362 begin
4363 NetServerName := P[1];
4364 if Length(NetServerName) > 64 then
4365 SetLength(NetServerName, 64);
4366 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4367 g_Net_Slist_Update;
4368 end;
4370 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4371 end
4372 else if cmd = 'sv_passwd' then
4373 begin
4374 if (Length(P) > 1) and (Length(P[1]) > 0) then
4375 begin
4376 NetPassword := P[1];
4377 if Length(NetPassword) > 24 then
4378 SetLength(NetPassword, 24);
4379 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4380 g_Net_Slist_Update;
4381 end;
4383 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4384 end
4385 else if cmd = 'sv_maxplrs' then
4386 begin
4387 if (Length(P) > 1) then
4388 begin
4389 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4390 if g_Game_IsServer and g_Game_IsNet then
4391 begin
4392 b := 0;
4393 for a := 0 to High(NetClients) do
4394 if NetClients[a].Used then
4395 begin
4396 Inc(b);
4397 if b > NetMaxClients then
4398 begin
4399 s := g_Player_Get(NetClients[a].Player).Name;
4400 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4401 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4402 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4403 end;
4404 end;
4405 if NetUseMaster then
4406 g_Net_Slist_Update;
4407 end;
4408 end;
4410 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4411 end
4412 else if cmd = 'sv_public' then
4413 begin
4414 if (Length(P) > 1) then
4415 begin
4416 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4417 if g_Game_IsServer and g_Game_IsNet then
4418 if NetUseMaster then
4419 begin
4420 if NetMPeer = nil then
4421 if not g_Net_Slist_Connect() then
4422 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4423 g_Net_Slist_Update();
4424 end
4425 else
4426 if NetMPeer <> nil then
4427 g_Net_Slist_Disconnect();
4428 end;
4430 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4431 end
4432 else if cmd = 'sv_intertime' then
4433 begin
4434 if (Length(P) > 1) then
4435 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4437 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4438 end
4439 else if cmd = 'p1_name' then
4440 begin
4441 if (Length(P) > 1) and gGameOn then
4442 begin
4443 if g_Game_IsClient then
4444 begin
4445 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4446 MC_SEND_PlayerSettings;
4447 end
4448 else
4449 if gPlayer1 <> nil then
4450 begin
4451 gPlayer1.Name := b_Text_Unformat(P[1]);
4452 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4453 end
4454 else
4455 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4456 end;
4457 end
4458 else if cmd = 'p2_name' then
4459 begin
4460 if (Length(P) > 1) and gGameOn then
4461 begin
4462 if g_Game_IsClient then
4463 begin
4464 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4465 MC_SEND_PlayerSettings;
4466 end
4467 else
4468 if gPlayer2 <> nil then
4469 begin
4470 gPlayer2.Name := b_Text_Unformat(P[1]);
4471 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4472 end
4473 else
4474 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4475 end;
4476 end
4477 else if cmd = 'p1_color' then
4478 begin
4479 if Length(P) > 3 then
4480 if g_Game_IsClient then
4481 begin
4482 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4483 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4484 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4485 MC_SEND_PlayerSettings;
4486 end
4487 else
4488 if gPlayer1 <> nil then
4489 begin
4490 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4491 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4492 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4493 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4494 end
4495 else
4496 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4497 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4498 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4499 end
4500 else if (cmd = 'p2_color') and not g_Game_IsNet then
4501 begin
4502 if Length(P) > 3 then
4503 if g_Game_IsClient then
4504 begin
4505 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4506 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4507 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4508 MC_SEND_PlayerSettings;
4509 end
4510 else
4511 if gPlayer2 <> nil then
4512 begin
4513 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4514 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4515 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4516 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4517 end
4518 else
4519 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4520 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4521 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4522 end
4523 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4524 begin
4525 if cmd = 'r_showtime' then
4526 begin
4527 if (Length(P) > 1) and
4528 ((P[1] = '1') or (P[1] = '0')) then
4529 gShowTime := (P[1][1] = '1');
4531 if gShowTime then
4532 g_Console_Add(_lc[I_MSG_TIME_ON])
4533 else
4534 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4535 end
4536 else if cmd = 'r_showscore' then
4537 begin
4538 if (Length(P) > 1) and
4539 ((P[1] = '1') or (P[1] = '0')) then
4540 gShowGoals := (P[1][1] = '1');
4542 if gShowGoals then
4543 g_Console_Add(_lc[I_MSG_SCORE_ON])
4544 else
4545 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4546 end
4547 else if cmd = 'r_showstat' then
4548 begin
4549 if (Length(P) > 1) and
4550 ((P[1] = '1') or (P[1] = '0')) then
4551 gShowStat := (P[1][1] = '1');
4553 if gShowStat then
4554 g_Console_Add(_lc[I_MSG_STATS_ON])
4555 else
4556 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4557 end
4558 else if cmd = 'r_showkillmsg' then
4559 begin
4560 if (Length(P) > 1) and
4561 ((P[1] = '1') or (P[1] = '0')) then
4562 gShowKillMsg := (P[1][1] = '1');
4564 if gShowKillMsg then
4565 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4566 else
4567 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4568 end
4569 else if cmd = 'r_showlives' then
4570 begin
4571 if (Length(P) > 1) and
4572 ((P[1] = '1') or (P[1] = '0')) then
4573 gShowLives := (P[1][1] = '1');
4575 if gShowLives then
4576 g_Console_Add(_lc[I_MSG_LIVES_ON])
4577 else
4578 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4579 end
4580 else if cmd = 'r_showspect' then
4581 begin
4582 if (Length(P) > 1) and
4583 ((P[1] = '1') or (P[1] = '0')) then
4584 gSpectHUD := (P[1][1] = '1');
4586 if gSpectHUD then
4587 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4588 else
4589 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4590 end
4591 else if cmd = 'r_showping' then
4592 begin
4593 if (Length(P) > 1) and
4594 ((P[1] = '1') or (P[1] = '0')) then
4595 gShowPing := (P[1][1] = '1');
4597 if gShowPing then
4598 g_Console_Add(_lc[I_MSG_PING_ON])
4599 else
4600 g_Console_Add(_lc[I_MSG_PING_OFF]);
4601 end
4602 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4603 begin
4604 if Length(P) > 1 then
4605 begin
4606 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4607 gGameSettings.GoalLimit := 0
4608 else
4609 begin
4610 b := 0;
4612 if gGameSettings.GameMode = GM_DM then
4613 begin // DM
4614 stat := g_Player_GetStats();
4615 if stat <> nil then
4616 for a := 0 to High(stat) do
4617 if stat[a].Frags > b then
4618 b := stat[a].Frags;
4619 end
4620 else // TDM/CTF
4621 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4623 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4624 end;
4626 if g_Game_IsNet then MH_SEND_GameSettings;
4627 end;
4629 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4630 end
4631 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4632 begin
4633 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4634 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4636 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4637 [gGameSettings.TimeLimit div 3600,
4638 (gGameSettings.TimeLimit div 60) mod 60,
4639 gGameSettings.TimeLimit mod 60]));
4640 if g_Game_IsNet then MH_SEND_GameSettings;
4641 end
4642 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4643 begin
4644 if Length(P) > 1 then
4645 begin
4646 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4647 gGameSettings.MaxLives := 0
4648 else
4649 begin
4650 b := 0;
4651 stat := g_Player_GetStats();
4652 if stat <> nil then
4653 for a := 0 to High(stat) do
4654 if stat[a].Lives > b then
4655 b := stat[a].Lives;
4656 gGameSettings.MaxLives :=
4657 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4658 end;
4659 end;
4661 g_Console_Add(Format(_lc[I_MSG_LIVES],
4662 [gGameSettings.MaxLives]));
4663 if g_Game_IsNet then MH_SEND_GameSettings;
4664 end;
4665 end;
4666 end;
4668 procedure DebugCommands(P: SArray);
4669 var
4670 a, b: Integer;
4671 cmd: string;
4672 //pt: TPoint;
4673 begin
4674 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
4675 if gDebugMode then
4676 begin
4677 cmd := LowerCase(P[0]);
4678 if cmd = 'd_window' then
4679 begin
4680 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
4681 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
4682 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
4683 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
4684 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
4685 end
4686 else if cmd = 'd_sounds' then
4687 begin
4688 if (Length(P) > 1) and
4689 ((P[1] = '1') or (P[1] = '0')) then
4690 g_Debug_Sounds := (P[1][1] = '1');
4692 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
4693 end
4694 else if cmd = 'd_frames' then
4695 begin
4696 if (Length(P) > 1) and
4697 ((P[1] = '1') or (P[1] = '0')) then
4698 g_Debug_Frames := (P[1][1] = '1');
4700 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
4701 end
4702 else if cmd = 'd_winmsg' then
4703 begin
4704 if (Length(P) > 1) and
4705 ((P[1] = '1') or (P[1] = '0')) then
4706 g_Debug_WinMsgs := (P[1][1] = '1');
4708 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
4709 end
4710 else if (cmd = 'd_monoff') and not g_Game_IsNet then
4711 begin
4712 if (Length(P) > 1) and
4713 ((P[1] = '1') or (P[1] = '0')) then
4714 g_Debug_MonsterOff := (P[1][1] = '1');
4716 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
4717 end
4718 else if (cmd = 'd_botoff') and not g_Game_IsNet then
4719 begin
4720 if Length(P) > 1 then
4721 case P[1][1] of
4722 '0': g_debug_BotAIOff := 0;
4723 '1': g_debug_BotAIOff := 1;
4724 '2': g_debug_BotAIOff := 2;
4725 '3': g_debug_BotAIOff := 3;
4726 end;
4728 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
4729 end
4730 else if cmd = 'd_monster' then
4731 begin
4732 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
4733 if Length(P) < 2 then
4734 begin
4735 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
4736 g_Console_Add('ID | Name');
4737 for b := MONSTER_DEMON to MONSTER_MAN do
4738 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
4739 end else
4740 begin
4741 a := StrToIntDef(P[1], 0);
4742 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4743 a := g_Monsters_GetIDByName(P[1]);
4745 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4746 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
4747 else
4748 begin
4749 with gPlayer1.Obj do
4750 b := g_Monsters_Create(a,
4751 X + Rect.X + (Rect.Width div 2),
4752 Y + Rect.Y + Rect.Height,
4753 gPlayer1.Direction, True);
4754 if (Length(P) > 2) and (b >= 0) then
4755 gMonsters[b].MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
4756 end;
4757 end;
4758 end
4759 else if (cmd = 'd_health') then
4760 begin
4761 if (Length(P) > 1) and
4762 ((P[1] = '1') or (P[1] = '0')) then
4763 g_debug_HealthBar := (P[1][1] = '1');
4765 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
4766 end
4767 else if (cmd = 'd_player') then
4768 begin
4769 if (Length(P) > 1) and
4770 ((P[1] = '1') or (P[1] = '0')) then
4771 g_debug_Player := (P[1][1] = '1');
4773 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
4774 end
4775 else if (cmd = 'd_joy') then
4776 begin
4777 for a := 1 to 8 do
4778 g_Console_Add(e_JoystickStateToString(a));
4779 end;
4780 end
4781 else
4782 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
4783 end;
4786 procedure GameCheats(P: SArray);
4787 var
4788 cmd: string;
4789 f, a: Integer;
4790 plr: TPlayer;
4791 begin
4792 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
4793 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
4794 begin
4795 g_Console_Add('not available');
4796 exit;
4797 end;
4798 plr := gPlayer1;
4799 if plr = nil then
4800 begin
4801 g_Console_Add('where is the player?!');
4802 exit;
4803 end;
4804 cmd := LowerCase(P[0]);
4805 // god
4806 if cmd = 'god' then
4807 begin
4808 plr.GodMode := not plr.GodMode;
4809 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
4810 exit;
4811 end;
4812 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
4813 if cmd = 'give' then
4814 begin
4815 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
4816 for f := 1 to High(P) do
4817 begin
4818 cmd := LowerCase(P[f]);
4819 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
4820 if (cmd = 'all') or (cmd = 'weapons') then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
4821 if cmd = 'exit' then
4822 begin
4823 if gTriggers <> nil then
4824 begin
4825 for a := 0 to High(gTriggers) do
4826 begin
4827 if gTriggers[a].TriggerType = TRIGGER_EXIT then
4828 begin
4829 g_Console_Add('player left the map');
4830 gExitByTrigger := True;
4831 g_Game_ExitLevel(gTriggers[a].Data.MapName);
4832 break;
4833 end;
4834 end;
4835 end;
4836 continue;
4837 end;
4838 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
4839 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got jetpack'); continue; end;
4840 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got envirosuit'); continue; end;
4841 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got berserk pack'); continue; end;
4842 g_Console_Add('i don''t know how to give '''+cmd+'''!');
4843 end;
4844 exit;
4845 end;
4846 // open
4847 if cmd = 'open' then
4848 begin
4849 g_Console_Add('player activated sesame');
4850 g_Triggers_OpenAll();
4851 exit;
4852 end;
4853 // fly
4854 if cmd = 'fly' then
4855 begin
4856 gFly := not gFly;
4857 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
4858 exit;
4859 end;
4860 // noclip
4861 if cmd = 'noclip' then
4862 begin
4863 plr.SwitchNoClip;
4864 g_Console_Add('wall hardeness adjusted');
4865 exit;
4866 end;
4867 // notarget
4868 if cmd = 'notarget' then
4869 begin
4870 plr.NoTarget := not plr.NoTarget;
4871 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
4872 exit;
4873 end;
4874 // noreload
4875 if cmd = 'noreload' then
4876 begin
4877 plr.NoReload := not plr.NoReload;
4878 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
4879 exit;
4880 end;
4881 // speedy
4882 if cmd = 'speedy' then
4883 begin
4884 MAX_RUNVEL := 32-MAX_RUNVEL;
4885 g_Console_Add('speed adjusted');
4886 exit;
4887 end;
4888 // jumpy
4889 if cmd = 'jumpy' then
4890 begin
4891 VEL_JUMP := 30-VEL_JUMP;
4892 g_Console_Add('jump height adjusted');
4893 exit;
4894 end;
4895 // automap
4896 if cmd = 'automap' then
4897 begin
4898 gShowMap := not gShowMap;
4899 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
4900 exit;
4901 end;
4902 // aimline
4903 if cmd = 'aimline' then
4904 begin
4905 gAimLine := not gAimLine;
4906 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
4907 exit;
4908 end;
4909 end;
4911 procedure GameCommands(P: SArray);
4912 var
4913 a, b: Integer;
4914 s, pw: String;
4915 chstr: string;
4916 cmd: string;
4917 pl: pTNetClient = nil;
4918 plr: TPlayer;
4919 prt: Word;
4920 nm: Boolean;
4921 listen: LongWord;
4922 begin
4923 // Îáùèå êîìàíäû:
4924 cmd := LowerCase(P[0]);
4925 chstr := '';
4926 if (cmd = 'quit') or
4927 (cmd = 'exit') then
4928 begin
4929 g_Game_Free();
4930 g_Game_Quit();
4931 Exit;
4932 end
4933 else if cmd = 'pause' then
4934 begin
4935 if (g_ActiveWindow = nil) then
4936 g_Game_Pause(not gPause);
4937 end
4938 else if cmd = 'endgame' then
4939 gExit := EXIT_SIMPLE
4940 else if cmd = 'restart' then
4941 begin
4942 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
4943 begin
4944 if g_Game_IsClient then
4945 begin
4946 g_Console_Add(_lc[I_MSG_SERVERONLY]);
4947 Exit;
4948 end;
4949 g_Game_Restart();
4950 end else
4951 g_Console_Add(_lc[I_MSG_NOT_GAME]);
4952 end
4953 else if cmd = 'kick' then
4954 begin
4955 if g_Game_IsServer then
4956 begin
4957 if Length(P) < 2 then
4958 begin
4959 g_Console_Add('kick <name>');
4960 Exit;
4961 end;
4962 if P[1] = '' then
4963 begin
4964 g_Console_Add('kick <name>');
4965 Exit;
4966 end;
4968 if g_Game_IsNet then
4969 pl := g_Net_Client_ByName(P[1]);
4970 if (pl <> nil) then
4971 begin
4972 s := g_Net_ClientName_ByID(pl^.ID);
4973 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
4974 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4975 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4976 if NetUseMaster then
4977 g_Net_Slist_Update;
4978 end else if gPlayers <> nil then
4979 for a := Low(gPlayers) to High(gPlayers) do
4980 if gPlayers[a] <> nil then
4981 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
4982 begin
4983 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
4984 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
4985 continue;
4986 gPlayers[a].Lives := 0;
4987 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4988 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
4989 g_Player_Remove(gPlayers[a].UID);
4990 if NetUseMaster then
4991 g_Net_Slist_Update;
4992 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
4993 g_Bot_MixNames();
4994 end;
4995 end else
4996 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
4997 end
4998 else if cmd = 'kick_id' then
4999 begin
5000 if g_Game_IsServer and g_Game_IsNet then
5001 begin
5002 if Length(P) < 2 then
5003 begin
5004 g_Console_Add('kick_id <client ID>');
5005 Exit;
5006 end;
5007 if P[1] = '' then
5008 begin
5009 g_Console_Add('kick_id <client ID>');
5010 Exit;
5011 end;
5013 a := StrToIntDef(P[1], 0);
5014 if (NetClients <> nil) and (a <= High(NetClients)) then
5015 begin
5016 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5017 begin
5018 s := g_Net_ClientName_ByID(NetClients[a].ID);
5019 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5020 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5021 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5022 if NetUseMaster then
5023 g_Net_Slist_Update;
5024 end;
5025 end;
5026 end else
5027 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5028 end
5029 else if cmd = 'ban' then
5030 begin
5031 if g_Game_IsServer and g_Game_IsNet then
5032 begin
5033 if Length(P) < 2 then
5034 begin
5035 g_Console_Add('ban <name>');
5036 Exit;
5037 end;
5038 if P[1] = '' then
5039 begin
5040 g_Console_Add('ban <name>');
5041 Exit;
5042 end;
5044 pl := g_Net_Client_ByName(P[1]);
5045 if (pl <> nil) then
5046 begin
5047 s := g_Net_ClientName_ByID(pl^.ID);
5048 g_Net_BanHost(pl^.Peer^.address.host, False);
5049 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5050 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5051 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5052 if NetUseMaster then
5053 g_Net_Slist_Update;
5054 end else
5055 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5056 end else
5057 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5058 end
5059 else if cmd = 'ban_id' then
5060 begin
5061 if g_Game_IsServer and g_Game_IsNet then
5062 begin
5063 if Length(P) < 2 then
5064 begin
5065 g_Console_Add('ban_id <client ID>');
5066 Exit;
5067 end;
5068 if P[1] = '' then
5069 begin
5070 g_Console_Add('ban_id <client ID>');
5071 Exit;
5072 end;
5074 a := StrToIntDef(P[1], 0);
5075 if (NetClients <> nil) and (a <= High(NetClients)) then
5076 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5077 begin
5078 s := g_Net_ClientName_ByID(NetClients[a].ID);
5079 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5080 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5081 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5082 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5083 if NetUseMaster then
5084 g_Net_Slist_Update;
5085 end;
5086 end else
5087 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5088 end
5089 else if cmd = 'permban' then
5090 begin
5091 if g_Game_IsServer and g_Game_IsNet then
5092 begin
5093 if Length(P) < 2 then
5094 begin
5095 g_Console_Add('permban <name>');
5096 Exit;
5097 end;
5098 if P[1] = '' then
5099 begin
5100 g_Console_Add('permban <name>');
5101 Exit;
5102 end;
5104 pl := g_Net_Client_ByName(P[1]);
5105 if (pl <> nil) then
5106 begin
5107 s := g_Net_ClientName_ByID(pl^.ID);
5108 g_Net_BanHost(pl^.Peer^.address.host);
5109 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5110 g_Net_SaveBanList();
5111 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5112 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5113 if NetUseMaster then
5114 g_Net_Slist_Update;
5115 end else
5116 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5117 end else
5118 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5119 end
5120 else if cmd = 'permban_id' then
5121 begin
5122 if g_Game_IsServer and g_Game_IsNet then
5123 begin
5124 if Length(P) < 2 then
5125 begin
5126 g_Console_Add('permban_id <client ID>');
5127 Exit;
5128 end;
5129 if P[1] = '' then
5130 begin
5131 g_Console_Add('permban_id <client ID>');
5132 Exit;
5133 end;
5135 a := StrToIntDef(P[1], 0);
5136 if (NetClients <> nil) and (a <= High(NetClients)) then
5137 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5138 begin
5139 s := g_Net_ClientName_ByID(NetClients[a].ID);
5140 g_Net_BanHost(NetClients[a].Peer^.address.host);
5141 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5142 g_Net_SaveBanList();
5143 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5144 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5145 if NetUseMaster then
5146 g_Net_Slist_Update;
5147 end;
5148 end else
5149 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5150 end
5151 else if cmd = 'unban' then
5152 begin
5153 if g_Game_IsServer and g_Game_IsNet then
5154 begin
5155 if Length(P) < 2 then
5156 begin
5157 g_Console_Add('unban <IP Address>');
5158 Exit;
5159 end;
5160 if P[1] = '' then
5161 begin
5162 g_Console_Add('unban <IP Address>');
5163 Exit;
5164 end;
5166 if g_Net_UnbanHost(P[1]) then
5167 begin
5168 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5169 g_Net_SaveBanList();
5170 end else
5171 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5172 end else
5173 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5174 end
5175 else if cmd = 'clientlist' then
5176 begin
5177 if g_Game_IsServer and g_Game_IsNet then
5178 begin
5179 b := 0;
5180 if NetClients <> nil then
5181 for a := Low(NetClients) to High(NetClients) do
5182 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5183 begin
5184 plr := g_Player_Get(NetClients[a].Player);
5185 if plr = nil then continue;
5186 Inc(b);
5187 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5188 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5189 end;
5190 if b = 0 then
5191 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5192 end else
5193 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5194 end
5195 else if cmd = 'connect' then
5196 begin
5197 if (NetMode = NET_NONE) then
5198 begin
5199 if Length(P) < 2 then
5200 begin
5201 g_Console_Add('connect <IP> [port] [password]');
5202 Exit;
5203 end;
5204 if P[1] = '' then
5205 begin
5206 g_Console_Add('connect <IP> [port] [password]');
5207 Exit;
5208 end;
5210 if Length(P) > 2 then
5211 prt := StrToIntDef(P[2], 25666)
5212 else
5213 prt := 25666;
5215 if Length(P) > 3 then
5216 pw := P[3]
5217 else
5218 pw := '';
5220 g_Game_StartClient(P[1], prt, pw);
5221 end;
5222 end
5223 else if cmd = 'disconnect' then
5224 begin
5225 if (NetMode = NET_CLIENT) then
5226 g_Net_Disconnect();
5227 end
5228 else if cmd = 'reconnect' then
5229 begin
5230 if (NetMode = NET_SERVER) then
5231 Exit;
5233 if (NetMode = NET_CLIENT) then
5234 begin
5235 g_Net_Disconnect();
5236 gExit := EXIT_SIMPLE;
5237 EndGame;
5238 end;
5240 //TODO: Use last successful password to reconnect, instead of ''
5241 g_Game_StartClient(NetClientIP, NetClientPort, '');
5242 end
5243 else if (cmd = 'addbot') or
5244 (cmd = 'bot_add') then
5245 begin
5246 if Length(P) > 1 then
5247 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5248 else
5249 g_Bot_Add(TEAM_NONE, 2);
5250 end
5251 else if cmd = 'bot_addlist' then
5252 begin
5253 if Length(P) > 1 then
5254 if Length(P) = 2 then
5255 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5256 else
5257 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5258 end
5259 else if cmd = 'bot_removeall' then
5260 g_Bot_RemoveAll()
5261 else if cmd = 'chat' then
5262 begin
5263 if g_Game_IsNet then
5264 begin
5265 if Length(P) > 1 then
5266 begin
5267 for a := 1 to High(P) do
5268 chstr := chstr + P[a] + ' ';
5270 if Length(chstr) > 200 then SetLength(chstr, 200);
5272 if Length(chstr) < 1 then
5273 begin
5274 g_Console_Add('chat <text>');
5275 Exit;
5276 end;
5278 chstr := b_Text_Format(chstr);
5279 if g_Game_IsClient then
5280 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5281 else
5282 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5283 end
5284 else
5285 g_Console_Add('chat <text>');
5286 end else
5287 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5288 end
5289 else if cmd = 'teamchat' then
5290 begin
5291 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5292 begin
5293 if Length(P) > 1 then
5294 begin
5295 for a := 1 to High(P) do
5296 chstr := chstr + P[a] + ' ';
5298 if Length(chstr) > 200 then SetLength(chstr, 200);
5300 if Length(chstr) < 1 then
5301 begin
5302 g_Console_Add('teamchat <text>');
5303 Exit;
5304 end;
5306 chstr := b_Text_Format(chstr);
5307 if g_Game_IsClient then
5308 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5309 else
5310 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5311 gPlayer1Settings.Team);
5312 end
5313 else
5314 g_Console_Add('teamchat <text>');
5315 end else
5316 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5317 end
5318 else if cmd = 'game' then
5319 begin
5320 if gGameSettings.GameType <> GT_NONE then
5321 begin
5322 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5323 Exit;
5324 end;
5325 if Length(P) = 1 then
5326 begin
5327 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5328 Exit;
5329 end;
5330 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5331 P[1] := addWadExtension(P[1]);
5332 if FileExists(MapsDir + P[1]) then
5333 begin
5334 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5335 if Length(P) < 3 then
5336 begin
5337 SetLength(P, 3);
5338 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5339 end;
5341 s := P[1] + ':\' + UpperCase(P[2]);
5343 if g_Map_Exist(MapsDir + s) then
5344 begin
5345 // Çàïóñêàåì ñâîþ èãðó
5346 g_Game_Free();
5347 with gGameSettings do
5348 begin
5349 GameMode := g_Game_TextToMode(gcGameMode);
5350 if gSwitchGameMode <> GM_NONE then
5351 GameMode := gSwitchGameMode;
5352 if GameMode = GM_NONE then GameMode := GM_DM;
5353 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5354 b := 1;
5355 if Length(P) >= 4 then
5356 b := StrToIntDef(P[3], 1);
5357 g_Game_StartCustom(s, GameMode, TimeLimit,
5358 GoalLimit, MaxLives, Options, b);
5359 end;
5360 end
5361 else
5362 if P[2] = '' then
5363 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5364 else
5365 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5366 end else
5367 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5368 end
5369 else if cmd = 'host' then
5370 begin
5371 if gGameSettings.GameType <> GT_NONE then
5372 begin
5373 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5374 Exit;
5375 end;
5376 if Length(P) < 4 then
5377 begin
5378 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5379 Exit;
5380 end;
5381 if not StrToIp(P[1], listen) then
5382 Exit;
5383 prt := StrToIntDef(P[2], 25666);
5385 P[3] := addWadExtension(P[3]);
5386 if FileExists(MapsDir + P[3]) then
5387 begin
5388 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5389 if Length(P) < 5 then
5390 begin
5391 SetLength(P, 5);
5392 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5393 end;
5395 s := P[3] + ':\' + UpperCase(P[4]);
5397 if g_Map_Exist(MapsDir + s) then
5398 begin
5399 // Çàïóñêàåì ñâîþ èãðó
5400 g_Game_Free();
5401 with gGameSettings do
5402 begin
5403 GameMode := g_Game_TextToMode(gcGameMode);
5404 if gSwitchGameMode <> GM_NONE then
5405 GameMode := gSwitchGameMode;
5406 if GameMode = GM_NONE then GameMode := GM_DM;
5407 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5408 b := 0;
5409 if Length(P) >= 6 then
5410 b := StrToIntDef(P[5], 0);
5411 g_Game_StartServer(s, GameMode, TimeLimit,
5412 GoalLimit, MaxLives, Options, b, listen, prt);
5413 end;
5414 end
5415 else
5416 if P[4] = '' then
5417 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5418 else
5419 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5420 end else
5421 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5422 end
5423 else if cmd = 'map' then
5424 begin
5425 if Length(P) = 1 then
5426 begin
5427 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5428 begin
5429 g_Console_Add(cmd + ' <MAP>');
5430 g_Console_Add(cmd + ' <WAD> [MAP]');
5431 end else
5432 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5433 end else
5434 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5435 begin
5436 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5437 if Length(P) < 3 then
5438 begin
5439 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5440 s := UpperCase(P[1]);
5441 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5442 begin // Êàðòà íàøëàñü
5443 gExitByTrigger := False;
5444 if gGameOn then
5445 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5446 gNextMap := s;
5447 gExit := EXIT_ENDLEVELCUSTOM;
5448 end
5449 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5450 g_Game_ChangeMap(s);
5451 end else
5452 begin
5453 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5454 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5455 P[1] := addWadExtension(P[1]);
5456 if FileExists(MapsDir + P[1]) then
5457 begin
5458 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5459 SetLength(P, 3);
5460 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5462 s := P[1] + ':\' + P[2];
5464 if g_Map_Exist(MapsDir + s) then
5465 begin
5466 gExitByTrigger := False;
5467 if gGameOn then
5468 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5469 gNextMap := s;
5470 gExit := EXIT_ENDLEVELCUSTOM;
5471 end
5472 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5473 g_Game_ChangeMap(s);
5474 end else
5475 if P[2] = '' then
5476 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5477 else
5478 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5479 end else
5480 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5481 end;
5482 end else
5483 begin
5484 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5485 P[1] := addWadExtension(P[1]);
5486 if FileExists(MapsDir + P[1]) then
5487 begin
5488 // Íàøëè WAD ôàéë
5489 P[2] := UpperCase(P[2]);
5490 s := P[1] + ':\' + P[2];
5492 if g_Map_Exist(MapsDir + s) then
5493 begin // Íàøëè êàðòó
5494 gExitByTrigger := False;
5495 if gGameOn then
5496 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5497 gNextMap := s;
5498 gExit := EXIT_ENDLEVELCUSTOM;
5499 end
5500 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5501 g_Game_ChangeMap(s);
5502 end else
5503 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5504 end else
5505 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5506 end;
5507 end else
5508 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5509 end
5510 else if cmd = 'nextmap' then
5511 begin
5512 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5513 g_Console_Add(_lc[I_MSG_NOT_GAME])
5514 else begin
5515 nm := True;
5516 if Length(P) = 1 then
5517 begin
5518 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5519 begin
5520 g_Console_Add(cmd + ' <MAP>');
5521 g_Console_Add(cmd + ' <WAD> [MAP]');
5522 end else begin
5523 nm := False;
5524 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5525 end;
5526 end else
5527 begin
5528 nm := False;
5529 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5530 begin
5531 if Length(P) < 3 then
5532 begin
5533 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5534 s := UpperCase(P[1]);
5535 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5536 begin // Êàðòà íàøëàñü
5537 gExitByTrigger := False;
5538 gNextMap := s;
5539 nm := True;
5540 end else
5541 begin
5542 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5543 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5544 P[1] := addWadExtension(P[1]);
5545 if FileExists(MapsDir + P[1]) then
5546 begin
5547 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5548 SetLength(P, 3);
5549 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5551 s := P[1] + ':\' + P[2];
5553 if g_Map_Exist(MapsDir + s) then
5554 begin // Óñòàíàâëèâàåì êàðòó
5555 gExitByTrigger := False;
5556 gNextMap := s;
5557 nm := True;
5558 end else
5559 if P[2] = '' then
5560 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5561 else
5562 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5563 end else
5564 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5565 end;
5566 end else
5567 begin
5568 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5569 P[1] := addWadExtension(P[1]);
5570 if FileExists(MapsDir + P[1]) then
5571 begin
5572 // Íàøëè WAD ôàéë
5573 P[2] := UpperCase(P[2]);
5574 s := P[1] + ':\' + P[2];
5576 if g_Map_Exist(MapsDir + s) then
5577 begin // Íàøëè êàðòó
5578 gExitByTrigger := False;
5579 gNextMap := s;
5580 nm := True;
5581 end else
5582 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5583 end else
5584 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5585 end;
5586 end else
5587 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5588 end;
5589 if nm then
5590 if gNextMap = '' then
5591 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
5592 else
5593 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
5594 end;
5595 end
5596 else if (cmd = 'endmap') or (cmd = 'goodbye') then
5597 begin
5598 if not gGameOn then
5599 g_Console_Add(_lc[I_MSG_NOT_GAME])
5600 else
5601 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5602 begin
5603 gExitByTrigger := False;
5604 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
5605 if (gNextMap = '') and (gTriggers <> nil) then
5606 for a := 0 to High(gTriggers) do
5607 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5608 begin
5609 gExitByTrigger := True;
5610 gNextMap := gTriggers[a].Data.MapName;
5611 Break;
5612 end;
5613 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
5614 if gNextMap = '' then
5615 gNextMap := g_Game_GetNextMap();
5616 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
5617 if Pos(':\', gNextMap) = 0 then
5618 s := gGameSettings.WAD + ':\' + gNextMap
5619 else
5620 s := gNextMap;
5621 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
5622 if g_Map_Exist(MapsDir + s) then
5623 gExit := EXIT_ENDLEVELCUSTOM
5624 else
5625 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
5626 end else
5627 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5628 end
5629 else if (cmd = 'event') then
5630 begin
5631 if (Length(P) <= 1) then
5632 begin
5633 for a := 0 to High(gEvents) do
5634 if gEvents[a].Command = '' then
5635 g_Console_Add(gEvents[a].Name + ' <none>')
5636 else
5637 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5638 Exit;
5639 end;
5640 if (Length(P) = 2) then
5641 begin
5642 for a := 0 to High(gEvents) do
5643 if gEvents[a].Name = P[1] then
5644 if gEvents[a].Command = '' then
5645 g_Console_Add(gEvents[a].Name + ' <none>')
5646 else
5647 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5648 Exit;
5649 end;
5650 for a := 0 to High(gEvents) do
5651 if gEvents[a].Name = P[1] then
5652 begin
5653 gEvents[a].Command := '';
5654 for b := 2 to High(P) do
5655 if Pos(' ', P[b]) = 0 then
5656 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
5657 else
5658 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
5659 gEvents[a].Command := Trim(gEvents[a].Command);
5660 Exit;
5661 end;
5662 end
5663 // Êîìàíäû Ñâîåé èãðû:
5664 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5665 begin
5666 if cmd = 'bot_addred' then
5667 begin
5668 if Length(P) > 1 then
5669 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
5670 else
5671 g_Bot_Add(TEAM_RED, 2);
5672 end
5673 else if cmd = 'bot_addblue' then
5674 begin
5675 if Length(P) > 1 then
5676 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
5677 else
5678 g_Bot_Add(TEAM_BLUE, 2);
5679 end
5680 else if cmd = 'suicide' then
5681 begin
5682 if gGameOn then
5683 begin
5684 if g_Game_IsClient then
5685 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
5686 else
5687 begin
5688 if gPlayer1 <> nil then
5689 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
5690 if gPlayer2 <> nil then
5691 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
5692 end;
5693 end;
5694 end
5695 else if cmd = 'spectate' then
5696 begin
5697 if not gGameOn then
5698 Exit;
5699 g_Game_Spectate();
5700 end
5701 else if cmd = 'say' then
5702 begin
5703 if g_Game_IsServer and g_Game_IsNet then
5704 begin
5705 if Length(P) > 1 then
5706 begin
5707 chstr := '';
5708 for a := 1 to High(P) do
5709 chstr := chstr + P[a] + ' ';
5711 if Length(chstr) > 200 then SetLength(chstr, 200);
5713 if Length(chstr) < 1 then
5714 begin
5715 g_Console_Add('say <text>');
5716 Exit;
5717 end;
5719 chstr := b_Text_Format(chstr);
5720 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
5721 end
5722 else g_Console_Add('say <text>');
5723 end else
5724 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5725 end
5726 else if cmd = 'tell' then
5727 begin
5728 if g_Game_IsServer and g_Game_IsNet then
5729 begin
5730 if (Length(P) > 2) and (P[1] <> '') then
5731 begin
5732 chstr := '';
5733 for a := 2 to High(P) do
5734 chstr := chstr + P[a] + ' ';
5736 if Length(chstr) > 200 then SetLength(chstr, 200);
5738 if Length(chstr) < 1 then
5739 begin
5740 g_Console_Add('tell <playername> <text>');
5741 Exit;
5742 end;
5744 pl := g_Net_Client_ByName(P[1]);
5745 if pl <> nil then
5746 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
5747 else
5748 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5749 end
5750 else g_Console_Add('tell <playername> <text>');
5751 end else
5752 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5753 end
5754 else if (cmd = 'overtime') and not g_Game_IsClient then
5755 begin
5756 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
5757 Exit;
5758 // Äîïîëíèòåëüíîå âðåìÿ:
5759 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
5761 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5762 [gGameSettings.TimeLimit div 3600,
5763 (gGameSettings.TimeLimit div 60) mod 60,
5764 gGameSettings.TimeLimit mod 60]));
5765 if g_Game_IsNet then MH_SEND_GameSettings;
5766 end
5767 else if (cmd = 'rcon_password') and g_Game_IsClient then
5768 begin
5769 if (Length(P) <= 1) then
5770 g_Console_Add('rcon_password <password>')
5771 else
5772 MC_SEND_RCONPassword(P[1]);
5773 end
5774 else if cmd = 'rcon' then
5775 begin
5776 if g_Game_IsClient then
5777 begin
5778 if Length(P) > 1 then
5779 begin
5780 chstr := '';
5781 for a := 1 to High(P) do
5782 chstr := chstr + P[a] + ' ';
5784 if Length(chstr) > 200 then SetLength(chstr, 200);
5786 if Length(chstr) < 1 then
5787 begin
5788 g_Console_Add('rcon <command>');
5789 Exit;
5790 end;
5792 MC_SEND_RCONCommand(chstr);
5793 end
5794 else g_Console_Add('rcon <command>');
5795 end;
5796 end
5797 else if cmd = 'ready' then
5798 begin
5799 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
5800 gLMSRespawnTime := gTime + 100;
5801 end
5802 else if (cmd = 'callvote') and g_Game_IsNet then
5803 begin
5804 if Length(P) > 1 then
5805 begin
5806 chstr := '';
5807 for a := 1 to High(P) do begin
5808 if a > 1 then chstr := chstr + ' ';
5809 chstr := chstr + P[a];
5810 end;
5812 if Length(chstr) > 200 then SetLength(chstr, 200);
5814 if Length(chstr) < 1 then
5815 begin
5816 g_Console_Add('callvote <command>');
5817 Exit;
5818 end;
5820 if g_Game_IsClient then
5821 MC_SEND_Vote(True, chstr)
5822 else
5823 g_Game_StartVote(chstr, gPlayer1Settings.Name);
5824 g_Console_Process('vote', True);
5825 end
5826 else
5827 g_Console_Add('callvote <command>');
5828 end
5829 else if (cmd = 'vote') and g_Game_IsNet then
5830 begin
5831 if g_Game_IsClient then
5832 MC_SEND_Vote(False)
5833 else if gVoteInProgress then
5834 begin
5835 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
5836 a := Floor((NetClientCount+1)/2.0) + 1
5837 else
5838 a := Floor(NetClientCount/2.0) + 1;
5839 if gVoted then
5840 begin
5841 Dec(gVoteCount);
5842 gVoted := False;
5843 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
5844 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
5845 end
5846 else
5847 begin
5848 Inc(gVoteCount);
5849 gVoted := True;
5850 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
5851 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
5852 g_Game_CheckVote;
5853 end;
5854 end;
5855 end
5856 end;
5857 end;
5859 procedure g_TakeScreenShot();
5860 var
5861 a: Word;
5862 FileName: string;
5863 ssdir, t: string;
5864 st: TStream;
5865 ok: Boolean;
5866 begin
5867 if e_NoGraphics then Exit;
5868 ssdir := GameDir+'/screenshots';
5869 if not findFileCI(ssdir, true) then
5870 begin
5871 // try to create dir
5872 try
5873 CreateDir(ssdir);
5874 except
5875 end;
5876 if not findFileCI(ssdir, true) then exit; // alas
5877 end;
5878 try
5879 for a := 1 to High(Word) do
5880 begin
5881 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
5882 t := FileName;
5883 if findFileCI(t, true) then continue;
5884 if not findFileCI(FileName) then
5885 begin
5886 ok := false;
5887 st := createDiskFile(FileName);
5888 try
5889 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
5890 ok := true;
5891 finally
5892 st.Free();
5893 end;
5894 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
5895 break;
5896 end;
5897 end;
5898 except
5899 end;
5900 end;
5902 procedure g_Game_InGameMenu(Show: Boolean);
5903 begin
5904 if (g_ActiveWindow = nil) and Show then
5905 begin
5906 if gGameSettings.GameType = GT_SINGLE then
5907 g_GUI_ShowWindow('GameSingleMenu')
5908 else
5909 begin
5910 if g_Game_IsClient then
5911 g_GUI_ShowWindow('GameClientMenu')
5912 else
5913 if g_Game_IsNet then
5914 g_GUI_ShowWindow('GameServerMenu')
5915 else
5916 g_GUI_ShowWindow('GameCustomMenu');
5917 end;
5918 g_Sound_PlayEx('MENU_OPEN');
5920 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
5921 if (not g_Game_IsNet) then
5922 g_Game_Pause(True);
5923 end
5924 else
5925 if (g_ActiveWindow <> nil) and (not Show) then
5926 begin
5927 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
5928 if (not g_Game_IsNet) then
5929 g_Game_Pause(False);
5930 end;
5931 end;
5933 procedure g_Game_Pause(Enable: Boolean);
5934 begin
5935 if not gGameOn then
5936 Exit;
5938 if gPause = Enable then
5939 Exit;
5941 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
5942 Exit;
5944 gPause := Enable;
5945 g_Game_PauseAllSounds(Enable);
5946 end;
5948 procedure g_Game_PauseAllSounds(Enable: Boolean);
5949 var
5950 i: Integer;
5951 begin
5952 // Òðèããåðû:
5953 if gTriggers <> nil then
5954 for i := 0 to High(gTriggers) do
5955 with gTriggers[i] do
5956 if (TriggerType = TRIGGER_SOUND) and
5957 (Sound <> nil) and
5958 Sound.IsPlaying() then
5959 begin
5960 Sound.Pause(Enable);
5961 end;
5963 // Çâóêè èãðîêîâ:
5964 if gPlayers <> nil then
5965 for i := 0 to High(gPlayers) do
5966 if gPlayers[i] <> nil then
5967 gPlayers[i].PauseSounds(Enable);
5969 // Ìóçûêà:
5970 if gMusic <> nil then
5971 gMusic.Pause(Enable);
5972 end;
5974 procedure g_Game_StopAllSounds(all: Boolean);
5975 var
5976 i: Integer;
5977 begin
5978 if gTriggers <> nil then
5979 for i := 0 to High(gTriggers) do
5980 with gTriggers[i] do
5981 if (TriggerType = TRIGGER_SOUND) and
5982 (Sound <> nil) then
5983 Sound.Stop();
5985 if gMusic <> nil then
5986 gMusic.Stop();
5988 if all then
5989 e_StopChannels();
5990 end;
5992 procedure g_Game_UpdateTriggerSounds();
5993 var
5994 i: Integer;
5995 begin
5996 if gTriggers <> nil then
5997 for i := 0 to High(gTriggers) do
5998 with gTriggers[i] do
5999 if (TriggerType = TRIGGER_SOUND) and
6000 (Sound <> nil) and
6001 (Data.Local) and
6002 Sound.IsPlaying() then
6003 begin
6004 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6005 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6006 begin
6007 Sound.SetPan(0.5 - Data.Pan/255.0);
6008 Sound.SetVolume(Data.Volume/255.0);
6009 end
6010 else
6011 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6012 end;
6013 end;
6015 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6016 begin
6017 Result := False;
6018 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6019 begin
6020 Result := True;
6021 Exit;
6022 end;
6023 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6024 begin
6025 Result := True;
6026 Exit;
6027 end;
6028 if gSpectMode <> SPECT_PLAYERS then
6029 Exit;
6030 if gSpectPID1 = UID then
6031 begin
6032 Result := True;
6033 Exit;
6034 end;
6035 if gSpectViewTwo and (gSpectPID2 = UID) then
6036 begin
6037 Result := True;
6038 Exit;
6039 end;
6040 end;
6042 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6043 var
6044 Pl: TPlayer;
6045 begin
6046 Result := False;
6047 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6048 begin
6049 Result := True;
6050 Exit;
6051 end;
6052 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6053 begin
6054 Result := True;
6055 Exit;
6056 end;
6057 if gSpectMode <> SPECT_PLAYERS then
6058 Exit;
6059 Pl := g_Player_Get(gSpectPID1);
6060 if (Pl <> nil) and (Pl.Team = Team) then
6061 begin
6062 Result := True;
6063 Exit;
6064 end;
6065 if gSpectViewTwo then
6066 begin
6067 Pl := g_Player_Get(gSpectPID2);
6068 if (Pl <> nil) and (Pl.Team = Team) then
6069 begin
6070 Result := True;
6071 Exit;
6072 end;
6073 end;
6074 end;
6076 procedure g_Game_Message(Msg: string; Time: Word);
6077 begin
6078 MessageText := b_Text_Format(Msg);
6079 MessageTime := Time;
6080 end;
6082 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6083 var
6084 a: Integer;
6085 begin
6086 case gAnnouncer of
6087 ANNOUNCE_NONE:
6088 Exit;
6089 ANNOUNCE_ME,
6090 ANNOUNCE_MEPLUS:
6091 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6092 Exit;
6093 end;
6094 for a := 0 to 3 do
6095 if goodsnd[a].IsPlaying() then
6096 Exit;
6098 goodsnd[Random(4)].Play();
6099 end;
6101 procedure g_Game_Announce_KillCombo(Param: Integer);
6102 var
6103 UID: Word;
6104 c, n: Byte;
6105 Pl: TPlayer;
6106 Name: String;
6107 begin
6108 UID := Param and $FFFF;
6109 c := Param shr 16;
6110 if c < 2 then
6111 Exit;
6113 Pl := g_Player_Get(UID);
6114 if Pl = nil then
6115 Name := '?'
6116 else
6117 Name := Pl.Name;
6119 case c of
6120 2: begin
6121 n := 0;
6122 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6123 end;
6124 3: begin
6125 n := 1;
6126 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6127 end;
6128 4: begin
6129 n := 2;
6130 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6131 end;
6132 else begin
6133 n := 3;
6134 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6135 end;
6136 end;
6138 case gAnnouncer of
6139 ANNOUNCE_NONE:
6140 Exit;
6141 ANNOUNCE_ME:
6142 if not g_Game_IsWatchedPlayer(UID) then
6143 Exit;
6144 ANNOUNCE_MEPLUS:
6145 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6146 Exit;
6147 end;
6149 if killsnd[n].IsPlaying() then
6150 killsnd[n].Stop();
6151 killsnd[n].Play();
6152 end;
6154 procedure g_Game_StartVote(Command, Initiator: string);
6155 var
6156 Need: Integer;
6157 begin
6158 if not gVotesEnabled then Exit;
6159 if gGameSettings.GameType <> GT_SERVER then Exit;
6160 if gVoteInProgress or gVotePassed then
6161 begin
6162 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6163 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6164 Exit;
6165 end;
6166 gVoteInProgress := True;
6167 gVotePassed := False;
6168 gVoteTimer := gTime + gVoteTimeout * 1000;
6169 gVoteCount := 0;
6170 gVoted := False;
6171 gVoteCommand := Command;
6173 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6174 Need := Floor((NetClientCount+1)/2.0)+1
6175 else
6176 Need := Floor(NetClientCount/2.0)+1;
6177 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6178 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6179 end;
6181 procedure g_Game_CheckVote;
6182 var
6183 I, Need: Integer;
6184 begin
6185 if gGameSettings.GameType <> GT_SERVER then Exit;
6186 if not gVoteInProgress then Exit;
6188 if (gTime >= gVoteTimer) then
6189 begin
6190 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6191 Need := Floor((NetClientCount+1)/2.0) + 1
6192 else
6193 Need := Floor(NetClientCount/2.0) + 1;
6194 if gVoteCount >= Need then
6195 begin
6196 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6197 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6198 gVotePassed := True;
6199 gVoteCmdTimer := gTime + 5000;
6200 end
6201 else
6202 begin
6203 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6204 MH_SEND_VoteEvent(NET_VE_FAILED);
6205 end;
6206 if NetClients <> nil then
6207 for I := Low(NetClients) to High(NetClients) do
6208 if NetClients[i].Used then
6209 NetClients[i].Voted := False;
6210 gVoteInProgress := False;
6211 gVoted := False;
6212 gVoteCount := 0;
6213 end
6214 else
6215 begin
6216 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6217 Need := Floor((NetClientCount+1)/2.0) + 1
6218 else
6219 Need := Floor(NetClientCount/2.0) + 1;
6220 if gVoteCount >= Need then
6221 begin
6222 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6223 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6224 gVoteInProgress := False;
6225 gVotePassed := True;
6226 gVoteCmdTimer := gTime + 5000;
6227 gVoted := False;
6228 gVoteCount := 0;
6229 if NetClients <> nil then
6230 for I := Low(NetClients) to High(NetClients) do
6231 if NetClients[i].Used then
6232 NetClients[i].Voted := False;
6233 end;
6234 end;
6235 end;
6237 procedure g_Game_LoadMapList(FileName: string);
6238 var
6239 ListFile: TextFile;
6240 s: string;
6241 begin
6242 MapList := nil;
6243 MapIndex := -1;
6245 if not FileExists(FileName) then Exit;
6247 AssignFile(ListFile, FileName);
6248 Reset(ListFile);
6249 while not EOF(ListFile) do
6250 begin
6251 ReadLn(ListFile, s);
6253 s := Trim(s);
6254 if s = '' then Continue;
6256 SetLength(MapList, Length(MapList)+1);
6257 MapList[High(MapList)] := s;
6258 end;
6259 CloseFile(ListFile);
6260 end;
6262 procedure g_Game_SetDebugMode();
6263 begin
6264 gDebugMode := True;
6265 // ×èòû (äàæå â ñâîåé èãðå):
6266 gCheats := True;
6267 end;
6269 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6270 var
6271 i: Word;
6272 begin
6273 if Length(LoadingStat.Msgs) = 0 then
6274 Exit;
6276 with LoadingStat do
6277 begin
6278 if not reWrite then
6279 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6280 if NextMsg = Length(Msgs) then
6281 begin // scroll
6282 for i := 0 to High(Msgs)-1 do
6283 Msgs[i] := Msgs[i+1];
6284 end
6285 else
6286 Inc(NextMsg);
6287 end else
6288 if NextMsg = 0 then
6289 Inc(NextMsg);
6291 Msgs[NextMsg-1] := Text;
6292 CurValue := 0;
6293 MaxValue := Max;
6294 ShowCount := 0;
6295 end;
6297 g_ActiveWindow := nil;
6299 ProcessLoading;
6300 end;
6302 procedure g_Game_StepLoading();
6303 begin
6304 with LoadingStat do
6305 begin
6306 Inc(CurValue);
6307 Inc(ShowCount);
6308 if (ShowCount > LOADING_SHOW_STEP) then
6309 begin
6310 ShowCount := 0;
6311 ProcessLoading;
6312 end;
6313 end;
6314 end;
6316 procedure g_Game_ClearLoading();
6317 var
6318 len: Word;
6319 begin
6320 with LoadingStat do
6321 begin
6322 CurValue := 0;
6323 MaxValue := 0;
6324 ShowCount := 0;
6325 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6326 if len < 1 then len := 1;
6327 SetLength(Msgs, len);
6328 for len := Low(Msgs) to High(Msgs) do
6329 Msgs[len] := '';
6330 NextMsg := 0;
6331 end;
6332 end;
6334 procedure Parse_Params(var pars: TParamStrValues);
6335 var
6336 i: Integer;
6337 s: String;
6338 begin
6339 SetLength(pars, 0);
6340 i := 1;
6341 while i <= ParamCount do
6342 begin
6343 s := ParamStr(i);
6344 if (s[1] = '-') and (Length(s) > 1) then
6345 begin
6346 if (s[2] = '-') and (Length(s) > 2) then
6347 begin // Îäèíî÷íûé ïàðàìåòð
6348 SetLength(pars, Length(pars) + 1);
6349 with pars[High(pars)] do
6350 begin
6351 Name := LowerCase(s);
6352 Value := '+';
6353 end;
6354 end
6355 else
6356 if (i < ParamCount) then
6357 begin // Ïàðàìåòð ñî çíà÷åíèåì
6358 Inc(i);
6359 SetLength(pars, Length(pars) + 1);
6360 with pars[High(pars)] do
6361 begin
6362 Name := LowerCase(s);
6363 Value := LowerCase(ParamStr(i));
6364 end;
6365 end;
6366 end;
6368 Inc(i);
6369 end;
6370 end;
6372 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6373 var
6374 i: Integer;
6375 begin
6376 Result := '';
6377 for i := 0 to High(pars) do
6378 if pars[i].Name = aName then
6379 begin
6380 Result := pars[i].Value;
6381 Break;
6382 end;
6383 end;
6385 procedure g_Game_Process_Params();
6386 var
6387 pars: TParamStrValues;
6388 map: String;
6389 GMode, n: Byte;
6390 LimT, LimS: Integer;
6391 Opt: LongWord;
6392 Lives: Integer;
6393 s: String;
6394 Port: Integer;
6395 ip: String;
6396 F: TextFile;
6397 begin
6398 Parse_Params(pars);
6400 // Debug mode:
6401 s := Find_Param_Value(pars, '--debug');
6402 if (s <> '') then
6403 g_Game_SetDebugMode();
6405 // Connect when game loads
6406 ip := Find_Param_Value(pars, '-connect');
6408 if ip <> '' then
6409 begin
6410 s := Find_Param_Value(pars, '-port');
6411 if (s = '') or not TryStrToInt(s, Port) then
6412 Port := 25666;
6414 s := Find_Param_Value(pars, '-pw');
6416 g_Game_StartClient(ip, Port, s);
6417 Exit;
6418 end;
6420 // Start map when game loads:
6421 map := LowerCase(Find_Param_Value(pars, '-map'));
6422 if isWadPath(map) then
6423 begin
6424 // Game mode:
6425 s := Find_Param_Value(pars, '-gm');
6426 GMode := g_Game_TextToMode(s);
6427 if GMode = GM_NONE then GMode := GM_DM;
6428 if GMode = GM_SINGLE then GMode := GM_COOP;
6430 // Time limit:
6431 s := Find_Param_Value(pars, '-limt');
6432 if (s = '') or (not TryStrToInt(s, LimT)) then
6433 LimT := 0;
6434 if LimT < 0 then
6435 LimT := 0;
6437 // Goal limit:
6438 s := Find_Param_Value(pars, '-lims');
6439 if (s = '') or (not TryStrToInt(s, LimS)) then
6440 LimS := 0;
6441 if LimS < 0 then
6442 LimS := 0;
6444 // Lives limit:
6445 s := Find_Param_Value(pars, '-lives');
6446 if (s = '') or (not TryStrToInt(s, Lives)) then
6447 Lives := 0;
6448 if Lives < 0 then
6449 Lives := 0;
6451 // Options:
6452 s := Find_Param_Value(pars, '-opt');
6453 if (s = '') then
6454 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6455 else
6456 Opt := StrToIntDef(s, 0);
6457 if Opt = 0 then
6458 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6460 // Close after map:
6461 s := Find_Param_Value(pars, '--close');
6462 if (s <> '') then
6463 gMapOnce := True;
6465 // Delete test map after play:
6466 s := Find_Param_Value(pars, '--testdelete');
6467 if (s <> '') then
6468 begin
6469 gMapToDelete := MapsDir + map;
6470 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6471 Halt(1);
6472 end;
6474 // Delete temporary WAD after play:
6475 s := Find_Param_Value(pars, '--tempdelete');
6476 if (s <> '') then
6477 begin
6478 gMapToDelete := MapsDir + map;
6479 gTempDelete := True;
6480 end;
6482 // Number of players:
6483 s := Find_Param_Value(pars, '-pl');
6484 if (s = '') then
6485 n := 1
6486 else
6487 n := StrToIntDef(s, 1);
6489 // Start:
6490 s := Find_Param_Value(pars, '-port');
6491 if (s = '') or not TryStrToInt(s, Port) then
6492 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6493 else
6494 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6495 end;
6497 // Execute script when game loads:
6498 s := Find_Param_Value(pars, '-exec');
6499 if s <> '' then
6500 begin
6501 if Pos(':\', s) = 0 then
6502 s := GameDir + '/' + s;
6504 {$I-}
6505 AssignFile(F, s);
6506 Reset(F);
6507 if IOResult <> 0 then
6508 begin
6509 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6510 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6511 CloseFile(F);
6512 Exit;
6513 end;
6514 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6515 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6517 while not EOF(F) do
6518 begin
6519 ReadLn(F, s);
6520 if IOResult <> 0 then
6521 begin
6522 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6523 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6524 CloseFile(F);
6525 Exit;
6526 end;
6527 if Pos('#', s) <> 1 then // script comment
6528 g_Console_Process(s, True);
6529 end;
6531 CloseFile(F);
6532 {$I+}
6533 end;
6535 SetLength(pars, 0);
6536 end;
6538 end.