DEADSOFTWARE

xdynrec: TColor type (rgb, and optional a)
[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(const MapPath: String);
100 procedure g_Game_ExitLevel(const Map: AnsiString);
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 := g_dbg_aimline_on;
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 // this will also reset "need-send" flag
1491 if mon.gncNeedSend then
1492 begin
1493 MH_SEND_MonsterPos(mon.UID);
1494 end
1495 else if (mon.MonsterType = MONSTER_BARREL) then
1496 begin
1497 if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1498 end
1499 else if (mon.MonsterState <> MONSTATE_SLEEP) then
1500 begin
1501 if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1502 end;
1503 end;
1505 function sendMonsPosUnexpected (mon: TMonster): Boolean;
1506 begin
1507 result := false; // don't stop
1508 // this will also reset "need-send" flag
1509 if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID);
1510 end;
1512 var
1513 reliableUpdate: Boolean;
1514 begin
1515 g_ResetDynlights();
1516 // Ïîðà âûêëþ÷àòü èãðó:
1517 if gExit = EXIT_QUIT then
1518 Exit;
1519 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1520 if gExit <> 0 then
1521 begin
1522 EndGame();
1523 if gExit = EXIT_QUIT then
1524 Exit;
1525 end;
1527 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1528 e_PollInput();
1530 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1531 g_Console_Update();
1533 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1534 begin
1535 gExit := EXIT_SIMPLE;
1536 EndGame();
1537 Exit;
1538 end;
1540 case gState of
1541 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1542 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1543 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1544 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1545 begin
1546 if g_Game_IsNet and g_Game_IsServer then
1547 begin
1548 gInterTime := gInterTime + GAME_TICK;
1549 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1550 if a <> gServInterTime then
1551 begin
1552 gServInterTime := a;
1553 MH_SEND_TimeSync(gServInterTime);
1554 end;
1555 end;
1557 if (not g_Game_IsClient) and
1560 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1561 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1562 and (g_ActiveWindow = nil)
1564 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1566 then
1567 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1568 g_Game_StopAllSounds(True);
1570 if gMapOnce then // Ýòî áûë òåñò
1571 gExit := EXIT_SIMPLE
1572 else
1573 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1574 g_Game_ChangeMap(gNextMap)
1575 else // Ñëåäóþùåé êàðòû íåò
1576 begin
1577 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1578 begin
1579 // Âûõîä â ãëàâíîå ìåíþ:
1580 g_Game_Free;
1581 g_GUI_ShowWindow('MainMenu');
1582 gMusic.SetByName('MUSIC_MENU');
1583 gMusic.Play();
1584 gState := STATE_MENU;
1585 end else
1586 begin
1587 // Ôèíàëüíàÿ êàðòèíêà:
1588 g_Game_ExecuteEvent('onwadend');
1589 g_Game_Free();
1590 if not gMusic.SetByName('MUSIC_endmus') then
1591 gMusic.SetByName('MUSIC_STDENDMUS');
1592 gMusic.Play();
1593 gState := STATE_ENDPIC;
1594 end;
1595 g_Game_ExecuteEvent('ongameend');
1596 end;
1598 Exit;
1599 end;
1601 if gState = STATE_INTERTEXT then
1602 if InterText.counter > 0 then
1603 InterText.counter := InterText.counter - 1;
1604 end;
1606 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1607 begin
1608 if EndingGameCounter = 0 then
1609 begin
1610 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1611 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1612 begin
1613 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1614 begin
1615 g_Game_ExecuteEvent('onwadend');
1616 if not gMusic.SetByName('MUSIC_endmus') then
1617 gMusic.SetByName('MUSIC_STDENDMUS');
1618 end
1619 else
1620 gMusic.SetByName('MUSIC_ROUNDMUS');
1622 gMusic.Play();
1623 gState := STATE_INTERCUSTOM;
1624 end
1625 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1626 begin
1627 gMusic.SetByName('MUSIC_INTERMUS');
1628 gMusic.Play();
1629 gState := STATE_INTERSINGLE;
1630 end;
1631 g_Game_ExecuteEvent('oninter');
1632 end
1633 else
1634 DecMin(EndingGameCounter, 6, 0);
1635 end;
1637 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1638 begin
1639 if gMapOnce then // Ýòî áûë òåñò
1640 begin
1641 gExit := EXIT_SIMPLE;
1642 Exit;
1643 end;
1644 end;
1646 STATE_SLIST:
1647 g_Serverlist_Control(slCurrent);
1648 end;
1650 if g_Game_IsNet then
1651 if not gConsoleShow then
1652 if not gChatShow then
1653 begin
1654 if g_ActiveWindow = nil then
1655 begin
1656 if e_KeyPressed(gGameControls.GameControls.Chat) then
1657 g_Console_Chat_Switch(False)
1658 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1659 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1660 g_Console_Chat_Switch(True);
1661 end;
1662 end else
1663 if not gChatEnter then
1664 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1665 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1666 gChatEnter := True;
1668 // Ñòàòèñòèêà ïî Tab:
1669 if gGameOn then
1670 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1671 (gGameSettings.GameType <> GT_SINGLE) and
1672 e_KeyPressed(gGameControls.GameControls.Stat);
1674 // Èãðà èäåò:
1675 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1676 begin
1677 // Âðåìÿ += 28 ìèëëèñåêóíä:
1678 gTime := gTime + GAME_TICK;
1680 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1681 if MessageTime = 0 then
1682 MessageText := '';
1683 if MessageTime > 0 then
1684 MessageTime := MessageTime - 1;
1686 if (g_Game_IsServer) then
1687 begin
1688 // Áûë çàäàí ëèìèò âðåìåíè:
1689 if (gGameSettings.TimeLimit > 0) then
1690 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1691 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1692 g_Game_NextLevel();
1693 Exit;
1694 end;
1696 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1697 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1698 g_Game_RestartRound(gLMSSoftSpawn);
1700 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1701 if gVoteInProgress and (gVoteTimer < gTime) then
1702 g_Game_CheckVote
1703 else if gVotePassed and (gVoteCmdTimer < gTime) then
1704 begin
1705 g_Console_Process(gVoteCommand);
1706 gVoteCommand := '';
1707 gVotePassed := False;
1708 end;
1710 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1711 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1712 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1713 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1714 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1716 // Áûë çàäàí ëèìèò ïîáåä:
1717 if (gGameSettings.GoalLimit > 0) then
1718 begin
1719 b := 0;
1721 if gGameSettings.GameMode = GM_DM then
1722 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1723 for i := 0 to High(gPlayers) do
1724 if gPlayers[i] <> nil then
1725 if gPlayers[i].Frags > b then
1726 b := gPlayers[i].Frags;
1727 end
1728 else
1729 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1730 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1731 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1732 end;
1734 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1735 if b >= gGameSettings.GoalLimit then
1736 begin
1737 g_Game_NextLevel();
1738 Exit;
1739 end;
1740 end;
1742 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1743 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1744 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1745 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1746 begin
1747 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1748 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1749 end // if not console
1750 else
1751 begin
1752 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1753 end;
1754 // process weapon switch queue
1755 end; // if server
1757 // Íàáëþäàòåëü
1758 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1759 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1760 begin
1761 if not gSpectKeyPress then
1762 begin
1763 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1764 begin
1765 // switch spect mode
1766 case gSpectMode of
1767 SPECT_NONE: ; // not spectator
1768 SPECT_STATS,
1769 SPECT_MAPVIEW: Inc(gSpectMode);
1770 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1771 end;
1772 gSpectKeyPress := True;
1773 end;
1774 if gSpectMode = SPECT_MAPVIEW then
1775 begin
1776 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1777 gSpectX := Max(gSpectX - gSpectStep, 0);
1778 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1779 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1780 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1781 gSpectY := Max(gSpectY - gSpectStep, 0);
1782 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1783 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1784 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1785 begin
1786 // decrease step
1787 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1788 gSpectKeyPress := True;
1789 end;
1790 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1791 begin
1792 // increase step
1793 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1794 gSpectKeyPress := True;
1795 end;
1796 end;
1797 if gSpectMode = SPECT_PLAYERS then
1798 begin
1799 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1800 begin
1801 // add second view
1802 gSpectViewTwo := True;
1803 gSpectKeyPress := True;
1804 end;
1805 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1806 begin
1807 // remove second view
1808 gSpectViewTwo := False;
1809 gSpectKeyPress := True;
1810 end;
1811 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1812 begin
1813 // prev player (view 1)
1814 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1815 gSpectKeyPress := True;
1816 end;
1817 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1818 begin
1819 // next player (view 1)
1820 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1821 gSpectKeyPress := True;
1822 end;
1823 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1824 begin
1825 // prev player (view 2)
1826 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1827 gSpectKeyPress := True;
1828 end;
1829 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1830 begin
1831 // next player (view 2)
1832 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1833 gSpectKeyPress := True;
1834 end;
1835 end;
1836 end
1837 else
1838 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1839 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1840 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1841 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1842 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1843 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1844 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1845 gSpectKeyPress := False;
1846 end;
1848 // Îáíîâëÿåì âñå îñòàëüíîå:
1849 g_Map_Update();
1850 g_Items_Update();
1851 g_Triggers_Update();
1852 g_Weapon_Update();
1853 g_Monsters_Update();
1854 g_GFX_Update();
1855 g_Player_UpdateAll();
1856 g_Player_UpdatePhysicalObjects();
1858 // server: send newly spawned monsters unconditionally
1859 if (gGameSettings.GameType = GT_SERVER) then
1860 begin
1861 if (Length(gMonstersSpawned) > 0) then
1862 begin
1863 for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1864 SetLength(gMonstersSpawned, 0);
1865 end;
1866 end;
1868 if (gSoundTriggerTime > 8) then
1869 begin
1870 g_Game_UpdateTriggerSounds();
1871 gSoundTriggerTime := 0;
1872 end
1873 else
1874 begin
1875 Inc(gSoundTriggerTime);
1876 end;
1878 if (NetMode = NET_SERVER) then
1879 begin
1880 Inc(NetTimeToUpdate);
1881 Inc(NetTimeToReliable);
1883 // send monster updates
1884 if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then
1885 begin
1886 // send all monsters (periodic sync)
1887 reliableUpdate := (NetTimeToReliable >= NetRelupdRate);
1889 for I := 0 to High(gPlayers) do
1890 begin
1891 if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID);
1892 end;
1894 g_Mons_ForEach(sendMonsPos);
1896 if reliableUpdate then
1897 begin
1898 NetTimeToReliable := 0;
1899 NetTimeToUpdate := NetUpdateRate;
1900 end
1901 else
1902 begin
1903 NetTimeToUpdate := 0;
1904 end;
1905 end
1906 else
1907 begin
1908 // send only mosters with some unexpected changes
1909 g_Mons_ForEach(sendMonsPosUnexpected);
1910 end;
1912 // send unexpected platform changes
1913 g_Map_NetSendInterestingPanels();
1915 if NetUseMaster then
1916 begin
1917 if gTime >= NetTimeToMaster then
1918 begin
1919 if (NetMHost = nil) or (NetMPeer = nil) then
1920 begin
1921 if not g_Net_Slist_Connect then g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1922 end;
1924 g_Net_Slist_Update;
1925 NetTimeToMaster := gTime + NetMasterRate;
1926 end;
1927 end;
1928 end
1929 else if (NetMode = NET_CLIENT) then
1930 begin
1931 MC_SEND_PlayerPos();
1932 end;
1933 end; // if gameOn ...
1935 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1936 if g_ActiveWindow <> nil then
1937 begin
1938 w := e_GetFirstKeyPressed();
1940 if (w <> IK_INVALID) then
1941 begin
1942 Msg.Msg := MESSAGE_DIKEY;
1943 Msg.wParam := w;
1944 g_ActiveWindow.OnMessage(Msg);
1945 end;
1947 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1948 if g_ActiveWindow <> nil then
1949 g_ActiveWindow.Update();
1951 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1952 if gResolutionChange then
1953 begin
1954 e_WriteLog('Changing resolution', MSG_NOTIFY);
1955 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1956 gResolutionChange := False;
1957 end;
1959 // Íóæíî ñìåíèòü ÿçûê:
1960 if gLanguageChange then
1961 begin
1962 //e_WriteLog('Read language file', MSG_NOTIFY);
1963 //g_Language_Load(DataDir + gLanguage + '.txt');
1964 g_Language_Set(gLanguage);
1965 g_Menu_Reset();
1966 gLanguageChange := False;
1967 end;
1968 end;
1970 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
1971 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
1972 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
1973 begin
1974 g_TakeScreenShot();
1975 LastScreenShot := GetTimer();
1976 end;
1978 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
1979 if e_KeyPressed(IK_F10) and
1980 gGameOn and
1981 (not gConsoleShow) and
1982 (g_ActiveWindow = nil) then
1983 begin
1984 KeyPress(IK_F10);
1985 end;
1987 Time := GetTimer() {div 1000};
1989 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
1990 if gDelayedEvents <> nil then
1991 for a := 0 to High(gDelayedEvents) do
1992 if gDelayedEvents[a].Pending and
1994 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
1995 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
1996 ) then
1997 begin
1998 case gDelayedEvents[a].DEType of
1999 DE_GLOBEVENT:
2000 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
2001 DE_BFGHIT:
2002 if gGameOn then
2003 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
2004 DE_KILLCOMBO:
2005 if gGameOn then
2006 begin
2007 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
2008 if g_Game_IsNet and g_Game_IsServer then
2009 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
2010 end;
2011 end;
2012 gDelayedEvents[a].Pending := False;
2013 end;
2015 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2016 UPSCounter := UPSCounter + 1;
2017 if Time - UPSTime >= 1000 then
2018 begin
2019 UPS := UPSCounter;
2020 UPSCounter := 0;
2021 UPSTime := Time;
2022 end;
2024 if gGameOn then
2025 begin
2026 g_Weapon_AddDynLights();
2027 g_Items_AddDynLights();
2028 end;
2029 end;
2031 procedure g_Game_LoadData();
2032 begin
2033 if DataLoaded then Exit;
2035 e_WriteLog('Loading game data...', MSG_NOTIFY);
2037 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2038 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2039 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2040 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2041 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2042 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2043 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB', GameWAD+':TEXTURES\FLAGHUD_R_BASE');
2044 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN');
2045 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD', GameWAD+':TEXTURES\FLAGHUD_R_DROP');
2046 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB', GameWAD+':TEXTURES\FLAGHUD_B_BASE');
2047 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN');
2048 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD', GameWAD+':TEXTURES\FLAGHUD_B_DROP');
2049 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2050 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2051 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2052 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2053 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2054 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2055 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2056 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2057 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2058 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2059 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2060 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2061 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2062 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2063 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2064 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2065 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2066 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2067 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2068 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2069 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2070 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2072 goodsnd[0] := TPlayableSound.Create();
2073 goodsnd[1] := TPlayableSound.Create();
2074 goodsnd[2] := TPlayableSound.Create();
2075 goodsnd[3] := TPlayableSound.Create();
2077 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2078 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2079 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2080 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2082 killsnd[0] := TPlayableSound.Create();
2083 killsnd[1] := TPlayableSound.Create();
2084 killsnd[2] := TPlayableSound.Create();
2085 killsnd[3] := TPlayableSound.Create();
2087 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2088 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2089 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2090 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2092 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2093 g_Items_LoadData();
2095 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2096 g_Weapon_LoadData();
2098 g_Monsters_LoadData();
2100 DataLoaded := True;
2101 end;
2103 procedure g_Game_FreeData();
2104 begin
2105 if not DataLoaded then Exit;
2107 g_Items_FreeData();
2108 g_Weapon_FreeData();
2109 g_Monsters_FreeData();
2111 e_WriteLog('Releasing game data...', MSG_NOTIFY);
2113 g_Texture_Delete('NOTEXTURE');
2114 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2115 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2116 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2117 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2118 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2119 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2120 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2121 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2122 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2123 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2124 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2125 g_Frames_DeleteByName('FRAMES_TELEPORT');
2126 g_Sound_Delete('SOUND_GAME_TELEPORT');
2127 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2128 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2129 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2130 g_Sound_Delete('SOUND_GAME_BULK1');
2131 g_Sound_Delete('SOUND_GAME_BULK2');
2132 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2133 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2134 g_Sound_Delete('SOUND_GAME_SWITCH1');
2135 g_Sound_Delete('SOUND_GAME_SWITCH0');
2137 goodsnd[0].Free();
2138 goodsnd[1].Free();
2139 goodsnd[2].Free();
2140 goodsnd[3].Free();
2142 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2143 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2144 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2145 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2147 killsnd[0].Free();
2148 killsnd[1].Free();
2149 killsnd[2].Free();
2150 killsnd[3].Free();
2152 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2153 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2154 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2155 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2157 DataLoaded := False;
2158 end;
2160 procedure DrawCustomStat();
2161 var
2162 pc, x, y, w, _y,
2163 w1, w2, w3,
2164 t, p, m: Integer;
2165 ww1, hh1: Word;
2166 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2167 s1, s2, topstr: String;
2168 begin
2169 e_TextureFontGetSize(gStdFont, ww2, hh2);
2171 e_PollInput();
2172 if e_KeyPressed(IK_TAB) then
2173 begin
2174 if not gStatsPressed then
2175 begin
2176 gStatsOff := not gStatsOff;
2177 gStatsPressed := True;
2178 end;
2179 end
2180 else
2181 gStatsPressed := False;
2183 if gStatsOff then
2184 begin
2185 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2186 w := (Length(s1) * ww2) div 2;
2187 x := gScreenWidth div 2 - w;
2188 y := 8;
2189 e_TextureFontPrint(x, y, s1, gStdFont);
2190 Exit;
2191 end;
2193 if (gGameSettings.GameMode = GM_COOP) then
2194 begin
2195 if gMissionFailed then
2196 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2197 else
2198 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2199 end
2200 else
2201 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2203 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2204 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2206 if g_Game_IsNet then
2207 begin
2208 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2209 if not gChatShow then
2210 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2211 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2212 end;
2214 if g_Game_IsClient then
2215 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2216 else
2217 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2218 if not gChatShow then
2219 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2220 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2222 x := 32;
2223 y := 16+hh1+16;
2225 w := gScreenWidth-x*2;
2227 w2 := (w-16) div 6;
2228 w3 := w2;
2229 w1 := w-16-w2-w3;
2231 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2232 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2234 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2236 case CustomStat.GameMode of
2237 GM_DM:
2238 begin
2239 if gGameSettings.MaxLives = 0 then
2240 s1 := _lc[I_GAME_DM]
2241 else
2242 s1 := _lc[I_GAME_LMS];
2243 end;
2244 GM_TDM:
2245 begin
2246 if gGameSettings.MaxLives = 0 then
2247 s1 := _lc[I_GAME_TDM]
2248 else
2249 s1 := _lc[I_GAME_TLMS];
2250 end;
2251 GM_CTF: s1 := _lc[I_GAME_CTF];
2252 GM_COOP:
2253 begin
2254 if gGameSettings.MaxLives = 0 then
2255 s1 := _lc[I_GAME_COOP]
2256 else
2257 s1 := _lc[I_GAME_SURV];
2258 end;
2259 else s1 := '';
2260 end;
2262 _y := y+16;
2263 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2264 _y := _y+8;
2266 _y := _y+16;
2267 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2268 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2270 _y := _y+16;
2271 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2272 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2273 (CustomStat.GameTime div 1000 div 60) mod 60,
2274 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2276 pc := Length(CustomStat.PlayerStat);
2277 if pc = 0 then Exit;
2279 if CustomStat.GameMode = GM_COOP then
2280 begin
2281 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2282 _y := _y+32;
2283 s2 := _lc[I_GAME_MONSTERS];
2284 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2285 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2286 _y := _y+16;
2287 s2 := _lc[I_GAME_SECRETS];
2288 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2289 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2290 if gLastMap then
2291 begin
2292 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2293 _y := _y-16;
2294 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2295 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2296 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2297 _y := _y+16;
2298 s2 := _lc[I_GAME_SECRETS_TOTAL];
2299 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2300 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2301 end;
2302 end;
2304 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2305 begin
2306 _y := _y+16+16;
2308 with CustomStat do
2309 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2310 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2311 else s1 := _lc[I_GAME_WIN_DRAW];
2313 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2314 _y := _y+40;
2316 for t := TEAM_RED to TEAM_BLUE do
2317 begin
2318 if t = TEAM_RED then
2319 begin
2320 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2321 gStdFont, 255, 0, 0, 1);
2322 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2323 gStdFont, 255, 0, 0, 1);
2324 r := 255;
2325 g := 0;
2326 b := 0;
2327 end
2328 else
2329 begin
2330 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2331 gStdFont, 0, 0, 255, 1);
2332 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2333 gStdFont, 0, 0, 255, 1);
2334 r := 0;
2335 g := 0;
2336 b := 255;
2337 end;
2339 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2340 _y := _y+24;
2342 for p := 0 to High(CustomStat.PlayerStat) do
2343 if CustomStat.PlayerStat[p].Team = t then
2344 with CustomStat.PlayerStat[p] do
2345 begin
2346 if Spectator then
2347 begin
2348 rr := r div 2;
2349 gg := g div 2;
2350 bb := b div 2;
2351 end
2352 else
2353 begin
2354 rr := r;
2355 gg := g;
2356 bb := b;
2357 end;
2358 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2359 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2360 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2361 _y := _y+24;
2362 end;
2364 _y := _y+16+16;
2365 end;
2366 end
2367 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2368 begin
2369 _y := _y+40;
2370 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2371 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2372 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2374 _y := _y+24;
2375 for p := 0 to High(CustomStat.PlayerStat) do
2376 with CustomStat.PlayerStat[p] do
2377 begin
2378 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2380 if Spectator then
2381 r := 127
2382 else
2383 r := 255;
2385 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2386 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2387 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2388 _y := _y+24;
2389 end;
2390 end;
2391 end;
2393 procedure DrawSingleStat();
2394 var
2395 tm, key_x, val_x, y: Integer;
2396 w1, w2, h: Word;
2397 s1, s2: String;
2399 procedure player_stat(n: Integer);
2400 var
2401 kpm: Real;
2403 begin
2404 // "Kills: # / #":
2405 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2406 s2 := Format(' %d', [gTotalMonsters]);
2408 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2409 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2410 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2411 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2412 s1 := s1 + '/';
2413 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2414 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2416 // "Kills-per-minute: ##.#":
2417 s1 := _lc[I_MENU_INTER_KPM];
2418 if tm > 0 then
2419 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2420 else
2421 kpm := SingleStat.PlayerStat[n].Kills;
2422 s2 := Format(' %.1f', [kpm]);
2424 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2425 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2427 // "Secrets found: # / #":
2428 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2429 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2431 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2432 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2433 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2434 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2435 s1 := s1 + '/';
2436 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2437 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2438 end;
2440 begin
2441 // "Level Complete":
2442 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2443 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2445 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2446 s1 := _lc[I_MENU_INTER_KPM];
2447 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2448 Inc(w1, 16);
2449 s1 := ' 9999.9';
2450 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2452 key_x := (gScreenWidth-w1-w2) div 2;
2453 val_x := key_x + w1;
2455 // "Time: #:##:##":
2456 tm := SingleStat.GameTime div 1000;
2457 s1 := _lc[I_MENU_INTER_TIME];
2458 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2460 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2461 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2463 if SingleStat.TwoPlayers then
2464 begin
2465 // "Player 1":
2466 s1 := _lc[I_MENU_PLAYER_1];
2467 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2468 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2470 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2471 y := 176;
2472 player_stat(0);
2474 // "Player 2":
2475 s1 := _lc[I_MENU_PLAYER_2];
2476 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2477 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2479 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2480 y := 336;
2481 player_stat(1);
2482 end
2483 else
2484 begin
2485 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2486 y := 128;
2487 player_stat(0);
2488 end;
2489 end;
2491 procedure DrawLoadingStat();
2492 var
2493 ww, hh: Word;
2494 xx, yy, i: Integer;
2495 s: String;
2496 begin
2497 if Length(LoadingStat.Msgs) = 0 then
2498 Exit;
2500 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2501 yy := (gScreenHeight div 3);
2502 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2503 xx := (gScreenWidth div 3);
2505 with LoadingStat do
2506 for i := 0 to NextMsg-1 do
2507 begin
2508 if (i = (NextMsg-1)) and (MaxValue > 0) then
2509 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2510 else
2511 s := Msgs[i];
2513 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2514 yy := yy + LOADING_INTERLINE;
2515 end;
2516 end;
2518 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2519 var
2520 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2522 function monDraw (mon: TMonster): Boolean;
2523 begin
2524 result := false; // don't stop
2525 with mon do
2526 begin
2527 if alive then
2528 begin
2529 // Ëåâûé âåðõíèé óãîë
2530 aX := Obj.X div ScaleSz + 1;
2531 aY := Obj.Y div ScaleSz + 1;
2532 // Ðàçìåðû
2533 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2534 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2535 // Ïðàâûé íèæíèé óãîë
2536 aX2 := aX + aX2 - 1;
2537 aY2 := aY + aY2 - 1;
2538 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2539 end;
2540 end;
2541 end;
2543 begin
2544 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2545 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2546 begin
2547 Scale := 1;
2548 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2549 ScaleSz := 16 div Scale;
2550 // Ðàçìåðû ìèíè-êàðòû:
2551 aX := max(gMapInfo.Width div ScaleSz, 1);
2552 aY := max(gMapInfo.Height div ScaleSz, 1);
2553 // Ðàìêà êàðòû:
2554 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2556 if gWalls <> nil then
2557 begin
2558 // Ðèñóåì ñòåíû:
2559 for a := 0 to High(gWalls) do
2560 with gWalls[a] do
2561 if PanelType <> 0 then
2562 begin
2563 // Ëåâûé âåðõíèé óãîë:
2564 aX := X div ScaleSz;
2565 aY := Y div ScaleSz;
2566 // Ðàçìåðû:
2567 aX2 := max(Width div ScaleSz, 1);
2568 aY2 := max(Height div ScaleSz, 1);
2569 // Ïðàâûé íèæíèé óãîë:
2570 aX2 := aX + aX2 - 1;
2571 aY2 := aY + aY2 - 1;
2573 case PanelType of
2574 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2575 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2576 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2577 end;
2578 end;
2579 end;
2580 if gSteps <> nil then
2581 begin
2582 // Ðèñóåì ñòóïåíè:
2583 for a := 0 to High(gSteps) do
2584 with gSteps[a] do
2585 if PanelType <> 0 then
2586 begin
2587 // Ëåâûé âåðõíèé óãîë:
2588 aX := X div ScaleSz;
2589 aY := Y div ScaleSz;
2590 // Ðàçìåðû:
2591 aX2 := max(Width div ScaleSz, 1);
2592 aY2 := max(Height div ScaleSz, 1);
2593 // Ïðàâûé íèæíèé óãîë:
2594 aX2 := aX + aX2 - 1;
2595 aY2 := aY + aY2 - 1;
2597 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2598 end;
2599 end;
2600 if gLifts <> nil then
2601 begin
2602 // Ðèñóåì ëèôòû:
2603 for a := 0 to High(gLifts) do
2604 with gLifts[a] do
2605 if PanelType <> 0 then
2606 begin
2607 // Ëåâûé âåðõíèé óãîë:
2608 aX := X div ScaleSz;
2609 aY := Y div ScaleSz;
2610 // Ðàçìåðû:
2611 aX2 := max(Width div ScaleSz, 1);
2612 aY2 := max(Height div ScaleSz, 1);
2613 // Ïðàâûé íèæíèé óãîë:
2614 aX2 := aX + aX2 - 1;
2615 aY2 := aY + aY2 - 1;
2617 case LiftType of
2618 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2619 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2620 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2621 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2622 end;
2623 end;
2624 end;
2625 if gWater <> nil then
2626 begin
2627 // Ðèñóåì âîäó:
2628 for a := 0 to High(gWater) do
2629 with gWater[a] do
2630 if PanelType <> 0 then
2631 begin
2632 // Ëåâûé âåðõíèé óãîë:
2633 aX := X div ScaleSz;
2634 aY := Y div ScaleSz;
2635 // Ðàçìåðû:
2636 aX2 := max(Width div ScaleSz, 1);
2637 aY2 := max(Height div ScaleSz, 1);
2638 // Ïðàâûé íèæíèé óãîë:
2639 aX2 := aX + aX2 - 1;
2640 aY2 := aY + aY2 - 1;
2642 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2643 end;
2644 end;
2645 if gAcid1 <> nil then
2646 begin
2647 // Ðèñóåì êèñëîòó 1:
2648 for a := 0 to High(gAcid1) do
2649 with gAcid1[a] do
2650 if PanelType <> 0 then
2651 begin
2652 // Ëåâûé âåðõíèé óãîë:
2653 aX := X div ScaleSz;
2654 aY := Y div ScaleSz;
2655 // Ðàçìåðû:
2656 aX2 := max(Width div ScaleSz, 1);
2657 aY2 := max(Height div ScaleSz, 1);
2658 // Ïðàâûé íèæíèé óãîë:
2659 aX2 := aX + aX2 - 1;
2660 aY2 := aY + aY2 - 1;
2662 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2663 end;
2664 end;
2665 if gAcid2 <> nil then
2666 begin
2667 // Ðèñóåì êèñëîòó 2:
2668 for a := 0 to High(gAcid2) do
2669 with gAcid2[a] do
2670 if PanelType <> 0 then
2671 begin
2672 // Ëåâûé âåðõíèé óãîë:
2673 aX := X div ScaleSz;
2674 aY := Y div ScaleSz;
2675 // Ðàçìåðû:
2676 aX2 := max(Width div ScaleSz, 1);
2677 aY2 := max(Height div ScaleSz, 1);
2678 // Ïðàâûé íèæíèé óãîë:
2679 aX2 := aX + aX2 - 1;
2680 aY2 := aY + aY2 - 1;
2682 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2683 end;
2684 end;
2685 if gPlayers <> nil then
2686 begin
2687 // Ðèñóåì èãðîêîâ:
2688 for a := 0 to High(gPlayers) do
2689 if gPlayers[a] <> nil then with gPlayers[a] do
2690 if alive then begin
2691 // Ëåâûé âåðõíèé óãîë:
2692 aX := Obj.X div ScaleSz + 1;
2693 aY := Obj.Y div ScaleSz + 1;
2694 // Ðàçìåðû:
2695 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2696 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2697 // Ïðàâûé íèæíèé óãîë:
2698 aX2 := aX + aX2 - 1;
2699 aY2 := aY + aY2 - 1;
2701 if gPlayers[a] = p then
2702 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2703 else
2704 case Team of
2705 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2706 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2707 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2708 end;
2709 end;
2710 end;
2711 // Ðèñóåì ìîíñòðîâ
2712 g_Mons_ForEach(monDraw);
2713 end;
2714 end;
2717 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2718 procedure renderDynLightsInternal ();
2719 var
2720 lln: Integer;
2721 lx, ly, lrad: Integer;
2722 begin
2723 //TODO: lights should be in separate grid, i think
2724 // but on the other side: grid may be slower for dynlights, as their lifetime is short
2725 if not gwin_has_stencil or (g_dynLightCount < 1) then exit;
2727 // setup OpenGL parameters
2728 glStencilMask($FFFFFFFF);
2729 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2730 glEnable(GL_STENCIL_TEST);
2731 glEnable(GL_SCISSOR_TEST);
2732 glClear(GL_STENCIL_BUFFER_BIT);
2733 glStencilFunc(GL_EQUAL, 0, $ff);
2735 for lln := 0 to g_dynLightCount-1 do
2736 begin
2737 lx := g_dynLights[lln].x;
2738 ly := g_dynLights[lln].y;
2739 lrad := g_dynLights[lln].radius;
2740 if lrad < 3 then continue;
2742 if lx-sX+lrad < 0 then continue;
2743 if ly-sY+lrad < 0 then continue;
2744 if lx-sX-lrad >= gPlayerScreenSize.X then continue;
2745 if ly-sY-lrad >= gPlayerScreenSize.Y then continue;
2747 // set scissor to optimize drawing
2748 //FIXME: broken for splitscreen mode
2749 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2750 // no need to clear stencil buffer, light blitting will do it for us
2751 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2752 // draw extruded panels
2753 glDisable(GL_TEXTURE_2D);
2754 glDisable(GL_BLEND);
2755 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2756 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2757 // render light texture
2758 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2759 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2760 // blend it
2761 glEnable(GL_BLEND);
2762 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2763 glEnable(GL_TEXTURE_2D);
2764 // color and opacity
2765 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2766 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2767 glBegin(GL_QUADS);
2768 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2769 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2770 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2771 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2772 glEnd();
2773 end;
2775 // done
2776 glDisable(GL_STENCIL_TEST);
2777 glDisable(GL_BLEND);
2778 glDisable(GL_SCISSOR_TEST);
2779 glScissor(0, 0, sWidth, sHeight);
2780 end;
2783 function fixViewportForScale (): Boolean;
2784 var
2785 nx0, ny0, nw, nh: Integer;
2786 begin
2787 result := false;
2788 if (g_dbg_scale <> 1.0) then
2789 begin
2790 result := true;
2791 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
2792 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
2793 nw := round(sWidth/g_dbg_scale);
2794 nh := round(sHeight/g_dbg_scale);
2795 sX := nx0;
2796 sY := ny0;
2797 sWidth := nw;
2798 sHeight := nh;
2799 end;
2800 end;
2803 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2804 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
2805 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
2806 type
2807 TDrawCB = procedure ();
2809 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
2810 var
2811 tagmask: Integer;
2812 pan: TPanel;
2813 begin
2814 profileFrameDraw.sectionBegin(profname);
2815 if gdbg_map_use_accel_render then
2816 begin
2817 tagmask := panelTypeToTag(panType);
2818 while (gDrawPanelList.count > 0) do
2819 begin
2820 pan := TPanel(gDrawPanelList.front());
2821 if ((pan.tag and tagmask) = 0) then break;
2822 if doDraw then pan.Draw();
2823 gDrawPanelList.popFront();
2824 end;
2825 end
2826 else
2827 begin
2828 if doDraw then g_Map_DrawPanels(panType);
2829 end;
2830 profileFrameDraw.sectionEnd();
2831 end;
2833 procedure drawOther (profname: AnsiString; cb: TDrawCB);
2834 begin
2835 profileFrameDraw.sectionBegin(profname);
2836 if assigned(cb) then cb();
2837 profileFrameDraw.sectionEnd();
2838 end;
2840 begin
2841 profileFrameDraw.sectionBegin('total');
2843 // our accelerated renderer will collect all panels to gDrawPanelList
2844 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
2845 profileFrameDraw.sectionBegin('collect');
2846 if gdbg_map_use_accel_render then
2847 begin
2848 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
2849 end;
2850 profileFrameDraw.sectionEnd();
2852 profileFrameDraw.sectionBegin('skyback');
2853 g_Map_DrawBack(backXOfs, backYOfs);
2854 profileFrameDraw.sectionEnd();
2856 if setTransMatrix then
2857 begin
2858 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
2859 glTranslatef(-sX, -sY, 0);
2860 end;
2862 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
2863 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
2864 drawOther('items', @g_Items_Draw);
2865 drawOther('weapons', @g_Weapon_Draw);
2866 drawOther('shells', @g_Player_DrawShells);
2867 drawOther('drawall', @g_Player_DrawAll);
2868 drawOther('corpses', @g_Player_DrawCorpses);
2869 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
2870 drawOther('monsters', @g_Monsters_Draw);
2871 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
2872 drawOther('gfx', @g_GFX_Draw);
2873 drawOther('flags', @g_Map_DrawFlags);
2874 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
2875 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
2876 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
2877 drawOther('dynlights', @renderDynLightsInternal);
2878 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
2880 if g_debug_HealthBar then
2881 begin
2882 g_Monsters_DrawHealth();
2883 g_Player_DrawHealth();
2884 end;
2886 profileFrameDraw.mainEnd(); // map rendering
2887 end;
2890 procedure DrawMapView(x, y, w, h: Integer);
2892 var
2893 bx, by: Integer;
2894 begin
2895 glPushMatrix();
2897 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2898 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2900 sX := x;
2901 sY := y;
2902 sWidth := w;
2903 sHeight := h;
2905 fixViewportForScale();
2906 renderMapInternal(-bx, -by, true);
2908 glPopMatrix();
2909 end;
2912 procedure DrawPlayer(p: TPlayer);
2913 var
2914 px, py, a, b, c, d: Integer;
2915 //R: TRect;
2916 begin
2917 if (p = nil) or (p.FDummy) then
2918 begin
2919 glPushMatrix();
2920 g_Map_DrawBack(0, 0);
2921 glPopMatrix();
2922 Exit;
2923 end;
2925 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
2926 profileFrameDraw.mainBegin(g_profile_frame_draw);
2928 gPlayerDrawn := p;
2930 glPushMatrix();
2932 px := p.GameX + PLAYER_RECT_CX;
2933 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
2935 if (g_dbg_scale = 1.0) then
2936 begin
2937 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
2938 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
2940 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
2941 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
2943 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
2944 else if (gMapInfo.Width < gPlayerScreenSize.X) then
2945 begin
2946 // hcenter
2947 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
2948 end;
2950 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
2951 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
2952 begin
2953 // vcenter
2954 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
2955 end;
2956 end
2957 else
2958 begin
2959 // scaled, ignore level bounds
2960 a := -px+(gPlayerScreenSize.X div 2);
2961 b := -py+(gPlayerScreenSize.Y div 2);
2962 end;
2964 if p.IncCam <> 0 then
2965 begin
2966 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
2967 begin
2968 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2969 begin
2970 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2971 end;
2972 end;
2974 if py < gPlayerScreenSize.Y div 2 then
2975 begin
2976 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2977 begin
2978 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2979 end;
2980 end;
2982 if p.IncCam < 0 then
2983 begin
2984 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
2985 end;
2987 if p.IncCam > 0 then
2988 begin
2989 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
2990 end;
2991 end;
2993 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
2994 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
2995 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2997 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
2998 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
2999 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3001 sX := -a;
3002 sY := -(b+p.IncCam);
3003 sWidth := gPlayerScreenSize.X;
3004 sHeight := gPlayerScreenSize.Y;
3006 //glTranslatef(a, b+p.IncCam, 0);
3008 if (p = gPlayer1) then g_Holmes_plrViewSize(sWidth, sHeight);
3010 fixViewportForScale();
3011 p.viewPortX := sX;
3012 p.viewPortY := sY;
3013 p.viewPortW := sWidth;
3014 p.viewPortH := sHeight;
3016 if (p = gPlayer1) then g_Holmes_plrViewPos(sX, sY);
3018 renderMapInternal(-c, -d, true);
3020 if p.FSpectator then
3021 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3022 p.GameY + PLAYER_RECT_CY - 4,
3023 'X', gStdFont, 255, 255, 255, 1, True);
3025 for a := 0 to High(gCollideMap) do
3026 for b := 0 to High(gCollideMap[a]) do
3027 begin
3028 d := 0;
3029 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3030 d := d + 1;
3031 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3032 d := d + 2;
3034 case d of
3035 1: e_DrawPoint(1, b, a, 200, 200, 200);
3036 2: e_DrawPoint(1, b, a, 64, 64, 255);
3037 3: e_DrawPoint(1, b, a, 255, 0, 255);
3038 end;
3039 end;
3042 glPopMatrix();
3044 p.DrawPain();
3045 p.DrawPickup();
3046 p.DrawRulez();
3047 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3048 if g_Debug_Player then
3049 g_Player_DrawDebug(p);
3050 p.DrawGUI();
3051 end;
3053 procedure drawProfilers ();
3054 var
3055 px: Integer = -1;
3056 py: Integer = -1;
3057 begin
3058 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3059 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3060 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3061 end;
3063 procedure g_Game_Draw();
3064 var
3065 ID: DWORD;
3066 w, h: Word;
3067 ww, hh: Byte;
3068 Time: Int64;
3069 back: string;
3070 plView1, plView2: TPlayer;
3071 Split: Boolean;
3072 begin
3073 if gExit = EXIT_QUIT then Exit;
3075 Time := GetTimer() {div 1000};
3076 FPSCounter := FPSCounter+1;
3077 if Time - FPSTime >= 1000 then
3078 begin
3079 FPS := FPSCounter;
3080 FPSCounter := 0;
3081 FPSTime := Time;
3082 end;
3084 if gGameOn or (gState = STATE_FOLD) then
3085 begin
3086 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3087 begin
3088 gSpectMode := SPECT_NONE;
3089 if not gRevertPlayers then
3090 begin
3091 plView1 := gPlayer1;
3092 plView2 := gPlayer2;
3093 end
3094 else
3095 begin
3096 plView1 := gPlayer2;
3097 plView2 := gPlayer1;
3098 end;
3099 end
3100 else
3101 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3102 begin
3103 gSpectMode := SPECT_NONE;
3104 if gPlayer2 = nil then
3105 plView1 := gPlayer1
3106 else
3107 plView1 := gPlayer2;
3108 plView2 := nil;
3109 end
3110 else
3111 begin
3112 plView1 := nil;
3113 plView2 := nil;
3114 end;
3116 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3117 gSpectMode := SPECT_STATS;
3119 if gSpectMode = SPECT_PLAYERS then
3120 if gPlayers <> nil then
3121 begin
3122 plView1 := GetActivePlayer_ByID(gSpectPID1);
3123 if plView1 = nil then
3124 begin
3125 gSpectPID1 := GetActivePlayerID_Next();
3126 plView1 := GetActivePlayer_ByID(gSpectPID1);
3127 end;
3128 if gSpectViewTwo then
3129 begin
3130 plView2 := GetActivePlayer_ByID(gSpectPID2);
3131 if plView2 = nil then
3132 begin
3133 gSpectPID2 := GetActivePlayerID_Next();
3134 plView2 := GetActivePlayer_ByID(gSpectPID2);
3135 end;
3136 end;
3137 end;
3139 if gSpectMode = SPECT_MAPVIEW then
3140 begin
3141 // Ðåæèì ïðîñìîòðà êàðòû
3142 Split := False;
3143 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3144 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3145 gHearPoint1.Active := True;
3146 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3147 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3148 gHearPoint2.Active := False;
3149 end
3150 else
3151 begin
3152 Split := (plView1 <> nil) and (plView2 <> nil);
3154 // Òî÷êè ñëóõà èãðîêîâ
3155 if plView1 <> nil then
3156 begin
3157 gHearPoint1.Active := True;
3158 gHearPoint1.Coords.X := plView1.GameX;
3159 gHearPoint1.Coords.Y := plView1.GameY;
3160 end else
3161 gHearPoint1.Active := False;
3162 if plView2 <> nil then
3163 begin
3164 gHearPoint2.Active := True;
3165 gHearPoint2.Coords.X := plView2.GameX;
3166 gHearPoint2.Coords.Y := plView2.GameY;
3167 end else
3168 gHearPoint2.Active := False;
3170 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3171 gPlayerScreenSize.X := gScreenWidth-196;
3172 if Split then
3173 begin
3174 gPlayerScreenSize.Y := gScreenHeight div 2;
3175 if gScreenHeight mod 2 = 0 then
3176 Dec(gPlayerScreenSize.Y);
3177 end
3178 else
3179 gPlayerScreenSize.Y := gScreenHeight;
3181 if Split then
3182 if gScreenHeight mod 2 = 0 then
3183 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3184 else
3185 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3187 DrawPlayer(plView1);
3188 gPlayer1ScreenCoord.X := sX;
3189 gPlayer1ScreenCoord.Y := sY;
3191 if Split then
3192 begin
3193 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3195 DrawPlayer(plView2);
3196 gPlayer2ScreenCoord.X := sX;
3197 gPlayer2ScreenCoord.Y := sY;
3198 end;
3200 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3202 if Split then
3203 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3204 end;
3206 if MessageText <> '' then
3207 begin
3208 w := 0;
3209 h := 0;
3210 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3211 if Split then
3212 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3213 (gScreenHeight div 2)-(h div 2), MessageText)
3214 else
3215 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3216 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3217 end;
3219 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3221 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3222 begin
3223 // Draw spectator GUI
3224 ww := 0;
3225 hh := 0;
3226 e_TextureFontGetSize(gStdFont, ww, hh);
3227 case gSpectMode of
3228 SPECT_STATS:
3229 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3230 SPECT_MAPVIEW:
3231 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3232 SPECT_PLAYERS:
3233 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3234 end;
3235 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3236 if gSpectMode = SPECT_MAPVIEW then
3237 begin
3238 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3239 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3240 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3241 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3242 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3243 end;
3244 if gSpectMode = SPECT_PLAYERS then
3245 begin
3246 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3247 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3248 if gSpectViewTwo then
3249 begin
3250 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3251 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3252 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3253 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3254 end
3255 else
3256 begin
3257 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3258 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3259 end;
3260 end;
3261 end;
3262 end;
3264 if gPause and gGameOn and (g_ActiveWindow = nil) then
3265 begin
3266 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3267 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3269 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3270 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3271 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3272 end;
3274 if not gGameOn then
3275 begin
3276 if (gState = STATE_MENU) then
3277 begin
3278 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3279 begin
3280 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3281 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3282 end;
3283 // F3 at menu will show game loading dialog
3284 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3285 if (g_ActiveWindow <> nil) then
3286 begin
3287 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3288 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3289 end
3290 else
3291 begin
3292 // F3 at titlepic will show game loading dialog
3293 if e_KeyPressed(IK_F3) then
3294 begin
3295 g_Menu_Show_LoadMenu(true);
3296 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3297 end;
3298 end;
3299 end;
3301 if gState = STATE_FOLD then
3302 begin
3303 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3304 end;
3306 if gState = STATE_INTERCUSTOM then
3307 begin
3308 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3309 begin
3310 back := 'TEXTURE_endpic';
3311 if not g_Texture_Get(back, ID) then
3312 back := _lc[I_TEXTURE_ENDPIC];
3313 end
3314 else
3315 back := 'INTER';
3317 if g_Texture_Get(back, ID) then
3318 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3319 else
3320 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3322 DrawCustomStat();
3324 if g_ActiveWindow <> nil then
3325 begin
3326 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3327 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3328 end;
3329 end;
3331 if gState = STATE_INTERSINGLE then
3332 begin
3333 if EndingGameCounter > 0 then
3334 begin
3335 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3336 end
3337 else
3338 begin
3339 back := 'INTER';
3341 if g_Texture_Get(back, ID) then
3342 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3343 else
3344 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3346 DrawSingleStat();
3348 if g_ActiveWindow <> nil then
3349 begin
3350 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3351 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3352 end;
3353 end;
3354 end;
3356 if gState = STATE_ENDPIC then
3357 begin
3358 ID := DWORD(-1);
3359 if not g_Texture_Get('TEXTURE_endpic', ID) then
3360 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3362 if ID <> DWORD(-1) then
3363 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3364 else
3365 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3367 if g_ActiveWindow <> nil then
3368 begin
3369 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3370 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3371 end;
3372 end;
3374 if gState = STATE_SLIST then
3375 begin
3376 if g_Texture_Get('MENU_BACKGROUND', ID) then
3377 begin
3378 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3379 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3380 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3381 end;
3382 g_Serverlist_Draw(slCurrent);
3383 end;
3384 end;
3386 if g_ActiveWindow <> nil then
3387 begin
3388 if gGameOn then
3389 begin
3390 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3391 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3392 end;
3393 g_ActiveWindow.Draw();
3394 end;
3396 // draw inspector
3397 if (g_holmes_enabled) then g_Holmes_Draw();
3399 g_Console_Draw();
3401 if g_debug_Sounds and gGameOn then
3402 begin
3403 for w := 0 to High(e_SoundsArray) do
3404 for h := 0 to e_SoundsArray[w].nRefs do
3405 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3406 end;
3408 if gShowFPS then
3409 begin
3410 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3411 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3412 end;
3414 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3415 drawTime(gScreenWidth-72, gScreenHeight-16);
3417 if gGameOn then drawProfilers();
3419 g_Holmes_DrawUI();
3420 end;
3422 procedure g_Game_Quit();
3423 begin
3424 g_Game_StopAllSounds(True);
3425 gMusic.Free();
3426 g_Game_SaveOptions();
3427 g_Game_FreeData();
3428 g_PlayerModel_FreeData();
3429 g_Texture_DeleteAll();
3430 g_Frames_DeleteAll();
3431 g_Menu_Free();
3433 if NetInitDone then g_Net_Free;
3435 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3436 if gMapToDelete <> '' then
3437 g_Game_DeleteTestMap();
3439 gExit := EXIT_QUIT;
3440 PushExitEvent();
3441 end;
3443 procedure g_FatalError(Text: String);
3444 begin
3445 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3446 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3448 gExit := EXIT_SIMPLE;
3449 end;
3451 procedure g_SimpleError(Text: String);
3452 begin
3453 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3454 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3455 end;
3457 procedure g_Game_SetupScreenSize();
3458 const
3459 RES_FACTOR = 4.0 / 3.0;
3460 var
3461 s: Single;
3462 rf: Single;
3463 bw, bh: Word;
3464 begin
3465 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3466 gPlayerScreenSize.X := gScreenWidth-196;
3467 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3468 gPlayerScreenSize.Y := gScreenHeight div 2
3469 else
3470 gPlayerScreenSize.Y := gScreenHeight;
3472 // Ðàçìåð çàäíåãî ïëàíà:
3473 if BackID <> DWORD(-1) then
3474 begin
3475 s := SKY_STRETCH;
3476 if (gScreenWidth*s > gMapInfo.Width) or
3477 (gScreenHeight*s > gMapInfo.Height) then
3478 begin
3479 gBackSize.X := gScreenWidth;
3480 gBackSize.Y := gScreenHeight;
3481 end
3482 else
3483 begin
3484 e_GetTextureSize(BackID, @bw, @bh);
3485 rf := Single(bw) / Single(bh);
3486 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3487 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3488 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3489 if (s < 1.0) then s := 1.0;
3490 gBackSize.X := Round(bw*s);
3491 gBackSize.Y := Round(bh*s);
3492 end;
3493 end;
3494 end;
3496 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3497 begin
3498 g_Window_SetSize(newWidth, newHeight, nowFull);
3499 end;
3501 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3502 begin
3503 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3504 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3505 Exit;
3506 if gPlayer1 = nil then
3507 begin
3508 if g_Game_IsClient then
3509 begin
3510 if NetPlrUID1 > -1 then
3511 begin
3512 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3513 gPlayer1 := g_Player_Get(NetPlrUID1);
3514 end;
3515 Exit;
3516 end;
3518 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3519 Team := gPlayer1Settings.Team;
3521 // Ñîçäàíèå ïåðâîãî èãðîêà:
3522 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3523 gPlayer1Settings.Color,
3524 Team, False));
3525 if gPlayer1 = nil then
3526 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3527 else
3528 begin
3529 gPlayer1.Name := gPlayer1Settings.Name;
3530 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3531 if g_Game_IsServer and g_Game_IsNet then
3532 MH_SEND_PlayerCreate(gPlayer1.UID);
3533 gPlayer1.Respawn(False, True);
3535 if g_Game_IsNet and NetUseMaster then
3536 g_Net_Slist_Update;
3537 end;
3539 Exit;
3540 end;
3541 if gPlayer2 = nil then
3542 begin
3543 if g_Game_IsClient then
3544 begin
3545 if NetPlrUID2 > -1 then
3546 gPlayer2 := g_Player_Get(NetPlrUID2);
3547 Exit;
3548 end;
3550 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3551 Team := gPlayer2Settings.Team;
3553 // Ñîçäàíèå âòîðîãî èãðîêà:
3554 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3555 gPlayer2Settings.Color,
3556 Team, False));
3557 if gPlayer2 = nil then
3558 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3559 else
3560 begin
3561 gPlayer2.Name := gPlayer2Settings.Name;
3562 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3563 if g_Game_IsServer and g_Game_IsNet then
3564 MH_SEND_PlayerCreate(gPlayer2.UID);
3565 gPlayer2.Respawn(False, True);
3567 if g_Game_IsNet and NetUseMaster then
3568 g_Net_Slist_Update;
3569 end;
3571 Exit;
3572 end;
3573 end;
3575 procedure g_Game_RemovePlayer();
3576 var
3577 Pl: TPlayer;
3578 begin
3579 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3580 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3581 Exit;
3582 Pl := gPlayer2;
3583 if Pl <> nil then
3584 begin
3585 if g_Game_IsServer then
3586 begin
3587 Pl.Lives := 0;
3588 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3589 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3590 g_Player_Remove(Pl.UID);
3592 if g_Game_IsNet and NetUseMaster then
3593 g_Net_Slist_Update;
3594 end else
3595 gPlayer2 := nil;
3596 Exit;
3597 end;
3598 Pl := gPlayer1;
3599 if Pl <> nil then
3600 begin
3601 if g_Game_IsServer then
3602 begin
3603 Pl.Lives := 0;
3604 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3605 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3606 g_Player_Remove(Pl.UID);
3608 if g_Game_IsNet and NetUseMaster then
3609 g_Net_Slist_Update;
3610 end else
3611 begin
3612 gPlayer1 := nil;
3613 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3614 end;
3615 Exit;
3616 end;
3617 end;
3619 procedure g_Game_Spectate();
3620 begin
3621 g_Game_RemovePlayer();
3622 if gPlayer1 <> nil then
3623 g_Game_RemovePlayer();
3624 end;
3626 procedure g_Game_SpectateCenterView();
3627 begin
3628 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3629 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3630 end;
3632 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3633 var
3634 i, nPl: Integer;
3635 begin
3636 g_Game_Free();
3638 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3640 g_Game_ClearLoading();
3642 // Íàñòðîéêè èãðû:
3643 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3644 gAimLine := False;
3645 gShowMap := False;
3646 gGameSettings.GameType := GT_SINGLE;
3647 gGameSettings.MaxLives := 0;
3648 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3649 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3650 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3651 gSwitchGameMode := GM_SINGLE;
3653 g_Game_ExecuteEvent('ongamestart');
3655 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3656 g_Game_SetupScreenSize();
3658 // Ñîçäàíèå ïåðâîãî èãðîêà:
3659 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3660 gPlayer1Settings.Color,
3661 gPlayer1Settings.Team, False));
3662 if gPlayer1 = nil then
3663 begin
3664 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3665 Exit;
3666 end;
3668 gPlayer1.Name := gPlayer1Settings.Name;
3669 nPl := 1;
3671 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3672 if TwoPlayers then
3673 begin
3674 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3675 gPlayer2Settings.Color,
3676 gPlayer2Settings.Team, False));
3677 if gPlayer2 = nil then
3678 begin
3679 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3680 Exit;
3681 end;
3683 gPlayer2.Name := gPlayer2Settings.Name;
3684 Inc(nPl);
3685 end;
3687 // Çàãðóçêà è çàïóñê êàðòû:
3688 if not g_Game_StartMap(MAP, True) then
3689 begin
3690 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3691 Exit;
3692 end;
3694 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3695 g_Player_Init();
3697 // Ñîçäàåì áîòîâ:
3698 for i := nPl+1 to nPlayers do
3699 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3700 end;
3702 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3703 TimeLimit, GoalLimit: Word;
3704 MaxLives: Byte;
3705 Options: LongWord; nPlayers: Byte);
3706 var
3707 i, nPl: Integer;
3708 begin
3709 g_Game_Free();
3711 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3713 g_Game_ClearLoading();
3715 // Íàñòðîéêè èãðû:
3716 gGameSettings.GameType := GT_CUSTOM;
3717 gGameSettings.GameMode := GameMode;
3718 gSwitchGameMode := GameMode;
3719 gGameSettings.TimeLimit := TimeLimit;
3720 gGameSettings.GoalLimit := GoalLimit;
3721 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3722 gGameSettings.Options := Options;
3724 gCoopTotalMonstersKilled := 0;
3725 gCoopTotalSecretsFound := 0;
3726 gCoopTotalMonsters := 0;
3727 gCoopTotalSecrets := 0;
3728 gAimLine := False;
3729 gShowMap := False;
3731 g_Game_ExecuteEvent('ongamestart');
3733 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3734 g_Game_SetupScreenSize();
3736 // Ðåæèì íàáëþäàòåëÿ:
3737 if nPlayers = 0 then
3738 begin
3739 gPlayer1 := nil;
3740 gPlayer2 := nil;
3741 end;
3743 nPl := 0;
3744 if nPlayers >= 1 then
3745 begin
3746 // Ñîçäàíèå ïåðâîãî èãðîêà:
3747 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3748 gPlayer1Settings.Color,
3749 gPlayer1Settings.Team, False));
3750 if gPlayer1 = nil then
3751 begin
3752 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3753 Exit;
3754 end;
3756 gPlayer1.Name := gPlayer1Settings.Name;
3757 Inc(nPl);
3758 end;
3760 if nPlayers >= 2 then
3761 begin
3762 // Ñîçäàíèå âòîðîãî èãðîêà:
3763 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3764 gPlayer2Settings.Color,
3765 gPlayer2Settings.Team, False));
3766 if gPlayer2 = nil then
3767 begin
3768 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3769 Exit;
3770 end;
3772 gPlayer2.Name := gPlayer2Settings.Name;
3773 Inc(nPl);
3774 end;
3776 // Çàãðóçêà è çàïóñê êàðòû:
3777 if not g_Game_StartMap(Map, True) then
3778 begin
3779 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3780 Exit;
3781 end;
3783 // Íåò òî÷åê ïîÿâëåíèÿ:
3784 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3785 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3786 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3787 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3788 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3789 begin
3790 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3791 Exit;
3792 end;
3794 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3795 g_Player_Init();
3797 // Ñîçäàåì áîòîâ:
3798 for i := nPl+1 to nPlayers do
3799 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3800 end;
3802 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3803 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3804 Options: LongWord; nPlayers: Byte;
3805 IPAddr: LongWord; Port: Word);
3806 begin
3807 g_Game_Free();
3809 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3811 g_Game_ClearLoading();
3813 // Íàñòðîéêè èãðû:
3814 gGameSettings.GameType := GT_SERVER;
3815 gGameSettings.GameMode := GameMode;
3816 gSwitchGameMode := GameMode;
3817 gGameSettings.TimeLimit := TimeLimit;
3818 gGameSettings.GoalLimit := GoalLimit;
3819 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3820 gGameSettings.Options := Options;
3822 gCoopTotalMonstersKilled := 0;
3823 gCoopTotalSecretsFound := 0;
3824 gCoopTotalMonsters := 0;
3825 gCoopTotalSecrets := 0;
3826 gAimLine := False;
3827 gShowMap := False;
3829 g_Game_ExecuteEvent('ongamestart');
3831 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3832 g_Game_SetupScreenSize();
3834 // Ðåæèì íàáëþäàòåëÿ:
3835 if nPlayers = 0 then
3836 begin
3837 gPlayer1 := nil;
3838 gPlayer2 := nil;
3839 end;
3841 if nPlayers >= 1 then
3842 begin
3843 // Ñîçäàíèå ïåðâîãî èãðîêà:
3844 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3845 gPlayer1Settings.Color,
3846 gPlayer1Settings.Team, False));
3847 if gPlayer1 = nil then
3848 begin
3849 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3850 Exit;
3851 end;
3853 gPlayer1.Name := gPlayer1Settings.Name;
3854 end;
3856 if nPlayers >= 2 then
3857 begin
3858 // Ñîçäàíèå âòîðîãî èãðîêà:
3859 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3860 gPlayer2Settings.Color,
3861 gPlayer2Settings.Team, False));
3862 if gPlayer2 = nil then
3863 begin
3864 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3865 Exit;
3866 end;
3868 gPlayer2.Name := gPlayer2Settings.Name;
3869 end;
3871 // Ñòàðòóåì ñåðâåð
3872 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3873 begin
3874 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3875 Exit;
3876 end;
3878 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3880 // Çàãðóçêà è çàïóñê êàðòû:
3881 if not g_Game_StartMap(Map, True) then
3882 begin
3883 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3884 Exit;
3885 end;
3887 // Íåò òî÷åê ïîÿâëåíèÿ:
3888 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3889 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3890 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3891 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3892 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3893 begin
3894 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3895 Exit;
3896 end;
3898 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3899 g_Player_Init();
3901 NetState := NET_STATE_GAME;
3902 end;
3904 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3905 var
3906 Map: String;
3907 WadName: string;
3908 Ptr: Pointer;
3909 T: Cardinal;
3910 MID: Byte;
3911 State: Byte;
3912 OuterLoop: Boolean;
3913 newResPath: string;
3914 InMsg: TMsg;
3915 begin
3916 g_Game_Free();
3918 State := 0;
3919 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3920 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3922 g_Game_ClearLoading();
3924 // Íàñòðîéêè èãðû:
3925 gGameSettings.GameType := GT_CLIENT;
3927 gCoopTotalMonstersKilled := 0;
3928 gCoopTotalSecretsFound := 0;
3929 gCoopTotalMonsters := 0;
3930 gCoopTotalSecrets := 0;
3931 gAimLine := False;
3932 gShowMap := False;
3934 g_Game_ExecuteEvent('ongamestart');
3936 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3937 g_Game_SetupScreenSize();
3939 NetState := NET_STATE_AUTH;
3941 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3942 // Ñòàðòóåì êëèåíò
3943 if not g_Net_Connect(Addr, Port) then
3944 begin
3945 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3946 NetState := NET_STATE_NONE;
3947 Exit;
3948 end;
3950 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3951 MC_SEND_Info(PW);
3952 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3954 OuterLoop := True;
3955 while OuterLoop do
3956 begin
3957 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3958 begin
3959 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3960 begin
3961 Ptr := NetEvent.packet^.data;
3962 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
3963 continue;
3965 MID := InMsg.ReadByte();
3967 if (MID = NET_MSG_INFO) and (State = 0) then
3968 begin
3969 NetMyID := InMsg.ReadByte();
3970 NetPlrUID1 := InMsg.ReadWord();
3972 WadName := InMsg.ReadString();
3973 Map := InMsg.ReadString();
3975 gWADHash := InMsg.ReadMD5();
3977 gGameSettings.GameMode := InMsg.ReadByte();
3978 gSwitchGameMode := gGameSettings.GameMode;
3979 gGameSettings.GoalLimit := InMsg.ReadWord();
3980 gGameSettings.TimeLimit := InMsg.ReadWord();
3981 gGameSettings.MaxLives := InMsg.ReadByte();
3982 gGameSettings.Options := InMsg.ReadLongWord();
3983 T := InMsg.ReadLongWord();
3985 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3986 if newResPath = '' then
3987 begin
3988 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3989 newResPath := g_Res_DownloadWAD(WadName);
3990 if newResPath = '' then
3991 begin
3992 g_FatalError(_lc[I_NET_ERR_HASH]);
3993 enet_packet_destroy(NetEvent.packet);
3994 NetState := NET_STATE_NONE;
3995 Exit;
3996 end;
3997 end;
3998 newResPath := ExtractRelativePath(MapsDir, newResPath);
4000 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4001 gPlayer1Settings.Color,
4002 gPlayer1Settings.Team, False));
4004 if gPlayer1 = nil then
4005 begin
4006 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4008 enet_packet_destroy(NetEvent.packet);
4009 NetState := NET_STATE_NONE;
4010 Exit;
4011 end;
4013 gPlayer1.Name := gPlayer1Settings.Name;
4014 gPlayer1.UID := NetPlrUID1;
4015 gPlayer1.Reset(True);
4017 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4018 begin
4019 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4021 enet_packet_destroy(NetEvent.packet);
4022 NetState := NET_STATE_NONE;
4023 Exit;
4024 end;
4026 gTime := T;
4028 State := 1;
4029 OuterLoop := False;
4030 enet_packet_destroy(NetEvent.packet);
4031 break;
4032 end
4033 else
4034 enet_packet_destroy(NetEvent.packet);
4035 end
4036 else
4037 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4038 begin
4039 State := 0;
4040 if (NetEvent.data <= NET_DISC_MAX) then
4041 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4042 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4043 OuterLoop := False;
4044 Break;
4045 end;
4046 end;
4048 ProcessLoading(true);
4050 e_PollInput();
4052 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4053 begin
4054 State := 0;
4055 break;
4056 end;
4057 end;
4059 if State <> 1 then
4060 begin
4061 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4062 NetState := NET_STATE_NONE;
4063 Exit;
4064 end;
4066 gLMSRespawn := LMS_RESPAWN_NONE;
4067 gLMSRespawnTime := 0;
4069 g_Player_Init();
4070 NetState := NET_STATE_GAME;
4071 MC_SEND_FullStateRequest;
4072 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
4073 end;
4075 procedure g_Game_SaveOptions();
4076 begin
4077 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4078 end;
4080 procedure g_Game_ChangeMap(const MapPath: String);
4081 var
4082 Force: Boolean;
4083 begin
4084 g_Game_ClearLoading();
4086 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4087 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4088 if gExitByTrigger then
4089 begin
4090 Force := False;
4091 gExitByTrigger := False;
4092 end;
4093 if not g_Game_StartMap(MapPath, Force) then
4094 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4095 end;
4097 procedure g_Game_Restart();
4098 var
4099 Map: string;
4100 begin
4101 if g_Game_IsClient then
4102 Exit;
4103 map := g_ExtractFileName(gMapInfo.Map);
4105 MessageTime := 0;
4106 gGameOn := False;
4107 g_Game_ClearLoading();
4108 g_Game_StartMap(Map, True, gCurrentMapFileName);
4109 end;
4111 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4112 var
4113 NewWAD, ResName: String;
4114 I: Integer;
4115 begin
4116 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4117 g_Player_RemoveAllCorpses();
4119 if (not g_Game_IsClient) and
4120 (gSwitchGameMode <> gGameSettings.GameMode) and
4121 (gGameSettings.GameMode <> GM_SINGLE) then
4122 begin
4123 if gSwitchGameMode = GM_CTF then
4124 gGameSettings.MaxLives := 0;
4125 gGameSettings.GameMode := gSwitchGameMode;
4126 Force := True;
4127 end else
4128 gSwitchGameMode := gGameSettings.GameMode;
4130 g_Player_ResetTeams();
4132 if isWadPath(Map) then
4133 begin
4134 NewWAD := g_ExtractWadName(Map);
4135 ResName := g_ExtractFileName(Map);
4136 if g_Game_IsServer then
4137 begin
4138 gWADHash := MD5File(MapsDir + NewWAD);
4139 g_Game_LoadWAD(NewWAD);
4140 end else
4141 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4142 g_Game_ClientWAD(NewWAD, gWADHash);
4143 end else
4144 ResName := Map;
4146 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4147 if Result then
4148 begin
4149 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4151 gState := STATE_NONE;
4152 g_ActiveWindow := nil;
4153 gGameOn := True;
4155 DisableCheats();
4156 ResetTimer();
4158 if gGameSettings.GameMode = GM_CTF then
4159 begin
4160 g_Map_ResetFlag(FLAG_RED);
4161 g_Map_ResetFlag(FLAG_BLUE);
4162 // CTF, à ôëàãîâ íåò:
4163 if not g_Map_HaveFlagPoints() then
4164 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4165 end;
4166 end
4167 else
4168 begin
4169 gState := STATE_MENU;
4170 gGameOn := False;
4171 end;
4173 gExit := 0;
4174 gPause := False;
4175 gTime := 0;
4176 NetTimeToUpdate := 1;
4177 NetTimeToReliable := 0;
4178 NetTimeToMaster := NetMasterRate;
4179 gLMSRespawn := LMS_RESPAWN_NONE;
4180 gLMSRespawnTime := 0;
4181 gMissionFailed := False;
4182 gNextMap := '';
4184 gCoopMonstersKilled := 0;
4185 gCoopSecretsFound := 0;
4187 gVoteInProgress := False;
4188 gVotePassed := False;
4189 gVoteCount := 0;
4190 gVoted := False;
4192 gStatsOff := False;
4194 if not gGameOn then Exit;
4196 g_Game_SpectateCenterView();
4198 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4199 begin
4200 gLMSRespawn := LMS_RESPAWN_WARMUP;
4201 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4202 gLMSSoftSpawn := True;
4203 if NetMode = NET_SERVER then
4204 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4205 else
4206 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4207 end;
4209 if NetMode = NET_SERVER then
4210 begin
4211 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4213 // Ìàñòåðñåðâåð
4214 if NetUseMaster then
4215 begin
4216 if (NetMHost = nil) or (NetMPeer = nil) then
4217 if not g_Net_Slist_Connect then
4218 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4220 g_Net_Slist_Update;
4221 end;
4223 if NetClients <> nil then
4224 for I := 0 to High(NetClients) do
4225 if NetClients[I].Used then
4226 begin
4227 NetClients[I].Voted := False;
4228 if NetClients[I].RequestedFullUpdate then
4229 begin
4230 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4231 NetClients[I].RequestedFullUpdate := False;
4232 end;
4233 end;
4235 g_Net_UnbanNonPermHosts();
4236 end;
4238 if gLastMap then
4239 begin
4240 gCoopTotalMonstersKilled := 0;
4241 gCoopTotalSecretsFound := 0;
4242 gCoopTotalMonsters := 0;
4243 gCoopTotalSecrets := 0;
4244 gLastMap := False;
4245 end;
4247 g_Game_ExecuteEvent('onmapstart');
4248 end;
4250 procedure SetFirstLevel();
4251 begin
4252 gNextMap := '';
4254 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4255 if MapList = nil then
4256 Exit;
4258 SortSArray(MapList);
4259 gNextMap := MapList[Low(MapList)];
4261 MapList := nil;
4262 end;
4264 procedure g_Game_ExitLevel(const Map: AnsiString);
4265 begin
4266 gNextMap := Map;
4268 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4269 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4270 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4271 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4273 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4274 if gGameSettings.GameType = GT_SINGLE then
4275 gExit := EXIT_ENDLEVELSINGLE
4276 else // Âûøëè â âûõîä â Ñâîåé èãðå
4277 begin
4278 gExit := EXIT_ENDLEVELCUSTOM;
4279 if gGameSettings.GameMode = GM_COOP then
4280 g_Player_RememberAll;
4282 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4283 begin
4284 gLastMap := True;
4285 if gGameSettings.GameMode = GM_COOP then
4286 gStatsOff := True;
4288 gStatsPressed := True;
4289 gNextMap := 'MAP01';
4291 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4292 g_Game_NextLevel;
4294 if g_Game_IsNet then
4295 begin
4296 MH_SEND_GameStats();
4297 MH_SEND_CoopStats();
4298 end;
4299 end;
4300 end;
4301 end;
4303 procedure g_Game_RestartLevel();
4304 var
4305 Map: string;
4306 begin
4307 if gGameSettings.GameMode = GM_SINGLE then
4308 begin
4309 g_Game_Restart();
4310 Exit;
4311 end;
4312 gExit := EXIT_ENDLEVELCUSTOM;
4313 Map := g_ExtractFileName(gMapInfo.Map);
4314 gNextMap := Map;
4315 end;
4317 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4318 var
4319 gWAD: String;
4320 begin
4321 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4322 Exit;
4323 if not g_Game_IsClient then
4324 Exit;
4325 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4326 if gWAD = '' then
4327 begin
4328 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4329 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4330 if gWAD = '' then
4331 begin
4332 g_Game_Free();
4333 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4334 Exit;
4335 end;
4336 end;
4337 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4338 g_Game_LoadWAD(NewWAD);
4339 end;
4341 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4342 var
4343 i, n, nb, nr: Integer;
4345 function monRespawn (mon: TMonster): Boolean;
4346 begin
4347 result := false; // don't stop
4348 if not mon.FNoRespawn then mon.Respawn();
4349 end;
4351 begin
4352 if not g_Game_IsServer then Exit;
4353 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4354 gLMSRespawn := LMS_RESPAWN_NONE;
4355 gLMSRespawnTime := 0;
4356 MessageTime := 0;
4358 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4359 begin
4360 gMissionFailed := True;
4361 g_Game_RestartLevel;
4362 Exit;
4363 end;
4365 n := 0; nb := 0; nr := 0;
4366 for i := Low(gPlayers) to High(gPlayers) do
4367 if (gPlayers[i] <> nil) and
4368 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4369 (gPlayers[i] is TBot)) then
4370 begin
4371 Inc(n);
4372 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4373 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4374 end;
4376 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4377 begin
4378 // wait a second until the fuckers finally decide to join
4379 gLMSRespawn := LMS_RESPAWN_WARMUP;
4380 gLMSRespawnTime := gTime + 1000;
4381 gLMSSoftSpawn := NoMapRestart;
4382 Exit;
4383 end;
4385 g_Player_RemoveAllCorpses;
4386 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4387 if g_Game_IsNet then
4388 MH_SEND_GameEvent(NET_EV_LMS_START);
4390 for i := Low(gPlayers) to High(gPlayers) do
4391 begin
4392 if gPlayers[i] = nil then continue;
4393 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4394 // don't touch normal spectators
4395 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4396 begin
4397 gPlayers[i].FNoRespawn := True;
4398 gPlayers[i].Lives := 0;
4399 if g_Game_IsNet then
4400 MH_SEND_PlayerStats(gPlayers[I].UID);
4401 continue;
4402 end;
4403 gPlayers[i].FNoRespawn := False;
4404 gPlayers[i].Lives := gGameSettings.MaxLives;
4405 gPlayers[i].Respawn(False, True);
4406 if gGameSettings.GameMode = GM_COOP then
4407 begin
4408 gPlayers[i].Frags := 0;
4409 gPlayers[i].RecallState;
4410 end;
4411 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4412 gPlayer1 := g_Player_Get(gLMSPID1);
4413 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4414 gPlayer2 := g_Player_Get(gLMSPID2);
4415 end;
4417 g_Items_RestartRound();
4420 g_Mons_ForEach(monRespawn);
4422 gLMSSoftSpawn := False;
4423 end;
4425 function g_Game_GetFirstMap(WAD: String): String;
4426 begin
4427 Result := '';
4429 MapList := g_Map_GetMapsList(WAD);
4430 if MapList = nil then
4431 Exit;
4433 SortSArray(MapList);
4434 Result := MapList[Low(MapList)];
4436 if not g_Map_Exist(WAD + ':\' + Result) then
4437 Result := '';
4439 MapList := nil;
4440 end;
4442 function g_Game_GetNextMap(): String;
4443 var
4444 I: Integer;
4445 Map: string;
4446 begin
4447 Result := '';
4449 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4450 if MapList = nil then
4451 Exit;
4453 Map := g_ExtractFileName(gMapInfo.Map);
4455 SortSArray(MapList);
4456 MapIndex := -255;
4457 for I := Low(MapList) to High(MapList) do
4458 if Map = MapList[I] then
4459 begin
4460 MapIndex := I;
4461 Break;
4462 end;
4464 if MapIndex <> -255 then
4465 begin
4466 if MapIndex = High(MapList) then
4467 Result := MapList[Low(MapList)]
4468 else
4469 Result := MapList[MapIndex + 1];
4471 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4472 end;
4474 MapList := nil;
4475 end;
4477 procedure g_Game_NextLevel();
4478 begin
4479 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4480 gExit := EXIT_ENDLEVELCUSTOM
4481 else
4482 begin
4483 gExit := EXIT_ENDLEVELSINGLE;
4484 Exit;
4485 end;
4487 if gNextMap <> '' then Exit;
4488 gNextMap := g_Game_GetNextMap();
4489 end;
4491 function g_Game_IsTestMap(): Boolean;
4492 begin
4493 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4494 end;
4496 procedure g_Game_DeleteTestMap();
4497 var
4498 a: Integer;
4499 //MapName: AnsiString;
4500 WadName: string;
4502 WAD: TWADFile;
4503 MapList: SArray;
4504 time: Integer;
4506 begin
4507 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4508 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4509 if (a = 0) then exit;
4511 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
4512 WadName := Copy(gMapToDelete, 1, a+3);
4513 Delete(gMapToDelete, 1, a+5);
4514 gMapToDelete := UpperCase(gMapToDelete);
4515 //MapName := '';
4516 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4519 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4520 if MapName <> TEST_MAP_NAME then
4521 Exit;
4523 if not gTempDelete then
4524 begin
4525 time := g_GetFileTime(WadName);
4526 WAD := TWADFile.Create();
4528 // ×èòàåì Wad-ôàéë:
4529 if not WAD.ReadFile(WadName) then
4530 begin // Íåò òàêîãî WAD-ôàéëà
4531 WAD.Free();
4532 Exit;
4533 end;
4535 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4536 WAD.CreateImage();
4537 MapList := WAD.GetResourcesList('');
4539 if MapList <> nil then
4540 for a := 0 to High(MapList) do
4541 if MapList[a] = MapName then
4542 begin
4543 // Óäàëÿåì è ñîõðàíÿåì:
4544 WAD.RemoveResource('', MapName);
4545 WAD.SaveTo(WadName);
4546 Break;
4547 end;
4549 WAD.Free();
4550 g_SetFileTime(WadName, time);
4551 end else
4553 if gTempDelete then DeleteFile(WadName);
4554 end;
4556 procedure GameCVars(P: SArray);
4557 var
4558 a, b: Integer;
4559 stat: TPlayerStatArray;
4560 cmd, s: string;
4561 config: TConfig;
4562 begin
4563 stat := nil;
4564 cmd := LowerCase(P[0]);
4565 if cmd = 'r_showfps' then
4566 begin
4567 if (Length(P) > 1) and
4568 ((P[1] = '1') or (P[1] = '0')) then
4569 gShowFPS := (P[1][1] = '1');
4571 if gShowFPS then
4572 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4573 else
4574 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4575 end
4576 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4577 begin
4578 with gGameSettings do
4579 begin
4580 if (Length(P) > 1) and
4581 ((P[1] = '1') or (P[1] = '0')) then
4582 begin
4583 if (P[1][1] = '1') then
4584 Options := Options or GAME_OPTION_TEAMDAMAGE
4585 else
4586 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4587 end;
4589 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4590 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4591 else
4592 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4594 if g_Game_IsNet then MH_SEND_GameSettings;
4595 end;
4596 end
4597 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4598 begin
4599 with gGameSettings do
4600 begin
4601 if (Length(P) > 1) and
4602 ((P[1] = '1') or (P[1] = '0')) then
4603 begin
4604 if (P[1][1] = '1') then
4605 Options := Options or GAME_OPTION_WEAPONSTAY
4606 else
4607 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4608 end;
4610 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4611 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4612 else
4613 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4615 if g_Game_IsNet then MH_SEND_GameSettings;
4616 end;
4617 end
4618 else if cmd = 'g_gamemode' then
4619 begin
4620 a := g_Game_TextToMode(P[1]);
4621 if a = GM_SINGLE then a := GM_COOP;
4622 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4623 begin
4624 gSwitchGameMode := a;
4625 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4626 (gState = STATE_INTERSINGLE) then
4627 gSwitchGameMode := GM_SINGLE;
4628 if not gGameOn then
4629 gGameSettings.GameMode := gSwitchGameMode;
4630 end;
4631 if gSwitchGameMode = gGameSettings.GameMode then
4632 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4633 [g_Game_ModeToText(gGameSettings.GameMode)]))
4634 else
4635 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4636 [g_Game_ModeToText(gGameSettings.GameMode),
4637 g_Game_ModeToText(gSwitchGameMode)]));
4638 end
4639 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4640 begin
4641 with gGameSettings do
4642 begin
4643 if (Length(P) > 1) and
4644 ((P[1] = '1') or (P[1] = '0')) then
4645 begin
4646 if (P[1][1] = '1') then
4647 Options := Options or GAME_OPTION_ALLOWEXIT
4648 else
4649 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4650 end;
4652 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4653 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4654 else
4655 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4656 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4658 if g_Game_IsNet then MH_SEND_GameSettings;
4659 end;
4660 end
4661 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4662 begin
4663 with gGameSettings do
4664 begin
4665 if (Length(P) > 1) and
4666 ((P[1] = '1') or (P[1] = '0')) then
4667 begin
4668 if (P[1][1] = '1') then
4669 Options := Options or GAME_OPTION_MONSTERS
4670 else
4671 Options := Options and (not GAME_OPTION_MONSTERS);
4672 end;
4674 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4675 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4676 else
4677 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4678 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4680 if g_Game_IsNet then MH_SEND_GameSettings;
4681 end;
4682 end
4683 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4684 begin
4685 with gGameSettings do
4686 begin
4687 if (Length(P) > 1) and
4688 ((P[1] = '1') or (P[1] = '0')) then
4689 begin
4690 if (P[1][1] = '1') then
4691 Options := Options or GAME_OPTION_BOTVSPLAYER
4692 else
4693 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4694 end;
4696 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4697 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4698 else
4699 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4701 if g_Game_IsNet then MH_SEND_GameSettings;
4702 end;
4703 end
4704 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4705 begin
4706 with gGameSettings do
4707 begin
4708 if (Length(P) > 1) and
4709 ((P[1] = '1') or (P[1] = '0')) then
4710 begin
4711 if (P[1][1] = '1') then
4712 Options := Options or GAME_OPTION_BOTVSMONSTER
4713 else
4714 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4715 end;
4717 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4718 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4719 else
4720 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4722 if g_Game_IsNet then MH_SEND_GameSettings;
4723 end;
4724 end
4725 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4726 begin
4727 if Length(P) > 1 then
4728 begin
4729 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4730 gGameSettings.WarmupTime := 30
4731 else
4732 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4733 end;
4735 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4736 [gGameSettings.WarmupTime]));
4737 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4738 end
4739 else if cmd = 'net_interp' then
4740 begin
4741 if (Length(P) > 1) then
4742 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4744 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4745 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4746 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4747 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4748 config.Free();
4749 end
4750 else if cmd = 'net_forceplayerupdate' then
4751 begin
4752 if (Length(P) > 1) and
4753 ((P[1] = '1') or (P[1] = '0')) then
4754 NetForcePlayerUpdate := (P[1][1] = '1');
4756 if NetForcePlayerUpdate then
4757 g_Console_Add('net_forceplayerupdate = 1')
4758 else
4759 g_Console_Add('net_forceplayerupdate = 0');
4760 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4761 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4762 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4763 config.Free();
4764 end
4765 else if cmd = 'net_predictself' then
4766 begin
4767 if (Length(P) > 1) and
4768 ((P[1] = '1') or (P[1] = '0')) then
4769 NetPredictSelf := (P[1][1] = '1');
4771 if NetPredictSelf then
4772 g_Console_Add('net_predictself = 1')
4773 else
4774 g_Console_Add('net_predictself = 0');
4775 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4776 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4777 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4778 config.Free();
4779 end
4780 else if cmd = 'sv_name' then
4781 begin
4782 if (Length(P) > 1) and (Length(P[1]) > 0) then
4783 begin
4784 NetServerName := P[1];
4785 if Length(NetServerName) > 64 then
4786 SetLength(NetServerName, 64);
4787 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4788 g_Net_Slist_Update;
4789 end;
4791 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4792 end
4793 else if cmd = 'sv_passwd' then
4794 begin
4795 if (Length(P) > 1) and (Length(P[1]) > 0) then
4796 begin
4797 NetPassword := P[1];
4798 if Length(NetPassword) > 24 then
4799 SetLength(NetPassword, 24);
4800 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4801 g_Net_Slist_Update;
4802 end;
4804 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4805 end
4806 else if cmd = 'sv_maxplrs' then
4807 begin
4808 if (Length(P) > 1) then
4809 begin
4810 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4811 if g_Game_IsServer and g_Game_IsNet then
4812 begin
4813 b := 0;
4814 for a := 0 to High(NetClients) do
4815 if NetClients[a].Used then
4816 begin
4817 Inc(b);
4818 if b > NetMaxClients then
4819 begin
4820 s := g_Player_Get(NetClients[a].Player).Name;
4821 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4822 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4823 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4824 end;
4825 end;
4826 if NetUseMaster then
4827 g_Net_Slist_Update;
4828 end;
4829 end;
4831 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4832 end
4833 else if cmd = 'sv_public' then
4834 begin
4835 if (Length(P) > 1) then
4836 begin
4837 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4838 if g_Game_IsServer and g_Game_IsNet then
4839 if NetUseMaster then
4840 begin
4841 if NetMPeer = nil then
4842 if not g_Net_Slist_Connect() then
4843 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4844 g_Net_Slist_Update();
4845 end
4846 else
4847 if NetMPeer <> nil then
4848 g_Net_Slist_Disconnect();
4849 end;
4851 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4852 end
4853 else if cmd = 'sv_intertime' then
4854 begin
4855 if (Length(P) > 1) then
4856 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4858 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4859 end
4860 else if cmd = 'p1_name' then
4861 begin
4862 if (Length(P) > 1) and gGameOn then
4863 begin
4864 if g_Game_IsClient then
4865 begin
4866 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4867 MC_SEND_PlayerSettings;
4868 end
4869 else
4870 if gPlayer1 <> nil then
4871 begin
4872 gPlayer1.Name := b_Text_Unformat(P[1]);
4873 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4874 end
4875 else
4876 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4877 end;
4878 end
4879 else if cmd = 'p2_name' then
4880 begin
4881 if (Length(P) > 1) and gGameOn then
4882 begin
4883 if g_Game_IsClient then
4884 begin
4885 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4886 MC_SEND_PlayerSettings;
4887 end
4888 else
4889 if gPlayer2 <> nil then
4890 begin
4891 gPlayer2.Name := b_Text_Unformat(P[1]);
4892 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4893 end
4894 else
4895 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4896 end;
4897 end
4898 else if cmd = 'p1_color' then
4899 begin
4900 if Length(P) > 3 then
4901 if g_Game_IsClient then
4902 begin
4903 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4904 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4905 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4906 MC_SEND_PlayerSettings;
4907 end
4908 else
4909 if gPlayer1 <> nil then
4910 begin
4911 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4912 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4913 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4914 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4915 end
4916 else
4917 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4918 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4919 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4920 end
4921 else if (cmd = 'p2_color') and not g_Game_IsNet then
4922 begin
4923 if Length(P) > 3 then
4924 if g_Game_IsClient then
4925 begin
4926 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4927 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4928 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4929 MC_SEND_PlayerSettings;
4930 end
4931 else
4932 if gPlayer2 <> nil then
4933 begin
4934 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4935 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4936 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4937 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4938 end
4939 else
4940 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4941 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4942 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4943 end
4944 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4945 begin
4946 if cmd = 'r_showtime' then
4947 begin
4948 if (Length(P) > 1) and
4949 ((P[1] = '1') or (P[1] = '0')) then
4950 gShowTime := (P[1][1] = '1');
4952 if gShowTime then
4953 g_Console_Add(_lc[I_MSG_TIME_ON])
4954 else
4955 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4956 end
4957 else if cmd = 'r_showscore' then
4958 begin
4959 if (Length(P) > 1) and
4960 ((P[1] = '1') or (P[1] = '0')) then
4961 gShowGoals := (P[1][1] = '1');
4963 if gShowGoals then
4964 g_Console_Add(_lc[I_MSG_SCORE_ON])
4965 else
4966 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4967 end
4968 else if cmd = 'r_showstat' then
4969 begin
4970 if (Length(P) > 1) and
4971 ((P[1] = '1') or (P[1] = '0')) then
4972 gShowStat := (P[1][1] = '1');
4974 if gShowStat then
4975 g_Console_Add(_lc[I_MSG_STATS_ON])
4976 else
4977 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4978 end
4979 else if cmd = 'r_showkillmsg' then
4980 begin
4981 if (Length(P) > 1) and
4982 ((P[1] = '1') or (P[1] = '0')) then
4983 gShowKillMsg := (P[1][1] = '1');
4985 if gShowKillMsg then
4986 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4987 else
4988 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4989 end
4990 else if cmd = 'r_showlives' then
4991 begin
4992 if (Length(P) > 1) and
4993 ((P[1] = '1') or (P[1] = '0')) then
4994 gShowLives := (P[1][1] = '1');
4996 if gShowLives then
4997 g_Console_Add(_lc[I_MSG_LIVES_ON])
4998 else
4999 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5000 end
5001 else if cmd = 'r_showspect' then
5002 begin
5003 if (Length(P) > 1) and
5004 ((P[1] = '1') or (P[1] = '0')) then
5005 gSpectHUD := (P[1][1] = '1');
5007 if gSpectHUD then
5008 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5009 else
5010 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5011 end
5012 else if cmd = 'r_showping' then
5013 begin
5014 if (Length(P) > 1) and
5015 ((P[1] = '1') or (P[1] = '0')) then
5016 gShowPing := (P[1][1] = '1');
5018 if gShowPing then
5019 g_Console_Add(_lc[I_MSG_PING_ON])
5020 else
5021 g_Console_Add(_lc[I_MSG_PING_OFF]);
5022 end
5023 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5024 begin
5025 if Length(P) > 1 then
5026 begin
5027 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5028 gGameSettings.GoalLimit := 0
5029 else
5030 begin
5031 b := 0;
5033 if gGameSettings.GameMode = GM_DM then
5034 begin // DM
5035 stat := g_Player_GetStats();
5036 if stat <> nil then
5037 for a := 0 to High(stat) do
5038 if stat[a].Frags > b then
5039 b := stat[a].Frags;
5040 end
5041 else // TDM/CTF
5042 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5044 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5045 end;
5047 if g_Game_IsNet then MH_SEND_GameSettings;
5048 end;
5050 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5051 end
5052 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5053 begin
5054 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5055 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5057 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5058 [gGameSettings.TimeLimit div 3600,
5059 (gGameSettings.TimeLimit div 60) mod 60,
5060 gGameSettings.TimeLimit mod 60]));
5061 if g_Game_IsNet then MH_SEND_GameSettings;
5062 end
5063 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5064 begin
5065 if Length(P) > 1 then
5066 begin
5067 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5068 gGameSettings.MaxLives := 0
5069 else
5070 begin
5071 b := 0;
5072 stat := g_Player_GetStats();
5073 if stat <> nil then
5074 for a := 0 to High(stat) do
5075 if stat[a].Lives > b then
5076 b := stat[a].Lives;
5077 gGameSettings.MaxLives :=
5078 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5079 end;
5080 end;
5082 g_Console_Add(Format(_lc[I_MSG_LIVES],
5083 [gGameSettings.MaxLives]));
5084 if g_Game_IsNet then MH_SEND_GameSettings;
5085 end;
5086 end;
5087 end;
5090 procedure DebugCommands(P: SArray);
5091 var
5092 a, b: Integer;
5093 cmd: string;
5094 //pt: TDFPoint;
5095 mon: TMonster;
5096 begin
5097 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5098 if gDebugMode then
5099 begin
5100 cmd := LowerCase(P[0]);
5101 if cmd = 'd_window' then
5102 begin
5103 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5104 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5105 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5106 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5107 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5108 end
5109 else if cmd = 'd_sounds' then
5110 begin
5111 if (Length(P) > 1) and
5112 ((P[1] = '1') or (P[1] = '0')) then
5113 g_Debug_Sounds := (P[1][1] = '1');
5115 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5116 end
5117 else if cmd = 'd_frames' then
5118 begin
5119 if (Length(P) > 1) and
5120 ((P[1] = '1') or (P[1] = '0')) then
5121 g_Debug_Frames := (P[1][1] = '1');
5123 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5124 end
5125 else if cmd = 'd_winmsg' then
5126 begin
5127 if (Length(P) > 1) and
5128 ((P[1] = '1') or (P[1] = '0')) then
5129 g_Debug_WinMsgs := (P[1][1] = '1');
5131 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5132 end
5133 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5134 begin
5135 if (Length(P) > 1) and
5136 ((P[1] = '1') or (P[1] = '0')) then
5137 g_Debug_MonsterOff := (P[1][1] = '1');
5139 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5140 end
5141 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5142 begin
5143 if Length(P) > 1 then
5144 case P[1][1] of
5145 '0': g_debug_BotAIOff := 0;
5146 '1': g_debug_BotAIOff := 1;
5147 '2': g_debug_BotAIOff := 2;
5148 '3': g_debug_BotAIOff := 3;
5149 end;
5151 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5152 end
5153 else if cmd = 'd_monster' then
5154 begin
5155 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5156 if Length(P) < 2 then
5157 begin
5158 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5159 g_Console_Add('ID | Name');
5160 for b := MONSTER_DEMON to MONSTER_MAN do
5161 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5162 end else
5163 begin
5164 a := StrToIntDef(P[1], 0);
5165 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5166 a := g_Mons_TypeIdByName(P[1]);
5168 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5169 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5170 else
5171 begin
5172 with gPlayer1.Obj do
5173 begin
5174 mon := g_Monsters_Create(a,
5175 X + Rect.X + (Rect.Width div 2),
5176 Y + Rect.Y + Rect.Height,
5177 gPlayer1.Direction, True);
5178 end;
5179 if (Length(P) > 2) and (mon <> nil) then
5180 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5181 end;
5182 end;
5183 end
5184 else if (cmd = 'd_health') then
5185 begin
5186 if (Length(P) > 1) and
5187 ((P[1] = '1') or (P[1] = '0')) then
5188 g_debug_HealthBar := (P[1][1] = '1');
5190 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5191 end
5192 else if (cmd = 'd_player') then
5193 begin
5194 if (Length(P) > 1) and
5195 ((P[1] = '1') or (P[1] = '0')) then
5196 g_debug_Player := (P[1][1] = '1');
5198 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5199 end
5200 else if (cmd = 'd_joy') then
5201 begin
5202 for a := 1 to 8 do
5203 g_Console_Add(e_JoystickStateToString(a));
5204 end;
5205 end
5206 else
5207 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5208 end;
5211 procedure GameCheats(P: SArray);
5212 var
5213 cmd: string;
5214 f, a: Integer;
5215 plr: TPlayer;
5216 begin
5217 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5218 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5219 begin
5220 g_Console_Add('not available');
5221 exit;
5222 end;
5223 plr := gPlayer1;
5224 if plr = nil then
5225 begin
5226 g_Console_Add('where is the player?!');
5227 exit;
5228 end;
5229 cmd := LowerCase(P[0]);
5230 // god
5231 if cmd = 'god' then
5232 begin
5233 plr.GodMode := not plr.GodMode;
5234 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5235 exit;
5236 end;
5237 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5238 if cmd = 'give' then
5239 begin
5240 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5241 for f := 1 to High(P) do
5242 begin
5243 cmd := LowerCase(P[f]);
5244 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5245 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5246 if cmd = 'exit' then
5247 begin
5248 if gTriggers <> nil then
5249 begin
5250 for a := 0 to High(gTriggers) do
5251 begin
5252 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5253 begin
5254 g_Console_Add('player left the map');
5255 gExitByTrigger := True;
5256 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5257 g_Game_ExitLevel(gTriggers[a].tgcMap);
5258 break;
5259 end;
5260 end;
5261 end;
5262 continue;
5263 end;
5265 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5266 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5267 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5268 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5269 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5271 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5272 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5274 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5275 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;
5277 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5278 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5280 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5281 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5283 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5284 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5286 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5287 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5288 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5290 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5291 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5292 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5293 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;
5294 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5295 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5297 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;
5298 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;
5299 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;
5300 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;
5301 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;
5302 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;
5304 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5305 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;
5307 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;
5308 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;
5310 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5312 if cmd = 'ammo' then
5313 begin
5314 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5315 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5316 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5317 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5318 plr.GiveItem(ITEM_AMMO_FUELCAN);
5319 g_Console_Add('player got some ammo');
5320 continue;
5321 end;
5323 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5324 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5326 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5327 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5329 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5330 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5332 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5333 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5335 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5337 if cmd = 'weapons' then
5338 begin
5339 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5340 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5341 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5342 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5343 plr.GiveItem(ITEM_WEAPON_PLASMA);
5344 plr.GiveItem(ITEM_WEAPON_BFG);
5345 g_Console_Add('player got weapons');
5346 continue;
5347 end;
5349 if cmd = 'keys' then
5350 begin
5351 plr.GiveItem(ITEM_KEY_RED);
5352 plr.GiveItem(ITEM_KEY_GREEN);
5353 plr.GiveItem(ITEM_KEY_BLUE);
5354 g_Console_Add('player got all keys');
5355 continue;
5356 end;
5358 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5359 end;
5360 exit;
5361 end;
5362 // open
5363 if cmd = 'open' then
5364 begin
5365 g_Console_Add('player activated sesame');
5366 g_Triggers_OpenAll();
5367 exit;
5368 end;
5369 // fly
5370 if cmd = 'fly' then
5371 begin
5372 gFly := not gFly;
5373 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5374 exit;
5375 end;
5376 // noclip
5377 if cmd = 'noclip' then
5378 begin
5379 plr.SwitchNoClip;
5380 g_Console_Add('wall hardeness adjusted');
5381 exit;
5382 end;
5383 // notarget
5384 if cmd = 'notarget' then
5385 begin
5386 plr.NoTarget := not plr.NoTarget;
5387 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5388 exit;
5389 end;
5390 // noreload
5391 if cmd = 'noreload' then
5392 begin
5393 plr.NoReload := not plr.NoReload;
5394 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5395 exit;
5396 end;
5397 // speedy
5398 if cmd = 'speedy' then
5399 begin
5400 MAX_RUNVEL := 32-MAX_RUNVEL;
5401 g_Console_Add('speed adjusted');
5402 exit;
5403 end;
5404 // jumpy
5405 if cmd = 'jumpy' then
5406 begin
5407 VEL_JUMP := 30-VEL_JUMP;
5408 g_Console_Add('jump height adjusted');
5409 exit;
5410 end;
5411 // automap
5412 if cmd = 'automap' then
5413 begin
5414 gShowMap := not gShowMap;
5415 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5416 exit;
5417 end;
5418 // aimline
5419 if cmd = 'aimline' then
5420 begin
5421 gAimLine := not gAimLine;
5422 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5423 exit;
5424 end;
5425 end;
5427 procedure GameCommands(P: SArray);
5428 var
5429 a, b: Integer;
5430 s, pw: String;
5431 chstr: string;
5432 cmd: string;
5433 pl: pTNetClient = nil;
5434 plr: TPlayer;
5435 prt: Word;
5436 nm: Boolean;
5437 listen: LongWord;
5438 begin
5439 // Îáùèå êîìàíäû:
5440 cmd := LowerCase(P[0]);
5441 chstr := '';
5442 if (cmd = 'quit') or
5443 (cmd = 'exit') then
5444 begin
5445 g_Game_Free();
5446 g_Game_Quit();
5447 Exit;
5448 end
5449 else if cmd = 'pause' then
5450 begin
5451 if (g_ActiveWindow = nil) then
5452 g_Game_Pause(not gPause);
5453 end
5454 else if cmd = 'endgame' then
5455 gExit := EXIT_SIMPLE
5456 else if cmd = 'restart' then
5457 begin
5458 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5459 begin
5460 if g_Game_IsClient then
5461 begin
5462 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5463 Exit;
5464 end;
5465 g_Game_Restart();
5466 end else
5467 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5468 end
5469 else if cmd = 'kick' then
5470 begin
5471 if g_Game_IsServer then
5472 begin
5473 if Length(P) < 2 then
5474 begin
5475 g_Console_Add('kick <name>');
5476 Exit;
5477 end;
5478 if P[1] = '' then
5479 begin
5480 g_Console_Add('kick <name>');
5481 Exit;
5482 end;
5484 if g_Game_IsNet then
5485 pl := g_Net_Client_ByName(P[1]);
5486 if (pl <> nil) then
5487 begin
5488 s := g_Net_ClientName_ByID(pl^.ID);
5489 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5490 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5491 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5492 if NetUseMaster then
5493 g_Net_Slist_Update;
5494 end else if gPlayers <> nil then
5495 for a := Low(gPlayers) to High(gPlayers) do
5496 if gPlayers[a] <> nil then
5497 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5498 begin
5499 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5500 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5501 continue;
5502 gPlayers[a].Lives := 0;
5503 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5504 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5505 g_Player_Remove(gPlayers[a].UID);
5506 if NetUseMaster then
5507 g_Net_Slist_Update;
5508 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5509 g_Bot_MixNames();
5510 end;
5511 end else
5512 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5513 end
5514 else if cmd = 'kick_id' then
5515 begin
5516 if g_Game_IsServer and g_Game_IsNet then
5517 begin
5518 if Length(P) < 2 then
5519 begin
5520 g_Console_Add('kick_id <client ID>');
5521 Exit;
5522 end;
5523 if P[1] = '' then
5524 begin
5525 g_Console_Add('kick_id <client ID>');
5526 Exit;
5527 end;
5529 a := StrToIntDef(P[1], 0);
5530 if (NetClients <> nil) and (a <= High(NetClients)) then
5531 begin
5532 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5533 begin
5534 s := g_Net_ClientName_ByID(NetClients[a].ID);
5535 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5536 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5537 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5538 if NetUseMaster then
5539 g_Net_Slist_Update;
5540 end;
5541 end;
5542 end else
5543 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5544 end
5545 else if cmd = 'ban' then
5546 begin
5547 if g_Game_IsServer and g_Game_IsNet then
5548 begin
5549 if Length(P) < 2 then
5550 begin
5551 g_Console_Add('ban <name>');
5552 Exit;
5553 end;
5554 if P[1] = '' then
5555 begin
5556 g_Console_Add('ban <name>');
5557 Exit;
5558 end;
5560 pl := g_Net_Client_ByName(P[1]);
5561 if (pl <> nil) then
5562 begin
5563 s := g_Net_ClientName_ByID(pl^.ID);
5564 g_Net_BanHost(pl^.Peer^.address.host, False);
5565 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5566 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5567 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5568 if NetUseMaster then
5569 g_Net_Slist_Update;
5570 end else
5571 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5572 end else
5573 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5574 end
5575 else if cmd = 'ban_id' then
5576 begin
5577 if g_Game_IsServer and g_Game_IsNet then
5578 begin
5579 if Length(P) < 2 then
5580 begin
5581 g_Console_Add('ban_id <client ID>');
5582 Exit;
5583 end;
5584 if P[1] = '' then
5585 begin
5586 g_Console_Add('ban_id <client ID>');
5587 Exit;
5588 end;
5590 a := StrToIntDef(P[1], 0);
5591 if (NetClients <> nil) and (a <= High(NetClients)) then
5592 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5593 begin
5594 s := g_Net_ClientName_ByID(NetClients[a].ID);
5595 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5596 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
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 = 'permban' 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('permban <name>');
5612 Exit;
5613 end;
5614 if P[1] = '' then
5615 begin
5616 g_Console_Add('permban <name>');
5617 Exit;
5618 end;
5620 pl := g_Net_Client_ByName(P[1]);
5621 if (pl <> nil) then
5622 begin
5623 s := g_Net_ClientName_ByID(pl^.ID);
5624 g_Net_BanHost(pl^.Peer^.address.host);
5625 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5626 g_Net_SaveBanList();
5627 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5628 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5629 if NetUseMaster then
5630 g_Net_Slist_Update;
5631 end else
5632 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5633 end else
5634 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5635 end
5636 else if cmd = 'permban_id' then
5637 begin
5638 if g_Game_IsServer and g_Game_IsNet then
5639 begin
5640 if Length(P) < 2 then
5641 begin
5642 g_Console_Add('permban_id <client ID>');
5643 Exit;
5644 end;
5645 if P[1] = '' then
5646 begin
5647 g_Console_Add('permban_id <client ID>');
5648 Exit;
5649 end;
5651 a := StrToIntDef(P[1], 0);
5652 if (NetClients <> nil) and (a <= High(NetClients)) then
5653 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5654 begin
5655 s := g_Net_ClientName_ByID(NetClients[a].ID);
5656 g_Net_BanHost(NetClients[a].Peer^.address.host);
5657 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5658 g_Net_SaveBanList();
5659 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5660 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5661 if NetUseMaster then
5662 g_Net_Slist_Update;
5663 end;
5664 end else
5665 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5666 end
5667 else if cmd = 'unban' then
5668 begin
5669 if g_Game_IsServer and g_Game_IsNet then
5670 begin
5671 if Length(P) < 2 then
5672 begin
5673 g_Console_Add('unban <IP Address>');
5674 Exit;
5675 end;
5676 if P[1] = '' then
5677 begin
5678 g_Console_Add('unban <IP Address>');
5679 Exit;
5680 end;
5682 if g_Net_UnbanHost(P[1]) then
5683 begin
5684 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5685 g_Net_SaveBanList();
5686 end else
5687 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5688 end else
5689 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5690 end
5691 else if cmd = 'clientlist' then
5692 begin
5693 if g_Game_IsServer and g_Game_IsNet then
5694 begin
5695 b := 0;
5696 if NetClients <> nil then
5697 for a := Low(NetClients) to High(NetClients) do
5698 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5699 begin
5700 plr := g_Player_Get(NetClients[a].Player);
5701 if plr = nil then continue;
5702 Inc(b);
5703 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5704 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5705 end;
5706 if b = 0 then
5707 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5708 end else
5709 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5710 end
5711 else if cmd = 'connect' then
5712 begin
5713 if (NetMode = NET_NONE) then
5714 begin
5715 if Length(P) < 2 then
5716 begin
5717 g_Console_Add('connect <IP> [port] [password]');
5718 Exit;
5719 end;
5720 if P[1] = '' then
5721 begin
5722 g_Console_Add('connect <IP> [port] [password]');
5723 Exit;
5724 end;
5726 if Length(P) > 2 then
5727 prt := StrToIntDef(P[2], 25666)
5728 else
5729 prt := 25666;
5731 if Length(P) > 3 then
5732 pw := P[3]
5733 else
5734 pw := '';
5736 g_Game_StartClient(P[1], prt, pw);
5737 end;
5738 end
5739 else if cmd = 'disconnect' then
5740 begin
5741 if (NetMode = NET_CLIENT) then
5742 g_Net_Disconnect();
5743 end
5744 else if cmd = 'reconnect' then
5745 begin
5746 if (NetMode = NET_SERVER) then
5747 Exit;
5749 if (NetMode = NET_CLIENT) then
5750 begin
5751 g_Net_Disconnect();
5752 gExit := EXIT_SIMPLE;
5753 EndGame;
5754 end;
5756 //TODO: Use last successful password to reconnect, instead of ''
5757 g_Game_StartClient(NetClientIP, NetClientPort, '');
5758 end
5759 else if (cmd = 'addbot') or
5760 (cmd = 'bot_add') then
5761 begin
5762 if Length(P) > 1 then
5763 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5764 else
5765 g_Bot_Add(TEAM_NONE, 2);
5766 end
5767 else if cmd = 'bot_addlist' then
5768 begin
5769 if Length(P) > 1 then
5770 if Length(P) = 2 then
5771 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5772 else
5773 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5774 end
5775 else if cmd = 'bot_removeall' then
5776 g_Bot_RemoveAll()
5777 else if cmd = 'chat' then
5778 begin
5779 if g_Game_IsNet then
5780 begin
5781 if Length(P) > 1 then
5782 begin
5783 for a := 1 to High(P) do
5784 chstr := chstr + P[a] + ' ';
5786 if Length(chstr) > 200 then SetLength(chstr, 200);
5788 if Length(chstr) < 1 then
5789 begin
5790 g_Console_Add('chat <text>');
5791 Exit;
5792 end;
5794 chstr := b_Text_Format(chstr);
5795 if g_Game_IsClient then
5796 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5797 else
5798 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5799 end
5800 else
5801 g_Console_Add('chat <text>');
5802 end else
5803 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5804 end
5805 else if cmd = 'teamchat' then
5806 begin
5807 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5808 begin
5809 if Length(P) > 1 then
5810 begin
5811 for a := 1 to High(P) do
5812 chstr := chstr + P[a] + ' ';
5814 if Length(chstr) > 200 then SetLength(chstr, 200);
5816 if Length(chstr) < 1 then
5817 begin
5818 g_Console_Add('teamchat <text>');
5819 Exit;
5820 end;
5822 chstr := b_Text_Format(chstr);
5823 if g_Game_IsClient then
5824 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5825 else
5826 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5827 gPlayer1Settings.Team);
5828 end
5829 else
5830 g_Console_Add('teamchat <text>');
5831 end else
5832 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5833 end
5834 else if cmd = 'game' then
5835 begin
5836 if gGameSettings.GameType <> GT_NONE then
5837 begin
5838 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5839 Exit;
5840 end;
5841 if Length(P) = 1 then
5842 begin
5843 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5844 Exit;
5845 end;
5846 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5847 P[1] := addWadExtension(P[1]);
5848 if FileExists(MapsDir + P[1]) then
5849 begin
5850 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5851 if Length(P) < 3 then
5852 begin
5853 SetLength(P, 3);
5854 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5855 end;
5857 s := P[1] + ':\' + UpperCase(P[2]);
5859 if g_Map_Exist(MapsDir + s) then
5860 begin
5861 // Çàïóñêàåì ñâîþ èãðó
5862 g_Game_Free();
5863 with gGameSettings do
5864 begin
5865 GameMode := g_Game_TextToMode(gcGameMode);
5866 if gSwitchGameMode <> GM_NONE then
5867 GameMode := gSwitchGameMode;
5868 if GameMode = GM_NONE then GameMode := GM_DM;
5869 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5870 b := 1;
5871 if Length(P) >= 4 then
5872 b := StrToIntDef(P[3], 1);
5873 g_Game_StartCustom(s, GameMode, TimeLimit,
5874 GoalLimit, MaxLives, Options, b);
5875 end;
5876 end
5877 else
5878 if P[2] = '' then
5879 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5880 else
5881 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5882 end else
5883 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5884 end
5885 else if cmd = 'host' then
5886 begin
5887 if gGameSettings.GameType <> GT_NONE then
5888 begin
5889 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5890 Exit;
5891 end;
5892 if Length(P) < 4 then
5893 begin
5894 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5895 Exit;
5896 end;
5897 if not StrToIp(P[1], listen) then
5898 Exit;
5899 prt := StrToIntDef(P[2], 25666);
5901 P[3] := addWadExtension(P[3]);
5902 if FileExists(MapsDir + P[3]) then
5903 begin
5904 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5905 if Length(P) < 5 then
5906 begin
5907 SetLength(P, 5);
5908 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5909 end;
5911 s := P[3] + ':\' + UpperCase(P[4]);
5913 if g_Map_Exist(MapsDir + s) then
5914 begin
5915 // Çàïóñêàåì ñâîþ èãðó
5916 g_Game_Free();
5917 with gGameSettings do
5918 begin
5919 GameMode := g_Game_TextToMode(gcGameMode);
5920 if gSwitchGameMode <> GM_NONE then
5921 GameMode := gSwitchGameMode;
5922 if GameMode = GM_NONE then GameMode := GM_DM;
5923 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5924 b := 0;
5925 if Length(P) >= 6 then
5926 b := StrToIntDef(P[5], 0);
5927 g_Game_StartServer(s, GameMode, TimeLimit,
5928 GoalLimit, MaxLives, Options, b, listen, prt);
5929 end;
5930 end
5931 else
5932 if P[4] = '' then
5933 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5934 else
5935 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5936 end else
5937 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5938 end
5939 else if cmd = 'map' then
5940 begin
5941 if Length(P) = 1 then
5942 begin
5943 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5944 begin
5945 g_Console_Add(cmd + ' <MAP>');
5946 g_Console_Add(cmd + ' <WAD> [MAP]');
5947 end else
5948 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5949 end else
5950 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5951 begin
5952 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5953 if Length(P) < 3 then
5954 begin
5955 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5956 s := UpperCase(P[1]);
5957 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5958 begin // Êàðòà íàøëàñü
5959 gExitByTrigger := False;
5960 if gGameOn then
5961 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5962 gNextMap := s;
5963 gExit := EXIT_ENDLEVELCUSTOM;
5964 end
5965 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5966 g_Game_ChangeMap(s);
5967 end else
5968 begin
5969 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5970 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5971 P[1] := addWadExtension(P[1]);
5972 if FileExists(MapsDir + P[1]) then
5973 begin
5974 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5975 SetLength(P, 3);
5976 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5978 s := P[1] + ':\' + P[2];
5980 if g_Map_Exist(MapsDir + s) then
5981 begin
5982 gExitByTrigger := False;
5983 if gGameOn then
5984 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5985 gNextMap := s;
5986 gExit := EXIT_ENDLEVELCUSTOM;
5987 end
5988 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5989 g_Game_ChangeMap(s);
5990 end else
5991 if P[2] = '' then
5992 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5993 else
5994 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5995 end else
5996 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5997 end;
5998 end else
5999 begin
6000 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6001 P[1] := addWadExtension(P[1]);
6002 if FileExists(MapsDir + P[1]) then
6003 begin
6004 // Íàøëè WAD ôàéë
6005 P[2] := UpperCase(P[2]);
6006 s := P[1] + ':\' + P[2];
6008 if g_Map_Exist(MapsDir + s) then
6009 begin // Íàøëè êàðòó
6010 gExitByTrigger := False;
6011 if gGameOn then
6012 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6013 gNextMap := s;
6014 gExit := EXIT_ENDLEVELCUSTOM;
6015 end
6016 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6017 g_Game_ChangeMap(s);
6018 end else
6019 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6020 end else
6021 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6022 end;
6023 end else
6024 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6025 end
6026 else if cmd = 'nextmap' then
6027 begin
6028 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6029 g_Console_Add(_lc[I_MSG_NOT_GAME])
6030 else begin
6031 nm := True;
6032 if Length(P) = 1 then
6033 begin
6034 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6035 begin
6036 g_Console_Add(cmd + ' <MAP>');
6037 g_Console_Add(cmd + ' <WAD> [MAP]');
6038 end else begin
6039 nm := False;
6040 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6041 end;
6042 end else
6043 begin
6044 nm := False;
6045 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6046 begin
6047 if Length(P) < 3 then
6048 begin
6049 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6050 s := UpperCase(P[1]);
6051 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6052 begin // Êàðòà íàøëàñü
6053 gExitByTrigger := False;
6054 gNextMap := s;
6055 nm := True;
6056 end else
6057 begin
6058 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6059 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6060 P[1] := addWadExtension(P[1]);
6061 if FileExists(MapsDir + P[1]) then
6062 begin
6063 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6064 SetLength(P, 3);
6065 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6067 s := P[1] + ':\' + P[2];
6069 if g_Map_Exist(MapsDir + s) then
6070 begin // Óñòàíàâëèâàåì êàðòó
6071 gExitByTrigger := False;
6072 gNextMap := s;
6073 nm := True;
6074 end else
6075 if P[2] = '' then
6076 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6077 else
6078 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6079 end else
6080 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6081 end;
6082 end else
6083 begin
6084 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6085 P[1] := addWadExtension(P[1]);
6086 if FileExists(MapsDir + P[1]) then
6087 begin
6088 // Íàøëè WAD ôàéë
6089 P[2] := UpperCase(P[2]);
6090 s := P[1] + ':\' + P[2];
6092 if g_Map_Exist(MapsDir + s) then
6093 begin // Íàøëè êàðòó
6094 gExitByTrigger := False;
6095 gNextMap := s;
6096 nm := True;
6097 end else
6098 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6099 end else
6100 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6101 end;
6102 end else
6103 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6104 end;
6105 if nm then
6106 if gNextMap = '' then
6107 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6108 else
6109 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6110 end;
6111 end
6112 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6113 begin
6114 if not gGameOn then
6115 g_Console_Add(_lc[I_MSG_NOT_GAME])
6116 else
6117 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6118 begin
6119 gExitByTrigger := False;
6120 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6121 if (gNextMap = '') and (gTriggers <> nil) then
6122 for a := 0 to High(gTriggers) do
6123 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6124 begin
6125 gExitByTrigger := True;
6126 //gNextMap := gTriggers[a].Data.MapName;
6127 gNextMap := gTriggers[a].tgcMap;
6128 Break;
6129 end;
6130 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6131 if gNextMap = '' then
6132 gNextMap := g_Game_GetNextMap();
6133 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6134 if not isWadPath(gNextMap) then
6135 s := gGameSettings.WAD + ':\' + gNextMap
6136 else
6137 s := gNextMap;
6138 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6139 if g_Map_Exist(MapsDir + s) then
6140 gExit := EXIT_ENDLEVELCUSTOM
6141 else
6142 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6143 end else
6144 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6145 end
6146 else if (cmd = 'event') then
6147 begin
6148 if (Length(P) <= 1) then
6149 begin
6150 for a := 0 to High(gEvents) do
6151 if gEvents[a].Command = '' then
6152 g_Console_Add(gEvents[a].Name + ' <none>')
6153 else
6154 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6155 Exit;
6156 end;
6157 if (Length(P) = 2) then
6158 begin
6159 for a := 0 to High(gEvents) do
6160 if gEvents[a].Name = P[1] then
6161 if gEvents[a].Command = '' then
6162 g_Console_Add(gEvents[a].Name + ' <none>')
6163 else
6164 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6165 Exit;
6166 end;
6167 for a := 0 to High(gEvents) do
6168 if gEvents[a].Name = P[1] then
6169 begin
6170 gEvents[a].Command := '';
6171 for b := 2 to High(P) do
6172 if Pos(' ', P[b]) = 0 then
6173 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6174 else
6175 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6176 gEvents[a].Command := Trim(gEvents[a].Command);
6177 Exit;
6178 end;
6179 end
6180 else if cmd = 'suicide' then
6181 begin
6182 if gGameOn then
6183 begin
6184 if g_Game_IsClient then
6185 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6186 else
6187 begin
6188 if gPlayer1 <> nil then
6189 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6190 if gPlayer2 <> nil then
6191 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6192 end;
6193 end;
6194 end
6195 // Êîìàíäû Ñâîåé èãðû:
6196 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6197 begin
6198 if cmd = 'bot_addred' then
6199 begin
6200 if Length(P) > 1 then
6201 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6202 else
6203 g_Bot_Add(TEAM_RED, 2);
6204 end
6205 else if cmd = 'bot_addblue' then
6206 begin
6207 if Length(P) > 1 then
6208 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6209 else
6210 g_Bot_Add(TEAM_BLUE, 2);
6211 end
6212 else if cmd = 'spectate' then
6213 begin
6214 if not gGameOn then
6215 Exit;
6216 g_Game_Spectate();
6217 end
6218 else if cmd = 'say' then
6219 begin
6220 if g_Game_IsServer and g_Game_IsNet then
6221 begin
6222 if Length(P) > 1 then
6223 begin
6224 chstr := '';
6225 for a := 1 to High(P) do
6226 chstr := chstr + P[a] + ' ';
6228 if Length(chstr) > 200 then SetLength(chstr, 200);
6230 if Length(chstr) < 1 then
6231 begin
6232 g_Console_Add('say <text>');
6233 Exit;
6234 end;
6236 chstr := b_Text_Format(chstr);
6237 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6238 end
6239 else g_Console_Add('say <text>');
6240 end else
6241 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6242 end
6243 else if cmd = 'tell' then
6244 begin
6245 if g_Game_IsServer and g_Game_IsNet then
6246 begin
6247 if (Length(P) > 2) and (P[1] <> '') then
6248 begin
6249 chstr := '';
6250 for a := 2 to High(P) do
6251 chstr := chstr + P[a] + ' ';
6253 if Length(chstr) > 200 then SetLength(chstr, 200);
6255 if Length(chstr) < 1 then
6256 begin
6257 g_Console_Add('tell <playername> <text>');
6258 Exit;
6259 end;
6261 pl := g_Net_Client_ByName(P[1]);
6262 if pl <> nil then
6263 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6264 else
6265 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6266 end
6267 else g_Console_Add('tell <playername> <text>');
6268 end else
6269 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6270 end
6271 else if (cmd = 'overtime') and not g_Game_IsClient then
6272 begin
6273 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6274 Exit;
6275 // Äîïîëíèòåëüíîå âðåìÿ:
6276 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6278 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6279 [gGameSettings.TimeLimit div 3600,
6280 (gGameSettings.TimeLimit div 60) mod 60,
6281 gGameSettings.TimeLimit mod 60]));
6282 if g_Game_IsNet then MH_SEND_GameSettings;
6283 end
6284 else if (cmd = 'rcon_password') and g_Game_IsClient then
6285 begin
6286 if (Length(P) <= 1) then
6287 g_Console_Add('rcon_password <password>')
6288 else
6289 MC_SEND_RCONPassword(P[1]);
6290 end
6291 else if cmd = 'rcon' then
6292 begin
6293 if g_Game_IsClient then
6294 begin
6295 if Length(P) > 1 then
6296 begin
6297 chstr := '';
6298 for a := 1 to High(P) do
6299 chstr := chstr + P[a] + ' ';
6301 if Length(chstr) > 200 then SetLength(chstr, 200);
6303 if Length(chstr) < 1 then
6304 begin
6305 g_Console_Add('rcon <command>');
6306 Exit;
6307 end;
6309 MC_SEND_RCONCommand(chstr);
6310 end
6311 else g_Console_Add('rcon <command>');
6312 end;
6313 end
6314 else if cmd = 'ready' then
6315 begin
6316 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6317 gLMSRespawnTime := gTime + 100;
6318 end
6319 else if (cmd = 'callvote') and g_Game_IsNet then
6320 begin
6321 if Length(P) > 1 then
6322 begin
6323 chstr := '';
6324 for a := 1 to High(P) do begin
6325 if a > 1 then chstr := chstr + ' ';
6326 chstr := chstr + P[a];
6327 end;
6329 if Length(chstr) > 200 then SetLength(chstr, 200);
6331 if Length(chstr) < 1 then
6332 begin
6333 g_Console_Add('callvote <command>');
6334 Exit;
6335 end;
6337 if g_Game_IsClient then
6338 MC_SEND_Vote(True, chstr)
6339 else
6340 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6341 g_Console_Process('vote', True);
6342 end
6343 else
6344 g_Console_Add('callvote <command>');
6345 end
6346 else if (cmd = 'vote') and g_Game_IsNet then
6347 begin
6348 if g_Game_IsClient then
6349 MC_SEND_Vote(False)
6350 else if gVoteInProgress then
6351 begin
6352 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6353 a := Floor((NetClientCount+1)/2.0) + 1
6354 else
6355 a := Floor(NetClientCount/2.0) + 1;
6356 if gVoted then
6357 begin
6358 Dec(gVoteCount);
6359 gVoted := False;
6360 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6361 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6362 end
6363 else
6364 begin
6365 Inc(gVoteCount);
6366 gVoted := True;
6367 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6368 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6369 g_Game_CheckVote;
6370 end;
6371 end;
6372 end
6373 end;
6374 end;
6376 procedure g_TakeScreenShot();
6377 var
6378 a: Word;
6379 FileName: string;
6380 ssdir, t: string;
6381 st: TStream;
6382 ok: Boolean;
6383 begin
6384 if e_NoGraphics then Exit;
6385 ssdir := GameDir+'/screenshots';
6386 if not findFileCI(ssdir, true) then
6387 begin
6388 // try to create dir
6389 try
6390 CreateDir(ssdir);
6391 except
6392 end;
6393 if not findFileCI(ssdir, true) then exit; // alas
6394 end;
6395 try
6396 for a := 1 to High(Word) do
6397 begin
6398 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6399 t := FileName;
6400 if findFileCI(t, true) then continue;
6401 if not findFileCI(FileName) then
6402 begin
6403 ok := false;
6404 st := createDiskFile(FileName);
6405 try
6406 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6407 ok := true;
6408 finally
6409 st.Free();
6410 end;
6411 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6412 break;
6413 end;
6414 end;
6415 except
6416 end;
6417 end;
6419 procedure g_Game_InGameMenu(Show: Boolean);
6420 begin
6421 if (g_ActiveWindow = nil) and Show then
6422 begin
6423 if gGameSettings.GameType = GT_SINGLE then
6424 g_GUI_ShowWindow('GameSingleMenu')
6425 else
6426 begin
6427 if g_Game_IsClient then
6428 g_GUI_ShowWindow('GameClientMenu')
6429 else
6430 if g_Game_IsNet then
6431 g_GUI_ShowWindow('GameServerMenu')
6432 else
6433 g_GUI_ShowWindow('GameCustomMenu');
6434 end;
6435 g_Sound_PlayEx('MENU_OPEN');
6437 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6438 if (not g_Game_IsNet) then
6439 g_Game_Pause(True);
6440 end
6441 else
6442 if (g_ActiveWindow <> nil) and (not Show) then
6443 begin
6444 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6445 if (not g_Game_IsNet) then
6446 g_Game_Pause(False);
6447 end;
6448 end;
6450 procedure g_Game_Pause(Enable: Boolean);
6451 begin
6452 if not gGameOn then
6453 Exit;
6455 if gPause = Enable then
6456 Exit;
6458 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6459 Exit;
6461 gPause := Enable;
6462 g_Game_PauseAllSounds(Enable);
6463 end;
6465 procedure g_Game_PauseAllSounds(Enable: Boolean);
6466 var
6467 i: Integer;
6468 begin
6469 // Òðèããåðû:
6470 if gTriggers <> nil then
6471 for i := 0 to High(gTriggers) do
6472 with gTriggers[i] do
6473 if (TriggerType = TRIGGER_SOUND) and
6474 (Sound <> nil) and
6475 Sound.IsPlaying() then
6476 begin
6477 Sound.Pause(Enable);
6478 end;
6480 // Çâóêè èãðîêîâ:
6481 if gPlayers <> nil then
6482 for i := 0 to High(gPlayers) do
6483 if gPlayers[i] <> nil then
6484 gPlayers[i].PauseSounds(Enable);
6486 // Ìóçûêà:
6487 if gMusic <> nil then
6488 gMusic.Pause(Enable);
6489 end;
6491 procedure g_Game_StopAllSounds(all: Boolean);
6492 var
6493 i: Integer;
6494 begin
6495 if gTriggers <> nil then
6496 for i := 0 to High(gTriggers) do
6497 with gTriggers[i] do
6498 if (TriggerType = TRIGGER_SOUND) and
6499 (Sound <> nil) then
6500 Sound.Stop();
6502 if gMusic <> nil then
6503 gMusic.Stop();
6505 if all then
6506 e_StopChannels();
6507 end;
6509 procedure g_Game_UpdateTriggerSounds();
6510 var
6511 i: Integer;
6512 begin
6513 if gTriggers <> nil then
6514 for i := 0 to High(gTriggers) do
6515 with gTriggers[i] do
6516 if (TriggerType = TRIGGER_SOUND) and
6517 (Sound <> nil) and
6518 (tgcLocal) and
6519 Sound.IsPlaying() then
6520 begin
6521 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6522 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6523 begin
6524 Sound.SetPan(0.5 - tgcPan/255.0);
6525 Sound.SetVolume(tgcVolume/255.0);
6526 end
6527 else
6528 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6529 end;
6530 end;
6532 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6533 begin
6534 Result := False;
6535 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6536 begin
6537 Result := True;
6538 Exit;
6539 end;
6540 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6541 begin
6542 Result := True;
6543 Exit;
6544 end;
6545 if gSpectMode <> SPECT_PLAYERS then
6546 Exit;
6547 if gSpectPID1 = UID then
6548 begin
6549 Result := True;
6550 Exit;
6551 end;
6552 if gSpectViewTwo and (gSpectPID2 = UID) then
6553 begin
6554 Result := True;
6555 Exit;
6556 end;
6557 end;
6559 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6560 var
6561 Pl: TPlayer;
6562 begin
6563 Result := False;
6564 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6565 begin
6566 Result := True;
6567 Exit;
6568 end;
6569 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6570 begin
6571 Result := True;
6572 Exit;
6573 end;
6574 if gSpectMode <> SPECT_PLAYERS then
6575 Exit;
6576 Pl := g_Player_Get(gSpectPID1);
6577 if (Pl <> nil) and (Pl.Team = Team) then
6578 begin
6579 Result := True;
6580 Exit;
6581 end;
6582 if gSpectViewTwo then
6583 begin
6584 Pl := g_Player_Get(gSpectPID2);
6585 if (Pl <> nil) and (Pl.Team = Team) then
6586 begin
6587 Result := True;
6588 Exit;
6589 end;
6590 end;
6591 end;
6593 procedure g_Game_Message(Msg: string; Time: Word);
6594 begin
6595 MessageText := b_Text_Format(Msg);
6596 MessageTime := Time;
6597 end;
6599 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6600 var
6601 a: Integer;
6602 begin
6603 case gAnnouncer of
6604 ANNOUNCE_NONE:
6605 Exit;
6606 ANNOUNCE_ME,
6607 ANNOUNCE_MEPLUS:
6608 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6609 Exit;
6610 end;
6611 for a := 0 to 3 do
6612 if goodsnd[a].IsPlaying() then
6613 Exit;
6615 goodsnd[Random(4)].Play();
6616 end;
6618 procedure g_Game_Announce_KillCombo(Param: Integer);
6619 var
6620 UID: Word;
6621 c, n: Byte;
6622 Pl: TPlayer;
6623 Name: String;
6624 begin
6625 UID := Param and $FFFF;
6626 c := Param shr 16;
6627 if c < 2 then
6628 Exit;
6630 Pl := g_Player_Get(UID);
6631 if Pl = nil then
6632 Name := '?'
6633 else
6634 Name := Pl.Name;
6636 case c of
6637 2: begin
6638 n := 0;
6639 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6640 end;
6641 3: begin
6642 n := 1;
6643 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6644 end;
6645 4: begin
6646 n := 2;
6647 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6648 end;
6649 else begin
6650 n := 3;
6651 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6652 end;
6653 end;
6655 case gAnnouncer of
6656 ANNOUNCE_NONE:
6657 Exit;
6658 ANNOUNCE_ME:
6659 if not g_Game_IsWatchedPlayer(UID) then
6660 Exit;
6661 ANNOUNCE_MEPLUS:
6662 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6663 Exit;
6664 end;
6666 if killsnd[n].IsPlaying() then
6667 killsnd[n].Stop();
6668 killsnd[n].Play();
6669 end;
6671 procedure g_Game_StartVote(Command, Initiator: string);
6672 var
6673 Need: Integer;
6674 begin
6675 if not gVotesEnabled then Exit;
6676 if gGameSettings.GameType <> GT_SERVER then Exit;
6677 if gVoteInProgress or gVotePassed then
6678 begin
6679 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6680 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6681 Exit;
6682 end;
6683 gVoteInProgress := True;
6684 gVotePassed := False;
6685 gVoteTimer := gTime + gVoteTimeout * 1000;
6686 gVoteCount := 0;
6687 gVoted := False;
6688 gVoteCommand := Command;
6690 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6691 Need := Floor((NetClientCount+1)/2.0)+1
6692 else
6693 Need := Floor(NetClientCount/2.0)+1;
6694 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6695 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6696 end;
6698 procedure g_Game_CheckVote;
6699 var
6700 I, Need: Integer;
6701 begin
6702 if gGameSettings.GameType <> GT_SERVER then Exit;
6703 if not gVoteInProgress then Exit;
6705 if (gTime >= gVoteTimer) then
6706 begin
6707 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6708 Need := Floor((NetClientCount+1)/2.0) + 1
6709 else
6710 Need := Floor(NetClientCount/2.0) + 1;
6711 if gVoteCount >= Need then
6712 begin
6713 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6714 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6715 gVotePassed := True;
6716 gVoteCmdTimer := gTime + 5000;
6717 end
6718 else
6719 begin
6720 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6721 MH_SEND_VoteEvent(NET_VE_FAILED);
6722 end;
6723 if NetClients <> nil then
6724 for I := Low(NetClients) to High(NetClients) do
6725 if NetClients[i].Used then
6726 NetClients[i].Voted := False;
6727 gVoteInProgress := False;
6728 gVoted := False;
6729 gVoteCount := 0;
6730 end
6731 else
6732 begin
6733 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6734 Need := Floor((NetClientCount+1)/2.0) + 1
6735 else
6736 Need := Floor(NetClientCount/2.0) + 1;
6737 if gVoteCount >= Need then
6738 begin
6739 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6740 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6741 gVoteInProgress := False;
6742 gVotePassed := True;
6743 gVoteCmdTimer := gTime + 5000;
6744 gVoted := False;
6745 gVoteCount := 0;
6746 if NetClients <> nil then
6747 for I := Low(NetClients) to High(NetClients) do
6748 if NetClients[i].Used then
6749 NetClients[i].Voted := False;
6750 end;
6751 end;
6752 end;
6754 procedure g_Game_LoadMapList(FileName: string);
6755 var
6756 ListFile: TextFile;
6757 s: string;
6758 begin
6759 MapList := nil;
6760 MapIndex := -1;
6762 if not FileExists(FileName) then Exit;
6764 AssignFile(ListFile, FileName);
6765 Reset(ListFile);
6766 while not EOF(ListFile) do
6767 begin
6768 ReadLn(ListFile, s);
6770 s := Trim(s);
6771 if s = '' then Continue;
6773 SetLength(MapList, Length(MapList)+1);
6774 MapList[High(MapList)] := s;
6775 end;
6776 CloseFile(ListFile);
6777 end;
6779 procedure g_Game_SetDebugMode();
6780 begin
6781 gDebugMode := True;
6782 // ×èòû (äàæå â ñâîåé èãðå):
6783 gCheats := True;
6784 end;
6786 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6787 var
6788 i: Word;
6789 begin
6790 if Length(LoadingStat.Msgs) = 0 then
6791 Exit;
6793 with LoadingStat do
6794 begin
6795 if not reWrite then
6796 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6797 if NextMsg = Length(Msgs) then
6798 begin // scroll
6799 for i := 0 to High(Msgs)-1 do
6800 Msgs[i] := Msgs[i+1];
6801 end
6802 else
6803 Inc(NextMsg);
6804 end else
6805 if NextMsg = 0 then
6806 Inc(NextMsg);
6808 Msgs[NextMsg-1] := Text;
6809 CurValue := 0;
6810 MaxValue := Max;
6811 ShowCount := 0;
6812 end;
6814 g_ActiveWindow := nil;
6816 ProcessLoading(true);
6817 end;
6819 procedure g_Game_StepLoading();
6820 begin
6821 with LoadingStat do
6822 begin
6823 Inc(CurValue);
6824 Inc(ShowCount);
6825 if (ShowCount > LOADING_SHOW_STEP) then
6826 begin
6827 ShowCount := 0;
6828 ProcessLoading();
6829 end;
6830 end;
6831 end;
6833 procedure g_Game_ClearLoading();
6834 var
6835 len: Word;
6836 begin
6837 with LoadingStat do
6838 begin
6839 CurValue := 0;
6840 MaxValue := 0;
6841 ShowCount := 0;
6842 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6843 if len < 1 then len := 1;
6844 SetLength(Msgs, len);
6845 for len := Low(Msgs) to High(Msgs) do
6846 Msgs[len] := '';
6847 NextMsg := 0;
6848 end;
6849 end;
6851 procedure Parse_Params(var pars: TParamStrValues);
6852 var
6853 i: Integer;
6854 s: String;
6855 begin
6856 SetLength(pars, 0);
6857 i := 1;
6858 while i <= ParamCount do
6859 begin
6860 s := ParamStr(i);
6861 if (s[1] = '-') and (Length(s) > 1) then
6862 begin
6863 if (s[2] = '-') and (Length(s) > 2) then
6864 begin // Îäèíî÷íûé ïàðàìåòð
6865 SetLength(pars, Length(pars) + 1);
6866 with pars[High(pars)] do
6867 begin
6868 Name := LowerCase(s);
6869 Value := '+';
6870 end;
6871 end
6872 else
6873 if (i < ParamCount) then
6874 begin // Ïàðàìåòð ñî çíà÷åíèåì
6875 Inc(i);
6876 SetLength(pars, Length(pars) + 1);
6877 with pars[High(pars)] do
6878 begin
6879 Name := LowerCase(s);
6880 Value := LowerCase(ParamStr(i));
6881 end;
6882 end;
6883 end;
6885 Inc(i);
6886 end;
6887 end;
6889 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6890 var
6891 i: Integer;
6892 begin
6893 Result := '';
6894 for i := 0 to High(pars) do
6895 if pars[i].Name = aName then
6896 begin
6897 Result := pars[i].Value;
6898 Break;
6899 end;
6900 end;
6902 procedure g_Game_Process_Params();
6903 var
6904 pars: TParamStrValues;
6905 map: String;
6906 GMode, n: Byte;
6907 LimT, LimS: Integer;
6908 Opt: LongWord;
6909 Lives: Integer;
6910 s: String;
6911 Port: Integer;
6912 ip: String;
6913 F: TextFile;
6914 begin
6915 Parse_Params(pars);
6917 // Debug mode:
6918 s := Find_Param_Value(pars, '--debug');
6919 if (s <> '') then
6920 begin
6921 g_Game_SetDebugMode();
6922 s := Find_Param_Value(pars, '--netdump');
6923 if (s <> '') then
6924 NetDump := True;
6925 end;
6927 // Connect when game loads
6928 ip := Find_Param_Value(pars, '-connect');
6930 if ip <> '' then
6931 begin
6932 s := Find_Param_Value(pars, '-port');
6933 if (s = '') or not TryStrToInt(s, Port) then
6934 Port := 25666;
6936 s := Find_Param_Value(pars, '-pw');
6938 g_Game_StartClient(ip, Port, s);
6939 Exit;
6940 end;
6942 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
6943 if (s <> '') then
6944 begin
6945 gDefaultMegawadStart := s;
6946 end;
6948 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
6949 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
6950 begin
6951 gDefaultMegawadStart := DF_Default_Megawad_Start;
6952 end;
6954 // Start map when game loads:
6955 map := LowerCase(Find_Param_Value(pars, '-map'));
6956 if isWadPath(map) then
6957 begin
6958 // Game mode:
6959 s := Find_Param_Value(pars, '-gm');
6960 GMode := g_Game_TextToMode(s);
6961 if GMode = GM_NONE then GMode := GM_DM;
6962 if GMode = GM_SINGLE then GMode := GM_COOP;
6964 // Time limit:
6965 s := Find_Param_Value(pars, '-limt');
6966 if (s = '') or (not TryStrToInt(s, LimT)) then
6967 LimT := 0;
6968 if LimT < 0 then
6969 LimT := 0;
6971 // Goal limit:
6972 s := Find_Param_Value(pars, '-lims');
6973 if (s = '') or (not TryStrToInt(s, LimS)) then
6974 LimS := 0;
6975 if LimS < 0 then
6976 LimS := 0;
6978 // Lives limit:
6979 s := Find_Param_Value(pars, '-lives');
6980 if (s = '') or (not TryStrToInt(s, Lives)) then
6981 Lives := 0;
6982 if Lives < 0 then
6983 Lives := 0;
6985 // Options:
6986 s := Find_Param_Value(pars, '-opt');
6987 if (s = '') then
6988 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6989 else
6990 Opt := StrToIntDef(s, 0);
6991 if Opt = 0 then
6992 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6994 // Close after map:
6995 s := Find_Param_Value(pars, '--close');
6996 if (s <> '') then
6997 gMapOnce := True;
6999 // Delete test map after play:
7000 s := Find_Param_Value(pars, '--testdelete');
7001 if (s <> '') then
7002 begin
7003 gMapToDelete := MapsDir + map;
7004 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
7005 Halt(1);
7006 end;
7008 // Delete temporary WAD after play:
7009 s := Find_Param_Value(pars, '--tempdelete');
7010 if (s <> '') then
7011 begin
7012 gMapToDelete := MapsDir + map;
7013 gTempDelete := True;
7014 end;
7016 // Number of players:
7017 s := Find_Param_Value(pars, '-pl');
7018 if (s = '') then
7019 n := 1
7020 else
7021 n := StrToIntDef(s, 1);
7023 // Start:
7024 s := Find_Param_Value(pars, '-port');
7025 if (s = '') or not TryStrToInt(s, Port) then
7026 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7027 else
7028 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7029 end;
7031 // Execute script when game loads:
7032 s := Find_Param_Value(pars, '-exec');
7033 if s <> '' then
7034 begin
7035 if not isWadPath(s) then
7036 s := GameDir + '/' + s;
7038 {$I-}
7039 AssignFile(F, s);
7040 Reset(F);
7041 if IOResult <> 0 then
7042 begin
7043 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7044 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7045 CloseFile(F);
7046 Exit;
7047 end;
7048 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
7049 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7051 while not EOF(F) do
7052 begin
7053 ReadLn(F, s);
7054 if IOResult <> 0 then
7055 begin
7056 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7057 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7058 CloseFile(F);
7059 Exit;
7060 end;
7061 if Pos('#', s) <> 1 then // script comment
7062 g_Console_Process(s, True);
7063 end;
7065 CloseFile(F);
7066 {$I+}
7067 end;
7069 SetLength(pars, 0);
7070 end;
7072 begin
7073 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7074 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7075 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7076 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7078 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7079 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7080 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7081 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7083 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7084 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7086 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7087 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7089 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7091 conRegVar('dbg_scale', @g_dbg_scale, 0.01, 5.0, 'experimental deBUG scale mode', '', true);
7092 end.