DEADSOFTWARE

implemented weapon queue (no network code for it yet)
[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 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1287 end;
1288 end;
1290 procedure g_Game_Update();
1291 var
1292 Msg: g_gui.TMessage;
1293 Time: Int64;
1294 a: Byte;
1295 w: Word;
1296 i, b: Integer;
1297 begin
1298 // Ïîðà âûêëþ÷àòü èãðó:
1299 if gExit = EXIT_QUIT then
1300 Exit;
1301 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1302 if gExit <> 0 then
1303 begin
1304 EndGame();
1305 if gExit = EXIT_QUIT then
1306 Exit;
1307 end;
1309 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1310 e_PollInput();
1312 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1313 g_Console_Update();
1315 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1316 begin
1317 gExit := EXIT_SIMPLE;
1318 EndGame();
1319 Exit;
1320 end;
1322 case gState of
1323 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1324 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1325 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1326 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1327 begin
1328 if g_Game_IsNet and g_Game_IsServer then
1329 begin
1330 gInterTime := gInterTime + GAME_TICK;
1331 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1332 if a <> gServInterTime then
1333 begin
1334 gServInterTime := a;
1335 MH_SEND_TimeSync(gServInterTime);
1336 end;
1337 end;
1339 if (not g_Game_IsClient) and
1342 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1343 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1344 and (g_ActiveWindow = nil)
1346 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1348 then
1349 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1350 g_Game_StopAllSounds(True);
1352 if gMapOnce then // Ýòî áûë òåñò
1353 gExit := EXIT_SIMPLE
1354 else
1355 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1356 g_Game_ChangeMap(gNextMap)
1357 else // Ñëåäóþùåé êàðòû íåò
1358 begin
1359 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1360 begin
1361 // Âûõîä â ãëàâíîå ìåíþ:
1362 g_Game_Free;
1363 g_GUI_ShowWindow('MainMenu');
1364 gMusic.SetByName('MUSIC_MENU');
1365 gMusic.Play();
1366 gState := STATE_MENU;
1367 end else
1368 begin
1369 // Ôèíàëüíàÿ êàðòèíêà:
1370 g_Game_ExecuteEvent('onwadend');
1371 g_Game_Free();
1372 if not gMusic.SetByName('MUSIC_endmus') then
1373 gMusic.SetByName('MUSIC_STDENDMUS');
1374 gMusic.Play();
1375 gState := STATE_ENDPIC;
1376 end;
1377 g_Game_ExecuteEvent('ongameend');
1378 end;
1380 Exit;
1381 end;
1383 if gState = STATE_INTERTEXT then
1384 if InterText.counter > 0 then
1385 InterText.counter := InterText.counter - 1;
1386 end;
1388 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1389 begin
1390 if EndingGameCounter = 0 then
1391 begin
1392 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1393 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1394 begin
1395 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1396 begin
1397 g_Game_ExecuteEvent('onwadend');
1398 if not gMusic.SetByName('MUSIC_endmus') then
1399 gMusic.SetByName('MUSIC_STDENDMUS');
1400 end
1401 else
1402 gMusic.SetByName('MUSIC_ROUNDMUS');
1404 gMusic.Play();
1405 gState := STATE_INTERCUSTOM;
1406 end
1407 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1408 begin
1409 gMusic.SetByName('MUSIC_INTERMUS');
1410 gMusic.Play();
1411 gState := STATE_INTERSINGLE;
1412 end;
1413 g_Game_ExecuteEvent('oninter');
1414 end
1415 else
1416 DecMin(EndingGameCounter, 6, 0);
1417 end;
1419 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1420 begin
1421 if gMapOnce then // Ýòî áûë òåñò
1422 begin
1423 gExit := EXIT_SIMPLE;
1424 Exit;
1425 end;
1426 end;
1428 STATE_SLIST:
1429 g_Serverlist_Control(slCurrent);
1430 end;
1432 if g_Game_IsNet then
1433 if not gConsoleShow then
1434 if not gChatShow then
1435 begin
1436 if g_ActiveWindow = nil then
1437 begin
1438 if e_KeyPressed(gGameControls.GameControls.Chat) then
1439 g_Console_Chat_Switch(False)
1440 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1441 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1442 g_Console_Chat_Switch(True);
1443 end;
1444 end else
1445 if not gChatEnter then
1446 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1447 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1448 gChatEnter := True;
1450 // Ñòàòèñòèêà ïî Tab:
1451 if gGameOn then
1452 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1453 (gGameSettings.GameType <> GT_SINGLE) and
1454 e_KeyPressed(gGameControls.GameControls.Stat);
1456 // Èãðà èäåò:
1457 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1458 begin
1459 // Âðåìÿ += 28 ìèëëèñåêóíä:
1460 gTime := gTime + GAME_TICK;
1462 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1463 if MessageTime = 0 then
1464 MessageText := '';
1465 if MessageTime > 0 then
1466 MessageTime := MessageTime - 1;
1468 if (g_Game_IsServer) then
1469 begin
1470 // Áûë çàäàí ëèìèò âðåìåíè:
1471 if (gGameSettings.TimeLimit > 0) then
1472 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1473 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1474 g_Game_NextLevel();
1475 Exit;
1476 end;
1478 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1479 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1480 g_Game_RestartRound(gLMSSoftSpawn);
1482 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1483 if gVoteInProgress and (gVoteTimer < gTime) then
1484 g_Game_CheckVote
1485 else if gVotePassed and (gVoteCmdTimer < gTime) then
1486 begin
1487 g_Console_Process(gVoteCommand);
1488 gVoteCommand := '';
1489 gVotePassed := False;
1490 end;
1492 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1493 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1494 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1495 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1496 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1498 // Áûë çàäàí ëèìèò ïîáåä:
1499 if (gGameSettings.GoalLimit > 0) then
1500 begin
1501 b := 0;
1503 if gGameSettings.GameMode = GM_DM then
1504 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1505 for i := 0 to High(gPlayers) do
1506 if gPlayers[i] <> nil then
1507 if gPlayers[i].Frags > b then
1508 b := gPlayers[i].Frags;
1509 end
1510 else
1511 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1512 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1513 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1514 end;
1516 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1517 if b >= gGameSettings.GoalLimit then
1518 begin
1519 g_Game_NextLevel();
1520 Exit;
1521 end;
1522 end;
1524 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1525 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1526 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1527 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1528 begin
1529 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1530 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1531 end // if not console
1532 else
1533 begin
1534 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1535 end;
1536 // process weapon switch queue
1537 if gPlayer1 <> nil then gPlayer1.RealizeCurrentWeapon();
1538 if gPlayer2 <> nil then gPlayer2.RealizeCurrentWeapon();
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 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a shotgun'); continue; end;
4843 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a supershotgun'); continue; end;
4844 if (cmd = 'chain') or (cmd = 'chaingun') then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a chaingun'); continue; end;
4845 if (cmd = 'launcher') or (cmd = 'rocketlauncher') then begin plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER); plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got a rocket launcher'); continue; end;
4846 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got a plasma gun'); continue; end;
4847 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got a BFG-9000'); continue; end;
4848 g_Console_Add('i don''t know how to give '''+cmd+'''!');
4849 end;
4850 exit;
4851 end;
4852 // open
4853 if cmd = 'open' then
4854 begin
4855 g_Console_Add('player activated sesame');
4856 g_Triggers_OpenAll();
4857 exit;
4858 end;
4859 // fly
4860 if cmd = 'fly' then
4861 begin
4862 gFly := not gFly;
4863 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
4864 exit;
4865 end;
4866 // noclip
4867 if cmd = 'noclip' then
4868 begin
4869 plr.SwitchNoClip;
4870 g_Console_Add('wall hardeness adjusted');
4871 exit;
4872 end;
4873 // notarget
4874 if cmd = 'notarget' then
4875 begin
4876 plr.NoTarget := not plr.NoTarget;
4877 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
4878 exit;
4879 end;
4880 // noreload
4881 if cmd = 'noreload' then
4882 begin
4883 plr.NoReload := not plr.NoReload;
4884 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
4885 exit;
4886 end;
4887 // speedy
4888 if cmd = 'speedy' then
4889 begin
4890 MAX_RUNVEL := 32-MAX_RUNVEL;
4891 g_Console_Add('speed adjusted');
4892 exit;
4893 end;
4894 // jumpy
4895 if cmd = 'jumpy' then
4896 begin
4897 VEL_JUMP := 30-VEL_JUMP;
4898 g_Console_Add('jump height adjusted');
4899 exit;
4900 end;
4901 // automap
4902 if cmd = 'automap' then
4903 begin
4904 gShowMap := not gShowMap;
4905 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
4906 exit;
4907 end;
4908 // aimline
4909 if cmd = 'aimline' then
4910 begin
4911 gAimLine := not gAimLine;
4912 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
4913 exit;
4914 end;
4915 end;
4917 procedure GameCommands(P: SArray);
4918 var
4919 a, b: Integer;
4920 s, pw: String;
4921 chstr: string;
4922 cmd: string;
4923 pl: pTNetClient = nil;
4924 plr: TPlayer;
4925 prt: Word;
4926 nm: Boolean;
4927 listen: LongWord;
4928 begin
4929 // Îáùèå êîìàíäû:
4930 cmd := LowerCase(P[0]);
4931 chstr := '';
4932 if (cmd = 'quit') or
4933 (cmd = 'exit') then
4934 begin
4935 g_Game_Free();
4936 g_Game_Quit();
4937 Exit;
4938 end
4939 else if cmd = 'pause' then
4940 begin
4941 if (g_ActiveWindow = nil) then
4942 g_Game_Pause(not gPause);
4943 end
4944 else if cmd = 'endgame' then
4945 gExit := EXIT_SIMPLE
4946 else if cmd = 'restart' then
4947 begin
4948 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
4949 begin
4950 if g_Game_IsClient then
4951 begin
4952 g_Console_Add(_lc[I_MSG_SERVERONLY]);
4953 Exit;
4954 end;
4955 g_Game_Restart();
4956 end else
4957 g_Console_Add(_lc[I_MSG_NOT_GAME]);
4958 end
4959 else if cmd = 'kick' then
4960 begin
4961 if g_Game_IsServer then
4962 begin
4963 if Length(P) < 2 then
4964 begin
4965 g_Console_Add('kick <name>');
4966 Exit;
4967 end;
4968 if P[1] = '' then
4969 begin
4970 g_Console_Add('kick <name>');
4971 Exit;
4972 end;
4974 if g_Game_IsNet then
4975 pl := g_Net_Client_ByName(P[1]);
4976 if (pl <> nil) then
4977 begin
4978 s := g_Net_ClientName_ByID(pl^.ID);
4979 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
4980 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4981 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4982 if NetUseMaster then
4983 g_Net_Slist_Update;
4984 end else if gPlayers <> nil then
4985 for a := Low(gPlayers) to High(gPlayers) do
4986 if gPlayers[a] <> nil then
4987 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
4988 begin
4989 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
4990 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
4991 continue;
4992 gPlayers[a].Lives := 0;
4993 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
4994 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
4995 g_Player_Remove(gPlayers[a].UID);
4996 if NetUseMaster then
4997 g_Net_Slist_Update;
4998 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
4999 g_Bot_MixNames();
5000 end;
5001 end else
5002 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5003 end
5004 else if cmd = 'kick_id' then
5005 begin
5006 if g_Game_IsServer and g_Game_IsNet then
5007 begin
5008 if Length(P) < 2 then
5009 begin
5010 g_Console_Add('kick_id <client ID>');
5011 Exit;
5012 end;
5013 if P[1] = '' then
5014 begin
5015 g_Console_Add('kick_id <client ID>');
5016 Exit;
5017 end;
5019 a := StrToIntDef(P[1], 0);
5020 if (NetClients <> nil) and (a <= High(NetClients)) then
5021 begin
5022 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5023 begin
5024 s := g_Net_ClientName_ByID(NetClients[a].ID);
5025 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5026 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5027 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5028 if NetUseMaster then
5029 g_Net_Slist_Update;
5030 end;
5031 end;
5032 end else
5033 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5034 end
5035 else if cmd = 'ban' then
5036 begin
5037 if g_Game_IsServer and g_Game_IsNet then
5038 begin
5039 if Length(P) < 2 then
5040 begin
5041 g_Console_Add('ban <name>');
5042 Exit;
5043 end;
5044 if P[1] = '' then
5045 begin
5046 g_Console_Add('ban <name>');
5047 Exit;
5048 end;
5050 pl := g_Net_Client_ByName(P[1]);
5051 if (pl <> nil) then
5052 begin
5053 s := g_Net_ClientName_ByID(pl^.ID);
5054 g_Net_BanHost(pl^.Peer^.address.host, False);
5055 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5056 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5057 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5058 if NetUseMaster then
5059 g_Net_Slist_Update;
5060 end else
5061 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5062 end else
5063 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5064 end
5065 else if cmd = 'ban_id' then
5066 begin
5067 if g_Game_IsServer and g_Game_IsNet then
5068 begin
5069 if Length(P) < 2 then
5070 begin
5071 g_Console_Add('ban_id <client ID>');
5072 Exit;
5073 end;
5074 if P[1] = '' then
5075 begin
5076 g_Console_Add('ban_id <client ID>');
5077 Exit;
5078 end;
5080 a := StrToIntDef(P[1], 0);
5081 if (NetClients <> nil) and (a <= High(NetClients)) then
5082 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5083 begin
5084 s := g_Net_ClientName_ByID(NetClients[a].ID);
5085 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5086 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5087 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5088 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5089 if NetUseMaster then
5090 g_Net_Slist_Update;
5091 end;
5092 end else
5093 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5094 end
5095 else if cmd = 'permban' then
5096 begin
5097 if g_Game_IsServer and g_Game_IsNet then
5098 begin
5099 if Length(P) < 2 then
5100 begin
5101 g_Console_Add('permban <name>');
5102 Exit;
5103 end;
5104 if P[1] = '' then
5105 begin
5106 g_Console_Add('permban <name>');
5107 Exit;
5108 end;
5110 pl := g_Net_Client_ByName(P[1]);
5111 if (pl <> nil) then
5112 begin
5113 s := g_Net_ClientName_ByID(pl^.ID);
5114 g_Net_BanHost(pl^.Peer^.address.host);
5115 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5116 g_Net_SaveBanList();
5117 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5118 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5119 if NetUseMaster then
5120 g_Net_Slist_Update;
5121 end else
5122 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5123 end else
5124 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5125 end
5126 else if cmd = 'permban_id' then
5127 begin
5128 if g_Game_IsServer and g_Game_IsNet then
5129 begin
5130 if Length(P) < 2 then
5131 begin
5132 g_Console_Add('permban_id <client ID>');
5133 Exit;
5134 end;
5135 if P[1] = '' then
5136 begin
5137 g_Console_Add('permban_id <client ID>');
5138 Exit;
5139 end;
5141 a := StrToIntDef(P[1], 0);
5142 if (NetClients <> nil) and (a <= High(NetClients)) then
5143 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5144 begin
5145 s := g_Net_ClientName_ByID(NetClients[a].ID);
5146 g_Net_BanHost(NetClients[a].Peer^.address.host);
5147 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5148 g_Net_SaveBanList();
5149 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5150 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5151 if NetUseMaster then
5152 g_Net_Slist_Update;
5153 end;
5154 end else
5155 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5156 end
5157 else if cmd = 'unban' then
5158 begin
5159 if g_Game_IsServer and g_Game_IsNet then
5160 begin
5161 if Length(P) < 2 then
5162 begin
5163 g_Console_Add('unban <IP Address>');
5164 Exit;
5165 end;
5166 if P[1] = '' then
5167 begin
5168 g_Console_Add('unban <IP Address>');
5169 Exit;
5170 end;
5172 if g_Net_UnbanHost(P[1]) then
5173 begin
5174 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5175 g_Net_SaveBanList();
5176 end else
5177 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5178 end else
5179 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5180 end
5181 else if cmd = 'clientlist' then
5182 begin
5183 if g_Game_IsServer and g_Game_IsNet then
5184 begin
5185 b := 0;
5186 if NetClients <> nil then
5187 for a := Low(NetClients) to High(NetClients) do
5188 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5189 begin
5190 plr := g_Player_Get(NetClients[a].Player);
5191 if plr = nil then continue;
5192 Inc(b);
5193 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5194 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5195 end;
5196 if b = 0 then
5197 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5198 end else
5199 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5200 end
5201 else if cmd = 'connect' then
5202 begin
5203 if (NetMode = NET_NONE) then
5204 begin
5205 if Length(P) < 2 then
5206 begin
5207 g_Console_Add('connect <IP> [port] [password]');
5208 Exit;
5209 end;
5210 if P[1] = '' then
5211 begin
5212 g_Console_Add('connect <IP> [port] [password]');
5213 Exit;
5214 end;
5216 if Length(P) > 2 then
5217 prt := StrToIntDef(P[2], 25666)
5218 else
5219 prt := 25666;
5221 if Length(P) > 3 then
5222 pw := P[3]
5223 else
5224 pw := '';
5226 g_Game_StartClient(P[1], prt, pw);
5227 end;
5228 end
5229 else if cmd = 'disconnect' then
5230 begin
5231 if (NetMode = NET_CLIENT) then
5232 g_Net_Disconnect();
5233 end
5234 else if cmd = 'reconnect' then
5235 begin
5236 if (NetMode = NET_SERVER) then
5237 Exit;
5239 if (NetMode = NET_CLIENT) then
5240 begin
5241 g_Net_Disconnect();
5242 gExit := EXIT_SIMPLE;
5243 EndGame;
5244 end;
5246 //TODO: Use last successful password to reconnect, instead of ''
5247 g_Game_StartClient(NetClientIP, NetClientPort, '');
5248 end
5249 else if (cmd = 'addbot') or
5250 (cmd = 'bot_add') then
5251 begin
5252 if Length(P) > 1 then
5253 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5254 else
5255 g_Bot_Add(TEAM_NONE, 2);
5256 end
5257 else if cmd = 'bot_addlist' then
5258 begin
5259 if Length(P) > 1 then
5260 if Length(P) = 2 then
5261 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5262 else
5263 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5264 end
5265 else if cmd = 'bot_removeall' then
5266 g_Bot_RemoveAll()
5267 else if cmd = 'chat' then
5268 begin
5269 if g_Game_IsNet then
5270 begin
5271 if Length(P) > 1 then
5272 begin
5273 for a := 1 to High(P) do
5274 chstr := chstr + P[a] + ' ';
5276 if Length(chstr) > 200 then SetLength(chstr, 200);
5278 if Length(chstr) < 1 then
5279 begin
5280 g_Console_Add('chat <text>');
5281 Exit;
5282 end;
5284 chstr := b_Text_Format(chstr);
5285 if g_Game_IsClient then
5286 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5287 else
5288 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5289 end
5290 else
5291 g_Console_Add('chat <text>');
5292 end else
5293 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5294 end
5295 else if cmd = 'teamchat' then
5296 begin
5297 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5298 begin
5299 if Length(P) > 1 then
5300 begin
5301 for a := 1 to High(P) do
5302 chstr := chstr + P[a] + ' ';
5304 if Length(chstr) > 200 then SetLength(chstr, 200);
5306 if Length(chstr) < 1 then
5307 begin
5308 g_Console_Add('teamchat <text>');
5309 Exit;
5310 end;
5312 chstr := b_Text_Format(chstr);
5313 if g_Game_IsClient then
5314 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5315 else
5316 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5317 gPlayer1Settings.Team);
5318 end
5319 else
5320 g_Console_Add('teamchat <text>');
5321 end else
5322 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5323 end
5324 else if cmd = 'game' then
5325 begin
5326 if gGameSettings.GameType <> GT_NONE then
5327 begin
5328 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5329 Exit;
5330 end;
5331 if Length(P) = 1 then
5332 begin
5333 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5334 Exit;
5335 end;
5336 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5337 P[1] := addWadExtension(P[1]);
5338 if FileExists(MapsDir + P[1]) then
5339 begin
5340 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5341 if Length(P) < 3 then
5342 begin
5343 SetLength(P, 3);
5344 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5345 end;
5347 s := P[1] + ':\' + UpperCase(P[2]);
5349 if g_Map_Exist(MapsDir + s) then
5350 begin
5351 // Çàïóñêàåì ñâîþ èãðó
5352 g_Game_Free();
5353 with gGameSettings do
5354 begin
5355 GameMode := g_Game_TextToMode(gcGameMode);
5356 if gSwitchGameMode <> GM_NONE then
5357 GameMode := gSwitchGameMode;
5358 if GameMode = GM_NONE then GameMode := GM_DM;
5359 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5360 b := 1;
5361 if Length(P) >= 4 then
5362 b := StrToIntDef(P[3], 1);
5363 g_Game_StartCustom(s, GameMode, TimeLimit,
5364 GoalLimit, MaxLives, Options, b);
5365 end;
5366 end
5367 else
5368 if P[2] = '' then
5369 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5370 else
5371 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5372 end else
5373 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5374 end
5375 else if cmd = 'host' then
5376 begin
5377 if gGameSettings.GameType <> GT_NONE then
5378 begin
5379 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5380 Exit;
5381 end;
5382 if Length(P) < 4 then
5383 begin
5384 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5385 Exit;
5386 end;
5387 if not StrToIp(P[1], listen) then
5388 Exit;
5389 prt := StrToIntDef(P[2], 25666);
5391 P[3] := addWadExtension(P[3]);
5392 if FileExists(MapsDir + P[3]) then
5393 begin
5394 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5395 if Length(P) < 5 then
5396 begin
5397 SetLength(P, 5);
5398 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5399 end;
5401 s := P[3] + ':\' + UpperCase(P[4]);
5403 if g_Map_Exist(MapsDir + s) then
5404 begin
5405 // Çàïóñêàåì ñâîþ èãðó
5406 g_Game_Free();
5407 with gGameSettings do
5408 begin
5409 GameMode := g_Game_TextToMode(gcGameMode);
5410 if gSwitchGameMode <> GM_NONE then
5411 GameMode := gSwitchGameMode;
5412 if GameMode = GM_NONE then GameMode := GM_DM;
5413 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5414 b := 0;
5415 if Length(P) >= 6 then
5416 b := StrToIntDef(P[5], 0);
5417 g_Game_StartServer(s, GameMode, TimeLimit,
5418 GoalLimit, MaxLives, Options, b, listen, prt);
5419 end;
5420 end
5421 else
5422 if P[4] = '' then
5423 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5424 else
5425 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5426 end else
5427 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5428 end
5429 else if cmd = 'map' then
5430 begin
5431 if Length(P) = 1 then
5432 begin
5433 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5434 begin
5435 g_Console_Add(cmd + ' <MAP>');
5436 g_Console_Add(cmd + ' <WAD> [MAP]');
5437 end else
5438 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5439 end else
5440 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5441 begin
5442 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5443 if Length(P) < 3 then
5444 begin
5445 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5446 s := UpperCase(P[1]);
5447 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5448 begin // Êàðòà íàøëàñü
5449 gExitByTrigger := False;
5450 if gGameOn then
5451 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5452 gNextMap := s;
5453 gExit := EXIT_ENDLEVELCUSTOM;
5454 end
5455 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5456 g_Game_ChangeMap(s);
5457 end else
5458 begin
5459 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5460 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5461 P[1] := addWadExtension(P[1]);
5462 if FileExists(MapsDir + P[1]) then
5463 begin
5464 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5465 SetLength(P, 3);
5466 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5468 s := P[1] + ':\' + P[2];
5470 if g_Map_Exist(MapsDir + s) then
5471 begin
5472 gExitByTrigger := False;
5473 if gGameOn then
5474 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5475 gNextMap := s;
5476 gExit := EXIT_ENDLEVELCUSTOM;
5477 end
5478 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5479 g_Game_ChangeMap(s);
5480 end else
5481 if P[2] = '' then
5482 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5483 else
5484 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5485 end else
5486 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5487 end;
5488 end else
5489 begin
5490 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5491 P[1] := addWadExtension(P[1]);
5492 if FileExists(MapsDir + P[1]) then
5493 begin
5494 // Íàøëè WAD ôàéë
5495 P[2] := UpperCase(P[2]);
5496 s := P[1] + ':\' + P[2];
5498 if g_Map_Exist(MapsDir + s) then
5499 begin // Íàøëè êàðòó
5500 gExitByTrigger := False;
5501 if gGameOn then
5502 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5503 gNextMap := s;
5504 gExit := EXIT_ENDLEVELCUSTOM;
5505 end
5506 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5507 g_Game_ChangeMap(s);
5508 end else
5509 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5510 end else
5511 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5512 end;
5513 end else
5514 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5515 end
5516 else if cmd = 'nextmap' then
5517 begin
5518 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5519 g_Console_Add(_lc[I_MSG_NOT_GAME])
5520 else begin
5521 nm := True;
5522 if Length(P) = 1 then
5523 begin
5524 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5525 begin
5526 g_Console_Add(cmd + ' <MAP>');
5527 g_Console_Add(cmd + ' <WAD> [MAP]');
5528 end else begin
5529 nm := False;
5530 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5531 end;
5532 end else
5533 begin
5534 nm := False;
5535 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5536 begin
5537 if Length(P) < 3 then
5538 begin
5539 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5540 s := UpperCase(P[1]);
5541 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5542 begin // Êàðòà íàøëàñü
5543 gExitByTrigger := False;
5544 gNextMap := s;
5545 nm := True;
5546 end else
5547 begin
5548 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5549 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5550 P[1] := addWadExtension(P[1]);
5551 if FileExists(MapsDir + P[1]) then
5552 begin
5553 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5554 SetLength(P, 3);
5555 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5557 s := P[1] + ':\' + P[2];
5559 if g_Map_Exist(MapsDir + s) then
5560 begin // Óñòàíàâëèâàåì êàðòó
5561 gExitByTrigger := False;
5562 gNextMap := s;
5563 nm := True;
5564 end else
5565 if P[2] = '' then
5566 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5567 else
5568 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5569 end else
5570 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5571 end;
5572 end else
5573 begin
5574 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5575 P[1] := addWadExtension(P[1]);
5576 if FileExists(MapsDir + P[1]) then
5577 begin
5578 // Íàøëè WAD ôàéë
5579 P[2] := UpperCase(P[2]);
5580 s := P[1] + ':\' + P[2];
5582 if g_Map_Exist(MapsDir + s) then
5583 begin // Íàøëè êàðòó
5584 gExitByTrigger := False;
5585 gNextMap := s;
5586 nm := True;
5587 end else
5588 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5589 end else
5590 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5591 end;
5592 end else
5593 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5594 end;
5595 if nm then
5596 if gNextMap = '' then
5597 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
5598 else
5599 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
5600 end;
5601 end
5602 else if (cmd = 'endmap') or (cmd = 'goodbye') then
5603 begin
5604 if not gGameOn then
5605 g_Console_Add(_lc[I_MSG_NOT_GAME])
5606 else
5607 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5608 begin
5609 gExitByTrigger := False;
5610 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
5611 if (gNextMap = '') and (gTriggers <> nil) then
5612 for a := 0 to High(gTriggers) do
5613 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5614 begin
5615 gExitByTrigger := True;
5616 gNextMap := gTriggers[a].Data.MapName;
5617 Break;
5618 end;
5619 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
5620 if gNextMap = '' then
5621 gNextMap := g_Game_GetNextMap();
5622 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
5623 if Pos(':\', gNextMap) = 0 then
5624 s := gGameSettings.WAD + ':\' + gNextMap
5625 else
5626 s := gNextMap;
5627 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
5628 if g_Map_Exist(MapsDir + s) then
5629 gExit := EXIT_ENDLEVELCUSTOM
5630 else
5631 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
5632 end else
5633 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5634 end
5635 else if (cmd = 'event') then
5636 begin
5637 if (Length(P) <= 1) then
5638 begin
5639 for a := 0 to High(gEvents) do
5640 if gEvents[a].Command = '' then
5641 g_Console_Add(gEvents[a].Name + ' <none>')
5642 else
5643 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5644 Exit;
5645 end;
5646 if (Length(P) = 2) then
5647 begin
5648 for a := 0 to High(gEvents) do
5649 if gEvents[a].Name = P[1] then
5650 if gEvents[a].Command = '' then
5651 g_Console_Add(gEvents[a].Name + ' <none>')
5652 else
5653 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5654 Exit;
5655 end;
5656 for a := 0 to High(gEvents) do
5657 if gEvents[a].Name = P[1] then
5658 begin
5659 gEvents[a].Command := '';
5660 for b := 2 to High(P) do
5661 if Pos(' ', P[b]) = 0 then
5662 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
5663 else
5664 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
5665 gEvents[a].Command := Trim(gEvents[a].Command);
5666 Exit;
5667 end;
5668 end
5669 // Êîìàíäû Ñâîåé èãðû:
5670 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5671 begin
5672 if cmd = 'bot_addred' then
5673 begin
5674 if Length(P) > 1 then
5675 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
5676 else
5677 g_Bot_Add(TEAM_RED, 2);
5678 end
5679 else if cmd = 'bot_addblue' then
5680 begin
5681 if Length(P) > 1 then
5682 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
5683 else
5684 g_Bot_Add(TEAM_BLUE, 2);
5685 end
5686 else if cmd = 'suicide' then
5687 begin
5688 if gGameOn then
5689 begin
5690 if g_Game_IsClient then
5691 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
5692 else
5693 begin
5694 if gPlayer1 <> nil then
5695 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
5696 if gPlayer2 <> nil then
5697 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
5698 end;
5699 end;
5700 end
5701 else if cmd = 'spectate' then
5702 begin
5703 if not gGameOn then
5704 Exit;
5705 g_Game_Spectate();
5706 end
5707 else if cmd = 'say' then
5708 begin
5709 if g_Game_IsServer and g_Game_IsNet then
5710 begin
5711 if Length(P) > 1 then
5712 begin
5713 chstr := '';
5714 for a := 1 to High(P) do
5715 chstr := chstr + P[a] + ' ';
5717 if Length(chstr) > 200 then SetLength(chstr, 200);
5719 if Length(chstr) < 1 then
5720 begin
5721 g_Console_Add('say <text>');
5722 Exit;
5723 end;
5725 chstr := b_Text_Format(chstr);
5726 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
5727 end
5728 else g_Console_Add('say <text>');
5729 end else
5730 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5731 end
5732 else if cmd = 'tell' then
5733 begin
5734 if g_Game_IsServer and g_Game_IsNet then
5735 begin
5736 if (Length(P) > 2) and (P[1] <> '') then
5737 begin
5738 chstr := '';
5739 for a := 2 to High(P) do
5740 chstr := chstr + P[a] + ' ';
5742 if Length(chstr) > 200 then SetLength(chstr, 200);
5744 if Length(chstr) < 1 then
5745 begin
5746 g_Console_Add('tell <playername> <text>');
5747 Exit;
5748 end;
5750 pl := g_Net_Client_ByName(P[1]);
5751 if pl <> nil then
5752 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
5753 else
5754 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5755 end
5756 else g_Console_Add('tell <playername> <text>');
5757 end else
5758 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5759 end
5760 else if (cmd = 'overtime') and not g_Game_IsClient then
5761 begin
5762 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
5763 Exit;
5764 // Äîïîëíèòåëüíîå âðåìÿ:
5765 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
5767 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5768 [gGameSettings.TimeLimit div 3600,
5769 (gGameSettings.TimeLimit div 60) mod 60,
5770 gGameSettings.TimeLimit mod 60]));
5771 if g_Game_IsNet then MH_SEND_GameSettings;
5772 end
5773 else if (cmd = 'rcon_password') and g_Game_IsClient then
5774 begin
5775 if (Length(P) <= 1) then
5776 g_Console_Add('rcon_password <password>')
5777 else
5778 MC_SEND_RCONPassword(P[1]);
5779 end
5780 else if cmd = 'rcon' then
5781 begin
5782 if g_Game_IsClient then
5783 begin
5784 if Length(P) > 1 then
5785 begin
5786 chstr := '';
5787 for a := 1 to High(P) do
5788 chstr := chstr + P[a] + ' ';
5790 if Length(chstr) > 200 then SetLength(chstr, 200);
5792 if Length(chstr) < 1 then
5793 begin
5794 g_Console_Add('rcon <command>');
5795 Exit;
5796 end;
5798 MC_SEND_RCONCommand(chstr);
5799 end
5800 else g_Console_Add('rcon <command>');
5801 end;
5802 end
5803 else if cmd = 'ready' then
5804 begin
5805 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
5806 gLMSRespawnTime := gTime + 100;
5807 end
5808 else if (cmd = 'callvote') and g_Game_IsNet then
5809 begin
5810 if Length(P) > 1 then
5811 begin
5812 chstr := '';
5813 for a := 1 to High(P) do begin
5814 if a > 1 then chstr := chstr + ' ';
5815 chstr := chstr + P[a];
5816 end;
5818 if Length(chstr) > 200 then SetLength(chstr, 200);
5820 if Length(chstr) < 1 then
5821 begin
5822 g_Console_Add('callvote <command>');
5823 Exit;
5824 end;
5826 if g_Game_IsClient then
5827 MC_SEND_Vote(True, chstr)
5828 else
5829 g_Game_StartVote(chstr, gPlayer1Settings.Name);
5830 g_Console_Process('vote', True);
5831 end
5832 else
5833 g_Console_Add('callvote <command>');
5834 end
5835 else if (cmd = 'vote') and g_Game_IsNet then
5836 begin
5837 if g_Game_IsClient then
5838 MC_SEND_Vote(False)
5839 else if gVoteInProgress then
5840 begin
5841 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
5842 a := Floor((NetClientCount+1)/2.0) + 1
5843 else
5844 a := Floor(NetClientCount/2.0) + 1;
5845 if gVoted then
5846 begin
5847 Dec(gVoteCount);
5848 gVoted := False;
5849 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
5850 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
5851 end
5852 else
5853 begin
5854 Inc(gVoteCount);
5855 gVoted := True;
5856 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
5857 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
5858 g_Game_CheckVote;
5859 end;
5860 end;
5861 end
5862 end;
5863 end;
5865 procedure g_TakeScreenShot();
5866 var
5867 a: Word;
5868 FileName: string;
5869 ssdir, t: string;
5870 st: TStream;
5871 ok: Boolean;
5872 begin
5873 if e_NoGraphics then Exit;
5874 ssdir := GameDir+'/screenshots';
5875 if not findFileCI(ssdir, true) then
5876 begin
5877 // try to create dir
5878 try
5879 CreateDir(ssdir);
5880 except
5881 end;
5882 if not findFileCI(ssdir, true) then exit; // alas
5883 end;
5884 try
5885 for a := 1 to High(Word) do
5886 begin
5887 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
5888 t := FileName;
5889 if findFileCI(t, true) then continue;
5890 if not findFileCI(FileName) then
5891 begin
5892 ok := false;
5893 st := createDiskFile(FileName);
5894 try
5895 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
5896 ok := true;
5897 finally
5898 st.Free();
5899 end;
5900 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
5901 break;
5902 end;
5903 end;
5904 except
5905 end;
5906 end;
5908 procedure g_Game_InGameMenu(Show: Boolean);
5909 begin
5910 if (g_ActiveWindow = nil) and Show then
5911 begin
5912 if gGameSettings.GameType = GT_SINGLE then
5913 g_GUI_ShowWindow('GameSingleMenu')
5914 else
5915 begin
5916 if g_Game_IsClient then
5917 g_GUI_ShowWindow('GameClientMenu')
5918 else
5919 if g_Game_IsNet then
5920 g_GUI_ShowWindow('GameServerMenu')
5921 else
5922 g_GUI_ShowWindow('GameCustomMenu');
5923 end;
5924 g_Sound_PlayEx('MENU_OPEN');
5926 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
5927 if (not g_Game_IsNet) then
5928 g_Game_Pause(True);
5929 end
5930 else
5931 if (g_ActiveWindow <> nil) and (not Show) then
5932 begin
5933 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
5934 if (not g_Game_IsNet) then
5935 g_Game_Pause(False);
5936 end;
5937 end;
5939 procedure g_Game_Pause(Enable: Boolean);
5940 begin
5941 if not gGameOn then
5942 Exit;
5944 if gPause = Enable then
5945 Exit;
5947 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
5948 Exit;
5950 gPause := Enable;
5951 g_Game_PauseAllSounds(Enable);
5952 end;
5954 procedure g_Game_PauseAllSounds(Enable: Boolean);
5955 var
5956 i: Integer;
5957 begin
5958 // Òðèããåðû:
5959 if gTriggers <> nil then
5960 for i := 0 to High(gTriggers) do
5961 with gTriggers[i] do
5962 if (TriggerType = TRIGGER_SOUND) and
5963 (Sound <> nil) and
5964 Sound.IsPlaying() then
5965 begin
5966 Sound.Pause(Enable);
5967 end;
5969 // Çâóêè èãðîêîâ:
5970 if gPlayers <> nil then
5971 for i := 0 to High(gPlayers) do
5972 if gPlayers[i] <> nil then
5973 gPlayers[i].PauseSounds(Enable);
5975 // Ìóçûêà:
5976 if gMusic <> nil then
5977 gMusic.Pause(Enable);
5978 end;
5980 procedure g_Game_StopAllSounds(all: Boolean);
5981 var
5982 i: Integer;
5983 begin
5984 if gTriggers <> nil then
5985 for i := 0 to High(gTriggers) do
5986 with gTriggers[i] do
5987 if (TriggerType = TRIGGER_SOUND) and
5988 (Sound <> nil) then
5989 Sound.Stop();
5991 if gMusic <> nil then
5992 gMusic.Stop();
5994 if all then
5995 e_StopChannels();
5996 end;
5998 procedure g_Game_UpdateTriggerSounds();
5999 var
6000 i: Integer;
6001 begin
6002 if gTriggers <> nil then
6003 for i := 0 to High(gTriggers) do
6004 with gTriggers[i] do
6005 if (TriggerType = TRIGGER_SOUND) and
6006 (Sound <> nil) and
6007 (Data.Local) and
6008 Sound.IsPlaying() then
6009 begin
6010 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6011 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6012 begin
6013 Sound.SetPan(0.5 - Data.Pan/255.0);
6014 Sound.SetVolume(Data.Volume/255.0);
6015 end
6016 else
6017 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6018 end;
6019 end;
6021 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6022 begin
6023 Result := False;
6024 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6025 begin
6026 Result := True;
6027 Exit;
6028 end;
6029 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6030 begin
6031 Result := True;
6032 Exit;
6033 end;
6034 if gSpectMode <> SPECT_PLAYERS then
6035 Exit;
6036 if gSpectPID1 = UID then
6037 begin
6038 Result := True;
6039 Exit;
6040 end;
6041 if gSpectViewTwo and (gSpectPID2 = UID) then
6042 begin
6043 Result := True;
6044 Exit;
6045 end;
6046 end;
6048 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6049 var
6050 Pl: TPlayer;
6051 begin
6052 Result := False;
6053 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6054 begin
6055 Result := True;
6056 Exit;
6057 end;
6058 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6059 begin
6060 Result := True;
6061 Exit;
6062 end;
6063 if gSpectMode <> SPECT_PLAYERS then
6064 Exit;
6065 Pl := g_Player_Get(gSpectPID1);
6066 if (Pl <> nil) and (Pl.Team = Team) then
6067 begin
6068 Result := True;
6069 Exit;
6070 end;
6071 if gSpectViewTwo then
6072 begin
6073 Pl := g_Player_Get(gSpectPID2);
6074 if (Pl <> nil) and (Pl.Team = Team) then
6075 begin
6076 Result := True;
6077 Exit;
6078 end;
6079 end;
6080 end;
6082 procedure g_Game_Message(Msg: string; Time: Word);
6083 begin
6084 MessageText := b_Text_Format(Msg);
6085 MessageTime := Time;
6086 end;
6088 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6089 var
6090 a: Integer;
6091 begin
6092 case gAnnouncer of
6093 ANNOUNCE_NONE:
6094 Exit;
6095 ANNOUNCE_ME,
6096 ANNOUNCE_MEPLUS:
6097 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6098 Exit;
6099 end;
6100 for a := 0 to 3 do
6101 if goodsnd[a].IsPlaying() then
6102 Exit;
6104 goodsnd[Random(4)].Play();
6105 end;
6107 procedure g_Game_Announce_KillCombo(Param: Integer);
6108 var
6109 UID: Word;
6110 c, n: Byte;
6111 Pl: TPlayer;
6112 Name: String;
6113 begin
6114 UID := Param and $FFFF;
6115 c := Param shr 16;
6116 if c < 2 then
6117 Exit;
6119 Pl := g_Player_Get(UID);
6120 if Pl = nil then
6121 Name := '?'
6122 else
6123 Name := Pl.Name;
6125 case c of
6126 2: begin
6127 n := 0;
6128 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6129 end;
6130 3: begin
6131 n := 1;
6132 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6133 end;
6134 4: begin
6135 n := 2;
6136 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6137 end;
6138 else begin
6139 n := 3;
6140 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6141 end;
6142 end;
6144 case gAnnouncer of
6145 ANNOUNCE_NONE:
6146 Exit;
6147 ANNOUNCE_ME:
6148 if not g_Game_IsWatchedPlayer(UID) then
6149 Exit;
6150 ANNOUNCE_MEPLUS:
6151 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6152 Exit;
6153 end;
6155 if killsnd[n].IsPlaying() then
6156 killsnd[n].Stop();
6157 killsnd[n].Play();
6158 end;
6160 procedure g_Game_StartVote(Command, Initiator: string);
6161 var
6162 Need: Integer;
6163 begin
6164 if not gVotesEnabled then Exit;
6165 if gGameSettings.GameType <> GT_SERVER then Exit;
6166 if gVoteInProgress or gVotePassed then
6167 begin
6168 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6169 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6170 Exit;
6171 end;
6172 gVoteInProgress := True;
6173 gVotePassed := False;
6174 gVoteTimer := gTime + gVoteTimeout * 1000;
6175 gVoteCount := 0;
6176 gVoted := False;
6177 gVoteCommand := Command;
6179 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6180 Need := Floor((NetClientCount+1)/2.0)+1
6181 else
6182 Need := Floor(NetClientCount/2.0)+1;
6183 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6184 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6185 end;
6187 procedure g_Game_CheckVote;
6188 var
6189 I, Need: Integer;
6190 begin
6191 if gGameSettings.GameType <> GT_SERVER then Exit;
6192 if not gVoteInProgress then Exit;
6194 if (gTime >= gVoteTimer) then
6195 begin
6196 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6197 Need := Floor((NetClientCount+1)/2.0) + 1
6198 else
6199 Need := Floor(NetClientCount/2.0) + 1;
6200 if gVoteCount >= Need then
6201 begin
6202 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6203 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6204 gVotePassed := True;
6205 gVoteCmdTimer := gTime + 5000;
6206 end
6207 else
6208 begin
6209 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6210 MH_SEND_VoteEvent(NET_VE_FAILED);
6211 end;
6212 if NetClients <> nil then
6213 for I := Low(NetClients) to High(NetClients) do
6214 if NetClients[i].Used then
6215 NetClients[i].Voted := False;
6216 gVoteInProgress := False;
6217 gVoted := False;
6218 gVoteCount := 0;
6219 end
6220 else
6221 begin
6222 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6223 Need := Floor((NetClientCount+1)/2.0) + 1
6224 else
6225 Need := Floor(NetClientCount/2.0) + 1;
6226 if gVoteCount >= Need then
6227 begin
6228 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6229 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6230 gVoteInProgress := False;
6231 gVotePassed := True;
6232 gVoteCmdTimer := gTime + 5000;
6233 gVoted := False;
6234 gVoteCount := 0;
6235 if NetClients <> nil then
6236 for I := Low(NetClients) to High(NetClients) do
6237 if NetClients[i].Used then
6238 NetClients[i].Voted := False;
6239 end;
6240 end;
6241 end;
6243 procedure g_Game_LoadMapList(FileName: string);
6244 var
6245 ListFile: TextFile;
6246 s: string;
6247 begin
6248 MapList := nil;
6249 MapIndex := -1;
6251 if not FileExists(FileName) then Exit;
6253 AssignFile(ListFile, FileName);
6254 Reset(ListFile);
6255 while not EOF(ListFile) do
6256 begin
6257 ReadLn(ListFile, s);
6259 s := Trim(s);
6260 if s = '' then Continue;
6262 SetLength(MapList, Length(MapList)+1);
6263 MapList[High(MapList)] := s;
6264 end;
6265 CloseFile(ListFile);
6266 end;
6268 procedure g_Game_SetDebugMode();
6269 begin
6270 gDebugMode := True;
6271 // ×èòû (äàæå â ñâîåé èãðå):
6272 gCheats := True;
6273 end;
6275 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6276 var
6277 i: Word;
6278 begin
6279 if Length(LoadingStat.Msgs) = 0 then
6280 Exit;
6282 with LoadingStat do
6283 begin
6284 if not reWrite then
6285 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6286 if NextMsg = Length(Msgs) then
6287 begin // scroll
6288 for i := 0 to High(Msgs)-1 do
6289 Msgs[i] := Msgs[i+1];
6290 end
6291 else
6292 Inc(NextMsg);
6293 end else
6294 if NextMsg = 0 then
6295 Inc(NextMsg);
6297 Msgs[NextMsg-1] := Text;
6298 CurValue := 0;
6299 MaxValue := Max;
6300 ShowCount := 0;
6301 end;
6303 g_ActiveWindow := nil;
6305 ProcessLoading;
6306 end;
6308 procedure g_Game_StepLoading();
6309 begin
6310 with LoadingStat do
6311 begin
6312 Inc(CurValue);
6313 Inc(ShowCount);
6314 if (ShowCount > LOADING_SHOW_STEP) then
6315 begin
6316 ShowCount := 0;
6317 ProcessLoading;
6318 end;
6319 end;
6320 end;
6322 procedure g_Game_ClearLoading();
6323 var
6324 len: Word;
6325 begin
6326 with LoadingStat do
6327 begin
6328 CurValue := 0;
6329 MaxValue := 0;
6330 ShowCount := 0;
6331 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6332 if len < 1 then len := 1;
6333 SetLength(Msgs, len);
6334 for len := Low(Msgs) to High(Msgs) do
6335 Msgs[len] := '';
6336 NextMsg := 0;
6337 end;
6338 end;
6340 procedure Parse_Params(var pars: TParamStrValues);
6341 var
6342 i: Integer;
6343 s: String;
6344 begin
6345 SetLength(pars, 0);
6346 i := 1;
6347 while i <= ParamCount do
6348 begin
6349 s := ParamStr(i);
6350 if (s[1] = '-') and (Length(s) > 1) then
6351 begin
6352 if (s[2] = '-') and (Length(s) > 2) then
6353 begin // Îäèíî÷íûé ïàðàìåòð
6354 SetLength(pars, Length(pars) + 1);
6355 with pars[High(pars)] do
6356 begin
6357 Name := LowerCase(s);
6358 Value := '+';
6359 end;
6360 end
6361 else
6362 if (i < ParamCount) then
6363 begin // Ïàðàìåòð ñî çíà÷åíèåì
6364 Inc(i);
6365 SetLength(pars, Length(pars) + 1);
6366 with pars[High(pars)] do
6367 begin
6368 Name := LowerCase(s);
6369 Value := LowerCase(ParamStr(i));
6370 end;
6371 end;
6372 end;
6374 Inc(i);
6375 end;
6376 end;
6378 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6379 var
6380 i: Integer;
6381 begin
6382 Result := '';
6383 for i := 0 to High(pars) do
6384 if pars[i].Name = aName then
6385 begin
6386 Result := pars[i].Value;
6387 Break;
6388 end;
6389 end;
6391 procedure g_Game_Process_Params();
6392 var
6393 pars: TParamStrValues;
6394 map: String;
6395 GMode, n: Byte;
6396 LimT, LimS: Integer;
6397 Opt: LongWord;
6398 Lives: Integer;
6399 s: String;
6400 Port: Integer;
6401 ip: String;
6402 F: TextFile;
6403 begin
6404 Parse_Params(pars);
6406 // Debug mode:
6407 s := Find_Param_Value(pars, '--debug');
6408 if (s <> '') then
6409 g_Game_SetDebugMode();
6411 // Connect when game loads
6412 ip := Find_Param_Value(pars, '-connect');
6414 if ip <> '' then
6415 begin
6416 s := Find_Param_Value(pars, '-port');
6417 if (s = '') or not TryStrToInt(s, Port) then
6418 Port := 25666;
6420 s := Find_Param_Value(pars, '-pw');
6422 g_Game_StartClient(ip, Port, s);
6423 Exit;
6424 end;
6426 // Start map when game loads:
6427 map := LowerCase(Find_Param_Value(pars, '-map'));
6428 if isWadPath(map) then
6429 begin
6430 // Game mode:
6431 s := Find_Param_Value(pars, '-gm');
6432 GMode := g_Game_TextToMode(s);
6433 if GMode = GM_NONE then GMode := GM_DM;
6434 if GMode = GM_SINGLE then GMode := GM_COOP;
6436 // Time limit:
6437 s := Find_Param_Value(pars, '-limt');
6438 if (s = '') or (not TryStrToInt(s, LimT)) then
6439 LimT := 0;
6440 if LimT < 0 then
6441 LimT := 0;
6443 // Goal limit:
6444 s := Find_Param_Value(pars, '-lims');
6445 if (s = '') or (not TryStrToInt(s, LimS)) then
6446 LimS := 0;
6447 if LimS < 0 then
6448 LimS := 0;
6450 // Lives limit:
6451 s := Find_Param_Value(pars, '-lives');
6452 if (s = '') or (not TryStrToInt(s, Lives)) then
6453 Lives := 0;
6454 if Lives < 0 then
6455 Lives := 0;
6457 // Options:
6458 s := Find_Param_Value(pars, '-opt');
6459 if (s = '') then
6460 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6461 else
6462 Opt := StrToIntDef(s, 0);
6463 if Opt = 0 then
6464 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6466 // Close after map:
6467 s := Find_Param_Value(pars, '--close');
6468 if (s <> '') then
6469 gMapOnce := True;
6471 // Delete test map after play:
6472 s := Find_Param_Value(pars, '--testdelete');
6473 if (s <> '') then
6474 begin
6475 gMapToDelete := MapsDir + map;
6476 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6477 Halt(1);
6478 end;
6480 // Delete temporary WAD after play:
6481 s := Find_Param_Value(pars, '--tempdelete');
6482 if (s <> '') then
6483 begin
6484 gMapToDelete := MapsDir + map;
6485 gTempDelete := True;
6486 end;
6488 // Number of players:
6489 s := Find_Param_Value(pars, '-pl');
6490 if (s = '') then
6491 n := 1
6492 else
6493 n := StrToIntDef(s, 1);
6495 // Start:
6496 s := Find_Param_Value(pars, '-port');
6497 if (s = '') or not TryStrToInt(s, Port) then
6498 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6499 else
6500 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6501 end;
6503 // Execute script when game loads:
6504 s := Find_Param_Value(pars, '-exec');
6505 if s <> '' then
6506 begin
6507 if Pos(':\', s) = 0 then
6508 s := GameDir + '/' + s;
6510 {$I-}
6511 AssignFile(F, s);
6512 Reset(F);
6513 if IOResult <> 0 then
6514 begin
6515 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6516 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6517 CloseFile(F);
6518 Exit;
6519 end;
6520 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6521 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6523 while not EOF(F) do
6524 begin
6525 ReadLn(F, s);
6526 if IOResult <> 0 then
6527 begin
6528 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6529 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6530 CloseFile(F);
6531 Exit;
6532 end;
6533 if Pos('#', s) <> 1 then // script comment
6534 g_Console_Process(s, True);
6535 end;
6537 CloseFile(F);
6538 {$I+}
6539 end;
6541 SetLength(pars, 0);
6542 end;
6544 end.