DEADSOFTWARE

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