DEADSOFTWARE

profiler
[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 {$INCLUDE ../shared/a_modes.inc}
17 unit g_game;
19 interface
21 uses
22 g_basic, g_player, e_graphics, Classes, g_res_downloader,
23 SysUtils, g_sound, g_gui, MAPSTRUCT, wadreader, md5, xprofiler;
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 ProfilerCommands(P: SArray);
128 procedure g_Game_Process_Params;
129 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
130 procedure g_Game_StepLoading();
131 procedure g_Game_ClearLoading();
132 procedure g_Game_SetDebugMode();
133 procedure DrawLoadingStat();
135 { procedure SetWinPause(Enable: Boolean); }
137 const
138 GAME_TICK = 28;
140 LOADING_SHOW_STEP = 100;
141 LOADING_INTERLINE = 20;
143 GT_NONE = 0;
144 GT_SINGLE = 1;
145 GT_CUSTOM = 2;
146 GT_SERVER = 3;
147 GT_CLIENT = 4;
149 GM_NONE = 0;
150 GM_DM = 1;
151 GM_TDM = 2;
152 GM_CTF = 3;
153 GM_COOP = 4;
154 GM_SINGLE = 5;
156 MESSAGE_DIKEY = WM_USER + 1;
158 EXIT_QUIT = 1;
159 EXIT_SIMPLE = 2;
160 EXIT_RESTART = 3;
161 EXIT_ENDLEVELSINGLE = 4;
162 EXIT_ENDLEVELCUSTOM = 5;
164 GAME_OPTION_RESERVED = 1;
165 GAME_OPTION_TEAMDAMAGE = 2;
166 GAME_OPTION_ALLOWEXIT = 4;
167 GAME_OPTION_WEAPONSTAY = 8;
168 GAME_OPTION_MONSTERS = 16;
169 GAME_OPTION_BOTVSPLAYER = 32;
170 GAME_OPTION_BOTVSMONSTER = 64;
172 STATE_NONE = 0;
173 STATE_MENU = 1;
174 STATE_FOLD = 2;
175 STATE_INTERCUSTOM = 3;
176 STATE_INTERSINGLE = 4;
177 STATE_INTERTEXT = 5;
178 STATE_INTERPIC = 6;
179 STATE_ENDPIC = 7;
180 STATE_SLIST = 8;
182 LMS_RESPAWN_NONE = 0;
183 LMS_RESPAWN_WARMUP = 1;
184 LMS_RESPAWN_FINAL = 2;
186 SPECT_NONE = 0;
187 SPECT_STATS = 1;
188 SPECT_MAPVIEW = 2;
189 SPECT_PLAYERS = 3;
191 DE_GLOBEVENT = 0;
192 DE_BFGHIT = 1;
193 DE_KILLCOMBO = 2;
195 ANNOUNCE_NONE = 0;
196 ANNOUNCE_ME = 1;
197 ANNOUNCE_MEPLUS = 2;
198 ANNOUNCE_ALL = 3;
200 CONFIG_FILENAME = 'Doom2DF.cfg';
201 LOG_FILENAME = 'Doom2DF.log';
203 TEST_MAP_NAME = '$$$_TEST_$$$';
205 STD_PLAYER_MODEL = 'Doomer';
207 var
208 gStdFont: DWORD;
209 gGameSettings: TGameSettings;
210 gPlayer1Settings: TPlayerSettings;
211 gPlayer2Settings: TPlayerSettings;
212 gGameOn: Boolean;
213 gPlayerScreenSize: TPoint;
214 gPlayer1ScreenCoord: TPoint;
215 gPlayer2ScreenCoord: TPoint;
216 gPlayer1: TPlayer = nil;
217 gPlayer2: TPlayer = nil;
218 gPlayerDrawn: TPlayer = nil;
219 gTime: LongWord;
220 gSwitchGameMode: Byte = GM_DM;
221 gHearPoint1, gHearPoint2: THearPoint;
222 gSoundEffectsDF: Boolean = False;
223 gSoundTriggerTime: Word = 0;
224 gAnnouncer: Byte = ANNOUNCE_NONE;
225 goodsnd: array[0..3] of TPlayableSound;
226 killsnd: array[0..3] of TPlayableSound;
227 gDefInterTime: ShortInt = -1;
228 gInterEndTime: LongWord = 0;
229 gInterTime: LongWord = 0;
230 gServInterTime: Byte = 0;
231 gGameStartTime: LongWord = 0;
232 gTotalMonsters: Integer = 0;
233 gPause: Boolean;
234 gShowTime: Boolean = True;
235 gShowFPS: Boolean = False;
236 gShowGoals: Boolean = True;
237 gShowStat: Boolean = True;
238 gShowKillMsg: Boolean = True;
239 gShowLives: Boolean = True;
240 gShowPing: Boolean = False;
241 gShowMap: Boolean = False;
242 gExit: Byte = 0;
243 gState: Byte = STATE_NONE;
244 sX, sY: Integer;
245 sWidth, sHeight: Word;
246 gSpectMode: Byte = SPECT_NONE;
247 gSpectHUD: Boolean = True;
248 gSpectKeyPress: Boolean = False;
249 gSpectX: Integer = 0;
250 gSpectY: Integer = 0;
251 gSpectStep: Byte = 8;
252 gSpectViewTwo: Boolean = False;
253 gSpectPID1: Integer = -1;
254 gSpectPID2: Integer = -1;
255 gMusic: TMusic = nil;
256 gLoadGameMode: Boolean;
257 gCheats: Boolean = False;
258 gMapOnce: Boolean = False;
259 gMapToDelete: String;
260 gTempDelete: Boolean = False;
261 gLastMap: Boolean = False;
262 gWinPosX, gWinPosY: Integer;
263 gWinSizeX, gWinSizeY: Integer;
264 gWinFrameX, gWinFrameY, gWinCaption: Integer;
265 gWinActive: Boolean = True; // by default window is active, lol
266 gResolutionChange: Boolean = False;
267 gRC_Width, gRC_Height: Word;
268 gRC_FullScreen, gRC_Maximized: Boolean;
269 gLanguageChange: Boolean = False;
270 gDebugMode: Boolean = False;
271 g_debug_Sounds: Boolean = False;
272 g_debug_Frames: Boolean = False;
273 g_debug_WinMsgs: Boolean = False;
274 g_debug_MonsterOff: Boolean = False;
275 g_debug_BotAIOff: Byte = 0;
276 g_debug_HealthBar: Boolean = False;
277 g_Debug_Player: Boolean = False;
278 gCoopMonstersKilled: Word = 0;
279 gCoopSecretsFound: Word = 0;
280 gCoopTotalMonstersKilled: Word = 0;
281 gCoopTotalSecretsFound: Word = 0;
282 gCoopTotalMonsters: Word = 0;
283 gCoopTotalSecrets: Word = 0;
284 gStatsOff: Boolean = False;
285 gStatsPressed: Boolean = False;
286 gExitByTrigger: Boolean = False;
287 gNextMap: String = '';
288 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
289 gLMSRespawnTime: Cardinal = 0;
290 gLMSSoftSpawn: Boolean = False;
291 gMissionFailed: Boolean = False;
292 gVoteInProgress: Boolean = False;
293 gVotePassed: Boolean = False;
294 gVoteCommand: string = '';
295 gVoteTimer: Cardinal = 0;
296 gVoteCmdTimer: Cardinal = 0;
297 gVoteCount: Integer = 0;
298 gVoteTimeout: Cardinal = 30;
299 gVoted: Boolean = False;
300 gVotesEnabled: Boolean = True;
301 gEvents: Array of TGameEvent;
302 gDelayedEvents: Array of TDelayedEvent;
304 // move button values:
305 // bits 0-1: l/r state:
306 // 0: neither left, nor right pressed
307 // 1: left pressed
308 // 2: right pressed
309 // bits 4-5: l/r state when strafe was pressed
310 P1MoveButton: Byte = 0;
311 P2MoveButton: Byte = 0;
313 g_profile_frame_update: Boolean = false;
314 g_profile_frame_draw: Boolean = true;
316 procedure g_ResetDynlights ();
317 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
318 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
320 implementation
322 uses
323 g_textures, g_main, g_window, g_menu,
324 e_input, e_log, g_console, g_items, g_map,
325 g_playermodel, g_gfx, g_options, g_weapons, Math,
326 g_triggers, MAPDEF, g_monsters, e_sound, CONFIG,
327 BinEditor, g_language, g_net, SDL,
328 ENet, e_fixedbuffer, g_netmsg, g_netmaster, GL, GLExt,
329 utils, sfs;
332 // ////////////////////////////////////////////////////////////////////////// //
333 type
334 TDynLight = record
335 x, y, radius: Integer;
336 r, g, b, a: Single;
337 exploCount: Integer;
338 exploRadius: Integer;
339 end;
341 var
342 g_dynLights: array of TDynLight = nil;
343 g_dynLightCount: Integer = 0;
344 g_playerLight: Boolean = false;
346 procedure g_ResetDynlights ();
347 var
348 lnum, idx: Integer;
349 begin
350 if not gwin_has_stencil then begin g_dynLightCount := 0; exit; end;
351 lnum := 0;
352 for idx := 0 to g_dynLightCount-1 do
353 begin
354 if g_dynLights[idx].exploCount = -666 then
355 begin
356 // skip it
357 end
358 else
359 begin
360 // explosion
361 Inc(g_dynLights[idx].exploCount);
362 if (g_dynLights[idx].exploCount < 10) then
363 begin
364 g_dynLights[idx].radius := g_dynLights[idx].exploRadius+g_dynLights[idx].exploCount*8;
365 g_dynLights[idx].a := 0.4+g_dynLights[idx].exploCount/10;
366 if (g_dynLights[idx].a > 0.8) then g_dynLights[idx].a := 0.8;
367 if lnum <> idx then g_dynLights[lnum] := g_dynLights[idx];
368 Inc(lnum);
369 end;
370 end;
371 end;
372 g_dynLightCount := lnum;
373 end;
375 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
376 begin
377 if not gwin_has_stencil then exit;
378 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
379 g_dynLights[g_dynLightCount].x := x;
380 g_dynLights[g_dynLightCount].y := y;
381 g_dynLights[g_dynLightCount].radius := radius;
382 g_dynLights[g_dynLightCount].r := r;
383 g_dynLights[g_dynLightCount].g := g;
384 g_dynLights[g_dynLightCount].b := b;
385 g_dynLights[g_dynLightCount].a := a;
386 g_dynLights[g_dynLightCount].exploCount := -666;
387 Inc(g_dynLightCount);
388 end;
390 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
391 begin
392 if not gwin_has_stencil then exit;
393 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
394 g_dynLights[g_dynLightCount].x := x;
395 g_dynLights[g_dynLightCount].y := y;
396 g_dynLights[g_dynLightCount].radius := 0;
397 g_dynLights[g_dynLightCount].exploRadius := radius;
398 g_dynLights[g_dynLightCount].r := r;
399 g_dynLights[g_dynLightCount].g := g;
400 g_dynLights[g_dynLightCount].b := b;
401 g_dynLights[g_dynLightCount].a := 0;
402 g_dynLights[g_dynLightCount].exploCount := 0;
403 Inc(g_dynLightCount);
404 end;
407 // ////////////////////////////////////////////////////////////////////////// //
408 procedure drawProfiles (x, y: Integer; title: AnsiString);
409 var
410 wdt, hgt: Integer;
411 yy: Integer;
413 procedure drawItems ();
414 begin
415 repeat
416 e_TextureFontPrintEx(x+2+4*xprofItDepth, yy, Format('%s: %d', [xprofItName, xprofItMicro]), gStdFont, 255, 255, 0, 1, false);
417 Inc(yy, 16+2);
418 if xprofItHasChildren then
419 begin
420 xprofItDive();
421 drawItems();
422 xprofItPop();
423 end;
424 until xprofItNext();
425 end;
427 begin
428 // gScreenWidth
429 if not xprofItReset() then exit;
430 wdt := 256;
431 hgt := 16+2+xprofTotalCount*(16+2); // title, items
432 // background
433 e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
434 // title
435 e_TextureFontPrintEx(x+2, y+2, Format('%s: %d', [title, Integer(xprofTotalMicro)]), gStdFont, 255, 255, 0, 1, false);
436 yy := y+16+2;
437 //drawItems();
438 end;
441 // ////////////////////////////////////////////////////////////////////////// //
442 type
443 TEndCustomGameStat = record
444 PlayerStat: TPlayerStatArray;
445 TeamStat: TTeamStat;
446 GameTime: LongWord;
447 GameMode: Byte;
448 Map, MapName: String;
449 end;
451 TEndSingleGameStat = record
452 PlayerStat: Array [0..1] of record
453 Kills: Integer;
454 Secrets: Integer;
455 end;
456 GameTime: LongWord;
457 TwoPlayers: Boolean;
458 TotalSecrets: Integer;
459 end;
461 TLoadingStat = record
462 CurValue: Integer;
463 MaxValue: Integer;
464 ShowCount: Integer;
465 Msgs: Array of String;
466 NextMsg: Word;
467 end;
469 TParamStrValue = record
470 Name: String;
471 Value: String;
472 end;
474 TParamStrValues = Array of TParamStrValue;
476 const
477 INTER_ACTION_TEXT = 1;
478 INTER_ACTION_PIC = 2;
479 INTER_ACTION_MUSIC = 3;
481 var
482 FPS, UPS: Word;
483 FPSCounter, UPSCounter: Word;
484 FPSTime, UPSTime: LongWord;
485 DataLoaded: Boolean = False;
486 LastScreenShot: Int64;
487 IsDrawStat: Boolean = False;
488 CustomStat: TEndCustomGameStat;
489 SingleStat: TEndSingleGameStat;
490 LoadingStat: TLoadingStat;
491 EndingGameCounter: Byte = 0;
492 MessageText: String;
493 MessageTime: Word;
494 MapList: SArray = nil;
495 MapIndex: Integer = -1;
496 MegaWAD: record
497 info: TMegaWADInfo;
498 endpic: String;
499 endmus: String;
500 res: record
501 text: Array of ShortString;
502 anim: Array of ShortString;
503 pic: Array of ShortString;
504 mus: Array of ShortString;
505 end;
506 triggers: Array of record
507 event: ShortString;
508 actions: Array of record
509 action, p1, p2: Integer;
510 end;
511 end;
512 cur_trigger: Integer;
513 cur_action: Integer;
514 end;
515 //InterPic: String;
516 InterText: record
517 lines: SArray;
518 img: String;
519 cur_line: Integer;
520 cur_char: Integer;
521 counter: Integer;
522 endtext: Boolean;
523 end;
525 function Compare(a, b: TPlayerStat): Integer;
526 begin
527 if a.Spectator then Result := 1
528 else if b.Spectator then Result := -1
529 else if a.Frags < b.Frags then Result := 1
530 else if a.Frags > b.Frags then Result := -1
531 else if a.Deaths < b.Deaths then Result := -1
532 else if a.Deaths > b.Deaths then Result := 1
533 else if a.Kills < b.Kills then Result := -1
534 else Result := 1;
535 end;
537 procedure SortGameStat(var stat: TPlayerStatArray);
538 var
539 I, J: Integer;
540 T: TPlayerStat;
541 begin
542 if stat = nil then Exit;
544 for I := High(stat) downto Low(stat) do
545 for J := Low(stat) to High(stat) - 1 do
546 if Compare(stat[J], stat[J + 1]) = 1 then
547 begin
548 T := stat[J];
549 stat[J] := stat[J + 1];
550 stat[J + 1] := T;
551 end;
552 end;
554 function g_Game_ModeToText(Mode: Byte): string;
555 begin
556 Result := '';
557 case Mode of
558 GM_DM: Result := _lc[I_MENU_GAME_TYPE_DM];
559 GM_TDM: Result := _lc[I_MENU_GAME_TYPE_TDM];
560 GM_CTF: Result := _lc[I_MENU_GAME_TYPE_CTF];
561 GM_COOP: Result := _lc[I_MENU_GAME_TYPE_COOP];
562 GM_SINGLE: Result := _lc[I_MENU_GAME_TYPE_SINGLE];
563 end;
564 end;
566 function g_Game_TextToMode(Mode: string): Byte;
567 begin
568 Result := GM_NONE;
569 Mode := UpperCase(Mode);
570 if Mode = _lc[I_MENU_GAME_TYPE_DM] then
571 begin
572 Result := GM_DM;
573 Exit;
574 end;
575 if Mode = _lc[I_MENU_GAME_TYPE_TDM] then
576 begin
577 Result := GM_TDM;
578 Exit;
579 end;
580 if Mode = _lc[I_MENU_GAME_TYPE_CTF] then
581 begin
582 Result := GM_CTF;
583 Exit;
584 end;
585 if Mode = _lc[I_MENU_GAME_TYPE_COOP] then
586 begin
587 Result := GM_COOP;
588 Exit;
589 end;
590 if Mode = _lc[I_MENU_GAME_TYPE_SINGLE] then
591 begin
592 Result := GM_SINGLE;
593 Exit;
594 end;
595 end;
597 function g_Game_IsNet(): Boolean;
598 begin
599 Result := (gGameSettings.GameType in [GT_SERVER, GT_CLIENT]);
600 end;
602 function g_Game_IsServer(): Boolean;
603 begin
604 Result := (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM, GT_SERVER]);
605 end;
607 function g_Game_IsClient(): Boolean;
608 begin
609 Result := (gGameSettings.GameType = GT_CLIENT);
610 end;
612 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
613 var
614 w: TWADFile;
615 cfg: TConfig;
616 p: Pointer;
617 len: Integer;
618 begin
619 Result.name := ExtractFileName(WAD);
620 Result.description := '';
621 Result.author := '';
623 w := TWADFile.Create();
624 w.ReadFile(WAD);
626 if not w.GetResource('INTERSCRIPT', p, len) then
627 begin
628 w.Free();
629 Exit;
630 end;
632 cfg := TConfig.CreateMem(p, len);
633 Result.name := cfg.ReadStr('megawad', 'name', ExtractFileName(WAD));
634 Result.description := cfg.ReadStr('megawad', 'description', '');
635 Result.author := cfg.ReadStr('megawad', 'author', '');
636 Result.pic := cfg.ReadStr('megawad', 'pic', '');
637 cfg.Free();
639 FreeMem(p);
640 end;
642 procedure g_Game_FreeWAD();
643 var
644 a: Integer;
645 begin
646 for a := 0 to High(MegaWAD.res.pic) do
647 if MegaWAD.res.pic[a] <> '' then
648 g_Texture_Delete(MegaWAD.res.pic[a]);
650 for a := 0 to High(MegaWAD.res.mus) do
651 if MegaWAD.res.mus[a] <> '' then
652 g_Sound_Delete(MegaWAD.res.mus[a]);
654 MegaWAD.res.pic := nil;
655 MegaWAD.res.text := nil;
656 MegaWAD.res.anim := nil;
657 MegaWAD.res.mus := nil;
658 MegaWAD.triggers := nil;
660 g_Texture_Delete('TEXTURE_endpic');
661 g_Sound_Delete('MUSIC_endmus');
663 ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
664 gGameSettings.WAD := '';
665 end;
667 procedure g_Game_LoadWAD(WAD: string);
668 var
669 w: TWADFile;
670 cfg: TConfig;
671 p: Pointer;
672 {b, }len: Integer;
673 s: string;
674 begin
675 g_Game_FreeWAD();
676 gGameSettings.WAD := WAD;
677 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
678 Exit;
680 MegaWAD.info := g_Game_GetMegaWADInfo(MapsDir + WAD);
682 w := TWADFile.Create();
683 w.ReadFile(MapsDir + WAD);
685 if not w.GetResource('INTERSCRIPT', p, len) then
686 begin
687 w.Free();
688 Exit;
689 end;
691 cfg := TConfig.CreateMem(p, len);
693 {b := 1;
694 while True do
695 begin
696 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
697 if s = '' then Break;
698 b := b+1;
700 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
701 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
703 g_Texture_CreateWADEx(s, s);
704 end;
706 b := 1;
707 while True do
708 begin
709 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
710 if s = '' then Break;
711 b := b+1;
713 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
714 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
716 g_Music_CreateWADEx(s, s);
717 end;}
719 MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
720 if MegaWAD.endpic <> '' then
721 begin
722 s := g_ExtractWadName(MegaWAD.endpic);
723 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
724 g_Texture_CreateWADEx('TEXTURE_endpic', s+MegaWAD.endpic);
725 end;
726 MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
727 if MegaWAD.endmus <> '' then
728 begin
729 s := g_ExtractWadName(MegaWAD.endmus);
730 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
731 g_Sound_CreateWADEx('MUSIC_endmus', s+MegaWAD.endmus, True);
732 end;
734 cfg.Free();
735 FreeMem(p);
736 w.Free();
737 end;
739 {procedure start_trigger(t: string);
740 begin
741 end;
743 function next_trigger(): Boolean;
744 begin
745 end;}
747 procedure DisableCheats();
748 begin
749 MAX_RUNVEL := 8;
750 VEL_JUMP := 10;
751 gFly := False;
753 if gPlayer1 <> nil then gPlayer1.GodMode := False;
754 if gPlayer2 <> nil then gPlayer2.GodMode := False;
755 if gPlayer1 <> nil then gPlayer1.NoTarget := False;
756 if gPlayer2 <> nil then gPlayer2.NoTarget := False;
757 end;
759 procedure g_Game_ExecuteEvent(Name: String);
760 var
761 a: Integer;
762 begin
763 if Name = '' then
764 Exit;
765 if gEvents = nil then
766 Exit;
767 for a := 0 to High(gEvents) do
768 if gEvents[a].Name = Name then
769 begin
770 if gEvents[a].Command <> '' then
771 g_Console_Process(gEvents[a].Command, True);
772 break;
773 end;
774 end;
776 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
777 var
778 a, n: Integer;
779 begin
780 n := -1;
781 if gDelayedEvents <> nil then
782 for a := 0 to High(gDelayedEvents) do
783 if not gDelayedEvents[a].Pending then
784 begin
785 n := a;
786 break;
787 end;
788 if n = -1 then
789 begin
790 SetLength(gDelayedEvents, Length(gDelayedEvents) + 1);
791 n := High(gDelayedEvents);
792 end;
793 gDelayedEvents[n].Pending := True;
794 gDelayedEvents[n].DEType := DEType;
795 gDelayedEvents[n].DENum := Num;
796 gDelayedEvents[n].DEStr := Str;
797 if DEType = DE_GLOBEVENT then
798 gDelayedEvents[n].Time := (GetTimer() {div 1000}) + Time
799 else
800 gDelayedEvents[n].Time := gTime + Time;
801 Result := n;
802 end;
804 procedure EndGame();
805 var
806 a: Integer;
807 FileName: string;
808 begin
809 if g_Game_IsNet and g_Game_IsServer then
810 MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed));
812 // Ñòîï èãðà:
813 gPause := False;
814 gGameOn := False;
816 g_Game_StopAllSounds(False);
818 MessageTime := 0;
819 MessageText := '';
821 EndingGameCounter := 0;
822 g_ActiveWindow := nil;
824 gLMSRespawn := LMS_RESPAWN_NONE;
825 gLMSRespawnTime := 0;
827 case gExit of
828 EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
829 begin
830 g_Game_Free();
832 if gMapOnce then
833 begin // Ýòî áûë òåñò
834 g_Game_Quit();
835 end
836 else
837 begin // Âûõîä â ãëàâíîå ìåíþ
838 gMusic.SetByName('MUSIC_MENU');
839 gMusic.Play();
840 if gState <> STATE_SLIST then
841 begin
842 g_GUI_ShowWindow('MainMenu');
843 gState := STATE_MENU;
844 end else
845 begin
846 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
847 slReturnPressed := True;
848 if g_Net_Slist_Fetch(slCurrent) then
849 begin
850 if slCurrent = nil then
851 slWaitStr := _lc[I_NET_SLIST_NOSERVERS];
852 end
853 else
854 slWaitStr := _lc[I_NET_SLIST_ERROR];
855 end;
857 g_Game_ExecuteEvent('ongameend');
858 end;
859 end;
861 EXIT_RESTART: // Íà÷àòü óðîâåíü ñíà÷àëà
862 begin
863 if not g_Game_IsClient then g_Game_Restart();
864 end;
866 EXIT_ENDLEVELCUSTOM: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
867 begin
868 // Ñòàòèñòèêà Ñâîåé èãðû:
869 FileName := g_ExtractWadName(gMapInfo.Map);
871 CustomStat.GameTime := gTime;
872 CustomStat.Map := ExtractFileName(FileName)+':'+g_ExtractFileName(gMapInfo.Map); //ResName;
873 CustomStat.MapName := gMapInfo.Name;
874 CustomStat.GameMode := gGameSettings.GameMode;
875 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
876 CustomStat.TeamStat := gTeamStat;
878 CustomStat.PlayerStat := nil;
880 // Ñòàòèñòèêà èãðîêîâ:
881 if gPlayers <> nil then
882 begin
883 for a := 0 to High(gPlayers) do
884 if gPlayers[a] <> nil then
885 begin
886 SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
887 with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
888 begin
889 Name := gPlayers[a].Name;
890 Frags := gPlayers[a].Frags;
891 Deaths := gPlayers[a].Death;
892 Kills := gPlayers[a].Kills;
893 Team := gPlayers[a].Team;
894 Color := gPlayers[a].Model.Color;
895 Spectator := gPlayers[a].FSpectator;
896 end;
897 end;
899 SortGameStat(CustomStat.PlayerStat);
900 end;
902 g_Game_ExecuteEvent('onmapend');
904 // Çàòóõàþùèé ýêðàí:
905 EndingGameCounter := 255;
906 gState := STATE_FOLD;
907 gInterTime := 0;
908 if gDefInterTime < 0 then
909 gInterEndTime := IfThen((gGameSettings.GameType = GT_SERVER) and (gPlayer1 = nil), 15000, 25000)
910 else
911 gInterEndTime := gDefInterTime * 1000;
912 end;
914 EXIT_ENDLEVELSINGLE: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
915 begin
916 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
917 SingleStat.GameTime := gTime;
918 SingleStat.TwoPlayers := gPlayer2 <> nil;
919 SingleStat.TotalSecrets := gSecretsCount;
920 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
921 SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills;
922 SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets;
923 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
924 if SingleStat.TwoPlayers then
925 begin
926 SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills;
927 SingleStat.PlayerStat[1].Secrets := gPlayer2.Secrets;
928 end;
930 g_Game_ExecuteEvent('onmapend');
932 // Åñòü åùå êàðòû:
933 if gNextMap <> '' then
934 begin
935 gMusic.SetByName('MUSIC_INTERMUS');
936 gMusic.Play();
937 gState := STATE_INTERSINGLE;
939 g_Game_ExecuteEvent('oninter');
940 end
941 else // Áîëüøå íåò êàðò
942 begin
943 // Çàòóõàþùèé ýêðàí:
944 EndingGameCounter := 255;
945 gState := STATE_FOLD;
946 end;
947 end;
948 end;
950 // Îêîí÷àíèå îáðàáîòàíî:
951 if gExit <> EXIT_QUIT then
952 gExit := 0;
953 end;
955 procedure DrawStat();
956 var
957 pc, x, y, w, h: Integer;
958 w1, w2, w3, w4: Integer;
959 a, aa: Integer;
960 cw, ch, r, g, b, rr, gg, bb: Byte;
961 s1, s2, s3: String;
962 _y: Integer;
963 stat: TPlayerStatArray;
964 wad, map: string;
965 mapstr: string;
966 begin
967 s1 := '';
968 s2 := '';
969 s3 := '';
970 pc := g_Player_GetCount;
971 e_TextureFontGetSize(gStdFont, cw, ch);
973 w := gScreenWidth-(gScreenWidth div 5);
974 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
975 h := 32+ch*(11+pc)
976 else
977 h := 40+ch*5+(ch+8)*pc;
978 x := (gScreenWidth div 2)-(w div 2);
979 y := (gScreenHeight div 2)-(h div 2);
981 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
982 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
984 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
985 map := g_ExtractFileName(gMapInfo.Map);
986 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
988 case gGameSettings.GameMode of
989 GM_DM:
990 begin
991 if gGameSettings.MaxLives = 0 then
992 s1 := _lc[I_GAME_DM]
993 else
994 s1 := _lc[I_GAME_LMS];
995 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
996 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
997 end;
999 GM_TDM:
1000 begin
1001 if gGameSettings.MaxLives = 0 then
1002 s1 := _lc[I_GAME_TDM]
1003 else
1004 s1 := _lc[I_GAME_TLMS];
1005 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
1006 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1007 end;
1009 GM_CTF:
1010 begin
1011 s1 := _lc[I_GAME_CTF];
1012 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]);
1013 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1014 end;
1016 GM_COOP:
1017 begin
1018 if gGameSettings.MaxLives = 0 then
1019 s1 := _lc[I_GAME_COOP]
1020 else
1021 s1 := _lc[I_GAME_SURV];
1022 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
1023 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
1024 end;
1026 else
1027 begin
1028 s1 := '';
1029 s2 := '';
1030 end;
1031 end;
1033 _y := y+8;
1034 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
1035 _y := _y+ch+8;
1036 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
1037 _y := _y+ch+8;
1038 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
1040 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3,
1041 gStdFont, 200, 200, 200, 1);
1043 if NetMode = NET_SERVER then
1044 e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1)
1045 else
1046 if NetMode = NET_CLIENT then
1047 e_TextureFontPrintEx(x+8, y + 8,
1048 NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1);
1050 if pc = 0 then
1051 Exit;
1052 stat := g_Player_GetStats();
1053 SortGameStat(stat);
1055 w2 := (w-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1056 w3 := (w-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1057 w4 := w3;
1058 w1 := w-16-w2-w3-w4; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1060 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1061 begin
1062 _y := _y+ch+ch;
1064 for a := TEAM_RED to TEAM_BLUE do
1065 begin
1066 if a = TEAM_RED then
1067 begin
1068 s1 := _lc[I_GAME_TEAM_RED];
1069 r := 255;
1070 g := 0;
1071 b := 0;
1072 end
1073 else
1074 begin
1075 s1 := _lc[I_GAME_TEAM_BLUE];
1076 r := 0;
1077 g := 0;
1078 b := 255;
1079 end;
1081 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
1082 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals),
1083 gStdFont, r, g, b, 1);
1085 _y := _y+ch+(ch div 4);
1086 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
1087 _y := _y+(ch div 4);
1089 for aa := 0 to High(stat) do
1090 if stat[aa].Team = a then
1091 with stat[aa] do
1092 begin
1093 if Spectator then
1094 begin
1095 rr := r div 2;
1096 gg := g div 2;
1097 bb := b div 2;
1098 end
1099 else
1100 begin
1101 rr := r;
1102 gg := g;
1103 bb := b;
1104 end;
1105 // Èìÿ
1106 e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
1107 // Ïèíã/ïîòåðè
1108 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
1109 // Ôðàãè
1110 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
1111 // Ñìåðòè
1112 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
1113 _y := _y+ch;
1114 end;
1116 _y := _y+ch;
1117 end;
1118 end
1119 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
1120 begin
1121 _y := _y+ch+ch;
1122 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
1123 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
1124 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
1125 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
1127 _y := _y+ch+8;
1128 for aa := 0 to High(stat) do
1129 with stat[aa] do
1130 begin
1131 if Spectator then
1132 begin
1133 r := 127;
1134 g := 64;
1135 end
1136 else
1137 begin
1138 r := 255;
1139 g := 127;
1140 end;
1141 // Öâåò èãðîêà
1142 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
1143 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
1144 // Èìÿ
1145 e_TextureFontPrintEx(x+16+16+8, _y+4, Name, gStdFont, r, g, 0, 1);
1146 // Ïèíã/ïîòåðè
1147 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
1148 // Ôðàãè
1149 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
1150 // Ñìåðòè
1151 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
1152 _y := _y+ch+8;
1153 end;
1154 end
1155 end;
1157 procedure g_Game_Init();
1158 var
1159 SR: TSearchRec;
1160 begin
1161 gExit := 0;
1162 gMapToDelete := '';
1163 gTempDelete := False;
1165 sfsGCDisable(); // temporary disable removing of temporary volumes
1167 try
1168 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
1169 g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
1170 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
1171 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
1173 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
1174 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
1175 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
1177 g_Game_ClearLoading();
1178 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
1179 g_Game_SetLoadingText('', 0, False);
1181 g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
1182 g_Console_Init();
1184 g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
1185 g_PlayerModel_LoadData();
1187 if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then
1188 repeat
1189 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1190 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1191 until FindNext(SR) <> 0;
1192 FindClose(SR);
1194 if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then
1195 repeat
1196 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1197 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1198 until FindNext(SR) <> 0;
1199 FindClose(SR);
1201 if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then
1202 repeat
1203 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1204 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1205 until FindNext(SR) <> 0;
1206 FindClose(SR);
1208 gGameOn := False;
1209 gPause := False;
1210 gTime := 0;
1211 LastScreenShot := 0;
1213 {e_MouseInfo.Accel := 1.0;}
1215 g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
1216 g_Game_LoadData();
1218 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1219 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
1220 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
1221 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True);
1222 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
1224 g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
1225 g_Menu_Init();
1227 gMusic := TMusic.Create();
1228 gMusic.SetByName('MUSIC_MENU');
1229 gMusic.Play();
1231 gGameSettings.WarmupTime := 30;
1233 gState := STATE_MENU;
1235 SetLength(gEvents, 6);
1236 gEvents[0].Name := 'ongamestart';
1237 gEvents[1].Name := 'ongameend';
1238 gEvents[2].Name := 'onmapstart';
1239 gEvents[3].Name := 'onmapend';
1240 gEvents[4].Name := 'oninter';
1241 gEvents[5].Name := 'onwadend';
1242 finally
1243 sfsGCEnable(); // enable releasing unused volumes
1244 end;
1245 end;
1247 procedure g_Game_Free();
1248 begin
1249 if NetMode = NET_CLIENT then g_Net_Disconnect();
1250 if NetMode = NET_SERVER then g_Net_Host_Die();
1252 g_Map_Free();
1253 g_Player_Free();
1254 g_Player_RemoveAllCorpses();
1256 gGameSettings.GameType := GT_NONE;
1257 if gGameSettings.GameMode = GM_SINGLE then
1258 gGameSettings.GameMode := GM_DM;
1259 gSwitchGameMode := gGameSettings.GameMode;
1261 gChatShow := False;
1262 gExitByTrigger := False;
1263 end;
1265 function IsActivePlayer(p: TPlayer): Boolean;
1266 begin
1267 Result := False;
1268 if p = nil then
1269 Exit;
1270 Result := (not p.FDummy) and (not p.FSpectator);
1271 end;
1273 function GetActivePlayer_ByID(ID: Integer): TPlayer;
1274 var
1275 a: Integer;
1276 begin
1277 Result := nil;
1278 if ID < 0 then
1279 Exit;
1280 if gPlayers = nil then
1281 Exit;
1282 for a := Low(gPlayers) to High(gPlayers) do
1283 if IsActivePlayer(gPlayers[a]) then
1284 begin
1285 if gPlayers[a].UID <> ID then
1286 continue;
1287 Result := gPlayers[a];
1288 break;
1289 end;
1290 end;
1292 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1293 var
1294 a, idx: Integer;
1295 ids: Array of Word;
1296 begin
1297 Result := -1;
1298 if gPlayers = nil then
1299 Exit;
1300 SetLength(ids, 0);
1301 idx := -1;
1302 for a := Low(gPlayers) to High(gPlayers) do
1303 if IsActivePlayer(gPlayers[a]) then
1304 begin
1305 SetLength(ids, Length(ids) + 1);
1306 ids[High(ids)] := gPlayers[a].UID;
1307 if gPlayers[a].UID = Skip then
1308 idx := High(ids);
1309 end;
1310 if Length(ids) = 0 then
1311 Exit;
1312 if idx = -1 then
1313 Result := ids[0]
1314 else
1315 Result := ids[(idx + 1) mod Length(ids)];
1316 end;
1318 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1319 var
1320 a, idx: Integer;
1321 ids: Array of Word;
1322 begin
1323 Result := -1;
1324 if gPlayers = nil then
1325 Exit;
1326 SetLength(ids, 0);
1327 idx := -1;
1328 for a := Low(gPlayers) to High(gPlayers) do
1329 if IsActivePlayer(gPlayers[a]) then
1330 begin
1331 SetLength(ids, Length(ids) + 1);
1332 ids[High(ids)] := gPlayers[a].UID;
1333 if gPlayers[a].UID = Skip then
1334 idx := High(ids);
1335 end;
1336 if Length(ids) = 0 then
1337 Exit;
1338 if idx = -1 then
1339 Result := ids[Length(ids) - 1]
1340 else
1341 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1342 end;
1344 function isKeyPressed (key1: Word; key2: Word): Boolean;
1345 begin
1346 if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
1347 if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
1348 result := false;
1349 end;
1351 procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false);
1352 var
1353 time: Word;
1354 strafeDir: Byte;
1355 i: Integer;
1356 begin
1357 if (plr = nil) then exit;
1358 if (p2hack) then time := 1000 else time := 1;
1359 strafeDir := MoveButton shr 4;
1360 MoveButton := MoveButton and $0F;
1361 with ctrl do
1362 begin
1363 if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1364 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1365 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1367 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1368 if MoveButton = 1 then plr.PressKey(KEY_LEFT, time)
1369 else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time);
1371 // if we have "strafe" key, turn off old strafe mechanics
1372 if isKeyPressed(KeyStrafe, KeyStrafe2) then
1373 begin
1374 // new strafe mechanics
1375 if (strafeDir = 0) then strafeDir := MoveButton; // start strafing
1376 // now set direction according to strafe (reversed)
1377 if (strafeDir = 2) then plr.SetDirection(D_LEFT)
1378 else if (strafeDir = 1) then plr.SetDirection(D_RIGHT);
1379 end
1380 else
1381 begin
1382 strafeDir := 0; // not strafing anymore
1383 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1384 if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(D_LEFT)
1385 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1386 else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(D_RIGHT)
1387 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1388 else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1));
1389 end;
1391 // fix movebutton state
1392 MoveButton := MoveButton or (strafeDir shl 4);
1394 // Îñòàëüíûå êëàâèøè:
1395 if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time);
1396 if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time);
1397 if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time);
1398 if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE);
1399 if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON);
1400 if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON);
1401 if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN);
1403 for i := 0 to High(KeyWeapon) do
1404 if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then
1405 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1406 end;
1408 // HACK: add dynlight here
1409 if gwin_k8_enable_light_experiments then
1410 begin
1411 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1412 begin
1413 g_playerLight := true;
1414 end;
1415 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1416 begin
1417 g_playerLight := false;
1418 end;
1419 end;
1421 if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1422 end;
1424 procedure g_Game_Update();
1425 var
1426 Msg: g_gui.TMessage;
1427 Time: Int64;
1428 a: Byte;
1429 w: Word;
1430 i, b: Integer;
1431 begin
1432 g_ResetDynlights();
1433 // Ïîðà âûêëþ÷àòü èãðó:
1434 if gExit = EXIT_QUIT then
1435 Exit;
1436 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1437 if gExit <> 0 then
1438 begin
1439 EndGame();
1440 if gExit = EXIT_QUIT then
1441 Exit;
1442 end;
1444 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1445 e_PollInput();
1447 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1448 g_Console_Update();
1450 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1451 begin
1452 gExit := EXIT_SIMPLE;
1453 EndGame();
1454 Exit;
1455 end;
1457 case gState of
1458 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1459 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1460 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1461 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1462 begin
1463 if g_Game_IsNet and g_Game_IsServer then
1464 begin
1465 gInterTime := gInterTime + GAME_TICK;
1466 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1467 if a <> gServInterTime then
1468 begin
1469 gServInterTime := a;
1470 MH_SEND_TimeSync(gServInterTime);
1471 end;
1472 end;
1474 if (not g_Game_IsClient) and
1477 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1478 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1479 and (g_ActiveWindow = nil)
1481 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1483 then
1484 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1485 g_Game_StopAllSounds(True);
1487 if gMapOnce then // Ýòî áûë òåñò
1488 gExit := EXIT_SIMPLE
1489 else
1490 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1491 g_Game_ChangeMap(gNextMap)
1492 else // Ñëåäóþùåé êàðòû íåò
1493 begin
1494 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1495 begin
1496 // Âûõîä â ãëàâíîå ìåíþ:
1497 g_Game_Free;
1498 g_GUI_ShowWindow('MainMenu');
1499 gMusic.SetByName('MUSIC_MENU');
1500 gMusic.Play();
1501 gState := STATE_MENU;
1502 end else
1503 begin
1504 // Ôèíàëüíàÿ êàðòèíêà:
1505 g_Game_ExecuteEvent('onwadend');
1506 g_Game_Free();
1507 if not gMusic.SetByName('MUSIC_endmus') then
1508 gMusic.SetByName('MUSIC_STDENDMUS');
1509 gMusic.Play();
1510 gState := STATE_ENDPIC;
1511 end;
1512 g_Game_ExecuteEvent('ongameend');
1513 end;
1515 Exit;
1516 end;
1518 if gState = STATE_INTERTEXT then
1519 if InterText.counter > 0 then
1520 InterText.counter := InterText.counter - 1;
1521 end;
1523 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1524 begin
1525 if EndingGameCounter = 0 then
1526 begin
1527 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1528 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1529 begin
1530 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1531 begin
1532 g_Game_ExecuteEvent('onwadend');
1533 if not gMusic.SetByName('MUSIC_endmus') then
1534 gMusic.SetByName('MUSIC_STDENDMUS');
1535 end
1536 else
1537 gMusic.SetByName('MUSIC_ROUNDMUS');
1539 gMusic.Play();
1540 gState := STATE_INTERCUSTOM;
1541 end
1542 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1543 begin
1544 gMusic.SetByName('MUSIC_INTERMUS');
1545 gMusic.Play();
1546 gState := STATE_INTERSINGLE;
1547 end;
1548 g_Game_ExecuteEvent('oninter');
1549 end
1550 else
1551 DecMin(EndingGameCounter, 6, 0);
1552 end;
1554 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1555 begin
1556 if gMapOnce then // Ýòî áûë òåñò
1557 begin
1558 gExit := EXIT_SIMPLE;
1559 Exit;
1560 end;
1561 end;
1563 STATE_SLIST:
1564 g_Serverlist_Control(slCurrent);
1565 end;
1567 if g_Game_IsNet then
1568 if not gConsoleShow then
1569 if not gChatShow then
1570 begin
1571 if g_ActiveWindow = nil then
1572 begin
1573 if e_KeyPressed(gGameControls.GameControls.Chat) then
1574 g_Console_Chat_Switch(False)
1575 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1576 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1577 g_Console_Chat_Switch(True);
1578 end;
1579 end else
1580 if not gChatEnter then
1581 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1582 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1583 gChatEnter := True;
1585 // Ñòàòèñòèêà ïî Tab:
1586 if gGameOn then
1587 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1588 (gGameSettings.GameType <> GT_SINGLE) and
1589 e_KeyPressed(gGameControls.GameControls.Stat);
1591 // Èãðà èäåò:
1592 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1593 begin
1594 // Âðåìÿ += 28 ìèëëèñåêóíä:
1595 gTime := gTime + GAME_TICK;
1597 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1598 if MessageTime = 0 then
1599 MessageText := '';
1600 if MessageTime > 0 then
1601 MessageTime := MessageTime - 1;
1603 if (g_Game_IsServer) then
1604 begin
1605 // Áûë çàäàí ëèìèò âðåìåíè:
1606 if (gGameSettings.TimeLimit > 0) then
1607 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1608 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1609 g_Game_NextLevel();
1610 Exit;
1611 end;
1613 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1614 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1615 g_Game_RestartRound(gLMSSoftSpawn);
1617 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1618 if gVoteInProgress and (gVoteTimer < gTime) then
1619 g_Game_CheckVote
1620 else if gVotePassed and (gVoteCmdTimer < gTime) then
1621 begin
1622 g_Console_Process(gVoteCommand);
1623 gVoteCommand := '';
1624 gVotePassed := False;
1625 end;
1627 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1628 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1629 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1630 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1631 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1633 // Áûë çàäàí ëèìèò ïîáåä:
1634 if (gGameSettings.GoalLimit > 0) then
1635 begin
1636 b := 0;
1638 if gGameSettings.GameMode = GM_DM then
1639 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1640 for i := 0 to High(gPlayers) do
1641 if gPlayers[i] <> nil then
1642 if gPlayers[i].Frags > b then
1643 b := gPlayers[i].Frags;
1644 end
1645 else
1646 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1647 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1648 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1649 end;
1651 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1652 if b >= gGameSettings.GoalLimit then
1653 begin
1654 g_Game_NextLevel();
1655 Exit;
1656 end;
1657 end;
1659 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1660 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1661 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1662 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1663 begin
1664 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1665 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1666 end // if not console
1667 else
1668 begin
1669 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1670 end;
1671 // process weapon switch queue
1672 end; // if server
1674 // Íàáëþäàòåëü
1675 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1676 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1677 begin
1678 if not gSpectKeyPress then
1679 begin
1680 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1681 begin
1682 // switch spect mode
1683 case gSpectMode of
1684 SPECT_NONE: ; // not spectator
1685 SPECT_STATS,
1686 SPECT_MAPVIEW: Inc(gSpectMode);
1687 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1688 end;
1689 gSpectKeyPress := True;
1690 end;
1691 if gSpectMode = SPECT_MAPVIEW then
1692 begin
1693 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1694 gSpectX := Max(gSpectX - gSpectStep, 0);
1695 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1696 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1697 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1698 gSpectY := Max(gSpectY - gSpectStep, 0);
1699 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1700 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1701 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1702 begin
1703 // decrease step
1704 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1705 gSpectKeyPress := True;
1706 end;
1707 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1708 begin
1709 // increase step
1710 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1711 gSpectKeyPress := True;
1712 end;
1713 end;
1714 if gSpectMode = SPECT_PLAYERS then
1715 begin
1716 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1717 begin
1718 // add second view
1719 gSpectViewTwo := True;
1720 gSpectKeyPress := True;
1721 end;
1722 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1723 begin
1724 // remove second view
1725 gSpectViewTwo := False;
1726 gSpectKeyPress := True;
1727 end;
1728 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1729 begin
1730 // prev player (view 1)
1731 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1732 gSpectKeyPress := True;
1733 end;
1734 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1735 begin
1736 // next player (view 1)
1737 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1738 gSpectKeyPress := True;
1739 end;
1740 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1741 begin
1742 // prev player (view 2)
1743 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1744 gSpectKeyPress := True;
1745 end;
1746 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1747 begin
1748 // next player (view 2)
1749 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1750 gSpectKeyPress := True;
1751 end;
1752 end;
1753 end
1754 else
1755 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1756 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1757 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1758 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1759 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1760 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1761 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1762 gSpectKeyPress := False;
1763 end;
1765 // Îáíîâëÿåì âñå îñòàëüíîå:
1766 g_Map_Update();
1767 g_Items_Update();
1768 g_Triggers_Update();
1769 g_Weapon_Update();
1770 g_Monsters_Update();
1771 g_GFX_Update();
1772 g_Player_UpdateAll();
1773 g_Player_UpdatePhysicalObjects();
1774 if gGameSettings.GameType = GT_SERVER then
1775 if Length(gMonstersSpawned) > 0 then
1776 begin
1777 for I := 0 to High(gMonstersSpawned) do
1778 MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1779 SetLength(gMonstersSpawned, 0);
1780 end;
1782 if (gSoundTriggerTime > 8) then
1783 begin
1784 g_Game_UpdateTriggerSounds();
1785 gSoundTriggerTime := 0;
1786 end
1787 else
1788 Inc(gSoundTriggerTime);
1790 if (NetMode = NET_SERVER) then
1791 begin
1792 Inc(NetTimeToUpdate);
1793 Inc(NetTimeToReliable);
1794 if NetTimeToReliable >= NetRelupdRate then
1795 begin
1796 for I := 0 to High(gPlayers) do
1797 if gPlayers[I] <> nil then
1798 MH_SEND_PlayerPos(True, gPlayers[I].UID);
1800 if gMonsters <> nil then
1801 for I := 0 to High(gMonsters) do
1802 if gMonsters[I] <> nil then
1803 begin
1804 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1805 begin
1806 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1807 MH_SEND_MonsterPos(gMonsters[I].UID);
1808 end
1809 else
1810 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1811 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1812 (gMonsters[I].GameVelX <> 0) or
1813 (gMonsters[I].GameVelY <> 0) then
1814 MH_SEND_MonsterPos(gMonsters[I].UID);
1815 end;
1817 NetTimeToReliable := 0;
1818 NetTimeToUpdate := NetUpdateRate;
1819 end
1820 else if NetTimeToUpdate >= NetUpdateRate then
1821 begin
1822 if gPlayers <> nil then
1823 for I := 0 to High(gPlayers) do
1824 if gPlayers[I] <> nil then
1825 MH_SEND_PlayerPos(False, gPlayers[I].UID);
1827 if gMonsters <> nil then
1828 for I := 0 to High(gMonsters) do
1829 if gMonsters[I] <> nil then
1830 begin
1831 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1832 begin
1833 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1834 MH_SEND_MonsterPos(gMonsters[I].UID);
1835 end
1836 else
1837 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1838 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1839 (gMonsters[I].GameVelX <> 0) or
1840 (gMonsters[I].GameVelY <> 0) then
1841 MH_SEND_MonsterPos(gMonsters[I].UID);
1842 end;
1844 NetTimeToUpdate := 0;
1845 end;
1847 if NetUseMaster then
1848 if gTime >= NetTimeToMaster then
1849 begin
1850 if (NetMHost = nil) or (NetMPeer = nil) then
1851 if not g_Net_Slist_Connect then
1852 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1854 g_Net_Slist_Update;
1855 NetTimeToMaster := gTime + NetMasterRate;
1856 end;
1857 end
1858 else
1859 if NetMode = NET_CLIENT then
1860 MC_SEND_PlayerPos();
1861 end; // if gameOn ...
1863 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1864 if g_ActiveWindow <> nil then
1865 begin
1866 w := e_GetFirstKeyPressed();
1868 if (w <> IK_INVALID) then
1869 begin
1870 Msg.Msg := MESSAGE_DIKEY;
1871 Msg.wParam := w;
1872 g_ActiveWindow.OnMessage(Msg);
1873 end;
1875 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1876 if g_ActiveWindow <> nil then
1877 g_ActiveWindow.Update();
1879 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1880 if gResolutionChange then
1881 begin
1882 e_WriteLog('Changing resolution', MSG_NOTIFY);
1883 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1884 gResolutionChange := False;
1885 end;
1887 // Íóæíî ñìåíèòü ÿçûê:
1888 if gLanguageChange then
1889 begin
1890 //e_WriteLog('Read language file', MSG_NOTIFY);
1891 //g_Language_Load(DataDir + gLanguage + '.txt');
1892 g_Language_Set(gLanguage);
1893 g_Menu_Reset();
1894 gLanguageChange := False;
1895 end;
1896 end;
1898 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
1899 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
1900 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
1901 begin
1902 g_TakeScreenShot();
1903 LastScreenShot := GetTimer();
1904 end;
1906 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
1907 if e_KeyPressed(IK_F10) and
1908 gGameOn and
1909 (not gConsoleShow) and
1910 (g_ActiveWindow = nil) then
1911 begin
1912 KeyPress(IK_F10);
1913 end;
1915 Time := GetTimer() {div 1000};
1917 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
1918 if gDelayedEvents <> nil then
1919 for a := 0 to High(gDelayedEvents) do
1920 if gDelayedEvents[a].Pending and
1922 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
1923 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
1924 ) then
1925 begin
1926 case gDelayedEvents[a].DEType of
1927 DE_GLOBEVENT:
1928 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
1929 DE_BFGHIT:
1930 if gGameOn then
1931 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
1932 DE_KILLCOMBO:
1933 if gGameOn then
1934 begin
1935 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
1936 if g_Game_IsNet and g_Game_IsServer then
1937 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
1938 end;
1939 end;
1940 gDelayedEvents[a].Pending := False;
1941 end;
1943 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
1944 UPSCounter := UPSCounter + 1;
1945 if Time - UPSTime >= 1000 then
1946 begin
1947 UPS := UPSCounter;
1948 UPSCounter := 0;
1949 UPSTime := Time;
1950 end;
1952 if gGameOn then g_Weapon_AddDynLights();
1953 end;
1955 procedure g_Game_LoadData();
1956 begin
1957 if DataLoaded then Exit;
1959 e_WriteLog('Loading game data...', MSG_NOTIFY);
1961 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
1962 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
1963 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
1964 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
1965 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
1966 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
1967 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB');
1968 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS');
1969 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD');
1970 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB');
1971 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS');
1972 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD');
1973 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
1974 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
1975 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
1976 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
1977 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
1978 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
1979 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
1980 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
1981 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
1982 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
1983 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
1984 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
1985 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
1986 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
1987 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
1988 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
1989 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
1990 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
1991 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
1992 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
1993 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
1994 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
1996 goodsnd[0] := TPlayableSound.Create();
1997 goodsnd[1] := TPlayableSound.Create();
1998 goodsnd[2] := TPlayableSound.Create();
1999 goodsnd[3] := TPlayableSound.Create();
2001 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2002 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2003 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2004 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2006 killsnd[0] := TPlayableSound.Create();
2007 killsnd[1] := TPlayableSound.Create();
2008 killsnd[2] := TPlayableSound.Create();
2009 killsnd[3] := TPlayableSound.Create();
2011 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2012 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2013 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2014 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2016 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2017 g_Items_LoadData();
2019 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2020 g_Weapon_LoadData();
2022 g_Monsters_LoadData();
2024 DataLoaded := True;
2025 end;
2027 procedure g_Game_FreeData();
2028 begin
2029 if not DataLoaded then Exit;
2031 g_Items_FreeData();
2032 g_Weapon_FreeData();
2033 g_Monsters_FreeData();
2035 e_WriteLog('Releasing game data...', MSG_NOTIFY);
2037 g_Texture_Delete('NOTEXTURE');
2038 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2039 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2040 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2041 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2042 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2043 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2044 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2045 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2046 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2047 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2048 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2049 g_Frames_DeleteByName('FRAMES_TELEPORT');
2050 g_Sound_Delete('SOUND_GAME_TELEPORT');
2051 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2052 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2053 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2054 g_Sound_Delete('SOUND_GAME_BULK1');
2055 g_Sound_Delete('SOUND_GAME_BULK2');
2056 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2057 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2058 g_Sound_Delete('SOUND_GAME_SWITCH1');
2059 g_Sound_Delete('SOUND_GAME_SWITCH0');
2061 goodsnd[0].Free();
2062 goodsnd[1].Free();
2063 goodsnd[2].Free();
2064 goodsnd[3].Free();
2066 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2067 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2068 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2069 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2071 killsnd[0].Free();
2072 killsnd[1].Free();
2073 killsnd[2].Free();
2074 killsnd[3].Free();
2076 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2077 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2078 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2079 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2081 DataLoaded := False;
2082 end;
2084 procedure DrawCustomStat();
2085 var
2086 pc, x, y, w, _y,
2087 w1, w2, w3,
2088 t, p, m: Integer;
2089 ww1, hh1: Word;
2090 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2091 s1, s2, topstr: String;
2092 begin
2093 e_TextureFontGetSize(gStdFont, ww2, hh2);
2095 e_PollInput();
2096 if e_KeyPressed(IK_TAB) then
2097 begin
2098 if not gStatsPressed then
2099 begin
2100 gStatsOff := not gStatsOff;
2101 gStatsPressed := True;
2102 end;
2103 end
2104 else
2105 gStatsPressed := False;
2107 if gStatsOff then
2108 begin
2109 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2110 w := (Length(s1) * ww2) div 2;
2111 x := gScreenWidth div 2 - w;
2112 y := 8;
2113 e_TextureFontPrint(x, y, s1, gStdFont);
2114 Exit;
2115 end;
2117 if (gGameSettings.GameMode = GM_COOP) then
2118 begin
2119 if gMissionFailed then
2120 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2121 else
2122 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2123 end
2124 else
2125 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2127 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2128 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2130 if g_Game_IsNet then
2131 begin
2132 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2133 if not gChatShow then
2134 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2135 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2136 end;
2138 if g_Game_IsClient then
2139 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2140 else
2141 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2142 if not gChatShow then
2143 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2144 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2146 x := 32;
2147 y := 16+hh1+16;
2149 w := gScreenWidth-x*2;
2151 w2 := (w-16) div 6;
2152 w3 := w2;
2153 w1 := w-16-w2-w3;
2155 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2156 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2158 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2160 case CustomStat.GameMode of
2161 GM_DM:
2162 begin
2163 if gGameSettings.MaxLives = 0 then
2164 s1 := _lc[I_GAME_DM]
2165 else
2166 s1 := _lc[I_GAME_LMS];
2167 end;
2168 GM_TDM:
2169 begin
2170 if gGameSettings.MaxLives = 0 then
2171 s1 := _lc[I_GAME_TDM]
2172 else
2173 s1 := _lc[I_GAME_TLMS];
2174 end;
2175 GM_CTF: s1 := _lc[I_GAME_CTF];
2176 GM_COOP:
2177 begin
2178 if gGameSettings.MaxLives = 0 then
2179 s1 := _lc[I_GAME_COOP]
2180 else
2181 s1 := _lc[I_GAME_SURV];
2182 end;
2183 else s1 := '';
2184 end;
2186 _y := y+16;
2187 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2188 _y := _y+8;
2190 _y := _y+16;
2191 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2192 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2194 _y := _y+16;
2195 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2196 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2197 (CustomStat.GameTime div 1000 div 60) mod 60,
2198 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2200 pc := Length(CustomStat.PlayerStat);
2201 if pc = 0 then Exit;
2203 if CustomStat.GameMode = GM_COOP then
2204 begin
2205 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2206 _y := _y+32;
2207 s2 := _lc[I_GAME_MONSTERS];
2208 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2209 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2210 _y := _y+16;
2211 s2 := _lc[I_GAME_SECRETS];
2212 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2213 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2214 if gLastMap then
2215 begin
2216 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2217 _y := _y-16;
2218 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2219 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2220 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2221 _y := _y+16;
2222 s2 := _lc[I_GAME_SECRETS_TOTAL];
2223 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2224 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2225 end;
2226 end;
2228 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2229 begin
2230 _y := _y+16+16;
2232 with CustomStat do
2233 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2234 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2235 else s1 := _lc[I_GAME_WIN_DRAW];
2237 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2238 _y := _y+40;
2240 for t := TEAM_RED to TEAM_BLUE do
2241 begin
2242 if t = TEAM_RED then
2243 begin
2244 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2245 gStdFont, 255, 0, 0, 1);
2246 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2247 gStdFont, 255, 0, 0, 1);
2248 r := 255;
2249 g := 0;
2250 b := 0;
2251 end
2252 else
2253 begin
2254 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2255 gStdFont, 0, 0, 255, 1);
2256 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2257 gStdFont, 0, 0, 255, 1);
2258 r := 0;
2259 g := 0;
2260 b := 255;
2261 end;
2263 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2264 _y := _y+24;
2266 for p := 0 to High(CustomStat.PlayerStat) do
2267 if CustomStat.PlayerStat[p].Team = t then
2268 with CustomStat.PlayerStat[p] do
2269 begin
2270 if Spectator then
2271 begin
2272 rr := r div 2;
2273 gg := g div 2;
2274 bb := b div 2;
2275 end
2276 else
2277 begin
2278 rr := r;
2279 gg := g;
2280 bb := b;
2281 end;
2282 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2283 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2284 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2285 _y := _y+24;
2286 end;
2288 _y := _y+16+16;
2289 end;
2290 end
2291 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2292 begin
2293 _y := _y+40;
2294 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2295 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2296 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2298 _y := _y+24;
2299 for p := 0 to High(CustomStat.PlayerStat) do
2300 with CustomStat.PlayerStat[p] do
2301 begin
2302 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2304 if Spectator then
2305 r := 127
2306 else
2307 r := 255;
2309 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2310 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2311 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2312 _y := _y+24;
2313 end;
2314 end;
2315 end;
2317 procedure DrawSingleStat();
2318 var
2319 tm, key_x, val_x, y: Integer;
2320 w1, w2, h: Word;
2321 s1, s2: String;
2323 procedure player_stat(n: Integer);
2324 var
2325 kpm: Real;
2327 begin
2328 // "Kills: # / #":
2329 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2330 s2 := Format(' %d', [gTotalMonsters]);
2332 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2333 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2334 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2335 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2336 s1 := s1 + '/';
2337 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2338 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2340 // "Kills-per-minute: ##.#":
2341 s1 := _lc[I_MENU_INTER_KPM];
2342 if tm > 0 then
2343 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2344 else
2345 kpm := SingleStat.PlayerStat[n].Kills;
2346 s2 := Format(' %.1f', [kpm]);
2348 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2349 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2351 // "Secrets found: # / #":
2352 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2353 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2355 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2356 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2357 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2358 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2359 s1 := s1 + '/';
2360 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2361 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2362 end;
2364 begin
2365 // "Level Complete":
2366 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2367 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2369 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2370 s1 := _lc[I_MENU_INTER_KPM];
2371 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2372 Inc(w1, 16);
2373 s1 := ' 9999.9';
2374 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2376 key_x := (gScreenWidth-w1-w2) div 2;
2377 val_x := key_x + w1;
2379 // "Time: #:##:##":
2380 tm := SingleStat.GameTime div 1000;
2381 s1 := _lc[I_MENU_INTER_TIME];
2382 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2384 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2385 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2387 if SingleStat.TwoPlayers then
2388 begin
2389 // "Player 1":
2390 s1 := _lc[I_MENU_PLAYER_1];
2391 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2392 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2394 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2395 y := 176;
2396 player_stat(0);
2398 // "Player 2":
2399 s1 := _lc[I_MENU_PLAYER_2];
2400 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2401 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2403 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2404 y := 336;
2405 player_stat(1);
2406 end
2407 else
2408 begin
2409 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2410 y := 128;
2411 player_stat(0);
2412 end;
2413 end;
2415 procedure DrawLoadingStat();
2416 var
2417 ww, hh: Word;
2418 xx, yy, i: Integer;
2419 s: String;
2420 begin
2421 if Length(LoadingStat.Msgs) = 0 then
2422 Exit;
2424 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2425 yy := (gScreenHeight div 3);
2426 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2427 xx := (gScreenWidth div 3);
2429 with LoadingStat do
2430 for i := 0 to NextMsg-1 do
2431 begin
2432 if (i = (NextMsg-1)) and (MaxValue > 0) then
2433 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2434 else
2435 s := Msgs[i];
2437 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2438 yy := yy + LOADING_INTERLINE;
2439 end;
2440 end;
2442 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2443 var
2444 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2445 begin
2446 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2447 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2448 begin
2449 Scale := 1;
2450 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2451 ScaleSz := 16 div Scale;
2452 // Ðàçìåðû ìèíè-êàðòû:
2453 aX := max(gMapInfo.Width div ScaleSz, 1);
2454 aY := max(gMapInfo.Height div ScaleSz, 1);
2455 // Ðàìêà êàðòû:
2456 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2458 if gWalls <> nil then
2459 begin
2460 // Ðèñóåì ñòåíû:
2461 for a := 0 to High(gWalls) do
2462 with gWalls[a] do
2463 if PanelType <> 0 then
2464 begin
2465 // Ëåâûé âåðõíèé óãîë:
2466 aX := X div ScaleSz;
2467 aY := Y div ScaleSz;
2468 // Ðàçìåðû:
2469 aX2 := max(Width div ScaleSz, 1);
2470 aY2 := max(Height div ScaleSz, 1);
2471 // Ïðàâûé íèæíèé óãîë:
2472 aX2 := aX + aX2 - 1;
2473 aY2 := aY + aY2 - 1;
2475 case PanelType of
2476 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2477 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2478 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2479 end;
2480 end;
2481 end;
2482 if gSteps <> nil then
2483 begin
2484 // Ðèñóåì ñòóïåíè:
2485 for a := 0 to High(gSteps) do
2486 with gSteps[a] do
2487 if PanelType <> 0 then
2488 begin
2489 // Ëåâûé âåðõíèé óãîë:
2490 aX := X div ScaleSz;
2491 aY := Y div ScaleSz;
2492 // Ðàçìåðû:
2493 aX2 := max(Width div ScaleSz, 1);
2494 aY2 := max(Height div ScaleSz, 1);
2495 // Ïðàâûé íèæíèé óãîë:
2496 aX2 := aX + aX2 - 1;
2497 aY2 := aY + aY2 - 1;
2499 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2500 end;
2501 end;
2502 if gLifts <> nil then
2503 begin
2504 // Ðèñóåì ëèôòû:
2505 for a := 0 to High(gLifts) do
2506 with gLifts[a] do
2507 if PanelType <> 0 then
2508 begin
2509 // Ëåâûé âåðõíèé óãîë:
2510 aX := X div ScaleSz;
2511 aY := Y div ScaleSz;
2512 // Ðàçìåðû:
2513 aX2 := max(Width div ScaleSz, 1);
2514 aY2 := max(Height div ScaleSz, 1);
2515 // Ïðàâûé íèæíèé óãîë:
2516 aX2 := aX + aX2 - 1;
2517 aY2 := aY + aY2 - 1;
2519 case LiftType of
2520 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2521 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2522 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2523 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2524 end;
2525 end;
2526 end;
2527 if gWater <> nil then
2528 begin
2529 // Ðèñóåì âîäó:
2530 for a := 0 to High(gWater) do
2531 with gWater[a] do
2532 if PanelType <> 0 then
2533 begin
2534 // Ëåâûé âåðõíèé óãîë:
2535 aX := X div ScaleSz;
2536 aY := Y div ScaleSz;
2537 // Ðàçìåðû:
2538 aX2 := max(Width div ScaleSz, 1);
2539 aY2 := max(Height div ScaleSz, 1);
2540 // Ïðàâûé íèæíèé óãîë:
2541 aX2 := aX + aX2 - 1;
2542 aY2 := aY + aY2 - 1;
2544 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2545 end;
2546 end;
2547 if gAcid1 <> nil then
2548 begin
2549 // Ðèñóåì êèñëîòó 1:
2550 for a := 0 to High(gAcid1) do
2551 with gAcid1[a] do
2552 if PanelType <> 0 then
2553 begin
2554 // Ëåâûé âåðõíèé óãîë:
2555 aX := X div ScaleSz;
2556 aY := Y div ScaleSz;
2557 // Ðàçìåðû:
2558 aX2 := max(Width div ScaleSz, 1);
2559 aY2 := max(Height div ScaleSz, 1);
2560 // Ïðàâûé íèæíèé óãîë:
2561 aX2 := aX + aX2 - 1;
2562 aY2 := aY + aY2 - 1;
2564 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2565 end;
2566 end;
2567 if gAcid2 <> nil then
2568 begin
2569 // Ðèñóåì êèñëîòó 2:
2570 for a := 0 to High(gAcid2) do
2571 with gAcid2[a] do
2572 if PanelType <> 0 then
2573 begin
2574 // Ëåâûé âåðõíèé óãîë:
2575 aX := X div ScaleSz;
2576 aY := Y div ScaleSz;
2577 // Ðàçìåðû:
2578 aX2 := max(Width div ScaleSz, 1);
2579 aY2 := max(Height div ScaleSz, 1);
2580 // Ïðàâûé íèæíèé óãîë:
2581 aX2 := aX + aX2 - 1;
2582 aY2 := aY + aY2 - 1;
2584 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2585 end;
2586 end;
2587 if gPlayers <> nil then
2588 begin
2589 // Ðèñóåì èãðîêîâ:
2590 for a := 0 to High(gPlayers) do
2591 if gPlayers[a] <> nil then with gPlayers[a] do
2592 if Live then begin
2593 // Ëåâûé âåðõíèé óãîë:
2594 aX := Obj.X div ScaleSz + 1;
2595 aY := Obj.Y div ScaleSz + 1;
2596 // Ðàçìåðû:
2597 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2598 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2599 // Ïðàâûé íèæíèé óãîë:
2600 aX2 := aX + aX2 - 1;
2601 aY2 := aY + aY2 - 1;
2603 if gPlayers[a] = p then
2604 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2605 else
2606 case Team of
2607 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2608 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2609 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2610 end;
2611 end;
2612 end;
2613 if gMonsters <> nil then
2614 begin
2615 // Ðèñóåì ìîíñòðîâ:
2616 for a := 0 to High(gMonsters) do
2617 if gMonsters[a] <> nil then with gMonsters[a] do
2618 if Live then begin
2619 // Ëåâûé âåðõíèé óãîë:
2620 aX := Obj.X div ScaleSz + 1;
2621 aY := Obj.Y div ScaleSz + 1;
2622 // Ðàçìåðû:
2623 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2624 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2625 // Ïðàâûé íèæíèé óãîë:
2626 aX2 := aX + aX2 - 1;
2627 aY2 := aY + aY2 - 1;
2629 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2630 end;
2631 end;
2632 end;
2633 end;
2635 procedure DrawMapView(x, y, w, h: Integer);
2636 var
2637 bx, by: Integer;
2638 begin
2639 glPushMatrix();
2641 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2642 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2643 g_Map_DrawBack(-bx, -by);
2645 sX := x;
2646 sY := y;
2647 sWidth := w;
2648 sHeight := h;
2650 glTranslatef(-x, -y, 0);
2652 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_BACK);
2653 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_STEP);
2654 g_Items_Draw();
2655 g_Weapon_Draw();
2656 g_Player_DrawShells();
2657 g_Player_DrawAll();
2658 g_Player_DrawCorpses();
2659 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WALL);
2660 g_Monsters_Draw();
2661 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_CLOSEDOOR);
2662 g_GFX_Draw();
2663 g_Map_DrawFlags();
2664 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID1);
2665 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID2);
2666 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WATER);
2667 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_FORE);
2668 if g_debug_HealthBar then
2669 begin
2670 g_Monsters_DrawHealth();
2671 g_Player_DrawHealth();
2672 end;
2674 glPopMatrix();
2675 end;
2677 procedure DrawPlayer(p: TPlayer);
2678 var
2679 px, py, a, b, c, d: Integer;
2680 //R: TRect;
2681 lln: Integer;
2682 lx, ly, lrad: Integer;
2683 begin
2684 if (p = nil) or (p.FDummy) then
2685 begin
2686 glPushMatrix();
2687 g_Map_DrawBack(0, 0);
2688 glPopMatrix();
2689 Exit;
2690 end;
2692 gPlayerDrawn := p;
2694 glPushMatrix();
2696 px := p.GameX + PLAYER_RECT_CX;
2697 py := p.GameY + PLAYER_RECT_CY;
2699 if px > (gPlayerScreenSize.X div 2) then
2700 a := -px + (gPlayerScreenSize.X div 2)
2701 else
2702 a := 0;
2703 if py > (gPlayerScreenSize.Y div 2) then
2704 b := -py + (gPlayerScreenSize.Y div 2)
2705 else
2706 b := 0;
2707 if px > (gMapInfo.Width - (gPlayerScreenSize.X div 2)) then
2708 a := -gMapInfo.Width + gPlayerScreenSize.X;
2709 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2710 b := -gMapInfo.Height + gPlayerScreenSize.Y;
2711 if gMapInfo.Width <= gPlayerScreenSize.X then
2712 a := 0;
2713 if gMapInfo.Height <= gPlayerScreenSize.Y then
2714 b := 0;
2716 if p.IncCam <> 0 then
2717 begin
2718 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2719 begin
2720 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2721 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2722 end;
2724 if py < (gPlayerScreenSize.Y div 2) then
2725 begin
2726 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2727 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2728 end;
2730 if p.IncCam < 0 then
2731 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and
2732 (p.IncCam < 0) do
2733 p.IncCam := p.IncCam + 1;
2735 if p.IncCam > 0 then
2736 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and
2737 (p.IncCam > 0) do
2738 p.IncCam := p.IncCam - 1;
2739 end;
2741 if (px< gPlayerScreenSize.X div 2) or
2742 (gMapInfo.Width-gPlayerScreenSize.X <= 256) then
2743 c := 0
2744 else
2745 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then
2746 c := gBackSize.X - gPlayerScreenSize.X
2747 else
2748 c := Round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2750 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or
2751 (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then
2752 d := 0
2753 else
2754 if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then
2755 d := gBackSize.Y - gPlayerScreenSize.Y
2756 else
2757 d := Round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
2759 xprofBeginSection('map background');
2760 g_Map_DrawBack(-c, -d);
2761 xprofEndSection();
2763 sX := -a;
2764 sY := -(b+p.IncCam);
2765 sWidth := gPlayerScreenSize.X;
2766 sHeight := gPlayerScreenSize.Y;
2768 glTranslatef(a, b+p.IncCam, 0);
2770 xprofBeginSection('map rendering');
2772 xprofBeginSection('panel_back');
2773 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_BACK);
2774 xprofEndSection();
2776 xprofBeginSection('panel_step');
2777 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_STEP);
2778 xprofEndSection();
2780 xprofBeginSection('items');
2781 g_Items_Draw();
2782 xprofEndSection();
2784 xprofBeginSection('weapons');
2785 g_Weapon_Draw();
2786 xprofEndSection();
2788 xprofBeginSection('shells');
2789 g_Player_DrawShells();
2790 xprofEndSection();
2792 xprofBeginSection('drawall');
2793 g_Player_DrawAll();
2794 xprofEndSection();
2796 xprofBeginSection('corpses');
2797 g_Player_DrawCorpses();
2798 xprofEndSection();
2800 xprofBeginSection('panel_wall');
2801 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WALL);
2802 xprofEndSection();
2804 xprofBeginSection('monsters');
2805 g_Monsters_Draw();
2806 xprofEndSection();
2808 xprofBeginSection('panel_closedoor');
2809 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_CLOSEDOOR);
2810 xprofEndSection();
2812 xprofBeginSection('gfx');
2813 g_GFX_Draw();
2814 xprofEndSection();
2816 xprofBeginSection('flags');
2817 g_Map_DrawFlags();
2818 xprofEndSection();
2820 xprofBeginSection('panel_acid1');
2821 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID1);
2822 xprofEndSection();
2824 xprofBeginSection('panel_acid2');
2825 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID2);
2826 xprofEndSection();
2828 xprofBeginSection('panel_water');
2829 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WATER);
2830 xprofEndSection();
2832 //TODO: lights should be in separate grid, i think
2833 // but on the other side: grid may be slower for dynlights, as their lifetime is short
2834 if gwin_has_stencil and (g_dynLightCount > 0) then
2835 begin
2836 xprofBeginSection('dynlights');
2838 // setup OpenGL parameters
2839 glStencilMask($FFFFFFFF);
2840 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2841 glEnable(GL_STENCIL_TEST);
2842 glEnable(GL_SCISSOR_TEST);
2843 glClear(GL_STENCIL_BUFFER_BIT);
2844 glStencilFunc(GL_EQUAL, 0, $ff);
2846 for lln := 0 to g_dynLightCount-1 do
2847 begin
2848 lx := g_dynLights[lln].x;
2849 ly := g_dynLights[lln].y;
2850 lrad := g_dynLights[lln].radius;
2851 if lrad < 3 then continue;
2853 if lx-sX+lrad < 0 then continue;
2854 if ly-sY+lrad < 0 then continue;
2855 if lx-sX-lrad >= gPlayerScreenSize.X then continue;
2856 if ly-sY-lrad >= gPlayerScreenSize.Y then continue;
2858 // set scissor to optimize drawing
2859 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2860 // no need to clear stencil buffer, light blitting will do it for us
2861 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2862 // draw extruded panels
2863 glDisable(GL_TEXTURE_2D);
2864 glDisable(GL_BLEND);
2865 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2866 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2867 // render light texture
2868 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2869 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2870 // blend it
2871 glEnable(GL_BLEND);
2872 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2873 glEnable(GL_TEXTURE_2D);
2874 // color and opacity
2875 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2876 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2877 glBegin(GL_QUADS);
2878 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2879 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2880 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2881 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2882 glEnd();
2883 end;
2885 // done
2886 glDisable(GL_STENCIL_TEST);
2887 glDisable(GL_BLEND);
2888 glDisable(GL_SCISSOR_TEST);
2889 glScissor(0, 0, sWidth, sHeight);
2891 xprofEndSection();
2892 end
2893 else
2894 begin
2895 xprofBeginSection('dynlights');
2896 xprofEndSection();
2897 end;
2899 xprofBeginSection('panel_fore');
2900 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_FORE);
2901 xprofEndSection();
2903 if g_debug_HealthBar then
2904 begin
2905 xprofBeginSection('monster health');
2906 g_Monsters_DrawHealth();
2907 xprofEndSection();
2909 xprofBeginSection('player health');
2910 g_Player_DrawHealth();
2911 xprofEndSection();
2912 end;
2914 if p.FSpectator then
2915 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2916 p.GameY + PLAYER_RECT_CY - 4,
2917 'X', gStdFont, 255, 255, 255, 1, True);
2919 for a := 0 to High(gCollideMap) do
2920 for b := 0 to High(gCollideMap[a]) do
2921 begin
2922 d := 0;
2923 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2924 d := d + 1;
2925 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2926 d := d + 2;
2928 case d of
2929 1: e_DrawPoint(1, b, a, 200, 200, 200);
2930 2: e_DrawPoint(1, b, a, 64, 64, 255);
2931 3: e_DrawPoint(1, b, a, 255, 0, 255);
2932 end;
2933 end;
2936 glPopMatrix();
2938 xprofEndSection(); // map rendering
2940 p.DrawPain();
2941 p.DrawPickup();
2942 p.DrawRulez();
2943 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
2944 if g_Debug_Player then
2945 g_Player_DrawDebug(p);
2946 p.DrawGUI();
2947 end;
2949 procedure g_Game_Draw();
2950 var
2951 ID: DWORD;
2952 w, h: Word;
2953 ww, hh: Byte;
2954 Time: Int64;
2955 back: string;
2956 plView1, plView2: TPlayer;
2957 Split: Boolean;
2958 begin
2959 if gExit = EXIT_QUIT then Exit;
2961 Time := GetTimer() {div 1000};
2962 FPSCounter := FPSCounter+1;
2963 if Time - FPSTime >= 1000 then
2964 begin
2965 FPS := FPSCounter;
2966 FPSCounter := 0;
2967 FPSTime := Time;
2968 end;
2970 xprofBegin(g_profile_frame_draw);
2972 if gGameOn or (gState = STATE_FOLD) then
2973 begin
2974 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
2975 begin
2976 gSpectMode := SPECT_NONE;
2977 if not gRevertPlayers then
2978 begin
2979 plView1 := gPlayer1;
2980 plView2 := gPlayer2;
2981 end
2982 else
2983 begin
2984 plView1 := gPlayer2;
2985 plView2 := gPlayer1;
2986 end;
2987 end
2988 else
2989 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
2990 begin
2991 gSpectMode := SPECT_NONE;
2992 if gPlayer2 = nil then
2993 plView1 := gPlayer1
2994 else
2995 plView1 := gPlayer2;
2996 plView2 := nil;
2997 end
2998 else
2999 begin
3000 plView1 := nil;
3001 plView2 := nil;
3002 end;
3004 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3005 gSpectMode := SPECT_STATS;
3007 if gSpectMode = SPECT_PLAYERS then
3008 if gPlayers <> nil then
3009 begin
3010 plView1 := GetActivePlayer_ByID(gSpectPID1);
3011 if plView1 = nil then
3012 begin
3013 gSpectPID1 := GetActivePlayerID_Next();
3014 plView1 := GetActivePlayer_ByID(gSpectPID1);
3015 end;
3016 if gSpectViewTwo then
3017 begin
3018 plView2 := GetActivePlayer_ByID(gSpectPID2);
3019 if plView2 = nil then
3020 begin
3021 gSpectPID2 := GetActivePlayerID_Next();
3022 plView2 := GetActivePlayer_ByID(gSpectPID2);
3023 end;
3024 end;
3025 end;
3027 if gSpectMode = SPECT_MAPVIEW then
3028 begin
3029 // Ðåæèì ïðîñìîòðà êàðòû
3030 Split := False;
3031 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3032 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3033 gHearPoint1.Active := True;
3034 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3035 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3036 gHearPoint2.Active := False;
3037 end
3038 else
3039 begin
3040 Split := (plView1 <> nil) and (plView2 <> nil);
3042 // Òî÷êè ñëóõà èãðîêîâ
3043 if plView1 <> nil then
3044 begin
3045 gHearPoint1.Active := True;
3046 gHearPoint1.Coords.X := plView1.GameX;
3047 gHearPoint1.Coords.Y := plView1.GameY;
3048 end else
3049 gHearPoint1.Active := False;
3050 if plView2 <> nil then
3051 begin
3052 gHearPoint2.Active := True;
3053 gHearPoint2.Coords.X := plView2.GameX;
3054 gHearPoint2.Coords.Y := plView2.GameY;
3055 end else
3056 gHearPoint2.Active := False;
3058 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3059 gPlayerScreenSize.X := gScreenWidth-196;
3060 if Split then
3061 begin
3062 gPlayerScreenSize.Y := gScreenHeight div 2;
3063 if gScreenHeight mod 2 = 0 then
3064 Dec(gPlayerScreenSize.Y);
3065 end
3066 else
3067 gPlayerScreenSize.Y := gScreenHeight;
3069 if Split then
3070 if gScreenHeight mod 2 = 0 then
3071 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3072 else
3073 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3075 DrawPlayer(plView1);
3076 gPlayer1ScreenCoord.X := sX;
3077 gPlayer1ScreenCoord.Y := sY;
3079 if Split then
3080 begin
3081 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3083 DrawPlayer(plView2);
3084 gPlayer2ScreenCoord.X := sX;
3085 gPlayer2ScreenCoord.Y := sY;
3086 end;
3088 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3090 if Split then
3091 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3092 end;
3094 if MessageText <> '' then
3095 begin
3096 w := 0;
3097 h := 0;
3098 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3099 if Split then
3100 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3101 (gScreenHeight div 2)-(h div 2), MessageText)
3102 else
3103 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3104 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3105 end;
3107 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3109 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3110 begin
3111 // Draw spectator GUI
3112 ww := 0;
3113 hh := 0;
3114 e_TextureFontGetSize(gStdFont, ww, hh);
3115 case gSpectMode of
3116 SPECT_STATS:
3117 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3118 SPECT_MAPVIEW:
3119 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3120 SPECT_PLAYERS:
3121 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3122 end;
3123 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3124 if gSpectMode = SPECT_MAPVIEW then
3125 begin
3126 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3127 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3128 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3129 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3130 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3131 end;
3132 if gSpectMode = SPECT_PLAYERS then
3133 begin
3134 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3135 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3136 if gSpectViewTwo then
3137 begin
3138 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3139 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3140 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3141 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3142 end
3143 else
3144 begin
3145 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3146 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3147 end;
3148 end;
3149 end;
3150 end;
3152 if gPause and gGameOn and (g_ActiveWindow = nil) then
3153 begin
3154 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3156 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3157 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3158 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3159 end;
3161 if not gGameOn then
3162 begin
3163 if (gState = STATE_MENU) then
3164 begin
3165 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
3166 begin
3167 if g_Texture_Get('MENU_BACKGROUND', ID) then
3168 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3169 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3170 end;
3171 if g_ActiveWindow <> nil then
3172 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3173 end;
3175 if gState = STATE_FOLD then
3176 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3178 if gState = STATE_INTERCUSTOM then
3179 begin
3180 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3181 begin
3182 back := 'TEXTURE_endpic';
3183 if not g_Texture_Get(back, ID) then
3184 back := _lc[I_TEXTURE_ENDPIC];
3185 end
3186 else
3187 back := 'INTER';
3189 if g_Texture_Get(back, ID) then
3190 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3191 else
3192 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3194 DrawCustomStat();
3196 if g_ActiveWindow <> nil then
3197 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3198 end;
3200 if gState = STATE_INTERSINGLE then
3201 begin
3202 if EndingGameCounter > 0 then
3203 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter)
3204 else
3205 begin
3206 back := 'INTER';
3208 if g_Texture_Get(back, ID) then
3209 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3210 else
3211 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3213 DrawSingleStat();
3215 if g_ActiveWindow <> nil then
3216 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3217 end;
3218 end;
3220 if gState = STATE_ENDPIC then
3221 begin
3222 ID := DWORD(-1);
3223 if not g_Texture_Get('TEXTURE_endpic', ID) then
3224 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3226 if ID <> DWORD(-1) then
3227 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3228 else
3229 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3231 if g_ActiveWindow <> nil then
3232 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3233 end;
3235 if gState = STATE_SLIST then
3236 begin
3237 if g_Texture_Get('MENU_BACKGROUND', ID) then
3238 begin
3239 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3240 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3241 end;
3242 g_Serverlist_Draw(slCurrent);
3243 end;
3244 end;
3246 if g_ActiveWindow <> nil then
3247 begin
3248 if gGameOn then
3249 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3250 g_ActiveWindow.Draw();
3251 end;
3253 g_Console_Draw();
3255 if g_debug_Sounds and gGameOn then
3256 begin
3257 for w := 0 to High(e_SoundsArray) do
3258 for h := 0 to e_SoundsArray[w].nRefs do
3259 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3260 end;
3262 if gShowFPS then
3263 begin
3264 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3265 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3266 end;
3268 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3269 e_TextureFontPrint(gScreenWidth-72, 0,
3270 Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]),
3271 gStdFont);
3273 xprofEnd();
3274 if g_profile_frame_draw then drawProfiles(0, 0, 'MAP RENDER');
3275 end;
3277 procedure g_Game_Quit();
3278 begin
3279 g_Game_StopAllSounds(True);
3280 gMusic.Free();
3281 g_Game_SaveOptions();
3282 g_Game_FreeData();
3283 g_PlayerModel_FreeData();
3284 g_Texture_DeleteAll();
3285 g_Frames_DeleteAll();
3286 g_Menu_Free();
3288 if NetInitDone then g_Net_Free;
3290 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3291 if gMapToDelete <> '' then
3292 g_Game_DeleteTestMap();
3294 gExit := EXIT_QUIT;
3295 PushExitEvent();
3296 end;
3298 procedure g_FatalError(Text: String);
3299 begin
3300 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3301 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3303 gExit := EXIT_SIMPLE;
3304 end;
3306 procedure g_SimpleError(Text: String);
3307 begin
3308 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3309 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3310 end;
3312 procedure g_Game_SetupScreenSize();
3313 var
3314 d: Single;
3315 begin
3316 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3317 gPlayerScreenSize.X := gScreenWidth-196;
3318 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3319 gPlayerScreenSize.Y := gScreenHeight div 2
3320 else
3321 gPlayerScreenSize.Y := gScreenHeight;
3323 // Ðàçìåð çàäíåãî ïëàíà:
3324 if BackID <> DWORD(-1) then
3325 begin
3326 d := SKY_STRETCH;
3328 if (gScreenWidth*d > gMapInfo.Width) or
3329 (gScreenHeight*d > gMapInfo.Height) then
3330 d := 1.0;
3332 gBackSize.X := Round(gScreenWidth*d);
3333 gBackSize.Y := Round(gScreenHeight*d);
3334 end;
3335 end;
3337 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3338 begin
3339 g_Window_SetSize(newWidth, newHeight, nowFull);
3340 end;
3342 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3343 begin
3344 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3345 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3346 Exit;
3347 if gPlayer1 = nil then
3348 begin
3349 if g_Game_IsClient then
3350 begin
3351 if NetPlrUID1 > -1 then
3352 begin
3353 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3354 gPlayer1 := g_Player_Get(NetPlrUID1);
3355 end;
3356 Exit;
3357 end;
3359 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3360 Team := gPlayer1Settings.Team;
3362 // Ñîçäàíèå ïåðâîãî èãðîêà:
3363 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3364 gPlayer1Settings.Color,
3365 Team, False));
3366 if gPlayer1 = nil then
3367 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3368 else
3369 begin
3370 gPlayer1.Name := gPlayer1Settings.Name;
3371 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3372 if g_Game_IsServer and g_Game_IsNet then
3373 MH_SEND_PlayerCreate(gPlayer1.UID);
3374 gPlayer1.Respawn(False, True);
3376 if g_Game_IsNet and NetUseMaster then
3377 g_Net_Slist_Update;
3378 end;
3380 Exit;
3381 end;
3382 if gPlayer2 = nil then
3383 begin
3384 if g_Game_IsClient then
3385 begin
3386 if NetPlrUID2 > -1 then
3387 gPlayer2 := g_Player_Get(NetPlrUID2);
3388 Exit;
3389 end;
3391 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3392 Team := gPlayer2Settings.Team;
3394 // Ñîçäàíèå âòîðîãî èãðîêà:
3395 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3396 gPlayer2Settings.Color,
3397 Team, False));
3398 if gPlayer2 = nil then
3399 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3400 else
3401 begin
3402 gPlayer2.Name := gPlayer2Settings.Name;
3403 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3404 if g_Game_IsServer and g_Game_IsNet then
3405 MH_SEND_PlayerCreate(gPlayer2.UID);
3406 gPlayer2.Respawn(False, True);
3408 if g_Game_IsNet and NetUseMaster then
3409 g_Net_Slist_Update;
3410 end;
3412 Exit;
3413 end;
3414 end;
3416 procedure g_Game_RemovePlayer();
3417 var
3418 Pl: TPlayer;
3419 begin
3420 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3421 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3422 Exit;
3423 Pl := gPlayer2;
3424 if Pl <> nil then
3425 begin
3426 if g_Game_IsServer then
3427 begin
3428 Pl.Lives := 0;
3429 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3430 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3431 g_Player_Remove(Pl.UID);
3433 if g_Game_IsNet and NetUseMaster then
3434 g_Net_Slist_Update;
3435 end else
3436 gPlayer2 := nil;
3437 Exit;
3438 end;
3439 Pl := gPlayer1;
3440 if Pl <> nil then
3441 begin
3442 if g_Game_IsServer then
3443 begin
3444 Pl.Lives := 0;
3445 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3446 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3447 g_Player_Remove(Pl.UID);
3449 if g_Game_IsNet and NetUseMaster then
3450 g_Net_Slist_Update;
3451 end else
3452 begin
3453 gPlayer1 := nil;
3454 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3455 end;
3456 Exit;
3457 end;
3458 end;
3460 procedure g_Game_Spectate();
3461 begin
3462 g_Game_RemovePlayer();
3463 if gPlayer1 <> nil then
3464 g_Game_RemovePlayer();
3465 end;
3467 procedure g_Game_SpectateCenterView();
3468 begin
3469 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3470 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3471 end;
3473 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3474 var
3475 i, nPl: Integer;
3476 begin
3477 g_Game_Free();
3479 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3481 g_Game_ClearLoading();
3483 // Íàñòðîéêè èãðû:
3484 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3485 gAimLine := False;
3486 gShowMap := False;
3487 gGameSettings.GameType := GT_SINGLE;
3488 gGameSettings.MaxLives := 0;
3489 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3490 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3491 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3492 gSwitchGameMode := GM_SINGLE;
3494 g_Game_ExecuteEvent('ongamestart');
3496 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3497 g_Game_SetupScreenSize();
3499 // Ñîçäàíèå ïåðâîãî èãðîêà:
3500 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3501 gPlayer1Settings.Color,
3502 gPlayer1Settings.Team, False));
3503 if gPlayer1 = nil then
3504 begin
3505 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3506 Exit;
3507 end;
3509 gPlayer1.Name := gPlayer1Settings.Name;
3510 nPl := 1;
3512 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3513 if TwoPlayers then
3514 begin
3515 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3516 gPlayer2Settings.Color,
3517 gPlayer2Settings.Team, False));
3518 if gPlayer2 = nil then
3519 begin
3520 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3521 Exit;
3522 end;
3524 gPlayer2.Name := gPlayer2Settings.Name;
3525 Inc(nPl);
3526 end;
3528 // Çàãðóçêà è çàïóñê êàðòû:
3529 if not g_Game_StartMap(MAP, True) then
3530 begin
3531 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3532 Exit;
3533 end;
3535 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3536 g_Player_Init();
3538 // Ñîçäàåì áîòîâ:
3539 for i := nPl+1 to nPlayers do
3540 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3541 end;
3543 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3544 TimeLimit, GoalLimit: Word;
3545 MaxLives: Byte;
3546 Options: LongWord; nPlayers: Byte);
3547 var
3548 i, nPl: Integer;
3549 begin
3550 g_Game_Free();
3552 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3554 g_Game_ClearLoading();
3556 // Íàñòðîéêè èãðû:
3557 gGameSettings.GameType := GT_CUSTOM;
3558 gGameSettings.GameMode := GameMode;
3559 gSwitchGameMode := GameMode;
3560 gGameSettings.TimeLimit := TimeLimit;
3561 gGameSettings.GoalLimit := GoalLimit;
3562 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3563 gGameSettings.Options := Options;
3565 gCoopTotalMonstersKilled := 0;
3566 gCoopTotalSecretsFound := 0;
3567 gCoopTotalMonsters := 0;
3568 gCoopTotalSecrets := 0;
3569 gAimLine := False;
3570 gShowMap := False;
3572 g_Game_ExecuteEvent('ongamestart');
3574 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3575 g_Game_SetupScreenSize();
3577 // Ðåæèì íàáëþäàòåëÿ:
3578 if nPlayers = 0 then
3579 begin
3580 gPlayer1 := nil;
3581 gPlayer2 := nil;
3582 end;
3584 nPl := 0;
3585 if nPlayers >= 1 then
3586 begin
3587 // Ñîçäàíèå ïåðâîãî èãðîêà:
3588 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3589 gPlayer1Settings.Color,
3590 gPlayer1Settings.Team, False));
3591 if gPlayer1 = nil then
3592 begin
3593 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3594 Exit;
3595 end;
3597 gPlayer1.Name := gPlayer1Settings.Name;
3598 Inc(nPl);
3599 end;
3601 if nPlayers >= 2 then
3602 begin
3603 // Ñîçäàíèå âòîðîãî èãðîêà:
3604 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3605 gPlayer2Settings.Color,
3606 gPlayer2Settings.Team, False));
3607 if gPlayer2 = nil then
3608 begin
3609 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3610 Exit;
3611 end;
3613 gPlayer2.Name := gPlayer2Settings.Name;
3614 Inc(nPl);
3615 end;
3617 // Çàãðóçêà è çàïóñê êàðòû:
3618 if not g_Game_StartMap(Map, True) then
3619 begin
3620 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3621 Exit;
3622 end;
3624 // Íåò òî÷åê ïîÿâëåíèÿ:
3625 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3626 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3627 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3628 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3629 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3630 begin
3631 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3632 Exit;
3633 end;
3635 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3636 g_Player_Init();
3638 // Ñîçäàåì áîòîâ:
3639 for i := nPl+1 to nPlayers do
3640 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3641 end;
3643 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3644 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3645 Options: LongWord; nPlayers: Byte;
3646 IPAddr: LongWord; Port: Word);
3647 begin
3648 g_Game_Free();
3650 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3652 g_Game_ClearLoading();
3654 // Íàñòðîéêè èãðû:
3655 gGameSettings.GameType := GT_SERVER;
3656 gGameSettings.GameMode := GameMode;
3657 gSwitchGameMode := GameMode;
3658 gGameSettings.TimeLimit := TimeLimit;
3659 gGameSettings.GoalLimit := GoalLimit;
3660 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3661 gGameSettings.Options := Options;
3663 gCoopTotalMonstersKilled := 0;
3664 gCoopTotalSecretsFound := 0;
3665 gCoopTotalMonsters := 0;
3666 gCoopTotalSecrets := 0;
3667 gAimLine := False;
3668 gShowMap := False;
3670 g_Game_ExecuteEvent('ongamestart');
3672 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3673 g_Game_SetupScreenSize();
3675 // Ðåæèì íàáëþäàòåëÿ:
3676 if nPlayers = 0 then
3677 begin
3678 gPlayer1 := nil;
3679 gPlayer2 := nil;
3680 end;
3682 if nPlayers >= 1 then
3683 begin
3684 // Ñîçäàíèå ïåðâîãî èãðîêà:
3685 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3686 gPlayer1Settings.Color,
3687 gPlayer1Settings.Team, False));
3688 if gPlayer1 = nil then
3689 begin
3690 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3691 Exit;
3692 end;
3694 gPlayer1.Name := gPlayer1Settings.Name;
3695 end;
3697 if nPlayers >= 2 then
3698 begin
3699 // Ñîçäàíèå âòîðîãî èãðîêà:
3700 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3701 gPlayer2Settings.Color,
3702 gPlayer2Settings.Team, False));
3703 if gPlayer2 = nil then
3704 begin
3705 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3706 Exit;
3707 end;
3709 gPlayer2.Name := gPlayer2Settings.Name;
3710 end;
3712 // Ñòàðòóåì ñåðâåð
3713 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3714 begin
3715 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3716 Exit;
3717 end;
3719 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3721 // Çàãðóçêà è çàïóñê êàðòû:
3722 if not g_Game_StartMap(Map, True) then
3723 begin
3724 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3725 Exit;
3726 end;
3728 // Íåò òî÷åê ïîÿâëåíèÿ:
3729 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3730 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3731 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3732 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3733 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3734 begin
3735 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3736 Exit;
3737 end;
3739 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3740 g_Player_Init();
3742 NetState := NET_STATE_GAME;
3743 end;
3745 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3746 var
3747 Map: String;
3748 WadName: string;
3749 Ptr: Pointer;
3750 T: Cardinal;
3751 MID: Byte;
3752 State: Byte;
3753 OuterLoop: Boolean;
3754 newResPath: string;
3755 begin
3756 g_Game_Free();
3758 State := 0;
3759 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3760 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3762 g_Game_ClearLoading();
3764 // Íàñòðîéêè èãðû:
3765 gGameSettings.GameType := GT_CLIENT;
3767 gCoopTotalMonstersKilled := 0;
3768 gCoopTotalSecretsFound := 0;
3769 gCoopTotalMonsters := 0;
3770 gCoopTotalSecrets := 0;
3771 gAimLine := False;
3772 gShowMap := False;
3774 g_Game_ExecuteEvent('ongamestart');
3776 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3777 g_Game_SetupScreenSize();
3779 NetState := NET_STATE_AUTH;
3781 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3782 // Ñòàðòóåì êëèåíò
3783 if not g_Net_Connect(Addr, Port) then
3784 begin
3785 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3786 NetState := NET_STATE_NONE;
3787 Exit;
3788 end;
3790 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3791 MC_SEND_Info(PW);
3792 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3794 OuterLoop := True;
3795 while OuterLoop do
3796 begin
3797 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3798 begin
3799 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3800 begin
3801 Ptr := NetEvent.packet^.data;
3802 e_Raw_Seek(0);
3804 MID := e_Raw_Read_Byte(Ptr);
3806 if (MID = NET_MSG_INFO) and (State = 0) then
3807 begin
3808 NetMyID := e_Raw_Read_Byte(Ptr);
3809 NetPlrUID1 := e_Raw_Read_Word(Ptr);
3811 WadName := e_Raw_Read_String(Ptr);
3812 Map := e_Raw_Read_String(Ptr);
3814 gWADHash := e_Raw_Read_MD5(Ptr);
3816 gGameSettings.GameMode := e_Raw_Read_Byte(Ptr);
3817 gSwitchGameMode := gGameSettings.GameMode;
3818 gGameSettings.GoalLimit := e_Raw_Read_Word(Ptr);
3819 gGameSettings.TimeLimit := e_Raw_Read_Word(Ptr);
3820 gGameSettings.MaxLives := e_Raw_Read_Byte(Ptr);
3821 gGameSettings.Options := e_Raw_Read_LongWord(Ptr);
3822 T := e_Raw_Read_LongWord(Ptr);
3824 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3825 if newResPath = '' then
3826 begin
3827 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3828 newResPath := g_Res_DownloadWAD(WadName);
3829 if newResPath = '' then
3830 begin
3831 g_FatalError(_lc[I_NET_ERR_HASH]);
3832 enet_packet_destroy(NetEvent.packet);
3833 NetState := NET_STATE_NONE;
3834 Exit;
3835 end;
3836 end;
3837 newResPath := ExtractRelativePath(MapsDir, newResPath);
3839 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3840 gPlayer1Settings.Color,
3841 gPlayer1Settings.Team, False));
3843 if gPlayer1 = nil then
3844 begin
3845 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3847 enet_packet_destroy(NetEvent.packet);
3848 NetState := NET_STATE_NONE;
3849 Exit;
3850 end;
3852 gPlayer1.Name := gPlayer1Settings.Name;
3853 gPlayer1.UID := NetPlrUID1;
3854 gPlayer1.Reset(True);
3856 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3857 begin
3858 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3860 enet_packet_destroy(NetEvent.packet);
3861 NetState := NET_STATE_NONE;
3862 Exit;
3863 end;
3865 gTime := T;
3867 State := 1;
3868 OuterLoop := False;
3869 enet_packet_destroy(NetEvent.packet);
3870 break;
3871 end
3872 else
3873 enet_packet_destroy(NetEvent.packet);
3874 end
3875 else
3876 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3877 begin
3878 State := 0;
3879 if (NetEvent.data <= NET_DISC_MAX) then
3880 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3881 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3882 OuterLoop := False;
3883 Break;
3884 end;
3885 end;
3887 ProcessLoading();
3889 e_PollInput();
3891 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3892 begin
3893 State := 0;
3894 break;
3895 end;
3896 end;
3898 if State <> 1 then
3899 begin
3900 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3901 NetState := NET_STATE_NONE;
3902 Exit;
3903 end;
3905 gLMSRespawn := LMS_RESPAWN_NONE;
3906 gLMSRespawnTime := 0;
3908 g_Player_Init();
3909 NetState := NET_STATE_GAME;
3910 MC_SEND_FullStateRequest;
3911 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3912 end;
3914 procedure g_Game_SaveOptions();
3915 begin
3916 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3917 end;
3919 procedure g_Game_ChangeMap(MapPath: String);
3920 var
3921 Force: Boolean;
3922 begin
3923 g_Game_ClearLoading();
3925 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3926 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3927 if gExitByTrigger then
3928 begin
3929 Force := False;
3930 gExitByTrigger := False;
3931 end;
3932 if not g_Game_StartMap(MapPath, Force) then
3933 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3934 end;
3936 procedure g_Game_Restart();
3937 var
3938 Map: string;
3939 begin
3940 if g_Game_IsClient then
3941 Exit;
3942 map := g_ExtractFileName(gMapInfo.Map);
3944 MessageTime := 0;
3945 gGameOn := False;
3946 g_Game_ClearLoading();
3947 g_Game_StartMap(Map, True);
3948 end;
3950 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
3951 var
3952 NewWAD, ResName: String;
3953 I: Integer;
3954 begin
3955 g_Map_Free();
3956 g_Player_RemoveAllCorpses();
3958 if (not g_Game_IsClient) and
3959 (gSwitchGameMode <> gGameSettings.GameMode) and
3960 (gGameSettings.GameMode <> GM_SINGLE) then
3961 begin
3962 if gSwitchGameMode = GM_CTF then
3963 gGameSettings.MaxLives := 0;
3964 gGameSettings.GameMode := gSwitchGameMode;
3965 Force := True;
3966 end else
3967 gSwitchGameMode := gGameSettings.GameMode;
3969 g_Player_ResetTeams();
3971 if Pos(':\', Map) > 0 then
3972 begin
3973 NewWAD := g_ExtractWadName(Map);
3974 ResName := g_ExtractFileName(Map);
3975 if g_Game_IsServer then
3976 begin
3977 gWADHash := MD5File(MapsDir + NewWAD);
3978 g_Game_LoadWAD(NewWAD);
3979 end else
3980 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
3981 g_Game_ClientWAD(NewWAD, gWADHash);
3982 end else
3983 ResName := Map;
3985 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
3986 if Result then
3987 begin
3988 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
3990 gState := STATE_NONE;
3991 g_ActiveWindow := nil;
3992 gGameOn := True;
3994 DisableCheats();
3995 ResetTimer();
3997 if gGameSettings.GameMode = GM_CTF then
3998 begin
3999 g_Map_ResetFlag(FLAG_RED);
4000 g_Map_ResetFlag(FLAG_BLUE);
4001 // CTF, à ôëàãîâ íåò:
4002 if not g_Map_HaveFlagPoints() then
4003 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4004 end;
4005 end
4006 else
4007 begin
4008 gState := STATE_MENU;
4009 gGameOn := False;
4010 end;
4012 gExit := 0;
4013 gPause := False;
4014 gTime := 0;
4015 NetTimeToUpdate := 1;
4016 NetTimeToReliable := 0;
4017 NetTimeToMaster := NetMasterRate;
4018 gLMSRespawn := LMS_RESPAWN_NONE;
4019 gLMSRespawnTime := 0;
4020 gMissionFailed := False;
4021 gNextMap := '';
4023 gCoopMonstersKilled := 0;
4024 gCoopSecretsFound := 0;
4026 gVoteInProgress := False;
4027 gVotePassed := False;
4028 gVoteCount := 0;
4029 gVoted := False;
4031 gStatsOff := False;
4033 if not gGameOn then Exit;
4035 g_Game_SpectateCenterView();
4037 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4038 begin
4039 gLMSRespawn := LMS_RESPAWN_WARMUP;
4040 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4041 gLMSSoftSpawn := True;
4042 if NetMode = NET_SERVER then
4043 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4044 else
4045 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4046 end;
4048 if NetMode = NET_SERVER then
4049 begin
4050 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4052 // Ìàñòåðñåðâåð
4053 if NetUseMaster then
4054 begin
4055 if (NetMHost = nil) or (NetMPeer = nil) then
4056 if not g_Net_Slist_Connect then
4057 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4059 g_Net_Slist_Update;
4060 end;
4062 if NetClients <> nil then
4063 for I := 0 to High(NetClients) do
4064 if NetClients[I].Used then
4065 begin
4066 NetClients[I].Voted := False;
4067 if NetClients[I].RequestedFullUpdate then
4068 begin
4069 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4070 NetClients[I].RequestedFullUpdate := False;
4071 end;
4072 end;
4074 g_Net_UnbanNonPermHosts();
4075 end;
4077 if gLastMap then
4078 begin
4079 gCoopTotalMonstersKilled := 0;
4080 gCoopTotalSecretsFound := 0;
4081 gCoopTotalMonsters := 0;
4082 gCoopTotalSecrets := 0;
4083 gLastMap := False;
4084 end;
4086 g_Game_ExecuteEvent('onmapstart');
4087 end;
4089 procedure SetFirstLevel();
4090 begin
4091 gNextMap := '';
4093 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4094 if MapList = nil then
4095 Exit;
4097 SortSArray(MapList);
4098 gNextMap := MapList[Low(MapList)];
4100 MapList := nil;
4101 end;
4103 procedure g_Game_ExitLevel(Map: Char16);
4104 begin
4105 gNextMap := Map;
4107 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4108 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4109 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4110 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4112 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4113 if gGameSettings.GameType = GT_SINGLE then
4114 gExit := EXIT_ENDLEVELSINGLE
4115 else // Âûøëè â âûõîä â Ñâîåé èãðå
4116 begin
4117 gExit := EXIT_ENDLEVELCUSTOM;
4118 if gGameSettings.GameMode = GM_COOP then
4119 g_Player_RememberAll;
4121 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4122 begin
4123 gLastMap := True;
4124 if gGameSettings.GameMode = GM_COOP then
4125 gStatsOff := True;
4127 gStatsPressed := True;
4128 gNextMap := 'MAP01';
4130 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4131 g_Game_NextLevel;
4133 if g_Game_IsNet then
4134 begin
4135 MH_SEND_GameStats();
4136 MH_SEND_CoopStats();
4137 end;
4138 end;
4139 end;
4140 end;
4142 procedure g_Game_RestartLevel();
4143 var
4144 Map: string;
4145 begin
4146 if gGameSettings.GameMode = GM_SINGLE then
4147 begin
4148 g_Game_Restart();
4149 Exit;
4150 end;
4151 gExit := EXIT_ENDLEVELCUSTOM;
4152 Map := g_ExtractFileName(gMapInfo.Map);
4153 gNextMap := Map;
4154 end;
4156 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4157 var
4158 gWAD: String;
4159 begin
4160 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4161 Exit;
4162 if not g_Game_IsClient then
4163 Exit;
4164 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4165 if gWAD = '' then
4166 begin
4167 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4168 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4169 if gWAD = '' then
4170 begin
4171 g_Game_Free();
4172 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4173 Exit;
4174 end;
4175 end;
4176 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4177 g_Game_LoadWAD(NewWAD);
4178 end;
4180 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4181 var
4182 i, n, nb, nr: Integer;
4183 begin
4184 if not g_Game_IsServer then Exit;
4185 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4186 gLMSRespawn := LMS_RESPAWN_NONE;
4187 gLMSRespawnTime := 0;
4188 MessageTime := 0;
4190 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4191 begin
4192 gMissionFailed := True;
4193 g_Game_RestartLevel;
4194 Exit;
4195 end;
4197 n := 0; nb := 0; nr := 0;
4198 for i := Low(gPlayers) to High(gPlayers) do
4199 if (gPlayers[i] <> nil) and
4200 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4201 (gPlayers[i] is TBot)) then
4202 begin
4203 Inc(n);
4204 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4205 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4206 end;
4208 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4209 begin
4210 // wait a second until the fuckers finally decide to join
4211 gLMSRespawn := LMS_RESPAWN_WARMUP;
4212 gLMSRespawnTime := gTime + 1000;
4213 gLMSSoftSpawn := NoMapRestart;
4214 Exit;
4215 end;
4217 g_Player_RemoveAllCorpses;
4218 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4219 if g_Game_IsNet then
4220 MH_SEND_GameEvent(NET_EV_LMS_START);
4222 for i := Low(gPlayers) to High(gPlayers) do
4223 begin
4224 if gPlayers[i] = nil then continue;
4225 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4226 // don't touch normal spectators
4227 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4228 begin
4229 gPlayers[i].FNoRespawn := True;
4230 gPlayers[i].Lives := 0;
4231 if g_Game_IsNet then
4232 MH_SEND_PlayerStats(gPlayers[I].UID);
4233 continue;
4234 end;
4235 gPlayers[i].FNoRespawn := False;
4236 gPlayers[i].Lives := gGameSettings.MaxLives;
4237 gPlayers[i].Respawn(False, True);
4238 if gGameSettings.GameMode = GM_COOP then
4239 begin
4240 gPlayers[i].Frags := 0;
4241 gPlayers[i].RecallState;
4242 end;
4243 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4244 gPlayer1 := g_Player_Get(gLMSPID1);
4245 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4246 gPlayer2 := g_Player_Get(gLMSPID2);
4247 end;
4249 for i := Low(gItems) to High(gItems) do
4250 begin
4251 if gItems[i].Respawnable then
4252 begin
4253 gItems[i].QuietRespawn := True;
4254 gItems[i].RespawnTime := 0;
4255 end
4256 else
4257 begin
4258 g_Items_Remove(i);
4259 if g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
4260 end;
4261 end;
4263 for i := Low(gMonsters) to High(gMonsters) do
4264 begin
4265 if (gMonsters[i] <> nil) and not gMonsters[i].FNoRespawn then
4266 gMonsters[i].Respawn;
4267 end;
4269 gLMSSoftSpawn := False;
4270 end;
4272 function g_Game_GetFirstMap(WAD: String): String;
4273 begin
4274 Result := '';
4276 MapList := g_Map_GetMapsList(WAD);
4277 if MapList = nil then
4278 Exit;
4280 SortSArray(MapList);
4281 Result := MapList[Low(MapList)];
4283 if not g_Map_Exist(WAD + ':\' + Result) then
4284 Result := '';
4286 MapList := nil;
4287 end;
4289 function g_Game_GetNextMap(): String;
4290 var
4291 I: Integer;
4292 Map: string;
4293 begin
4294 Result := '';
4296 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4297 if MapList = nil then
4298 Exit;
4300 Map := g_ExtractFileName(gMapInfo.Map);
4302 SortSArray(MapList);
4303 MapIndex := -255;
4304 for I := Low(MapList) to High(MapList) do
4305 if Map = MapList[I] then
4306 begin
4307 MapIndex := I;
4308 Break;
4309 end;
4311 if MapIndex <> -255 then
4312 begin
4313 if MapIndex = High(MapList) then
4314 Result := MapList[Low(MapList)]
4315 else
4316 Result := MapList[MapIndex + 1];
4318 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4319 end;
4321 MapList := nil;
4322 end;
4324 procedure g_Game_NextLevel();
4325 begin
4326 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4327 gExit := EXIT_ENDLEVELCUSTOM
4328 else
4329 begin
4330 gExit := EXIT_ENDLEVELSINGLE;
4331 Exit;
4332 end;
4334 if gNextMap <> '' then Exit;
4335 gNextMap := g_Game_GetNextMap();
4336 end;
4338 function g_Game_IsTestMap(): Boolean;
4339 begin
4340 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4341 end;
4343 procedure g_Game_DeleteTestMap();
4344 var
4345 a: Integer;
4346 MapName: Char16;
4347 WadName: string;
4349 WAD: TWADFile;
4350 MapList: SArray;
4351 time: Integer;
4353 begin
4354 a := Pos('.wad:\', gMapToDelete);
4355 if a = 0 then
4356 Exit;
4358 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4359 WadName := Copy(gMapToDelete, 1, a + 3);
4360 Delete(gMapToDelete, 1, a + 5);
4361 gMapToDelete := UpperCase(gMapToDelete);
4362 MapName := '';
4363 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4366 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4367 if MapName <> TEST_MAP_NAME then
4368 Exit;
4370 if not gTempDelete then
4371 begin
4372 time := g_GetFileTime(WadName);
4373 WAD := TWADFile.Create();
4375 // ×èòàåì Wad-ôàéë:
4376 if not WAD.ReadFile(WadName) then
4377 begin // Íåò òàêîãî WAD-ôàéëà
4378 WAD.Free();
4379 Exit;
4380 end;
4382 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4383 WAD.CreateImage();
4384 MapList := WAD.GetResourcesList('');
4386 if MapList <> nil then
4387 for a := 0 to High(MapList) do
4388 if MapList[a] = MapName then
4389 begin
4390 // Óäàëÿåì è ñîõðàíÿåì:
4391 WAD.RemoveResource('', MapName);
4392 WAD.SaveTo(WadName);
4393 Break;
4394 end;
4396 WAD.Free();
4397 g_SetFileTime(WadName, time);
4398 end else
4400 if gTempDelete then DeleteFile(WadName);
4401 end;
4403 procedure GameCVars(P: SArray);
4404 var
4405 a, b: Integer;
4406 stat: TPlayerStatArray;
4407 cmd, s: string;
4408 config: TConfig;
4409 begin
4410 stat := nil;
4411 cmd := LowerCase(P[0]);
4412 if cmd = 'r_showfps' then
4413 begin
4414 if (Length(P) > 1) and
4415 ((P[1] = '1') or (P[1] = '0')) then
4416 gShowFPS := (P[1][1] = '1');
4418 if gShowFPS then
4419 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4420 else
4421 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4422 end
4423 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4424 begin
4425 with gGameSettings do
4426 begin
4427 if (Length(P) > 1) and
4428 ((P[1] = '1') or (P[1] = '0')) then
4429 begin
4430 if (P[1][1] = '1') then
4431 Options := Options or GAME_OPTION_TEAMDAMAGE
4432 else
4433 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4434 end;
4436 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4437 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4438 else
4439 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4441 if g_Game_IsNet then MH_SEND_GameSettings;
4442 end;
4443 end
4444 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4445 begin
4446 with gGameSettings do
4447 begin
4448 if (Length(P) > 1) and
4449 ((P[1] = '1') or (P[1] = '0')) then
4450 begin
4451 if (P[1][1] = '1') then
4452 Options := Options or GAME_OPTION_WEAPONSTAY
4453 else
4454 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4455 end;
4457 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4458 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4459 else
4460 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4462 if g_Game_IsNet then MH_SEND_GameSettings;
4463 end;
4464 end
4465 else if cmd = 'g_gamemode' then
4466 begin
4467 a := g_Game_TextToMode(P[1]);
4468 if a = GM_SINGLE then a := GM_COOP;
4469 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4470 begin
4471 gSwitchGameMode := a;
4472 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4473 (gState = STATE_INTERSINGLE) then
4474 gSwitchGameMode := GM_SINGLE;
4475 if not gGameOn then
4476 gGameSettings.GameMode := gSwitchGameMode;
4477 end;
4478 if gSwitchGameMode = gGameSettings.GameMode then
4479 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4480 [g_Game_ModeToText(gGameSettings.GameMode)]))
4481 else
4482 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4483 [g_Game_ModeToText(gGameSettings.GameMode),
4484 g_Game_ModeToText(gSwitchGameMode)]));
4485 end
4486 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4487 begin
4488 with gGameSettings do
4489 begin
4490 if (Length(P) > 1) and
4491 ((P[1] = '1') or (P[1] = '0')) then
4492 begin
4493 if (P[1][1] = '1') then
4494 Options := Options or GAME_OPTION_ALLOWEXIT
4495 else
4496 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4497 end;
4499 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4500 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4501 else
4502 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4503 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4505 if g_Game_IsNet then MH_SEND_GameSettings;
4506 end;
4507 end
4508 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4509 begin
4510 with gGameSettings do
4511 begin
4512 if (Length(P) > 1) and
4513 ((P[1] = '1') or (P[1] = '0')) then
4514 begin
4515 if (P[1][1] = '1') then
4516 Options := Options or GAME_OPTION_MONSTERS
4517 else
4518 Options := Options and (not GAME_OPTION_MONSTERS);
4519 end;
4521 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4522 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4523 else
4524 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4525 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4527 if g_Game_IsNet then MH_SEND_GameSettings;
4528 end;
4529 end
4530 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4531 begin
4532 with gGameSettings do
4533 begin
4534 if (Length(P) > 1) and
4535 ((P[1] = '1') or (P[1] = '0')) then
4536 begin
4537 if (P[1][1] = '1') then
4538 Options := Options or GAME_OPTION_BOTVSPLAYER
4539 else
4540 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4541 end;
4543 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4544 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4545 else
4546 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4548 if g_Game_IsNet then MH_SEND_GameSettings;
4549 end;
4550 end
4551 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4552 begin
4553 with gGameSettings do
4554 begin
4555 if (Length(P) > 1) and
4556 ((P[1] = '1') or (P[1] = '0')) then
4557 begin
4558 if (P[1][1] = '1') then
4559 Options := Options or GAME_OPTION_BOTVSMONSTER
4560 else
4561 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4562 end;
4564 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4565 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4566 else
4567 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4569 if g_Game_IsNet then MH_SEND_GameSettings;
4570 end;
4571 end
4572 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4573 begin
4574 if Length(P) > 1 then
4575 begin
4576 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4577 gGameSettings.WarmupTime := 30
4578 else
4579 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4580 end;
4582 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4583 [gGameSettings.WarmupTime]));
4584 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4585 end
4586 else if cmd = 'net_interp' then
4587 begin
4588 if (Length(P) > 1) then
4589 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4591 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4592 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4593 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4594 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4595 config.Free();
4596 end
4597 else if cmd = 'net_forceplayerupdate' then
4598 begin
4599 if (Length(P) > 1) and
4600 ((P[1] = '1') or (P[1] = '0')) then
4601 NetForcePlayerUpdate := (P[1][1] = '1');
4603 if NetForcePlayerUpdate then
4604 g_Console_Add('net_forceplayerupdate = 1')
4605 else
4606 g_Console_Add('net_forceplayerupdate = 0');
4607 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4608 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4609 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4610 config.Free();
4611 end
4612 else if cmd = 'net_predictself' then
4613 begin
4614 if (Length(P) > 1) and
4615 ((P[1] = '1') or (P[1] = '0')) then
4616 NetPredictSelf := (P[1][1] = '1');
4618 if NetPredictSelf then
4619 g_Console_Add('net_predictself = 1')
4620 else
4621 g_Console_Add('net_predictself = 0');
4622 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4623 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4624 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4625 config.Free();
4626 end
4627 else if cmd = 'sv_name' then
4628 begin
4629 if (Length(P) > 1) and (Length(P[1]) > 0) then
4630 begin
4631 NetServerName := P[1];
4632 if Length(NetServerName) > 64 then
4633 SetLength(NetServerName, 64);
4634 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4635 g_Net_Slist_Update;
4636 end;
4638 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4639 end
4640 else if cmd = 'sv_passwd' then
4641 begin
4642 if (Length(P) > 1) and (Length(P[1]) > 0) then
4643 begin
4644 NetPassword := P[1];
4645 if Length(NetPassword) > 24 then
4646 SetLength(NetPassword, 24);
4647 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4648 g_Net_Slist_Update;
4649 end;
4651 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4652 end
4653 else if cmd = 'sv_maxplrs' then
4654 begin
4655 if (Length(P) > 1) then
4656 begin
4657 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4658 if g_Game_IsServer and g_Game_IsNet then
4659 begin
4660 b := 0;
4661 for a := 0 to High(NetClients) do
4662 if NetClients[a].Used then
4663 begin
4664 Inc(b);
4665 if b > NetMaxClients then
4666 begin
4667 s := g_Player_Get(NetClients[a].Player).Name;
4668 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4669 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4670 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4671 end;
4672 end;
4673 if NetUseMaster then
4674 g_Net_Slist_Update;
4675 end;
4676 end;
4678 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4679 end
4680 else if cmd = 'sv_public' then
4681 begin
4682 if (Length(P) > 1) then
4683 begin
4684 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4685 if g_Game_IsServer and g_Game_IsNet then
4686 if NetUseMaster then
4687 begin
4688 if NetMPeer = nil then
4689 if not g_Net_Slist_Connect() then
4690 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4691 g_Net_Slist_Update();
4692 end
4693 else
4694 if NetMPeer <> nil then
4695 g_Net_Slist_Disconnect();
4696 end;
4698 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4699 end
4700 else if cmd = 'sv_intertime' then
4701 begin
4702 if (Length(P) > 1) then
4703 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4705 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4706 end
4707 else if cmd = 'p1_name' then
4708 begin
4709 if (Length(P) > 1) and gGameOn then
4710 begin
4711 if g_Game_IsClient then
4712 begin
4713 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4714 MC_SEND_PlayerSettings;
4715 end
4716 else
4717 if gPlayer1 <> nil then
4718 begin
4719 gPlayer1.Name := b_Text_Unformat(P[1]);
4720 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4721 end
4722 else
4723 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4724 end;
4725 end
4726 else if cmd = 'p2_name' then
4727 begin
4728 if (Length(P) > 1) and gGameOn then
4729 begin
4730 if g_Game_IsClient then
4731 begin
4732 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4733 MC_SEND_PlayerSettings;
4734 end
4735 else
4736 if gPlayer2 <> nil then
4737 begin
4738 gPlayer2.Name := b_Text_Unformat(P[1]);
4739 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4740 end
4741 else
4742 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4743 end;
4744 end
4745 else if cmd = 'p1_color' then
4746 begin
4747 if Length(P) > 3 then
4748 if g_Game_IsClient then
4749 begin
4750 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4751 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4752 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4753 MC_SEND_PlayerSettings;
4754 end
4755 else
4756 if gPlayer1 <> nil then
4757 begin
4758 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4759 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4760 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4761 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4762 end
4763 else
4764 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4765 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4766 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4767 end
4768 else if (cmd = 'p2_color') and not g_Game_IsNet then
4769 begin
4770 if Length(P) > 3 then
4771 if g_Game_IsClient then
4772 begin
4773 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4774 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4775 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4776 MC_SEND_PlayerSettings;
4777 end
4778 else
4779 if gPlayer2 <> nil then
4780 begin
4781 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4782 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4783 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4784 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4785 end
4786 else
4787 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4788 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4789 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4790 end
4791 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4792 begin
4793 if cmd = 'r_showtime' then
4794 begin
4795 if (Length(P) > 1) and
4796 ((P[1] = '1') or (P[1] = '0')) then
4797 gShowTime := (P[1][1] = '1');
4799 if gShowTime then
4800 g_Console_Add(_lc[I_MSG_TIME_ON])
4801 else
4802 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4803 end
4804 else if cmd = 'r_showscore' then
4805 begin
4806 if (Length(P) > 1) and
4807 ((P[1] = '1') or (P[1] = '0')) then
4808 gShowGoals := (P[1][1] = '1');
4810 if gShowGoals then
4811 g_Console_Add(_lc[I_MSG_SCORE_ON])
4812 else
4813 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4814 end
4815 else if cmd = 'r_showstat' then
4816 begin
4817 if (Length(P) > 1) and
4818 ((P[1] = '1') or (P[1] = '0')) then
4819 gShowStat := (P[1][1] = '1');
4821 if gShowStat then
4822 g_Console_Add(_lc[I_MSG_STATS_ON])
4823 else
4824 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4825 end
4826 else if cmd = 'r_showkillmsg' then
4827 begin
4828 if (Length(P) > 1) and
4829 ((P[1] = '1') or (P[1] = '0')) then
4830 gShowKillMsg := (P[1][1] = '1');
4832 if gShowKillMsg then
4833 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4834 else
4835 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4836 end
4837 else if cmd = 'r_showlives' then
4838 begin
4839 if (Length(P) > 1) and
4840 ((P[1] = '1') or (P[1] = '0')) then
4841 gShowLives := (P[1][1] = '1');
4843 if gShowLives then
4844 g_Console_Add(_lc[I_MSG_LIVES_ON])
4845 else
4846 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4847 end
4848 else if cmd = 'r_showspect' then
4849 begin
4850 if (Length(P) > 1) and
4851 ((P[1] = '1') or (P[1] = '0')) then
4852 gSpectHUD := (P[1][1] = '1');
4854 if gSpectHUD then
4855 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4856 else
4857 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4858 end
4859 else if cmd = 'r_showping' then
4860 begin
4861 if (Length(P) > 1) and
4862 ((P[1] = '1') or (P[1] = '0')) then
4863 gShowPing := (P[1][1] = '1');
4865 if gShowPing then
4866 g_Console_Add(_lc[I_MSG_PING_ON])
4867 else
4868 g_Console_Add(_lc[I_MSG_PING_OFF]);
4869 end
4870 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4871 begin
4872 if Length(P) > 1 then
4873 begin
4874 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4875 gGameSettings.GoalLimit := 0
4876 else
4877 begin
4878 b := 0;
4880 if gGameSettings.GameMode = GM_DM then
4881 begin // DM
4882 stat := g_Player_GetStats();
4883 if stat <> nil then
4884 for a := 0 to High(stat) do
4885 if stat[a].Frags > b then
4886 b := stat[a].Frags;
4887 end
4888 else // TDM/CTF
4889 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4891 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4892 end;
4894 if g_Game_IsNet then MH_SEND_GameSettings;
4895 end;
4897 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4898 end
4899 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4900 begin
4901 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4902 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4904 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4905 [gGameSettings.TimeLimit div 3600,
4906 (gGameSettings.TimeLimit div 60) mod 60,
4907 gGameSettings.TimeLimit mod 60]));
4908 if g_Game_IsNet then MH_SEND_GameSettings;
4909 end
4910 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4911 begin
4912 if Length(P) > 1 then
4913 begin
4914 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4915 gGameSettings.MaxLives := 0
4916 else
4917 begin
4918 b := 0;
4919 stat := g_Player_GetStats();
4920 if stat <> nil then
4921 for a := 0 to High(stat) do
4922 if stat[a].Lives > b then
4923 b := stat[a].Lives;
4924 gGameSettings.MaxLives :=
4925 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4926 end;
4927 end;
4929 g_Console_Add(Format(_lc[I_MSG_LIVES],
4930 [gGameSettings.MaxLives]));
4931 if g_Game_IsNet then MH_SEND_GameSettings;
4932 end;
4933 end;
4934 end;
4936 // profiler console commands
4937 procedure ProfilerCommands (P: SArray);
4938 var
4939 cmd: string;
4940 begin
4941 //if not gDebugMode then exit;
4942 cmd := LowerCase(P[0]);
4943 if cmd = 'dpp' then
4944 begin
4945 g_profile_frame_draw := not g_profile_frame_draw;
4946 exit;
4947 end;
4948 if cmd = 'dpu' then
4949 begin
4950 g_profile_frame_update := not g_profile_frame_update;
4951 exit;
4952 end;
4953 end;
4955 procedure DebugCommands(P: SArray);
4956 var
4957 a, b: Integer;
4958 cmd: string;
4959 //pt: TPoint;
4960 begin
4961 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
4962 if gDebugMode then
4963 begin
4964 cmd := LowerCase(P[0]);
4965 if cmd = 'd_window' then
4966 begin
4967 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
4968 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
4969 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
4970 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
4971 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
4972 end
4973 else if cmd = 'd_sounds' then
4974 begin
4975 if (Length(P) > 1) and
4976 ((P[1] = '1') or (P[1] = '0')) then
4977 g_Debug_Sounds := (P[1][1] = '1');
4979 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
4980 end
4981 else if cmd = 'd_frames' then
4982 begin
4983 if (Length(P) > 1) and
4984 ((P[1] = '1') or (P[1] = '0')) then
4985 g_Debug_Frames := (P[1][1] = '1');
4987 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
4988 end
4989 else if cmd = 'd_winmsg' then
4990 begin
4991 if (Length(P) > 1) and
4992 ((P[1] = '1') or (P[1] = '0')) then
4993 g_Debug_WinMsgs := (P[1][1] = '1');
4995 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
4996 end
4997 else if (cmd = 'd_monoff') and not g_Game_IsNet then
4998 begin
4999 if (Length(P) > 1) and
5000 ((P[1] = '1') or (P[1] = '0')) then
5001 g_Debug_MonsterOff := (P[1][1] = '1');
5003 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5004 end
5005 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5006 begin
5007 if Length(P) > 1 then
5008 case P[1][1] of
5009 '0': g_debug_BotAIOff := 0;
5010 '1': g_debug_BotAIOff := 1;
5011 '2': g_debug_BotAIOff := 2;
5012 '3': g_debug_BotAIOff := 3;
5013 end;
5015 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5016 end
5017 else if cmd = 'd_monster' then
5018 begin
5019 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
5020 if Length(P) < 2 then
5021 begin
5022 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5023 g_Console_Add('ID | Name');
5024 for b := MONSTER_DEMON to MONSTER_MAN do
5025 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
5026 end else
5027 begin
5028 a := StrToIntDef(P[1], 0);
5029 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5030 a := g_Monsters_GetIDByName(P[1]);
5032 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5033 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5034 else
5035 begin
5036 with gPlayer1.Obj do
5037 b := g_Monsters_Create(a,
5038 X + Rect.X + (Rect.Width div 2),
5039 Y + Rect.Y + Rect.Height,
5040 gPlayer1.Direction, True);
5041 if (Length(P) > 2) and (b >= 0) then
5042 gMonsters[b].MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5043 end;
5044 end;
5045 end
5046 else if (cmd = 'd_health') then
5047 begin
5048 if (Length(P) > 1) and
5049 ((P[1] = '1') or (P[1] = '0')) then
5050 g_debug_HealthBar := (P[1][1] = '1');
5052 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5053 end
5054 else if (cmd = 'd_player') then
5055 begin
5056 if (Length(P) > 1) and
5057 ((P[1] = '1') or (P[1] = '0')) then
5058 g_debug_Player := (P[1][1] = '1');
5060 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5061 end
5062 else if (cmd = 'd_joy') then
5063 begin
5064 for a := 1 to 8 do
5065 g_Console_Add(e_JoystickStateToString(a));
5066 end;
5067 end
5068 else
5069 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5070 end;
5073 procedure GameCheats(P: SArray);
5074 var
5075 cmd: string;
5076 f, a: Integer;
5077 plr: TPlayer;
5078 begin
5079 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5080 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5081 begin
5082 g_Console_Add('not available');
5083 exit;
5084 end;
5085 plr := gPlayer1;
5086 if plr = nil then
5087 begin
5088 g_Console_Add('where is the player?!');
5089 exit;
5090 end;
5091 cmd := LowerCase(P[0]);
5092 // god
5093 if cmd = 'god' then
5094 begin
5095 plr.GodMode := not plr.GodMode;
5096 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5097 exit;
5098 end;
5099 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5100 if cmd = 'give' then
5101 begin
5102 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5103 for f := 1 to High(P) do
5104 begin
5105 cmd := LowerCase(P[f]);
5106 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5107 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5108 if cmd = 'exit' then
5109 begin
5110 if gTriggers <> nil then
5111 begin
5112 for a := 0 to High(gTriggers) do
5113 begin
5114 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5115 begin
5116 g_Console_Add('player left the map');
5117 gExitByTrigger := True;
5118 g_Game_ExitLevel(gTriggers[a].Data.MapName);
5119 break;
5120 end;
5121 end;
5122 end;
5123 continue;
5124 end;
5126 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5127 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5128 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5129 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5130 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5132 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5133 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5135 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5136 if (cmd = 'medkit') or (cmd = 'medikit') or (cmd = 'medpack') or (cmd = 'medipack') then begin plr.GiveItem(ITEM_MEDKIT_LARGE); g_Console_Add('player got a '+cmd); continue; end;
5138 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5139 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5141 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5142 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5144 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5145 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5147 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5148 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5149 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5151 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5152 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5153 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5154 if (cmd = 'launcher') or (cmd = 'rocketlauncher') or (cmd = 'rl') then begin plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER); g_Console_Add('player got a rocket launcher'); continue; end;
5155 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5156 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5158 if (cmd = 'shotgunzz') or (cmd = 'sgzz') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a shotgun'); continue; end;
5159 if (cmd = 'supershotgunzz') or (cmd = 'ssgzz') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a supershotgun'); continue; end;
5160 if cmd = 'chaingunzz' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a chaingun'); continue; end;
5161 if (cmd = 'launcherzz') or (cmd = 'rocketlauncherzz') or (cmd = 'rlzz') then begin plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER); plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got a rocket launcher'); continue; end;
5162 if cmd = 'plasmagunzz' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got a plasma gun'); continue; end;
5163 if cmd = 'bfgzz' then begin plr.GiveItem(ITEM_WEAPON_BFG); plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got a BFG-9000'); continue; end;
5165 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5166 if cmd = 'superchaingunzz' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a superchaingun'); continue; end;
5168 if (cmd = 'flamer') or (cmd = 'flamethrower') or (cmd = 'ft') then begin plr.GiveItem(ITEM_WEAPON_FLAMETHROWER); g_Console_Add('player got a flame thrower'); continue; end;
5169 if (cmd = 'flamerzz') or (cmd = 'flamethrowerzz') or (cmd = 'ftzz') then begin plr.GiveItem(ITEM_WEAPON_FLAMETHROWER); plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got a flame thrower'); continue; end;
5171 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5173 if cmd = 'ammo' then
5174 begin
5175 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5176 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5177 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5178 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5179 plr.GiveItem(ITEM_AMMO_FUELCAN);
5180 g_Console_Add('player got some ammo');
5181 continue;
5182 end;
5184 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5185 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5187 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5188 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5190 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5191 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5193 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5194 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5196 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5198 if cmd = 'weapons' then
5199 begin
5200 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5201 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5202 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5203 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5204 plr.GiveItem(ITEM_WEAPON_PLASMA);
5205 plr.GiveItem(ITEM_WEAPON_BFG);
5206 g_Console_Add('player got weapons');
5207 continue;
5208 end;
5210 if cmd = 'keys' then
5211 begin
5212 plr.GiveItem(ITEM_KEY_RED);
5213 plr.GiveItem(ITEM_KEY_GREEN);
5214 plr.GiveItem(ITEM_KEY_BLUE);
5215 g_Console_Add('player got all keys');
5216 continue;
5217 end;
5219 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5220 end;
5221 exit;
5222 end;
5223 // open
5224 if cmd = 'open' then
5225 begin
5226 g_Console_Add('player activated sesame');
5227 g_Triggers_OpenAll();
5228 exit;
5229 end;
5230 // fly
5231 if cmd = 'fly' then
5232 begin
5233 gFly := not gFly;
5234 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5235 exit;
5236 end;
5237 // noclip
5238 if cmd = 'noclip' then
5239 begin
5240 plr.SwitchNoClip;
5241 g_Console_Add('wall hardeness adjusted');
5242 exit;
5243 end;
5244 // notarget
5245 if cmd = 'notarget' then
5246 begin
5247 plr.NoTarget := not plr.NoTarget;
5248 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5249 exit;
5250 end;
5251 // noreload
5252 if cmd = 'noreload' then
5253 begin
5254 plr.NoReload := not plr.NoReload;
5255 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5256 exit;
5257 end;
5258 // speedy
5259 if cmd = 'speedy' then
5260 begin
5261 MAX_RUNVEL := 32-MAX_RUNVEL;
5262 g_Console_Add('speed adjusted');
5263 exit;
5264 end;
5265 // jumpy
5266 if cmd = 'jumpy' then
5267 begin
5268 VEL_JUMP := 30-VEL_JUMP;
5269 g_Console_Add('jump height adjusted');
5270 exit;
5271 end;
5272 // automap
5273 if cmd = 'automap' then
5274 begin
5275 gShowMap := not gShowMap;
5276 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5277 exit;
5278 end;
5279 // aimline
5280 if cmd = 'aimline' then
5281 begin
5282 gAimLine := not gAimLine;
5283 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5284 exit;
5285 end;
5286 end;
5288 procedure GameCommands(P: SArray);
5289 var
5290 a, b: Integer;
5291 s, pw: String;
5292 chstr: string;
5293 cmd: string;
5294 pl: pTNetClient = nil;
5295 plr: TPlayer;
5296 prt: Word;
5297 nm: Boolean;
5298 listen: LongWord;
5299 begin
5300 // Îáùèå êîìàíäû:
5301 cmd := LowerCase(P[0]);
5302 chstr := '';
5303 if (cmd = 'quit') or
5304 (cmd = 'exit') then
5305 begin
5306 g_Game_Free();
5307 g_Game_Quit();
5308 Exit;
5309 end
5310 else if cmd = 'pause' then
5311 begin
5312 if (g_ActiveWindow = nil) then
5313 g_Game_Pause(not gPause);
5314 end
5315 else if cmd = 'endgame' then
5316 gExit := EXIT_SIMPLE
5317 else if cmd = 'restart' then
5318 begin
5319 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5320 begin
5321 if g_Game_IsClient then
5322 begin
5323 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5324 Exit;
5325 end;
5326 g_Game_Restart();
5327 end else
5328 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5329 end
5330 else if cmd = 'kick' then
5331 begin
5332 if g_Game_IsServer then
5333 begin
5334 if Length(P) < 2 then
5335 begin
5336 g_Console_Add('kick <name>');
5337 Exit;
5338 end;
5339 if P[1] = '' then
5340 begin
5341 g_Console_Add('kick <name>');
5342 Exit;
5343 end;
5345 if g_Game_IsNet then
5346 pl := g_Net_Client_ByName(P[1]);
5347 if (pl <> nil) then
5348 begin
5349 s := g_Net_ClientName_ByID(pl^.ID);
5350 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5351 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5352 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5353 if NetUseMaster then
5354 g_Net_Slist_Update;
5355 end else if gPlayers <> nil then
5356 for a := Low(gPlayers) to High(gPlayers) do
5357 if gPlayers[a] <> nil then
5358 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5359 begin
5360 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5361 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5362 continue;
5363 gPlayers[a].Lives := 0;
5364 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5365 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5366 g_Player_Remove(gPlayers[a].UID);
5367 if NetUseMaster then
5368 g_Net_Slist_Update;
5369 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5370 g_Bot_MixNames();
5371 end;
5372 end else
5373 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5374 end
5375 else if cmd = 'kick_id' then
5376 begin
5377 if g_Game_IsServer and g_Game_IsNet then
5378 begin
5379 if Length(P) < 2 then
5380 begin
5381 g_Console_Add('kick_id <client ID>');
5382 Exit;
5383 end;
5384 if P[1] = '' then
5385 begin
5386 g_Console_Add('kick_id <client ID>');
5387 Exit;
5388 end;
5390 a := StrToIntDef(P[1], 0);
5391 if (NetClients <> nil) and (a <= High(NetClients)) then
5392 begin
5393 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5394 begin
5395 s := g_Net_ClientName_ByID(NetClients[a].ID);
5396 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5397 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5398 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5399 if NetUseMaster then
5400 g_Net_Slist_Update;
5401 end;
5402 end;
5403 end else
5404 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5405 end
5406 else if cmd = 'ban' then
5407 begin
5408 if g_Game_IsServer and g_Game_IsNet then
5409 begin
5410 if Length(P) < 2 then
5411 begin
5412 g_Console_Add('ban <name>');
5413 Exit;
5414 end;
5415 if P[1] = '' then
5416 begin
5417 g_Console_Add('ban <name>');
5418 Exit;
5419 end;
5421 pl := g_Net_Client_ByName(P[1]);
5422 if (pl <> nil) then
5423 begin
5424 s := g_Net_ClientName_ByID(pl^.ID);
5425 g_Net_BanHost(pl^.Peer^.address.host, False);
5426 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5427 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5428 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5429 if NetUseMaster then
5430 g_Net_Slist_Update;
5431 end else
5432 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5433 end else
5434 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5435 end
5436 else if cmd = 'ban_id' then
5437 begin
5438 if g_Game_IsServer and g_Game_IsNet then
5439 begin
5440 if Length(P) < 2 then
5441 begin
5442 g_Console_Add('ban_id <client ID>');
5443 Exit;
5444 end;
5445 if P[1] = '' then
5446 begin
5447 g_Console_Add('ban_id <client ID>');
5448 Exit;
5449 end;
5451 a := StrToIntDef(P[1], 0);
5452 if (NetClients <> nil) and (a <= High(NetClients)) then
5453 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5454 begin
5455 s := g_Net_ClientName_ByID(NetClients[a].ID);
5456 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5457 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5458 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5459 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5460 if NetUseMaster then
5461 g_Net_Slist_Update;
5462 end;
5463 end else
5464 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5465 end
5466 else if cmd = 'permban' then
5467 begin
5468 if g_Game_IsServer and g_Game_IsNet then
5469 begin
5470 if Length(P) < 2 then
5471 begin
5472 g_Console_Add('permban <name>');
5473 Exit;
5474 end;
5475 if P[1] = '' then
5476 begin
5477 g_Console_Add('permban <name>');
5478 Exit;
5479 end;
5481 pl := g_Net_Client_ByName(P[1]);
5482 if (pl <> nil) then
5483 begin
5484 s := g_Net_ClientName_ByID(pl^.ID);
5485 g_Net_BanHost(pl^.Peer^.address.host);
5486 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5487 g_Net_SaveBanList();
5488 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5489 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5490 if NetUseMaster then
5491 g_Net_Slist_Update;
5492 end else
5493 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5494 end else
5495 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5496 end
5497 else if cmd = 'permban_id' then
5498 begin
5499 if g_Game_IsServer and g_Game_IsNet then
5500 begin
5501 if Length(P) < 2 then
5502 begin
5503 g_Console_Add('permban_id <client ID>');
5504 Exit;
5505 end;
5506 if P[1] = '' then
5507 begin
5508 g_Console_Add('permban_id <client ID>');
5509 Exit;
5510 end;
5512 a := StrToIntDef(P[1], 0);
5513 if (NetClients <> nil) and (a <= High(NetClients)) then
5514 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5515 begin
5516 s := g_Net_ClientName_ByID(NetClients[a].ID);
5517 g_Net_BanHost(NetClients[a].Peer^.address.host);
5518 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5519 g_Net_SaveBanList();
5520 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5521 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5522 if NetUseMaster then
5523 g_Net_Slist_Update;
5524 end;
5525 end else
5526 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5527 end
5528 else if cmd = 'unban' then
5529 begin
5530 if g_Game_IsServer and g_Game_IsNet then
5531 begin
5532 if Length(P) < 2 then
5533 begin
5534 g_Console_Add('unban <IP Address>');
5535 Exit;
5536 end;
5537 if P[1] = '' then
5538 begin
5539 g_Console_Add('unban <IP Address>');
5540 Exit;
5541 end;
5543 if g_Net_UnbanHost(P[1]) then
5544 begin
5545 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5546 g_Net_SaveBanList();
5547 end else
5548 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5549 end else
5550 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5551 end
5552 else if cmd = 'clientlist' then
5553 begin
5554 if g_Game_IsServer and g_Game_IsNet then
5555 begin
5556 b := 0;
5557 if NetClients <> nil then
5558 for a := Low(NetClients) to High(NetClients) do
5559 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5560 begin
5561 plr := g_Player_Get(NetClients[a].Player);
5562 if plr = nil then continue;
5563 Inc(b);
5564 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5565 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5566 end;
5567 if b = 0 then
5568 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5569 end else
5570 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5571 end
5572 else if cmd = 'connect' then
5573 begin
5574 if (NetMode = NET_NONE) then
5575 begin
5576 if Length(P) < 2 then
5577 begin
5578 g_Console_Add('connect <IP> [port] [password]');
5579 Exit;
5580 end;
5581 if P[1] = '' then
5582 begin
5583 g_Console_Add('connect <IP> [port] [password]');
5584 Exit;
5585 end;
5587 if Length(P) > 2 then
5588 prt := StrToIntDef(P[2], 25666)
5589 else
5590 prt := 25666;
5592 if Length(P) > 3 then
5593 pw := P[3]
5594 else
5595 pw := '';
5597 g_Game_StartClient(P[1], prt, pw);
5598 end;
5599 end
5600 else if cmd = 'disconnect' then
5601 begin
5602 if (NetMode = NET_CLIENT) then
5603 g_Net_Disconnect();
5604 end
5605 else if cmd = 'reconnect' then
5606 begin
5607 if (NetMode = NET_SERVER) then
5608 Exit;
5610 if (NetMode = NET_CLIENT) then
5611 begin
5612 g_Net_Disconnect();
5613 gExit := EXIT_SIMPLE;
5614 EndGame;
5615 end;
5617 //TODO: Use last successful password to reconnect, instead of ''
5618 g_Game_StartClient(NetClientIP, NetClientPort, '');
5619 end
5620 else if (cmd = 'addbot') or
5621 (cmd = 'bot_add') then
5622 begin
5623 if Length(P) > 1 then
5624 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5625 else
5626 g_Bot_Add(TEAM_NONE, 2);
5627 end
5628 else if cmd = 'bot_addlist' then
5629 begin
5630 if Length(P) > 1 then
5631 if Length(P) = 2 then
5632 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5633 else
5634 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5635 end
5636 else if cmd = 'bot_removeall' then
5637 g_Bot_RemoveAll()
5638 else if cmd = 'chat' then
5639 begin
5640 if g_Game_IsNet then
5641 begin
5642 if Length(P) > 1 then
5643 begin
5644 for a := 1 to High(P) do
5645 chstr := chstr + P[a] + ' ';
5647 if Length(chstr) > 200 then SetLength(chstr, 200);
5649 if Length(chstr) < 1 then
5650 begin
5651 g_Console_Add('chat <text>');
5652 Exit;
5653 end;
5655 chstr := b_Text_Format(chstr);
5656 if g_Game_IsClient then
5657 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5658 else
5659 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5660 end
5661 else
5662 g_Console_Add('chat <text>');
5663 end else
5664 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5665 end
5666 else if cmd = 'teamchat' then
5667 begin
5668 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5669 begin
5670 if Length(P) > 1 then
5671 begin
5672 for a := 1 to High(P) do
5673 chstr := chstr + P[a] + ' ';
5675 if Length(chstr) > 200 then SetLength(chstr, 200);
5677 if Length(chstr) < 1 then
5678 begin
5679 g_Console_Add('teamchat <text>');
5680 Exit;
5681 end;
5683 chstr := b_Text_Format(chstr);
5684 if g_Game_IsClient then
5685 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5686 else
5687 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5688 gPlayer1Settings.Team);
5689 end
5690 else
5691 g_Console_Add('teamchat <text>');
5692 end else
5693 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5694 end
5695 else if cmd = 'game' then
5696 begin
5697 if gGameSettings.GameType <> GT_NONE then
5698 begin
5699 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5700 Exit;
5701 end;
5702 if Length(P) = 1 then
5703 begin
5704 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5705 Exit;
5706 end;
5707 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5708 P[1] := addWadExtension(P[1]);
5709 if FileExists(MapsDir + P[1]) then
5710 begin
5711 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5712 if Length(P) < 3 then
5713 begin
5714 SetLength(P, 3);
5715 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5716 end;
5718 s := P[1] + ':\' + UpperCase(P[2]);
5720 if g_Map_Exist(MapsDir + s) then
5721 begin
5722 // Çàïóñêàåì ñâîþ èãðó
5723 g_Game_Free();
5724 with gGameSettings do
5725 begin
5726 GameMode := g_Game_TextToMode(gcGameMode);
5727 if gSwitchGameMode <> GM_NONE then
5728 GameMode := gSwitchGameMode;
5729 if GameMode = GM_NONE then GameMode := GM_DM;
5730 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5731 b := 1;
5732 if Length(P) >= 4 then
5733 b := StrToIntDef(P[3], 1);
5734 g_Game_StartCustom(s, GameMode, TimeLimit,
5735 GoalLimit, MaxLives, Options, b);
5736 end;
5737 end
5738 else
5739 if P[2] = '' then
5740 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5741 else
5742 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5743 end else
5744 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5745 end
5746 else if cmd = 'host' then
5747 begin
5748 if gGameSettings.GameType <> GT_NONE then
5749 begin
5750 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5751 Exit;
5752 end;
5753 if Length(P) < 4 then
5754 begin
5755 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5756 Exit;
5757 end;
5758 if not StrToIp(P[1], listen) then
5759 Exit;
5760 prt := StrToIntDef(P[2], 25666);
5762 P[3] := addWadExtension(P[3]);
5763 if FileExists(MapsDir + P[3]) then
5764 begin
5765 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5766 if Length(P) < 5 then
5767 begin
5768 SetLength(P, 5);
5769 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5770 end;
5772 s := P[3] + ':\' + UpperCase(P[4]);
5774 if g_Map_Exist(MapsDir + s) then
5775 begin
5776 // Çàïóñêàåì ñâîþ èãðó
5777 g_Game_Free();
5778 with gGameSettings do
5779 begin
5780 GameMode := g_Game_TextToMode(gcGameMode);
5781 if gSwitchGameMode <> GM_NONE then
5782 GameMode := gSwitchGameMode;
5783 if GameMode = GM_NONE then GameMode := GM_DM;
5784 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5785 b := 0;
5786 if Length(P) >= 6 then
5787 b := StrToIntDef(P[5], 0);
5788 g_Game_StartServer(s, GameMode, TimeLimit,
5789 GoalLimit, MaxLives, Options, b, listen, prt);
5790 end;
5791 end
5792 else
5793 if P[4] = '' then
5794 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5795 else
5796 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5797 end else
5798 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5799 end
5800 else if cmd = 'map' then
5801 begin
5802 if Length(P) = 1 then
5803 begin
5804 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5805 begin
5806 g_Console_Add(cmd + ' <MAP>');
5807 g_Console_Add(cmd + ' <WAD> [MAP]');
5808 end else
5809 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5810 end else
5811 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5812 begin
5813 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5814 if Length(P) < 3 then
5815 begin
5816 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5817 s := UpperCase(P[1]);
5818 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5819 begin // Êàðòà íàøëàñü
5820 gExitByTrigger := False;
5821 if gGameOn then
5822 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5823 gNextMap := s;
5824 gExit := EXIT_ENDLEVELCUSTOM;
5825 end
5826 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5827 g_Game_ChangeMap(s);
5828 end else
5829 begin
5830 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5831 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5832 P[1] := addWadExtension(P[1]);
5833 if FileExists(MapsDir + P[1]) then
5834 begin
5835 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5836 SetLength(P, 3);
5837 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5839 s := P[1] + ':\' + P[2];
5841 if g_Map_Exist(MapsDir + s) then
5842 begin
5843 gExitByTrigger := False;
5844 if gGameOn then
5845 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5846 gNextMap := s;
5847 gExit := EXIT_ENDLEVELCUSTOM;
5848 end
5849 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5850 g_Game_ChangeMap(s);
5851 end else
5852 if P[2] = '' then
5853 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5854 else
5855 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5856 end else
5857 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5858 end;
5859 end else
5860 begin
5861 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5862 P[1] := addWadExtension(P[1]);
5863 if FileExists(MapsDir + P[1]) then
5864 begin
5865 // Íàøëè WAD ôàéë
5866 P[2] := UpperCase(P[2]);
5867 s := P[1] + ':\' + P[2];
5869 if g_Map_Exist(MapsDir + s) then
5870 begin // Íàøëè êàðòó
5871 gExitByTrigger := False;
5872 if gGameOn then
5873 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5874 gNextMap := s;
5875 gExit := EXIT_ENDLEVELCUSTOM;
5876 end
5877 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5878 g_Game_ChangeMap(s);
5879 end else
5880 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5881 end else
5882 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5883 end;
5884 end else
5885 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5886 end
5887 else if cmd = 'nextmap' then
5888 begin
5889 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5890 g_Console_Add(_lc[I_MSG_NOT_GAME])
5891 else begin
5892 nm := True;
5893 if Length(P) = 1 then
5894 begin
5895 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5896 begin
5897 g_Console_Add(cmd + ' <MAP>');
5898 g_Console_Add(cmd + ' <WAD> [MAP]');
5899 end else begin
5900 nm := False;
5901 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5902 end;
5903 end else
5904 begin
5905 nm := False;
5906 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5907 begin
5908 if Length(P) < 3 then
5909 begin
5910 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5911 s := UpperCase(P[1]);
5912 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5913 begin // Êàðòà íàøëàñü
5914 gExitByTrigger := False;
5915 gNextMap := s;
5916 nm := True;
5917 end else
5918 begin
5919 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5920 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5921 P[1] := addWadExtension(P[1]);
5922 if FileExists(MapsDir + P[1]) then
5923 begin
5924 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5925 SetLength(P, 3);
5926 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5928 s := P[1] + ':\' + P[2];
5930 if g_Map_Exist(MapsDir + s) then
5931 begin // Óñòàíàâëèâàåì êàðòó
5932 gExitByTrigger := False;
5933 gNextMap := s;
5934 nm := True;
5935 end else
5936 if P[2] = '' then
5937 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5938 else
5939 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5940 end else
5941 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5942 end;
5943 end else
5944 begin
5945 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5946 P[1] := addWadExtension(P[1]);
5947 if FileExists(MapsDir + P[1]) then
5948 begin
5949 // Íàøëè WAD ôàéë
5950 P[2] := UpperCase(P[2]);
5951 s := P[1] + ':\' + P[2];
5953 if g_Map_Exist(MapsDir + s) then
5954 begin // Íàøëè êàðòó
5955 gExitByTrigger := False;
5956 gNextMap := s;
5957 nm := True;
5958 end else
5959 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5960 end else
5961 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5962 end;
5963 end else
5964 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5965 end;
5966 if nm then
5967 if gNextMap = '' then
5968 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
5969 else
5970 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
5971 end;
5972 end
5973 else if (cmd = 'endmap') or (cmd = 'goodbye') then
5974 begin
5975 if not gGameOn then
5976 g_Console_Add(_lc[I_MSG_NOT_GAME])
5977 else
5978 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5979 begin
5980 gExitByTrigger := False;
5981 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
5982 if (gNextMap = '') and (gTriggers <> nil) then
5983 for a := 0 to High(gTriggers) do
5984 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5985 begin
5986 gExitByTrigger := True;
5987 gNextMap := gTriggers[a].Data.MapName;
5988 Break;
5989 end;
5990 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
5991 if gNextMap = '' then
5992 gNextMap := g_Game_GetNextMap();
5993 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
5994 if Pos(':\', gNextMap) = 0 then
5995 s := gGameSettings.WAD + ':\' + gNextMap
5996 else
5997 s := gNextMap;
5998 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
5999 if g_Map_Exist(MapsDir + s) then
6000 gExit := EXIT_ENDLEVELCUSTOM
6001 else
6002 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6003 end else
6004 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6005 end
6006 else if (cmd = 'event') then
6007 begin
6008 if (Length(P) <= 1) then
6009 begin
6010 for a := 0 to High(gEvents) do
6011 if gEvents[a].Command = '' then
6012 g_Console_Add(gEvents[a].Name + ' <none>')
6013 else
6014 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6015 Exit;
6016 end;
6017 if (Length(P) = 2) then
6018 begin
6019 for a := 0 to High(gEvents) do
6020 if gEvents[a].Name = P[1] then
6021 if gEvents[a].Command = '' then
6022 g_Console_Add(gEvents[a].Name + ' <none>')
6023 else
6024 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6025 Exit;
6026 end;
6027 for a := 0 to High(gEvents) do
6028 if gEvents[a].Name = P[1] then
6029 begin
6030 gEvents[a].Command := '';
6031 for b := 2 to High(P) do
6032 if Pos(' ', P[b]) = 0 then
6033 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6034 else
6035 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6036 gEvents[a].Command := Trim(gEvents[a].Command);
6037 Exit;
6038 end;
6039 end
6040 // Êîìàíäû Ñâîåé èãðû:
6041 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6042 begin
6043 if cmd = 'bot_addred' then
6044 begin
6045 if Length(P) > 1 then
6046 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6047 else
6048 g_Bot_Add(TEAM_RED, 2);
6049 end
6050 else if cmd = 'bot_addblue' then
6051 begin
6052 if Length(P) > 1 then
6053 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6054 else
6055 g_Bot_Add(TEAM_BLUE, 2);
6056 end
6057 else if cmd = 'suicide' then
6058 begin
6059 if gGameOn then
6060 begin
6061 if g_Game_IsClient then
6062 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6063 else
6064 begin
6065 if gPlayer1 <> nil then
6066 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6067 if gPlayer2 <> nil then
6068 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6069 end;
6070 end;
6071 end
6072 else if cmd = 'spectate' then
6073 begin
6074 if not gGameOn then
6075 Exit;
6076 g_Game_Spectate();
6077 end
6078 else if cmd = 'say' then
6079 begin
6080 if g_Game_IsServer and g_Game_IsNet then
6081 begin
6082 if Length(P) > 1 then
6083 begin
6084 chstr := '';
6085 for a := 1 to High(P) do
6086 chstr := chstr + P[a] + ' ';
6088 if Length(chstr) > 200 then SetLength(chstr, 200);
6090 if Length(chstr) < 1 then
6091 begin
6092 g_Console_Add('say <text>');
6093 Exit;
6094 end;
6096 chstr := b_Text_Format(chstr);
6097 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6098 end
6099 else g_Console_Add('say <text>');
6100 end else
6101 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6102 end
6103 else if cmd = 'tell' then
6104 begin
6105 if g_Game_IsServer and g_Game_IsNet then
6106 begin
6107 if (Length(P) > 2) and (P[1] <> '') then
6108 begin
6109 chstr := '';
6110 for a := 2 to High(P) do
6111 chstr := chstr + P[a] + ' ';
6113 if Length(chstr) > 200 then SetLength(chstr, 200);
6115 if Length(chstr) < 1 then
6116 begin
6117 g_Console_Add('tell <playername> <text>');
6118 Exit;
6119 end;
6121 pl := g_Net_Client_ByName(P[1]);
6122 if pl <> nil then
6123 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6124 else
6125 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6126 end
6127 else g_Console_Add('tell <playername> <text>');
6128 end else
6129 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6130 end
6131 else if (cmd = 'overtime') and not g_Game_IsClient then
6132 begin
6133 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6134 Exit;
6135 // Äîïîëíèòåëüíîå âðåìÿ:
6136 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6138 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6139 [gGameSettings.TimeLimit div 3600,
6140 (gGameSettings.TimeLimit div 60) mod 60,
6141 gGameSettings.TimeLimit mod 60]));
6142 if g_Game_IsNet then MH_SEND_GameSettings;
6143 end
6144 else if (cmd = 'rcon_password') and g_Game_IsClient then
6145 begin
6146 if (Length(P) <= 1) then
6147 g_Console_Add('rcon_password <password>')
6148 else
6149 MC_SEND_RCONPassword(P[1]);
6150 end
6151 else if cmd = 'rcon' then
6152 begin
6153 if g_Game_IsClient then
6154 begin
6155 if Length(P) > 1 then
6156 begin
6157 chstr := '';
6158 for a := 1 to High(P) do
6159 chstr := chstr + P[a] + ' ';
6161 if Length(chstr) > 200 then SetLength(chstr, 200);
6163 if Length(chstr) < 1 then
6164 begin
6165 g_Console_Add('rcon <command>');
6166 Exit;
6167 end;
6169 MC_SEND_RCONCommand(chstr);
6170 end
6171 else g_Console_Add('rcon <command>');
6172 end;
6173 end
6174 else if cmd = 'ready' then
6175 begin
6176 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6177 gLMSRespawnTime := gTime + 100;
6178 end
6179 else if (cmd = 'callvote') and g_Game_IsNet then
6180 begin
6181 if Length(P) > 1 then
6182 begin
6183 chstr := '';
6184 for a := 1 to High(P) do begin
6185 if a > 1 then chstr := chstr + ' ';
6186 chstr := chstr + P[a];
6187 end;
6189 if Length(chstr) > 200 then SetLength(chstr, 200);
6191 if Length(chstr) < 1 then
6192 begin
6193 g_Console_Add('callvote <command>');
6194 Exit;
6195 end;
6197 if g_Game_IsClient then
6198 MC_SEND_Vote(True, chstr)
6199 else
6200 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6201 g_Console_Process('vote', True);
6202 end
6203 else
6204 g_Console_Add('callvote <command>');
6205 end
6206 else if (cmd = 'vote') and g_Game_IsNet then
6207 begin
6208 if g_Game_IsClient then
6209 MC_SEND_Vote(False)
6210 else if gVoteInProgress then
6211 begin
6212 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6213 a := Floor((NetClientCount+1)/2.0) + 1
6214 else
6215 a := Floor(NetClientCount/2.0) + 1;
6216 if gVoted then
6217 begin
6218 Dec(gVoteCount);
6219 gVoted := False;
6220 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6221 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6222 end
6223 else
6224 begin
6225 Inc(gVoteCount);
6226 gVoted := True;
6227 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6228 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6229 g_Game_CheckVote;
6230 end;
6231 end;
6232 end
6233 end;
6234 end;
6236 procedure g_TakeScreenShot();
6237 var
6238 a: Word;
6239 FileName: string;
6240 ssdir, t: string;
6241 st: TStream;
6242 ok: Boolean;
6243 begin
6244 if e_NoGraphics then Exit;
6245 ssdir := GameDir+'/screenshots';
6246 if not findFileCI(ssdir, true) then
6247 begin
6248 // try to create dir
6249 try
6250 CreateDir(ssdir);
6251 except
6252 end;
6253 if not findFileCI(ssdir, true) then exit; // alas
6254 end;
6255 try
6256 for a := 1 to High(Word) do
6257 begin
6258 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6259 t := FileName;
6260 if findFileCI(t, true) then continue;
6261 if not findFileCI(FileName) then
6262 begin
6263 ok := false;
6264 st := createDiskFile(FileName);
6265 try
6266 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6267 ok := true;
6268 finally
6269 st.Free();
6270 end;
6271 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6272 break;
6273 end;
6274 end;
6275 except
6276 end;
6277 end;
6279 procedure g_Game_InGameMenu(Show: Boolean);
6280 begin
6281 if (g_ActiveWindow = nil) and Show then
6282 begin
6283 if gGameSettings.GameType = GT_SINGLE then
6284 g_GUI_ShowWindow('GameSingleMenu')
6285 else
6286 begin
6287 if g_Game_IsClient then
6288 g_GUI_ShowWindow('GameClientMenu')
6289 else
6290 if g_Game_IsNet then
6291 g_GUI_ShowWindow('GameServerMenu')
6292 else
6293 g_GUI_ShowWindow('GameCustomMenu');
6294 end;
6295 g_Sound_PlayEx('MENU_OPEN');
6297 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6298 if (not g_Game_IsNet) then
6299 g_Game_Pause(True);
6300 end
6301 else
6302 if (g_ActiveWindow <> nil) and (not Show) then
6303 begin
6304 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6305 if (not g_Game_IsNet) then
6306 g_Game_Pause(False);
6307 end;
6308 end;
6310 procedure g_Game_Pause(Enable: Boolean);
6311 begin
6312 if not gGameOn then
6313 Exit;
6315 if gPause = Enable then
6316 Exit;
6318 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6319 Exit;
6321 gPause := Enable;
6322 g_Game_PauseAllSounds(Enable);
6323 end;
6325 procedure g_Game_PauseAllSounds(Enable: Boolean);
6326 var
6327 i: Integer;
6328 begin
6329 // Òðèããåðû:
6330 if gTriggers <> nil then
6331 for i := 0 to High(gTriggers) do
6332 with gTriggers[i] do
6333 if (TriggerType = TRIGGER_SOUND) and
6334 (Sound <> nil) and
6335 Sound.IsPlaying() then
6336 begin
6337 Sound.Pause(Enable);
6338 end;
6340 // Çâóêè èãðîêîâ:
6341 if gPlayers <> nil then
6342 for i := 0 to High(gPlayers) do
6343 if gPlayers[i] <> nil then
6344 gPlayers[i].PauseSounds(Enable);
6346 // Ìóçûêà:
6347 if gMusic <> nil then
6348 gMusic.Pause(Enable);
6349 end;
6351 procedure g_Game_StopAllSounds(all: Boolean);
6352 var
6353 i: Integer;
6354 begin
6355 if gTriggers <> nil then
6356 for i := 0 to High(gTriggers) do
6357 with gTriggers[i] do
6358 if (TriggerType = TRIGGER_SOUND) and
6359 (Sound <> nil) then
6360 Sound.Stop();
6362 if gMusic <> nil then
6363 gMusic.Stop();
6365 if all then
6366 e_StopChannels();
6367 end;
6369 procedure g_Game_UpdateTriggerSounds();
6370 var
6371 i: Integer;
6372 begin
6373 if gTriggers <> nil then
6374 for i := 0 to High(gTriggers) do
6375 with gTriggers[i] do
6376 if (TriggerType = TRIGGER_SOUND) and
6377 (Sound <> nil) and
6378 (Data.Local) and
6379 Sound.IsPlaying() then
6380 begin
6381 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6382 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6383 begin
6384 Sound.SetPan(0.5 - Data.Pan/255.0);
6385 Sound.SetVolume(Data.Volume/255.0);
6386 end
6387 else
6388 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6389 end;
6390 end;
6392 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6393 begin
6394 Result := False;
6395 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6396 begin
6397 Result := True;
6398 Exit;
6399 end;
6400 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6401 begin
6402 Result := True;
6403 Exit;
6404 end;
6405 if gSpectMode <> SPECT_PLAYERS then
6406 Exit;
6407 if gSpectPID1 = UID then
6408 begin
6409 Result := True;
6410 Exit;
6411 end;
6412 if gSpectViewTwo and (gSpectPID2 = UID) then
6413 begin
6414 Result := True;
6415 Exit;
6416 end;
6417 end;
6419 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6420 var
6421 Pl: TPlayer;
6422 begin
6423 Result := False;
6424 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6425 begin
6426 Result := True;
6427 Exit;
6428 end;
6429 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6430 begin
6431 Result := True;
6432 Exit;
6433 end;
6434 if gSpectMode <> SPECT_PLAYERS then
6435 Exit;
6436 Pl := g_Player_Get(gSpectPID1);
6437 if (Pl <> nil) and (Pl.Team = Team) then
6438 begin
6439 Result := True;
6440 Exit;
6441 end;
6442 if gSpectViewTwo then
6443 begin
6444 Pl := g_Player_Get(gSpectPID2);
6445 if (Pl <> nil) and (Pl.Team = Team) then
6446 begin
6447 Result := True;
6448 Exit;
6449 end;
6450 end;
6451 end;
6453 procedure g_Game_Message(Msg: string; Time: Word);
6454 begin
6455 MessageText := b_Text_Format(Msg);
6456 MessageTime := Time;
6457 end;
6459 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6460 var
6461 a: Integer;
6462 begin
6463 case gAnnouncer of
6464 ANNOUNCE_NONE:
6465 Exit;
6466 ANNOUNCE_ME,
6467 ANNOUNCE_MEPLUS:
6468 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6469 Exit;
6470 end;
6471 for a := 0 to 3 do
6472 if goodsnd[a].IsPlaying() then
6473 Exit;
6475 goodsnd[Random(4)].Play();
6476 end;
6478 procedure g_Game_Announce_KillCombo(Param: Integer);
6479 var
6480 UID: Word;
6481 c, n: Byte;
6482 Pl: TPlayer;
6483 Name: String;
6484 begin
6485 UID := Param and $FFFF;
6486 c := Param shr 16;
6487 if c < 2 then
6488 Exit;
6490 Pl := g_Player_Get(UID);
6491 if Pl = nil then
6492 Name := '?'
6493 else
6494 Name := Pl.Name;
6496 case c of
6497 2: begin
6498 n := 0;
6499 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6500 end;
6501 3: begin
6502 n := 1;
6503 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6504 end;
6505 4: begin
6506 n := 2;
6507 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6508 end;
6509 else begin
6510 n := 3;
6511 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6512 end;
6513 end;
6515 case gAnnouncer of
6516 ANNOUNCE_NONE:
6517 Exit;
6518 ANNOUNCE_ME:
6519 if not g_Game_IsWatchedPlayer(UID) then
6520 Exit;
6521 ANNOUNCE_MEPLUS:
6522 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6523 Exit;
6524 end;
6526 if killsnd[n].IsPlaying() then
6527 killsnd[n].Stop();
6528 killsnd[n].Play();
6529 end;
6531 procedure g_Game_StartVote(Command, Initiator: string);
6532 var
6533 Need: Integer;
6534 begin
6535 if not gVotesEnabled then Exit;
6536 if gGameSettings.GameType <> GT_SERVER then Exit;
6537 if gVoteInProgress or gVotePassed then
6538 begin
6539 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6540 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6541 Exit;
6542 end;
6543 gVoteInProgress := True;
6544 gVotePassed := False;
6545 gVoteTimer := gTime + gVoteTimeout * 1000;
6546 gVoteCount := 0;
6547 gVoted := False;
6548 gVoteCommand := Command;
6550 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6551 Need := Floor((NetClientCount+1)/2.0)+1
6552 else
6553 Need := Floor(NetClientCount/2.0)+1;
6554 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6555 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6556 end;
6558 procedure g_Game_CheckVote;
6559 var
6560 I, Need: Integer;
6561 begin
6562 if gGameSettings.GameType <> GT_SERVER then Exit;
6563 if not gVoteInProgress then Exit;
6565 if (gTime >= gVoteTimer) then
6566 begin
6567 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6568 Need := Floor((NetClientCount+1)/2.0) + 1
6569 else
6570 Need := Floor(NetClientCount/2.0) + 1;
6571 if gVoteCount >= Need then
6572 begin
6573 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6574 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6575 gVotePassed := True;
6576 gVoteCmdTimer := gTime + 5000;
6577 end
6578 else
6579 begin
6580 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6581 MH_SEND_VoteEvent(NET_VE_FAILED);
6582 end;
6583 if NetClients <> nil then
6584 for I := Low(NetClients) to High(NetClients) do
6585 if NetClients[i].Used then
6586 NetClients[i].Voted := False;
6587 gVoteInProgress := False;
6588 gVoted := False;
6589 gVoteCount := 0;
6590 end
6591 else
6592 begin
6593 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6594 Need := Floor((NetClientCount+1)/2.0) + 1
6595 else
6596 Need := Floor(NetClientCount/2.0) + 1;
6597 if gVoteCount >= Need then
6598 begin
6599 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6600 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6601 gVoteInProgress := False;
6602 gVotePassed := True;
6603 gVoteCmdTimer := gTime + 5000;
6604 gVoted := False;
6605 gVoteCount := 0;
6606 if NetClients <> nil then
6607 for I := Low(NetClients) to High(NetClients) do
6608 if NetClients[i].Used then
6609 NetClients[i].Voted := False;
6610 end;
6611 end;
6612 end;
6614 procedure g_Game_LoadMapList(FileName: string);
6615 var
6616 ListFile: TextFile;
6617 s: string;
6618 begin
6619 MapList := nil;
6620 MapIndex := -1;
6622 if not FileExists(FileName) then Exit;
6624 AssignFile(ListFile, FileName);
6625 Reset(ListFile);
6626 while not EOF(ListFile) do
6627 begin
6628 ReadLn(ListFile, s);
6630 s := Trim(s);
6631 if s = '' then Continue;
6633 SetLength(MapList, Length(MapList)+1);
6634 MapList[High(MapList)] := s;
6635 end;
6636 CloseFile(ListFile);
6637 end;
6639 procedure g_Game_SetDebugMode();
6640 begin
6641 gDebugMode := True;
6642 // ×èòû (äàæå â ñâîåé èãðå):
6643 gCheats := True;
6644 end;
6646 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6647 var
6648 i: Word;
6649 begin
6650 if Length(LoadingStat.Msgs) = 0 then
6651 Exit;
6653 with LoadingStat do
6654 begin
6655 if not reWrite then
6656 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6657 if NextMsg = Length(Msgs) then
6658 begin // scroll
6659 for i := 0 to High(Msgs)-1 do
6660 Msgs[i] := Msgs[i+1];
6661 end
6662 else
6663 Inc(NextMsg);
6664 end else
6665 if NextMsg = 0 then
6666 Inc(NextMsg);
6668 Msgs[NextMsg-1] := Text;
6669 CurValue := 0;
6670 MaxValue := Max;
6671 ShowCount := 0;
6672 end;
6674 g_ActiveWindow := nil;
6676 ProcessLoading;
6677 end;
6679 procedure g_Game_StepLoading();
6680 begin
6681 with LoadingStat do
6682 begin
6683 Inc(CurValue);
6684 Inc(ShowCount);
6685 if (ShowCount > LOADING_SHOW_STEP) then
6686 begin
6687 ShowCount := 0;
6688 ProcessLoading;
6689 end;
6690 end;
6691 end;
6693 procedure g_Game_ClearLoading();
6694 var
6695 len: Word;
6696 begin
6697 with LoadingStat do
6698 begin
6699 CurValue := 0;
6700 MaxValue := 0;
6701 ShowCount := 0;
6702 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6703 if len < 1 then len := 1;
6704 SetLength(Msgs, len);
6705 for len := Low(Msgs) to High(Msgs) do
6706 Msgs[len] := '';
6707 NextMsg := 0;
6708 end;
6709 end;
6711 procedure Parse_Params(var pars: TParamStrValues);
6712 var
6713 i: Integer;
6714 s: String;
6715 begin
6716 SetLength(pars, 0);
6717 i := 1;
6718 while i <= ParamCount do
6719 begin
6720 s := ParamStr(i);
6721 if (s[1] = '-') and (Length(s) > 1) then
6722 begin
6723 if (s[2] = '-') and (Length(s) > 2) then
6724 begin // Îäèíî÷íûé ïàðàìåòð
6725 SetLength(pars, Length(pars) + 1);
6726 with pars[High(pars)] do
6727 begin
6728 Name := LowerCase(s);
6729 Value := '+';
6730 end;
6731 end
6732 else
6733 if (i < ParamCount) then
6734 begin // Ïàðàìåòð ñî çíà÷åíèåì
6735 Inc(i);
6736 SetLength(pars, Length(pars) + 1);
6737 with pars[High(pars)] do
6738 begin
6739 Name := LowerCase(s);
6740 Value := LowerCase(ParamStr(i));
6741 end;
6742 end;
6743 end;
6745 Inc(i);
6746 end;
6747 end;
6749 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6750 var
6751 i: Integer;
6752 begin
6753 Result := '';
6754 for i := 0 to High(pars) do
6755 if pars[i].Name = aName then
6756 begin
6757 Result := pars[i].Value;
6758 Break;
6759 end;
6760 end;
6762 procedure g_Game_Process_Params();
6763 var
6764 pars: TParamStrValues;
6765 map: String;
6766 GMode, n: Byte;
6767 LimT, LimS: Integer;
6768 Opt: LongWord;
6769 Lives: Integer;
6770 s: String;
6771 Port: Integer;
6772 ip: String;
6773 F: TextFile;
6774 begin
6775 Parse_Params(pars);
6777 // Debug mode:
6778 s := Find_Param_Value(pars, '--debug');
6779 if (s <> '') then
6780 begin
6781 g_Game_SetDebugMode();
6782 s := Find_Param_Value(pars, '--netdump');
6783 if (s <> '') then
6784 NetDump := True;
6785 end;
6787 // Connect when game loads
6788 ip := Find_Param_Value(pars, '-connect');
6790 if ip <> '' then
6791 begin
6792 s := Find_Param_Value(pars, '-port');
6793 if (s = '') or not TryStrToInt(s, Port) then
6794 Port := 25666;
6796 s := Find_Param_Value(pars, '-pw');
6798 g_Game_StartClient(ip, Port, s);
6799 Exit;
6800 end;
6802 // Start map when game loads:
6803 map := LowerCase(Find_Param_Value(pars, '-map'));
6804 if isWadPath(map) then
6805 begin
6806 // Game mode:
6807 s := Find_Param_Value(pars, '-gm');
6808 GMode := g_Game_TextToMode(s);
6809 if GMode = GM_NONE then GMode := GM_DM;
6810 if GMode = GM_SINGLE then GMode := GM_COOP;
6812 // Time limit:
6813 s := Find_Param_Value(pars, '-limt');
6814 if (s = '') or (not TryStrToInt(s, LimT)) then
6815 LimT := 0;
6816 if LimT < 0 then
6817 LimT := 0;
6819 // Goal limit:
6820 s := Find_Param_Value(pars, '-lims');
6821 if (s = '') or (not TryStrToInt(s, LimS)) then
6822 LimS := 0;
6823 if LimS < 0 then
6824 LimS := 0;
6826 // Lives limit:
6827 s := Find_Param_Value(pars, '-lives');
6828 if (s = '') or (not TryStrToInt(s, Lives)) then
6829 Lives := 0;
6830 if Lives < 0 then
6831 Lives := 0;
6833 // Options:
6834 s := Find_Param_Value(pars, '-opt');
6835 if (s = '') then
6836 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6837 else
6838 Opt := StrToIntDef(s, 0);
6839 if Opt = 0 then
6840 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6842 // Close after map:
6843 s := Find_Param_Value(pars, '--close');
6844 if (s <> '') then
6845 gMapOnce := True;
6847 // Delete test map after play:
6848 s := Find_Param_Value(pars, '--testdelete');
6849 if (s <> '') then
6850 begin
6851 gMapToDelete := MapsDir + map;
6852 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6853 Halt(1);
6854 end;
6856 // Delete temporary WAD after play:
6857 s := Find_Param_Value(pars, '--tempdelete');
6858 if (s <> '') then
6859 begin
6860 gMapToDelete := MapsDir + map;
6861 gTempDelete := True;
6862 end;
6864 // Number of players:
6865 s := Find_Param_Value(pars, '-pl');
6866 if (s = '') then
6867 n := 1
6868 else
6869 n := StrToIntDef(s, 1);
6871 // Start:
6872 s := Find_Param_Value(pars, '-port');
6873 if (s = '') or not TryStrToInt(s, Port) then
6874 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6875 else
6876 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6877 end;
6879 // Execute script when game loads:
6880 s := Find_Param_Value(pars, '-exec');
6881 if s <> '' then
6882 begin
6883 if Pos(':\', s) = 0 then
6884 s := GameDir + '/' + s;
6886 {$I-}
6887 AssignFile(F, s);
6888 Reset(F);
6889 if IOResult <> 0 then
6890 begin
6891 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6892 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6893 CloseFile(F);
6894 Exit;
6895 end;
6896 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6897 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6899 while not EOF(F) do
6900 begin
6901 ReadLn(F, s);
6902 if IOResult <> 0 then
6903 begin
6904 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6905 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6906 CloseFile(F);
6907 Exit;
6908 end;
6909 if Pos('#', s) <> 1 then // script comment
6910 g_Console_Process(s, True);
6911 end;
6913 CloseFile(F);
6914 {$I+}
6915 end;
6917 SetLength(pars, 0);
6918 end;
6920 end.