DEADSOFTWARE

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