DEADSOFTWARE

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