DEADSOFTWARE

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