DEADSOFTWARE

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