DEADSOFTWARE

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