DEADSOFTWARE

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