DEADSOFTWARE

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