DEADSOFTWARE

allow 'mywad.wad:/map01' in '-map' and other similar places (previously, only ':...
[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 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2784 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
2785 procedure renderMapInternal (backXOfs, backYOfs: Integer; transX, transY: Integer; setTransMatrix: Boolean);
2786 type
2787 TDrawCB = procedure ();
2789 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
2790 var
2791 tagmask: Integer;
2792 pan: TPanel;
2793 begin
2794 profileFrameDraw.sectionBegin(profname);
2795 if gdbg_map_use_accel_render then
2796 begin
2797 tagmask := panelTypeToTag(panType);
2798 while (gDrawPanelList.count > 0) do
2799 begin
2800 pan := TPanel(gDrawPanelList.front());
2801 if ((pan.tag and tagmask) = 0) then break;
2802 if doDraw then pan.Draw();
2803 gDrawPanelList.popFront();
2804 end;
2805 end
2806 else
2807 begin
2808 if doDraw then g_Map_DrawPanels(panType);
2809 end;
2810 profileFrameDraw.sectionEnd();
2811 end;
2813 procedure drawOther (profname: AnsiString; cb: TDrawCB);
2814 begin
2815 profileFrameDraw.sectionBegin(profname);
2816 if assigned(cb) then cb();
2817 profileFrameDraw.sectionEnd();
2818 end;
2820 begin
2821 profileFrameDraw.sectionBegin('total');
2823 // our accelerated renderer will collect all panels to gDrawPanelList
2824 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
2825 profileFrameDraw.sectionBegin('collect');
2826 if gdbg_map_use_accel_render then
2827 begin
2828 if (g_dbg_scale <> 1.0) then
2829 begin
2830 g_Map_CollectDrawPanels(sX, sY, round(sWidth/g_dbg_scale)+1, round(sHeight/g_dbg_scale)+1);
2831 end
2832 else
2833 begin
2834 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
2835 end;
2836 end;
2837 profileFrameDraw.sectionEnd();
2839 profileFrameDraw.sectionBegin('skyback');
2840 g_Map_DrawBack(backXOfs, backYOfs);
2841 profileFrameDraw.sectionEnd();
2843 if setTransMatrix then
2844 begin
2845 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
2846 glTranslatef(transX, transY, 0);
2847 end;
2849 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
2850 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
2851 drawOther('items', @g_Items_Draw);
2852 drawOther('weapons', @g_Weapon_Draw);
2853 drawOther('shells', @g_Player_DrawShells);
2854 drawOther('drawall', @g_Player_DrawAll);
2855 drawOther('corpses', @g_Player_DrawCorpses);
2856 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
2857 drawOther('monsters', @g_Monsters_Draw);
2858 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
2859 drawOther('gfx', @g_GFX_Draw);
2860 drawOther('flags', @g_Map_DrawFlags);
2861 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
2862 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
2863 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
2864 drawOther('dynlights', @renderDynLightsInternal);
2865 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
2867 if g_debug_HealthBar then
2868 begin
2869 g_Monsters_DrawHealth();
2870 g_Player_DrawHealth();
2871 end;
2873 profileFrameDraw.mainEnd(); // map rendering
2874 end;
2877 procedure DrawMapView(x, y, w, h: Integer);
2879 var
2880 bx, by: Integer;
2881 begin
2882 glPushMatrix();
2884 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2885 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2887 sX := x;
2888 sY := y;
2889 sWidth := w;
2890 sHeight := h;
2892 renderMapInternal(-bx, -by, -x, -y, true);
2894 glPopMatrix();
2895 end;
2898 procedure DrawPlayer(p: TPlayer);
2899 var
2900 px, py, a, b, c, d: Integer;
2901 //R: TRect;
2902 begin
2903 if (p = nil) or (p.FDummy) then
2904 begin
2905 glPushMatrix();
2906 g_Map_DrawBack(0, 0);
2907 glPopMatrix();
2908 Exit;
2909 end;
2911 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
2912 profileFrameDraw.mainBegin(g_profile_frame_draw);
2914 gPlayerDrawn := p;
2916 glPushMatrix();
2918 px := p.GameX + PLAYER_RECT_CX;
2919 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
2921 if px > (gPlayerScreenSize.X div 2) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
2922 if py > (gPlayerScreenSize.Y div 2) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
2924 if px > gMapInfo.Width-(gPlayerScreenSize.X div 2) then a := -gMapInfo.Width+gPlayerScreenSize.X;
2925 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
2927 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
2928 else if (gMapInfo.Width < gPlayerScreenSize.X) then
2929 begin
2930 // hcenter
2931 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
2932 end;
2934 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
2935 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
2936 begin
2937 // vcenter
2938 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
2939 end;
2941 if p.IncCam <> 0 then
2942 begin
2943 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
2944 begin
2945 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2946 begin
2947 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2948 end;
2949 end;
2951 if py < gPlayerScreenSize.Y div 2 then
2952 begin
2953 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2954 begin
2955 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2956 end;
2957 end;
2959 if p.IncCam < 0 then
2960 begin
2961 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
2962 end;
2964 if p.IncCam > 0 then
2965 begin
2966 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
2967 end;
2968 end;
2970 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
2971 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
2972 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2974 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
2975 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
2976 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
2978 sX := -a;
2979 sY := -(b+p.IncCam);
2980 sWidth := gPlayerScreenSize.X;
2981 sHeight := gPlayerScreenSize.Y;
2983 //glTranslatef(a, b+p.IncCam, 0);
2985 p.viewPortX := sX;
2986 p.viewPortY := sY;
2987 p.viewPortW := sWidth;
2988 p.viewPortH := sHeight;
2990 if (p = gPlayer1) then
2991 begin
2992 g_Holmes_plrView(p.viewPortX, p.viewPortY, p.viewPortW, p.viewPortH);
2993 end;
2995 renderMapInternal(-c, -d, a, b+p.IncCam, true);
2997 if p.FSpectator then
2998 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2999 p.GameY + PLAYER_RECT_CY - 4,
3000 'X', gStdFont, 255, 255, 255, 1, True);
3002 for a := 0 to High(gCollideMap) do
3003 for b := 0 to High(gCollideMap[a]) do
3004 begin
3005 d := 0;
3006 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3007 d := d + 1;
3008 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3009 d := d + 2;
3011 case d of
3012 1: e_DrawPoint(1, b, a, 200, 200, 200);
3013 2: e_DrawPoint(1, b, a, 64, 64, 255);
3014 3: e_DrawPoint(1, b, a, 255, 0, 255);
3015 end;
3016 end;
3019 glPopMatrix();
3021 p.DrawPain();
3022 p.DrawPickup();
3023 p.DrawRulez();
3024 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3025 if g_Debug_Player then
3026 g_Player_DrawDebug(p);
3027 p.DrawGUI();
3028 end;
3030 procedure drawProfilers ();
3031 var
3032 px: Integer = -1;
3033 py: Integer = -1;
3034 begin
3035 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3036 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3037 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3038 end;
3040 procedure g_Game_Draw();
3041 var
3042 ID: DWORD;
3043 w, h: Word;
3044 ww, hh: Byte;
3045 Time: Int64;
3046 back: string;
3047 plView1, plView2: TPlayer;
3048 Split: Boolean;
3049 begin
3050 if gExit = EXIT_QUIT then Exit;
3052 Time := GetTimer() {div 1000};
3053 FPSCounter := FPSCounter+1;
3054 if Time - FPSTime >= 1000 then
3055 begin
3056 FPS := FPSCounter;
3057 FPSCounter := 0;
3058 FPSTime := Time;
3059 end;
3061 if gGameOn or (gState = STATE_FOLD) then
3062 begin
3063 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3064 begin
3065 gSpectMode := SPECT_NONE;
3066 if not gRevertPlayers then
3067 begin
3068 plView1 := gPlayer1;
3069 plView2 := gPlayer2;
3070 end
3071 else
3072 begin
3073 plView1 := gPlayer2;
3074 plView2 := gPlayer1;
3075 end;
3076 end
3077 else
3078 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3079 begin
3080 gSpectMode := SPECT_NONE;
3081 if gPlayer2 = nil then
3082 plView1 := gPlayer1
3083 else
3084 plView1 := gPlayer2;
3085 plView2 := nil;
3086 end
3087 else
3088 begin
3089 plView1 := nil;
3090 plView2 := nil;
3091 end;
3093 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3094 gSpectMode := SPECT_STATS;
3096 if gSpectMode = SPECT_PLAYERS then
3097 if gPlayers <> nil then
3098 begin
3099 plView1 := GetActivePlayer_ByID(gSpectPID1);
3100 if plView1 = nil then
3101 begin
3102 gSpectPID1 := GetActivePlayerID_Next();
3103 plView1 := GetActivePlayer_ByID(gSpectPID1);
3104 end;
3105 if gSpectViewTwo then
3106 begin
3107 plView2 := GetActivePlayer_ByID(gSpectPID2);
3108 if plView2 = nil then
3109 begin
3110 gSpectPID2 := GetActivePlayerID_Next();
3111 plView2 := GetActivePlayer_ByID(gSpectPID2);
3112 end;
3113 end;
3114 end;
3116 if gSpectMode = SPECT_MAPVIEW then
3117 begin
3118 // Ðåæèì ïðîñìîòðà êàðòû
3119 Split := False;
3120 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3121 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3122 gHearPoint1.Active := True;
3123 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3124 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3125 gHearPoint2.Active := False;
3126 end
3127 else
3128 begin
3129 Split := (plView1 <> nil) and (plView2 <> nil);
3131 // Òî÷êè ñëóõà èãðîêîâ
3132 if plView1 <> nil then
3133 begin
3134 gHearPoint1.Active := True;
3135 gHearPoint1.Coords.X := plView1.GameX;
3136 gHearPoint1.Coords.Y := plView1.GameY;
3137 end else
3138 gHearPoint1.Active := False;
3139 if plView2 <> nil then
3140 begin
3141 gHearPoint2.Active := True;
3142 gHearPoint2.Coords.X := plView2.GameX;
3143 gHearPoint2.Coords.Y := plView2.GameY;
3144 end else
3145 gHearPoint2.Active := False;
3147 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3148 gPlayerScreenSize.X := gScreenWidth-196;
3149 if Split then
3150 begin
3151 gPlayerScreenSize.Y := gScreenHeight div 2;
3152 if gScreenHeight mod 2 = 0 then
3153 Dec(gPlayerScreenSize.Y);
3154 end
3155 else
3156 gPlayerScreenSize.Y := gScreenHeight;
3158 if Split then
3159 if gScreenHeight mod 2 = 0 then
3160 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3161 else
3162 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3164 DrawPlayer(plView1);
3165 gPlayer1ScreenCoord.X := sX;
3166 gPlayer1ScreenCoord.Y := sY;
3168 if Split then
3169 begin
3170 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3172 DrawPlayer(plView2);
3173 gPlayer2ScreenCoord.X := sX;
3174 gPlayer2ScreenCoord.Y := sY;
3175 end;
3177 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3179 if Split then
3180 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3181 end;
3183 if MessageText <> '' then
3184 begin
3185 w := 0;
3186 h := 0;
3187 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3188 if Split then
3189 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3190 (gScreenHeight div 2)-(h div 2), MessageText)
3191 else
3192 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3193 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3194 end;
3196 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3198 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3199 begin
3200 // Draw spectator GUI
3201 ww := 0;
3202 hh := 0;
3203 e_TextureFontGetSize(gStdFont, ww, hh);
3204 case gSpectMode of
3205 SPECT_STATS:
3206 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3207 SPECT_MAPVIEW:
3208 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3209 SPECT_PLAYERS:
3210 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3211 end;
3212 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3213 if gSpectMode = SPECT_MAPVIEW then
3214 begin
3215 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3216 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3217 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3218 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3219 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3220 end;
3221 if gSpectMode = SPECT_PLAYERS then
3222 begin
3223 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3224 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3225 if gSpectViewTwo then
3226 begin
3227 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3228 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3229 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3230 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3231 end
3232 else
3233 begin
3234 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3235 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3236 end;
3237 end;
3238 end;
3239 end;
3241 if gPause and gGameOn and (g_ActiveWindow = nil) then
3242 begin
3243 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3244 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3246 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3247 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3248 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3249 end;
3251 if not gGameOn then
3252 begin
3253 if (gState = STATE_MENU) then
3254 begin
3255 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3256 begin
3257 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3258 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3259 end;
3260 // F3 at menu will show game loading dialog
3261 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3262 if (g_ActiveWindow <> nil) then
3263 begin
3264 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3265 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3266 end
3267 else
3268 begin
3269 // F3 at titlepic will show game loading dialog
3270 if e_KeyPressed(IK_F3) then
3271 begin
3272 g_Menu_Show_LoadMenu(true);
3273 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3274 end;
3275 end;
3276 end;
3278 if gState = STATE_FOLD then
3279 begin
3280 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3281 end;
3283 if gState = STATE_INTERCUSTOM then
3284 begin
3285 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3286 begin
3287 back := 'TEXTURE_endpic';
3288 if not g_Texture_Get(back, ID) then
3289 back := _lc[I_TEXTURE_ENDPIC];
3290 end
3291 else
3292 back := 'INTER';
3294 if g_Texture_Get(back, ID) then
3295 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3296 else
3297 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3299 DrawCustomStat();
3301 if g_ActiveWindow <> nil then
3302 begin
3303 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3304 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3305 end;
3306 end;
3308 if gState = STATE_INTERSINGLE then
3309 begin
3310 if EndingGameCounter > 0 then
3311 begin
3312 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3313 end
3314 else
3315 begin
3316 back := 'INTER';
3318 if g_Texture_Get(back, ID) then
3319 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3320 else
3321 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3323 DrawSingleStat();
3325 if g_ActiveWindow <> nil then
3326 begin
3327 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3328 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3329 end;
3330 end;
3331 end;
3333 if gState = STATE_ENDPIC then
3334 begin
3335 ID := DWORD(-1);
3336 if not g_Texture_Get('TEXTURE_endpic', ID) then
3337 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3339 if ID <> DWORD(-1) then
3340 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3341 else
3342 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3344 if g_ActiveWindow <> nil then
3345 begin
3346 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3347 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3348 end;
3349 end;
3351 if gState = STATE_SLIST then
3352 begin
3353 if g_Texture_Get('MENU_BACKGROUND', ID) then
3354 begin
3355 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3356 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3357 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3358 end;
3359 g_Serverlist_Draw(slCurrent);
3360 end;
3361 end;
3363 if g_ActiveWindow <> nil then
3364 begin
3365 if gGameOn then
3366 begin
3367 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3368 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3369 end;
3370 g_ActiveWindow.Draw();
3371 end;
3373 // draw inspector
3374 if (g_holmes_enabled) then g_Holmes_Draw();
3376 g_Console_Draw();
3378 if g_debug_Sounds and gGameOn then
3379 begin
3380 for w := 0 to High(e_SoundsArray) do
3381 for h := 0 to e_SoundsArray[w].nRefs do
3382 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3383 end;
3385 if gShowFPS then
3386 begin
3387 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3388 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3389 end;
3391 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3392 drawTime(gScreenWidth-72, gScreenHeight-16);
3394 if gGameOn then drawProfilers();
3396 g_Holmes_DrawUI();
3397 end;
3399 procedure g_Game_Quit();
3400 begin
3401 g_Game_StopAllSounds(True);
3402 gMusic.Free();
3403 g_Game_SaveOptions();
3404 g_Game_FreeData();
3405 g_PlayerModel_FreeData();
3406 g_Texture_DeleteAll();
3407 g_Frames_DeleteAll();
3408 g_Menu_Free();
3410 if NetInitDone then g_Net_Free;
3412 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3413 if gMapToDelete <> '' then
3414 g_Game_DeleteTestMap();
3416 gExit := EXIT_QUIT;
3417 PushExitEvent();
3418 end;
3420 procedure g_FatalError(Text: String);
3421 begin
3422 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3423 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3425 gExit := EXIT_SIMPLE;
3426 end;
3428 procedure g_SimpleError(Text: String);
3429 begin
3430 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3431 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3432 end;
3434 procedure g_Game_SetupScreenSize();
3435 const
3436 RES_FACTOR = 4.0 / 3.0;
3437 var
3438 s: Single;
3439 rf: Single;
3440 bw, bh: Word;
3441 begin
3442 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3443 gPlayerScreenSize.X := gScreenWidth-196;
3444 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3445 gPlayerScreenSize.Y := gScreenHeight div 2
3446 else
3447 gPlayerScreenSize.Y := gScreenHeight;
3449 // Ðàçìåð çàäíåãî ïëàíà:
3450 if BackID <> DWORD(-1) then
3451 begin
3452 s := SKY_STRETCH;
3453 if (gScreenWidth*s > gMapInfo.Width) or
3454 (gScreenHeight*s > gMapInfo.Height) then
3455 begin
3456 gBackSize.X := gScreenWidth;
3457 gBackSize.Y := gScreenHeight;
3458 end
3459 else
3460 begin
3461 e_GetTextureSize(BackID, @bw, @bh);
3462 rf := Single(bw) / Single(bh);
3463 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3464 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3465 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3466 if (s < 1.0) then s := 1.0;
3467 gBackSize.X := Round(bw*s);
3468 gBackSize.Y := Round(bh*s);
3469 end;
3470 end;
3471 end;
3473 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3474 begin
3475 g_Window_SetSize(newWidth, newHeight, nowFull);
3476 end;
3478 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3479 begin
3480 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3481 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3482 Exit;
3483 if gPlayer1 = nil then
3484 begin
3485 if g_Game_IsClient then
3486 begin
3487 if NetPlrUID1 > -1 then
3488 begin
3489 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3490 gPlayer1 := g_Player_Get(NetPlrUID1);
3491 end;
3492 Exit;
3493 end;
3495 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3496 Team := gPlayer1Settings.Team;
3498 // Ñîçäàíèå ïåðâîãî èãðîêà:
3499 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3500 gPlayer1Settings.Color,
3501 Team, False));
3502 if gPlayer1 = nil then
3503 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3504 else
3505 begin
3506 gPlayer1.Name := gPlayer1Settings.Name;
3507 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3508 if g_Game_IsServer and g_Game_IsNet then
3509 MH_SEND_PlayerCreate(gPlayer1.UID);
3510 gPlayer1.Respawn(False, True);
3512 if g_Game_IsNet and NetUseMaster then
3513 g_Net_Slist_Update;
3514 end;
3516 Exit;
3517 end;
3518 if gPlayer2 = nil then
3519 begin
3520 if g_Game_IsClient then
3521 begin
3522 if NetPlrUID2 > -1 then
3523 gPlayer2 := g_Player_Get(NetPlrUID2);
3524 Exit;
3525 end;
3527 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3528 Team := gPlayer2Settings.Team;
3530 // Ñîçäàíèå âòîðîãî èãðîêà:
3531 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3532 gPlayer2Settings.Color,
3533 Team, False));
3534 if gPlayer2 = nil then
3535 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3536 else
3537 begin
3538 gPlayer2.Name := gPlayer2Settings.Name;
3539 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3540 if g_Game_IsServer and g_Game_IsNet then
3541 MH_SEND_PlayerCreate(gPlayer2.UID);
3542 gPlayer2.Respawn(False, True);
3544 if g_Game_IsNet and NetUseMaster then
3545 g_Net_Slist_Update;
3546 end;
3548 Exit;
3549 end;
3550 end;
3552 procedure g_Game_RemovePlayer();
3553 var
3554 Pl: TPlayer;
3555 begin
3556 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3557 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3558 Exit;
3559 Pl := gPlayer2;
3560 if Pl <> nil then
3561 begin
3562 if g_Game_IsServer then
3563 begin
3564 Pl.Lives := 0;
3565 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3566 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3567 g_Player_Remove(Pl.UID);
3569 if g_Game_IsNet and NetUseMaster then
3570 g_Net_Slist_Update;
3571 end else
3572 gPlayer2 := nil;
3573 Exit;
3574 end;
3575 Pl := gPlayer1;
3576 if Pl <> nil then
3577 begin
3578 if g_Game_IsServer then
3579 begin
3580 Pl.Lives := 0;
3581 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3582 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3583 g_Player_Remove(Pl.UID);
3585 if g_Game_IsNet and NetUseMaster then
3586 g_Net_Slist_Update;
3587 end else
3588 begin
3589 gPlayer1 := nil;
3590 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3591 end;
3592 Exit;
3593 end;
3594 end;
3596 procedure g_Game_Spectate();
3597 begin
3598 g_Game_RemovePlayer();
3599 if gPlayer1 <> nil then
3600 g_Game_RemovePlayer();
3601 end;
3603 procedure g_Game_SpectateCenterView();
3604 begin
3605 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3606 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3607 end;
3609 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3610 var
3611 i, nPl: Integer;
3612 begin
3613 g_Game_Free();
3615 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3617 g_Game_ClearLoading();
3619 // Íàñòðîéêè èãðû:
3620 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3621 gAimLine := False;
3622 gShowMap := False;
3623 gGameSettings.GameType := GT_SINGLE;
3624 gGameSettings.MaxLives := 0;
3625 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3626 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3627 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3628 gSwitchGameMode := GM_SINGLE;
3630 g_Game_ExecuteEvent('ongamestart');
3632 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3633 g_Game_SetupScreenSize();
3635 // Ñîçäàíèå ïåðâîãî èãðîêà:
3636 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3637 gPlayer1Settings.Color,
3638 gPlayer1Settings.Team, False));
3639 if gPlayer1 = nil then
3640 begin
3641 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3642 Exit;
3643 end;
3645 gPlayer1.Name := gPlayer1Settings.Name;
3646 nPl := 1;
3648 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3649 if TwoPlayers then
3650 begin
3651 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3652 gPlayer2Settings.Color,
3653 gPlayer2Settings.Team, False));
3654 if gPlayer2 = nil then
3655 begin
3656 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3657 Exit;
3658 end;
3660 gPlayer2.Name := gPlayer2Settings.Name;
3661 Inc(nPl);
3662 end;
3664 // Çàãðóçêà è çàïóñê êàðòû:
3665 if not g_Game_StartMap(MAP, True) then
3666 begin
3667 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3668 Exit;
3669 end;
3671 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3672 g_Player_Init();
3674 // Ñîçäàåì áîòîâ:
3675 for i := nPl+1 to nPlayers do
3676 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3677 end;
3679 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3680 TimeLimit, GoalLimit: Word;
3681 MaxLives: Byte;
3682 Options: LongWord; nPlayers: Byte);
3683 var
3684 i, nPl: Integer;
3685 begin
3686 g_Game_Free();
3688 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3690 g_Game_ClearLoading();
3692 // Íàñòðîéêè èãðû:
3693 gGameSettings.GameType := GT_CUSTOM;
3694 gGameSettings.GameMode := GameMode;
3695 gSwitchGameMode := GameMode;
3696 gGameSettings.TimeLimit := TimeLimit;
3697 gGameSettings.GoalLimit := GoalLimit;
3698 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3699 gGameSettings.Options := Options;
3701 gCoopTotalMonstersKilled := 0;
3702 gCoopTotalSecretsFound := 0;
3703 gCoopTotalMonsters := 0;
3704 gCoopTotalSecrets := 0;
3705 gAimLine := False;
3706 gShowMap := False;
3708 g_Game_ExecuteEvent('ongamestart');
3710 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3711 g_Game_SetupScreenSize();
3713 // Ðåæèì íàáëþäàòåëÿ:
3714 if nPlayers = 0 then
3715 begin
3716 gPlayer1 := nil;
3717 gPlayer2 := nil;
3718 end;
3720 nPl := 0;
3721 if nPlayers >= 1 then
3722 begin
3723 // Ñîçäàíèå ïåðâîãî èãðîêà:
3724 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3725 gPlayer1Settings.Color,
3726 gPlayer1Settings.Team, False));
3727 if gPlayer1 = nil then
3728 begin
3729 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3730 Exit;
3731 end;
3733 gPlayer1.Name := gPlayer1Settings.Name;
3734 Inc(nPl);
3735 end;
3737 if nPlayers >= 2 then
3738 begin
3739 // Ñîçäàíèå âòîðîãî èãðîêà:
3740 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3741 gPlayer2Settings.Color,
3742 gPlayer2Settings.Team, False));
3743 if gPlayer2 = nil then
3744 begin
3745 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3746 Exit;
3747 end;
3749 gPlayer2.Name := gPlayer2Settings.Name;
3750 Inc(nPl);
3751 end;
3753 // Çàãðóçêà è çàïóñê êàðòû:
3754 if not g_Game_StartMap(Map, True) then
3755 begin
3756 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3757 Exit;
3758 end;
3760 // Íåò òî÷åê ïîÿâëåíèÿ:
3761 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3762 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3763 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3764 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3765 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3766 begin
3767 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3768 Exit;
3769 end;
3771 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3772 g_Player_Init();
3774 // Ñîçäàåì áîòîâ:
3775 for i := nPl+1 to nPlayers do
3776 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3777 end;
3779 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3780 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3781 Options: LongWord; nPlayers: Byte;
3782 IPAddr: LongWord; Port: Word);
3783 begin
3784 g_Game_Free();
3786 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3788 g_Game_ClearLoading();
3790 // Íàñòðîéêè èãðû:
3791 gGameSettings.GameType := GT_SERVER;
3792 gGameSettings.GameMode := GameMode;
3793 gSwitchGameMode := GameMode;
3794 gGameSettings.TimeLimit := TimeLimit;
3795 gGameSettings.GoalLimit := GoalLimit;
3796 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3797 gGameSettings.Options := Options;
3799 gCoopTotalMonstersKilled := 0;
3800 gCoopTotalSecretsFound := 0;
3801 gCoopTotalMonsters := 0;
3802 gCoopTotalSecrets := 0;
3803 gAimLine := False;
3804 gShowMap := False;
3806 g_Game_ExecuteEvent('ongamestart');
3808 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3809 g_Game_SetupScreenSize();
3811 // Ðåæèì íàáëþäàòåëÿ:
3812 if nPlayers = 0 then
3813 begin
3814 gPlayer1 := nil;
3815 gPlayer2 := nil;
3816 end;
3818 if nPlayers >= 1 then
3819 begin
3820 // Ñîçäàíèå ïåðâîãî èãðîêà:
3821 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3822 gPlayer1Settings.Color,
3823 gPlayer1Settings.Team, False));
3824 if gPlayer1 = nil then
3825 begin
3826 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3827 Exit;
3828 end;
3830 gPlayer1.Name := gPlayer1Settings.Name;
3831 end;
3833 if nPlayers >= 2 then
3834 begin
3835 // Ñîçäàíèå âòîðîãî èãðîêà:
3836 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3837 gPlayer2Settings.Color,
3838 gPlayer2Settings.Team, False));
3839 if gPlayer2 = nil then
3840 begin
3841 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3842 Exit;
3843 end;
3845 gPlayer2.Name := gPlayer2Settings.Name;
3846 end;
3848 // Ñòàðòóåì ñåðâåð
3849 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3850 begin
3851 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3852 Exit;
3853 end;
3855 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3857 // Çàãðóçêà è çàïóñê êàðòû:
3858 if not g_Game_StartMap(Map, True) then
3859 begin
3860 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3861 Exit;
3862 end;
3864 // Íåò òî÷åê ïîÿâëåíèÿ:
3865 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3866 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3867 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3868 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3869 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3870 begin
3871 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3872 Exit;
3873 end;
3875 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3876 g_Player_Init();
3878 NetState := NET_STATE_GAME;
3879 end;
3881 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3882 var
3883 Map: String;
3884 WadName: string;
3885 Ptr: Pointer;
3886 T: Cardinal;
3887 MID: Byte;
3888 State: Byte;
3889 OuterLoop: Boolean;
3890 newResPath: string;
3891 InMsg: TMsg;
3892 begin
3893 g_Game_Free();
3895 State := 0;
3896 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3897 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3899 g_Game_ClearLoading();
3901 // Íàñòðîéêè èãðû:
3902 gGameSettings.GameType := GT_CLIENT;
3904 gCoopTotalMonstersKilled := 0;
3905 gCoopTotalSecretsFound := 0;
3906 gCoopTotalMonsters := 0;
3907 gCoopTotalSecrets := 0;
3908 gAimLine := False;
3909 gShowMap := False;
3911 g_Game_ExecuteEvent('ongamestart');
3913 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3914 g_Game_SetupScreenSize();
3916 NetState := NET_STATE_AUTH;
3918 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3919 // Ñòàðòóåì êëèåíò
3920 if not g_Net_Connect(Addr, Port) then
3921 begin
3922 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3923 NetState := NET_STATE_NONE;
3924 Exit;
3925 end;
3927 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3928 MC_SEND_Info(PW);
3929 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3931 OuterLoop := True;
3932 while OuterLoop do
3933 begin
3934 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3935 begin
3936 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3937 begin
3938 Ptr := NetEvent.packet^.data;
3939 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
3940 continue;
3942 MID := InMsg.ReadByte();
3944 if (MID = NET_MSG_INFO) and (State = 0) then
3945 begin
3946 NetMyID := InMsg.ReadByte();
3947 NetPlrUID1 := InMsg.ReadWord();
3949 WadName := InMsg.ReadString();
3950 Map := InMsg.ReadString();
3952 gWADHash := InMsg.ReadMD5();
3954 gGameSettings.GameMode := InMsg.ReadByte();
3955 gSwitchGameMode := gGameSettings.GameMode;
3956 gGameSettings.GoalLimit := InMsg.ReadWord();
3957 gGameSettings.TimeLimit := InMsg.ReadWord();
3958 gGameSettings.MaxLives := InMsg.ReadByte();
3959 gGameSettings.Options := InMsg.ReadLongWord();
3960 T := InMsg.ReadLongWord();
3962 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3963 if newResPath = '' then
3964 begin
3965 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3966 newResPath := g_Res_DownloadWAD(WadName);
3967 if newResPath = '' then
3968 begin
3969 g_FatalError(_lc[I_NET_ERR_HASH]);
3970 enet_packet_destroy(NetEvent.packet);
3971 NetState := NET_STATE_NONE;
3972 Exit;
3973 end;
3974 end;
3975 newResPath := ExtractRelativePath(MapsDir, newResPath);
3977 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3978 gPlayer1Settings.Color,
3979 gPlayer1Settings.Team, False));
3981 if gPlayer1 = nil then
3982 begin
3983 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3985 enet_packet_destroy(NetEvent.packet);
3986 NetState := NET_STATE_NONE;
3987 Exit;
3988 end;
3990 gPlayer1.Name := gPlayer1Settings.Name;
3991 gPlayer1.UID := NetPlrUID1;
3992 gPlayer1.Reset(True);
3994 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3995 begin
3996 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3998 enet_packet_destroy(NetEvent.packet);
3999 NetState := NET_STATE_NONE;
4000 Exit;
4001 end;
4003 gTime := T;
4005 State := 1;
4006 OuterLoop := False;
4007 enet_packet_destroy(NetEvent.packet);
4008 break;
4009 end
4010 else
4011 enet_packet_destroy(NetEvent.packet);
4012 end
4013 else
4014 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4015 begin
4016 State := 0;
4017 if (NetEvent.data <= NET_DISC_MAX) then
4018 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4019 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4020 OuterLoop := False;
4021 Break;
4022 end;
4023 end;
4025 ProcessLoading(true);
4027 e_PollInput();
4029 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4030 begin
4031 State := 0;
4032 break;
4033 end;
4034 end;
4036 if State <> 1 then
4037 begin
4038 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4039 NetState := NET_STATE_NONE;
4040 Exit;
4041 end;
4043 gLMSRespawn := LMS_RESPAWN_NONE;
4044 gLMSRespawnTime := 0;
4046 g_Player_Init();
4047 NetState := NET_STATE_GAME;
4048 MC_SEND_FullStateRequest;
4049 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
4050 end;
4052 procedure g_Game_SaveOptions();
4053 begin
4054 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4055 end;
4057 procedure g_Game_ChangeMap(MapPath: String);
4058 var
4059 Force: Boolean;
4060 begin
4061 g_Game_ClearLoading();
4063 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4064 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4065 if gExitByTrigger then
4066 begin
4067 Force := False;
4068 gExitByTrigger := False;
4069 end;
4070 if not g_Game_StartMap(MapPath, Force) then
4071 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4072 end;
4074 procedure g_Game_Restart();
4075 var
4076 Map: string;
4077 begin
4078 if g_Game_IsClient then
4079 Exit;
4080 map := g_ExtractFileName(gMapInfo.Map);
4082 MessageTime := 0;
4083 gGameOn := False;
4084 g_Game_ClearLoading();
4085 g_Game_StartMap(Map, True, gCurrentMapFileName);
4086 end;
4088 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4089 var
4090 NewWAD, ResName: String;
4091 I: Integer;
4092 begin
4093 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4094 g_Player_RemoveAllCorpses();
4096 if (not g_Game_IsClient) and
4097 (gSwitchGameMode <> gGameSettings.GameMode) and
4098 (gGameSettings.GameMode <> GM_SINGLE) then
4099 begin
4100 if gSwitchGameMode = GM_CTF then
4101 gGameSettings.MaxLives := 0;
4102 gGameSettings.GameMode := gSwitchGameMode;
4103 Force := True;
4104 end else
4105 gSwitchGameMode := gGameSettings.GameMode;
4107 g_Player_ResetTeams();
4109 if isWadPath(Map) then
4110 begin
4111 NewWAD := g_ExtractWadName(Map);
4112 ResName := g_ExtractFileName(Map);
4113 if g_Game_IsServer then
4114 begin
4115 gWADHash := MD5File(MapsDir + NewWAD);
4116 g_Game_LoadWAD(NewWAD);
4117 end else
4118 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4119 g_Game_ClientWAD(NewWAD, gWADHash);
4120 end else
4121 ResName := Map;
4123 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4124 if Result then
4125 begin
4126 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4128 gState := STATE_NONE;
4129 g_ActiveWindow := nil;
4130 gGameOn := True;
4132 DisableCheats();
4133 ResetTimer();
4135 if gGameSettings.GameMode = GM_CTF then
4136 begin
4137 g_Map_ResetFlag(FLAG_RED);
4138 g_Map_ResetFlag(FLAG_BLUE);
4139 // CTF, à ôëàãîâ íåò:
4140 if not g_Map_HaveFlagPoints() then
4141 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4142 end;
4143 end
4144 else
4145 begin
4146 gState := STATE_MENU;
4147 gGameOn := False;
4148 end;
4150 gExit := 0;
4151 gPause := False;
4152 gTime := 0;
4153 NetTimeToUpdate := 1;
4154 NetTimeToReliable := 0;
4155 NetTimeToMaster := NetMasterRate;
4156 gLMSRespawn := LMS_RESPAWN_NONE;
4157 gLMSRespawnTime := 0;
4158 gMissionFailed := False;
4159 gNextMap := '';
4161 gCoopMonstersKilled := 0;
4162 gCoopSecretsFound := 0;
4164 gVoteInProgress := False;
4165 gVotePassed := False;
4166 gVoteCount := 0;
4167 gVoted := False;
4169 gStatsOff := False;
4171 if not gGameOn then Exit;
4173 g_Game_SpectateCenterView();
4175 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4176 begin
4177 gLMSRespawn := LMS_RESPAWN_WARMUP;
4178 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4179 gLMSSoftSpawn := True;
4180 if NetMode = NET_SERVER then
4181 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4182 else
4183 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4184 end;
4186 if NetMode = NET_SERVER then
4187 begin
4188 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4190 // Ìàñòåðñåðâåð
4191 if NetUseMaster then
4192 begin
4193 if (NetMHost = nil) or (NetMPeer = nil) then
4194 if not g_Net_Slist_Connect then
4195 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4197 g_Net_Slist_Update;
4198 end;
4200 if NetClients <> nil then
4201 for I := 0 to High(NetClients) do
4202 if NetClients[I].Used then
4203 begin
4204 NetClients[I].Voted := False;
4205 if NetClients[I].RequestedFullUpdate then
4206 begin
4207 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4208 NetClients[I].RequestedFullUpdate := False;
4209 end;
4210 end;
4212 g_Net_UnbanNonPermHosts();
4213 end;
4215 if gLastMap then
4216 begin
4217 gCoopTotalMonstersKilled := 0;
4218 gCoopTotalSecretsFound := 0;
4219 gCoopTotalMonsters := 0;
4220 gCoopTotalSecrets := 0;
4221 gLastMap := False;
4222 end;
4224 g_Game_ExecuteEvent('onmapstart');
4225 end;
4227 procedure SetFirstLevel();
4228 begin
4229 gNextMap := '';
4231 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4232 if MapList = nil then
4233 Exit;
4235 SortSArray(MapList);
4236 gNextMap := MapList[Low(MapList)];
4238 MapList := nil;
4239 end;
4241 procedure g_Game_ExitLevel(Map: Char16);
4242 begin
4243 gNextMap := Map;
4245 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4246 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4247 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4248 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4250 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4251 if gGameSettings.GameType = GT_SINGLE then
4252 gExit := EXIT_ENDLEVELSINGLE
4253 else // Âûøëè â âûõîä â Ñâîåé èãðå
4254 begin
4255 gExit := EXIT_ENDLEVELCUSTOM;
4256 if gGameSettings.GameMode = GM_COOP then
4257 g_Player_RememberAll;
4259 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4260 begin
4261 gLastMap := True;
4262 if gGameSettings.GameMode = GM_COOP then
4263 gStatsOff := True;
4265 gStatsPressed := True;
4266 gNextMap := 'MAP01';
4268 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4269 g_Game_NextLevel;
4271 if g_Game_IsNet then
4272 begin
4273 MH_SEND_GameStats();
4274 MH_SEND_CoopStats();
4275 end;
4276 end;
4277 end;
4278 end;
4280 procedure g_Game_RestartLevel();
4281 var
4282 Map: string;
4283 begin
4284 if gGameSettings.GameMode = GM_SINGLE then
4285 begin
4286 g_Game_Restart();
4287 Exit;
4288 end;
4289 gExit := EXIT_ENDLEVELCUSTOM;
4290 Map := g_ExtractFileName(gMapInfo.Map);
4291 gNextMap := Map;
4292 end;
4294 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4295 var
4296 gWAD: String;
4297 begin
4298 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4299 Exit;
4300 if not g_Game_IsClient then
4301 Exit;
4302 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4303 if gWAD = '' then
4304 begin
4305 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4306 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4307 if gWAD = '' then
4308 begin
4309 g_Game_Free();
4310 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4311 Exit;
4312 end;
4313 end;
4314 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4315 g_Game_LoadWAD(NewWAD);
4316 end;
4318 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4319 var
4320 i, n, nb, nr: Integer;
4322 function monRespawn (mon: TMonster): Boolean;
4323 begin
4324 result := false; // don't stop
4325 if not mon.FNoRespawn then mon.Respawn();
4326 end;
4328 begin
4329 if not g_Game_IsServer then Exit;
4330 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4331 gLMSRespawn := LMS_RESPAWN_NONE;
4332 gLMSRespawnTime := 0;
4333 MessageTime := 0;
4335 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4336 begin
4337 gMissionFailed := True;
4338 g_Game_RestartLevel;
4339 Exit;
4340 end;
4342 n := 0; nb := 0; nr := 0;
4343 for i := Low(gPlayers) to High(gPlayers) do
4344 if (gPlayers[i] <> nil) and
4345 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4346 (gPlayers[i] is TBot)) then
4347 begin
4348 Inc(n);
4349 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4350 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4351 end;
4353 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4354 begin
4355 // wait a second until the fuckers finally decide to join
4356 gLMSRespawn := LMS_RESPAWN_WARMUP;
4357 gLMSRespawnTime := gTime + 1000;
4358 gLMSSoftSpawn := NoMapRestart;
4359 Exit;
4360 end;
4362 g_Player_RemoveAllCorpses;
4363 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4364 if g_Game_IsNet then
4365 MH_SEND_GameEvent(NET_EV_LMS_START);
4367 for i := Low(gPlayers) to High(gPlayers) do
4368 begin
4369 if gPlayers[i] = nil then continue;
4370 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4371 // don't touch normal spectators
4372 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4373 begin
4374 gPlayers[i].FNoRespawn := True;
4375 gPlayers[i].Lives := 0;
4376 if g_Game_IsNet then
4377 MH_SEND_PlayerStats(gPlayers[I].UID);
4378 continue;
4379 end;
4380 gPlayers[i].FNoRespawn := False;
4381 gPlayers[i].Lives := gGameSettings.MaxLives;
4382 gPlayers[i].Respawn(False, True);
4383 if gGameSettings.GameMode = GM_COOP then
4384 begin
4385 gPlayers[i].Frags := 0;
4386 gPlayers[i].RecallState;
4387 end;
4388 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4389 gPlayer1 := g_Player_Get(gLMSPID1);
4390 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4391 gPlayer2 := g_Player_Get(gLMSPID2);
4392 end;
4394 g_Items_RestartRound();
4397 g_Mons_ForEach(monRespawn);
4399 gLMSSoftSpawn := False;
4400 end;
4402 function g_Game_GetFirstMap(WAD: String): String;
4403 begin
4404 Result := '';
4406 MapList := g_Map_GetMapsList(WAD);
4407 if MapList = nil then
4408 Exit;
4410 SortSArray(MapList);
4411 Result := MapList[Low(MapList)];
4413 if not g_Map_Exist(WAD + ':\' + Result) then
4414 Result := '';
4416 MapList := nil;
4417 end;
4419 function g_Game_GetNextMap(): String;
4420 var
4421 I: Integer;
4422 Map: string;
4423 begin
4424 Result := '';
4426 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4427 if MapList = nil then
4428 Exit;
4430 Map := g_ExtractFileName(gMapInfo.Map);
4432 SortSArray(MapList);
4433 MapIndex := -255;
4434 for I := Low(MapList) to High(MapList) do
4435 if Map = MapList[I] then
4436 begin
4437 MapIndex := I;
4438 Break;
4439 end;
4441 if MapIndex <> -255 then
4442 begin
4443 if MapIndex = High(MapList) then
4444 Result := MapList[Low(MapList)]
4445 else
4446 Result := MapList[MapIndex + 1];
4448 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4449 end;
4451 MapList := nil;
4452 end;
4454 procedure g_Game_NextLevel();
4455 begin
4456 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4457 gExit := EXIT_ENDLEVELCUSTOM
4458 else
4459 begin
4460 gExit := EXIT_ENDLEVELSINGLE;
4461 Exit;
4462 end;
4464 if gNextMap <> '' then Exit;
4465 gNextMap := g_Game_GetNextMap();
4466 end;
4468 function g_Game_IsTestMap(): Boolean;
4469 begin
4470 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4471 end;
4473 procedure g_Game_DeleteTestMap();
4474 var
4475 a: Integer;
4476 MapName: Char16;
4477 WadName: string;
4479 WAD: TWADFile;
4480 MapList: SArray;
4481 time: Integer;
4483 begin
4484 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4485 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4486 if a = 0 then
4487 Exit;
4489 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4490 WadName := Copy(gMapToDelete, 1, a + 3);
4491 Delete(gMapToDelete, 1, a + 5);
4492 gMapToDelete := UpperCase(gMapToDelete);
4493 MapName := '';
4494 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4497 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4498 if MapName <> TEST_MAP_NAME then
4499 Exit;
4501 if not gTempDelete then
4502 begin
4503 time := g_GetFileTime(WadName);
4504 WAD := TWADFile.Create();
4506 // ×èòàåì Wad-ôàéë:
4507 if not WAD.ReadFile(WadName) then
4508 begin // Íåò òàêîãî WAD-ôàéëà
4509 WAD.Free();
4510 Exit;
4511 end;
4513 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4514 WAD.CreateImage();
4515 MapList := WAD.GetResourcesList('');
4517 if MapList <> nil then
4518 for a := 0 to High(MapList) do
4519 if MapList[a] = MapName then
4520 begin
4521 // Óäàëÿåì è ñîõðàíÿåì:
4522 WAD.RemoveResource('', MapName);
4523 WAD.SaveTo(WadName);
4524 Break;
4525 end;
4527 WAD.Free();
4528 g_SetFileTime(WadName, time);
4529 end else
4531 if gTempDelete then DeleteFile(WadName);
4532 end;
4534 procedure GameCVars(P: SArray);
4535 var
4536 a, b: Integer;
4537 stat: TPlayerStatArray;
4538 cmd, s: string;
4539 config: TConfig;
4540 begin
4541 stat := nil;
4542 cmd := LowerCase(P[0]);
4543 if cmd = 'r_showfps' then
4544 begin
4545 if (Length(P) > 1) and
4546 ((P[1] = '1') or (P[1] = '0')) then
4547 gShowFPS := (P[1][1] = '1');
4549 if gShowFPS then
4550 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4551 else
4552 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4553 end
4554 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4555 begin
4556 with gGameSettings do
4557 begin
4558 if (Length(P) > 1) and
4559 ((P[1] = '1') or (P[1] = '0')) then
4560 begin
4561 if (P[1][1] = '1') then
4562 Options := Options or GAME_OPTION_TEAMDAMAGE
4563 else
4564 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4565 end;
4567 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4568 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4569 else
4570 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4572 if g_Game_IsNet then MH_SEND_GameSettings;
4573 end;
4574 end
4575 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4576 begin
4577 with gGameSettings do
4578 begin
4579 if (Length(P) > 1) and
4580 ((P[1] = '1') or (P[1] = '0')) then
4581 begin
4582 if (P[1][1] = '1') then
4583 Options := Options or GAME_OPTION_WEAPONSTAY
4584 else
4585 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4586 end;
4588 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4589 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4590 else
4591 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4593 if g_Game_IsNet then MH_SEND_GameSettings;
4594 end;
4595 end
4596 else if cmd = 'g_gamemode' then
4597 begin
4598 a := g_Game_TextToMode(P[1]);
4599 if a = GM_SINGLE then a := GM_COOP;
4600 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4601 begin
4602 gSwitchGameMode := a;
4603 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4604 (gState = STATE_INTERSINGLE) then
4605 gSwitchGameMode := GM_SINGLE;
4606 if not gGameOn then
4607 gGameSettings.GameMode := gSwitchGameMode;
4608 end;
4609 if gSwitchGameMode = gGameSettings.GameMode then
4610 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4611 [g_Game_ModeToText(gGameSettings.GameMode)]))
4612 else
4613 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4614 [g_Game_ModeToText(gGameSettings.GameMode),
4615 g_Game_ModeToText(gSwitchGameMode)]));
4616 end
4617 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4618 begin
4619 with gGameSettings do
4620 begin
4621 if (Length(P) > 1) and
4622 ((P[1] = '1') or (P[1] = '0')) then
4623 begin
4624 if (P[1][1] = '1') then
4625 Options := Options or GAME_OPTION_ALLOWEXIT
4626 else
4627 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4628 end;
4630 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4631 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4632 else
4633 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4634 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4636 if g_Game_IsNet then MH_SEND_GameSettings;
4637 end;
4638 end
4639 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4640 begin
4641 with gGameSettings do
4642 begin
4643 if (Length(P) > 1) and
4644 ((P[1] = '1') or (P[1] = '0')) then
4645 begin
4646 if (P[1][1] = '1') then
4647 Options := Options or GAME_OPTION_MONSTERS
4648 else
4649 Options := Options and (not GAME_OPTION_MONSTERS);
4650 end;
4652 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4653 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4654 else
4655 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4656 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4658 if g_Game_IsNet then MH_SEND_GameSettings;
4659 end;
4660 end
4661 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4662 begin
4663 with gGameSettings do
4664 begin
4665 if (Length(P) > 1) and
4666 ((P[1] = '1') or (P[1] = '0')) then
4667 begin
4668 if (P[1][1] = '1') then
4669 Options := Options or GAME_OPTION_BOTVSPLAYER
4670 else
4671 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4672 end;
4674 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4675 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4676 else
4677 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4679 if g_Game_IsNet then MH_SEND_GameSettings;
4680 end;
4681 end
4682 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4683 begin
4684 with gGameSettings do
4685 begin
4686 if (Length(P) > 1) and
4687 ((P[1] = '1') or (P[1] = '0')) then
4688 begin
4689 if (P[1][1] = '1') then
4690 Options := Options or GAME_OPTION_BOTVSMONSTER
4691 else
4692 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4693 end;
4695 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4696 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4697 else
4698 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4700 if g_Game_IsNet then MH_SEND_GameSettings;
4701 end;
4702 end
4703 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4704 begin
4705 if Length(P) > 1 then
4706 begin
4707 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4708 gGameSettings.WarmupTime := 30
4709 else
4710 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4711 end;
4713 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4714 [gGameSettings.WarmupTime]));
4715 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4716 end
4717 else if cmd = 'net_interp' then
4718 begin
4719 if (Length(P) > 1) then
4720 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4722 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4723 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4724 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4725 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4726 config.Free();
4727 end
4728 else if cmd = 'net_forceplayerupdate' then
4729 begin
4730 if (Length(P) > 1) and
4731 ((P[1] = '1') or (P[1] = '0')) then
4732 NetForcePlayerUpdate := (P[1][1] = '1');
4734 if NetForcePlayerUpdate then
4735 g_Console_Add('net_forceplayerupdate = 1')
4736 else
4737 g_Console_Add('net_forceplayerupdate = 0');
4738 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4739 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4740 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4741 config.Free();
4742 end
4743 else if cmd = 'net_predictself' then
4744 begin
4745 if (Length(P) > 1) and
4746 ((P[1] = '1') or (P[1] = '0')) then
4747 NetPredictSelf := (P[1][1] = '1');
4749 if NetPredictSelf then
4750 g_Console_Add('net_predictself = 1')
4751 else
4752 g_Console_Add('net_predictself = 0');
4753 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4754 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4755 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4756 config.Free();
4757 end
4758 else if cmd = 'sv_name' then
4759 begin
4760 if (Length(P) > 1) and (Length(P[1]) > 0) then
4761 begin
4762 NetServerName := P[1];
4763 if Length(NetServerName) > 64 then
4764 SetLength(NetServerName, 64);
4765 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4766 g_Net_Slist_Update;
4767 end;
4769 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4770 end
4771 else if cmd = 'sv_passwd' then
4772 begin
4773 if (Length(P) > 1) and (Length(P[1]) > 0) then
4774 begin
4775 NetPassword := P[1];
4776 if Length(NetPassword) > 24 then
4777 SetLength(NetPassword, 24);
4778 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4779 g_Net_Slist_Update;
4780 end;
4782 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4783 end
4784 else if cmd = 'sv_maxplrs' then
4785 begin
4786 if (Length(P) > 1) then
4787 begin
4788 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4789 if g_Game_IsServer and g_Game_IsNet then
4790 begin
4791 b := 0;
4792 for a := 0 to High(NetClients) do
4793 if NetClients[a].Used then
4794 begin
4795 Inc(b);
4796 if b > NetMaxClients then
4797 begin
4798 s := g_Player_Get(NetClients[a].Player).Name;
4799 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4800 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4801 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4802 end;
4803 end;
4804 if NetUseMaster then
4805 g_Net_Slist_Update;
4806 end;
4807 end;
4809 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4810 end
4811 else if cmd = 'sv_public' then
4812 begin
4813 if (Length(P) > 1) then
4814 begin
4815 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4816 if g_Game_IsServer and g_Game_IsNet then
4817 if NetUseMaster then
4818 begin
4819 if NetMPeer = nil then
4820 if not g_Net_Slist_Connect() then
4821 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4822 g_Net_Slist_Update();
4823 end
4824 else
4825 if NetMPeer <> nil then
4826 g_Net_Slist_Disconnect();
4827 end;
4829 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4830 end
4831 else if cmd = 'sv_intertime' then
4832 begin
4833 if (Length(P) > 1) then
4834 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4836 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4837 end
4838 else if cmd = 'p1_name' then
4839 begin
4840 if (Length(P) > 1) and gGameOn then
4841 begin
4842 if g_Game_IsClient then
4843 begin
4844 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4845 MC_SEND_PlayerSettings;
4846 end
4847 else
4848 if gPlayer1 <> nil then
4849 begin
4850 gPlayer1.Name := b_Text_Unformat(P[1]);
4851 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4852 end
4853 else
4854 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4855 end;
4856 end
4857 else if cmd = 'p2_name' then
4858 begin
4859 if (Length(P) > 1) and gGameOn then
4860 begin
4861 if g_Game_IsClient then
4862 begin
4863 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4864 MC_SEND_PlayerSettings;
4865 end
4866 else
4867 if gPlayer2 <> nil then
4868 begin
4869 gPlayer2.Name := b_Text_Unformat(P[1]);
4870 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4871 end
4872 else
4873 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4874 end;
4875 end
4876 else if cmd = 'p1_color' then
4877 begin
4878 if Length(P) > 3 then
4879 if g_Game_IsClient then
4880 begin
4881 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4882 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4883 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4884 MC_SEND_PlayerSettings;
4885 end
4886 else
4887 if gPlayer1 <> nil then
4888 begin
4889 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4890 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4891 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4892 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4893 end
4894 else
4895 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4896 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4897 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4898 end
4899 else if (cmd = 'p2_color') and not g_Game_IsNet then
4900 begin
4901 if Length(P) > 3 then
4902 if g_Game_IsClient then
4903 begin
4904 gPlayer2Settings.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 gPlayer2 <> nil then
4911 begin
4912 gPlayer2.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(gPlayer2.UID);
4916 end
4917 else
4918 gPlayer2Settings.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 gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4923 begin
4924 if cmd = 'r_showtime' then
4925 begin
4926 if (Length(P) > 1) and
4927 ((P[1] = '1') or (P[1] = '0')) then
4928 gShowTime := (P[1][1] = '1');
4930 if gShowTime then
4931 g_Console_Add(_lc[I_MSG_TIME_ON])
4932 else
4933 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4934 end
4935 else if cmd = 'r_showscore' then
4936 begin
4937 if (Length(P) > 1) and
4938 ((P[1] = '1') or (P[1] = '0')) then
4939 gShowGoals := (P[1][1] = '1');
4941 if gShowGoals then
4942 g_Console_Add(_lc[I_MSG_SCORE_ON])
4943 else
4944 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4945 end
4946 else if cmd = 'r_showstat' then
4947 begin
4948 if (Length(P) > 1) and
4949 ((P[1] = '1') or (P[1] = '0')) then
4950 gShowStat := (P[1][1] = '1');
4952 if gShowStat then
4953 g_Console_Add(_lc[I_MSG_STATS_ON])
4954 else
4955 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4956 end
4957 else if cmd = 'r_showkillmsg' then
4958 begin
4959 if (Length(P) > 1) and
4960 ((P[1] = '1') or (P[1] = '0')) then
4961 gShowKillMsg := (P[1][1] = '1');
4963 if gShowKillMsg then
4964 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4965 else
4966 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4967 end
4968 else if cmd = 'r_showlives' then
4969 begin
4970 if (Length(P) > 1) and
4971 ((P[1] = '1') or (P[1] = '0')) then
4972 gShowLives := (P[1][1] = '1');
4974 if gShowLives then
4975 g_Console_Add(_lc[I_MSG_LIVES_ON])
4976 else
4977 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4978 end
4979 else if cmd = 'r_showspect' then
4980 begin
4981 if (Length(P) > 1) and
4982 ((P[1] = '1') or (P[1] = '0')) then
4983 gSpectHUD := (P[1][1] = '1');
4985 if gSpectHUD then
4986 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4987 else
4988 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4989 end
4990 else if cmd = 'r_showping' then
4991 begin
4992 if (Length(P) > 1) and
4993 ((P[1] = '1') or (P[1] = '0')) then
4994 gShowPing := (P[1][1] = '1');
4996 if gShowPing then
4997 g_Console_Add(_lc[I_MSG_PING_ON])
4998 else
4999 g_Console_Add(_lc[I_MSG_PING_OFF]);
5000 end
5001 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5002 begin
5003 if Length(P) > 1 then
5004 begin
5005 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5006 gGameSettings.GoalLimit := 0
5007 else
5008 begin
5009 b := 0;
5011 if gGameSettings.GameMode = GM_DM then
5012 begin // DM
5013 stat := g_Player_GetStats();
5014 if stat <> nil then
5015 for a := 0 to High(stat) do
5016 if stat[a].Frags > b then
5017 b := stat[a].Frags;
5018 end
5019 else // TDM/CTF
5020 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5022 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5023 end;
5025 if g_Game_IsNet then MH_SEND_GameSettings;
5026 end;
5028 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5029 end
5030 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5031 begin
5032 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5033 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5035 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5036 [gGameSettings.TimeLimit div 3600,
5037 (gGameSettings.TimeLimit div 60) mod 60,
5038 gGameSettings.TimeLimit mod 60]));
5039 if g_Game_IsNet then MH_SEND_GameSettings;
5040 end
5041 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5042 begin
5043 if Length(P) > 1 then
5044 begin
5045 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5046 gGameSettings.MaxLives := 0
5047 else
5048 begin
5049 b := 0;
5050 stat := g_Player_GetStats();
5051 if stat <> nil then
5052 for a := 0 to High(stat) do
5053 if stat[a].Lives > b then
5054 b := stat[a].Lives;
5055 gGameSettings.MaxLives :=
5056 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5057 end;
5058 end;
5060 g_Console_Add(Format(_lc[I_MSG_LIVES],
5061 [gGameSettings.MaxLives]));
5062 if g_Game_IsNet then MH_SEND_GameSettings;
5063 end;
5064 end;
5065 end;
5068 procedure DebugCommands(P: SArray);
5069 var
5070 a, b: Integer;
5071 cmd: string;
5072 //pt: TDFPoint;
5073 mon: TMonster;
5074 begin
5075 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5076 if gDebugMode then
5077 begin
5078 cmd := LowerCase(P[0]);
5079 if cmd = 'd_window' then
5080 begin
5081 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5082 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5083 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5084 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5085 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5086 end
5087 else if cmd = 'd_sounds' then
5088 begin
5089 if (Length(P) > 1) and
5090 ((P[1] = '1') or (P[1] = '0')) then
5091 g_Debug_Sounds := (P[1][1] = '1');
5093 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5094 end
5095 else if cmd = 'd_frames' then
5096 begin
5097 if (Length(P) > 1) and
5098 ((P[1] = '1') or (P[1] = '0')) then
5099 g_Debug_Frames := (P[1][1] = '1');
5101 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5102 end
5103 else if cmd = 'd_winmsg' then
5104 begin
5105 if (Length(P) > 1) and
5106 ((P[1] = '1') or (P[1] = '0')) then
5107 g_Debug_WinMsgs := (P[1][1] = '1');
5109 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5110 end
5111 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5112 begin
5113 if (Length(P) > 1) and
5114 ((P[1] = '1') or (P[1] = '0')) then
5115 g_Debug_MonsterOff := (P[1][1] = '1');
5117 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5118 end
5119 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5120 begin
5121 if Length(P) > 1 then
5122 case P[1][1] of
5123 '0': g_debug_BotAIOff := 0;
5124 '1': g_debug_BotAIOff := 1;
5125 '2': g_debug_BotAIOff := 2;
5126 '3': g_debug_BotAIOff := 3;
5127 end;
5129 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5130 end
5131 else if cmd = 'd_monster' then
5132 begin
5133 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5134 if Length(P) < 2 then
5135 begin
5136 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5137 g_Console_Add('ID | Name');
5138 for b := MONSTER_DEMON to MONSTER_MAN do
5139 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5140 end else
5141 begin
5142 a := StrToIntDef(P[1], 0);
5143 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5144 a := g_Mons_TypeIdByName(P[1]);
5146 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5147 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5148 else
5149 begin
5150 with gPlayer1.Obj do
5151 begin
5152 mon := g_Monsters_Create(a,
5153 X + Rect.X + (Rect.Width div 2),
5154 Y + Rect.Y + Rect.Height,
5155 gPlayer1.Direction, True);
5156 end;
5157 if (Length(P) > 2) and (mon <> nil) then
5158 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5159 end;
5160 end;
5161 end
5162 else if (cmd = 'd_health') then
5163 begin
5164 if (Length(P) > 1) and
5165 ((P[1] = '1') or (P[1] = '0')) then
5166 g_debug_HealthBar := (P[1][1] = '1');
5168 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5169 end
5170 else if (cmd = 'd_player') then
5171 begin
5172 if (Length(P) > 1) and
5173 ((P[1] = '1') or (P[1] = '0')) then
5174 g_debug_Player := (P[1][1] = '1');
5176 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5177 end
5178 else if (cmd = 'd_joy') then
5179 begin
5180 for a := 1 to 8 do
5181 g_Console_Add(e_JoystickStateToString(a));
5182 end;
5183 end
5184 else
5185 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5186 end;
5189 procedure GameCheats(P: SArray);
5190 var
5191 cmd: string;
5192 f, a: Integer;
5193 plr: TPlayer;
5194 begin
5195 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5196 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5197 begin
5198 g_Console_Add('not available');
5199 exit;
5200 end;
5201 plr := gPlayer1;
5202 if plr = nil then
5203 begin
5204 g_Console_Add('where is the player?!');
5205 exit;
5206 end;
5207 cmd := LowerCase(P[0]);
5208 // god
5209 if cmd = 'god' then
5210 begin
5211 plr.GodMode := not plr.GodMode;
5212 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5213 exit;
5214 end;
5215 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5216 if cmd = 'give' then
5217 begin
5218 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5219 for f := 1 to High(P) do
5220 begin
5221 cmd := LowerCase(P[f]);
5222 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5223 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5224 if cmd = 'exit' then
5225 begin
5226 if gTriggers <> nil then
5227 begin
5228 for a := 0 to High(gTriggers) do
5229 begin
5230 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5231 begin
5232 g_Console_Add('player left the map');
5233 gExitByTrigger := True;
5234 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5235 g_Game_ExitLevel(gTriggers[a].trigData.trigMap);
5236 break;
5237 end;
5238 end;
5239 end;
5240 continue;
5241 end;
5243 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5244 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5245 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5246 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5247 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5249 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5250 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5252 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5253 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;
5255 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5256 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5258 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5259 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5261 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5262 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5264 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5265 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5266 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5268 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5269 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5270 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5271 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;
5272 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5273 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5275 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;
5276 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;
5277 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;
5278 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;
5279 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;
5280 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;
5282 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5283 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;
5285 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;
5286 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;
5288 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5290 if cmd = 'ammo' then
5291 begin
5292 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5293 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5294 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5295 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5296 plr.GiveItem(ITEM_AMMO_FUELCAN);
5297 g_Console_Add('player got some ammo');
5298 continue;
5299 end;
5301 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5302 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5304 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5305 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5307 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5308 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5310 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5311 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5313 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5315 if cmd = 'weapons' then
5316 begin
5317 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5318 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5319 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5320 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5321 plr.GiveItem(ITEM_WEAPON_PLASMA);
5322 plr.GiveItem(ITEM_WEAPON_BFG);
5323 g_Console_Add('player got weapons');
5324 continue;
5325 end;
5327 if cmd = 'keys' then
5328 begin
5329 plr.GiveItem(ITEM_KEY_RED);
5330 plr.GiveItem(ITEM_KEY_GREEN);
5331 plr.GiveItem(ITEM_KEY_BLUE);
5332 g_Console_Add('player got all keys');
5333 continue;
5334 end;
5336 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5337 end;
5338 exit;
5339 end;
5340 // open
5341 if cmd = 'open' then
5342 begin
5343 g_Console_Add('player activated sesame');
5344 g_Triggers_OpenAll();
5345 exit;
5346 end;
5347 // fly
5348 if cmd = 'fly' then
5349 begin
5350 gFly := not gFly;
5351 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5352 exit;
5353 end;
5354 // noclip
5355 if cmd = 'noclip' then
5356 begin
5357 plr.SwitchNoClip;
5358 g_Console_Add('wall hardeness adjusted');
5359 exit;
5360 end;
5361 // notarget
5362 if cmd = 'notarget' then
5363 begin
5364 plr.NoTarget := not plr.NoTarget;
5365 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5366 exit;
5367 end;
5368 // noreload
5369 if cmd = 'noreload' then
5370 begin
5371 plr.NoReload := not plr.NoReload;
5372 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5373 exit;
5374 end;
5375 // speedy
5376 if cmd = 'speedy' then
5377 begin
5378 MAX_RUNVEL := 32-MAX_RUNVEL;
5379 g_Console_Add('speed adjusted');
5380 exit;
5381 end;
5382 // jumpy
5383 if cmd = 'jumpy' then
5384 begin
5385 VEL_JUMP := 30-VEL_JUMP;
5386 g_Console_Add('jump height adjusted');
5387 exit;
5388 end;
5389 // automap
5390 if cmd = 'automap' then
5391 begin
5392 gShowMap := not gShowMap;
5393 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5394 exit;
5395 end;
5396 // aimline
5397 if cmd = 'aimline' then
5398 begin
5399 gAimLine := not gAimLine;
5400 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5401 exit;
5402 end;
5403 end;
5405 procedure GameCommands(P: SArray);
5406 var
5407 a, b: Integer;
5408 s, pw: String;
5409 chstr: string;
5410 cmd: string;
5411 pl: pTNetClient = nil;
5412 plr: TPlayer;
5413 prt: Word;
5414 nm: Boolean;
5415 listen: LongWord;
5416 begin
5417 // Îáùèå êîìàíäû:
5418 cmd := LowerCase(P[0]);
5419 chstr := '';
5420 if (cmd = 'quit') or
5421 (cmd = 'exit') then
5422 begin
5423 g_Game_Free();
5424 g_Game_Quit();
5425 Exit;
5426 end
5427 else if cmd = 'pause' then
5428 begin
5429 if (g_ActiveWindow = nil) then
5430 g_Game_Pause(not gPause);
5431 end
5432 else if cmd = 'endgame' then
5433 gExit := EXIT_SIMPLE
5434 else if cmd = 'restart' then
5435 begin
5436 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5437 begin
5438 if g_Game_IsClient then
5439 begin
5440 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5441 Exit;
5442 end;
5443 g_Game_Restart();
5444 end else
5445 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5446 end
5447 else if cmd = 'kick' then
5448 begin
5449 if g_Game_IsServer then
5450 begin
5451 if Length(P) < 2 then
5452 begin
5453 g_Console_Add('kick <name>');
5454 Exit;
5455 end;
5456 if P[1] = '' then
5457 begin
5458 g_Console_Add('kick <name>');
5459 Exit;
5460 end;
5462 if g_Game_IsNet then
5463 pl := g_Net_Client_ByName(P[1]);
5464 if (pl <> nil) then
5465 begin
5466 s := g_Net_ClientName_ByID(pl^.ID);
5467 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5468 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5469 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5470 if NetUseMaster then
5471 g_Net_Slist_Update;
5472 end else if gPlayers <> nil then
5473 for a := Low(gPlayers) to High(gPlayers) do
5474 if gPlayers[a] <> nil then
5475 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5476 begin
5477 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5478 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5479 continue;
5480 gPlayers[a].Lives := 0;
5481 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5482 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5483 g_Player_Remove(gPlayers[a].UID);
5484 if NetUseMaster then
5485 g_Net_Slist_Update;
5486 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5487 g_Bot_MixNames();
5488 end;
5489 end else
5490 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5491 end
5492 else if cmd = 'kick_id' then
5493 begin
5494 if g_Game_IsServer and g_Game_IsNet then
5495 begin
5496 if Length(P) < 2 then
5497 begin
5498 g_Console_Add('kick_id <client ID>');
5499 Exit;
5500 end;
5501 if P[1] = '' then
5502 begin
5503 g_Console_Add('kick_id <client ID>');
5504 Exit;
5505 end;
5507 a := StrToIntDef(P[1], 0);
5508 if (NetClients <> nil) and (a <= High(NetClients)) then
5509 begin
5510 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5511 begin
5512 s := g_Net_ClientName_ByID(NetClients[a].ID);
5513 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5514 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5515 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5516 if NetUseMaster then
5517 g_Net_Slist_Update;
5518 end;
5519 end;
5520 end else
5521 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5522 end
5523 else if cmd = 'ban' then
5524 begin
5525 if g_Game_IsServer and g_Game_IsNet then
5526 begin
5527 if Length(P) < 2 then
5528 begin
5529 g_Console_Add('ban <name>');
5530 Exit;
5531 end;
5532 if P[1] = '' then
5533 begin
5534 g_Console_Add('ban <name>');
5535 Exit;
5536 end;
5538 pl := g_Net_Client_ByName(P[1]);
5539 if (pl <> nil) then
5540 begin
5541 s := g_Net_ClientName_ByID(pl^.ID);
5542 g_Net_BanHost(pl^.Peer^.address.host, False);
5543 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5544 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5545 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5546 if NetUseMaster then
5547 g_Net_Slist_Update;
5548 end else
5549 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5550 end else
5551 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5552 end
5553 else if cmd = 'ban_id' then
5554 begin
5555 if g_Game_IsServer and g_Game_IsNet then
5556 begin
5557 if Length(P) < 2 then
5558 begin
5559 g_Console_Add('ban_id <client ID>');
5560 Exit;
5561 end;
5562 if P[1] = '' then
5563 begin
5564 g_Console_Add('ban_id <client ID>');
5565 Exit;
5566 end;
5568 a := StrToIntDef(P[1], 0);
5569 if (NetClients <> nil) and (a <= High(NetClients)) then
5570 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5571 begin
5572 s := g_Net_ClientName_ByID(NetClients[a].ID);
5573 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5574 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5575 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5576 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5577 if NetUseMaster then
5578 g_Net_Slist_Update;
5579 end;
5580 end else
5581 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5582 end
5583 else if cmd = 'permban' then
5584 begin
5585 if g_Game_IsServer and g_Game_IsNet then
5586 begin
5587 if Length(P) < 2 then
5588 begin
5589 g_Console_Add('permban <name>');
5590 Exit;
5591 end;
5592 if P[1] = '' then
5593 begin
5594 g_Console_Add('permban <name>');
5595 Exit;
5596 end;
5598 pl := g_Net_Client_ByName(P[1]);
5599 if (pl <> nil) then
5600 begin
5601 s := g_Net_ClientName_ByID(pl^.ID);
5602 g_Net_BanHost(pl^.Peer^.address.host);
5603 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5604 g_Net_SaveBanList();
5605 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5606 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5607 if NetUseMaster then
5608 g_Net_Slist_Update;
5609 end else
5610 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5611 end else
5612 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5613 end
5614 else if cmd = 'permban_id' then
5615 begin
5616 if g_Game_IsServer and g_Game_IsNet then
5617 begin
5618 if Length(P) < 2 then
5619 begin
5620 g_Console_Add('permban_id <client ID>');
5621 Exit;
5622 end;
5623 if P[1] = '' then
5624 begin
5625 g_Console_Add('permban_id <client ID>');
5626 Exit;
5627 end;
5629 a := StrToIntDef(P[1], 0);
5630 if (NetClients <> nil) and (a <= High(NetClients)) then
5631 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5632 begin
5633 s := g_Net_ClientName_ByID(NetClients[a].ID);
5634 g_Net_BanHost(NetClients[a].Peer^.address.host);
5635 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5636 g_Net_SaveBanList();
5637 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5638 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5639 if NetUseMaster then
5640 g_Net_Slist_Update;
5641 end;
5642 end else
5643 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5644 end
5645 else if cmd = 'unban' then
5646 begin
5647 if g_Game_IsServer and g_Game_IsNet then
5648 begin
5649 if Length(P) < 2 then
5650 begin
5651 g_Console_Add('unban <IP Address>');
5652 Exit;
5653 end;
5654 if P[1] = '' then
5655 begin
5656 g_Console_Add('unban <IP Address>');
5657 Exit;
5658 end;
5660 if g_Net_UnbanHost(P[1]) then
5661 begin
5662 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5663 g_Net_SaveBanList();
5664 end else
5665 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5666 end else
5667 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5668 end
5669 else if cmd = 'clientlist' then
5670 begin
5671 if g_Game_IsServer and g_Game_IsNet then
5672 begin
5673 b := 0;
5674 if NetClients <> nil then
5675 for a := Low(NetClients) to High(NetClients) do
5676 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5677 begin
5678 plr := g_Player_Get(NetClients[a].Player);
5679 if plr = nil then continue;
5680 Inc(b);
5681 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5682 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5683 end;
5684 if b = 0 then
5685 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5686 end else
5687 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5688 end
5689 else if cmd = 'connect' then
5690 begin
5691 if (NetMode = NET_NONE) then
5692 begin
5693 if Length(P) < 2 then
5694 begin
5695 g_Console_Add('connect <IP> [port] [password]');
5696 Exit;
5697 end;
5698 if P[1] = '' then
5699 begin
5700 g_Console_Add('connect <IP> [port] [password]');
5701 Exit;
5702 end;
5704 if Length(P) > 2 then
5705 prt := StrToIntDef(P[2], 25666)
5706 else
5707 prt := 25666;
5709 if Length(P) > 3 then
5710 pw := P[3]
5711 else
5712 pw := '';
5714 g_Game_StartClient(P[1], prt, pw);
5715 end;
5716 end
5717 else if cmd = 'disconnect' then
5718 begin
5719 if (NetMode = NET_CLIENT) then
5720 g_Net_Disconnect();
5721 end
5722 else if cmd = 'reconnect' then
5723 begin
5724 if (NetMode = NET_SERVER) then
5725 Exit;
5727 if (NetMode = NET_CLIENT) then
5728 begin
5729 g_Net_Disconnect();
5730 gExit := EXIT_SIMPLE;
5731 EndGame;
5732 end;
5734 //TODO: Use last successful password to reconnect, instead of ''
5735 g_Game_StartClient(NetClientIP, NetClientPort, '');
5736 end
5737 else if (cmd = 'addbot') or
5738 (cmd = 'bot_add') then
5739 begin
5740 if Length(P) > 1 then
5741 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5742 else
5743 g_Bot_Add(TEAM_NONE, 2);
5744 end
5745 else if cmd = 'bot_addlist' then
5746 begin
5747 if Length(P) > 1 then
5748 if Length(P) = 2 then
5749 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5750 else
5751 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5752 end
5753 else if cmd = 'bot_removeall' then
5754 g_Bot_RemoveAll()
5755 else if cmd = 'chat' then
5756 begin
5757 if g_Game_IsNet then
5758 begin
5759 if Length(P) > 1 then
5760 begin
5761 for a := 1 to High(P) do
5762 chstr := chstr + P[a] + ' ';
5764 if Length(chstr) > 200 then SetLength(chstr, 200);
5766 if Length(chstr) < 1 then
5767 begin
5768 g_Console_Add('chat <text>');
5769 Exit;
5770 end;
5772 chstr := b_Text_Format(chstr);
5773 if g_Game_IsClient then
5774 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5775 else
5776 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5777 end
5778 else
5779 g_Console_Add('chat <text>');
5780 end else
5781 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5782 end
5783 else if cmd = 'teamchat' then
5784 begin
5785 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5786 begin
5787 if Length(P) > 1 then
5788 begin
5789 for a := 1 to High(P) do
5790 chstr := chstr + P[a] + ' ';
5792 if Length(chstr) > 200 then SetLength(chstr, 200);
5794 if Length(chstr) < 1 then
5795 begin
5796 g_Console_Add('teamchat <text>');
5797 Exit;
5798 end;
5800 chstr := b_Text_Format(chstr);
5801 if g_Game_IsClient then
5802 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5803 else
5804 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5805 gPlayer1Settings.Team);
5806 end
5807 else
5808 g_Console_Add('teamchat <text>');
5809 end else
5810 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5811 end
5812 else if cmd = 'game' then
5813 begin
5814 if gGameSettings.GameType <> GT_NONE then
5815 begin
5816 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5817 Exit;
5818 end;
5819 if Length(P) = 1 then
5820 begin
5821 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5822 Exit;
5823 end;
5824 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5825 P[1] := addWadExtension(P[1]);
5826 if FileExists(MapsDir + P[1]) then
5827 begin
5828 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5829 if Length(P) < 3 then
5830 begin
5831 SetLength(P, 3);
5832 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5833 end;
5835 s := P[1] + ':\' + UpperCase(P[2]);
5837 if g_Map_Exist(MapsDir + s) then
5838 begin
5839 // Çàïóñêàåì ñâîþ èãðó
5840 g_Game_Free();
5841 with gGameSettings do
5842 begin
5843 GameMode := g_Game_TextToMode(gcGameMode);
5844 if gSwitchGameMode <> GM_NONE then
5845 GameMode := gSwitchGameMode;
5846 if GameMode = GM_NONE then GameMode := GM_DM;
5847 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5848 b := 1;
5849 if Length(P) >= 4 then
5850 b := StrToIntDef(P[3], 1);
5851 g_Game_StartCustom(s, GameMode, TimeLimit,
5852 GoalLimit, MaxLives, Options, b);
5853 end;
5854 end
5855 else
5856 if P[2] = '' then
5857 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5858 else
5859 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5860 end else
5861 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5862 end
5863 else if cmd = 'host' then
5864 begin
5865 if gGameSettings.GameType <> GT_NONE then
5866 begin
5867 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5868 Exit;
5869 end;
5870 if Length(P) < 4 then
5871 begin
5872 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5873 Exit;
5874 end;
5875 if not StrToIp(P[1], listen) then
5876 Exit;
5877 prt := StrToIntDef(P[2], 25666);
5879 P[3] := addWadExtension(P[3]);
5880 if FileExists(MapsDir + P[3]) then
5881 begin
5882 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5883 if Length(P) < 5 then
5884 begin
5885 SetLength(P, 5);
5886 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5887 end;
5889 s := P[3] + ':\' + UpperCase(P[4]);
5891 if g_Map_Exist(MapsDir + s) then
5892 begin
5893 // Çàïóñêàåì ñâîþ èãðó
5894 g_Game_Free();
5895 with gGameSettings do
5896 begin
5897 GameMode := g_Game_TextToMode(gcGameMode);
5898 if gSwitchGameMode <> GM_NONE then
5899 GameMode := gSwitchGameMode;
5900 if GameMode = GM_NONE then GameMode := GM_DM;
5901 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5902 b := 0;
5903 if Length(P) >= 6 then
5904 b := StrToIntDef(P[5], 0);
5905 g_Game_StartServer(s, GameMode, TimeLimit,
5906 GoalLimit, MaxLives, Options, b, listen, prt);
5907 end;
5908 end
5909 else
5910 if P[4] = '' then
5911 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5912 else
5913 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5914 end else
5915 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5916 end
5917 else if cmd = 'map' then
5918 begin
5919 if Length(P) = 1 then
5920 begin
5921 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5922 begin
5923 g_Console_Add(cmd + ' <MAP>');
5924 g_Console_Add(cmd + ' <WAD> [MAP]');
5925 end else
5926 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5927 end else
5928 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5929 begin
5930 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5931 if Length(P) < 3 then
5932 begin
5933 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5934 s := UpperCase(P[1]);
5935 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5936 begin // Êàðòà íàøëàñü
5937 gExitByTrigger := False;
5938 if gGameOn then
5939 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5940 gNextMap := s;
5941 gExit := EXIT_ENDLEVELCUSTOM;
5942 end
5943 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5944 g_Game_ChangeMap(s);
5945 end else
5946 begin
5947 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5948 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5949 P[1] := addWadExtension(P[1]);
5950 if FileExists(MapsDir + P[1]) then
5951 begin
5952 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5953 SetLength(P, 3);
5954 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5956 s := P[1] + ':\' + P[2];
5958 if g_Map_Exist(MapsDir + 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 if P[2] = '' then
5970 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5971 else
5972 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5973 end else
5974 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5975 end;
5976 end else
5977 begin
5978 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5979 P[1] := addWadExtension(P[1]);
5980 if FileExists(MapsDir + P[1]) then
5981 begin
5982 // Íàøëè WAD ôàéë
5983 P[2] := UpperCase(P[2]);
5984 s := P[1] + ':\' + P[2];
5986 if g_Map_Exist(MapsDir + s) then
5987 begin // Íàøëè êàðòó
5988 gExitByTrigger := False;
5989 if gGameOn then
5990 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5991 gNextMap := s;
5992 gExit := EXIT_ENDLEVELCUSTOM;
5993 end
5994 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5995 g_Game_ChangeMap(s);
5996 end else
5997 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5998 end else
5999 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6000 end;
6001 end else
6002 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6003 end
6004 else if cmd = 'nextmap' then
6005 begin
6006 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6007 g_Console_Add(_lc[I_MSG_NOT_GAME])
6008 else begin
6009 nm := True;
6010 if Length(P) = 1 then
6011 begin
6012 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6013 begin
6014 g_Console_Add(cmd + ' <MAP>');
6015 g_Console_Add(cmd + ' <WAD> [MAP]');
6016 end else begin
6017 nm := False;
6018 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6019 end;
6020 end else
6021 begin
6022 nm := False;
6023 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6024 begin
6025 if Length(P) < 3 then
6026 begin
6027 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6028 s := UpperCase(P[1]);
6029 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6030 begin // Êàðòà íàøëàñü
6031 gExitByTrigger := False;
6032 gNextMap := s;
6033 nm := True;
6034 end else
6035 begin
6036 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6037 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6038 P[1] := addWadExtension(P[1]);
6039 if FileExists(MapsDir + P[1]) then
6040 begin
6041 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6042 SetLength(P, 3);
6043 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6045 s := P[1] + ':\' + P[2];
6047 if g_Map_Exist(MapsDir + s) then
6048 begin // Óñòàíàâëèâàåì êàðòó
6049 gExitByTrigger := False;
6050 gNextMap := s;
6051 nm := True;
6052 end else
6053 if P[2] = '' then
6054 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6055 else
6056 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6057 end else
6058 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6059 end;
6060 end else
6061 begin
6062 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6063 P[1] := addWadExtension(P[1]);
6064 if FileExists(MapsDir + P[1]) then
6065 begin
6066 // Íàøëè WAD ôàéë
6067 P[2] := UpperCase(P[2]);
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 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6077 end else
6078 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6079 end;
6080 end else
6081 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6082 end;
6083 if nm then
6084 if gNextMap = '' then
6085 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6086 else
6087 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6088 end;
6089 end
6090 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6091 begin
6092 if not gGameOn then
6093 g_Console_Add(_lc[I_MSG_NOT_GAME])
6094 else
6095 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6096 begin
6097 gExitByTrigger := False;
6098 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6099 if (gNextMap = '') and (gTriggers <> nil) then
6100 for a := 0 to High(gTriggers) do
6101 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6102 begin
6103 gExitByTrigger := True;
6104 //gNextMap := gTriggers[a].Data.MapName;
6105 gNextMap := gTriggers[a].trigData.trigMap;
6106 Break;
6107 end;
6108 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6109 if gNextMap = '' then
6110 gNextMap := g_Game_GetNextMap();
6111 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6112 if not isWadPath(gNextMap) then
6113 s := gGameSettings.WAD + ':\' + gNextMap
6114 else
6115 s := gNextMap;
6116 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6117 if g_Map_Exist(MapsDir + s) then
6118 gExit := EXIT_ENDLEVELCUSTOM
6119 else
6120 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6121 end else
6122 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6123 end
6124 else if (cmd = 'event') then
6125 begin
6126 if (Length(P) <= 1) then
6127 begin
6128 for a := 0 to High(gEvents) do
6129 if gEvents[a].Command = '' then
6130 g_Console_Add(gEvents[a].Name + ' <none>')
6131 else
6132 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6133 Exit;
6134 end;
6135 if (Length(P) = 2) then
6136 begin
6137 for a := 0 to High(gEvents) do
6138 if gEvents[a].Name = P[1] then
6139 if gEvents[a].Command = '' then
6140 g_Console_Add(gEvents[a].Name + ' <none>')
6141 else
6142 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6143 Exit;
6144 end;
6145 for a := 0 to High(gEvents) do
6146 if gEvents[a].Name = P[1] then
6147 begin
6148 gEvents[a].Command := '';
6149 for b := 2 to High(P) do
6150 if Pos(' ', P[b]) = 0 then
6151 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6152 else
6153 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6154 gEvents[a].Command := Trim(gEvents[a].Command);
6155 Exit;
6156 end;
6157 end
6158 else if cmd = 'suicide' then
6159 begin
6160 if gGameOn then
6161 begin
6162 if g_Game_IsClient then
6163 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6164 else
6165 begin
6166 if gPlayer1 <> nil then
6167 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6168 if gPlayer2 <> nil then
6169 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6170 end;
6171 end;
6172 end
6173 // Êîìàíäû Ñâîåé èãðû:
6174 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6175 begin
6176 if cmd = 'bot_addred' then
6177 begin
6178 if Length(P) > 1 then
6179 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6180 else
6181 g_Bot_Add(TEAM_RED, 2);
6182 end
6183 else if cmd = 'bot_addblue' then
6184 begin
6185 if Length(P) > 1 then
6186 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6187 else
6188 g_Bot_Add(TEAM_BLUE, 2);
6189 end
6190 else if cmd = 'spectate' then
6191 begin
6192 if not gGameOn then
6193 Exit;
6194 g_Game_Spectate();
6195 end
6196 else if cmd = 'say' then
6197 begin
6198 if g_Game_IsServer and g_Game_IsNet then
6199 begin
6200 if Length(P) > 1 then
6201 begin
6202 chstr := '';
6203 for a := 1 to High(P) do
6204 chstr := chstr + P[a] + ' ';
6206 if Length(chstr) > 200 then SetLength(chstr, 200);
6208 if Length(chstr) < 1 then
6209 begin
6210 g_Console_Add('say <text>');
6211 Exit;
6212 end;
6214 chstr := b_Text_Format(chstr);
6215 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6216 end
6217 else g_Console_Add('say <text>');
6218 end else
6219 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6220 end
6221 else if cmd = 'tell' then
6222 begin
6223 if g_Game_IsServer and g_Game_IsNet then
6224 begin
6225 if (Length(P) > 2) and (P[1] <> '') then
6226 begin
6227 chstr := '';
6228 for a := 2 to High(P) do
6229 chstr := chstr + P[a] + ' ';
6231 if Length(chstr) > 200 then SetLength(chstr, 200);
6233 if Length(chstr) < 1 then
6234 begin
6235 g_Console_Add('tell <playername> <text>');
6236 Exit;
6237 end;
6239 pl := g_Net_Client_ByName(P[1]);
6240 if pl <> nil then
6241 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6242 else
6243 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6244 end
6245 else g_Console_Add('tell <playername> <text>');
6246 end else
6247 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6248 end
6249 else if (cmd = 'overtime') and not g_Game_IsClient then
6250 begin
6251 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6252 Exit;
6253 // Äîïîëíèòåëüíîå âðåìÿ:
6254 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6256 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6257 [gGameSettings.TimeLimit div 3600,
6258 (gGameSettings.TimeLimit div 60) mod 60,
6259 gGameSettings.TimeLimit mod 60]));
6260 if g_Game_IsNet then MH_SEND_GameSettings;
6261 end
6262 else if (cmd = 'rcon_password') and g_Game_IsClient then
6263 begin
6264 if (Length(P) <= 1) then
6265 g_Console_Add('rcon_password <password>')
6266 else
6267 MC_SEND_RCONPassword(P[1]);
6268 end
6269 else if cmd = 'rcon' then
6270 begin
6271 if g_Game_IsClient then
6272 begin
6273 if Length(P) > 1 then
6274 begin
6275 chstr := '';
6276 for a := 1 to High(P) do
6277 chstr := chstr + P[a] + ' ';
6279 if Length(chstr) > 200 then SetLength(chstr, 200);
6281 if Length(chstr) < 1 then
6282 begin
6283 g_Console_Add('rcon <command>');
6284 Exit;
6285 end;
6287 MC_SEND_RCONCommand(chstr);
6288 end
6289 else g_Console_Add('rcon <command>');
6290 end;
6291 end
6292 else if cmd = 'ready' then
6293 begin
6294 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6295 gLMSRespawnTime := gTime + 100;
6296 end
6297 else if (cmd = 'callvote') and g_Game_IsNet then
6298 begin
6299 if Length(P) > 1 then
6300 begin
6301 chstr := '';
6302 for a := 1 to High(P) do begin
6303 if a > 1 then chstr := chstr + ' ';
6304 chstr := chstr + P[a];
6305 end;
6307 if Length(chstr) > 200 then SetLength(chstr, 200);
6309 if Length(chstr) < 1 then
6310 begin
6311 g_Console_Add('callvote <command>');
6312 Exit;
6313 end;
6315 if g_Game_IsClient then
6316 MC_SEND_Vote(True, chstr)
6317 else
6318 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6319 g_Console_Process('vote', True);
6320 end
6321 else
6322 g_Console_Add('callvote <command>');
6323 end
6324 else if (cmd = 'vote') and g_Game_IsNet then
6325 begin
6326 if g_Game_IsClient then
6327 MC_SEND_Vote(False)
6328 else if gVoteInProgress then
6329 begin
6330 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6331 a := Floor((NetClientCount+1)/2.0) + 1
6332 else
6333 a := Floor(NetClientCount/2.0) + 1;
6334 if gVoted then
6335 begin
6336 Dec(gVoteCount);
6337 gVoted := False;
6338 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6339 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6340 end
6341 else
6342 begin
6343 Inc(gVoteCount);
6344 gVoted := True;
6345 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6346 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6347 g_Game_CheckVote;
6348 end;
6349 end;
6350 end
6351 end;
6352 end;
6354 procedure g_TakeScreenShot();
6355 var
6356 a: Word;
6357 FileName: string;
6358 ssdir, t: string;
6359 st: TStream;
6360 ok: Boolean;
6361 begin
6362 if e_NoGraphics then Exit;
6363 ssdir := GameDir+'/screenshots';
6364 if not findFileCI(ssdir, true) then
6365 begin
6366 // try to create dir
6367 try
6368 CreateDir(ssdir);
6369 except
6370 end;
6371 if not findFileCI(ssdir, true) then exit; // alas
6372 end;
6373 try
6374 for a := 1 to High(Word) do
6375 begin
6376 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6377 t := FileName;
6378 if findFileCI(t, true) then continue;
6379 if not findFileCI(FileName) then
6380 begin
6381 ok := false;
6382 st := createDiskFile(FileName);
6383 try
6384 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6385 ok := true;
6386 finally
6387 st.Free();
6388 end;
6389 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6390 break;
6391 end;
6392 end;
6393 except
6394 end;
6395 end;
6397 procedure g_Game_InGameMenu(Show: Boolean);
6398 begin
6399 if (g_ActiveWindow = nil) and Show then
6400 begin
6401 if gGameSettings.GameType = GT_SINGLE then
6402 g_GUI_ShowWindow('GameSingleMenu')
6403 else
6404 begin
6405 if g_Game_IsClient then
6406 g_GUI_ShowWindow('GameClientMenu')
6407 else
6408 if g_Game_IsNet then
6409 g_GUI_ShowWindow('GameServerMenu')
6410 else
6411 g_GUI_ShowWindow('GameCustomMenu');
6412 end;
6413 g_Sound_PlayEx('MENU_OPEN');
6415 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6416 if (not g_Game_IsNet) then
6417 g_Game_Pause(True);
6418 end
6419 else
6420 if (g_ActiveWindow <> nil) and (not Show) then
6421 begin
6422 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6423 if (not g_Game_IsNet) then
6424 g_Game_Pause(False);
6425 end;
6426 end;
6428 procedure g_Game_Pause(Enable: Boolean);
6429 begin
6430 if not gGameOn then
6431 Exit;
6433 if gPause = Enable then
6434 Exit;
6436 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6437 Exit;
6439 gPause := Enable;
6440 g_Game_PauseAllSounds(Enable);
6441 end;
6443 procedure g_Game_PauseAllSounds(Enable: Boolean);
6444 var
6445 i: Integer;
6446 begin
6447 // Òðèããåðû:
6448 if gTriggers <> nil then
6449 for i := 0 to High(gTriggers) do
6450 with gTriggers[i] do
6451 if (TriggerType = TRIGGER_SOUND) and
6452 (Sound <> nil) and
6453 Sound.IsPlaying() then
6454 begin
6455 Sound.Pause(Enable);
6456 end;
6458 // Çâóêè èãðîêîâ:
6459 if gPlayers <> nil then
6460 for i := 0 to High(gPlayers) do
6461 if gPlayers[i] <> nil then
6462 gPlayers[i].PauseSounds(Enable);
6464 // Ìóçûêà:
6465 if gMusic <> nil then
6466 gMusic.Pause(Enable);
6467 end;
6469 procedure g_Game_StopAllSounds(all: Boolean);
6470 var
6471 i: Integer;
6472 begin
6473 if gTriggers <> nil then
6474 for i := 0 to High(gTriggers) do
6475 with gTriggers[i] do
6476 if (TriggerType = TRIGGER_SOUND) and
6477 (Sound <> nil) then
6478 Sound.Stop();
6480 if gMusic <> nil then
6481 gMusic.Stop();
6483 if all then
6484 e_StopChannels();
6485 end;
6487 procedure g_Game_UpdateTriggerSounds();
6488 var
6489 i: Integer;
6490 begin
6491 if gTriggers <> nil then
6492 for i := 0 to High(gTriggers) do
6493 with gTriggers[i] do
6494 if (TriggerType = TRIGGER_SOUND) and
6495 (Sound <> nil) and
6496 (trigData.trigLocal) and
6497 Sound.IsPlaying() then
6498 begin
6499 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6500 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6501 begin
6502 Sound.SetPan(0.5 - trigData.trigPan/255.0);
6503 Sound.SetVolume(trigData.trigVolume/255.0);
6504 end
6505 else
6506 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), trigData.trigVolume/255.0);
6507 end;
6508 end;
6510 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6511 begin
6512 Result := False;
6513 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6514 begin
6515 Result := True;
6516 Exit;
6517 end;
6518 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6519 begin
6520 Result := True;
6521 Exit;
6522 end;
6523 if gSpectMode <> SPECT_PLAYERS then
6524 Exit;
6525 if gSpectPID1 = UID then
6526 begin
6527 Result := True;
6528 Exit;
6529 end;
6530 if gSpectViewTwo and (gSpectPID2 = UID) then
6531 begin
6532 Result := True;
6533 Exit;
6534 end;
6535 end;
6537 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6538 var
6539 Pl: TPlayer;
6540 begin
6541 Result := False;
6542 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6543 begin
6544 Result := True;
6545 Exit;
6546 end;
6547 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6548 begin
6549 Result := True;
6550 Exit;
6551 end;
6552 if gSpectMode <> SPECT_PLAYERS then
6553 Exit;
6554 Pl := g_Player_Get(gSpectPID1);
6555 if (Pl <> nil) and (Pl.Team = Team) then
6556 begin
6557 Result := True;
6558 Exit;
6559 end;
6560 if gSpectViewTwo then
6561 begin
6562 Pl := g_Player_Get(gSpectPID2);
6563 if (Pl <> nil) and (Pl.Team = Team) then
6564 begin
6565 Result := True;
6566 Exit;
6567 end;
6568 end;
6569 end;
6571 procedure g_Game_Message(Msg: string; Time: Word);
6572 begin
6573 MessageText := b_Text_Format(Msg);
6574 MessageTime := Time;
6575 end;
6577 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6578 var
6579 a: Integer;
6580 begin
6581 case gAnnouncer of
6582 ANNOUNCE_NONE:
6583 Exit;
6584 ANNOUNCE_ME,
6585 ANNOUNCE_MEPLUS:
6586 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6587 Exit;
6588 end;
6589 for a := 0 to 3 do
6590 if goodsnd[a].IsPlaying() then
6591 Exit;
6593 goodsnd[Random(4)].Play();
6594 end;
6596 procedure g_Game_Announce_KillCombo(Param: Integer);
6597 var
6598 UID: Word;
6599 c, n: Byte;
6600 Pl: TPlayer;
6601 Name: String;
6602 begin
6603 UID := Param and $FFFF;
6604 c := Param shr 16;
6605 if c < 2 then
6606 Exit;
6608 Pl := g_Player_Get(UID);
6609 if Pl = nil then
6610 Name := '?'
6611 else
6612 Name := Pl.Name;
6614 case c of
6615 2: begin
6616 n := 0;
6617 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6618 end;
6619 3: begin
6620 n := 1;
6621 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6622 end;
6623 4: begin
6624 n := 2;
6625 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6626 end;
6627 else begin
6628 n := 3;
6629 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6630 end;
6631 end;
6633 case gAnnouncer of
6634 ANNOUNCE_NONE:
6635 Exit;
6636 ANNOUNCE_ME:
6637 if not g_Game_IsWatchedPlayer(UID) then
6638 Exit;
6639 ANNOUNCE_MEPLUS:
6640 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6641 Exit;
6642 end;
6644 if killsnd[n].IsPlaying() then
6645 killsnd[n].Stop();
6646 killsnd[n].Play();
6647 end;
6649 procedure g_Game_StartVote(Command, Initiator: string);
6650 var
6651 Need: Integer;
6652 begin
6653 if not gVotesEnabled then Exit;
6654 if gGameSettings.GameType <> GT_SERVER then Exit;
6655 if gVoteInProgress or gVotePassed then
6656 begin
6657 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6658 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6659 Exit;
6660 end;
6661 gVoteInProgress := True;
6662 gVotePassed := False;
6663 gVoteTimer := gTime + gVoteTimeout * 1000;
6664 gVoteCount := 0;
6665 gVoted := False;
6666 gVoteCommand := Command;
6668 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6669 Need := Floor((NetClientCount+1)/2.0)+1
6670 else
6671 Need := Floor(NetClientCount/2.0)+1;
6672 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6673 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6674 end;
6676 procedure g_Game_CheckVote;
6677 var
6678 I, Need: Integer;
6679 begin
6680 if gGameSettings.GameType <> GT_SERVER then Exit;
6681 if not gVoteInProgress then Exit;
6683 if (gTime >= gVoteTimer) then
6684 begin
6685 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6686 Need := Floor((NetClientCount+1)/2.0) + 1
6687 else
6688 Need := Floor(NetClientCount/2.0) + 1;
6689 if gVoteCount >= Need then
6690 begin
6691 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6692 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6693 gVotePassed := True;
6694 gVoteCmdTimer := gTime + 5000;
6695 end
6696 else
6697 begin
6698 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6699 MH_SEND_VoteEvent(NET_VE_FAILED);
6700 end;
6701 if NetClients <> nil then
6702 for I := Low(NetClients) to High(NetClients) do
6703 if NetClients[i].Used then
6704 NetClients[i].Voted := False;
6705 gVoteInProgress := False;
6706 gVoted := False;
6707 gVoteCount := 0;
6708 end
6709 else
6710 begin
6711 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6712 Need := Floor((NetClientCount+1)/2.0) + 1
6713 else
6714 Need := Floor(NetClientCount/2.0) + 1;
6715 if gVoteCount >= Need then
6716 begin
6717 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6718 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6719 gVoteInProgress := False;
6720 gVotePassed := True;
6721 gVoteCmdTimer := gTime + 5000;
6722 gVoted := False;
6723 gVoteCount := 0;
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 end;
6729 end;
6730 end;
6732 procedure g_Game_LoadMapList(FileName: string);
6733 var
6734 ListFile: TextFile;
6735 s: string;
6736 begin
6737 MapList := nil;
6738 MapIndex := -1;
6740 if not FileExists(FileName) then Exit;
6742 AssignFile(ListFile, FileName);
6743 Reset(ListFile);
6744 while not EOF(ListFile) do
6745 begin
6746 ReadLn(ListFile, s);
6748 s := Trim(s);
6749 if s = '' then Continue;
6751 SetLength(MapList, Length(MapList)+1);
6752 MapList[High(MapList)] := s;
6753 end;
6754 CloseFile(ListFile);
6755 end;
6757 procedure g_Game_SetDebugMode();
6758 begin
6759 gDebugMode := True;
6760 // ×èòû (äàæå â ñâîåé èãðå):
6761 gCheats := True;
6762 end;
6764 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6765 var
6766 i: Word;
6767 begin
6768 if Length(LoadingStat.Msgs) = 0 then
6769 Exit;
6771 with LoadingStat do
6772 begin
6773 if not reWrite then
6774 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6775 if NextMsg = Length(Msgs) then
6776 begin // scroll
6777 for i := 0 to High(Msgs)-1 do
6778 Msgs[i] := Msgs[i+1];
6779 end
6780 else
6781 Inc(NextMsg);
6782 end else
6783 if NextMsg = 0 then
6784 Inc(NextMsg);
6786 Msgs[NextMsg-1] := Text;
6787 CurValue := 0;
6788 MaxValue := Max;
6789 ShowCount := 0;
6790 end;
6792 g_ActiveWindow := nil;
6794 ProcessLoading(true);
6795 end;
6797 procedure g_Game_StepLoading();
6798 begin
6799 with LoadingStat do
6800 begin
6801 Inc(CurValue);
6802 Inc(ShowCount);
6803 if (ShowCount > LOADING_SHOW_STEP) then
6804 begin
6805 ShowCount := 0;
6806 ProcessLoading();
6807 end;
6808 end;
6809 end;
6811 procedure g_Game_ClearLoading();
6812 var
6813 len: Word;
6814 begin
6815 with LoadingStat do
6816 begin
6817 CurValue := 0;
6818 MaxValue := 0;
6819 ShowCount := 0;
6820 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6821 if len < 1 then len := 1;
6822 SetLength(Msgs, len);
6823 for len := Low(Msgs) to High(Msgs) do
6824 Msgs[len] := '';
6825 NextMsg := 0;
6826 end;
6827 end;
6829 procedure Parse_Params(var pars: TParamStrValues);
6830 var
6831 i: Integer;
6832 s: String;
6833 begin
6834 SetLength(pars, 0);
6835 i := 1;
6836 while i <= ParamCount do
6837 begin
6838 s := ParamStr(i);
6839 if (s[1] = '-') and (Length(s) > 1) then
6840 begin
6841 if (s[2] = '-') and (Length(s) > 2) then
6842 begin // Îäèíî÷íûé ïàðàìåòð
6843 SetLength(pars, Length(pars) + 1);
6844 with pars[High(pars)] do
6845 begin
6846 Name := LowerCase(s);
6847 Value := '+';
6848 end;
6849 end
6850 else
6851 if (i < ParamCount) then
6852 begin // Ïàðàìåòð ñî çíà÷åíèåì
6853 Inc(i);
6854 SetLength(pars, Length(pars) + 1);
6855 with pars[High(pars)] do
6856 begin
6857 Name := LowerCase(s);
6858 Value := LowerCase(ParamStr(i));
6859 end;
6860 end;
6861 end;
6863 Inc(i);
6864 end;
6865 end;
6867 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6868 var
6869 i: Integer;
6870 begin
6871 Result := '';
6872 for i := 0 to High(pars) do
6873 if pars[i].Name = aName then
6874 begin
6875 Result := pars[i].Value;
6876 Break;
6877 end;
6878 end;
6880 procedure g_Game_Process_Params();
6881 var
6882 pars: TParamStrValues;
6883 map: String;
6884 GMode, n: Byte;
6885 LimT, LimS: Integer;
6886 Opt: LongWord;
6887 Lives: Integer;
6888 s: String;
6889 Port: Integer;
6890 ip: String;
6891 F: TextFile;
6892 begin
6893 Parse_Params(pars);
6895 // Debug mode:
6896 s := Find_Param_Value(pars, '--debug');
6897 if (s <> '') then
6898 begin
6899 g_Game_SetDebugMode();
6900 s := Find_Param_Value(pars, '--netdump');
6901 if (s <> '') then
6902 NetDump := True;
6903 end;
6905 // Connect when game loads
6906 ip := Find_Param_Value(pars, '-connect');
6908 if ip <> '' then
6909 begin
6910 s := Find_Param_Value(pars, '-port');
6911 if (s = '') or not TryStrToInt(s, Port) then
6912 Port := 25666;
6914 s := Find_Param_Value(pars, '-pw');
6916 g_Game_StartClient(ip, Port, s);
6917 Exit;
6918 end;
6920 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
6921 if (s <> '') then
6922 begin
6923 gDefaultMegawadStart := s;
6924 end;
6926 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
6927 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
6928 begin
6929 gDefaultMegawadStart := DF_Default_Megawad_Start;
6930 end;
6932 // Start map when game loads:
6933 map := LowerCase(Find_Param_Value(pars, '-map'));
6934 if isWadPath(map) then
6935 begin
6936 // Game mode:
6937 s := Find_Param_Value(pars, '-gm');
6938 GMode := g_Game_TextToMode(s);
6939 if GMode = GM_NONE then GMode := GM_DM;
6940 if GMode = GM_SINGLE then GMode := GM_COOP;
6942 // Time limit:
6943 s := Find_Param_Value(pars, '-limt');
6944 if (s = '') or (not TryStrToInt(s, LimT)) then
6945 LimT := 0;
6946 if LimT < 0 then
6947 LimT := 0;
6949 // Goal limit:
6950 s := Find_Param_Value(pars, '-lims');
6951 if (s = '') or (not TryStrToInt(s, LimS)) then
6952 LimS := 0;
6953 if LimS < 0 then
6954 LimS := 0;
6956 // Lives limit:
6957 s := Find_Param_Value(pars, '-lives');
6958 if (s = '') or (not TryStrToInt(s, Lives)) then
6959 Lives := 0;
6960 if Lives < 0 then
6961 Lives := 0;
6963 // Options:
6964 s := Find_Param_Value(pars, '-opt');
6965 if (s = '') then
6966 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6967 else
6968 Opt := StrToIntDef(s, 0);
6969 if Opt = 0 then
6970 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6972 // Close after map:
6973 s := Find_Param_Value(pars, '--close');
6974 if (s <> '') then
6975 gMapOnce := True;
6977 // Delete test map after play:
6978 s := Find_Param_Value(pars, '--testdelete');
6979 if (s <> '') then
6980 begin
6981 gMapToDelete := MapsDir + map;
6982 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6983 Halt(1);
6984 end;
6986 // Delete temporary WAD after play:
6987 s := Find_Param_Value(pars, '--tempdelete');
6988 if (s <> '') then
6989 begin
6990 gMapToDelete := MapsDir + map;
6991 gTempDelete := True;
6992 end;
6994 // Number of players:
6995 s := Find_Param_Value(pars, '-pl');
6996 if (s = '') then
6997 n := 1
6998 else
6999 n := StrToIntDef(s, 1);
7001 // Start:
7002 s := Find_Param_Value(pars, '-port');
7003 if (s = '') or not TryStrToInt(s, Port) then
7004 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7005 else
7006 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7007 end;
7009 // Execute script when game loads:
7010 s := Find_Param_Value(pars, '-exec');
7011 if s <> '' then
7012 begin
7013 if not isWadPath(s) then
7014 s := GameDir + '/' + s;
7016 {$I-}
7017 AssignFile(F, s);
7018 Reset(F);
7019 if IOResult <> 0 then
7020 begin
7021 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7022 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7023 CloseFile(F);
7024 Exit;
7025 end;
7026 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
7027 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7029 while not EOF(F) do
7030 begin
7031 ReadLn(F, s);
7032 if IOResult <> 0 then
7033 begin
7034 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7035 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7036 CloseFile(F);
7037 Exit;
7038 end;
7039 if Pos('#', s) <> 1 then // script comment
7040 g_Console_Process(s, True);
7041 end;
7043 CloseFile(F);
7044 {$I+}
7045 end;
7047 SetLength(pars, 0);
7048 end;
7050 begin
7051 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7052 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7053 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7054 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7056 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7057 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7058 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7059 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7061 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7062 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7064 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7065 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7067 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7069 conRegVar('dbg_scale', @g_dbg_scale, 0.01, 5.0, 'experimental deBUG scale mode', '', true);
7070 end.