DEADSOFTWARE

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