DEADSOFTWARE

bye-bye, bineditor, we won't miss you
[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 SysUtils, Classes,
23 MAPDEF,
24 g_basic, g_player, e_graphics, g_res_downloader,
25 g_sound, g_gui, wadreader, md5, xprofiler;
27 type
28 TGameSettings = record
29 GameType: Byte;
30 GameMode: Byte;
31 TimeLimit: Word;
32 GoalLimit: Word;
33 WarmupTime: Word;
34 MaxLives: Byte;
35 Options: LongWord;
36 WAD: String;
37 end;
39 TGameEvent = record
40 Name: String;
41 Command: String;
42 end;
44 TDelayedEvent = record
45 Pending: Boolean;
46 Time: LongWord;
47 DEType: Byte;
48 DENum: Integer;
49 DEStr: String;
50 end;
52 TPlayerSettings = record
53 Name: String;
54 Model: String;
55 Color: TRGB;
56 Team: Byte;
57 end;
59 TMegaWADInfo = record
60 Name: String;
61 Description: String;
62 Author: String;
63 Pic: String;
64 end;
66 THearPoint = record
67 Active: Boolean;
68 Coords: TDFPoint;
69 end;
71 function g_Game_IsNet(): Boolean;
72 function g_Game_IsServer(): Boolean;
73 function g_Game_IsClient(): Boolean;
74 procedure g_Game_Init();
75 procedure g_Game_Free (freeTextures: Boolean=true);
76 procedure g_Game_LoadData();
77 procedure g_Game_FreeData();
78 procedure g_Game_Update();
79 procedure g_Game_Draw();
80 procedure g_Game_Quit();
81 procedure g_Game_SetupScreenSize();
82 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
83 function g_Game_ModeToText(Mode: Byte): string;
84 function g_Game_TextToMode(Mode: string): Byte;
85 procedure g_Game_ExecuteEvent(Name: String);
86 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
87 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
88 procedure g_Game_RemovePlayer();
89 procedure g_Game_Spectate();
90 procedure g_Game_SpectateCenterView();
91 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
92 procedure g_Game_StartCustom(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte);
93 procedure g_Game_StartServer(Map: String; GameMode: Byte; TimeLimit, GoalLimit: Word; MaxLives: Byte; Options: LongWord; nPlayers: Byte; IPAddr: LongWord; Port: Word);
94 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
95 procedure g_Game_Restart();
96 procedure g_Game_RestartLevel();
97 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
98 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
99 procedure g_Game_SaveOptions();
100 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
101 procedure g_Game_ChangeMap(const MapPath: String);
102 procedure g_Game_ExitLevel(const Map: AnsiString);
103 function g_Game_GetFirstMap(WAD: String): String;
104 function g_Game_GetNextMap(): String;
105 procedure g_Game_NextLevel();
106 procedure g_Game_Pause(Enable: Boolean);
107 procedure g_Game_InGameMenu(Show: Boolean);
108 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
109 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
110 procedure g_Game_Message(Msg: String; Time: Word);
111 procedure g_Game_LoadMapList(FileName: String);
112 procedure g_Game_PauseAllSounds(Enable: Boolean);
113 procedure g_Game_StopAllSounds(all: Boolean);
114 procedure g_Game_UpdateTriggerSounds();
115 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
116 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
117 procedure g_Game_Announce_KillCombo(Param: Integer);
118 procedure g_Game_StartVote(Command, Initiator: string);
119 procedure g_Game_CheckVote;
120 procedure g_TakeScreenShot();
121 procedure g_FatalError(Text: String);
122 procedure g_SimpleError(Text: String);
123 function g_Game_IsTestMap(): Boolean;
124 procedure g_Game_DeleteTestMap();
125 procedure GameCVars(P: SArray);
126 procedure GameCommands(P: SArray);
127 procedure GameCheats(P: SArray);
128 procedure DebugCommands(P: SArray);
129 procedure g_Game_Process_Params;
130 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
131 procedure g_Game_StepLoading();
132 procedure g_Game_ClearLoading();
133 procedure g_Game_SetDebugMode();
134 procedure DrawLoadingStat();
136 { procedure SetWinPause(Enable: Boolean); }
138 const
139 GAME_TICK = 28;
141 LOADING_SHOW_STEP = 100;
142 LOADING_INTERLINE = 20;
144 GT_NONE = 0;
145 GT_SINGLE = 1;
146 GT_CUSTOM = 2;
147 GT_SERVER = 3;
148 GT_CLIENT = 4;
150 GM_NONE = 0;
151 GM_DM = 1;
152 GM_TDM = 2;
153 GM_CTF = 3;
154 GM_COOP = 4;
155 GM_SINGLE = 5;
157 MESSAGE_DIKEY = WM_USER + 1;
159 EXIT_QUIT = 1;
160 EXIT_SIMPLE = 2;
161 EXIT_RESTART = 3;
162 EXIT_ENDLEVELSINGLE = 4;
163 EXIT_ENDLEVELCUSTOM = 5;
165 GAME_OPTION_RESERVED = 1;
166 GAME_OPTION_TEAMDAMAGE = 2;
167 GAME_OPTION_ALLOWEXIT = 4;
168 GAME_OPTION_WEAPONSTAY = 8;
169 GAME_OPTION_MONSTERS = 16;
170 GAME_OPTION_BOTVSPLAYER = 32;
171 GAME_OPTION_BOTVSMONSTER = 64;
173 STATE_NONE = 0;
174 STATE_MENU = 1;
175 STATE_FOLD = 2;
176 STATE_INTERCUSTOM = 3;
177 STATE_INTERSINGLE = 4;
178 STATE_INTERTEXT = 5;
179 STATE_INTERPIC = 6;
180 STATE_ENDPIC = 7;
181 STATE_SLIST = 8;
183 LMS_RESPAWN_NONE = 0;
184 LMS_RESPAWN_WARMUP = 1;
185 LMS_RESPAWN_FINAL = 2;
187 SPECT_NONE = 0;
188 SPECT_STATS = 1;
189 SPECT_MAPVIEW = 2;
190 SPECT_PLAYERS = 3;
192 DE_GLOBEVENT = 0;
193 DE_BFGHIT = 1;
194 DE_KILLCOMBO = 2;
196 ANNOUNCE_NONE = 0;
197 ANNOUNCE_ME = 1;
198 ANNOUNCE_MEPLUS = 2;
199 ANNOUNCE_ALL = 3;
201 CONFIG_FILENAME = 'Doom2DF.cfg';
202 LOG_FILENAME = 'Doom2DF.log';
204 TEST_MAP_NAME = '$$$_TEST_$$$';
206 STD_PLAYER_MODEL = 'Doomer';
208 var
209 gStdFont: DWORD;
210 gGameSettings: TGameSettings;
211 gPlayer1Settings: TPlayerSettings;
212 gPlayer2Settings: TPlayerSettings;
213 gGameOn: Boolean;
214 gPlayerScreenSize: TDFPoint;
215 gPlayer1ScreenCoord: TDFPoint;
216 gPlayer2ScreenCoord: TDFPoint;
217 gPlayer1: TPlayer = nil;
218 gPlayer2: TPlayer = nil;
219 gPlayerDrawn: TPlayer = nil;
220 gTime: LongWord;
221 gSwitchGameMode: Byte = GM_DM;
222 gHearPoint1, gHearPoint2: THearPoint;
223 gSoundEffectsDF: Boolean = False;
224 gSoundTriggerTime: Word = 0;
225 gAnnouncer: Byte = ANNOUNCE_NONE;
226 goodsnd: array[0..3] of TPlayableSound;
227 killsnd: array[0..3] of TPlayableSound;
228 gDefInterTime: ShortInt = -1;
229 gInterEndTime: LongWord = 0;
230 gInterTime: LongWord = 0;
231 gServInterTime: Byte = 0;
232 gGameStartTime: LongWord = 0;
233 gTotalMonsters: Integer = 0;
234 gPause: Boolean;
235 gShowTime: Boolean = True;
236 gShowFPS: Boolean = False;
237 gShowGoals: Boolean = True;
238 gShowStat: Boolean = True;
239 gShowKillMsg: Boolean = True;
240 gShowLives: Boolean = True;
241 gShowPing: Boolean = False;
242 gShowMap: Boolean = False;
243 gExit: Byte = 0;
244 gState: Byte = STATE_NONE;
245 sX, sY: Integer;
246 sWidth, sHeight: Word;
247 gSpectMode: Byte = SPECT_NONE;
248 gSpectHUD: Boolean = True;
249 gSpectKeyPress: Boolean = False;
250 gSpectX: Integer = 0;
251 gSpectY: Integer = 0;
252 gSpectStep: Byte = 8;
253 gSpectViewTwo: Boolean = False;
254 gSpectPID1: Integer = -1;
255 gSpectPID2: Integer = -1;
256 gMusic: TMusic = nil;
257 gLoadGameMode: Boolean;
258 gCheats: Boolean = False;
259 gMapOnce: Boolean = False;
260 gMapToDelete: String;
261 gTempDelete: Boolean = False;
262 gLastMap: Boolean = False;
263 gWinPosX, gWinPosY: Integer;
264 gWinSizeX, gWinSizeY: Integer;
265 gWinFrameX, gWinFrameY, gWinCaption: Integer;
266 gWinActive: Boolean = True; // by default window is active, lol
267 gResolutionChange: Boolean = False;
268 gRC_Width, gRC_Height: Word;
269 gRC_FullScreen, gRC_Maximized: Boolean;
270 gLanguageChange: Boolean = False;
271 gDebugMode: Boolean = False;
272 g_debug_Sounds: Boolean = False;
273 g_debug_Frames: Boolean = False;
274 g_debug_WinMsgs: Boolean = False;
275 g_debug_MonsterOff: Boolean = False;
276 g_debug_BotAIOff: Byte = 0;
277 g_debug_HealthBar: Boolean = False;
278 g_Debug_Player: Boolean = False;
279 gCoopMonstersKilled: Word = 0;
280 gCoopSecretsFound: Word = 0;
281 gCoopTotalMonstersKilled: Word = 0;
282 gCoopTotalSecretsFound: Word = 0;
283 gCoopTotalMonsters: Word = 0;
284 gCoopTotalSecrets: Word = 0;
285 gStatsOff: Boolean = False;
286 gStatsPressed: Boolean = False;
287 gExitByTrigger: Boolean = False;
288 gNextMap: String = '';
289 gLMSRespawn: Byte = LMS_RESPAWN_NONE;
290 gLMSRespawnTime: Cardinal = 0;
291 gLMSSoftSpawn: Boolean = False;
292 gMissionFailed: Boolean = False;
293 gVoteInProgress: Boolean = False;
294 gVotePassed: Boolean = False;
295 gVoteCommand: string = '';
296 gVoteTimer: Cardinal = 0;
297 gVoteCmdTimer: Cardinal = 0;
298 gVoteCount: Integer = 0;
299 gVoteTimeout: Cardinal = 30;
300 gVoted: Boolean = False;
301 gVotesEnabled: Boolean = True;
302 gEvents: Array of TGameEvent;
303 gDelayedEvents: Array of TDelayedEvent;
305 // move button values:
306 // bits 0-1: l/r state:
307 // 0: neither left, nor right pressed
308 // 1: left pressed
309 // 2: right pressed
310 // bits 4-5: l/r state when strafe was pressed
311 P1MoveButton: Byte = 0;
312 P2MoveButton: Byte = 0;
314 g_profile_frame_update: Boolean = false;
315 g_profile_frame_draw: Boolean = false;
316 g_profile_collision: Boolean = false;
317 g_profile_los: Boolean = false;
318 g_profile_history_size: Integer = 1000;
320 g_rlayer_back: Boolean = true;
321 g_rlayer_step: Boolean = true;
322 g_rlayer_wall: Boolean = true;
323 g_rlayer_door: Boolean = true;
324 g_rlayer_acid1: Boolean = true;
325 g_rlayer_acid2: Boolean = true;
326 g_rlayer_water: Boolean = true;
327 g_rlayer_fore: Boolean = true;
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 e_texture, 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 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 * for each light:
2754 * glClear(GL_STENCIL_BUFFER_BIT);
2755 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2756 * draw shadow volume into stencil buffer
2757 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2758 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
2759 * turn off blending
2760 * draw color-less quad with light alpha (WARNING! don't touch color!)
2761 * glEnable(GL_BLEND);
2762 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
2763 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
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 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
2916 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
2917 glTranslatef(-sX, -sY, 0);
2918 end;
2920 // rendering mode
2921 ambColor := gCurrentMap['light_ambient'].rgba;
2922 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
2925 if hasAmbient then
2926 begin
2927 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
2928 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
2929 glClear(GL_COLOR_BUFFER_BIT);
2930 end;
2932 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
2935 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
2936 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
2937 drawOther('items', @g_Items_Draw);
2938 drawOther('weapons', @g_Weapon_Draw);
2939 drawOther('shells', @g_Player_DrawShells);
2940 drawOther('drawall', @g_Player_DrawAll);
2941 drawOther('corpses', @g_Player_DrawCorpses);
2942 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
2943 drawOther('monsters', @g_Monsters_Draw);
2944 drawOther('itemdrop', @g_Items_DrawDrop);
2945 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
2946 drawOther('gfx', @g_GFX_Draw);
2947 drawOther('flags', @g_Map_DrawFlags);
2948 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
2949 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
2950 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
2951 drawOther('dynlights', @renderDynLightsInternal);
2953 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
2954 begin
2955 renderAmbientQuad(hasAmbient, ambColor);
2956 end;
2958 doAmbient := true;
2959 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
2962 if g_debug_HealthBar then
2963 begin
2964 g_Monsters_DrawHealth();
2965 g_Player_DrawHealth();
2966 end;
2968 profileFrameDraw.mainEnd(); // map rendering
2969 end;
2972 procedure DrawMapView(x, y, w, h: Integer);
2974 var
2975 bx, by: Integer;
2976 begin
2977 glPushMatrix();
2979 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2980 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2982 sX := x;
2983 sY := y;
2984 sWidth := w;
2985 sHeight := h;
2987 fixViewportForScale();
2988 renderMapInternal(-bx, -by, true);
2990 glPopMatrix();
2991 end;
2994 procedure DrawPlayer(p: TPlayer);
2995 var
2996 px, py, a, b, c, d: Integer;
2997 //R: TRect;
2998 begin
2999 if (p = nil) or (p.FDummy) then
3000 begin
3001 glPushMatrix();
3002 g_Map_DrawBack(0, 0);
3003 glPopMatrix();
3004 Exit;
3005 end;
3007 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
3008 profileFrameDraw.mainBegin(g_profile_frame_draw);
3010 gPlayerDrawn := p;
3012 glPushMatrix();
3014 px := p.GameX + PLAYER_RECT_CX;
3015 py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft;
3017 if (g_dbg_scale = 1.0) then
3018 begin
3019 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
3020 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
3022 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
3023 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
3025 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
3026 else if (gMapInfo.Width < gPlayerScreenSize.X) then
3027 begin
3028 // hcenter
3029 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
3030 end;
3032 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
3033 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
3034 begin
3035 // vcenter
3036 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
3037 end;
3038 end
3039 else
3040 begin
3041 // scaled, ignore level bounds
3042 a := -px+(gPlayerScreenSize.X div 2);
3043 b := -py+(gPlayerScreenSize.Y div 2);
3044 end;
3046 if p.IncCam <> 0 then
3047 begin
3048 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
3049 begin
3050 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
3051 begin
3052 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
3053 end;
3054 end;
3056 if py < gPlayerScreenSize.Y div 2 then
3057 begin
3058 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
3059 begin
3060 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
3061 end;
3062 end;
3064 if p.IncCam < 0 then
3065 begin
3066 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
3067 end;
3069 if p.IncCam > 0 then
3070 begin
3071 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
3072 end;
3073 end;
3075 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
3076 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
3077 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
3079 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
3080 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
3081 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
3083 sX := -a;
3084 sY := -(b+p.IncCam);
3085 sWidth := gPlayerScreenSize.X;
3086 sHeight := gPlayerScreenSize.Y;
3088 //glTranslatef(a, b+p.IncCam, 0);
3090 if (p = gPlayer1) then g_Holmes_plrViewSize(sWidth, sHeight);
3092 fixViewportForScale();
3093 p.viewPortX := sX;
3094 p.viewPortY := sY;
3095 p.viewPortW := sWidth;
3096 p.viewPortH := sHeight;
3098 if (p = gPlayer1) then g_Holmes_plrViewPos(sX, sY);
3100 renderMapInternal(-c, -d, true);
3102 if p.FSpectator then
3103 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
3104 p.GameY + PLAYER_RECT_CY - 4,
3105 'X', gStdFont, 255, 255, 255, 1, True);
3107 for a := 0 to High(gCollideMap) do
3108 for b := 0 to High(gCollideMap[a]) do
3109 begin
3110 d := 0;
3111 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
3112 d := d + 1;
3113 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
3114 d := d + 2;
3116 case d of
3117 1: e_DrawPoint(1, b, a, 200, 200, 200);
3118 2: e_DrawPoint(1, b, a, 64, 64, 255);
3119 3: e_DrawPoint(1, b, a, 255, 0, 255);
3120 end;
3121 end;
3124 glPopMatrix();
3126 p.DrawPain();
3127 p.DrawPickup();
3128 p.DrawRulez();
3129 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3130 if g_Debug_Player then
3131 g_Player_DrawDebug(p);
3132 p.DrawGUI();
3133 end;
3135 procedure drawProfilers ();
3136 var
3137 px: Integer = -1;
3138 py: Integer = -1;
3139 begin
3140 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
3141 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
3142 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
3143 end;
3145 procedure g_Game_Draw();
3146 var
3147 ID: DWORD;
3148 w, h: Word;
3149 ww, hh: Byte;
3150 Time: Int64;
3151 back: string;
3152 plView1, plView2: TPlayer;
3153 Split: Boolean;
3154 begin
3155 if gExit = EXIT_QUIT then Exit;
3157 Time := GetTimer() {div 1000};
3158 FPSCounter := FPSCounter+1;
3159 if Time - FPSTime >= 1000 then
3160 begin
3161 FPS := FPSCounter;
3162 FPSCounter := 0;
3163 FPSTime := Time;
3164 end;
3166 if gGameOn or (gState = STATE_FOLD) then
3167 begin
3168 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3169 begin
3170 gSpectMode := SPECT_NONE;
3171 if not gRevertPlayers then
3172 begin
3173 plView1 := gPlayer1;
3174 plView2 := gPlayer2;
3175 end
3176 else
3177 begin
3178 plView1 := gPlayer2;
3179 plView2 := gPlayer1;
3180 end;
3181 end
3182 else
3183 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3184 begin
3185 gSpectMode := SPECT_NONE;
3186 if gPlayer2 = nil then
3187 plView1 := gPlayer1
3188 else
3189 plView1 := gPlayer2;
3190 plView2 := nil;
3191 end
3192 else
3193 begin
3194 plView1 := nil;
3195 plView2 := nil;
3196 end;
3198 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3199 gSpectMode := SPECT_STATS;
3201 if gSpectMode = SPECT_PLAYERS then
3202 if gPlayers <> nil then
3203 begin
3204 plView1 := GetActivePlayer_ByID(gSpectPID1);
3205 if plView1 = nil then
3206 begin
3207 gSpectPID1 := GetActivePlayerID_Next();
3208 plView1 := GetActivePlayer_ByID(gSpectPID1);
3209 end;
3210 if gSpectViewTwo then
3211 begin
3212 plView2 := GetActivePlayer_ByID(gSpectPID2);
3213 if plView2 = nil then
3214 begin
3215 gSpectPID2 := GetActivePlayerID_Next();
3216 plView2 := GetActivePlayer_ByID(gSpectPID2);
3217 end;
3218 end;
3219 end;
3221 if gSpectMode = SPECT_MAPVIEW then
3222 begin
3223 // Ðåæèì ïðîñìîòðà êàðòû
3224 Split := False;
3225 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3226 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3227 gHearPoint1.Active := True;
3228 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3229 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3230 gHearPoint2.Active := False;
3231 end
3232 else
3233 begin
3234 Split := (plView1 <> nil) and (plView2 <> nil);
3236 // Òî÷êè ñëóõà èãðîêîâ
3237 if plView1 <> nil then
3238 begin
3239 gHearPoint1.Active := True;
3240 gHearPoint1.Coords.X := plView1.GameX;
3241 gHearPoint1.Coords.Y := plView1.GameY;
3242 end else
3243 gHearPoint1.Active := False;
3244 if plView2 <> nil then
3245 begin
3246 gHearPoint2.Active := True;
3247 gHearPoint2.Coords.X := plView2.GameX;
3248 gHearPoint2.Coords.Y := plView2.GameY;
3249 end else
3250 gHearPoint2.Active := False;
3252 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3253 gPlayerScreenSize.X := gScreenWidth-196;
3254 if Split then
3255 begin
3256 gPlayerScreenSize.Y := gScreenHeight div 2;
3257 if gScreenHeight mod 2 = 0 then
3258 Dec(gPlayerScreenSize.Y);
3259 end
3260 else
3261 gPlayerScreenSize.Y := gScreenHeight;
3263 if Split then
3264 if gScreenHeight mod 2 = 0 then
3265 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3266 else
3267 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3269 DrawPlayer(plView1);
3270 gPlayer1ScreenCoord.X := sX;
3271 gPlayer1ScreenCoord.Y := sY;
3273 if Split then
3274 begin
3275 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3277 DrawPlayer(plView2);
3278 gPlayer2ScreenCoord.X := sX;
3279 gPlayer2ScreenCoord.Y := sY;
3280 end;
3282 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3284 if Split then
3285 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3286 end;
3288 if MessageText <> '' then
3289 begin
3290 w := 0;
3291 h := 0;
3292 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3293 if Split then
3294 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3295 (gScreenHeight div 2)-(h div 2), MessageText)
3296 else
3297 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3298 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3299 end;
3301 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3303 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3304 begin
3305 // Draw spectator GUI
3306 ww := 0;
3307 hh := 0;
3308 e_TextureFontGetSize(gStdFont, ww, hh);
3309 case gSpectMode of
3310 SPECT_STATS:
3311 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3312 SPECT_MAPVIEW:
3313 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3314 SPECT_PLAYERS:
3315 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3316 end;
3317 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3318 if gSpectMode = SPECT_MAPVIEW then
3319 begin
3320 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3321 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3322 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3323 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3324 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3325 end;
3326 if gSpectMode = SPECT_PLAYERS then
3327 begin
3328 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3329 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3330 if gSpectViewTwo then
3331 begin
3332 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3333 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3334 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3335 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3336 end
3337 else
3338 begin
3339 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3340 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3341 end;
3342 end;
3343 end;
3344 end;
3346 if gPause and gGameOn and (g_ActiveWindow = nil) then
3347 begin
3348 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3349 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3351 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3352 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3353 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3354 end;
3356 if not gGameOn then
3357 begin
3358 if (gState = STATE_MENU) then
3359 begin
3360 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then
3361 begin
3362 if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3363 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3364 end;
3365 // F3 at menu will show game loading dialog
3366 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
3367 if (g_ActiveWindow <> nil) then
3368 begin
3369 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3370 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3371 end
3372 else
3373 begin
3374 // F3 at titlepic will show game loading dialog
3375 if e_KeyPressed(IK_F3) then
3376 begin
3377 g_Menu_Show_LoadMenu(true);
3378 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3379 end;
3380 end;
3381 end;
3383 if gState = STATE_FOLD then
3384 begin
3385 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3386 end;
3388 if gState = STATE_INTERCUSTOM then
3389 begin
3390 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3391 begin
3392 back := 'TEXTURE_endpic';
3393 if not g_Texture_Get(back, ID) then
3394 back := _lc[I_TEXTURE_ENDPIC];
3395 end
3396 else
3397 back := 'INTER';
3399 if g_Texture_Get(back, ID) then
3400 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3401 else
3402 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3404 DrawCustomStat();
3406 if g_ActiveWindow <> nil then
3407 begin
3408 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3409 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3410 end;
3411 end;
3413 if gState = STATE_INTERSINGLE then
3414 begin
3415 if EndingGameCounter > 0 then
3416 begin
3417 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3418 end
3419 else
3420 begin
3421 back := 'INTER';
3423 if g_Texture_Get(back, ID) then
3424 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3425 else
3426 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3428 DrawSingleStat();
3430 if g_ActiveWindow <> nil then
3431 begin
3432 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3433 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3434 end;
3435 end;
3436 end;
3438 if gState = STATE_ENDPIC then
3439 begin
3440 ID := DWORD(-1);
3441 if not g_Texture_Get('TEXTURE_endpic', ID) then
3442 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3444 if ID <> DWORD(-1) then
3445 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3446 else
3447 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3449 if g_ActiveWindow <> nil then
3450 begin
3451 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3452 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3453 end;
3454 end;
3456 if gState = STATE_SLIST then
3457 begin
3458 if g_Texture_Get('MENU_BACKGROUND', ID) then
3459 begin
3460 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3461 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3462 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3463 end;
3464 g_Serverlist_Draw(slCurrent);
3465 end;
3466 end;
3468 if g_ActiveWindow <> nil then
3469 begin
3470 if gGameOn then
3471 begin
3472 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3473 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3474 end;
3475 g_ActiveWindow.Draw();
3476 end;
3478 // draw inspector
3479 if (g_holmes_enabled) then g_Holmes_Draw();
3481 g_Console_Draw();
3483 if g_debug_Sounds and gGameOn then
3484 begin
3485 for w := 0 to High(e_SoundsArray) do
3486 for h := 0 to e_SoundsArray[w].nRefs do
3487 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3488 end;
3490 if gShowFPS then
3491 begin
3492 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3493 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3494 end;
3496 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3497 drawTime(gScreenWidth-72, gScreenHeight-16);
3499 if gGameOn then drawProfilers();
3501 g_Holmes_DrawUI();
3502 end;
3504 procedure g_Game_Quit();
3505 begin
3506 g_Game_StopAllSounds(True);
3507 gMusic.Free();
3508 g_Game_SaveOptions();
3509 g_Game_FreeData();
3510 g_PlayerModel_FreeData();
3511 g_Texture_DeleteAll();
3512 g_Frames_DeleteAll();
3513 g_Menu_Free();
3515 if NetInitDone then g_Net_Free;
3517 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3518 if gMapToDelete <> '' then
3519 g_Game_DeleteTestMap();
3521 gExit := EXIT_QUIT;
3522 PushExitEvent();
3523 end;
3525 procedure g_FatalError(Text: String);
3526 begin
3527 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3528 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3530 gExit := EXIT_SIMPLE;
3531 end;
3533 procedure g_SimpleError(Text: String);
3534 begin
3535 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3536 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3537 end;
3539 procedure g_Game_SetupScreenSize();
3540 const
3541 RES_FACTOR = 4.0 / 3.0;
3542 var
3543 s: Single;
3544 rf: Single;
3545 bw, bh: Word;
3546 begin
3547 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3548 gPlayerScreenSize.X := gScreenWidth-196;
3549 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3550 gPlayerScreenSize.Y := gScreenHeight div 2
3551 else
3552 gPlayerScreenSize.Y := gScreenHeight;
3554 // Ðàçìåð çàäíåãî ïëàíà:
3555 if BackID <> DWORD(-1) then
3556 begin
3557 s := SKY_STRETCH;
3558 if (gScreenWidth*s > gMapInfo.Width) or
3559 (gScreenHeight*s > gMapInfo.Height) then
3560 begin
3561 gBackSize.X := gScreenWidth;
3562 gBackSize.Y := gScreenHeight;
3563 end
3564 else
3565 begin
3566 e_GetTextureSize(BackID, @bw, @bh);
3567 rf := Single(bw) / Single(bh);
3568 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3569 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3570 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3571 if (s < 1.0) then s := 1.0;
3572 gBackSize.X := Round(bw*s);
3573 gBackSize.Y := Round(bh*s);
3574 end;
3575 end;
3576 end;
3578 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3579 begin
3580 g_Window_SetSize(newWidth, newHeight, nowFull);
3581 end;
3583 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3584 begin
3585 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3586 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3587 Exit;
3588 if gPlayer1 = nil then
3589 begin
3590 if g_Game_IsClient then
3591 begin
3592 if NetPlrUID1 > -1 then
3593 begin
3594 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3595 gPlayer1 := g_Player_Get(NetPlrUID1);
3596 end;
3597 Exit;
3598 end;
3600 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3601 Team := gPlayer1Settings.Team;
3603 // Ñîçäàíèå ïåðâîãî èãðîêà:
3604 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3605 gPlayer1Settings.Color,
3606 Team, False));
3607 if gPlayer1 = nil then
3608 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3609 else
3610 begin
3611 gPlayer1.Name := gPlayer1Settings.Name;
3612 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3613 if g_Game_IsServer and g_Game_IsNet then
3614 MH_SEND_PlayerCreate(gPlayer1.UID);
3615 gPlayer1.Respawn(False, True);
3617 if g_Game_IsNet and NetUseMaster then
3618 g_Net_Slist_Update;
3619 end;
3621 Exit;
3622 end;
3623 if gPlayer2 = nil then
3624 begin
3625 if g_Game_IsClient then
3626 begin
3627 if NetPlrUID2 > -1 then
3628 gPlayer2 := g_Player_Get(NetPlrUID2);
3629 Exit;
3630 end;
3632 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3633 Team := gPlayer2Settings.Team;
3635 // Ñîçäàíèå âòîðîãî èãðîêà:
3636 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3637 gPlayer2Settings.Color,
3638 Team, False));
3639 if gPlayer2 = nil then
3640 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3641 else
3642 begin
3643 gPlayer2.Name := gPlayer2Settings.Name;
3644 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3645 if g_Game_IsServer and g_Game_IsNet then
3646 MH_SEND_PlayerCreate(gPlayer2.UID);
3647 gPlayer2.Respawn(False, True);
3649 if g_Game_IsNet and NetUseMaster then
3650 g_Net_Slist_Update;
3651 end;
3653 Exit;
3654 end;
3655 end;
3657 procedure g_Game_RemovePlayer();
3658 var
3659 Pl: TPlayer;
3660 begin
3661 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3662 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3663 Exit;
3664 Pl := gPlayer2;
3665 if Pl <> nil then
3666 begin
3667 if g_Game_IsServer then
3668 begin
3669 Pl.Lives := 0;
3670 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3671 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3672 g_Player_Remove(Pl.UID);
3674 if g_Game_IsNet and NetUseMaster then
3675 g_Net_Slist_Update;
3676 end else
3677 gPlayer2 := nil;
3678 Exit;
3679 end;
3680 Pl := gPlayer1;
3681 if Pl <> nil then
3682 begin
3683 if g_Game_IsServer then
3684 begin
3685 Pl.Lives := 0;
3686 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3687 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3688 g_Player_Remove(Pl.UID);
3690 if g_Game_IsNet and NetUseMaster then
3691 g_Net_Slist_Update;
3692 end else
3693 begin
3694 gPlayer1 := nil;
3695 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3696 end;
3697 Exit;
3698 end;
3699 end;
3701 procedure g_Game_Spectate();
3702 begin
3703 g_Game_RemovePlayer();
3704 if gPlayer1 <> nil then
3705 g_Game_RemovePlayer();
3706 end;
3708 procedure g_Game_SpectateCenterView();
3709 begin
3710 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3711 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3712 end;
3714 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3715 var
3716 i, nPl: Integer;
3717 begin
3718 g_Game_Free();
3720 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3722 g_Game_ClearLoading();
3724 // Íàñòðîéêè èãðû:
3725 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3726 gAimLine := False;
3727 gShowMap := False;
3728 gGameSettings.GameType := GT_SINGLE;
3729 gGameSettings.MaxLives := 0;
3730 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3731 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3732 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3733 gSwitchGameMode := GM_SINGLE;
3735 g_Game_ExecuteEvent('ongamestart');
3737 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3738 g_Game_SetupScreenSize();
3740 // Ñîçäàíèå ïåðâîãî èãðîêà:
3741 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3742 gPlayer1Settings.Color,
3743 gPlayer1Settings.Team, False));
3744 if gPlayer1 = nil then
3745 begin
3746 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3747 Exit;
3748 end;
3750 gPlayer1.Name := gPlayer1Settings.Name;
3751 nPl := 1;
3753 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3754 if TwoPlayers then
3755 begin
3756 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3757 gPlayer2Settings.Color,
3758 gPlayer2Settings.Team, False));
3759 if gPlayer2 = nil then
3760 begin
3761 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3762 Exit;
3763 end;
3765 gPlayer2.Name := gPlayer2Settings.Name;
3766 Inc(nPl);
3767 end;
3769 // Çàãðóçêà è çàïóñê êàðòû:
3770 if not g_Game_StartMap(MAP, True) then
3771 begin
3772 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3773 Exit;
3774 end;
3776 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3777 g_Player_Init();
3779 // Ñîçäàåì áîòîâ:
3780 for i := nPl+1 to nPlayers do
3781 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3782 end;
3784 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3785 TimeLimit, GoalLimit: Word;
3786 MaxLives: Byte;
3787 Options: LongWord; nPlayers: Byte);
3788 var
3789 i, nPl: Integer;
3790 begin
3791 g_Game_Free();
3793 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3795 g_Game_ClearLoading();
3797 // Íàñòðîéêè èãðû:
3798 gGameSettings.GameType := GT_CUSTOM;
3799 gGameSettings.GameMode := GameMode;
3800 gSwitchGameMode := GameMode;
3801 gGameSettings.TimeLimit := TimeLimit;
3802 gGameSettings.GoalLimit := GoalLimit;
3803 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3804 gGameSettings.Options := Options;
3806 gCoopTotalMonstersKilled := 0;
3807 gCoopTotalSecretsFound := 0;
3808 gCoopTotalMonsters := 0;
3809 gCoopTotalSecrets := 0;
3810 gAimLine := False;
3811 gShowMap := False;
3813 g_Game_ExecuteEvent('ongamestart');
3815 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3816 g_Game_SetupScreenSize();
3818 // Ðåæèì íàáëþäàòåëÿ:
3819 if nPlayers = 0 then
3820 begin
3821 gPlayer1 := nil;
3822 gPlayer2 := nil;
3823 end;
3825 nPl := 0;
3826 if nPlayers >= 1 then
3827 begin
3828 // Ñîçäàíèå ïåðâîãî èãðîêà:
3829 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3830 gPlayer1Settings.Color,
3831 gPlayer1Settings.Team, False));
3832 if gPlayer1 = nil then
3833 begin
3834 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3835 Exit;
3836 end;
3838 gPlayer1.Name := gPlayer1Settings.Name;
3839 Inc(nPl);
3840 end;
3842 if nPlayers >= 2 then
3843 begin
3844 // Ñîçäàíèå âòîðîãî èãðîêà:
3845 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3846 gPlayer2Settings.Color,
3847 gPlayer2Settings.Team, False));
3848 if gPlayer2 = nil then
3849 begin
3850 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3851 Exit;
3852 end;
3854 gPlayer2.Name := gPlayer2Settings.Name;
3855 Inc(nPl);
3856 end;
3858 // Çàãðóçêà è çàïóñê êàðòû:
3859 if not g_Game_StartMap(Map, True) then
3860 begin
3861 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3862 Exit;
3863 end;
3865 // Íåò òî÷åê ïîÿâëåíèÿ:
3866 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3867 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3868 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3869 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3870 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3871 begin
3872 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3873 Exit;
3874 end;
3876 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3877 g_Player_Init();
3879 // Ñîçäàåì áîòîâ:
3880 for i := nPl+1 to nPlayers do
3881 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3882 end;
3884 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3885 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3886 Options: LongWord; nPlayers: Byte;
3887 IPAddr: LongWord; Port: Word);
3888 begin
3889 g_Game_Free();
3891 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3893 g_Game_ClearLoading();
3895 // Íàñòðîéêè èãðû:
3896 gGameSettings.GameType := GT_SERVER;
3897 gGameSettings.GameMode := GameMode;
3898 gSwitchGameMode := GameMode;
3899 gGameSettings.TimeLimit := TimeLimit;
3900 gGameSettings.GoalLimit := GoalLimit;
3901 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3902 gGameSettings.Options := Options;
3904 gCoopTotalMonstersKilled := 0;
3905 gCoopTotalSecretsFound := 0;
3906 gCoopTotalMonsters := 0;
3907 gCoopTotalSecrets := 0;
3908 gAimLine := False;
3909 gShowMap := False;
3911 g_Game_ExecuteEvent('ongamestart');
3913 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3914 g_Game_SetupScreenSize();
3916 // Ðåæèì íàáëþäàòåëÿ:
3917 if nPlayers = 0 then
3918 begin
3919 gPlayer1 := nil;
3920 gPlayer2 := nil;
3921 end;
3923 if nPlayers >= 1 then
3924 begin
3925 // Ñîçäàíèå ïåðâîãî èãðîêà:
3926 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3927 gPlayer1Settings.Color,
3928 gPlayer1Settings.Team, False));
3929 if gPlayer1 = nil then
3930 begin
3931 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3932 Exit;
3933 end;
3935 gPlayer1.Name := gPlayer1Settings.Name;
3936 end;
3938 if nPlayers >= 2 then
3939 begin
3940 // Ñîçäàíèå âòîðîãî èãðîêà:
3941 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3942 gPlayer2Settings.Color,
3943 gPlayer2Settings.Team, False));
3944 if gPlayer2 = nil then
3945 begin
3946 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3947 Exit;
3948 end;
3950 gPlayer2.Name := gPlayer2Settings.Name;
3951 end;
3953 // Ñòàðòóåì ñåðâåð
3954 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3955 begin
3956 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3957 Exit;
3958 end;
3960 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3962 // Çàãðóçêà è çàïóñê êàðòû:
3963 if not g_Game_StartMap(Map, True) then
3964 begin
3965 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3966 Exit;
3967 end;
3969 // Íåò òî÷åê ïîÿâëåíèÿ:
3970 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3971 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3972 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3973 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3974 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3975 begin
3976 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3977 Exit;
3978 end;
3980 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3981 g_Player_Init();
3983 NetState := NET_STATE_GAME;
3984 end;
3986 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3987 var
3988 Map: String;
3989 WadName: string;
3990 Ptr: Pointer;
3991 T: Cardinal;
3992 MID: Byte;
3993 State: Byte;
3994 OuterLoop: Boolean;
3995 newResPath: string;
3996 InMsg: TMsg;
3997 begin
3998 g_Game_Free();
4000 State := 0;
4001 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
4002 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
4004 g_Game_ClearLoading();
4006 // Íàñòðîéêè èãðû:
4007 gGameSettings.GameType := GT_CLIENT;
4009 gCoopTotalMonstersKilled := 0;
4010 gCoopTotalSecretsFound := 0;
4011 gCoopTotalMonsters := 0;
4012 gCoopTotalSecrets := 0;
4013 gAimLine := False;
4014 gShowMap := False;
4016 g_Game_ExecuteEvent('ongamestart');
4018 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
4019 g_Game_SetupScreenSize();
4021 NetState := NET_STATE_AUTH;
4023 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
4024 // Ñòàðòóåì êëèåíò
4025 if not g_Net_Connect(Addr, Port) then
4026 begin
4027 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4028 NetState := NET_STATE_NONE;
4029 Exit;
4030 end;
4032 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
4033 MC_SEND_Info(PW);
4034 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
4036 OuterLoop := True;
4037 while OuterLoop do
4038 begin
4039 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
4040 begin
4041 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
4042 begin
4043 Ptr := NetEvent.packet^.data;
4044 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
4045 continue;
4047 MID := InMsg.ReadByte();
4049 if (MID = NET_MSG_INFO) and (State = 0) then
4050 begin
4051 NetMyID := InMsg.ReadByte();
4052 NetPlrUID1 := InMsg.ReadWord();
4054 WadName := InMsg.ReadString();
4055 Map := InMsg.ReadString();
4057 gWADHash := InMsg.ReadMD5();
4059 gGameSettings.GameMode := InMsg.ReadByte();
4060 gSwitchGameMode := gGameSettings.GameMode;
4061 gGameSettings.GoalLimit := InMsg.ReadWord();
4062 gGameSettings.TimeLimit := InMsg.ReadWord();
4063 gGameSettings.MaxLives := InMsg.ReadByte();
4064 gGameSettings.Options := InMsg.ReadLongWord();
4065 T := InMsg.ReadLongWord();
4067 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
4068 if newResPath = '' then
4069 begin
4070 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4071 newResPath := g_Res_DownloadWAD(WadName);
4072 if newResPath = '' then
4073 begin
4074 g_FatalError(_lc[I_NET_ERR_HASH]);
4075 enet_packet_destroy(NetEvent.packet);
4076 NetState := NET_STATE_NONE;
4077 Exit;
4078 end;
4079 end;
4080 newResPath := ExtractRelativePath(MapsDir, newResPath);
4082 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
4083 gPlayer1Settings.Color,
4084 gPlayer1Settings.Team, False));
4086 if gPlayer1 = nil then
4087 begin
4088 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
4090 enet_packet_destroy(NetEvent.packet);
4091 NetState := NET_STATE_NONE;
4092 Exit;
4093 end;
4095 gPlayer1.Name := gPlayer1Settings.Name;
4096 gPlayer1.UID := NetPlrUID1;
4097 gPlayer1.Reset(True);
4099 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
4100 begin
4101 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
4103 enet_packet_destroy(NetEvent.packet);
4104 NetState := NET_STATE_NONE;
4105 Exit;
4106 end;
4108 gTime := T;
4110 State := 1;
4111 OuterLoop := False;
4112 enet_packet_destroy(NetEvent.packet);
4113 break;
4114 end
4115 else
4116 enet_packet_destroy(NetEvent.packet);
4117 end
4118 else
4119 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
4120 begin
4121 State := 0;
4122 if (NetEvent.data <= NET_DISC_MAX) then
4123 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
4124 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
4125 OuterLoop := False;
4126 Break;
4127 end;
4128 end;
4130 ProcessLoading(true);
4132 e_PollInput();
4134 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
4135 begin
4136 State := 0;
4137 break;
4138 end;
4139 end;
4141 if State <> 1 then
4142 begin
4143 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
4144 NetState := NET_STATE_NONE;
4145 Exit;
4146 end;
4148 gLMSRespawn := LMS_RESPAWN_NONE;
4149 gLMSRespawnTime := 0;
4151 g_Player_Init();
4152 NetState := NET_STATE_GAME;
4153 MC_SEND_FullStateRequest;
4154 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
4155 end;
4157 procedure g_Game_SaveOptions();
4158 begin
4159 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4160 end;
4162 procedure g_Game_ChangeMap(const MapPath: String);
4163 var
4164 Force: Boolean;
4165 begin
4166 g_Game_ClearLoading();
4168 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4169 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4170 if gExitByTrigger then
4171 begin
4172 Force := False;
4173 gExitByTrigger := False;
4174 end;
4175 if not g_Game_StartMap(MapPath, Force) then
4176 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4177 end;
4179 procedure g_Game_Restart();
4180 var
4181 Map: string;
4182 begin
4183 if g_Game_IsClient then
4184 Exit;
4185 map := g_ExtractFileName(gMapInfo.Map);
4187 MessageTime := 0;
4188 gGameOn := False;
4189 g_Game_ClearLoading();
4190 g_Game_StartMap(Map, True, gCurrentMapFileName);
4191 end;
4193 function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
4194 var
4195 NewWAD, ResName: String;
4196 I: Integer;
4197 begin
4198 g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
4199 g_Player_RemoveAllCorpses();
4201 if (not g_Game_IsClient) and
4202 (gSwitchGameMode <> gGameSettings.GameMode) and
4203 (gGameSettings.GameMode <> GM_SINGLE) then
4204 begin
4205 if gSwitchGameMode = GM_CTF then
4206 gGameSettings.MaxLives := 0;
4207 gGameSettings.GameMode := gSwitchGameMode;
4208 Force := True;
4209 end else
4210 gSwitchGameMode := gGameSettings.GameMode;
4212 g_Player_ResetTeams();
4214 if isWadPath(Map) then
4215 begin
4216 NewWAD := g_ExtractWadName(Map);
4217 ResName := g_ExtractFileName(Map);
4218 if g_Game_IsServer then
4219 begin
4220 gWADHash := MD5File(MapsDir + NewWAD);
4221 g_Game_LoadWAD(NewWAD);
4222 end else
4223 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4224 g_Game_ClientWAD(NewWAD, gWADHash);
4225 end else
4226 ResName := Map;
4228 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4229 if Result then
4230 begin
4231 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4233 gState := STATE_NONE;
4234 g_ActiveWindow := nil;
4235 gGameOn := True;
4237 DisableCheats();
4238 ResetTimer();
4240 if gGameSettings.GameMode = GM_CTF then
4241 begin
4242 g_Map_ResetFlag(FLAG_RED);
4243 g_Map_ResetFlag(FLAG_BLUE);
4244 // CTF, à ôëàãîâ íåò:
4245 if not g_Map_HaveFlagPoints() then
4246 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4247 end;
4248 end
4249 else
4250 begin
4251 gState := STATE_MENU;
4252 gGameOn := False;
4253 end;
4255 gExit := 0;
4256 gPause := False;
4257 gTime := 0;
4258 NetTimeToUpdate := 1;
4259 NetTimeToReliable := 0;
4260 NetTimeToMaster := NetMasterRate;
4261 gLMSRespawn := LMS_RESPAWN_NONE;
4262 gLMSRespawnTime := 0;
4263 gMissionFailed := False;
4264 gNextMap := '';
4266 gCoopMonstersKilled := 0;
4267 gCoopSecretsFound := 0;
4269 gVoteInProgress := False;
4270 gVotePassed := False;
4271 gVoteCount := 0;
4272 gVoted := False;
4274 gStatsOff := False;
4276 if not gGameOn then Exit;
4278 g_Game_SpectateCenterView();
4280 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4281 begin
4282 gLMSRespawn := LMS_RESPAWN_WARMUP;
4283 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4284 gLMSSoftSpawn := True;
4285 if NetMode = NET_SERVER then
4286 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4287 else
4288 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4289 end;
4291 if NetMode = NET_SERVER then
4292 begin
4293 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4295 // Ìàñòåðñåðâåð
4296 if NetUseMaster then
4297 begin
4298 if (NetMHost = nil) or (NetMPeer = nil) then
4299 if not g_Net_Slist_Connect then
4300 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4302 g_Net_Slist_Update;
4303 end;
4305 if NetClients <> nil then
4306 for I := 0 to High(NetClients) do
4307 if NetClients[I].Used then
4308 begin
4309 NetClients[I].Voted := False;
4310 if NetClients[I].RequestedFullUpdate then
4311 begin
4312 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4313 NetClients[I].RequestedFullUpdate := False;
4314 end;
4315 end;
4317 g_Net_UnbanNonPermHosts();
4318 end;
4320 if gLastMap then
4321 begin
4322 gCoopTotalMonstersKilled := 0;
4323 gCoopTotalSecretsFound := 0;
4324 gCoopTotalMonsters := 0;
4325 gCoopTotalSecrets := 0;
4326 gLastMap := False;
4327 end;
4329 g_Game_ExecuteEvent('onmapstart');
4330 end;
4332 procedure SetFirstLevel();
4333 begin
4334 gNextMap := '';
4336 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4337 if MapList = nil then
4338 Exit;
4340 SortSArray(MapList);
4341 gNextMap := MapList[Low(MapList)];
4343 MapList := nil;
4344 end;
4346 procedure g_Game_ExitLevel(const Map: AnsiString);
4347 begin
4348 gNextMap := Map;
4350 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4351 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4352 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4353 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4355 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4356 if gGameSettings.GameType = GT_SINGLE then
4357 gExit := EXIT_ENDLEVELSINGLE
4358 else // Âûøëè â âûõîä â Ñâîåé èãðå
4359 begin
4360 gExit := EXIT_ENDLEVELCUSTOM;
4361 if gGameSettings.GameMode = GM_COOP then
4362 g_Player_RememberAll;
4364 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4365 begin
4366 gLastMap := True;
4367 if gGameSettings.GameMode = GM_COOP then
4368 gStatsOff := True;
4370 gStatsPressed := True;
4371 gNextMap := 'MAP01';
4373 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4374 g_Game_NextLevel;
4376 if g_Game_IsNet then
4377 begin
4378 MH_SEND_GameStats();
4379 MH_SEND_CoopStats();
4380 end;
4381 end;
4382 end;
4383 end;
4385 procedure g_Game_RestartLevel();
4386 var
4387 Map: string;
4388 begin
4389 if gGameSettings.GameMode = GM_SINGLE then
4390 begin
4391 g_Game_Restart();
4392 Exit;
4393 end;
4394 gExit := EXIT_ENDLEVELCUSTOM;
4395 Map := g_ExtractFileName(gMapInfo.Map);
4396 gNextMap := Map;
4397 end;
4399 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4400 var
4401 gWAD: String;
4402 begin
4403 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4404 Exit;
4405 if not g_Game_IsClient then
4406 Exit;
4407 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4408 if gWAD = '' then
4409 begin
4410 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4411 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4412 if gWAD = '' then
4413 begin
4414 g_Game_Free();
4415 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4416 Exit;
4417 end;
4418 end;
4419 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4420 g_Game_LoadWAD(NewWAD);
4421 end;
4423 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4424 var
4425 i, n, nb, nr: Integer;
4427 function monRespawn (mon: TMonster): Boolean;
4428 begin
4429 result := false; // don't stop
4430 if not mon.FNoRespawn then mon.Respawn();
4431 end;
4433 begin
4434 if not g_Game_IsServer then Exit;
4435 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4436 gLMSRespawn := LMS_RESPAWN_NONE;
4437 gLMSRespawnTime := 0;
4438 MessageTime := 0;
4440 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4441 begin
4442 gMissionFailed := True;
4443 g_Game_RestartLevel;
4444 Exit;
4445 end;
4447 n := 0; nb := 0; nr := 0;
4448 for i := Low(gPlayers) to High(gPlayers) do
4449 if (gPlayers[i] <> nil) and
4450 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4451 (gPlayers[i] is TBot)) then
4452 begin
4453 Inc(n);
4454 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4455 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4456 end;
4458 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4459 begin
4460 // wait a second until the fuckers finally decide to join
4461 gLMSRespawn := LMS_RESPAWN_WARMUP;
4462 gLMSRespawnTime := gTime + 1000;
4463 gLMSSoftSpawn := NoMapRestart;
4464 Exit;
4465 end;
4467 g_Player_RemoveAllCorpses;
4468 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4469 if g_Game_IsNet then
4470 MH_SEND_GameEvent(NET_EV_LMS_START);
4472 for i := Low(gPlayers) to High(gPlayers) do
4473 begin
4474 if gPlayers[i] = nil then continue;
4475 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4476 // don't touch normal spectators
4477 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4478 begin
4479 gPlayers[i].FNoRespawn := True;
4480 gPlayers[i].Lives := 0;
4481 if g_Game_IsNet then
4482 MH_SEND_PlayerStats(gPlayers[I].UID);
4483 continue;
4484 end;
4485 gPlayers[i].FNoRespawn := False;
4486 gPlayers[i].Lives := gGameSettings.MaxLives;
4487 gPlayers[i].Respawn(False, True);
4488 if gGameSettings.GameMode = GM_COOP then
4489 begin
4490 gPlayers[i].Frags := 0;
4491 gPlayers[i].RecallState;
4492 end;
4493 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4494 gPlayer1 := g_Player_Get(gLMSPID1);
4495 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4496 gPlayer2 := g_Player_Get(gLMSPID2);
4497 end;
4499 g_Items_RestartRound();
4502 g_Mons_ForEach(monRespawn);
4504 gLMSSoftSpawn := False;
4505 end;
4507 function g_Game_GetFirstMap(WAD: String): String;
4508 begin
4509 Result := '';
4511 MapList := g_Map_GetMapsList(WAD);
4512 if MapList = nil then
4513 Exit;
4515 SortSArray(MapList);
4516 Result := MapList[Low(MapList)];
4518 if not g_Map_Exist(WAD + ':\' + Result) then
4519 Result := '';
4521 MapList := nil;
4522 end;
4524 function g_Game_GetNextMap(): String;
4525 var
4526 I: Integer;
4527 Map: string;
4528 begin
4529 Result := '';
4531 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4532 if MapList = nil then
4533 Exit;
4535 Map := g_ExtractFileName(gMapInfo.Map);
4537 SortSArray(MapList);
4538 MapIndex := -255;
4539 for I := Low(MapList) to High(MapList) do
4540 if Map = MapList[I] then
4541 begin
4542 MapIndex := I;
4543 Break;
4544 end;
4546 if MapIndex <> -255 then
4547 begin
4548 if MapIndex = High(MapList) then
4549 Result := MapList[Low(MapList)]
4550 else
4551 Result := MapList[MapIndex + 1];
4553 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4554 end;
4556 MapList := nil;
4557 end;
4559 procedure g_Game_NextLevel();
4560 begin
4561 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4562 gExit := EXIT_ENDLEVELCUSTOM
4563 else
4564 begin
4565 gExit := EXIT_ENDLEVELSINGLE;
4566 Exit;
4567 end;
4569 if gNextMap <> '' then Exit;
4570 gNextMap := g_Game_GetNextMap();
4571 end;
4573 function g_Game_IsTestMap(): Boolean;
4574 begin
4575 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4576 end;
4578 procedure g_Game_DeleteTestMap();
4579 var
4580 a: Integer;
4581 //MapName: AnsiString;
4582 WadName: string;
4584 WAD: TWADFile;
4585 MapList: SArray;
4586 time: Integer;
4588 begin
4589 a := Pos('.wad:\', toLowerCase1251(gMapToDelete));
4590 if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete));
4591 if (a = 0) then exit;
4593 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû
4594 WadName := Copy(gMapToDelete, 1, a+3);
4595 Delete(gMapToDelete, 1, a+5);
4596 gMapToDelete := UpperCase(gMapToDelete);
4597 //MapName := '';
4598 //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4601 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4602 if MapName <> TEST_MAP_NAME then
4603 Exit;
4605 if not gTempDelete then
4606 begin
4607 time := g_GetFileTime(WadName);
4608 WAD := TWADFile.Create();
4610 // ×èòàåì Wad-ôàéë:
4611 if not WAD.ReadFile(WadName) then
4612 begin // Íåò òàêîãî WAD-ôàéëà
4613 WAD.Free();
4614 Exit;
4615 end;
4617 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4618 WAD.CreateImage();
4619 MapList := WAD.GetResourcesList('');
4621 if MapList <> nil then
4622 for a := 0 to High(MapList) do
4623 if MapList[a] = MapName then
4624 begin
4625 // Óäàëÿåì è ñîõðàíÿåì:
4626 WAD.RemoveResource('', MapName);
4627 WAD.SaveTo(WadName);
4628 Break;
4629 end;
4631 WAD.Free();
4632 g_SetFileTime(WadName, time);
4633 end else
4635 if gTempDelete then DeleteFile(WadName);
4636 end;
4638 procedure GameCVars(P: SArray);
4639 var
4640 a, b: Integer;
4641 stat: TPlayerStatArray;
4642 cmd, s: string;
4643 config: TConfig;
4644 begin
4645 stat := nil;
4646 cmd := LowerCase(P[0]);
4647 if cmd = 'r_showfps' then
4648 begin
4649 if (Length(P) > 1) and
4650 ((P[1] = '1') or (P[1] = '0')) then
4651 gShowFPS := (P[1][1] = '1');
4653 if gShowFPS then
4654 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4655 else
4656 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4657 end
4658 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4659 begin
4660 with gGameSettings do
4661 begin
4662 if (Length(P) > 1) and
4663 ((P[1] = '1') or (P[1] = '0')) then
4664 begin
4665 if (P[1][1] = '1') then
4666 Options := Options or GAME_OPTION_TEAMDAMAGE
4667 else
4668 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4669 end;
4671 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4672 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4673 else
4674 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4676 if g_Game_IsNet then MH_SEND_GameSettings;
4677 end;
4678 end
4679 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4680 begin
4681 with gGameSettings do
4682 begin
4683 if (Length(P) > 1) and
4684 ((P[1] = '1') or (P[1] = '0')) then
4685 begin
4686 if (P[1][1] = '1') then
4687 Options := Options or GAME_OPTION_WEAPONSTAY
4688 else
4689 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4690 end;
4692 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4693 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4694 else
4695 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4697 if g_Game_IsNet then MH_SEND_GameSettings;
4698 end;
4699 end
4700 else if cmd = 'g_gamemode' then
4701 begin
4702 a := g_Game_TextToMode(P[1]);
4703 if a = GM_SINGLE then a := GM_COOP;
4704 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4705 begin
4706 gSwitchGameMode := a;
4707 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4708 (gState = STATE_INTERSINGLE) then
4709 gSwitchGameMode := GM_SINGLE;
4710 if not gGameOn then
4711 gGameSettings.GameMode := gSwitchGameMode;
4712 end;
4713 if gSwitchGameMode = gGameSettings.GameMode then
4714 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4715 [g_Game_ModeToText(gGameSettings.GameMode)]))
4716 else
4717 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4718 [g_Game_ModeToText(gGameSettings.GameMode),
4719 g_Game_ModeToText(gSwitchGameMode)]));
4720 end
4721 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4722 begin
4723 with gGameSettings do
4724 begin
4725 if (Length(P) > 1) and
4726 ((P[1] = '1') or (P[1] = '0')) then
4727 begin
4728 if (P[1][1] = '1') then
4729 Options := Options or GAME_OPTION_ALLOWEXIT
4730 else
4731 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4732 end;
4734 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4735 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4736 else
4737 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4738 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4740 if g_Game_IsNet then MH_SEND_GameSettings;
4741 end;
4742 end
4743 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4744 begin
4745 with gGameSettings do
4746 begin
4747 if (Length(P) > 1) and
4748 ((P[1] = '1') or (P[1] = '0')) then
4749 begin
4750 if (P[1][1] = '1') then
4751 Options := Options or GAME_OPTION_MONSTERS
4752 else
4753 Options := Options and (not GAME_OPTION_MONSTERS);
4754 end;
4756 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4757 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4758 else
4759 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4760 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4762 if g_Game_IsNet then MH_SEND_GameSettings;
4763 end;
4764 end
4765 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4766 begin
4767 with gGameSettings do
4768 begin
4769 if (Length(P) > 1) and
4770 ((P[1] = '1') or (P[1] = '0')) then
4771 begin
4772 if (P[1][1] = '1') then
4773 Options := Options or GAME_OPTION_BOTVSPLAYER
4774 else
4775 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4776 end;
4778 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4779 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4780 else
4781 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4783 if g_Game_IsNet then MH_SEND_GameSettings;
4784 end;
4785 end
4786 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4787 begin
4788 with gGameSettings do
4789 begin
4790 if (Length(P) > 1) and
4791 ((P[1] = '1') or (P[1] = '0')) then
4792 begin
4793 if (P[1][1] = '1') then
4794 Options := Options or GAME_OPTION_BOTVSMONSTER
4795 else
4796 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4797 end;
4799 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4800 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4801 else
4802 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4804 if g_Game_IsNet then MH_SEND_GameSettings;
4805 end;
4806 end
4807 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4808 begin
4809 if Length(P) > 1 then
4810 begin
4811 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4812 gGameSettings.WarmupTime := 30
4813 else
4814 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4815 end;
4817 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4818 [gGameSettings.WarmupTime]));
4819 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4820 end
4821 else if cmd = 'net_interp' then
4822 begin
4823 if (Length(P) > 1) then
4824 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4826 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4827 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4828 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4829 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4830 config.Free();
4831 end
4832 else if cmd = 'net_forceplayerupdate' then
4833 begin
4834 if (Length(P) > 1) and
4835 ((P[1] = '1') or (P[1] = '0')) then
4836 NetForcePlayerUpdate := (P[1][1] = '1');
4838 if NetForcePlayerUpdate then
4839 g_Console_Add('net_forceplayerupdate = 1')
4840 else
4841 g_Console_Add('net_forceplayerupdate = 0');
4842 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4843 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4844 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4845 config.Free();
4846 end
4847 else if cmd = 'net_predictself' then
4848 begin
4849 if (Length(P) > 1) and
4850 ((P[1] = '1') or (P[1] = '0')) then
4851 NetPredictSelf := (P[1][1] = '1');
4853 if NetPredictSelf then
4854 g_Console_Add('net_predictself = 1')
4855 else
4856 g_Console_Add('net_predictself = 0');
4857 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4858 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4859 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4860 config.Free();
4861 end
4862 else if cmd = 'sv_name' then
4863 begin
4864 if (Length(P) > 1) and (Length(P[1]) > 0) then
4865 begin
4866 NetServerName := P[1];
4867 if Length(NetServerName) > 64 then
4868 SetLength(NetServerName, 64);
4869 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4870 g_Net_Slist_Update;
4871 end;
4873 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4874 end
4875 else if cmd = 'sv_passwd' then
4876 begin
4877 if (Length(P) > 1) and (Length(P[1]) > 0) then
4878 begin
4879 NetPassword := P[1];
4880 if Length(NetPassword) > 24 then
4881 SetLength(NetPassword, 24);
4882 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4883 g_Net_Slist_Update;
4884 end;
4886 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4887 end
4888 else if cmd = 'sv_maxplrs' then
4889 begin
4890 if (Length(P) > 1) then
4891 begin
4892 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4893 if g_Game_IsServer and g_Game_IsNet then
4894 begin
4895 b := 0;
4896 for a := 0 to High(NetClients) do
4897 if NetClients[a].Used then
4898 begin
4899 Inc(b);
4900 if b > NetMaxClients then
4901 begin
4902 s := g_Player_Get(NetClients[a].Player).Name;
4903 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4904 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4905 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4906 end;
4907 end;
4908 if NetUseMaster then
4909 g_Net_Slist_Update;
4910 end;
4911 end;
4913 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4914 end
4915 else if cmd = 'sv_public' then
4916 begin
4917 if (Length(P) > 1) then
4918 begin
4919 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4920 if g_Game_IsServer and g_Game_IsNet then
4921 if NetUseMaster then
4922 begin
4923 if NetMPeer = nil then
4924 if not g_Net_Slist_Connect() then
4925 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4926 g_Net_Slist_Update();
4927 end
4928 else
4929 if NetMPeer <> nil then
4930 g_Net_Slist_Disconnect();
4931 end;
4933 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4934 end
4935 else if cmd = 'sv_intertime' then
4936 begin
4937 if (Length(P) > 1) then
4938 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4940 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4941 end
4942 else if cmd = 'p1_name' then
4943 begin
4944 if (Length(P) > 1) and gGameOn then
4945 begin
4946 if g_Game_IsClient then
4947 begin
4948 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4949 MC_SEND_PlayerSettings;
4950 end
4951 else
4952 if gPlayer1 <> nil then
4953 begin
4954 gPlayer1.Name := b_Text_Unformat(P[1]);
4955 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4956 end
4957 else
4958 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4959 end;
4960 end
4961 else if cmd = 'p2_name' then
4962 begin
4963 if (Length(P) > 1) and gGameOn then
4964 begin
4965 if g_Game_IsClient then
4966 begin
4967 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4968 MC_SEND_PlayerSettings;
4969 end
4970 else
4971 if gPlayer2 <> nil then
4972 begin
4973 gPlayer2.Name := b_Text_Unformat(P[1]);
4974 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4975 end
4976 else
4977 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4978 end;
4979 end
4980 else if cmd = 'p1_color' then
4981 begin
4982 if Length(P) > 3 then
4983 if g_Game_IsClient then
4984 begin
4985 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4986 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4987 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4988 MC_SEND_PlayerSettings;
4989 end
4990 else
4991 if gPlayer1 <> nil then
4992 begin
4993 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4994 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4995 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4996 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4997 end
4998 else
4999 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5000 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5001 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5002 end
5003 else if (cmd = 'p2_color') and not g_Game_IsNet then
5004 begin
5005 if Length(P) > 3 then
5006 if g_Game_IsClient then
5007 begin
5008 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5009 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5010 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5011 MC_SEND_PlayerSettings;
5012 end
5013 else
5014 if gPlayer2 <> nil then
5015 begin
5016 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5017 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5018 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5019 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
5020 end
5021 else
5022 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
5023 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
5024 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
5025 end
5026 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5027 begin
5028 if cmd = 'r_showtime' then
5029 begin
5030 if (Length(P) > 1) and
5031 ((P[1] = '1') or (P[1] = '0')) then
5032 gShowTime := (P[1][1] = '1');
5034 if gShowTime then
5035 g_Console_Add(_lc[I_MSG_TIME_ON])
5036 else
5037 g_Console_Add(_lc[I_MSG_TIME_OFF]);
5038 end
5039 else if cmd = 'r_showscore' then
5040 begin
5041 if (Length(P) > 1) and
5042 ((P[1] = '1') or (P[1] = '0')) then
5043 gShowGoals := (P[1][1] = '1');
5045 if gShowGoals then
5046 g_Console_Add(_lc[I_MSG_SCORE_ON])
5047 else
5048 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
5049 end
5050 else if cmd = 'r_showstat' then
5051 begin
5052 if (Length(P) > 1) and
5053 ((P[1] = '1') or (P[1] = '0')) then
5054 gShowStat := (P[1][1] = '1');
5056 if gShowStat then
5057 g_Console_Add(_lc[I_MSG_STATS_ON])
5058 else
5059 g_Console_Add(_lc[I_MSG_STATS_OFF]);
5060 end
5061 else if cmd = 'r_showkillmsg' then
5062 begin
5063 if (Length(P) > 1) and
5064 ((P[1] = '1') or (P[1] = '0')) then
5065 gShowKillMsg := (P[1][1] = '1');
5067 if gShowKillMsg then
5068 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
5069 else
5070 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
5071 end
5072 else if cmd = 'r_showlives' then
5073 begin
5074 if (Length(P) > 1) and
5075 ((P[1] = '1') or (P[1] = '0')) then
5076 gShowLives := (P[1][1] = '1');
5078 if gShowLives then
5079 g_Console_Add(_lc[I_MSG_LIVES_ON])
5080 else
5081 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
5082 end
5083 else if cmd = 'r_showspect' then
5084 begin
5085 if (Length(P) > 1) and
5086 ((P[1] = '1') or (P[1] = '0')) then
5087 gSpectHUD := (P[1][1] = '1');
5089 if gSpectHUD then
5090 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
5091 else
5092 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
5093 end
5094 else if cmd = 'r_showping' then
5095 begin
5096 if (Length(P) > 1) and
5097 ((P[1] = '1') or (P[1] = '0')) then
5098 gShowPing := (P[1][1] = '1');
5100 if gShowPing then
5101 g_Console_Add(_lc[I_MSG_PING_ON])
5102 else
5103 g_Console_Add(_lc[I_MSG_PING_OFF]);
5104 end
5105 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
5106 begin
5107 if Length(P) > 1 then
5108 begin
5109 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
5110 gGameSettings.GoalLimit := 0
5111 else
5112 begin
5113 b := 0;
5115 if gGameSettings.GameMode = GM_DM then
5116 begin // DM
5117 stat := g_Player_GetStats();
5118 if stat <> nil then
5119 for a := 0 to High(stat) do
5120 if stat[a].Frags > b then
5121 b := stat[a].Frags;
5122 end
5123 else // TDM/CTF
5124 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
5126 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
5127 end;
5129 if g_Game_IsNet then MH_SEND_GameSettings;
5130 end;
5132 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
5133 end
5134 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
5135 begin
5136 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
5137 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
5139 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5140 [gGameSettings.TimeLimit div 3600,
5141 (gGameSettings.TimeLimit div 60) mod 60,
5142 gGameSettings.TimeLimit mod 60]));
5143 if g_Game_IsNet then MH_SEND_GameSettings;
5144 end
5145 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
5146 begin
5147 if Length(P) > 1 then
5148 begin
5149 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
5150 gGameSettings.MaxLives := 0
5151 else
5152 begin
5153 b := 0;
5154 stat := g_Player_GetStats();
5155 if stat <> nil then
5156 for a := 0 to High(stat) do
5157 if stat[a].Lives > b then
5158 b := stat[a].Lives;
5159 gGameSettings.MaxLives :=
5160 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5161 end;
5162 end;
5164 g_Console_Add(Format(_lc[I_MSG_LIVES],
5165 [gGameSettings.MaxLives]));
5166 if g_Game_IsNet then MH_SEND_GameSettings;
5167 end;
5168 end;
5169 end;
5171 procedure PrintHeapStats();
5172 var
5173 hs: TFPCHeapStatus;
5174 begin
5175 hs := GetFPCHeapStatus();
5176 e_LogWriteLn ('v===== heap status =====v');
5177 e_LogWriteFln('max heap size = %d k', [hs.MaxHeapSize div 1024]);
5178 e_LogWriteFln('max heap used = %d k', [hs.MaxHeapUsed div 1024]);
5179 e_LogWriteFln('cur heap size = %d k', [hs.CurrHeapSize div 1024]);
5180 e_LogWriteFln('cur heap used = %d k', [hs.CurrHeapUsed div 1024]);
5181 e_LogWriteFln('cur heap free = %d k', [hs.CurrHeapFree div 1024]);
5182 e_LogWriteLn ('^=======================^');
5183 end;
5185 procedure DebugCommands(P: SArray);
5186 var
5187 a, b: Integer;
5188 cmd: string;
5189 //pt: TDFPoint;
5190 mon: TMonster;
5191 begin
5192 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5193 if gDebugMode then
5194 begin
5195 cmd := LowerCase(P[0]);
5196 if cmd = 'd_window' then
5197 begin
5198 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5199 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5200 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5201 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5202 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5203 end
5204 else if cmd = 'd_sounds' then
5205 begin
5206 if (Length(P) > 1) and
5207 ((P[1] = '1') or (P[1] = '0')) then
5208 g_Debug_Sounds := (P[1][1] = '1');
5210 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5211 end
5212 else if cmd = 'd_frames' then
5213 begin
5214 if (Length(P) > 1) and
5215 ((P[1] = '1') or (P[1] = '0')) then
5216 g_Debug_Frames := (P[1][1] = '1');
5218 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5219 end
5220 else if cmd = 'd_winmsg' then
5221 begin
5222 if (Length(P) > 1) and
5223 ((P[1] = '1') or (P[1] = '0')) then
5224 g_Debug_WinMsgs := (P[1][1] = '1');
5226 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5227 end
5228 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5229 begin
5230 if (Length(P) > 1) and
5231 ((P[1] = '1') or (P[1] = '0')) then
5232 g_Debug_MonsterOff := (P[1][1] = '1');
5234 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5235 end
5236 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5237 begin
5238 if Length(P) > 1 then
5239 case P[1][1] of
5240 '0': g_debug_BotAIOff := 0;
5241 '1': g_debug_BotAIOff := 1;
5242 '2': g_debug_BotAIOff := 2;
5243 '3': g_debug_BotAIOff := 3;
5244 end;
5246 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5247 end
5248 else if cmd = 'd_monster' then
5249 begin
5250 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.alive) and (not g_Game_IsNet) then
5251 if Length(P) < 2 then
5252 begin
5253 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5254 g_Console_Add('ID | Name');
5255 for b := MONSTER_DEMON to MONSTER_MAN do
5256 g_Console_Add(Format('%2d | %s', [b, g_Mons_NameByTypeId(b)]));
5257 conwriteln('behav. num'#10'normal 0'#10'killer 1'#10'maniac 2'#10'insane 3'#10'cannibal 4'#10'good 5');
5258 end else
5259 begin
5260 a := StrToIntDef(P[1], 0);
5261 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5262 a := g_Mons_TypeIdByName(P[1]);
5264 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5265 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5266 else
5267 begin
5268 with gPlayer1.Obj do
5269 begin
5270 mon := g_Monsters_Create(a,
5271 X + Rect.X + (Rect.Width div 2),
5272 Y + Rect.Y + Rect.Height,
5273 gPlayer1.Direction, True);
5274 end;
5275 if (Length(P) > 2) and (mon <> nil) then
5276 begin
5277 if (CompareText(P[2], 'normal') = 0) then mon.MonsterBehaviour := BH_NORMAL
5278 else if (CompareText(P[2], 'killer') = 0) then mon.MonsterBehaviour := BH_KILLER
5279 else if (CompareText(P[2], 'maniac') = 0) then mon.MonsterBehaviour := BH_MANIAC
5280 else if (CompareText(P[2], 'insane') = 0) then mon.MonsterBehaviour := BH_INSANE
5281 else if (CompareText(P[2], 'cannibal') = 0) then mon.MonsterBehaviour := BH_CANNIBAL
5282 else if (CompareText(P[2], 'good') = 0) then mon.MonsterBehaviour := BH_GOOD
5283 else if (CompareText(P[2], 'friend') = 0) then mon.MonsterBehaviour := BH_GOOD
5284 else if (CompareText(P[2], 'friendly') = 0) then mon.MonsterBehaviour := BH_GOOD
5285 else mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5286 end;
5287 end;
5288 end;
5289 end
5290 else if (cmd = 'd_health') then
5291 begin
5292 if (Length(P) > 1) and
5293 ((P[1] = '1') or (P[1] = '0')) then
5294 g_debug_HealthBar := (P[1][1] = '1');
5296 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5297 end
5298 else if (cmd = 'd_player') then
5299 begin
5300 if (Length(P) > 1) and
5301 ((P[1] = '1') or (P[1] = '0')) then
5302 g_debug_Player := (P[1][1] = '1');
5304 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5305 end
5306 else if (cmd = 'd_joy') then
5307 begin
5308 for a := 1 to 8 do
5309 g_Console_Add(e_JoystickStateToString(a));
5310 end
5311 else if (cmd = 'd_mem') then
5312 begin
5313 PrintHeapStats();
5314 end;
5315 end
5316 else
5317 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5318 end;
5321 procedure GameCheats(P: SArray);
5322 var
5323 cmd: string;
5324 f, a: Integer;
5325 plr: TPlayer;
5326 begin
5327 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5328 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5329 begin
5330 g_Console_Add('not available');
5331 exit;
5332 end;
5333 plr := gPlayer1;
5334 if plr = nil then
5335 begin
5336 g_Console_Add('where is the player?!');
5337 exit;
5338 end;
5339 cmd := LowerCase(P[0]);
5340 // god
5341 if cmd = 'god' then
5342 begin
5343 plr.GodMode := not plr.GodMode;
5344 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5345 exit;
5346 end;
5347 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5348 if cmd = 'give' then
5349 begin
5350 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5351 for f := 1 to High(P) do
5352 begin
5353 cmd := LowerCase(P[f]);
5354 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5355 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5356 if cmd = 'exit' then
5357 begin
5358 if gTriggers <> nil then
5359 begin
5360 for a := 0 to High(gTriggers) do
5361 begin
5362 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5363 begin
5364 g_Console_Add('player left the map');
5365 gExitByTrigger := True;
5366 //g_Game_ExitLevel(gTriggers[a].Data.MapName);
5367 g_Game_ExitLevel(gTriggers[a].tgcMap);
5368 break;
5369 end;
5370 end;
5371 end;
5372 continue;
5373 end;
5375 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5376 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5377 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5378 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5379 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5381 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5382 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5384 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5385 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;
5387 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5388 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5390 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5391 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5393 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5394 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5396 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5397 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5398 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5400 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5401 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5402 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5403 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;
5404 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5405 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5407 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;
5408 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;
5409 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;
5410 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;
5411 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;
5412 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;
5414 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5415 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;
5417 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;
5418 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;
5420 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5422 if cmd = 'ammo' then
5423 begin
5424 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5425 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5426 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5427 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5428 plr.GiveItem(ITEM_AMMO_FUELCAN);
5429 g_Console_Add('player got some ammo');
5430 continue;
5431 end;
5433 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5434 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5436 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5437 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5439 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5440 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5442 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5443 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5445 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5447 if cmd = 'weapons' then
5448 begin
5449 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5450 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5451 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5452 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5453 plr.GiveItem(ITEM_WEAPON_PLASMA);
5454 plr.GiveItem(ITEM_WEAPON_BFG);
5455 g_Console_Add('player got weapons');
5456 continue;
5457 end;
5459 if cmd = 'keys' then
5460 begin
5461 plr.GiveItem(ITEM_KEY_RED);
5462 plr.GiveItem(ITEM_KEY_GREEN);
5463 plr.GiveItem(ITEM_KEY_BLUE);
5464 g_Console_Add('player got all keys');
5465 continue;
5466 end;
5468 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5469 end;
5470 exit;
5471 end;
5472 // open
5473 if cmd = 'open' then
5474 begin
5475 g_Console_Add('player activated sesame');
5476 g_Triggers_OpenAll();
5477 exit;
5478 end;
5479 // fly
5480 if cmd = 'fly' then
5481 begin
5482 gFly := not gFly;
5483 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5484 exit;
5485 end;
5486 // noclip
5487 if cmd = 'noclip' then
5488 begin
5489 plr.SwitchNoClip;
5490 g_Console_Add('wall hardeness adjusted');
5491 exit;
5492 end;
5493 // notarget
5494 if cmd = 'notarget' then
5495 begin
5496 plr.NoTarget := not plr.NoTarget;
5497 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5498 exit;
5499 end;
5500 // noreload
5501 if cmd = 'noreload' then
5502 begin
5503 plr.NoReload := not plr.NoReload;
5504 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5505 exit;
5506 end;
5507 // speedy
5508 if cmd = 'speedy' then
5509 begin
5510 MAX_RUNVEL := 32-MAX_RUNVEL;
5511 g_Console_Add('speed adjusted');
5512 exit;
5513 end;
5514 // jumpy
5515 if cmd = 'jumpy' then
5516 begin
5517 VEL_JUMP := 30-VEL_JUMP;
5518 g_Console_Add('jump height adjusted');
5519 exit;
5520 end;
5521 // automap
5522 if cmd = 'automap' then
5523 begin
5524 gShowMap := not gShowMap;
5525 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5526 exit;
5527 end;
5528 // aimline
5529 if cmd = 'aimline' then
5530 begin
5531 gAimLine := not gAimLine;
5532 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5533 exit;
5534 end;
5535 end;
5537 procedure GameCommands(P: SArray);
5538 var
5539 a, b: Integer;
5540 s, pw: String;
5541 chstr: string;
5542 cmd: string;
5543 pl: pTNetClient = nil;
5544 plr: TPlayer;
5545 prt: Word;
5546 nm: Boolean;
5547 listen: LongWord;
5548 begin
5549 // Îáùèå êîìàíäû:
5550 cmd := LowerCase(P[0]);
5551 chstr := '';
5552 if (cmd = 'quit') or
5553 (cmd = 'exit') then
5554 begin
5555 g_Game_Free();
5556 g_Game_Quit();
5557 Exit;
5558 end
5559 else if cmd = 'pause' then
5560 begin
5561 if (g_ActiveWindow = nil) then
5562 g_Game_Pause(not gPause);
5563 end
5564 else if cmd = 'endgame' then
5565 gExit := EXIT_SIMPLE
5566 else if cmd = 'restart' then
5567 begin
5568 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5569 begin
5570 if g_Game_IsClient then
5571 begin
5572 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5573 Exit;
5574 end;
5575 g_Game_Restart();
5576 end else
5577 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5578 end
5579 else if cmd = 'kick' then
5580 begin
5581 if g_Game_IsServer then
5582 begin
5583 if Length(P) < 2 then
5584 begin
5585 g_Console_Add('kick <name>');
5586 Exit;
5587 end;
5588 if P[1] = '' then
5589 begin
5590 g_Console_Add('kick <name>');
5591 Exit;
5592 end;
5594 if g_Game_IsNet then
5595 pl := g_Net_Client_ByName(P[1]);
5596 if (pl <> nil) then
5597 begin
5598 s := g_Net_ClientName_ByID(pl^.ID);
5599 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5600 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5601 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5602 if NetUseMaster then
5603 g_Net_Slist_Update;
5604 end else if gPlayers <> nil then
5605 for a := Low(gPlayers) to High(gPlayers) do
5606 if gPlayers[a] <> nil then
5607 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5608 begin
5609 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5610 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5611 continue;
5612 gPlayers[a].Lives := 0;
5613 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5614 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5615 g_Player_Remove(gPlayers[a].UID);
5616 if NetUseMaster then
5617 g_Net_Slist_Update;
5618 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5619 g_Bot_MixNames();
5620 end;
5621 end else
5622 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5623 end
5624 else if cmd = 'kick_id' then
5625 begin
5626 if g_Game_IsServer and g_Game_IsNet then
5627 begin
5628 if Length(P) < 2 then
5629 begin
5630 g_Console_Add('kick_id <client ID>');
5631 Exit;
5632 end;
5633 if P[1] = '' then
5634 begin
5635 g_Console_Add('kick_id <client ID>');
5636 Exit;
5637 end;
5639 a := StrToIntDef(P[1], 0);
5640 if (NetClients <> nil) and (a <= High(NetClients)) then
5641 begin
5642 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5643 begin
5644 s := g_Net_ClientName_ByID(NetClients[a].ID);
5645 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5646 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5647 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5648 if NetUseMaster then
5649 g_Net_Slist_Update;
5650 end;
5651 end;
5652 end else
5653 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5654 end
5655 else if cmd = 'ban' 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 <name>');
5662 Exit;
5663 end;
5664 if P[1] = '' then
5665 begin
5666 g_Console_Add('ban <name>');
5667 Exit;
5668 end;
5670 pl := g_Net_Client_ByName(P[1]);
5671 if (pl <> nil) then
5672 begin
5673 s := g_Net_ClientName_ByID(pl^.ID);
5674 g_Net_BanHost(pl^.Peer^.address.host, False);
5675 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5676 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5677 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5678 if NetUseMaster then
5679 g_Net_Slist_Update;
5680 end else
5681 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5682 end else
5683 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5684 end
5685 else if cmd = 'ban_id' 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('ban_id <client ID>');
5692 Exit;
5693 end;
5694 if P[1] = '' then
5695 begin
5696 g_Console_Add('ban_id <client ID>');
5697 Exit;
5698 end;
5700 a := StrToIntDef(P[1], 0);
5701 if (NetClients <> nil) and (a <= High(NetClients)) then
5702 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5703 begin
5704 s := g_Net_ClientName_ByID(NetClients[a].ID);
5705 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5706 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
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;
5712 end else
5713 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5714 end
5715 else if cmd = 'permban' then
5716 begin
5717 if g_Game_IsServer and g_Game_IsNet then
5718 begin
5719 if Length(P) < 2 then
5720 begin
5721 g_Console_Add('permban <name>');
5722 Exit;
5723 end;
5724 if P[1] = '' then
5725 begin
5726 g_Console_Add('permban <name>');
5727 Exit;
5728 end;
5730 pl := g_Net_Client_ByName(P[1]);
5731 if (pl <> nil) then
5732 begin
5733 s := g_Net_ClientName_ByID(pl^.ID);
5734 g_Net_BanHost(pl^.Peer^.address.host);
5735 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5736 g_Net_SaveBanList();
5737 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5738 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5739 if NetUseMaster then
5740 g_Net_Slist_Update;
5741 end else
5742 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5743 end else
5744 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5745 end
5746 else if cmd = 'permban_id' then
5747 begin
5748 if g_Game_IsServer and g_Game_IsNet then
5749 begin
5750 if Length(P) < 2 then
5751 begin
5752 g_Console_Add('permban_id <client ID>');
5753 Exit;
5754 end;
5755 if P[1] = '' then
5756 begin
5757 g_Console_Add('permban_id <client ID>');
5758 Exit;
5759 end;
5761 a := StrToIntDef(P[1], 0);
5762 if (NetClients <> nil) and (a <= High(NetClients)) then
5763 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5764 begin
5765 s := g_Net_ClientName_ByID(NetClients[a].ID);
5766 g_Net_BanHost(NetClients[a].Peer^.address.host);
5767 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5768 g_Net_SaveBanList();
5769 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5770 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5771 if NetUseMaster then
5772 g_Net_Slist_Update;
5773 end;
5774 end else
5775 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5776 end
5777 else if cmd = 'unban' then
5778 begin
5779 if g_Game_IsServer and g_Game_IsNet then
5780 begin
5781 if Length(P) < 2 then
5782 begin
5783 g_Console_Add('unban <IP Address>');
5784 Exit;
5785 end;
5786 if P[1] = '' then
5787 begin
5788 g_Console_Add('unban <IP Address>');
5789 Exit;
5790 end;
5792 if g_Net_UnbanHost(P[1]) then
5793 begin
5794 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5795 g_Net_SaveBanList();
5796 end else
5797 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5798 end else
5799 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5800 end
5801 else if cmd = 'clientlist' then
5802 begin
5803 if g_Game_IsServer and g_Game_IsNet then
5804 begin
5805 b := 0;
5806 if NetClients <> nil then
5807 for a := Low(NetClients) to High(NetClients) do
5808 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5809 begin
5810 plr := g_Player_Get(NetClients[a].Player);
5811 if plr = nil then continue;
5812 Inc(b);
5813 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5814 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5815 end;
5816 if b = 0 then
5817 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5818 end else
5819 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5820 end
5821 else if cmd = 'connect' then
5822 begin
5823 if (NetMode = NET_NONE) then
5824 begin
5825 if Length(P) < 2 then
5826 begin
5827 g_Console_Add('connect <IP> [port] [password]');
5828 Exit;
5829 end;
5830 if P[1] = '' then
5831 begin
5832 g_Console_Add('connect <IP> [port] [password]');
5833 Exit;
5834 end;
5836 if Length(P) > 2 then
5837 prt := StrToIntDef(P[2], 25666)
5838 else
5839 prt := 25666;
5841 if Length(P) > 3 then
5842 pw := P[3]
5843 else
5844 pw := '';
5846 g_Game_StartClient(P[1], prt, pw);
5847 end;
5848 end
5849 else if cmd = 'disconnect' then
5850 begin
5851 if (NetMode = NET_CLIENT) then
5852 g_Net_Disconnect();
5853 end
5854 else if cmd = 'reconnect' then
5855 begin
5856 if (NetMode = NET_SERVER) then
5857 Exit;
5859 if (NetMode = NET_CLIENT) then
5860 begin
5861 g_Net_Disconnect();
5862 gExit := EXIT_SIMPLE;
5863 EndGame;
5864 end;
5866 //TODO: Use last successful password to reconnect, instead of ''
5867 g_Game_StartClient(NetClientIP, NetClientPort, '');
5868 end
5869 else if (cmd = 'addbot') or
5870 (cmd = 'bot_add') then
5871 begin
5872 if Length(P) > 1 then
5873 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5874 else
5875 g_Bot_Add(TEAM_NONE, 2);
5876 end
5877 else if cmd = 'bot_addlist' then
5878 begin
5879 if Length(P) > 1 then
5880 if Length(P) = 2 then
5881 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5882 else
5883 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5884 end
5885 else if cmd = 'bot_removeall' then
5886 g_Bot_RemoveAll()
5887 else if cmd = 'chat' then
5888 begin
5889 if g_Game_IsNet then
5890 begin
5891 if Length(P) > 1 then
5892 begin
5893 for a := 1 to High(P) do
5894 chstr := chstr + P[a] + ' ';
5896 if Length(chstr) > 200 then SetLength(chstr, 200);
5898 if Length(chstr) < 1 then
5899 begin
5900 g_Console_Add('chat <text>');
5901 Exit;
5902 end;
5904 chstr := b_Text_Format(chstr);
5905 if g_Game_IsClient then
5906 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5907 else
5908 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5909 end
5910 else
5911 g_Console_Add('chat <text>');
5912 end else
5913 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5914 end
5915 else if cmd = 'teamchat' then
5916 begin
5917 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5918 begin
5919 if Length(P) > 1 then
5920 begin
5921 for a := 1 to High(P) do
5922 chstr := chstr + P[a] + ' ';
5924 if Length(chstr) > 200 then SetLength(chstr, 200);
5926 if Length(chstr) < 1 then
5927 begin
5928 g_Console_Add('teamchat <text>');
5929 Exit;
5930 end;
5932 chstr := b_Text_Format(chstr);
5933 if g_Game_IsClient then
5934 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5935 else
5936 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5937 gPlayer1Settings.Team);
5938 end
5939 else
5940 g_Console_Add('teamchat <text>');
5941 end else
5942 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5943 end
5944 else if cmd = 'game' then
5945 begin
5946 if gGameSettings.GameType <> GT_NONE then
5947 begin
5948 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5949 Exit;
5950 end;
5951 if Length(P) = 1 then
5952 begin
5953 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5954 Exit;
5955 end;
5956 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5957 P[1] := addWadExtension(P[1]);
5958 if FileExists(MapsDir + P[1]) then
5959 begin
5960 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5961 if Length(P) < 3 then
5962 begin
5963 SetLength(P, 3);
5964 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5965 end;
5967 s := P[1] + ':\' + UpperCase(P[2]);
5969 if g_Map_Exist(MapsDir + s) then
5970 begin
5971 // Çàïóñêàåì ñâîþ èãðó
5972 g_Game_Free();
5973 with gGameSettings do
5974 begin
5975 GameMode := g_Game_TextToMode(gcGameMode);
5976 if gSwitchGameMode <> GM_NONE then
5977 GameMode := gSwitchGameMode;
5978 if GameMode = GM_NONE then GameMode := GM_DM;
5979 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5980 b := 1;
5981 if Length(P) >= 4 then
5982 b := StrToIntDef(P[3], 1);
5983 g_Game_StartCustom(s, GameMode, TimeLimit,
5984 GoalLimit, MaxLives, Options, b);
5985 end;
5986 end
5987 else
5988 if P[2] = '' then
5989 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5990 else
5991 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5992 end else
5993 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5994 end
5995 else if cmd = 'host' then
5996 begin
5997 if gGameSettings.GameType <> GT_NONE then
5998 begin
5999 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6000 Exit;
6001 end;
6002 if Length(P) < 4 then
6003 begin
6004 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
6005 Exit;
6006 end;
6007 if not StrToIp(P[1], listen) then
6008 Exit;
6009 prt := StrToIntDef(P[2], 25666);
6011 P[3] := addWadExtension(P[3]);
6012 if FileExists(MapsDir + P[3]) then
6013 begin
6014 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
6015 if Length(P) < 5 then
6016 begin
6017 SetLength(P, 5);
6018 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
6019 end;
6021 s := P[3] + ':\' + UpperCase(P[4]);
6023 if g_Map_Exist(MapsDir + s) then
6024 begin
6025 // Çàïóñêàåì ñâîþ èãðó
6026 g_Game_Free();
6027 with gGameSettings do
6028 begin
6029 GameMode := g_Game_TextToMode(gcGameMode);
6030 if gSwitchGameMode <> GM_NONE then
6031 GameMode := gSwitchGameMode;
6032 if GameMode = GM_NONE then GameMode := GM_DM;
6033 if GameMode = GM_SINGLE then GameMode := GM_COOP;
6034 b := 0;
6035 if Length(P) >= 6 then
6036 b := StrToIntDef(P[5], 0);
6037 g_Game_StartServer(s, GameMode, TimeLimit,
6038 GoalLimit, MaxLives, Options, b, listen, prt);
6039 end;
6040 end
6041 else
6042 if P[4] = '' then
6043 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
6044 else
6045 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
6046 end else
6047 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
6048 end
6049 else if cmd = 'map' then
6050 begin
6051 if Length(P) = 1 then
6052 begin
6053 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6054 begin
6055 g_Console_Add(cmd + ' <MAP>');
6056 g_Console_Add(cmd + ' <WAD> [MAP]');
6057 end else
6058 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6059 end else
6060 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6061 begin
6062 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
6063 if Length(P) < 3 then
6064 begin
6065 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6066 s := UpperCase(P[1]);
6067 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6068 begin // Êàðòà íàøëàñü
6069 gExitByTrigger := False;
6070 if gGameOn then
6071 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6072 gNextMap := s;
6073 gExit := EXIT_ENDLEVELCUSTOM;
6074 end
6075 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6076 g_Game_ChangeMap(s);
6077 end else
6078 begin
6079 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6080 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6081 P[1] := addWadExtension(P[1]);
6082 if FileExists(MapsDir + P[1]) then
6083 begin
6084 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6085 SetLength(P, 3);
6086 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6088 s := P[1] + ':\' + P[2];
6090 if g_Map_Exist(MapsDir + s) then
6091 begin
6092 gExitByTrigger := False;
6093 if gGameOn then
6094 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6095 gNextMap := s;
6096 gExit := EXIT_ENDLEVELCUSTOM;
6097 end
6098 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6099 g_Game_ChangeMap(s);
6100 end else
6101 if P[2] = '' then
6102 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6103 else
6104 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6105 end else
6106 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6107 end;
6108 end else
6109 begin
6110 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6111 P[1] := addWadExtension(P[1]);
6112 if FileExists(MapsDir + P[1]) then
6113 begin
6114 // Íàøëè WAD ôàéë
6115 P[2] := UpperCase(P[2]);
6116 s := P[1] + ':\' + P[2];
6118 if g_Map_Exist(MapsDir + s) then
6119 begin // Íàøëè êàðòó
6120 gExitByTrigger := False;
6121 if gGameOn then
6122 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
6123 gNextMap := s;
6124 gExit := EXIT_ENDLEVELCUSTOM;
6125 end
6126 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
6127 g_Game_ChangeMap(s);
6128 end else
6129 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6130 end else
6131 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6132 end;
6133 end else
6134 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6135 end
6136 else if cmd = 'nextmap' then
6137 begin
6138 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
6139 g_Console_Add(_lc[I_MSG_NOT_GAME])
6140 else begin
6141 nm := True;
6142 if Length(P) = 1 then
6143 begin
6144 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6145 begin
6146 g_Console_Add(cmd + ' <MAP>');
6147 g_Console_Add(cmd + ' <WAD> [MAP]');
6148 end else begin
6149 nm := False;
6150 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6151 end;
6152 end else
6153 begin
6154 nm := False;
6155 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6156 begin
6157 if Length(P) < 3 then
6158 begin
6159 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6160 s := UpperCase(P[1]);
6161 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6162 begin // Êàðòà íàøëàñü
6163 gExitByTrigger := False;
6164 gNextMap := s;
6165 nm := True;
6166 end else
6167 begin
6168 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6169 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6170 P[1] := addWadExtension(P[1]);
6171 if FileExists(MapsDir + P[1]) then
6172 begin
6173 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6174 SetLength(P, 3);
6175 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6177 s := P[1] + ':\' + P[2];
6179 if g_Map_Exist(MapsDir + s) then
6180 begin // Óñòàíàâëèâàåì êàðòó
6181 gExitByTrigger := False;
6182 gNextMap := s;
6183 nm := True;
6184 end else
6185 if P[2] = '' then
6186 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6187 else
6188 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6189 end else
6190 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6191 end;
6192 end else
6193 begin
6194 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6195 P[1] := addWadExtension(P[1]);
6196 if FileExists(MapsDir + P[1]) then
6197 begin
6198 // Íàøëè WAD ôàéë
6199 P[2] := UpperCase(P[2]);
6200 s := P[1] + ':\' + P[2];
6202 if g_Map_Exist(MapsDir + s) then
6203 begin // Íàøëè êàðòó
6204 gExitByTrigger := False;
6205 gNextMap := s;
6206 nm := True;
6207 end else
6208 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6209 end else
6210 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6211 end;
6212 end else
6213 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6214 end;
6215 if nm then
6216 if gNextMap = '' then
6217 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6218 else
6219 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6220 end;
6221 end
6222 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6223 begin
6224 if not gGameOn then
6225 g_Console_Add(_lc[I_MSG_NOT_GAME])
6226 else
6227 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6228 begin
6229 gExitByTrigger := False;
6230 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6231 if (gNextMap = '') and (gTriggers <> nil) then
6232 for a := 0 to High(gTriggers) do
6233 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6234 begin
6235 gExitByTrigger := True;
6236 //gNextMap := gTriggers[a].Data.MapName;
6237 gNextMap := gTriggers[a].tgcMap;
6238 Break;
6239 end;
6240 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6241 if gNextMap = '' then
6242 gNextMap := g_Game_GetNextMap();
6243 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6244 if not isWadPath(gNextMap) then
6245 s := gGameSettings.WAD + ':\' + gNextMap
6246 else
6247 s := gNextMap;
6248 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6249 if g_Map_Exist(MapsDir + s) then
6250 gExit := EXIT_ENDLEVELCUSTOM
6251 else
6252 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6253 end else
6254 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6255 end
6256 else if (cmd = 'event') then
6257 begin
6258 if (Length(P) <= 1) then
6259 begin
6260 for a := 0 to High(gEvents) do
6261 if gEvents[a].Command = '' then
6262 g_Console_Add(gEvents[a].Name + ' <none>')
6263 else
6264 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6265 Exit;
6266 end;
6267 if (Length(P) = 2) then
6268 begin
6269 for a := 0 to High(gEvents) do
6270 if gEvents[a].Name = P[1] then
6271 if gEvents[a].Command = '' then
6272 g_Console_Add(gEvents[a].Name + ' <none>')
6273 else
6274 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6275 Exit;
6276 end;
6277 for a := 0 to High(gEvents) do
6278 if gEvents[a].Name = P[1] then
6279 begin
6280 gEvents[a].Command := '';
6281 for b := 2 to High(P) do
6282 if Pos(' ', P[b]) = 0 then
6283 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6284 else
6285 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6286 gEvents[a].Command := Trim(gEvents[a].Command);
6287 Exit;
6288 end;
6289 end
6290 else if cmd = 'suicide' then
6291 begin
6292 if gGameOn then
6293 begin
6294 if g_Game_IsClient then
6295 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6296 else
6297 begin
6298 if gPlayer1 <> nil then
6299 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6300 if gPlayer2 <> nil then
6301 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6302 end;
6303 end;
6304 end
6305 // Êîìàíäû Ñâîåé èãðû:
6306 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6307 begin
6308 if cmd = 'bot_addred' then
6309 begin
6310 if Length(P) > 1 then
6311 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6312 else
6313 g_Bot_Add(TEAM_RED, 2);
6314 end
6315 else if cmd = 'bot_addblue' then
6316 begin
6317 if Length(P) > 1 then
6318 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6319 else
6320 g_Bot_Add(TEAM_BLUE, 2);
6321 end
6322 else if cmd = 'spectate' then
6323 begin
6324 if not gGameOn then
6325 Exit;
6326 g_Game_Spectate();
6327 end
6328 else if cmd = 'say' then
6329 begin
6330 if g_Game_IsServer and g_Game_IsNet then
6331 begin
6332 if Length(P) > 1 then
6333 begin
6334 chstr := '';
6335 for a := 1 to High(P) do
6336 chstr := chstr + P[a] + ' ';
6338 if Length(chstr) > 200 then SetLength(chstr, 200);
6340 if Length(chstr) < 1 then
6341 begin
6342 g_Console_Add('say <text>');
6343 Exit;
6344 end;
6346 chstr := b_Text_Format(chstr);
6347 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6348 end
6349 else g_Console_Add('say <text>');
6350 end else
6351 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6352 end
6353 else if cmd = 'tell' then
6354 begin
6355 if g_Game_IsServer and g_Game_IsNet then
6356 begin
6357 if (Length(P) > 2) and (P[1] <> '') then
6358 begin
6359 chstr := '';
6360 for a := 2 to High(P) do
6361 chstr := chstr + P[a] + ' ';
6363 if Length(chstr) > 200 then SetLength(chstr, 200);
6365 if Length(chstr) < 1 then
6366 begin
6367 g_Console_Add('tell <playername> <text>');
6368 Exit;
6369 end;
6371 pl := g_Net_Client_ByName(P[1]);
6372 if pl <> nil then
6373 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6374 else
6375 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6376 end
6377 else g_Console_Add('tell <playername> <text>');
6378 end else
6379 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6380 end
6381 else if (cmd = 'overtime') and not g_Game_IsClient then
6382 begin
6383 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6384 Exit;
6385 // Äîïîëíèòåëüíîå âðåìÿ:
6386 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6388 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6389 [gGameSettings.TimeLimit div 3600,
6390 (gGameSettings.TimeLimit div 60) mod 60,
6391 gGameSettings.TimeLimit mod 60]));
6392 if g_Game_IsNet then MH_SEND_GameSettings;
6393 end
6394 else if (cmd = 'rcon_password') and g_Game_IsClient then
6395 begin
6396 if (Length(P) <= 1) then
6397 g_Console_Add('rcon_password <password>')
6398 else
6399 MC_SEND_RCONPassword(P[1]);
6400 end
6401 else if cmd = 'rcon' then
6402 begin
6403 if g_Game_IsClient then
6404 begin
6405 if Length(P) > 1 then
6406 begin
6407 chstr := '';
6408 for a := 1 to High(P) do
6409 chstr := chstr + P[a] + ' ';
6411 if Length(chstr) > 200 then SetLength(chstr, 200);
6413 if Length(chstr) < 1 then
6414 begin
6415 g_Console_Add('rcon <command>');
6416 Exit;
6417 end;
6419 MC_SEND_RCONCommand(chstr);
6420 end
6421 else g_Console_Add('rcon <command>');
6422 end;
6423 end
6424 else if cmd = 'ready' then
6425 begin
6426 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6427 gLMSRespawnTime := gTime + 100;
6428 end
6429 else if (cmd = 'callvote') and g_Game_IsNet then
6430 begin
6431 if Length(P) > 1 then
6432 begin
6433 chstr := '';
6434 for a := 1 to High(P) do begin
6435 if a > 1 then chstr := chstr + ' ';
6436 chstr := chstr + P[a];
6437 end;
6439 if Length(chstr) > 200 then SetLength(chstr, 200);
6441 if Length(chstr) < 1 then
6442 begin
6443 g_Console_Add('callvote <command>');
6444 Exit;
6445 end;
6447 if g_Game_IsClient then
6448 MC_SEND_Vote(True, chstr)
6449 else
6450 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6451 g_Console_Process('vote', True);
6452 end
6453 else
6454 g_Console_Add('callvote <command>');
6455 end
6456 else if (cmd = 'vote') and g_Game_IsNet then
6457 begin
6458 if g_Game_IsClient then
6459 MC_SEND_Vote(False)
6460 else if gVoteInProgress then
6461 begin
6462 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6463 a := Floor((NetClientCount+1)/2.0) + 1
6464 else
6465 a := Floor(NetClientCount/2.0) + 1;
6466 if gVoted then
6467 begin
6468 Dec(gVoteCount);
6469 gVoted := False;
6470 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6471 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6472 end
6473 else
6474 begin
6475 Inc(gVoteCount);
6476 gVoted := True;
6477 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6478 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6479 g_Game_CheckVote;
6480 end;
6481 end;
6482 end
6483 end;
6484 end;
6486 procedure g_TakeScreenShot();
6487 var
6488 a: Word;
6489 FileName: string;
6490 ssdir, t: string;
6491 st: TStream;
6492 ok: Boolean;
6493 begin
6494 if e_NoGraphics then Exit;
6495 ssdir := GameDir+'/screenshots';
6496 if not findFileCI(ssdir, true) then
6497 begin
6498 // try to create dir
6499 try
6500 CreateDir(ssdir);
6501 except
6502 end;
6503 if not findFileCI(ssdir, true) then exit; // alas
6504 end;
6505 try
6506 for a := 1 to High(Word) do
6507 begin
6508 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6509 t := FileName;
6510 if findFileCI(t, true) then continue;
6511 if not findFileCI(FileName) then
6512 begin
6513 ok := false;
6514 st := createDiskFile(FileName);
6515 try
6516 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6517 ok := true;
6518 finally
6519 st.Free();
6520 end;
6521 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6522 break;
6523 end;
6524 end;
6525 except
6526 end;
6527 end;
6529 procedure g_Game_InGameMenu(Show: Boolean);
6530 begin
6531 if (g_ActiveWindow = nil) and Show then
6532 begin
6533 if gGameSettings.GameType = GT_SINGLE then
6534 g_GUI_ShowWindow('GameSingleMenu')
6535 else
6536 begin
6537 if g_Game_IsClient then
6538 g_GUI_ShowWindow('GameClientMenu')
6539 else
6540 if g_Game_IsNet then
6541 g_GUI_ShowWindow('GameServerMenu')
6542 else
6543 g_GUI_ShowWindow('GameCustomMenu');
6544 end;
6545 g_Sound_PlayEx('MENU_OPEN');
6547 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6548 if (not g_Game_IsNet) then
6549 g_Game_Pause(True);
6550 end
6551 else
6552 if (g_ActiveWindow <> nil) and (not Show) then
6553 begin
6554 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6555 if (not g_Game_IsNet) then
6556 g_Game_Pause(False);
6557 end;
6558 end;
6560 procedure g_Game_Pause(Enable: Boolean);
6561 begin
6562 if not gGameOn then
6563 Exit;
6565 if gPause = Enable then
6566 Exit;
6568 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6569 Exit;
6571 gPause := Enable;
6572 g_Game_PauseAllSounds(Enable);
6573 end;
6575 procedure g_Game_PauseAllSounds(Enable: Boolean);
6576 var
6577 i: Integer;
6578 begin
6579 // Òðèããåðû:
6580 if gTriggers <> nil then
6581 for i := 0 to High(gTriggers) do
6582 with gTriggers[i] do
6583 if (TriggerType = TRIGGER_SOUND) and
6584 (Sound <> nil) and
6585 Sound.IsPlaying() then
6586 begin
6587 Sound.Pause(Enable);
6588 end;
6590 // Çâóêè èãðîêîâ:
6591 if gPlayers <> nil then
6592 for i := 0 to High(gPlayers) do
6593 if gPlayers[i] <> nil then
6594 gPlayers[i].PauseSounds(Enable);
6596 // Ìóçûêà:
6597 if gMusic <> nil then
6598 gMusic.Pause(Enable);
6599 end;
6601 procedure g_Game_StopAllSounds(all: Boolean);
6602 var
6603 i: Integer;
6604 begin
6605 if gTriggers <> nil then
6606 for i := 0 to High(gTriggers) do
6607 with gTriggers[i] do
6608 if (TriggerType = TRIGGER_SOUND) and
6609 (Sound <> nil) then
6610 Sound.Stop();
6612 if gMusic <> nil then
6613 gMusic.Stop();
6615 if all then
6616 e_StopChannels();
6617 end;
6619 procedure g_Game_UpdateTriggerSounds();
6620 var
6621 i: Integer;
6622 begin
6623 if gTriggers <> nil then
6624 for i := 0 to High(gTriggers) do
6625 with gTriggers[i] do
6626 if (TriggerType = TRIGGER_SOUND) and
6627 (Sound <> nil) and
6628 (tgcLocal) and
6629 Sound.IsPlaying() then
6630 begin
6631 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6632 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6633 begin
6634 Sound.SetPan(0.5 - tgcPan/255.0);
6635 Sound.SetVolume(tgcVolume/255.0);
6636 end
6637 else
6638 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0);
6639 end;
6640 end;
6642 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6643 begin
6644 Result := False;
6645 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6646 begin
6647 Result := True;
6648 Exit;
6649 end;
6650 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6651 begin
6652 Result := True;
6653 Exit;
6654 end;
6655 if gSpectMode <> SPECT_PLAYERS then
6656 Exit;
6657 if gSpectPID1 = UID then
6658 begin
6659 Result := True;
6660 Exit;
6661 end;
6662 if gSpectViewTwo and (gSpectPID2 = UID) then
6663 begin
6664 Result := True;
6665 Exit;
6666 end;
6667 end;
6669 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6670 var
6671 Pl: TPlayer;
6672 begin
6673 Result := False;
6674 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6675 begin
6676 Result := True;
6677 Exit;
6678 end;
6679 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6680 begin
6681 Result := True;
6682 Exit;
6683 end;
6684 if gSpectMode <> SPECT_PLAYERS then
6685 Exit;
6686 Pl := g_Player_Get(gSpectPID1);
6687 if (Pl <> nil) and (Pl.Team = Team) then
6688 begin
6689 Result := True;
6690 Exit;
6691 end;
6692 if gSpectViewTwo then
6693 begin
6694 Pl := g_Player_Get(gSpectPID2);
6695 if (Pl <> nil) and (Pl.Team = Team) then
6696 begin
6697 Result := True;
6698 Exit;
6699 end;
6700 end;
6701 end;
6703 procedure g_Game_Message(Msg: string; Time: Word);
6704 begin
6705 MessageText := b_Text_Format(Msg);
6706 MessageTime := Time;
6707 end;
6709 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6710 var
6711 a: Integer;
6712 begin
6713 case gAnnouncer of
6714 ANNOUNCE_NONE:
6715 Exit;
6716 ANNOUNCE_ME,
6717 ANNOUNCE_MEPLUS:
6718 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6719 Exit;
6720 end;
6721 for a := 0 to 3 do
6722 if goodsnd[a].IsPlaying() then
6723 Exit;
6725 goodsnd[Random(4)].Play();
6726 end;
6728 procedure g_Game_Announce_KillCombo(Param: Integer);
6729 var
6730 UID: Word;
6731 c, n: Byte;
6732 Pl: TPlayer;
6733 Name: String;
6734 begin
6735 UID := Param and $FFFF;
6736 c := Param shr 16;
6737 if c < 2 then
6738 Exit;
6740 Pl := g_Player_Get(UID);
6741 if Pl = nil then
6742 Name := '?'
6743 else
6744 Name := Pl.Name;
6746 case c of
6747 2: begin
6748 n := 0;
6749 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6750 end;
6751 3: begin
6752 n := 1;
6753 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6754 end;
6755 4: begin
6756 n := 2;
6757 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6758 end;
6759 else begin
6760 n := 3;
6761 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6762 end;
6763 end;
6765 case gAnnouncer of
6766 ANNOUNCE_NONE:
6767 Exit;
6768 ANNOUNCE_ME:
6769 if not g_Game_IsWatchedPlayer(UID) then
6770 Exit;
6771 ANNOUNCE_MEPLUS:
6772 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6773 Exit;
6774 end;
6776 if killsnd[n].IsPlaying() then
6777 killsnd[n].Stop();
6778 killsnd[n].Play();
6779 end;
6781 procedure g_Game_StartVote(Command, Initiator: string);
6782 var
6783 Need: Integer;
6784 begin
6785 if not gVotesEnabled then Exit;
6786 if gGameSettings.GameType <> GT_SERVER then Exit;
6787 if gVoteInProgress or gVotePassed then
6788 begin
6789 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6790 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6791 Exit;
6792 end;
6793 gVoteInProgress := True;
6794 gVotePassed := False;
6795 gVoteTimer := gTime + gVoteTimeout * 1000;
6796 gVoteCount := 0;
6797 gVoted := False;
6798 gVoteCommand := Command;
6800 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6801 Need := Floor((NetClientCount+1)/2.0)+1
6802 else
6803 Need := Floor(NetClientCount/2.0)+1;
6804 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6805 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6806 end;
6808 procedure g_Game_CheckVote;
6809 var
6810 I, Need: Integer;
6811 begin
6812 if gGameSettings.GameType <> GT_SERVER then Exit;
6813 if not gVoteInProgress then Exit;
6815 if (gTime >= gVoteTimer) then
6816 begin
6817 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6818 Need := Floor((NetClientCount+1)/2.0) + 1
6819 else
6820 Need := Floor(NetClientCount/2.0) + 1;
6821 if gVoteCount >= Need then
6822 begin
6823 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6824 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6825 gVotePassed := True;
6826 gVoteCmdTimer := gTime + 5000;
6827 end
6828 else
6829 begin
6830 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6831 MH_SEND_VoteEvent(NET_VE_FAILED);
6832 end;
6833 if NetClients <> nil then
6834 for I := Low(NetClients) to High(NetClients) do
6835 if NetClients[i].Used then
6836 NetClients[i].Voted := False;
6837 gVoteInProgress := False;
6838 gVoted := False;
6839 gVoteCount := 0;
6840 end
6841 else
6842 begin
6843 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6844 Need := Floor((NetClientCount+1)/2.0) + 1
6845 else
6846 Need := Floor(NetClientCount/2.0) + 1;
6847 if gVoteCount >= Need then
6848 begin
6849 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6850 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6851 gVoteInProgress := False;
6852 gVotePassed := True;
6853 gVoteCmdTimer := gTime + 5000;
6854 gVoted := False;
6855 gVoteCount := 0;
6856 if NetClients <> nil then
6857 for I := Low(NetClients) to High(NetClients) do
6858 if NetClients[i].Used then
6859 NetClients[i].Voted := False;
6860 end;
6861 end;
6862 end;
6864 procedure g_Game_LoadMapList(FileName: string);
6865 var
6866 ListFile: TextFile;
6867 s: string;
6868 begin
6869 MapList := nil;
6870 MapIndex := -1;
6872 if not FileExists(FileName) then Exit;
6874 AssignFile(ListFile, FileName);
6875 Reset(ListFile);
6876 while not EOF(ListFile) do
6877 begin
6878 ReadLn(ListFile, s);
6880 s := Trim(s);
6881 if s = '' then Continue;
6883 SetLength(MapList, Length(MapList)+1);
6884 MapList[High(MapList)] := s;
6885 end;
6886 CloseFile(ListFile);
6887 end;
6889 procedure g_Game_SetDebugMode();
6890 begin
6891 gDebugMode := True;
6892 // ×èòû (äàæå â ñâîåé èãðå):
6893 gCheats := True;
6894 end;
6896 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6897 var
6898 i: Word;
6899 begin
6900 if Length(LoadingStat.Msgs) = 0 then
6901 Exit;
6903 with LoadingStat do
6904 begin
6905 if not reWrite then
6906 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6907 if NextMsg = Length(Msgs) then
6908 begin // scroll
6909 for i := 0 to High(Msgs)-1 do
6910 Msgs[i] := Msgs[i+1];
6911 end
6912 else
6913 Inc(NextMsg);
6914 end else
6915 if NextMsg = 0 then
6916 Inc(NextMsg);
6918 Msgs[NextMsg-1] := Text;
6919 CurValue := 0;
6920 MaxValue := Max;
6921 ShowCount := 0;
6922 end;
6924 g_ActiveWindow := nil;
6926 ProcessLoading(true);
6927 end;
6929 procedure g_Game_StepLoading();
6930 begin
6931 with LoadingStat do
6932 begin
6933 Inc(CurValue);
6934 Inc(ShowCount);
6935 if (ShowCount > LOADING_SHOW_STEP) then
6936 begin
6937 ShowCount := 0;
6938 ProcessLoading();
6939 end;
6940 end;
6941 end;
6943 procedure g_Game_ClearLoading();
6944 var
6945 len: Word;
6946 begin
6947 with LoadingStat do
6948 begin
6949 CurValue := 0;
6950 MaxValue := 0;
6951 ShowCount := 0;
6952 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6953 if len < 1 then len := 1;
6954 SetLength(Msgs, len);
6955 for len := Low(Msgs) to High(Msgs) do
6956 Msgs[len] := '';
6957 NextMsg := 0;
6958 end;
6959 end;
6961 procedure Parse_Params(var pars: TParamStrValues);
6962 var
6963 i: Integer;
6964 s: String;
6965 begin
6966 SetLength(pars, 0);
6967 i := 1;
6968 while i <= ParamCount do
6969 begin
6970 s := ParamStr(i);
6971 if (s[1] = '-') and (Length(s) > 1) then
6972 begin
6973 if (s[2] = '-') and (Length(s) > 2) then
6974 begin // Îäèíî÷íûé ïàðàìåòð
6975 SetLength(pars, Length(pars) + 1);
6976 with pars[High(pars)] do
6977 begin
6978 Name := LowerCase(s);
6979 Value := '+';
6980 end;
6981 end
6982 else
6983 if (i < ParamCount) then
6984 begin // Ïàðàìåòð ñî çíà÷åíèåì
6985 Inc(i);
6986 SetLength(pars, Length(pars) + 1);
6987 with pars[High(pars)] do
6988 begin
6989 Name := LowerCase(s);
6990 Value := LowerCase(ParamStr(i));
6991 end;
6992 end;
6993 end;
6995 Inc(i);
6996 end;
6997 end;
6999 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
7000 var
7001 i: Integer;
7002 begin
7003 Result := '';
7004 for i := 0 to High(pars) do
7005 if pars[i].Name = aName then
7006 begin
7007 Result := pars[i].Value;
7008 Break;
7009 end;
7010 end;
7012 procedure g_Game_Process_Params();
7013 var
7014 pars: TParamStrValues;
7015 map: String;
7016 GMode, n: Byte;
7017 LimT, LimS: Integer;
7018 Opt: LongWord;
7019 Lives: Integer;
7020 s: String;
7021 Port: Integer;
7022 ip: String;
7023 F: TextFile;
7024 begin
7025 Parse_Params(pars);
7027 // Debug mode:
7028 s := Find_Param_Value(pars, '--debug');
7029 if (s <> '') then
7030 begin
7031 g_Game_SetDebugMode();
7032 s := Find_Param_Value(pars, '--netdump');
7033 if (s <> '') then
7034 NetDump := True;
7035 end;
7037 // Connect when game loads
7038 ip := Find_Param_Value(pars, '-connect');
7040 if ip <> '' then
7041 begin
7042 s := Find_Param_Value(pars, '-port');
7043 if (s = '') or not TryStrToInt(s, Port) then
7044 Port := 25666;
7046 s := Find_Param_Value(pars, '-pw');
7048 g_Game_StartClient(ip, Port, s);
7049 Exit;
7050 end;
7052 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
7053 if (s <> '') then
7054 begin
7055 gDefaultMegawadStart := s;
7056 end;
7058 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
7059 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
7060 begin
7061 gDefaultMegawadStart := DF_Default_Megawad_Start;
7062 end;
7064 // Start map when game loads:
7065 map := LowerCase(Find_Param_Value(pars, '-map'));
7066 if isWadPath(map) then
7067 begin
7068 // Game mode:
7069 s := Find_Param_Value(pars, '-gm');
7070 GMode := g_Game_TextToMode(s);
7071 if GMode = GM_NONE then GMode := GM_DM;
7072 if GMode = GM_SINGLE then GMode := GM_COOP;
7074 // Time limit:
7075 s := Find_Param_Value(pars, '-limt');
7076 if (s = '') or (not TryStrToInt(s, LimT)) then
7077 LimT := 0;
7078 if LimT < 0 then
7079 LimT := 0;
7081 // Goal limit:
7082 s := Find_Param_Value(pars, '-lims');
7083 if (s = '') or (not TryStrToInt(s, LimS)) then
7084 LimS := 0;
7085 if LimS < 0 then
7086 LimS := 0;
7088 // Lives limit:
7089 s := Find_Param_Value(pars, '-lives');
7090 if (s = '') or (not TryStrToInt(s, Lives)) then
7091 Lives := 0;
7092 if Lives < 0 then
7093 Lives := 0;
7095 // Options:
7096 s := Find_Param_Value(pars, '-opt');
7097 if (s = '') then
7098 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
7099 else
7100 Opt := StrToIntDef(s, 0);
7101 if Opt = 0 then
7102 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
7104 // Close after map:
7105 s := Find_Param_Value(pars, '--close');
7106 if (s <> '') then
7107 gMapOnce := True;
7109 // Delete test map after play:
7110 s := Find_Param_Value(pars, '--testdelete');
7111 if (s <> '') then
7112 begin
7113 gMapToDelete := MapsDir + map;
7114 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
7115 Halt(1);
7116 end;
7118 // Delete temporary WAD after play:
7119 s := Find_Param_Value(pars, '--tempdelete');
7120 if (s <> '') then
7121 begin
7122 gMapToDelete := MapsDir + map;
7123 gTempDelete := True;
7124 end;
7126 // Number of players:
7127 s := Find_Param_Value(pars, '-pl');
7128 if (s = '') then
7129 n := 1
7130 else
7131 n := StrToIntDef(s, 1);
7133 // Start:
7134 s := Find_Param_Value(pars, '-port');
7135 if (s = '') or not TryStrToInt(s, Port) then
7136 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
7137 else
7138 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
7139 end;
7141 // Execute script when game loads:
7142 s := Find_Param_Value(pars, '-exec');
7143 if s <> '' then
7144 begin
7145 if not isWadPath(s) then
7146 s := GameDir + '/' + s;
7148 {$I-}
7149 AssignFile(F, s);
7150 Reset(F);
7151 if IOResult <> 0 then
7152 begin
7153 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7154 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7155 CloseFile(F);
7156 Exit;
7157 end;
7158 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
7159 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7161 while not EOF(F) do
7162 begin
7163 ReadLn(F, s);
7164 if IOResult <> 0 then
7165 begin
7166 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7167 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7168 CloseFile(F);
7169 Exit;
7170 end;
7171 if Pos('#', s) <> 1 then // script comment
7172 g_Console_Process(s, True);
7173 end;
7175 CloseFile(F);
7176 {$I+}
7177 end;
7179 SetLength(pars, 0);
7180 end;
7182 begin
7183 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
7184 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
7185 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7186 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7188 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7189 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7190 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7191 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7193 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7194 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7196 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7197 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7199 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7201 conRegVar('dbg_scale', @g_dbg_scale, 0.01, 100.0, 'experimental deBUG scale mode', '', false);
7202 end.