DEADSOFTWARE

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