DEADSOFTWARE

trigger data now cached on map loading (so it should be as fast as the previous trigg...
[d2df-sdl.git] / src / game / g_game.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_game;
19 interface
21 uses
22 g_basic, g_player, e_graphics, Classes, g_res_downloader,
23 SysUtils, g_sound, g_gui, MAPDEF, wadreader, md5, xprofiler;
25 type
26 TGameSettings = record
27 GameType: Byte;
28 GameMode: Byte;
29 TimeLimit: Word;
30 GoalLimit: Word;
31 WarmupTime: Word;
32 MaxLives: Byte;
33 Options: LongWord;
34 WAD: String;
35 end;
37 TGameEvent = record
38 Name: String;
39 Command: String;
40 end;
42 TDelayedEvent = record
43 Pending: Boolean;
44 Time: LongWord;
45 DEType: Byte;
46 DENum: Integer;
47 DEStr: String;
48 end;
50 TPlayerSettings = record
51 Name: String;
52 Model: String;
53 Color: TRGB;
54 Team: Byte;
55 end;
57 TMegaWADInfo = record
58 Name: String;
59 Description: String;
60 Author: String;
61 Pic: String;
62 end;
64 THearPoint = record
65 Active: Boolean;
66 Coords: TDFPoint;
67 end;
69 function g_Game_IsNet(): Boolean;
70 function g_Game_IsServer(): Boolean;
71 function g_Game_IsClient(): Boolean;
72 procedure g_Game_Init();
73 procedure g_Game_Free (freeTextures: Boolean=true);
74 procedure g_Game_LoadData();
75 procedure g_Game_FreeData();
76 procedure g_Game_Update();
77 procedure g_Game_Draw();
78 procedure g_Game_Quit();
79 procedure g_Game_SetupScreenSize();
80 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
81 function g_Game_ModeToText(Mode: Byte): string;
82 function g_Game_TextToMode(Mode: string): Byte;
83 procedure g_Game_ExecuteEvent(Name: String);
84 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
85 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
86 procedure g_Game_RemovePlayer();
87 procedure g_Game_Spectate();
88 procedure g_Game_SpectateCenterView();
89 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
90 procedure g_Game_StartCustom(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte);
91 procedure g_Game_StartServer(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte; IPAddr: LongWord; Port: Word);
92 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
93 procedure g_Game_Restart();
94 procedure g_Game_RestartLevel();
95 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
96 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
97 procedure g_Game_SaveOptions();
98 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
99 procedure g_Game_ChangeMap(MapPath: String);
100 procedure g_Game_ExitLevel(Map: Char16);
101 function g_Game_GetFirstMap(WAD: String): String;
102 function g_Game_GetNextMap(): String;
103 procedure g_Game_NextLevel();
104 procedure g_Game_Pause(Enable: Boolean);
105 procedure g_Game_InGameMenu(Show: Boolean);
106 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
107 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
108 procedure g_Game_Message(Msg: String; Time: Word);
109 procedure g_Game_LoadMapList(FileName: String);
110 procedure g_Game_PauseAllSounds(Enable: Boolean);
111 procedure g_Game_StopAllSounds(all: Boolean);
112 procedure g_Game_UpdateTriggerSounds();
113 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
114 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
115 procedure g_Game_Announce_KillCombo(Param: Integer);
116 procedure g_Game_StartVote(Command, Initiator: string);
117 procedure g_Game_CheckVote;
118 procedure g_TakeScreenShot();
119 procedure g_FatalError(Text: String);
120 procedure g_SimpleError(Text: String);
121 function g_Game_IsTestMap(): Boolean;
122 procedure g_Game_DeleteTestMap();
123 procedure GameCVars(P: SArray);
124 procedure GameCommands(P: SArray);
125 procedure GameCheats(P: SArray);
126 procedure DebugCommands(P: SArray);
127 procedure g_Game_Process_Params;
128 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
129 procedure g_Game_StepLoading();
130 procedure g_Game_ClearLoading();
131 procedure g_Game_SetDebugMode();
132 procedure DrawLoadingStat();
134 { procedure SetWinPause(Enable: Boolean); }
136 const
137 GAME_TICK = 28;
139 LOADING_SHOW_STEP = 100;
140 LOADING_INTERLINE = 20;
142 GT_NONE = 0;
143 GT_SINGLE = 1;
144 GT_CUSTOM = 2;
145 GT_SERVER = 3;
146 GT_CLIENT = 4;
148 GM_NONE = 0;
149 GM_DM = 1;
150 GM_TDM = 2;
151 GM_CTF = 3;
152 GM_COOP = 4;
153 GM_SINGLE = 5;
155 MESSAGE_DIKEY = WM_USER + 1;
157 EXIT_QUIT = 1;
158 EXIT_SIMPLE = 2;
159 EXIT_RESTART = 3;
160 EXIT_ENDLEVELSINGLE = 4;
161 EXIT_ENDLEVELCUSTOM = 5;
163 GAME_OPTION_RESERVED = 1;
164 GAME_OPTION_TEAMDAMAGE = 2;
165 GAME_OPTION_ALLOWEXIT = 4;
166 GAME_OPTION_WEAPONSTAY = 8;
167 GAME_OPTION_MONSTERS = 16;
168 GAME_OPTION_BOTVSPLAYER = 32;
169 GAME_OPTION_BOTVSMONSTER = 64;
171 STATE_NONE = 0;
172 STATE_MENU = 1;
173 STATE_FOLD = 2;
174 STATE_INTERCUSTOM = 3;
175 STATE_INTERSINGLE = 4;
176 STATE_INTERTEXT = 5;
177 STATE_INTERPIC = 6;
178 STATE_ENDPIC = 7;
179 STATE_SLIST = 8;
181 LMS_RESPAWN_NONE = 0;
182 LMS_RESPAWN_WARMUP = 1;
183 LMS_RESPAWN_FINAL = 2;
185 SPECT_NONE = 0;
186 SPECT_STATS = 1;
187 SPECT_MAPVIEW = 2;
188 SPECT_PLAYERS = 3;
190 DE_GLOBEVENT = 0;
191 DE_BFGHIT = 1;
192 DE_KILLCOMBO = 2;
194 ANNOUNCE_NONE = 0;
195 ANNOUNCE_ME = 1;
196 ANNOUNCE_MEPLUS = 2;
197 ANNOUNCE_ALL = 3;
199 CONFIG_FILENAME = 'Doom2DF.cfg';
200 LOG_FILENAME = 'Doom2DF.log';
202 TEST_MAP_NAME = '$$$_TEST_$$$';
204 STD_PLAYER_MODEL = 'Doomer';
206 var
207 gStdFont: DWORD;
208 gGameSettings: TGameSettings;
209 gPlayer1Settings: TPlayerSettings;
210 gPlayer2Settings: TPlayerSettings;
211 gGameOn: Boolean;
212 gPlayerScreenSize: TDFPoint;
213 gPlayer1ScreenCoord: TDFPoint;
214 gPlayer2ScreenCoord: TDFPoint;
215 gPlayer1: TPlayer = nil;
216 gPlayer2: TPlayer = nil;
217 gPlayerDrawn: TPlayer = nil;
218 gTime: LongWord;
219 gSwitchGameMode: Byte = GM_DM;
220 gHearPoint1, gHearPoint2: THearPoint;
221 gSoundEffectsDF: Boolean = False;
222 gSoundTriggerTime: Word = 0;
223 gAnnouncer: Byte = ANNOUNCE_NONE;
224 goodsnd: array[0..3] of TPlayableSound;
225 killsnd: array[0..3] of TPlayableSound;
226 gDefInterTime: ShortInt = -1;
227 gInterEndTime: LongWord = 0;
228 gInterTime: LongWord = 0;
229 gServInterTime: Byte = 0;
230 gGameStartTime: LongWord = 0;
231 gTotalMonsters: Integer = 0;
232 gPause: Boolean;
233 gShowTime: Boolean = True;
234 gShowFPS: Boolean = False;
235 gShowGoals: Boolean = True;
236 gShowStat: Boolean = True;
237 gShowKillMsg: Boolean = True;
238 gShowLives: Boolean = True;
239 gShowPing: Boolean = False;
240 gShowMap: Boolean = False;
241 gExit: Byte = 0;
242 gState: Byte = STATE_NONE;
243 sX, sY: Integer;
244 sWidth, sHeight: Word;
245 gSpectMode: Byte = SPECT_NONE;
246 gSpectHUD: Boolean = True;
247 gSpectKeyPress: Boolean = False;
248 gSpectX: Integer = 0;
249 gSpectY: Integer = 0;
250 gSpectStep: Byte = 8;
251 gSpectViewTwo: Boolean = False;
252 gSpectPID1: Integer = -1;
253 gSpectPID2: Integer = -1;
254 gMusic: TMusic = nil;
255 gLoadGameMode: Boolean;
256 gCheats: Boolean = False;
257 gMapOnce: Boolean = False;
258 gMapToDelete: String;
259 gTempDelete: Boolean = False;
260 gLastMap: Boolean = False;
261 gWinPosX, gWinPosY: Integer;
262 gWinSizeX, gWinSizeY: Integer;
263 gWinFrameX, gWinFrameY, gWinCaption: Integer;
264 gWinActive: Boolean = True; // by default window is active, lol
265 gResolutionChange: Boolean = False;
266 gRC_Width, gRC_Height: Word;
267 gRC_FullScreen, gRC_Maximized: Boolean;
268 gLanguageChange: Boolean = False;
269 gDebugMode: Boolean = False;
270 g_debug_Sounds: Boolean = False;
271 g_debug_Frames: Boolean = False;
272 g_debug_WinMsgs: Boolean = False;
273 g_debug_MonsterOff: Boolean = False;
274 g_debug_BotAIOff: Byte = 0;
275 g_debug_HealthBar: Boolean = False;
276 g_Debug_Player: Boolean = False;
277 gCoopMonstersKilled: Word = 0;
278 gCoopSecretsFound: Word = 0;
279 gCoopTotalMonstersKilled: Word = 0;
280 gCoopTotalSecretsFound: Word = 0;
281 gCoopTotalMonsters: Word = 0;
282 gCoopTotalSecrets: Word = 0;
283 gStatsOff: Boolean = False;
284 gStatsPressed: Boolean = False;
285 gExitByTrigger: Boolean = False;
286 gNextMap: String = '';
287 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
288 gLMSRespawnTime: Cardinal = 0;
289 gLMSSoftSpawn: Boolean = False;
290 gMissionFailed: Boolean = False;
291 gVoteInProgress: Boolean = False;
292 gVotePassed: Boolean = False;
293 gVoteCommand: string = '';
294 gVoteTimer: Cardinal = 0;
295 gVoteCmdTimer: Cardinal = 0;
296 gVoteCount: Integer = 0;
297 gVoteTimeout: Cardinal = 30;
298 gVoted: Boolean = False;
299 gVotesEnabled: Boolean = True;
300 gEvents: Array of TGameEvent;
301 gDelayedEvents: Array of TDelayedEvent;
303 // move button values:
304 // bits 0-1: l/r state:
305 // 0: neither left, nor right pressed
306 // 1: left pressed
307 // 2: right pressed
308 // bits 4-5: l/r state when strafe was pressed
309 P1MoveButton: Byte = 0;
310 P2MoveButton: Byte = 0;
312 g_profile_frame_update: Boolean = false;
313 g_profile_frame_draw: Boolean = false;
314 g_profile_collision: Boolean = false;
315 g_profile_los: Boolean = false;
316 g_profile_history_size: Integer = 1000;
318 g_rlayer_back: Boolean = true;
319 g_rlayer_step: Boolean = true;
320 g_rlayer_wall: Boolean = true;
321 g_rlayer_door: Boolean = true;
322 g_rlayer_acid1: Boolean = true;
323 g_rlayer_acid2: Boolean = true;
324 g_rlayer_water: Boolean = true;
325 g_rlayer_fore: Boolean = true;
327 g_dbg_scale: Single = 1.0;
330 procedure g_ResetDynlights ();
331 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
332 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
334 function conIsCheatsEnabled (): Boolean;
337 implementation
339 uses
340 g_textures, g_main, g_window, g_menu,
341 e_input, e_log, g_console, g_items, g_map, g_panel,
342 g_playermodel, g_gfx, g_options, g_weapons, Math,
343 g_triggers, g_monsters, e_sound, CONFIG,
344 BinEditor, g_language, g_net, SDL,
345 ENet, e_msg, g_netmsg, g_netmaster, GL, GLExt,
346 utils, sfs, g_holmes;
349 // ////////////////////////////////////////////////////////////////////////// //
350 function conIsCheatsEnabled (): Boolean;
351 begin
352 result := false;
353 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
354 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then exit;
355 result := true;
356 end;
359 // ////////////////////////////////////////////////////////////////////////// //
360 var
361 profileFrameDraw: TProfiler = nil;
364 // ////////////////////////////////////////////////////////////////////////// //
365 type
366 TDynLight = record
367 x, y, radius: Integer;
368 r, g, b, a: Single;
369 exploCount: Integer;
370 exploRadius: Integer;
371 end;
373 var
374 g_dynLights: array of TDynLight = nil;
375 g_dynLightCount: Integer = 0;
376 g_playerLight: Boolean = false;
378 procedure g_ResetDynlights ();
379 var
380 lnum, idx: Integer;
381 begin
382 if not gwin_has_stencil then begin g_dynLightCount := 0; exit; end;
383 lnum := 0;
384 for idx := 0 to g_dynLightCount-1 do
385 begin
386 if g_dynLights[idx].exploCount = -666 then
387 begin
388 // skip it
389 end
390 else
391 begin
392 // explosion
393 Inc(g_dynLights[idx].exploCount);
394 if (g_dynLights[idx].exploCount < 10) then
395 begin
396 g_dynLights[idx].radius := g_dynLights[idx].exploRadius+g_dynLights[idx].exploCount*8;
397 g_dynLights[idx].a := 0.4+g_dynLights[idx].exploCount/10;
398 if (g_dynLights[idx].a > 0.8) then g_dynLights[idx].a := 0.8;
399 if lnum <> idx then g_dynLights[lnum] := g_dynLights[idx];
400 Inc(lnum);
401 end;
402 end;
403 end;
404 g_dynLightCount := lnum;
405 end;
407 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
408 begin
409 if not gwin_has_stencil then exit;
410 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
411 g_dynLights[g_dynLightCount].x := x;
412 g_dynLights[g_dynLightCount].y := y;
413 g_dynLights[g_dynLightCount].radius := radius;
414 g_dynLights[g_dynLightCount].r := r;
415 g_dynLights[g_dynLightCount].g := g;
416 g_dynLights[g_dynLightCount].b := b;
417 g_dynLights[g_dynLightCount].a := a;
418 g_dynLights[g_dynLightCount].exploCount := -666;
419 Inc(g_dynLightCount);
420 end;
422 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
423 begin
424 if not gwin_has_stencil then exit;
425 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
426 g_dynLights[g_dynLightCount].x := x;
427 g_dynLights[g_dynLightCount].y := y;
428 g_dynLights[g_dynLightCount].radius := 0;
429 g_dynLights[g_dynLightCount].exploRadius := radius;
430 g_dynLights[g_dynLightCount].r := r;
431 g_dynLights[g_dynLightCount].g := g;
432 g_dynLights[g_dynLightCount].b := b;
433 g_dynLights[g_dynLightCount].a := 0;
434 g_dynLights[g_dynLightCount].exploCount := 0;
435 Inc(g_dynLightCount);
436 end;
439 // ////////////////////////////////////////////////////////////////////////// //
440 function calcProfilesHeight (prof: TProfiler): Integer;
441 begin
442 result := 0;
443 if (prof = nil) then exit;
444 if (length(prof.bars) = 0) then exit;
445 result := length(prof.bars)*(16+2);
446 end;
448 // returns width
449 function drawProfiles (x, y: Integer; prof: TProfiler): Integer;
450 var
451 wdt, hgt: Integer;
452 yy: Integer;
453 ii: Integer;
454 begin
455 result := 0;
456 if (prof = nil) then exit;
457 // gScreenWidth
458 if (length(prof.bars) = 0) then exit;
459 wdt := 192;
460 hgt := calcProfilesHeight(prof);
461 if (x < 0) then x := gScreenWidth-(wdt-1)+x;
462 if (y < 0) then y := gScreenHeight-(hgt-1)+y;
463 // background
464 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
465 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
466 e_DarkenQuadWH(x, y, wdt, hgt, 150);
467 // title
468 yy := y+2;
469 for ii := 0 to High(prof.bars) do
470 begin
471 e_TextureFontPrintEx(x+2+4*prof.bars[ii].level, yy, Format('%s: %d', [prof.bars[ii].name, prof.bars[ii].value]), gStdFont, 255, 255, 0, 1, false);
472 Inc(yy, 16+2);
473 end;
474 result := wdt;
475 end;
478 // ////////////////////////////////////////////////////////////////////////// //
479 type
480 TEndCustomGameStat = record
481 PlayerStat: TPlayerStatArray;
482 TeamStat: TTeamStat;
483 GameTime: LongWord;
484 GameMode: Byte;
485 Map, MapName: String;
486 end;
488 TEndSingleGameStat = record
489 PlayerStat: Array [0..1] of record
490 Kills: Integer;
491 Secrets: Integer;
492 end;
493 GameTime: LongWord;
494 TwoPlayers: Boolean;
495 TotalSecrets: Integer;
496 end;
498 TLoadingStat = record
499 CurValue: Integer;
500 MaxValue: Integer;
501 ShowCount: Integer;
502 Msgs: Array of String;
503 NextMsg: Word;
504 end;
506 TParamStrValue = record
507 Name: String;
508 Value: String;
509 end;
511 TParamStrValues = Array of TParamStrValue;
513 const
514 INTER_ACTION_TEXT = 1;
515 INTER_ACTION_PIC = 2;
516 INTER_ACTION_MUSIC = 3;
518 var
519 FPS, UPS: Word;
520 FPSCounter, UPSCounter: Word;
521 FPSTime, UPSTime: LongWord;
522 DataLoaded: Boolean = False;
523 LastScreenShot: Int64;
524 IsDrawStat: Boolean = False;
525 CustomStat: TEndCustomGameStat;
526 SingleStat: TEndSingleGameStat;
527 LoadingStat: TLoadingStat;
528 EndingGameCounter: Byte = 0;
529 MessageText: String;
530 MessageTime: Word;
531 MapList: SArray = nil;
532 MapIndex: Integer = -1;
533 MegaWAD: record
534 info: TMegaWADInfo;
535 endpic: String;
536 endmus: String;
537 res: record
538 text: Array of ShortString;
539 anim: Array of ShortString;
540 pic: Array of ShortString;
541 mus: Array of ShortString;
542 end;
543 triggers: Array of record
544 event: ShortString;
545 actions: Array of record
546 action, p1, p2: Integer;
547 end;
548 end;
549 cur_trigger: Integer;
550 cur_action: Integer;
551 end;
552 //InterPic: String;
553 InterText: record
554 lines: SArray;
555 img: String;
556 cur_line: Integer;
557 cur_char: Integer;
558 counter: Integer;
559 endtext: Boolean;
560 end;
562 function Compare(a, b: TPlayerStat): Integer;
563 begin
564 if a.Spectator then Result := 1
565 else if b.Spectator then Result := -1
566 else if a.Frags < b.Frags then Result := 1
567 else if a.Frags > b.Frags then Result := -1
568 else if a.Deaths < b.Deaths then Result := -1
569 else if a.Deaths > b.Deaths then Result := 1
570 else if a.Kills < b.Kills then Result := -1
571 else Result := 1;
572 end;
574 procedure SortGameStat(var stat: TPlayerStatArray);
575 var
576 I, J: Integer;
577 T: TPlayerStat;
578 begin
579 if stat = nil then Exit;
581 for I := High(stat) downto Low(stat) do
582 for J := Low(stat) to High(stat) - 1 do
583 if Compare(stat[J], stat[J + 1]) = 1 then
584 begin
585 T := stat[J];
586 stat[J] := stat[J + 1];
587 stat[J + 1] := T;
588 end;
589 end;
591 function g_Game_ModeToText(Mode: Byte): string;
592 begin
593 Result := '';
594 case Mode of
595 GM_DM: Result := _lc[I_MENU_GAME_TYPE_DM];
596 GM_TDM: Result := _lc[I_MENU_GAME_TYPE_TDM];
597 GM_CTF: Result := _lc[I_MENU_GAME_TYPE_CTF];
598 GM_COOP: Result := _lc[I_MENU_GAME_TYPE_COOP];
599 GM_SINGLE: Result := _lc[I_MENU_GAME_TYPE_SINGLE];
600 end;
601 end;
603 function g_Game_TextToMode(Mode: string): Byte;
604 begin
605 Result := GM_NONE;
606 Mode := UpperCase(Mode);
607 if Mode = _lc[I_MENU_GAME_TYPE_DM] then
608 begin
609 Result := GM_DM;
610 Exit;
611 end;
612 if Mode = _lc[I_MENU_GAME_TYPE_TDM] then
613 begin
614 Result := GM_TDM;
615 Exit;
616 end;
617 if Mode = _lc[I_MENU_GAME_TYPE_CTF] then
618 begin
619 Result := GM_CTF;
620 Exit;
621 end;
622 if Mode = _lc[I_MENU_GAME_TYPE_COOP] then
623 begin
624 Result := GM_COOP;
625 Exit;
626 end;
627 if Mode = _lc[I_MENU_GAME_TYPE_SINGLE] then
628 begin
629 Result := GM_SINGLE;
630 Exit;
631 end;
632 end;
634 function g_Game_IsNet(): Boolean;
635 begin
636 Result := (gGameSettings.GameType in [GT_SERVER, GT_CLIENT]);
637 end;
639 function g_Game_IsServer(): Boolean;
640 begin
641 Result := (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM, GT_SERVER]);
642 end;
644 function g_Game_IsClient(): Boolean;
645 begin
646 Result := (gGameSettings.GameType = GT_CLIENT);
647 end;
649 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
650 var
651 w: TWADFile;
652 cfg: TConfig;
653 p: Pointer;
654 len: Integer;
655 begin
656 Result.name := ExtractFileName(WAD);
657 Result.description := '';
658 Result.author := '';
660 w := TWADFile.Create();
661 w.ReadFile(WAD);
663 if not w.GetResource('INTERSCRIPT', p, len) then
664 begin
665 w.Free();
666 Exit;
667 end;
669 cfg := TConfig.CreateMem(p, len);
670 Result.name := cfg.ReadStr('megawad', 'name', ExtractFileName(WAD));
671 Result.description := cfg.ReadStr('megawad', 'description', '');
672 Result.author := cfg.ReadStr('megawad', 'author', '');
673 Result.pic := cfg.ReadStr('megawad', 'pic', '');
674 cfg.Free();
676 FreeMem(p);
677 end;
679 procedure g_Game_FreeWAD();
680 var
681 a: Integer;
682 begin
683 for a := 0 to High(MegaWAD.res.pic) do
684 if MegaWAD.res.pic[a] <> '' then
685 g_Texture_Delete(MegaWAD.res.pic[a]);
687 for a := 0 to High(MegaWAD.res.mus) do
688 if MegaWAD.res.mus[a] <> '' then
689 g_Sound_Delete(MegaWAD.res.mus[a]);
691 MegaWAD.res.pic := nil;
692 MegaWAD.res.text := nil;
693 MegaWAD.res.anim := nil;
694 MegaWAD.res.mus := nil;
695 MegaWAD.triggers := nil;
697 g_Texture_Delete('TEXTURE_endpic');
698 g_Sound_Delete('MUSIC_endmus');
700 ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
701 gGameSettings.WAD := '';
702 end;
704 procedure g_Game_LoadWAD(WAD: string);
705 var
706 w: TWADFile;
707 cfg: TConfig;
708 p: Pointer;
709 {b, }len: Integer;
710 s: string;
711 begin
712 g_Game_FreeWAD();
713 gGameSettings.WAD := WAD;
714 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
715 Exit;
717 MegaWAD.info := g_Game_GetMegaWADInfo(MapsDir + WAD);
719 w := TWADFile.Create();
720 w.ReadFile(MapsDir + WAD);
722 if not w.GetResource('INTERSCRIPT', p, len) then
723 begin
724 w.Free();
725 Exit;
726 end;
728 cfg := TConfig.CreateMem(p, len);
730 {b := 1;
731 while True do
732 begin
733 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
734 if s = '' then Break;
735 b := b+1;
737 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
738 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
740 g_Texture_CreateWADEx(s, s);
741 end;
743 b := 1;
744 while True do
745 begin
746 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
747 if s = '' then Break;
748 b := b+1;
750 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
751 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
753 g_Music_CreateWADEx(s, s);
754 end;}
756 MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
757 if MegaWAD.endpic <> '' then
758 begin
759 s := g_ExtractWadName(MegaWAD.endpic);
760 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
761 g_Texture_CreateWADEx('TEXTURE_endpic', s+MegaWAD.endpic);
762 end;
763 MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
764 if MegaWAD.endmus <> '' then
765 begin
766 s := g_ExtractWadName(MegaWAD.endmus);
767 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
768 g_Sound_CreateWADEx('MUSIC_endmus', s+MegaWAD.endmus, True);
769 end;
771 cfg.Free();
772 FreeMem(p);
773 w.Free();
774 end;
776 {procedure start_trigger(t: string);
777 begin
778 end;
780 function next_trigger(): Boolean;
781 begin
782 end;}
784 procedure DisableCheats();
785 begin
786 MAX_RUNVEL := 8;
787 VEL_JUMP := 10;
788 gFly := False;
790 if gPlayer1 <> nil then gPlayer1.GodMode := False;
791 if gPlayer2 <> nil then gPlayer2.GodMode := False;
792 if gPlayer1 <> nil then gPlayer1.NoTarget := False;
793 if gPlayer2 <> nil then gPlayer2.NoTarget := False;
795 {$IF DEFINED(D2F_DEBUG)}
796 if gPlayer1 <> nil then gPlayer1.NoTarget := True;
797 gAimLine := 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(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(Map: Char16);
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: Char16;
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
4510 Exit;
4512 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4513 WadName := Copy(gMapToDelete, 1, a + 3);
4514 Delete(gMapToDelete, 1, a + 5);
4515 gMapToDelete := UpperCase(gMapToDelete);
4516 MapName := '';
4517 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4520 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4521 if MapName <> TEST_MAP_NAME then
4522 Exit;
4524 if not gTempDelete then
4525 begin
4526 time := g_GetFileTime(WadName);
4527 WAD := TWADFile.Create();
4529 // ×èòàåì Wad-ôàéë:
4530 if not WAD.ReadFile(WadName) then
4531 begin // Íåò òàêîãî WAD-ôàéëà
4532 WAD.Free();
4533 Exit;
4534 end;
4536 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4537 WAD.CreateImage();
4538 MapList := WAD.GetResourcesList('');
4540 if MapList <> nil then
4541 for a := 0 to High(MapList) do
4542 if MapList[a] = MapName then
4543 begin
4544 // Óäàëÿåì è ñîõðàíÿåì:
4545 WAD.RemoveResource('', MapName);
4546 WAD.SaveTo(WadName);
4547 Break;
4548 end;
4550 WAD.Free();
4551 g_SetFileTime(WadName, time);
4552 end else
4554 if gTempDelete then DeleteFile(WadName);
4555 end;
4557 procedure GameCVars(P: SArray);
4558 var
4559 a, b: Integer;
4560 stat: TPlayerStatArray;
4561 cmd, s: string;
4562 config: TConfig;
4563 begin
4564 stat := nil;
4565 cmd := LowerCase(P[0]);
4566 if cmd = 'r_showfps' then
4567 begin
4568 if (Length(P) > 1) and
4569 ((P[1] = '1') or (P[1] = '0')) then
4570 gShowFPS := (P[1][1] = '1');
4572 if gShowFPS then
4573 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4574 else
4575 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4576 end
4577 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4578 begin
4579 with gGameSettings do
4580 begin
4581 if (Length(P) > 1) and
4582 ((P[1] = '1') or (P[1] = '0')) then
4583 begin
4584 if (P[1][1] = '1') then
4585 Options := Options or GAME_OPTION_TEAMDAMAGE
4586 else
4587 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4588 end;
4590 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4591 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4592 else
4593 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4595 if g_Game_IsNet then MH_SEND_GameSettings;
4596 end;
4597 end
4598 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4599 begin
4600 with gGameSettings do
4601 begin
4602 if (Length(P) > 1) and
4603 ((P[1] = '1') or (P[1] = '0')) then
4604 begin
4605 if (P[1][1] = '1') then
4606 Options := Options or GAME_OPTION_WEAPONSTAY
4607 else
4608 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4609 end;
4611 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4612 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4613 else
4614 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4616 if g_Game_IsNet then MH_SEND_GameSettings;
4617 end;
4618 end
4619 else if cmd = 'g_gamemode' then
4620 begin
4621 a := g_Game_TextToMode(P[1]);
4622 if a = GM_SINGLE then a := GM_COOP;
4623 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4624 begin
4625 gSwitchGameMode := a;
4626 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4627 (gState = STATE_INTERSINGLE) then
4628 gSwitchGameMode := GM_SINGLE;
4629 if not gGameOn then
4630 gGameSettings.GameMode := gSwitchGameMode;
4631 end;
4632 if gSwitchGameMode = gGameSettings.GameMode then
4633 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4634 [g_Game_ModeToText(gGameSettings.GameMode)]))
4635 else
4636 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4637 [g_Game_ModeToText(gGameSettings.GameMode),
4638 g_Game_ModeToText(gSwitchGameMode)]));
4639 end
4640 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4641 begin
4642 with gGameSettings do
4643 begin
4644 if (Length(P) > 1) and
4645 ((P[1] = '1') or (P[1] = '0')) then
4646 begin
4647 if (P[1][1] = '1') then
4648 Options := Options or GAME_OPTION_ALLOWEXIT
4649 else
4650 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4651 end;
4653 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4654 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4655 else
4656 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4657 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4659 if g_Game_IsNet then MH_SEND_GameSettings;
4660 end;
4661 end
4662 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4663 begin
4664 with gGameSettings do
4665 begin
4666 if (Length(P) > 1) and
4667 ((P[1] = '1') or (P[1] = '0')) then
4668 begin
4669 if (P[1][1] = '1') then
4670 Options := Options or GAME_OPTION_MONSTERS
4671 else
4672 Options := Options and (not GAME_OPTION_MONSTERS);
4673 end;
4675 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4676 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4677 else
4678 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4679 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4681 if g_Game_IsNet then MH_SEND_GameSettings;
4682 end;
4683 end
4684 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4685 begin
4686 with gGameSettings do
4687 begin
4688 if (Length(P) > 1) and
4689 ((P[1] = '1') or (P[1] = '0')) then
4690 begin
4691 if (P[1][1] = '1') then
4692 Options := Options or GAME_OPTION_BOTVSPLAYER
4693 else
4694 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4695 end;
4697 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4698 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4699 else
4700 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4702 if g_Game_IsNet then MH_SEND_GameSettings;
4703 end;
4704 end
4705 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4706 begin
4707 with gGameSettings do
4708 begin
4709 if (Length(P) > 1) and
4710 ((P[1] = '1') or (P[1] = '0')) then
4711 begin
4712 if (P[1][1] = '1') then
4713 Options := Options or GAME_OPTION_BOTVSMONSTER
4714 else
4715 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4716 end;
4718 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4719 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4720 else
4721 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4723 if g_Game_IsNet then MH_SEND_GameSettings;
4724 end;
4725 end
4726 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4727 begin
4728 if Length(P) > 1 then
4729 begin
4730 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4731 gGameSettings.WarmupTime := 30
4732 else
4733 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4734 end;
4736 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4737 [gGameSettings.WarmupTime]));
4738 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4739 end
4740 else if cmd = 'net_interp' then
4741 begin
4742 if (Length(P) > 1) then
4743 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4745 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4746 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4747 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4748 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4749 config.Free();
4750 end
4751 else if cmd = 'net_forceplayerupdate' then
4752 begin
4753 if (Length(P) > 1) and
4754 ((P[1] = '1') or (P[1] = '0')) then
4755 NetForcePlayerUpdate := (P[1][1] = '1');
4757 if NetForcePlayerUpdate then
4758 g_Console_Add('net_forceplayerupdate = 1')
4759 else
4760 g_Console_Add('net_forceplayerupdate = 0');
4761 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4762 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4763 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4764 config.Free();
4765 end
4766 else if cmd = 'net_predictself' then
4767 begin
4768 if (Length(P) > 1) and
4769 ((P[1] = '1') or (P[1] = '0')) then
4770 NetPredictSelf := (P[1][1] = '1');
4772 if NetPredictSelf then
4773 g_Console_Add('net_predictself = 1')
4774 else
4775 g_Console_Add('net_predictself = 0');
4776 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4777 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4778 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4779 config.Free();
4780 end
4781 else if cmd = 'sv_name' then
4782 begin
4783 if (Length(P) > 1) and (Length(P[1]) > 0) then
4784 begin
4785 NetServerName := P[1];
4786 if Length(NetServerName) > 64 then
4787 SetLength(NetServerName, 64);
4788 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4789 g_Net_Slist_Update;
4790 end;
4792 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4793 end
4794 else if cmd = 'sv_passwd' then
4795 begin
4796 if (Length(P) > 1) and (Length(P[1]) > 0) then
4797 begin
4798 NetPassword := P[1];
4799 if Length(NetPassword) > 24 then
4800 SetLength(NetPassword, 24);
4801 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4802 g_Net_Slist_Update;
4803 end;
4805 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4806 end
4807 else if cmd = 'sv_maxplrs' then
4808 begin
4809 if (Length(P) > 1) then
4810 begin
4811 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4812 if g_Game_IsServer and g_Game_IsNet then
4813 begin
4814 b := 0;
4815 for a := 0 to High(NetClients) do
4816 if NetClients[a].Used then
4817 begin
4818 Inc(b);
4819 if b > NetMaxClients then
4820 begin
4821 s := g_Player_Get(NetClients[a].Player).Name;
4822 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4823 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4824 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4825 end;
4826 end;
4827 if NetUseMaster then
4828 g_Net_Slist_Update;
4829 end;
4830 end;
4832 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4833 end
4834 else if cmd = 'sv_public' then
4835 begin
4836 if (Length(P) > 1) then
4837 begin
4838 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4839 if g_Game_IsServer and g_Game_IsNet then
4840 if NetUseMaster then
4841 begin
4842 if NetMPeer = nil then
4843 if not g_Net_Slist_Connect() then
4844 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4845 g_Net_Slist_Update();
4846 end
4847 else
4848 if NetMPeer <> nil then
4849 g_Net_Slist_Disconnect();
4850 end;
4852 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4853 end
4854 else if cmd = 'sv_intertime' then
4855 begin
4856 if (Length(P) > 1) then
4857 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4859 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4860 end
4861 else if cmd = 'p1_name' then
4862 begin
4863 if (Length(P) > 1) and gGameOn then
4864 begin
4865 if g_Game_IsClient then
4866 begin
4867 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4868 MC_SEND_PlayerSettings;
4869 end
4870 else
4871 if gPlayer1 <> nil then
4872 begin
4873 gPlayer1.Name := b_Text_Unformat(P[1]);
4874 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4875 end
4876 else
4877 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4878 end;
4879 end
4880 else if cmd = 'p2_name' then
4881 begin
4882 if (Length(P) > 1) and gGameOn then
4883 begin
4884 if g_Game_IsClient then
4885 begin
4886 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4887 MC_SEND_PlayerSettings;
4888 end
4889 else
4890 if gPlayer2 <> nil then
4891 begin
4892 gPlayer2.Name := b_Text_Unformat(P[1]);
4893 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4894 end
4895 else
4896 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4897 end;
4898 end
4899 else if cmd = 'p1_color' then
4900 begin
4901 if Length(P) > 3 then
4902 if g_Game_IsClient then
4903 begin
4904 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4905 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4906 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4907 MC_SEND_PlayerSettings;
4908 end
4909 else
4910 if gPlayer1 <> nil then
4911 begin
4912 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4913 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4914 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4915 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4916 end
4917 else
4918 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4919 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4920 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4921 end
4922 else if (cmd = 'p2_color') and not g_Game_IsNet then
4923 begin
4924 if Length(P) > 3 then
4925 if g_Game_IsClient then
4926 begin
4927 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4928 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4929 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4930 MC_SEND_PlayerSettings;
4931 end
4932 else
4933 if gPlayer2 <> nil then
4934 begin
4935 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4936 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4937 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4938 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4939 end
4940 else
4941 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4942 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4943 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4944 end
4945 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4946 begin
4947 if cmd = 'r_showtime' then
4948 begin
4949 if (Length(P) > 1) and
4950 ((P[1] = '1') or (P[1] = '0')) then
4951 gShowTime := (P[1][1] = '1');
4953 if gShowTime then
4954 g_Console_Add(_lc[I_MSG_TIME_ON])
4955 else
4956 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4957 end
4958 else if cmd = 'r_showscore' then
4959 begin
4960 if (Length(P) > 1) and
4961 ((P[1] = '1') or (P[1] = '0')) then
4962 gShowGoals := (P[1][1] = '1');
4964 if gShowGoals then
4965 g_Console_Add(_lc[I_MSG_SCORE_ON])
4966 else
4967 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4968 end
4969 else if cmd = 'r_showstat' then
4970 begin
4971 if (Length(P) > 1) and
4972 ((P[1] = '1') or (P[1] = '0')) then
4973 gShowStat := (P[1][1] = '1');
4975 if gShowStat then
4976 g_Console_Add(_lc[I_MSG_STATS_ON])
4977 else
4978 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4979 end
4980 else if cmd = 'r_showkillmsg' then
4981 begin
4982 if (Length(P) > 1) and
4983 ((P[1] = '1') or (P[1] = '0')) then
4984 gShowKillMsg := (P[1][1] = '1');
4986 if gShowKillMsg then
4987 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4988 else
4989 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4990 end
4991 else if cmd = 'r_showlives' then
4992 begin
4993 if (Length(P) > 1) and
4994 ((P[1] = '1') or (P[1] = '0')) then
4995 gShowLives := (P[1][1] = '1');
4997 if gShowLives then
4998 g_Console_Add(_lc[I_MSG_LIVES_ON])
4999 else
5000 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5001 end
5002 else if cmd = 'r_showspect' then
5003 begin
5004 if (Length(P) > 1) and
5005 ((P[1] = '1') or (P[1] = '0')) then
5006 gSpectHUD := (P[1][1] = '1');
5008 if gSpectHUD then
5009 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5010 else
5011 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5012 end
5013 else if cmd = 'r_showping' then
5014 begin
5015 if (Length(P) > 1) and
5016 ((P[1] = '1') or (P[1] = '0')) then
5017 gShowPing := (P[1][1] = '1');
5019 if gShowPing then
5020 g_Console_Add(_lc[I_MSG_PING_ON])
5021 else
5022 g_Console_Add(_lc[I_MSG_PING_OFF]);
5023 end
5024 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5025 begin
5026 if Length(P) > 1 then
5027 begin
5028 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5029 gGameSettings.GoalLimit := 0
5030 else
5031 begin
5032 b := 0;
5034 if gGameSettings.GameMode = GM_DM then
5035 begin // DM
5036 stat := g_Player_GetStats();
5037 if stat <> nil then
5038 for a := 0 to High(stat) do
5039 if stat[a].Frags > b then
5040 b := stat[a].Frags;
5041 end
5042 else // TDM/CTF
5043 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5045 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5046 end;
5048 if g_Game_IsNet then MH_SEND_GameSettings;
5049 end;
5051 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5052 end
5053 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5054 begin
5055 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5056 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5058 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5059 [gGameSettings.TimeLimit div 3600,
5060 (gGameSettings.TimeLimit div 60) mod 60,
5061 gGameSettings.TimeLimit mod 60]));
5062 if g_Game_IsNet then MH_SEND_GameSettings;
5063 end
5064 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5065 begin
5066 if Length(P) > 1 then
5067 begin
5068 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5069 gGameSettings.MaxLives := 0
5070 else
5071 begin
5072 b := 0;
5073 stat := g_Player_GetStats();
5074 if stat <> nil then
5075 for a := 0 to High(stat) do
5076 if stat[a].Lives > b then
5077 b := stat[a].Lives;
5078 gGameSettings.MaxLives :=
5079 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5080 end;
5081 end;
5083 g_Console_Add(Format(_lc[I_MSG_LIVES],
5084 [gGameSettings.MaxLives]));
5085 if g_Game_IsNet then MH_SEND_GameSettings;
5086 end;
5087 end;
5088 end;
5091 procedure DebugCommands(P: SArray);
5092 var
5093 a, b: Integer;
5094 cmd: string;
5095 //pt: TDFPoint;
5096 mon: TMonster;
5097 begin
5098 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5099 if gDebugMode then
5100 begin
5101 cmd := LowerCase(P[0]);
5102 if cmd = 'd_window' then
5103 begin
5104 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5105 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5106 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5107 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5108 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5109 end
5110 else if cmd = 'd_sounds' then
5111 begin
5112 if (Length(P) > 1) and
5113 ((P[1] = '1') or (P[1] = '0')) then
5114 g_Debug_Sounds := (P[1][1] = '1');
5116 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5117 end
5118 else if cmd = 'd_frames' then
5119 begin
5120 if (Length(P) > 1) and
5121 ((P[1] = '1') or (P[1] = '0')) then
5122 g_Debug_Frames := (P[1][1] = '1');
5124 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5125 end
5126 else if cmd = 'd_winmsg' then
5127 begin
5128 if (Length(P) > 1) and
5129 ((P[1] = '1') or (P[1] = '0')) then
5130 g_Debug_WinMsgs := (P[1][1] = '1');
5132 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5133 end
5134 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5135 begin
5136 if (Length(P) > 1) and
5137 ((P[1] = '1') or (P[1] = '0')) then
5138 g_Debug_MonsterOff := (P[1][1] = '1');
5140 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5141 end
5142 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5143 begin
5144 if Length(P) > 1 then
5145 case P[1][1] of
5146 '0': g_debug_BotAIOff := 0;
5147 '1': g_debug_BotAIOff := 1;
5148 '2': g_debug_BotAIOff := 2;
5149 '3': g_debug_BotAIOff := 3;
5150 end;
5152 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5153 end
5154 else if cmd = 'd_monster' then
5155 begin
5156 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5157 if Length(P) < 2 then
5158 begin
5159 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5160 g_Console_Add('ID | Name');
5161 for b := MONSTER_DEMON to MONSTER_MAN do
5162 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5163 end else
5164 begin
5165 a := StrToIntDef(P[1], 0);
5166 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5167 a := g_Mons_TypeIdByName(P[1]);
5169 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5170 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5171 else
5172 begin
5173 with gPlayer1.Obj do
5174 begin
5175 mon := g_Monsters_Create(a,
5176 X + Rect.X + (Rect.Width div 2),
5177 Y + Rect.Y + Rect.Height,
5178 gPlayer1.Direction, True);
5179 end;
5180 if (Length(P) > 2) and (mon <> nil) then
5181 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5182 end;
5183 end;
5184 end
5185 else if (cmd = 'd_health') then
5186 begin
5187 if (Length(P) > 1) and
5188 ((P[1] = '1') or (P[1] = '0')) then
5189 g_debug_HealthBar := (P[1][1] = '1');
5191 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5192 end
5193 else if (cmd = 'd_player') then
5194 begin
5195 if (Length(P) > 1) and
5196 ((P[1] = '1') or (P[1] = '0')) then
5197 g_debug_Player := (P[1][1] = '1');
5199 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5200 end
5201 else if (cmd = 'd_joy') then
5202 begin
5203 for a := 1 to 8 do
5204 g_Console_Add(e_JoystickStateToString(a));
5205 end;
5206 end
5207 else
5208 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5209 end;
5212 procedure GameCheats(P: SArray);
5213 var
5214 cmd: string;
5215 f, a: Integer;
5216 plr: TPlayer;
5217 begin
5218 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5219 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5220 begin
5221 g_Console_Add('not available');
5222 exit;
5223 end;
5224 plr := gPlayer1;
5225 if plr = nil then
5226 begin
5227 g_Console_Add('where is the player?!');
5228 exit;
5229 end;
5230 cmd := LowerCase(P[0]);
5231 // god
5232 if cmd = 'god' then
5233 begin
5234 plr.GodMode := not plr.GodMode;
5235 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5236 exit;
5237 end;
5238 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5239 if cmd = 'give' then
5240 begin
5241 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5242 for f := 1 to High(P) do
5243 begin
5244 cmd := LowerCase(P[f]);
5245 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5246 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5247 if cmd = 'exit' then
5248 begin
5249 if gTriggers <> nil then
5250 begin
5251 for a := 0 to High(gTriggers) do
5252 begin
5253 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5254 begin
5255 g_Console_Add('player left the map');
5256 gExitByTrigger := True;
5257 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5258 g_Game_ExitLevel(gTriggers[a].tgcMap);
5259 break;
5260 end;
5261 end;
5262 end;
5263 continue;
5264 end;
5266 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5267 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5268 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5269 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5270 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5272 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5273 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5275 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5276 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;
5278 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5279 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5281 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5282 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5284 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5285 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5287 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5288 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5289 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5291 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5292 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5293 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5294 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;
5295 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5296 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5298 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;
5299 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;
5300 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;
5301 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;
5302 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;
5303 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;
5305 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5306 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;
5308 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;
5309 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;
5311 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5313 if cmd = 'ammo' then
5314 begin
5315 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5316 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5317 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5318 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5319 plr.GiveItem(ITEM_AMMO_FUELCAN);
5320 g_Console_Add('player got some ammo');
5321 continue;
5322 end;
5324 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5325 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5327 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5328 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5330 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5331 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5333 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5334 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5336 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5338 if cmd = 'weapons' then
5339 begin
5340 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5341 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5342 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5343 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5344 plr.GiveItem(ITEM_WEAPON_PLASMA);
5345 plr.GiveItem(ITEM_WEAPON_BFG);
5346 g_Console_Add('player got weapons');
5347 continue;
5348 end;
5350 if cmd = 'keys' then
5351 begin
5352 plr.GiveItem(ITEM_KEY_RED);
5353 plr.GiveItem(ITEM_KEY_GREEN);
5354 plr.GiveItem(ITEM_KEY_BLUE);
5355 g_Console_Add('player got all keys');
5356 continue;
5357 end;
5359 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5360 end;
5361 exit;
5362 end;
5363 // open
5364 if cmd = 'open' then
5365 begin
5366 g_Console_Add('player activated sesame');
5367 g_Triggers_OpenAll();
5368 exit;
5369 end;
5370 // fly
5371 if cmd = 'fly' then
5372 begin
5373 gFly := not gFly;
5374 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5375 exit;
5376 end;
5377 // noclip
5378 if cmd = 'noclip' then
5379 begin
5380 plr.SwitchNoClip;
5381 g_Console_Add('wall hardeness adjusted');
5382 exit;
5383 end;
5384 // notarget
5385 if cmd = 'notarget' then
5386 begin
5387 plr.NoTarget := not plr.NoTarget;
5388 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5389 exit;
5390 end;
5391 // noreload
5392 if cmd = 'noreload' then
5393 begin
5394 plr.NoReload := not plr.NoReload;
5395 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5396 exit;
5397 end;
5398 // speedy
5399 if cmd = 'speedy' then
5400 begin
5401 MAX_RUNVEL := 32-MAX_RUNVEL;
5402 g_Console_Add('speed adjusted');
5403 exit;
5404 end;
5405 // jumpy
5406 if cmd = 'jumpy' then
5407 begin
5408 VEL_JUMP := 30-VEL_JUMP;
5409 g_Console_Add('jump height adjusted');
5410 exit;
5411 end;
5412 // automap
5413 if cmd = 'automap' then
5414 begin
5415 gShowMap := not gShowMap;
5416 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5417 exit;
5418 end;
5419 // aimline
5420 if cmd = 'aimline' then
5421 begin
5422 gAimLine := not gAimLine;
5423 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5424 exit;
5425 end;
5426 end;
5428 procedure GameCommands(P: SArray);
5429 var
5430 a, b: Integer;
5431 s, pw: String;
5432 chstr: string;
5433 cmd: string;
5434 pl: pTNetClient = nil;
5435 plr: TPlayer;
5436 prt: Word;
5437 nm: Boolean;
5438 listen: LongWord;
5439 begin
5440 // Îáùèå êîìàíäû:
5441 cmd := LowerCase(P[0]);
5442 chstr := '';
5443 if (cmd = 'quit') or
5444 (cmd = 'exit') then
5445 begin
5446 g_Game_Free();
5447 g_Game_Quit();
5448 Exit;
5449 end
5450 else if cmd = 'pause' then
5451 begin
5452 if (g_ActiveWindow = nil) then
5453 g_Game_Pause(not gPause);
5454 end
5455 else if cmd = 'endgame' then
5456 gExit := EXIT_SIMPLE
5457 else if cmd = 'restart' then
5458 begin
5459 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5460 begin
5461 if g_Game_IsClient then
5462 begin
5463 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5464 Exit;
5465 end;
5466 g_Game_Restart();
5467 end else
5468 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5469 end
5470 else if cmd = 'kick' then
5471 begin
5472 if g_Game_IsServer then
5473 begin
5474 if Length(P) < 2 then
5475 begin
5476 g_Console_Add('kick <name>');
5477 Exit;
5478 end;
5479 if P[1] = '' then
5480 begin
5481 g_Console_Add('kick <name>');
5482 Exit;
5483 end;
5485 if g_Game_IsNet then
5486 pl := g_Net_Client_ByName(P[1]);
5487 if (pl <> nil) then
5488 begin
5489 s := g_Net_ClientName_ByID(pl^.ID);
5490 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5491 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5492 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5493 if NetUseMaster then
5494 g_Net_Slist_Update;
5495 end else if gPlayers <> nil then
5496 for a := Low(gPlayers) to High(gPlayers) do
5497 if gPlayers[a] <> nil then
5498 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5499 begin
5500 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5501 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5502 continue;
5503 gPlayers[a].Lives := 0;
5504 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5505 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5506 g_Player_Remove(gPlayers[a].UID);
5507 if NetUseMaster then
5508 g_Net_Slist_Update;
5509 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5510 g_Bot_MixNames();
5511 end;
5512 end else
5513 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5514 end
5515 else if cmd = 'kick_id' then
5516 begin
5517 if g_Game_IsServer and g_Game_IsNet then
5518 begin
5519 if Length(P) < 2 then
5520 begin
5521 g_Console_Add('kick_id <client ID>');
5522 Exit;
5523 end;
5524 if P[1] = '' then
5525 begin
5526 g_Console_Add('kick_id <client ID>');
5527 Exit;
5528 end;
5530 a := StrToIntDef(P[1], 0);
5531 if (NetClients <> nil) and (a <= High(NetClients)) then
5532 begin
5533 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5534 begin
5535 s := g_Net_ClientName_ByID(NetClients[a].ID);
5536 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5537 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5538 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5539 if NetUseMaster then
5540 g_Net_Slist_Update;
5541 end;
5542 end;
5543 end else
5544 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5545 end
5546 else if cmd = 'ban' then
5547 begin
5548 if g_Game_IsServer and g_Game_IsNet then
5549 begin
5550 if Length(P) < 2 then
5551 begin
5552 g_Console_Add('ban <name>');
5553 Exit;
5554 end;
5555 if P[1] = '' then
5556 begin
5557 g_Console_Add('ban <name>');
5558 Exit;
5559 end;
5561 pl := g_Net_Client_ByName(P[1]);
5562 if (pl <> nil) then
5563 begin
5564 s := g_Net_ClientName_ByID(pl^.ID);
5565 g_Net_BanHost(pl^.Peer^.address.host, False);
5566 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5567 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5568 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5569 if NetUseMaster then
5570 g_Net_Slist_Update;
5571 end else
5572 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5573 end else
5574 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5575 end
5576 else if cmd = 'ban_id' then
5577 begin
5578 if g_Game_IsServer and g_Game_IsNet then
5579 begin
5580 if Length(P) < 2 then
5581 begin
5582 g_Console_Add('ban_id <client ID>');
5583 Exit;
5584 end;
5585 if P[1] = '' then
5586 begin
5587 g_Console_Add('ban_id <client ID>');
5588 Exit;
5589 end;
5591 a := StrToIntDef(P[1], 0);
5592 if (NetClients <> nil) and (a <= High(NetClients)) then
5593 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5594 begin
5595 s := g_Net_ClientName_ByID(NetClients[a].ID);
5596 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5597 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5598 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5599 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5600 if NetUseMaster then
5601 g_Net_Slist_Update;
5602 end;
5603 end else
5604 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5605 end
5606 else if cmd = 'permban' then
5607 begin
5608 if g_Game_IsServer and g_Game_IsNet then
5609 begin
5610 if Length(P) < 2 then
5611 begin
5612 g_Console_Add('permban <name>');
5613 Exit;
5614 end;
5615 if P[1] = '' then
5616 begin
5617 g_Console_Add('permban <name>');
5618 Exit;
5619 end;
5621 pl := g_Net_Client_ByName(P[1]);
5622 if (pl <> nil) then
5623 begin
5624 s := g_Net_ClientName_ByID(pl^.ID);
5625 g_Net_BanHost(pl^.Peer^.address.host);
5626 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5627 g_Net_SaveBanList();
5628 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5629 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5630 if NetUseMaster then
5631 g_Net_Slist_Update;
5632 end else
5633 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5634 end else
5635 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5636 end
5637 else if cmd = 'permban_id' then
5638 begin
5639 if g_Game_IsServer and g_Game_IsNet then
5640 begin
5641 if Length(P) < 2 then
5642 begin
5643 g_Console_Add('permban_id <client ID>');
5644 Exit;
5645 end;
5646 if P[1] = '' then
5647 begin
5648 g_Console_Add('permban_id <client ID>');
5649 Exit;
5650 end;
5652 a := StrToIntDef(P[1], 0);
5653 if (NetClients <> nil) and (a <= High(NetClients)) then
5654 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5655 begin
5656 s := g_Net_ClientName_ByID(NetClients[a].ID);
5657 g_Net_BanHost(NetClients[a].Peer^.address.host);
5658 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5659 g_Net_SaveBanList();
5660 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5661 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5662 if NetUseMaster then
5663 g_Net_Slist_Update;
5664 end;
5665 end else
5666 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5667 end
5668 else if cmd = 'unban' then
5669 begin
5670 if g_Game_IsServer and g_Game_IsNet then
5671 begin
5672 if Length(P) < 2 then
5673 begin
5674 g_Console_Add('unban <IP Address>');
5675 Exit;
5676 end;
5677 if P[1] = '' then
5678 begin
5679 g_Console_Add('unban <IP Address>');
5680 Exit;
5681 end;
5683 if g_Net_UnbanHost(P[1]) then
5684 begin
5685 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5686 g_Net_SaveBanList();
5687 end else
5688 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5689 end else
5690 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5691 end
5692 else if cmd = 'clientlist' then
5693 begin
5694 if g_Game_IsServer and g_Game_IsNet then
5695 begin
5696 b := 0;
5697 if NetClients <> nil then
5698 for a := Low(NetClients) to High(NetClients) do
5699 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5700 begin
5701 plr := g_Player_Get(NetClients[a].Player);
5702 if plr = nil then continue;
5703 Inc(b);
5704 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5705 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5706 end;
5707 if b = 0 then
5708 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5709 end else
5710 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5711 end
5712 else if cmd = 'connect' then
5713 begin
5714 if (NetMode = NET_NONE) then
5715 begin
5716 if Length(P) < 2 then
5717 begin
5718 g_Console_Add('connect <IP> [port] [password]');
5719 Exit;
5720 end;
5721 if P[1] = '' then
5722 begin
5723 g_Console_Add('connect <IP> [port] [password]');
5724 Exit;
5725 end;
5727 if Length(P) > 2 then
5728 prt := StrToIntDef(P[2], 25666)
5729 else
5730 prt := 25666;
5732 if Length(P) > 3 then
5733 pw := P[3]
5734 else
5735 pw := '';
5737 g_Game_StartClient(P[1], prt, pw);
5738 end;
5739 end
5740 else if cmd = 'disconnect' then
5741 begin
5742 if (NetMode = NET_CLIENT) then
5743 g_Net_Disconnect();
5744 end
5745 else if cmd = 'reconnect' then
5746 begin
5747 if (NetMode = NET_SERVER) then
5748 Exit;
5750 if (NetMode = NET_CLIENT) then
5751 begin
5752 g_Net_Disconnect();
5753 gExit := EXIT_SIMPLE;
5754 EndGame;
5755 end;
5757 //TODO: Use last successful password to reconnect, instead of ''
5758 g_Game_StartClient(NetClientIP, NetClientPort, '');
5759 end
5760 else if (cmd = 'addbot') or
5761 (cmd = 'bot_add') then
5762 begin
5763 if Length(P) > 1 then
5764 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5765 else
5766 g_Bot_Add(TEAM_NONE, 2);
5767 end
5768 else if cmd = 'bot_addlist' then
5769 begin
5770 if Length(P) > 1 then
5771 if Length(P) = 2 then
5772 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5773 else
5774 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5775 end
5776 else if cmd = 'bot_removeall' then
5777 g_Bot_RemoveAll()
5778 else if cmd = 'chat' then
5779 begin
5780 if g_Game_IsNet then
5781 begin
5782 if Length(P) > 1 then
5783 begin
5784 for a := 1 to High(P) do
5785 chstr := chstr + P[a] + ' ';
5787 if Length(chstr) > 200 then SetLength(chstr, 200);
5789 if Length(chstr) < 1 then
5790 begin
5791 g_Console_Add('chat <text>');
5792 Exit;
5793 end;
5795 chstr := b_Text_Format(chstr);
5796 if g_Game_IsClient then
5797 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5798 else
5799 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5800 end
5801 else
5802 g_Console_Add('chat <text>');
5803 end else
5804 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5805 end
5806 else if cmd = 'teamchat' then
5807 begin
5808 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5809 begin
5810 if Length(P) > 1 then
5811 begin
5812 for a := 1 to High(P) do
5813 chstr := chstr + P[a] + ' ';
5815 if Length(chstr) > 200 then SetLength(chstr, 200);
5817 if Length(chstr) < 1 then
5818 begin
5819 g_Console_Add('teamchat <text>');
5820 Exit;
5821 end;
5823 chstr := b_Text_Format(chstr);
5824 if g_Game_IsClient then
5825 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5826 else
5827 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5828 gPlayer1Settings.Team);
5829 end
5830 else
5831 g_Console_Add('teamchat <text>');
5832 end else
5833 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5834 end
5835 else if cmd = 'game' then
5836 begin
5837 if gGameSettings.GameType <> GT_NONE then
5838 begin
5839 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5840 Exit;
5841 end;
5842 if Length(P) = 1 then
5843 begin
5844 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5845 Exit;
5846 end;
5847 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5848 P[1] := addWadExtension(P[1]);
5849 if FileExists(MapsDir + P[1]) then
5850 begin
5851 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5852 if Length(P) < 3 then
5853 begin
5854 SetLength(P, 3);
5855 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5856 end;
5858 s := P[1] + ':\' + UpperCase(P[2]);
5860 if g_Map_Exist(MapsDir + s) then
5861 begin
5862 // Çàïóñêàåì ñâîþ èãðó
5863 g_Game_Free();
5864 with gGameSettings do
5865 begin
5866 GameMode := g_Game_TextToMode(gcGameMode);
5867 if gSwitchGameMode <> GM_NONE then
5868 GameMode := gSwitchGameMode;
5869 if GameMode = GM_NONE then GameMode := GM_DM;
5870 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5871 b := 1;
5872 if Length(P) >= 4 then
5873 b := StrToIntDef(P[3], 1);
5874 g_Game_StartCustom(s, GameMode, TimeLimit,
5875 GoalLimit, MaxLives, Options, b);
5876 end;
5877 end
5878 else
5879 if P[2] = '' then
5880 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5881 else
5882 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5883 end else
5884 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5885 end
5886 else if cmd = 'host' then
5887 begin
5888 if gGameSettings.GameType <> GT_NONE then
5889 begin
5890 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5891 Exit;
5892 end;
5893 if Length(P) < 4 then
5894 begin
5895 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5896 Exit;
5897 end;
5898 if not StrToIp(P[1], listen) then
5899 Exit;
5900 prt := StrToIntDef(P[2], 25666);
5902 P[3] := addWadExtension(P[3]);
5903 if FileExists(MapsDir + P[3]) then
5904 begin
5905 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5906 if Length(P) < 5 then
5907 begin
5908 SetLength(P, 5);
5909 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5910 end;
5912 s := P[3] + ':\' + UpperCase(P[4]);
5914 if g_Map_Exist(MapsDir + s) then
5915 begin
5916 // Çàïóñêàåì ñâîþ èãðó
5917 g_Game_Free();
5918 with gGameSettings do
5919 begin
5920 GameMode := g_Game_TextToMode(gcGameMode);
5921 if gSwitchGameMode <> GM_NONE then
5922 GameMode := gSwitchGameMode;
5923 if GameMode = GM_NONE then GameMode := GM_DM;
5924 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5925 b := 0;
5926 if Length(P) >= 6 then
5927 b := StrToIntDef(P[5], 0);
5928 g_Game_StartServer(s, GameMode, TimeLimit,
5929 GoalLimit, MaxLives, Options, b, listen, prt);
5930 end;
5931 end
5932 else
5933 if P[4] = '' then
5934 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5935 else
5936 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5937 end else
5938 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5939 end
5940 else if cmd = 'map' then
5941 begin
5942 if Length(P) = 1 then
5943 begin
5944 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5945 begin
5946 g_Console_Add(cmd + ' <MAP>');
5947 g_Console_Add(cmd + ' <WAD> [MAP]');
5948 end else
5949 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5950 end else
5951 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5952 begin
5953 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5954 if Length(P) < 3 then
5955 begin
5956 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5957 s := UpperCase(P[1]);
5958 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5959 begin // Êàðòà íàøëàñü
5960 gExitByTrigger := False;
5961 if gGameOn then
5962 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5963 gNextMap := s;
5964 gExit := EXIT_ENDLEVELCUSTOM;
5965 end
5966 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5967 g_Game_ChangeMap(s);
5968 end else
5969 begin
5970 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5971 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5972 P[1] := addWadExtension(P[1]);
5973 if FileExists(MapsDir + P[1]) then
5974 begin
5975 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5976 SetLength(P, 3);
5977 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5979 s := P[1] + ':\' + P[2];
5981 if g_Map_Exist(MapsDir + s) then
5982 begin
5983 gExitByTrigger := False;
5984 if gGameOn then
5985 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5986 gNextMap := s;
5987 gExit := EXIT_ENDLEVELCUSTOM;
5988 end
5989 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5990 g_Game_ChangeMap(s);
5991 end else
5992 if P[2] = '' then
5993 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5994 else
5995 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5996 end else
5997 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5998 end;
5999 end else
6000 begin
6001 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6002 P[1] := addWadExtension(P[1]);
6003 if FileExists(MapsDir + P[1]) then
6004 begin
6005 // Íàøëè WAD ôàéë
6006 P[2] := UpperCase(P[2]);
6007 s := P[1] + ':\' + P[2];
6009 if g_Map_Exist(MapsDir + s) then
6010 begin // Íàøëè êàðòó
6011 gExitByTrigger := False;
6012 if gGameOn then
6013 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6014 gNextMap := s;
6015 gExit := EXIT_ENDLEVELCUSTOM;
6016 end
6017 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6018 g_Game_ChangeMap(s);
6019 end else
6020 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6021 end else
6022 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6023 end;
6024 end else
6025 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6026 end
6027 else if cmd = 'nextmap' then
6028 begin
6029 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6030 g_Console_Add(_lc[I_MSG_NOT_GAME])
6031 else begin
6032 nm := True;
6033 if Length(P) = 1 then
6034 begin
6035 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6036 begin
6037 g_Console_Add(cmd + ' <MAP>');
6038 g_Console_Add(cmd + ' <WAD> [MAP]');
6039 end else begin
6040 nm := False;
6041 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6042 end;
6043 end else
6044 begin
6045 nm := False;
6046 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6047 begin
6048 if Length(P) < 3 then
6049 begin
6050 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6051 s := UpperCase(P[1]);
6052 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6053 begin // Êàðòà íàøëàñü
6054 gExitByTrigger := False;
6055 gNextMap := s;
6056 nm := True;
6057 end else
6058 begin
6059 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6060 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6061 P[1] := addWadExtension(P[1]);
6062 if FileExists(MapsDir + P[1]) then
6063 begin
6064 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6065 SetLength(P, 3);
6066 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6068 s := P[1] + ':\' + P[2];
6070 if g_Map_Exist(MapsDir + s) then
6071 begin // Óñòàíàâëèâàåì êàðòó
6072 gExitByTrigger := False;
6073 gNextMap := s;
6074 nm := True;
6075 end else
6076 if P[2] = '' then
6077 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6078 else
6079 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6080 end else
6081 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6082 end;
6083 end else
6084 begin
6085 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6086 P[1] := addWadExtension(P[1]);
6087 if FileExists(MapsDir + P[1]) then
6088 begin
6089 // Íàøëè WAD ôàéë
6090 P[2] := UpperCase(P[2]);
6091 s := P[1] + ':\' + P[2];
6093 if g_Map_Exist(MapsDir + s) then
6094 begin // Íàøëè êàðòó
6095 gExitByTrigger := False;
6096 gNextMap := s;
6097 nm := True;
6098 end else
6099 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6100 end else
6101 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6102 end;
6103 end else
6104 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6105 end;
6106 if nm then
6107 if gNextMap = '' then
6108 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6109 else
6110 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6111 end;
6112 end
6113 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6114 begin
6115 if not gGameOn then
6116 g_Console_Add(_lc[I_MSG_NOT_GAME])
6117 else
6118 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6119 begin
6120 gExitByTrigger := False;
6121 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6122 if (gNextMap = '') and (gTriggers <> nil) then
6123 for a := 0 to High(gTriggers) do
6124 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6125 begin
6126 gExitByTrigger := True;
6127 //gNextMap := gTriggers[a].Data.MapName;
6128 gNextMap := gTriggers[a].tgcMap;
6129 Break;
6130 end;
6131 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6132 if gNextMap = '' then
6133 gNextMap := g_Game_GetNextMap();
6134 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6135 if not isWadPath(gNextMap) then
6136 s := gGameSettings.WAD + ':\' + gNextMap
6137 else
6138 s := gNextMap;
6139 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6140 if g_Map_Exist(MapsDir + s) then
6141 gExit := EXIT_ENDLEVELCUSTOM
6142 else
6143 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6144 end else
6145 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6146 end
6147 else if (cmd = 'event') then
6148 begin
6149 if (Length(P) <= 1) then
6150 begin
6151 for a := 0 to High(gEvents) do
6152 if gEvents[a].Command = '' then
6153 g_Console_Add(gEvents[a].Name + ' <none>')
6154 else
6155 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6156 Exit;
6157 end;
6158 if (Length(P) = 2) then
6159 begin
6160 for a := 0 to High(gEvents) do
6161 if gEvents[a].Name = P[1] then
6162 if gEvents[a].Command = '' then
6163 g_Console_Add(gEvents[a].Name + ' <none>')
6164 else
6165 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6166 Exit;
6167 end;
6168 for a := 0 to High(gEvents) do
6169 if gEvents[a].Name = P[1] then
6170 begin
6171 gEvents[a].Command := '';
6172 for b := 2 to High(P) do
6173 if Pos(' ', P[b]) = 0 then
6174 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6175 else
6176 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6177 gEvents[a].Command := Trim(gEvents[a].Command);
6178 Exit;
6179 end;
6180 end
6181 else if cmd = 'suicide' then
6182 begin
6183 if gGameOn then
6184 begin
6185 if g_Game_IsClient then
6186 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6187 else
6188 begin
6189 if gPlayer1 <> nil then
6190 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6191 if gPlayer2 <> nil then
6192 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6193 end;
6194 end;
6195 end
6196 // Êîìàíäû Ñâîåé èãðû:
6197 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6198 begin
6199 if cmd = 'bot_addred' then
6200 begin
6201 if Length(P) > 1 then
6202 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6203 else
6204 g_Bot_Add(TEAM_RED, 2);
6205 end
6206 else if cmd = 'bot_addblue' then
6207 begin
6208 if Length(P) > 1 then
6209 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6210 else
6211 g_Bot_Add(TEAM_BLUE, 2);
6212 end
6213 else if cmd = 'spectate' then
6214 begin
6215 if not gGameOn then
6216 Exit;
6217 g_Game_Spectate();
6218 end
6219 else if cmd = 'say' then
6220 begin
6221 if g_Game_IsServer and g_Game_IsNet then
6222 begin
6223 if Length(P) > 1 then
6224 begin
6225 chstr := '';
6226 for a := 1 to High(P) do
6227 chstr := chstr + P[a] + ' ';
6229 if Length(chstr) > 200 then SetLength(chstr, 200);
6231 if Length(chstr) < 1 then
6232 begin
6233 g_Console_Add('say <text>');
6234 Exit;
6235 end;
6237 chstr := b_Text_Format(chstr);
6238 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6239 end
6240 else g_Console_Add('say <text>');
6241 end else
6242 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6243 end
6244 else if cmd = 'tell' then
6245 begin
6246 if g_Game_IsServer and g_Game_IsNet then
6247 begin
6248 if (Length(P) > 2) and (P[1] <> '') then
6249 begin
6250 chstr := '';
6251 for a := 2 to High(P) do
6252 chstr := chstr + P[a] + ' ';
6254 if Length(chstr) > 200 then SetLength(chstr, 200);
6256 if Length(chstr) < 1 then
6257 begin
6258 g_Console_Add('tell <playername> <text>');
6259 Exit;
6260 end;
6262 pl := g_Net_Client_ByName(P[1]);
6263 if pl <> nil then
6264 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6265 else
6266 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6267 end
6268 else g_Console_Add('tell <playername> <text>');
6269 end else
6270 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6271 end
6272 else if (cmd = 'overtime') and not g_Game_IsClient then
6273 begin
6274 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6275 Exit;
6276 // Äîïîëíèòåëüíîå âðåìÿ:
6277 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6279 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6280 [gGameSettings.TimeLimit div 3600,
6281 (gGameSettings.TimeLimit div 60) mod 60,
6282 gGameSettings.TimeLimit mod 60]));
6283 if g_Game_IsNet then MH_SEND_GameSettings;
6284 end
6285 else if (cmd = 'rcon_password') and g_Game_IsClient then
6286 begin
6287 if (Length(P) <= 1) then
6288 g_Console_Add('rcon_password <password>')
6289 else
6290 MC_SEND_RCONPassword(P[1]);
6291 end
6292 else if cmd = 'rcon' then
6293 begin
6294 if g_Game_IsClient then
6295 begin
6296 if Length(P) > 1 then
6297 begin
6298 chstr := '';
6299 for a := 1 to High(P) do
6300 chstr := chstr + P[a] + ' ';
6302 if Length(chstr) > 200 then SetLength(chstr, 200);
6304 if Length(chstr) < 1 then
6305 begin
6306 g_Console_Add('rcon <command>');
6307 Exit;
6308 end;
6310 MC_SEND_RCONCommand(chstr);
6311 end
6312 else g_Console_Add('rcon <command>');
6313 end;
6314 end
6315 else if cmd = 'ready' then
6316 begin
6317 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6318 gLMSRespawnTime := gTime + 100;
6319 end
6320 else if (cmd = 'callvote') and g_Game_IsNet then
6321 begin
6322 if Length(P) > 1 then
6323 begin
6324 chstr := '';
6325 for a := 1 to High(P) do begin
6326 if a > 1 then chstr := chstr + ' ';
6327 chstr := chstr + P[a];
6328 end;
6330 if Length(chstr) > 200 then SetLength(chstr, 200);
6332 if Length(chstr) < 1 then
6333 begin
6334 g_Console_Add('callvote <command>');
6335 Exit;
6336 end;
6338 if g_Game_IsClient then
6339 MC_SEND_Vote(True, chstr)
6340 else
6341 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6342 g_Console_Process('vote', True);
6343 end
6344 else
6345 g_Console_Add('callvote <command>');
6346 end
6347 else if (cmd = 'vote') and g_Game_IsNet then
6348 begin
6349 if g_Game_IsClient then
6350 MC_SEND_Vote(False)
6351 else if gVoteInProgress then
6352 begin
6353 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6354 a := Floor((NetClientCount+1)/2.0) + 1
6355 else
6356 a := Floor(NetClientCount/2.0) + 1;
6357 if gVoted then
6358 begin
6359 Dec(gVoteCount);
6360 gVoted := False;
6361 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6362 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6363 end
6364 else
6365 begin
6366 Inc(gVoteCount);
6367 gVoted := True;
6368 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6369 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6370 g_Game_CheckVote;
6371 end;
6372 end;
6373 end
6374 end;
6375 end;
6377 procedure g_TakeScreenShot();
6378 var
6379 a: Word;
6380 FileName: string;
6381 ssdir, t: string;
6382 st: TStream;
6383 ok: Boolean;
6384 begin
6385 if e_NoGraphics then Exit;
6386 ssdir := GameDir+'/screenshots';
6387 if not findFileCI(ssdir, true) then
6388 begin
6389 // try to create dir
6390 try
6391 CreateDir(ssdir);
6392 except
6393 end;
6394 if not findFileCI(ssdir, true) then exit; // alas
6395 end;
6396 try
6397 for a := 1 to High(Word) do
6398 begin
6399 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6400 t := FileName;
6401 if findFileCI(t, true) then continue;
6402 if not findFileCI(FileName) then
6403 begin
6404 ok := false;
6405 st := createDiskFile(FileName);
6406 try
6407 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6408 ok := true;
6409 finally
6410 st.Free();
6411 end;
6412 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6413 break;
6414 end;
6415 end;
6416 except
6417 end;
6418 end;
6420 procedure g_Game_InGameMenu(Show: Boolean);
6421 begin
6422 if (g_ActiveWindow = nil) and Show then
6423 begin
6424 if gGameSettings.GameType = GT_SINGLE then
6425 g_GUI_ShowWindow('GameSingleMenu')
6426 else
6427 begin
6428 if g_Game_IsClient then
6429 g_GUI_ShowWindow('GameClientMenu')
6430 else
6431 if g_Game_IsNet then
6432 g_GUI_ShowWindow('GameServerMenu')
6433 else
6434 g_GUI_ShowWindow('GameCustomMenu');
6435 end;
6436 g_Sound_PlayEx('MENU_OPEN');
6438 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6439 if (not g_Game_IsNet) then
6440 g_Game_Pause(True);
6441 end
6442 else
6443 if (g_ActiveWindow <> nil) and (not Show) then
6444 begin
6445 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6446 if (not g_Game_IsNet) then
6447 g_Game_Pause(False);
6448 end;
6449 end;
6451 procedure g_Game_Pause(Enable: Boolean);
6452 begin
6453 if not gGameOn then
6454 Exit;
6456 if gPause = Enable then
6457 Exit;
6459 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6460 Exit;
6462 gPause := Enable;
6463 g_Game_PauseAllSounds(Enable);
6464 end;
6466 procedure g_Game_PauseAllSounds(Enable: Boolean);
6467 var
6468 i: Integer;
6469 begin
6470 // Òðèããåðû:
6471 if gTriggers <> nil then
6472 for i := 0 to High(gTriggers) do
6473 with gTriggers[i] do
6474 if (TriggerType = TRIGGER_SOUND) and
6475 (Sound <> nil) and
6476 Sound.IsPlaying() then
6477 begin
6478 Sound.Pause(Enable);
6479 end;
6481 // Çâóêè èãðîêîâ:
6482 if gPlayers <> nil then
6483 for i := 0 to High(gPlayers) do
6484 if gPlayers[i] <> nil then
6485 gPlayers[i].PauseSounds(Enable);
6487 // Ìóçûêà:
6488 if gMusic <> nil then
6489 gMusic.Pause(Enable);
6490 end;
6492 procedure g_Game_StopAllSounds(all: Boolean);
6493 var
6494 i: Integer;
6495 begin
6496 if gTriggers <> nil then
6497 for i := 0 to High(gTriggers) do
6498 with gTriggers[i] do
6499 if (TriggerType = TRIGGER_SOUND) and
6500 (Sound <> nil) then
6501 Sound.Stop();
6503 if gMusic <> nil then
6504 gMusic.Stop();
6506 if all then
6507 e_StopChannels();
6508 end;
6510 procedure g_Game_UpdateTriggerSounds();
6511 var
6512 i: Integer;
6513 begin
6514 if gTriggers <> nil then
6515 for i := 0 to High(gTriggers) do
6516 with gTriggers[i] do
6517 if (TriggerType = TRIGGER_SOUND) and
6518 (Sound <> nil) and
6519 (tgcLocal) and
6520 Sound.IsPlaying() then
6521 begin
6522 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6523 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6524 begin
6525 Sound.SetPan(0.5 - tgcPan/255.0);
6526 Sound.SetVolume(tgcVolume/255.0);
6527 end
6528 else
6529 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6530 end;
6531 end;
6533 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6534 begin
6535 Result := False;
6536 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6537 begin
6538 Result := True;
6539 Exit;
6540 end;
6541 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6542 begin
6543 Result := True;
6544 Exit;
6545 end;
6546 if gSpectMode <> SPECT_PLAYERS then
6547 Exit;
6548 if gSpectPID1 = UID then
6549 begin
6550 Result := True;
6551 Exit;
6552 end;
6553 if gSpectViewTwo and (gSpectPID2 = UID) then
6554 begin
6555 Result := True;
6556 Exit;
6557 end;
6558 end;
6560 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6561 var
6562 Pl: TPlayer;
6563 begin
6564 Result := False;
6565 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6566 begin
6567 Result := True;
6568 Exit;
6569 end;
6570 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6571 begin
6572 Result := True;
6573 Exit;
6574 end;
6575 if gSpectMode <> SPECT_PLAYERS then
6576 Exit;
6577 Pl := g_Player_Get(gSpectPID1);
6578 if (Pl <> nil) and (Pl.Team = Team) then
6579 begin
6580 Result := True;
6581 Exit;
6582 end;
6583 if gSpectViewTwo then
6584 begin
6585 Pl := g_Player_Get(gSpectPID2);
6586 if (Pl <> nil) and (Pl.Team = Team) then
6587 begin
6588 Result := True;
6589 Exit;
6590 end;
6591 end;
6592 end;
6594 procedure g_Game_Message(Msg: string; Time: Word);
6595 begin
6596 MessageText := b_Text_Format(Msg);
6597 MessageTime := Time;
6598 end;
6600 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6601 var
6602 a: Integer;
6603 begin
6604 case gAnnouncer of
6605 ANNOUNCE_NONE:
6606 Exit;
6607 ANNOUNCE_ME,
6608 ANNOUNCE_MEPLUS:
6609 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6610 Exit;
6611 end;
6612 for a := 0 to 3 do
6613 if goodsnd[a].IsPlaying() then
6614 Exit;
6616 goodsnd[Random(4)].Play();
6617 end;
6619 procedure g_Game_Announce_KillCombo(Param: Integer);
6620 var
6621 UID: Word;
6622 c, n: Byte;
6623 Pl: TPlayer;
6624 Name: String;
6625 begin
6626 UID := Param and $FFFF;
6627 c := Param shr 16;
6628 if c < 2 then
6629 Exit;
6631 Pl := g_Player_Get(UID);
6632 if Pl = nil then
6633 Name := '?'
6634 else
6635 Name := Pl.Name;
6637 case c of
6638 2: begin
6639 n := 0;
6640 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6641 end;
6642 3: begin
6643 n := 1;
6644 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6645 end;
6646 4: begin
6647 n := 2;
6648 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6649 end;
6650 else begin
6651 n := 3;
6652 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6653 end;
6654 end;
6656 case gAnnouncer of
6657 ANNOUNCE_NONE:
6658 Exit;
6659 ANNOUNCE_ME:
6660 if not g_Game_IsWatchedPlayer(UID) then
6661 Exit;
6662 ANNOUNCE_MEPLUS:
6663 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6664 Exit;
6665 end;
6667 if killsnd[n].IsPlaying() then
6668 killsnd[n].Stop();
6669 killsnd[n].Play();
6670 end;
6672 procedure g_Game_StartVote(Command, Initiator: string);
6673 var
6674 Need: Integer;
6675 begin
6676 if not gVotesEnabled then Exit;
6677 if gGameSettings.GameType <> GT_SERVER then Exit;
6678 if gVoteInProgress or gVotePassed then
6679 begin
6680 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6681 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6682 Exit;
6683 end;
6684 gVoteInProgress := True;
6685 gVotePassed := False;
6686 gVoteTimer := gTime + gVoteTimeout * 1000;
6687 gVoteCount := 0;
6688 gVoted := False;
6689 gVoteCommand := Command;
6691 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6692 Need := Floor((NetClientCount+1)/2.0)+1
6693 else
6694 Need := Floor(NetClientCount/2.0)+1;
6695 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6696 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6697 end;
6699 procedure g_Game_CheckVote;
6700 var
6701 I, Need: Integer;
6702 begin
6703 if gGameSettings.GameType <> GT_SERVER then Exit;
6704 if not gVoteInProgress then Exit;
6706 if (gTime >= gVoteTimer) then
6707 begin
6708 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6709 Need := Floor((NetClientCount+1)/2.0) + 1
6710 else
6711 Need := Floor(NetClientCount/2.0) + 1;
6712 if gVoteCount >= Need then
6713 begin
6714 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6715 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6716 gVotePassed := True;
6717 gVoteCmdTimer := gTime + 5000;
6718 end
6719 else
6720 begin
6721 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6722 MH_SEND_VoteEvent(NET_VE_FAILED);
6723 end;
6724 if NetClients <> nil then
6725 for I := Low(NetClients) to High(NetClients) do
6726 if NetClients[i].Used then
6727 NetClients[i].Voted := False;
6728 gVoteInProgress := False;
6729 gVoted := False;
6730 gVoteCount := 0;
6731 end
6732 else
6733 begin
6734 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6735 Need := Floor((NetClientCount+1)/2.0) + 1
6736 else
6737 Need := Floor(NetClientCount/2.0) + 1;
6738 if gVoteCount >= Need then
6739 begin
6740 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6741 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6742 gVoteInProgress := False;
6743 gVotePassed := True;
6744 gVoteCmdTimer := gTime + 5000;
6745 gVoted := False;
6746 gVoteCount := 0;
6747 if NetClients <> nil then
6748 for I := Low(NetClients) to High(NetClients) do
6749 if NetClients[i].Used then
6750 NetClients[i].Voted := False;
6751 end;
6752 end;
6753 end;
6755 procedure g_Game_LoadMapList(FileName: string);
6756 var
6757 ListFile: TextFile;
6758 s: string;
6759 begin
6760 MapList := nil;
6761 MapIndex := -1;
6763 if not FileExists(FileName) then Exit;
6765 AssignFile(ListFile, FileName);
6766 Reset(ListFile);
6767 while not EOF(ListFile) do
6768 begin
6769 ReadLn(ListFile, s);
6771 s := Trim(s);
6772 if s = '' then Continue;
6774 SetLength(MapList, Length(MapList)+1);
6775 MapList[High(MapList)] := s;
6776 end;
6777 CloseFile(ListFile);
6778 end;
6780 procedure g_Game_SetDebugMode();
6781 begin
6782 gDebugMode := True;
6783 // ×èòû (äàæå â ñâîåé èãðå):
6784 gCheats := True;
6785 end;
6787 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6788 var
6789 i: Word;
6790 begin
6791 if Length(LoadingStat.Msgs) = 0 then
6792 Exit;
6794 with LoadingStat do
6795 begin
6796 if not reWrite then
6797 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6798 if NextMsg = Length(Msgs) then
6799 begin // scroll
6800 for i := 0 to High(Msgs)-1 do
6801 Msgs[i] := Msgs[i+1];
6802 end
6803 else
6804 Inc(NextMsg);
6805 end else
6806 if NextMsg = 0 then
6807 Inc(NextMsg);
6809 Msgs[NextMsg-1] := Text;
6810 CurValue := 0;
6811 MaxValue := Max;
6812 ShowCount := 0;
6813 end;
6815 g_ActiveWindow := nil;
6817 ProcessLoading(true);
6818 end;
6820 procedure g_Game_StepLoading();
6821 begin
6822 with LoadingStat do
6823 begin
6824 Inc(CurValue);
6825 Inc(ShowCount);
6826 if (ShowCount > LOADING_SHOW_STEP) then
6827 begin
6828 ShowCount := 0;
6829 ProcessLoading();
6830 end;
6831 end;
6832 end;
6834 procedure g_Game_ClearLoading();
6835 var
6836 len: Word;
6837 begin
6838 with LoadingStat do
6839 begin
6840 CurValue := 0;
6841 MaxValue := 0;
6842 ShowCount := 0;
6843 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6844 if len < 1 then len := 1;
6845 SetLength(Msgs, len);
6846 for len := Low(Msgs) to High(Msgs) do
6847 Msgs[len] := '';
6848 NextMsg := 0;
6849 end;
6850 end;
6852 procedure Parse_Params(var pars: TParamStrValues);
6853 var
6854 i: Integer;
6855 s: String;
6856 begin
6857 SetLength(pars, 0);
6858 i := 1;
6859 while i <= ParamCount do
6860 begin
6861 s := ParamStr(i);
6862 if (s[1] = '-') and (Length(s) > 1) then
6863 begin
6864 if (s[2] = '-') and (Length(s) > 2) then
6865 begin // Îäèíî÷íûé ïàðàìåòð
6866 SetLength(pars, Length(pars) + 1);
6867 with pars[High(pars)] do
6868 begin
6869 Name := LowerCase(s);
6870 Value := '+';
6871 end;
6872 end
6873 else
6874 if (i < ParamCount) then
6875 begin // Ïàðàìåòð ñî çíà÷åíèåì
6876 Inc(i);
6877 SetLength(pars, Length(pars) + 1);
6878 with pars[High(pars)] do
6879 begin
6880 Name := LowerCase(s);
6881 Value := LowerCase(ParamStr(i));
6882 end;
6883 end;
6884 end;
6886 Inc(i);
6887 end;
6888 end;
6890 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6891 var
6892 i: Integer;
6893 begin
6894 Result := '';
6895 for i := 0 to High(pars) do
6896 if pars[i].Name = aName then
6897 begin
6898 Result := pars[i].Value;
6899 Break;
6900 end;
6901 end;
6903 procedure g_Game_Process_Params();
6904 var
6905 pars: TParamStrValues;
6906 map: String;
6907 GMode, n: Byte;
6908 LimT, LimS: Integer;
6909 Opt: LongWord;
6910 Lives: Integer;
6911 s: String;
6912 Port: Integer;
6913 ip: String;
6914 F: TextFile;
6915 begin
6916 Parse_Params(pars);
6918 // Debug mode:
6919 s := Find_Param_Value(pars, '--debug');
6920 if (s <> '') then
6921 begin
6922 g_Game_SetDebugMode();
6923 s := Find_Param_Value(pars, '--netdump');
6924 if (s <> '') then
6925 NetDump := True;
6926 end;
6928 // Connect when game loads
6929 ip := Find_Param_Value(pars, '-connect');
6931 if ip <> '' then
6932 begin
6933 s := Find_Param_Value(pars, '-port');
6934 if (s = '') or not TryStrToInt(s, Port) then
6935 Port := 25666;
6937 s := Find_Param_Value(pars, '-pw');
6939 g_Game_StartClient(ip, Port, s);
6940 Exit;
6941 end;
6943 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
6944 if (s <> '') then
6945 begin
6946 gDefaultMegawadStart := s;
6947 end;
6949 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
6950 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
6951 begin
6952 gDefaultMegawadStart := DF_Default_Megawad_Start;
6953 end;
6955 // Start map when game loads:
6956 map := LowerCase(Find_Param_Value(pars, '-map'));
6957 if isWadPath(map) then
6958 begin
6959 // Game mode:
6960 s := Find_Param_Value(pars, '-gm');
6961 GMode := g_Game_TextToMode(s);
6962 if GMode = GM_NONE then GMode := GM_DM;
6963 if GMode = GM_SINGLE then GMode := GM_COOP;
6965 // Time limit:
6966 s := Find_Param_Value(pars, '-limt');
6967 if (s = '') or (not TryStrToInt(s, LimT)) then
6968 LimT := 0;
6969 if LimT < 0 then
6970 LimT := 0;
6972 // Goal limit:
6973 s := Find_Param_Value(pars, '-lims');
6974 if (s = '') or (not TryStrToInt(s, LimS)) then
6975 LimS := 0;
6976 if LimS < 0 then
6977 LimS := 0;
6979 // Lives limit:
6980 s := Find_Param_Value(pars, '-lives');
6981 if (s = '') or (not TryStrToInt(s, Lives)) then
6982 Lives := 0;
6983 if Lives < 0 then
6984 Lives := 0;
6986 // Options:
6987 s := Find_Param_Value(pars, '-opt');
6988 if (s = '') then
6989 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6990 else
6991 Opt := StrToIntDef(s, 0);
6992 if Opt = 0 then
6993 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6995 // Close after map:
6996 s := Find_Param_Value(pars, '--close');
6997 if (s <> '') then
6998 gMapOnce := True;
7000 // Delete test map after play:
7001 s := Find_Param_Value(pars, '--testdelete');
7002 if (s <> '') then
7003 begin
7004 gMapToDelete := MapsDir + map;
7005 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
7006 Halt(1);
7007 end;
7009 // Delete temporary WAD after play:
7010 s := Find_Param_Value(pars, '--tempdelete');
7011 if (s <> '') then
7012 begin
7013 gMapToDelete := MapsDir + map;
7014 gTempDelete := True;
7015 end;
7017 // Number of players:
7018 s := Find_Param_Value(pars, '-pl');
7019 if (s = '') then
7020 n := 1
7021 else
7022 n := StrToIntDef(s, 1);
7024 // Start:
7025 s := Find_Param_Value(pars, '-port');
7026 if (s = '') or not TryStrToInt(s, Port) then
7027 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7028 else
7029 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7030 end;
7032 // Execute script when game loads:
7033 s := Find_Param_Value(pars, '-exec');
7034 if s <> '' then
7035 begin
7036 if not isWadPath(s) then
7037 s := GameDir + '/' + s;
7039 {$I-}
7040 AssignFile(F, s);
7041 Reset(F);
7042 if IOResult <> 0 then
7043 begin
7044 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7045 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7046 CloseFile(F);
7047 Exit;
7048 end;
7049 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
7050 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7052 while not EOF(F) do
7053 begin
7054 ReadLn(F, s);
7055 if IOResult <> 0 then
7056 begin
7057 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7058 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7059 CloseFile(F);
7060 Exit;
7061 end;
7062 if Pos('#', s) <> 1 then // script comment
7063 g_Console_Process(s, True);
7064 end;
7066 CloseFile(F);
7067 {$I+}
7068 end;
7070 SetLength(pars, 0);
7071 end;
7073 begin
7074 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7075 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7076 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7077 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7079 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7080 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7081 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7082 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7084 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7085 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7087 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7088 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7090 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7092 conRegVar('dbg_scale', @g_dbg_scale, 0.01, 5.0, 'experimental deBUG scale mode', '', true);
7093 end.