DEADSOFTWARE

alot of debugging code
[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;
319 postdrawMouse: procedure = nil;
321 procedure g_ResetDynlights ();
322 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
323 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;
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 g_Console_Draw();
3259 if g_debug_Sounds and gGameOn then
3260 begin
3261 for w := 0 to High(e_SoundsArray) do
3262 for h := 0 to e_SoundsArray[w].nRefs do
3263 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3264 end;
3266 if gShowFPS then
3267 begin
3268 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3269 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3270 end;
3272 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3273 e_TextureFontPrint(gScreenWidth-72, 0,
3274 Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]),
3275 gStdFont);
3277 if gGameOn and assigned(postdrawMouse) then postdrawMouse();
3279 if gGameOn then drawProfilers();
3280 end;
3282 procedure g_Game_Quit();
3283 begin
3284 g_Game_StopAllSounds(True);
3285 gMusic.Free();
3286 g_Game_SaveOptions();
3287 g_Game_FreeData();
3288 g_PlayerModel_FreeData();
3289 g_Texture_DeleteAll();
3290 g_Frames_DeleteAll();
3291 g_Menu_Free();
3293 if NetInitDone then g_Net_Free;
3295 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3296 if gMapToDelete <> '' then
3297 g_Game_DeleteTestMap();
3299 gExit := EXIT_QUIT;
3300 PushExitEvent();
3301 end;
3303 procedure g_FatalError(Text: String);
3304 begin
3305 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3306 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3308 gExit := EXIT_SIMPLE;
3309 end;
3311 procedure g_SimpleError(Text: String);
3312 begin
3313 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3314 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3315 end;
3317 procedure g_Game_SetupScreenSize();
3318 const
3319 RES_FACTOR = 4.0 / 3.0;
3320 var
3321 s: Single;
3322 rf: Single;
3323 bw, bh: Word;
3324 begin
3325 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3326 gPlayerScreenSize.X := gScreenWidth-196;
3327 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3328 gPlayerScreenSize.Y := gScreenHeight div 2
3329 else
3330 gPlayerScreenSize.Y := gScreenHeight;
3332 // Ðàçìåð çàäíåãî ïëàíà:
3333 if BackID <> DWORD(-1) then
3334 begin
3335 s := SKY_STRETCH;
3336 if (gScreenWidth*s > gMapInfo.Width) or
3337 (gScreenHeight*s > gMapInfo.Height) then
3338 begin
3339 gBackSize.X := gScreenWidth;
3340 gBackSize.Y := gScreenHeight;
3341 end
3342 else
3343 begin
3344 e_GetTextureSize(BackID, @bw, @bh);
3345 rf := Single(bw) / Single(bh);
3346 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3347 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3348 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3349 if (s < 1.0) then s := 1.0;
3350 gBackSize.X := Round(bw*s);
3351 gBackSize.Y := Round(bh*s);
3352 end;
3353 end;
3354 end;
3356 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3357 begin
3358 g_Window_SetSize(newWidth, newHeight, nowFull);
3359 end;
3361 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3362 begin
3363 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3364 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3365 Exit;
3366 if gPlayer1 = nil then
3367 begin
3368 if g_Game_IsClient then
3369 begin
3370 if NetPlrUID1 > -1 then
3371 begin
3372 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3373 gPlayer1 := g_Player_Get(NetPlrUID1);
3374 end;
3375 Exit;
3376 end;
3378 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3379 Team := gPlayer1Settings.Team;
3381 // Ñîçäàíèå ïåðâîãî èãðîêà:
3382 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3383 gPlayer1Settings.Color,
3384 Team, False));
3385 if gPlayer1 = nil then
3386 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3387 else
3388 begin
3389 gPlayer1.Name := gPlayer1Settings.Name;
3390 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3391 if g_Game_IsServer and g_Game_IsNet then
3392 MH_SEND_PlayerCreate(gPlayer1.UID);
3393 gPlayer1.Respawn(False, True);
3395 if g_Game_IsNet and NetUseMaster then
3396 g_Net_Slist_Update;
3397 end;
3399 Exit;
3400 end;
3401 if gPlayer2 = nil then
3402 begin
3403 if g_Game_IsClient then
3404 begin
3405 if NetPlrUID2 > -1 then
3406 gPlayer2 := g_Player_Get(NetPlrUID2);
3407 Exit;
3408 end;
3410 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3411 Team := gPlayer2Settings.Team;
3413 // Ñîçäàíèå âòîðîãî èãðîêà:
3414 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3415 gPlayer2Settings.Color,
3416 Team, False));
3417 if gPlayer2 = nil then
3418 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3419 else
3420 begin
3421 gPlayer2.Name := gPlayer2Settings.Name;
3422 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3423 if g_Game_IsServer and g_Game_IsNet then
3424 MH_SEND_PlayerCreate(gPlayer2.UID);
3425 gPlayer2.Respawn(False, True);
3427 if g_Game_IsNet and NetUseMaster then
3428 g_Net_Slist_Update;
3429 end;
3431 Exit;
3432 end;
3433 end;
3435 procedure g_Game_RemovePlayer();
3436 var
3437 Pl: TPlayer;
3438 begin
3439 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3440 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3441 Exit;
3442 Pl := gPlayer2;
3443 if Pl <> nil then
3444 begin
3445 if g_Game_IsServer then
3446 begin
3447 Pl.Lives := 0;
3448 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3449 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3450 g_Player_Remove(Pl.UID);
3452 if g_Game_IsNet and NetUseMaster then
3453 g_Net_Slist_Update;
3454 end else
3455 gPlayer2 := nil;
3456 Exit;
3457 end;
3458 Pl := gPlayer1;
3459 if Pl <> nil then
3460 begin
3461 if g_Game_IsServer then
3462 begin
3463 Pl.Lives := 0;
3464 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3465 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3466 g_Player_Remove(Pl.UID);
3468 if g_Game_IsNet and NetUseMaster then
3469 g_Net_Slist_Update;
3470 end else
3471 begin
3472 gPlayer1 := nil;
3473 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3474 end;
3475 Exit;
3476 end;
3477 end;
3479 procedure g_Game_Spectate();
3480 begin
3481 g_Game_RemovePlayer();
3482 if gPlayer1 <> nil then
3483 g_Game_RemovePlayer();
3484 end;
3486 procedure g_Game_SpectateCenterView();
3487 begin
3488 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3489 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3490 end;
3492 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3493 var
3494 i, nPl: Integer;
3495 begin
3496 g_Game_Free();
3498 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3500 g_Game_ClearLoading();
3502 // Íàñòðîéêè èãðû:
3503 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3504 gAimLine := False;
3505 gShowMap := False;
3506 gGameSettings.GameType := GT_SINGLE;
3507 gGameSettings.MaxLives := 0;
3508 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3509 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3510 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3511 gSwitchGameMode := GM_SINGLE;
3513 g_Game_ExecuteEvent('ongamestart');
3515 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3516 g_Game_SetupScreenSize();
3518 // Ñîçäàíèå ïåðâîãî èãðîêà:
3519 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3520 gPlayer1Settings.Color,
3521 gPlayer1Settings.Team, False));
3522 if gPlayer1 = nil then
3523 begin
3524 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3525 Exit;
3526 end;
3528 gPlayer1.Name := gPlayer1Settings.Name;
3529 nPl := 1;
3531 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3532 if TwoPlayers then
3533 begin
3534 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3535 gPlayer2Settings.Color,
3536 gPlayer2Settings.Team, False));
3537 if gPlayer2 = nil then
3538 begin
3539 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3540 Exit;
3541 end;
3543 gPlayer2.Name := gPlayer2Settings.Name;
3544 Inc(nPl);
3545 end;
3547 // Çàãðóçêà è çàïóñê êàðòû:
3548 if not g_Game_StartMap(MAP, True) then
3549 begin
3550 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3551 Exit;
3552 end;
3554 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3555 g_Player_Init();
3557 // Ñîçäàåì áîòîâ:
3558 for i := nPl+1 to nPlayers do
3559 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3560 end;
3562 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3563 TimeLimit, GoalLimit: Word;
3564 MaxLives: Byte;
3565 Options: LongWord; nPlayers: Byte);
3566 var
3567 i, nPl: Integer;
3568 begin
3569 g_Game_Free();
3571 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3573 g_Game_ClearLoading();
3575 // Íàñòðîéêè èãðû:
3576 gGameSettings.GameType := GT_CUSTOM;
3577 gGameSettings.GameMode := GameMode;
3578 gSwitchGameMode := GameMode;
3579 gGameSettings.TimeLimit := TimeLimit;
3580 gGameSettings.GoalLimit := GoalLimit;
3581 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3582 gGameSettings.Options := Options;
3584 gCoopTotalMonstersKilled := 0;
3585 gCoopTotalSecretsFound := 0;
3586 gCoopTotalMonsters := 0;
3587 gCoopTotalSecrets := 0;
3588 gAimLine := False;
3589 gShowMap := False;
3591 g_Game_ExecuteEvent('ongamestart');
3593 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3594 g_Game_SetupScreenSize();
3596 // Ðåæèì íàáëþäàòåëÿ:
3597 if nPlayers = 0 then
3598 begin
3599 gPlayer1 := nil;
3600 gPlayer2 := nil;
3601 end;
3603 nPl := 0;
3604 if nPlayers >= 1 then
3605 begin
3606 // Ñîçäàíèå ïåðâîãî èãðîêà:
3607 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3608 gPlayer1Settings.Color,
3609 gPlayer1Settings.Team, False));
3610 if gPlayer1 = nil then
3611 begin
3612 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3613 Exit;
3614 end;
3616 gPlayer1.Name := gPlayer1Settings.Name;
3617 Inc(nPl);
3618 end;
3620 if nPlayers >= 2 then
3621 begin
3622 // Ñîçäàíèå âòîðîãî èãðîêà:
3623 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3624 gPlayer2Settings.Color,
3625 gPlayer2Settings.Team, False));
3626 if gPlayer2 = nil then
3627 begin
3628 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3629 Exit;
3630 end;
3632 gPlayer2.Name := gPlayer2Settings.Name;
3633 Inc(nPl);
3634 end;
3636 // Çàãðóçêà è çàïóñê êàðòû:
3637 if not g_Game_StartMap(Map, True) then
3638 begin
3639 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3640 Exit;
3641 end;
3643 // Íåò òî÷åê ïîÿâëåíèÿ:
3644 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3645 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3646 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3647 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3648 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3649 begin
3650 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3651 Exit;
3652 end;
3654 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3655 g_Player_Init();
3657 // Ñîçäàåì áîòîâ:
3658 for i := nPl+1 to nPlayers do
3659 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3660 end;
3662 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3663 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3664 Options: LongWord; nPlayers: Byte;
3665 IPAddr: LongWord; Port: Word);
3666 begin
3667 g_Game_Free();
3669 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3671 g_Game_ClearLoading();
3673 // Íàñòðîéêè èãðû:
3674 gGameSettings.GameType := GT_SERVER;
3675 gGameSettings.GameMode := GameMode;
3676 gSwitchGameMode := GameMode;
3677 gGameSettings.TimeLimit := TimeLimit;
3678 gGameSettings.GoalLimit := GoalLimit;
3679 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3680 gGameSettings.Options := Options;
3682 gCoopTotalMonstersKilled := 0;
3683 gCoopTotalSecretsFound := 0;
3684 gCoopTotalMonsters := 0;
3685 gCoopTotalSecrets := 0;
3686 gAimLine := False;
3687 gShowMap := False;
3689 g_Game_ExecuteEvent('ongamestart');
3691 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3692 g_Game_SetupScreenSize();
3694 // Ðåæèì íàáëþäàòåëÿ:
3695 if nPlayers = 0 then
3696 begin
3697 gPlayer1 := nil;
3698 gPlayer2 := nil;
3699 end;
3701 if nPlayers >= 1 then
3702 begin
3703 // Ñîçäàíèå ïåðâîãî èãðîêà:
3704 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3705 gPlayer1Settings.Color,
3706 gPlayer1Settings.Team, False));
3707 if gPlayer1 = nil then
3708 begin
3709 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3710 Exit;
3711 end;
3713 gPlayer1.Name := gPlayer1Settings.Name;
3714 end;
3716 if nPlayers >= 2 then
3717 begin
3718 // Ñîçäàíèå âòîðîãî èãðîêà:
3719 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3720 gPlayer2Settings.Color,
3721 gPlayer2Settings.Team, False));
3722 if gPlayer2 = nil then
3723 begin
3724 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3725 Exit;
3726 end;
3728 gPlayer2.Name := gPlayer2Settings.Name;
3729 end;
3731 // Ñòàðòóåì ñåðâåð
3732 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3733 begin
3734 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3735 Exit;
3736 end;
3738 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3740 // Çàãðóçêà è çàïóñê êàðòû:
3741 if not g_Game_StartMap(Map, True) then
3742 begin
3743 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3744 Exit;
3745 end;
3747 // Íåò òî÷åê ïîÿâëåíèÿ:
3748 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3749 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3750 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3751 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3752 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3753 begin
3754 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3755 Exit;
3756 end;
3758 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3759 g_Player_Init();
3761 NetState := NET_STATE_GAME;
3762 end;
3764 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3765 var
3766 Map: String;
3767 WadName: string;
3768 Ptr: Pointer;
3769 T: Cardinal;
3770 MID: Byte;
3771 State: Byte;
3772 OuterLoop: Boolean;
3773 newResPath: string;
3774 InMsg: TMsg;
3775 begin
3776 g_Game_Free();
3778 State := 0;
3779 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3780 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3782 g_Game_ClearLoading();
3784 // Íàñòðîéêè èãðû:
3785 gGameSettings.GameType := GT_CLIENT;
3787 gCoopTotalMonstersKilled := 0;
3788 gCoopTotalSecretsFound := 0;
3789 gCoopTotalMonsters := 0;
3790 gCoopTotalSecrets := 0;
3791 gAimLine := False;
3792 gShowMap := False;
3794 g_Game_ExecuteEvent('ongamestart');
3796 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3797 g_Game_SetupScreenSize();
3799 NetState := NET_STATE_AUTH;
3801 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3802 // Ñòàðòóåì êëèåíò
3803 if not g_Net_Connect(Addr, Port) then
3804 begin
3805 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3806 NetState := NET_STATE_NONE;
3807 Exit;
3808 end;
3810 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3811 MC_SEND_Info(PW);
3812 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3814 OuterLoop := True;
3815 while OuterLoop do
3816 begin
3817 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3818 begin
3819 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3820 begin
3821 Ptr := NetEvent.packet^.data;
3822 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
3823 continue;
3825 MID := InMsg.ReadByte();
3827 if (MID = NET_MSG_INFO) and (State = 0) then
3828 begin
3829 NetMyID := InMsg.ReadByte();
3830 NetPlrUID1 := InMsg.ReadWord();
3832 WadName := InMsg.ReadString();
3833 Map := InMsg.ReadString();
3835 gWADHash := InMsg.ReadMD5();
3837 gGameSettings.GameMode := InMsg.ReadByte();
3838 gSwitchGameMode := gGameSettings.GameMode;
3839 gGameSettings.GoalLimit := InMsg.ReadWord();
3840 gGameSettings.TimeLimit := InMsg.ReadWord();
3841 gGameSettings.MaxLives := InMsg.ReadByte();
3842 gGameSettings.Options := InMsg.ReadLongWord();
3843 T := InMsg.ReadLongWord();
3845 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3846 if newResPath = '' then
3847 begin
3848 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3849 newResPath := g_Res_DownloadWAD(WadName);
3850 if newResPath = '' then
3851 begin
3852 g_FatalError(_lc[I_NET_ERR_HASH]);
3853 enet_packet_destroy(NetEvent.packet);
3854 NetState := NET_STATE_NONE;
3855 Exit;
3856 end;
3857 end;
3858 newResPath := ExtractRelativePath(MapsDir, newResPath);
3860 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3861 gPlayer1Settings.Color,
3862 gPlayer1Settings.Team, False));
3864 if gPlayer1 = nil then
3865 begin
3866 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3868 enet_packet_destroy(NetEvent.packet);
3869 NetState := NET_STATE_NONE;
3870 Exit;
3871 end;
3873 gPlayer1.Name := gPlayer1Settings.Name;
3874 gPlayer1.UID := NetPlrUID1;
3875 gPlayer1.Reset(True);
3877 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3878 begin
3879 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3881 enet_packet_destroy(NetEvent.packet);
3882 NetState := NET_STATE_NONE;
3883 Exit;
3884 end;
3886 gTime := T;
3888 State := 1;
3889 OuterLoop := False;
3890 enet_packet_destroy(NetEvent.packet);
3891 break;
3892 end
3893 else
3894 enet_packet_destroy(NetEvent.packet);
3895 end
3896 else
3897 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3898 begin
3899 State := 0;
3900 if (NetEvent.data <= NET_DISC_MAX) then
3901 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3902 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3903 OuterLoop := False;
3904 Break;
3905 end;
3906 end;
3908 ProcessLoading();
3910 e_PollInput();
3912 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3913 begin
3914 State := 0;
3915 break;
3916 end;
3917 end;
3919 if State <> 1 then
3920 begin
3921 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3922 NetState := NET_STATE_NONE;
3923 Exit;
3924 end;
3926 gLMSRespawn := LMS_RESPAWN_NONE;
3927 gLMSRespawnTime := 0;
3929 g_Player_Init();
3930 NetState := NET_STATE_GAME;
3931 MC_SEND_FullStateRequest;
3932 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3933 end;
3935 procedure g_Game_SaveOptions();
3936 begin
3937 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3938 end;
3940 procedure g_Game_ChangeMap(MapPath: String);
3941 var
3942 Force: Boolean;
3943 begin
3944 g_Game_ClearLoading();
3946 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3947 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3948 if gExitByTrigger then
3949 begin
3950 Force := False;
3951 gExitByTrigger := False;
3952 end;
3953 if not g_Game_StartMap(MapPath, Force) then
3954 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3955 end;
3957 procedure g_Game_Restart();
3958 var
3959 Map: string;
3960 begin
3961 if g_Game_IsClient then
3962 Exit;
3963 map := g_ExtractFileName(gMapInfo.Map);
3965 MessageTime := 0;
3966 gGameOn := False;
3967 g_Game_ClearLoading();
3968 g_Game_StartMap(Map, True);
3969 end;
3971 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
3972 var
3973 NewWAD, ResName: String;
3974 I: Integer;
3975 begin
3976 g_Map_Free();
3977 g_Player_RemoveAllCorpses();
3979 if (not g_Game_IsClient) and
3980 (gSwitchGameMode <> gGameSettings.GameMode) and
3981 (gGameSettings.GameMode <> GM_SINGLE) then
3982 begin
3983 if gSwitchGameMode = GM_CTF then
3984 gGameSettings.MaxLives := 0;
3985 gGameSettings.GameMode := gSwitchGameMode;
3986 Force := True;
3987 end else
3988 gSwitchGameMode := gGameSettings.GameMode;
3990 g_Player_ResetTeams();
3992 if Pos(':\', Map) > 0 then
3993 begin
3994 NewWAD := g_ExtractWadName(Map);
3995 ResName := g_ExtractFileName(Map);
3996 if g_Game_IsServer then
3997 begin
3998 gWADHash := MD5File(MapsDir + NewWAD);
3999 g_Game_LoadWAD(NewWAD);
4000 end else
4001 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4002 g_Game_ClientWAD(NewWAD, gWADHash);
4003 end else
4004 ResName := Map;
4006 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4007 if Result then
4008 begin
4009 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4011 gState := STATE_NONE;
4012 g_ActiveWindow := nil;
4013 gGameOn := True;
4015 DisableCheats();
4016 ResetTimer();
4018 if gGameSettings.GameMode = GM_CTF then
4019 begin
4020 g_Map_ResetFlag(FLAG_RED);
4021 g_Map_ResetFlag(FLAG_BLUE);
4022 // CTF, à ôëàãîâ íåò:
4023 if not g_Map_HaveFlagPoints() then
4024 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4025 end;
4026 end
4027 else
4028 begin
4029 gState := STATE_MENU;
4030 gGameOn := False;
4031 end;
4033 gExit := 0;
4034 gPause := False;
4035 gTime := 0;
4036 NetTimeToUpdate := 1;
4037 NetTimeToReliable := 0;
4038 NetTimeToMaster := NetMasterRate;
4039 gLMSRespawn := LMS_RESPAWN_NONE;
4040 gLMSRespawnTime := 0;
4041 gMissionFailed := False;
4042 gNextMap := '';
4044 gCoopMonstersKilled := 0;
4045 gCoopSecretsFound := 0;
4047 gVoteInProgress := False;
4048 gVotePassed := False;
4049 gVoteCount := 0;
4050 gVoted := False;
4052 gStatsOff := False;
4054 if not gGameOn then Exit;
4056 g_Game_SpectateCenterView();
4058 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4059 begin
4060 gLMSRespawn := LMS_RESPAWN_WARMUP;
4061 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4062 gLMSSoftSpawn := True;
4063 if NetMode = NET_SERVER then
4064 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4065 else
4066 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4067 end;
4069 if NetMode = NET_SERVER then
4070 begin
4071 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4073 // Ìàñòåðñåðâåð
4074 if NetUseMaster then
4075 begin
4076 if (NetMHost = nil) or (NetMPeer = nil) then
4077 if not g_Net_Slist_Connect then
4078 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4080 g_Net_Slist_Update;
4081 end;
4083 if NetClients <> nil then
4084 for I := 0 to High(NetClients) do
4085 if NetClients[I].Used then
4086 begin
4087 NetClients[I].Voted := False;
4088 if NetClients[I].RequestedFullUpdate then
4089 begin
4090 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4091 NetClients[I].RequestedFullUpdate := False;
4092 end;
4093 end;
4095 g_Net_UnbanNonPermHosts();
4096 end;
4098 if gLastMap then
4099 begin
4100 gCoopTotalMonstersKilled := 0;
4101 gCoopTotalSecretsFound := 0;
4102 gCoopTotalMonsters := 0;
4103 gCoopTotalSecrets := 0;
4104 gLastMap := False;
4105 end;
4107 g_Game_ExecuteEvent('onmapstart');
4108 end;
4110 procedure SetFirstLevel();
4111 begin
4112 gNextMap := '';
4114 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4115 if MapList = nil then
4116 Exit;
4118 SortSArray(MapList);
4119 gNextMap := MapList[Low(MapList)];
4121 MapList := nil;
4122 end;
4124 procedure g_Game_ExitLevel(Map: Char16);
4125 begin
4126 gNextMap := Map;
4128 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4129 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4130 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4131 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4133 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4134 if gGameSettings.GameType = GT_SINGLE then
4135 gExit := EXIT_ENDLEVELSINGLE
4136 else // Âûøëè â âûõîä â Ñâîåé èãðå
4137 begin
4138 gExit := EXIT_ENDLEVELCUSTOM;
4139 if gGameSettings.GameMode = GM_COOP then
4140 g_Player_RememberAll;
4142 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4143 begin
4144 gLastMap := True;
4145 if gGameSettings.GameMode = GM_COOP then
4146 gStatsOff := True;
4148 gStatsPressed := True;
4149 gNextMap := 'MAP01';
4151 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4152 g_Game_NextLevel;
4154 if g_Game_IsNet then
4155 begin
4156 MH_SEND_GameStats();
4157 MH_SEND_CoopStats();
4158 end;
4159 end;
4160 end;
4161 end;
4163 procedure g_Game_RestartLevel();
4164 var
4165 Map: string;
4166 begin
4167 if gGameSettings.GameMode = GM_SINGLE then
4168 begin
4169 g_Game_Restart();
4170 Exit;
4171 end;
4172 gExit := EXIT_ENDLEVELCUSTOM;
4173 Map := g_ExtractFileName(gMapInfo.Map);
4174 gNextMap := Map;
4175 end;
4177 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4178 var
4179 gWAD: String;
4180 begin
4181 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4182 Exit;
4183 if not g_Game_IsClient then
4184 Exit;
4185 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4186 if gWAD = '' then
4187 begin
4188 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4189 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4190 if gWAD = '' then
4191 begin
4192 g_Game_Free();
4193 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4194 Exit;
4195 end;
4196 end;
4197 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4198 g_Game_LoadWAD(NewWAD);
4199 end;
4201 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4202 var
4203 i, n, nb, nr: Integer;
4205 function monRespawn (mon: TMonster): Boolean;
4206 begin
4207 result := false; // don't stop
4208 if not mon.FNoRespawn then mon.Respawn();
4209 end;
4211 begin
4212 if not g_Game_IsServer then Exit;
4213 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4214 gLMSRespawn := LMS_RESPAWN_NONE;
4215 gLMSRespawnTime := 0;
4216 MessageTime := 0;
4218 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4219 begin
4220 gMissionFailed := True;
4221 g_Game_RestartLevel;
4222 Exit;
4223 end;
4225 n := 0; nb := 0; nr := 0;
4226 for i := Low(gPlayers) to High(gPlayers) do
4227 if (gPlayers[i] <> nil) and
4228 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4229 (gPlayers[i] is TBot)) then
4230 begin
4231 Inc(n);
4232 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4233 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4234 end;
4236 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4237 begin
4238 // wait a second until the fuckers finally decide to join
4239 gLMSRespawn := LMS_RESPAWN_WARMUP;
4240 gLMSRespawnTime := gTime + 1000;
4241 gLMSSoftSpawn := NoMapRestart;
4242 Exit;
4243 end;
4245 g_Player_RemoveAllCorpses;
4246 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4247 if g_Game_IsNet then
4248 MH_SEND_GameEvent(NET_EV_LMS_START);
4250 for i := Low(gPlayers) to High(gPlayers) do
4251 begin
4252 if gPlayers[i] = nil then continue;
4253 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4254 // don't touch normal spectators
4255 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4256 begin
4257 gPlayers[i].FNoRespawn := True;
4258 gPlayers[i].Lives := 0;
4259 if g_Game_IsNet then
4260 MH_SEND_PlayerStats(gPlayers[I].UID);
4261 continue;
4262 end;
4263 gPlayers[i].FNoRespawn := False;
4264 gPlayers[i].Lives := gGameSettings.MaxLives;
4265 gPlayers[i].Respawn(False, True);
4266 if gGameSettings.GameMode = GM_COOP then
4267 begin
4268 gPlayers[i].Frags := 0;
4269 gPlayers[i].RecallState;
4270 end;
4271 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4272 gPlayer1 := g_Player_Get(gLMSPID1);
4273 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4274 gPlayer2 := g_Player_Get(gLMSPID2);
4275 end;
4277 g_Items_RestartRound();
4280 g_Mons_ForEach(monRespawn);
4282 gLMSSoftSpawn := False;
4283 end;
4285 function g_Game_GetFirstMap(WAD: String): String;
4286 begin
4287 Result := '';
4289 MapList := g_Map_GetMapsList(WAD);
4290 if MapList = nil then
4291 Exit;
4293 SortSArray(MapList);
4294 Result := MapList[Low(MapList)];
4296 if not g_Map_Exist(WAD + ':\' + Result) then
4297 Result := '';
4299 MapList := nil;
4300 end;
4302 function g_Game_GetNextMap(): String;
4303 var
4304 I: Integer;
4305 Map: string;
4306 begin
4307 Result := '';
4309 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4310 if MapList = nil then
4311 Exit;
4313 Map := g_ExtractFileName(gMapInfo.Map);
4315 SortSArray(MapList);
4316 MapIndex := -255;
4317 for I := Low(MapList) to High(MapList) do
4318 if Map = MapList[I] then
4319 begin
4320 MapIndex := I;
4321 Break;
4322 end;
4324 if MapIndex <> -255 then
4325 begin
4326 if MapIndex = High(MapList) then
4327 Result := MapList[Low(MapList)]
4328 else
4329 Result := MapList[MapIndex + 1];
4331 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4332 end;
4334 MapList := nil;
4335 end;
4337 procedure g_Game_NextLevel();
4338 begin
4339 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4340 gExit := EXIT_ENDLEVELCUSTOM
4341 else
4342 begin
4343 gExit := EXIT_ENDLEVELSINGLE;
4344 Exit;
4345 end;
4347 if gNextMap <> '' then Exit;
4348 gNextMap := g_Game_GetNextMap();
4349 end;
4351 function g_Game_IsTestMap(): Boolean;
4352 begin
4353 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4354 end;
4356 procedure g_Game_DeleteTestMap();
4357 var
4358 a: Integer;
4359 MapName: Char16;
4360 WadName: string;
4362 WAD: TWADFile;
4363 MapList: SArray;
4364 time: Integer;
4366 begin
4367 a := Pos('.wad:\', gMapToDelete);
4368 if a = 0 then
4369 Exit;
4371 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4372 WadName := Copy(gMapToDelete, 1, a + 3);
4373 Delete(gMapToDelete, 1, a + 5);
4374 gMapToDelete := UpperCase(gMapToDelete);
4375 MapName := '';
4376 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4379 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4380 if MapName <> TEST_MAP_NAME then
4381 Exit;
4383 if not gTempDelete then
4384 begin
4385 time := g_GetFileTime(WadName);
4386 WAD := TWADFile.Create();
4388 // ×èòàåì Wad-ôàéë:
4389 if not WAD.ReadFile(WadName) then
4390 begin // Íåò òàêîãî WAD-ôàéëà
4391 WAD.Free();
4392 Exit;
4393 end;
4395 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4396 WAD.CreateImage();
4397 MapList := WAD.GetResourcesList('');
4399 if MapList <> nil then
4400 for a := 0 to High(MapList) do
4401 if MapList[a] = MapName then
4402 begin
4403 // Óäàëÿåì è ñîõðàíÿåì:
4404 WAD.RemoveResource('', MapName);
4405 WAD.SaveTo(WadName);
4406 Break;
4407 end;
4409 WAD.Free();
4410 g_SetFileTime(WadName, time);
4411 end else
4413 if gTempDelete then DeleteFile(WadName);
4414 end;
4416 procedure GameCVars(P: SArray);
4417 var
4418 a, b: Integer;
4419 stat: TPlayerStatArray;
4420 cmd, s: string;
4421 config: TConfig;
4422 begin
4423 stat := nil;
4424 cmd := LowerCase(P[0]);
4425 if cmd = 'r_showfps' then
4426 begin
4427 if (Length(P) > 1) and
4428 ((P[1] = '1') or (P[1] = '0')) then
4429 gShowFPS := (P[1][1] = '1');
4431 if gShowFPS then
4432 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4433 else
4434 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4435 end
4436 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4437 begin
4438 with gGameSettings do
4439 begin
4440 if (Length(P) > 1) and
4441 ((P[1] = '1') or (P[1] = '0')) then
4442 begin
4443 if (P[1][1] = '1') then
4444 Options := Options or GAME_OPTION_TEAMDAMAGE
4445 else
4446 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4447 end;
4449 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4450 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4451 else
4452 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4454 if g_Game_IsNet then MH_SEND_GameSettings;
4455 end;
4456 end
4457 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4458 begin
4459 with gGameSettings do
4460 begin
4461 if (Length(P) > 1) and
4462 ((P[1] = '1') or (P[1] = '0')) then
4463 begin
4464 if (P[1][1] = '1') then
4465 Options := Options or GAME_OPTION_WEAPONSTAY
4466 else
4467 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4468 end;
4470 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4471 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4472 else
4473 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4475 if g_Game_IsNet then MH_SEND_GameSettings;
4476 end;
4477 end
4478 else if cmd = 'g_gamemode' then
4479 begin
4480 a := g_Game_TextToMode(P[1]);
4481 if a = GM_SINGLE then a := GM_COOP;
4482 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4483 begin
4484 gSwitchGameMode := a;
4485 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4486 (gState = STATE_INTERSINGLE) then
4487 gSwitchGameMode := GM_SINGLE;
4488 if not gGameOn then
4489 gGameSettings.GameMode := gSwitchGameMode;
4490 end;
4491 if gSwitchGameMode = gGameSettings.GameMode then
4492 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4493 [g_Game_ModeToText(gGameSettings.GameMode)]))
4494 else
4495 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4496 [g_Game_ModeToText(gGameSettings.GameMode),
4497 g_Game_ModeToText(gSwitchGameMode)]));
4498 end
4499 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4500 begin
4501 with gGameSettings do
4502 begin
4503 if (Length(P) > 1) and
4504 ((P[1] = '1') or (P[1] = '0')) then
4505 begin
4506 if (P[1][1] = '1') then
4507 Options := Options or GAME_OPTION_ALLOWEXIT
4508 else
4509 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4510 end;
4512 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4513 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4514 else
4515 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4516 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4518 if g_Game_IsNet then MH_SEND_GameSettings;
4519 end;
4520 end
4521 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4522 begin
4523 with gGameSettings do
4524 begin
4525 if (Length(P) > 1) and
4526 ((P[1] = '1') or (P[1] = '0')) then
4527 begin
4528 if (P[1][1] = '1') then
4529 Options := Options or GAME_OPTION_MONSTERS
4530 else
4531 Options := Options and (not GAME_OPTION_MONSTERS);
4532 end;
4534 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4535 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4536 else
4537 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4538 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4540 if g_Game_IsNet then MH_SEND_GameSettings;
4541 end;
4542 end
4543 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4544 begin
4545 with gGameSettings do
4546 begin
4547 if (Length(P) > 1) and
4548 ((P[1] = '1') or (P[1] = '0')) then
4549 begin
4550 if (P[1][1] = '1') then
4551 Options := Options or GAME_OPTION_BOTVSPLAYER
4552 else
4553 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4554 end;
4556 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4557 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4558 else
4559 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4561 if g_Game_IsNet then MH_SEND_GameSettings;
4562 end;
4563 end
4564 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4565 begin
4566 with gGameSettings do
4567 begin
4568 if (Length(P) > 1) and
4569 ((P[1] = '1') or (P[1] = '0')) then
4570 begin
4571 if (P[1][1] = '1') then
4572 Options := Options or GAME_OPTION_BOTVSMONSTER
4573 else
4574 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4575 end;
4577 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4578 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4579 else
4580 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4582 if g_Game_IsNet then MH_SEND_GameSettings;
4583 end;
4584 end
4585 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4586 begin
4587 if Length(P) > 1 then
4588 begin
4589 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4590 gGameSettings.WarmupTime := 30
4591 else
4592 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4593 end;
4595 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4596 [gGameSettings.WarmupTime]));
4597 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4598 end
4599 else if cmd = 'net_interp' then
4600 begin
4601 if (Length(P) > 1) then
4602 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4604 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4605 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4606 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4607 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4608 config.Free();
4609 end
4610 else if cmd = 'net_forceplayerupdate' then
4611 begin
4612 if (Length(P) > 1) and
4613 ((P[1] = '1') or (P[1] = '0')) then
4614 NetForcePlayerUpdate := (P[1][1] = '1');
4616 if NetForcePlayerUpdate then
4617 g_Console_Add('net_forceplayerupdate = 1')
4618 else
4619 g_Console_Add('net_forceplayerupdate = 0');
4620 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4621 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4622 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4623 config.Free();
4624 end
4625 else if cmd = 'net_predictself' then
4626 begin
4627 if (Length(P) > 1) and
4628 ((P[1] = '1') or (P[1] = '0')) then
4629 NetPredictSelf := (P[1][1] = '1');
4631 if NetPredictSelf then
4632 g_Console_Add('net_predictself = 1')
4633 else
4634 g_Console_Add('net_predictself = 0');
4635 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4636 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4637 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4638 config.Free();
4639 end
4640 else if cmd = 'sv_name' then
4641 begin
4642 if (Length(P) > 1) and (Length(P[1]) > 0) then
4643 begin
4644 NetServerName := P[1];
4645 if Length(NetServerName) > 64 then
4646 SetLength(NetServerName, 64);
4647 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4648 g_Net_Slist_Update;
4649 end;
4651 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4652 end
4653 else if cmd = 'sv_passwd' then
4654 begin
4655 if (Length(P) > 1) and (Length(P[1]) > 0) then
4656 begin
4657 NetPassword := P[1];
4658 if Length(NetPassword) > 24 then
4659 SetLength(NetPassword, 24);
4660 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4661 g_Net_Slist_Update;
4662 end;
4664 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4665 end
4666 else if cmd = 'sv_maxplrs' then
4667 begin
4668 if (Length(P) > 1) then
4669 begin
4670 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4671 if g_Game_IsServer and g_Game_IsNet then
4672 begin
4673 b := 0;
4674 for a := 0 to High(NetClients) do
4675 if NetClients[a].Used then
4676 begin
4677 Inc(b);
4678 if b > NetMaxClients then
4679 begin
4680 s := g_Player_Get(NetClients[a].Player).Name;
4681 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4682 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4683 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4684 end;
4685 end;
4686 if NetUseMaster then
4687 g_Net_Slist_Update;
4688 end;
4689 end;
4691 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4692 end
4693 else if cmd = 'sv_public' then
4694 begin
4695 if (Length(P) > 1) then
4696 begin
4697 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4698 if g_Game_IsServer and g_Game_IsNet then
4699 if NetUseMaster then
4700 begin
4701 if NetMPeer = nil then
4702 if not g_Net_Slist_Connect() then
4703 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4704 g_Net_Slist_Update();
4705 end
4706 else
4707 if NetMPeer <> nil then
4708 g_Net_Slist_Disconnect();
4709 end;
4711 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4712 end
4713 else if cmd = 'sv_intertime' then
4714 begin
4715 if (Length(P) > 1) then
4716 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4718 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4719 end
4720 else if cmd = 'p1_name' then
4721 begin
4722 if (Length(P) > 1) and gGameOn then
4723 begin
4724 if g_Game_IsClient then
4725 begin
4726 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4727 MC_SEND_PlayerSettings;
4728 end
4729 else
4730 if gPlayer1 <> nil then
4731 begin
4732 gPlayer1.Name := b_Text_Unformat(P[1]);
4733 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4734 end
4735 else
4736 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4737 end;
4738 end
4739 else if cmd = 'p2_name' then
4740 begin
4741 if (Length(P) > 1) and gGameOn then
4742 begin
4743 if g_Game_IsClient then
4744 begin
4745 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4746 MC_SEND_PlayerSettings;
4747 end
4748 else
4749 if gPlayer2 <> nil then
4750 begin
4751 gPlayer2.Name := b_Text_Unformat(P[1]);
4752 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4753 end
4754 else
4755 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4756 end;
4757 end
4758 else if cmd = 'p1_color' then
4759 begin
4760 if Length(P) > 3 then
4761 if g_Game_IsClient then
4762 begin
4763 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4764 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4765 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4766 MC_SEND_PlayerSettings;
4767 end
4768 else
4769 if gPlayer1 <> nil then
4770 begin
4771 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4772 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4773 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4774 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4775 end
4776 else
4777 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4778 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4779 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4780 end
4781 else if (cmd = 'p2_color') and not g_Game_IsNet then
4782 begin
4783 if Length(P) > 3 then
4784 if g_Game_IsClient then
4785 begin
4786 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4787 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4788 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4789 MC_SEND_PlayerSettings;
4790 end
4791 else
4792 if gPlayer2 <> nil then
4793 begin
4794 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4795 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4796 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4797 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4798 end
4799 else
4800 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4801 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4802 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4803 end
4804 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4805 begin
4806 if cmd = 'r_showtime' then
4807 begin
4808 if (Length(P) > 1) and
4809 ((P[1] = '1') or (P[1] = '0')) then
4810 gShowTime := (P[1][1] = '1');
4812 if gShowTime then
4813 g_Console_Add(_lc[I_MSG_TIME_ON])
4814 else
4815 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4816 end
4817 else if cmd = 'r_showscore' then
4818 begin
4819 if (Length(P) > 1) and
4820 ((P[1] = '1') or (P[1] = '0')) then
4821 gShowGoals := (P[1][1] = '1');
4823 if gShowGoals then
4824 g_Console_Add(_lc[I_MSG_SCORE_ON])
4825 else
4826 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4827 end
4828 else if cmd = 'r_showstat' then
4829 begin
4830 if (Length(P) > 1) and
4831 ((P[1] = '1') or (P[1] = '0')) then
4832 gShowStat := (P[1][1] = '1');
4834 if gShowStat then
4835 g_Console_Add(_lc[I_MSG_STATS_ON])
4836 else
4837 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4838 end
4839 else if cmd = 'r_showkillmsg' then
4840 begin
4841 if (Length(P) > 1) and
4842 ((P[1] = '1') or (P[1] = '0')) then
4843 gShowKillMsg := (P[1][1] = '1');
4845 if gShowKillMsg then
4846 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4847 else
4848 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4849 end
4850 else if cmd = 'r_showlives' then
4851 begin
4852 if (Length(P) > 1) and
4853 ((P[1] = '1') or (P[1] = '0')) then
4854 gShowLives := (P[1][1] = '1');
4856 if gShowLives then
4857 g_Console_Add(_lc[I_MSG_LIVES_ON])
4858 else
4859 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4860 end
4861 else if cmd = 'r_showspect' then
4862 begin
4863 if (Length(P) > 1) and
4864 ((P[1] = '1') or (P[1] = '0')) then
4865 gSpectHUD := (P[1][1] = '1');
4867 if gSpectHUD then
4868 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4869 else
4870 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4871 end
4872 else if cmd = 'r_showping' then
4873 begin
4874 if (Length(P) > 1) and
4875 ((P[1] = '1') or (P[1] = '0')) then
4876 gShowPing := (P[1][1] = '1');
4878 if gShowPing then
4879 g_Console_Add(_lc[I_MSG_PING_ON])
4880 else
4881 g_Console_Add(_lc[I_MSG_PING_OFF]);
4882 end
4883 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4884 begin
4885 if Length(P) > 1 then
4886 begin
4887 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4888 gGameSettings.GoalLimit := 0
4889 else
4890 begin
4891 b := 0;
4893 if gGameSettings.GameMode = GM_DM then
4894 begin // DM
4895 stat := g_Player_GetStats();
4896 if stat <> nil then
4897 for a := 0 to High(stat) do
4898 if stat[a].Frags > b then
4899 b := stat[a].Frags;
4900 end
4901 else // TDM/CTF
4902 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4904 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4905 end;
4907 if g_Game_IsNet then MH_SEND_GameSettings;
4908 end;
4910 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4911 end
4912 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4913 begin
4914 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4915 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4917 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4918 [gGameSettings.TimeLimit div 3600,
4919 (gGameSettings.TimeLimit div 60) mod 60,
4920 gGameSettings.TimeLimit mod 60]));
4921 if g_Game_IsNet then MH_SEND_GameSettings;
4922 end
4923 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4924 begin
4925 if Length(P) > 1 then
4926 begin
4927 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4928 gGameSettings.MaxLives := 0
4929 else
4930 begin
4931 b := 0;
4932 stat := g_Player_GetStats();
4933 if stat <> nil then
4934 for a := 0 to High(stat) do
4935 if stat[a].Lives > b then
4936 b := stat[a].Lives;
4937 gGameSettings.MaxLives :=
4938 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4939 end;
4940 end;
4942 g_Console_Add(Format(_lc[I_MSG_LIVES],
4943 [gGameSettings.MaxLives]));
4944 if g_Game_IsNet then MH_SEND_GameSettings;
4945 end;
4946 end;
4947 end;
4949 // profiler console commands
4950 procedure ProfilerCommands (P: SArray);
4951 var
4952 cmd: string;
4954 function getBool (idx: Integer): Integer;
4955 begin
4956 if (idx < 0) or (idx > High(P)) then begin result := -1; exit; end;
4957 result := 0;
4958 if (P[idx] = '1') or (P[idx] = 'on') or (P[idx] = 'true') or (P[idx] = 'tan') then result := 1;
4959 end;
4961 procedure binaryFlag (var flag: Boolean; msg: string);
4962 begin
4963 if (Length(p) > 2) then
4964 begin
4965 g_Console_Add('too many arguments to '''+P[0]+'''');
4966 end
4967 else
4968 begin
4969 case getBool(1) of
4970 -1: begin end;
4971 0: flag := false;
4972 1: flag := true;
4973 end;
4974 if flag then g_Console_Add(msg+': tan') else g_Console_Add(msg+': ona');
4975 end;
4976 end;
4978 begin
4979 //if not gDebugMode then exit;
4980 cmd := LowerCase(P[0]);
4982 if (cmd = 'pf_draw_frame') then begin binaryFlag(g_profile_frame_draw, 'render profiles'); exit; end;
4983 if (cmd = 'pf_update_frame') then begin binaryFlag(g_profile_frame_update, 'update profiles (not yet)'); exit; end;
4984 if (cmd = 'pf_coldet') then begin binaryFlag(g_profile_collision, 'coldet profiles'); exit; end;
4985 if (cmd = 'pf_los') then begin binaryFlag(g_profile_los, 'monster LOS profiles'); exit; end;
4986 if (cmd = 'r_sq_draw') then begin binaryFlag(gdbg_map_use_accel_render, 'accelerated rendering'); exit; end;
4987 if (cmd = 'cd_sq_enabled') then begin binaryFlag(gdbg_map_use_accel_coldet, 'accelerated map coldet'); exit; end;
4988 if (cmd = 'mon_sq_enabled') then begin binaryFlag(gmon_debug_use_sqaccel, 'accelerated monster coldet'); exit; end;
4989 if (cmd = 'wtrace_sq_enabled') then begin binaryFlag(gwep_debug_fast_trace, 'accelerated weapon hitscan'); exit; end;
4990 if (cmd = 'pr_enabled') then begin binaryFlag(gpart_dbg_enabled, 'particles'); exit; end;
4991 if (cmd = 'pr_phys_enabled') then begin binaryFlag(gpart_dbg_phys_enabled, 'particle physics'); exit; end;
4992 if (cmd = 'los_enabled') then begin binaryFlag(gmon_dbg_los_enabled, 'LOS calculations'); exit; end;
4993 if (cmd = 'mon_think') then begin binaryFlag(gmon_debug_think, 'monster thinking'); exit; end;
4994 end;
4997 procedure DebugCommands(P: SArray);
4998 var
4999 a, b: Integer;
5000 cmd: string;
5001 //pt: TPoint;
5002 mon: TMonster;
5003 begin
5004 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5005 if gDebugMode then
5006 begin
5007 cmd := LowerCase(P[0]);
5008 if cmd = 'd_window' then
5009 begin
5010 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5011 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5012 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5013 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5014 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5015 end
5016 else if cmd = 'd_sounds' then
5017 begin
5018 if (Length(P) > 1) and
5019 ((P[1] = '1') or (P[1] = '0')) then
5020 g_Debug_Sounds := (P[1][1] = '1');
5022 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5023 end
5024 else if cmd = 'd_frames' then
5025 begin
5026 if (Length(P) > 1) and
5027 ((P[1] = '1') or (P[1] = '0')) then
5028 g_Debug_Frames := (P[1][1] = '1');
5030 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5031 end
5032 else if cmd = 'd_winmsg' then
5033 begin
5034 if (Length(P) > 1) and
5035 ((P[1] = '1') or (P[1] = '0')) then
5036 g_Debug_WinMsgs := (P[1][1] = '1');
5038 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5039 end
5040 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5041 begin
5042 if (Length(P) > 1) and
5043 ((P[1] = '1') or (P[1] = '0')) then
5044 g_Debug_MonsterOff := (P[1][1] = '1');
5046 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5047 end
5048 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5049 begin
5050 if Length(P) > 1 then
5051 case P[1][1] of
5052 '0': g_debug_BotAIOff := 0;
5053 '1': g_debug_BotAIOff := 1;
5054 '2': g_debug_BotAIOff := 2;
5055 '3': g_debug_BotAIOff := 3;
5056 end;
5058 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5059 end
5060 else if cmd = 'd_monster' then
5061 begin
5062 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
5063 if Length(P) < 2 then
5064 begin
5065 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5066 g_Console_Add('ID | Name');
5067 for b := MONSTER_DEMON to MONSTER_MAN do
5068 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
5069 end else
5070 begin
5071 a := StrToIntDef(P[1], 0);
5072 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5073 a := g_Monsters_GetIDByName(P[1]);
5075 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5076 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5077 else
5078 begin
5079 with gPlayer1.Obj do
5080 begin
5081 mon := g_Monsters_Create(a,
5082 X + Rect.X + (Rect.Width div 2),
5083 Y + Rect.Y + Rect.Height,
5084 gPlayer1.Direction, True);
5085 end;
5086 if (Length(P) > 2) and (mon <> nil) then
5087 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5088 end;
5089 end;
5090 end
5091 else if (cmd = 'd_health') then
5092 begin
5093 if (Length(P) > 1) and
5094 ((P[1] = '1') or (P[1] = '0')) then
5095 g_debug_HealthBar := (P[1][1] = '1');
5097 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5098 end
5099 else if (cmd = 'd_player') then
5100 begin
5101 if (Length(P) > 1) and
5102 ((P[1] = '1') or (P[1] = '0')) then
5103 g_debug_Player := (P[1][1] = '1');
5105 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5106 end
5107 else if (cmd = 'd_joy') then
5108 begin
5109 for a := 1 to 8 do
5110 g_Console_Add(e_JoystickStateToString(a));
5111 end;
5112 end
5113 else
5114 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5115 end;
5118 procedure GameCheats(P: SArray);
5119 var
5120 cmd: string;
5121 f, a: Integer;
5122 plr: TPlayer;
5123 begin
5124 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5125 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5126 begin
5127 g_Console_Add('not available');
5128 exit;
5129 end;
5130 plr := gPlayer1;
5131 if plr = nil then
5132 begin
5133 g_Console_Add('where is the player?!');
5134 exit;
5135 end;
5136 cmd := LowerCase(P[0]);
5137 // god
5138 if cmd = 'god' then
5139 begin
5140 plr.GodMode := not plr.GodMode;
5141 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5142 exit;
5143 end;
5144 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5145 if cmd = 'give' then
5146 begin
5147 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5148 for f := 1 to High(P) do
5149 begin
5150 cmd := LowerCase(P[f]);
5151 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5152 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5153 if cmd = 'exit' then
5154 begin
5155 if gTriggers <> nil then
5156 begin
5157 for a := 0 to High(gTriggers) do
5158 begin
5159 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5160 begin
5161 g_Console_Add('player left the map');
5162 gExitByTrigger := True;
5163 g_Game_ExitLevel(gTriggers[a].Data.MapName);
5164 break;
5165 end;
5166 end;
5167 end;
5168 continue;
5169 end;
5171 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5172 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5173 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5174 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5175 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5177 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5178 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5180 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5181 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;
5183 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5184 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5186 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5187 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5189 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5190 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5192 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5193 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5194 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5196 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5197 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5198 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5199 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;
5200 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5201 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5203 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;
5204 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;
5205 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;
5206 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;
5207 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;
5208 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;
5210 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5211 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;
5213 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;
5214 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;
5216 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5218 if cmd = 'ammo' then
5219 begin
5220 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5221 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5222 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5223 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5224 plr.GiveItem(ITEM_AMMO_FUELCAN);
5225 g_Console_Add('player got some ammo');
5226 continue;
5227 end;
5229 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5230 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5232 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5233 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5235 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5236 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5238 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5239 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5241 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5243 if cmd = 'weapons' then
5244 begin
5245 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5246 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5247 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5248 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5249 plr.GiveItem(ITEM_WEAPON_PLASMA);
5250 plr.GiveItem(ITEM_WEAPON_BFG);
5251 g_Console_Add('player got weapons');
5252 continue;
5253 end;
5255 if cmd = 'keys' then
5256 begin
5257 plr.GiveItem(ITEM_KEY_RED);
5258 plr.GiveItem(ITEM_KEY_GREEN);
5259 plr.GiveItem(ITEM_KEY_BLUE);
5260 g_Console_Add('player got all keys');
5261 continue;
5262 end;
5264 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5265 end;
5266 exit;
5267 end;
5268 // open
5269 if cmd = 'open' then
5270 begin
5271 g_Console_Add('player activated sesame');
5272 g_Triggers_OpenAll();
5273 exit;
5274 end;
5275 // fly
5276 if cmd = 'fly' then
5277 begin
5278 gFly := not gFly;
5279 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5280 exit;
5281 end;
5282 // noclip
5283 if cmd = 'noclip' then
5284 begin
5285 plr.SwitchNoClip;
5286 g_Console_Add('wall hardeness adjusted');
5287 exit;
5288 end;
5289 // notarget
5290 if cmd = 'notarget' then
5291 begin
5292 plr.NoTarget := not plr.NoTarget;
5293 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5294 exit;
5295 end;
5296 // noreload
5297 if cmd = 'noreload' then
5298 begin
5299 plr.NoReload := not plr.NoReload;
5300 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5301 exit;
5302 end;
5303 // speedy
5304 if cmd = 'speedy' then
5305 begin
5306 MAX_RUNVEL := 32-MAX_RUNVEL;
5307 g_Console_Add('speed adjusted');
5308 exit;
5309 end;
5310 // jumpy
5311 if cmd = 'jumpy' then
5312 begin
5313 VEL_JUMP := 30-VEL_JUMP;
5314 g_Console_Add('jump height adjusted');
5315 exit;
5316 end;
5317 // automap
5318 if cmd = 'automap' then
5319 begin
5320 gShowMap := not gShowMap;
5321 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5322 exit;
5323 end;
5324 // aimline
5325 if cmd = 'aimline' then
5326 begin
5327 gAimLine := not gAimLine;
5328 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5329 exit;
5330 end;
5331 end;
5333 procedure GameCommands(P: SArray);
5334 var
5335 a, b: Integer;
5336 s, pw: String;
5337 chstr: string;
5338 cmd: string;
5339 pl: pTNetClient = nil;
5340 plr: TPlayer;
5341 prt: Word;
5342 nm: Boolean;
5343 listen: LongWord;
5344 begin
5345 // Îáùèå êîìàíäû:
5346 cmd := LowerCase(P[0]);
5347 chstr := '';
5348 if (cmd = 'quit') or
5349 (cmd = 'exit') then
5350 begin
5351 g_Game_Free();
5352 g_Game_Quit();
5353 Exit;
5354 end
5355 else if cmd = 'pause' then
5356 begin
5357 if (g_ActiveWindow = nil) then
5358 g_Game_Pause(not gPause);
5359 end
5360 else if cmd = 'endgame' then
5361 gExit := EXIT_SIMPLE
5362 else if cmd = 'restart' then
5363 begin
5364 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5365 begin
5366 if g_Game_IsClient then
5367 begin
5368 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5369 Exit;
5370 end;
5371 g_Game_Restart();
5372 end else
5373 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5374 end
5375 else if cmd = 'kick' then
5376 begin
5377 if g_Game_IsServer then
5378 begin
5379 if Length(P) < 2 then
5380 begin
5381 g_Console_Add('kick <name>');
5382 Exit;
5383 end;
5384 if P[1] = '' then
5385 begin
5386 g_Console_Add('kick <name>');
5387 Exit;
5388 end;
5390 if g_Game_IsNet then
5391 pl := g_Net_Client_ByName(P[1]);
5392 if (pl <> nil) then
5393 begin
5394 s := g_Net_ClientName_ByID(pl^.ID);
5395 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5396 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5397 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5398 if NetUseMaster then
5399 g_Net_Slist_Update;
5400 end else if gPlayers <> nil then
5401 for a := Low(gPlayers) to High(gPlayers) do
5402 if gPlayers[a] <> nil then
5403 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5404 begin
5405 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5406 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5407 continue;
5408 gPlayers[a].Lives := 0;
5409 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5410 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5411 g_Player_Remove(gPlayers[a].UID);
5412 if NetUseMaster then
5413 g_Net_Slist_Update;
5414 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5415 g_Bot_MixNames();
5416 end;
5417 end else
5418 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5419 end
5420 else if cmd = 'kick_id' then
5421 begin
5422 if g_Game_IsServer and g_Game_IsNet then
5423 begin
5424 if Length(P) < 2 then
5425 begin
5426 g_Console_Add('kick_id <client ID>');
5427 Exit;
5428 end;
5429 if P[1] = '' then
5430 begin
5431 g_Console_Add('kick_id <client ID>');
5432 Exit;
5433 end;
5435 a := StrToIntDef(P[1], 0);
5436 if (NetClients <> nil) and (a <= High(NetClients)) then
5437 begin
5438 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5439 begin
5440 s := g_Net_ClientName_ByID(NetClients[a].ID);
5441 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5442 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5443 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5444 if NetUseMaster then
5445 g_Net_Slist_Update;
5446 end;
5447 end;
5448 end else
5449 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5450 end
5451 else if cmd = 'ban' then
5452 begin
5453 if g_Game_IsServer and g_Game_IsNet then
5454 begin
5455 if Length(P) < 2 then
5456 begin
5457 g_Console_Add('ban <name>');
5458 Exit;
5459 end;
5460 if P[1] = '' then
5461 begin
5462 g_Console_Add('ban <name>');
5463 Exit;
5464 end;
5466 pl := g_Net_Client_ByName(P[1]);
5467 if (pl <> nil) then
5468 begin
5469 s := g_Net_ClientName_ByID(pl^.ID);
5470 g_Net_BanHost(pl^.Peer^.address.host, False);
5471 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5472 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5473 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5474 if NetUseMaster then
5475 g_Net_Slist_Update;
5476 end else
5477 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5478 end else
5479 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5480 end
5481 else if cmd = 'ban_id' then
5482 begin
5483 if g_Game_IsServer and g_Game_IsNet then
5484 begin
5485 if Length(P) < 2 then
5486 begin
5487 g_Console_Add('ban_id <client ID>');
5488 Exit;
5489 end;
5490 if P[1] = '' then
5491 begin
5492 g_Console_Add('ban_id <client ID>');
5493 Exit;
5494 end;
5496 a := StrToIntDef(P[1], 0);
5497 if (NetClients <> nil) and (a <= High(NetClients)) then
5498 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5499 begin
5500 s := g_Net_ClientName_ByID(NetClients[a].ID);
5501 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5502 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5503 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5504 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5505 if NetUseMaster then
5506 g_Net_Slist_Update;
5507 end;
5508 end else
5509 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5510 end
5511 else if cmd = 'permban' then
5512 begin
5513 if g_Game_IsServer and g_Game_IsNet then
5514 begin
5515 if Length(P) < 2 then
5516 begin
5517 g_Console_Add('permban <name>');
5518 Exit;
5519 end;
5520 if P[1] = '' then
5521 begin
5522 g_Console_Add('permban <name>');
5523 Exit;
5524 end;
5526 pl := g_Net_Client_ByName(P[1]);
5527 if (pl <> nil) then
5528 begin
5529 s := g_Net_ClientName_ByID(pl^.ID);
5530 g_Net_BanHost(pl^.Peer^.address.host);
5531 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5532 g_Net_SaveBanList();
5533 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5534 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5535 if NetUseMaster then
5536 g_Net_Slist_Update;
5537 end else
5538 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5539 end else
5540 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5541 end
5542 else if cmd = 'permban_id' then
5543 begin
5544 if g_Game_IsServer and g_Game_IsNet then
5545 begin
5546 if Length(P) < 2 then
5547 begin
5548 g_Console_Add('permban_id <client ID>');
5549 Exit;
5550 end;
5551 if P[1] = '' then
5552 begin
5553 g_Console_Add('permban_id <client ID>');
5554 Exit;
5555 end;
5557 a := StrToIntDef(P[1], 0);
5558 if (NetClients <> nil) and (a <= High(NetClients)) then
5559 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5560 begin
5561 s := g_Net_ClientName_ByID(NetClients[a].ID);
5562 g_Net_BanHost(NetClients[a].Peer^.address.host);
5563 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5564 g_Net_SaveBanList();
5565 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5566 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5567 if NetUseMaster then
5568 g_Net_Slist_Update;
5569 end;
5570 end else
5571 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5572 end
5573 else if cmd = 'unban' then
5574 begin
5575 if g_Game_IsServer and g_Game_IsNet then
5576 begin
5577 if Length(P) < 2 then
5578 begin
5579 g_Console_Add('unban <IP Address>');
5580 Exit;
5581 end;
5582 if P[1] = '' then
5583 begin
5584 g_Console_Add('unban <IP Address>');
5585 Exit;
5586 end;
5588 if g_Net_UnbanHost(P[1]) then
5589 begin
5590 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5591 g_Net_SaveBanList();
5592 end else
5593 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5594 end else
5595 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5596 end
5597 else if cmd = 'clientlist' then
5598 begin
5599 if g_Game_IsServer and g_Game_IsNet then
5600 begin
5601 b := 0;
5602 if NetClients <> nil then
5603 for a := Low(NetClients) to High(NetClients) do
5604 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5605 begin
5606 plr := g_Player_Get(NetClients[a].Player);
5607 if plr = nil then continue;
5608 Inc(b);
5609 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5610 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5611 end;
5612 if b = 0 then
5613 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5614 end else
5615 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5616 end
5617 else if cmd = 'connect' then
5618 begin
5619 if (NetMode = NET_NONE) then
5620 begin
5621 if Length(P) < 2 then
5622 begin
5623 g_Console_Add('connect <IP> [port] [password]');
5624 Exit;
5625 end;
5626 if P[1] = '' then
5627 begin
5628 g_Console_Add('connect <IP> [port] [password]');
5629 Exit;
5630 end;
5632 if Length(P) > 2 then
5633 prt := StrToIntDef(P[2], 25666)
5634 else
5635 prt := 25666;
5637 if Length(P) > 3 then
5638 pw := P[3]
5639 else
5640 pw := '';
5642 g_Game_StartClient(P[1], prt, pw);
5643 end;
5644 end
5645 else if cmd = 'disconnect' then
5646 begin
5647 if (NetMode = NET_CLIENT) then
5648 g_Net_Disconnect();
5649 end
5650 else if cmd = 'reconnect' then
5651 begin
5652 if (NetMode = NET_SERVER) then
5653 Exit;
5655 if (NetMode = NET_CLIENT) then
5656 begin
5657 g_Net_Disconnect();
5658 gExit := EXIT_SIMPLE;
5659 EndGame;
5660 end;
5662 //TODO: Use last successful password to reconnect, instead of ''
5663 g_Game_StartClient(NetClientIP, NetClientPort, '');
5664 end
5665 else if (cmd = 'addbot') or
5666 (cmd = 'bot_add') then
5667 begin
5668 if Length(P) > 1 then
5669 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5670 else
5671 g_Bot_Add(TEAM_NONE, 2);
5672 end
5673 else if cmd = 'bot_addlist' then
5674 begin
5675 if Length(P) > 1 then
5676 if Length(P) = 2 then
5677 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5678 else
5679 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5680 end
5681 else if cmd = 'bot_removeall' then
5682 g_Bot_RemoveAll()
5683 else if cmd = 'chat' then
5684 begin
5685 if g_Game_IsNet then
5686 begin
5687 if Length(P) > 1 then
5688 begin
5689 for a := 1 to High(P) do
5690 chstr := chstr + P[a] + ' ';
5692 if Length(chstr) > 200 then SetLength(chstr, 200);
5694 if Length(chstr) < 1 then
5695 begin
5696 g_Console_Add('chat <text>');
5697 Exit;
5698 end;
5700 chstr := b_Text_Format(chstr);
5701 if g_Game_IsClient then
5702 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5703 else
5704 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5705 end
5706 else
5707 g_Console_Add('chat <text>');
5708 end else
5709 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5710 end
5711 else if cmd = 'teamchat' then
5712 begin
5713 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5714 begin
5715 if Length(P) > 1 then
5716 begin
5717 for a := 1 to High(P) do
5718 chstr := chstr + P[a] + ' ';
5720 if Length(chstr) > 200 then SetLength(chstr, 200);
5722 if Length(chstr) < 1 then
5723 begin
5724 g_Console_Add('teamchat <text>');
5725 Exit;
5726 end;
5728 chstr := b_Text_Format(chstr);
5729 if g_Game_IsClient then
5730 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5731 else
5732 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5733 gPlayer1Settings.Team);
5734 end
5735 else
5736 g_Console_Add('teamchat <text>');
5737 end else
5738 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5739 end
5740 else if cmd = 'game' then
5741 begin
5742 if gGameSettings.GameType <> GT_NONE then
5743 begin
5744 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5745 Exit;
5746 end;
5747 if Length(P) = 1 then
5748 begin
5749 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5750 Exit;
5751 end;
5752 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5753 P[1] := addWadExtension(P[1]);
5754 if FileExists(MapsDir + P[1]) then
5755 begin
5756 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5757 if Length(P) < 3 then
5758 begin
5759 SetLength(P, 3);
5760 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5761 end;
5763 s := P[1] + ':\' + UpperCase(P[2]);
5765 if g_Map_Exist(MapsDir + s) then
5766 begin
5767 // Çàïóñêàåì ñâîþ èãðó
5768 g_Game_Free();
5769 with gGameSettings do
5770 begin
5771 GameMode := g_Game_TextToMode(gcGameMode);
5772 if gSwitchGameMode <> GM_NONE then
5773 GameMode := gSwitchGameMode;
5774 if GameMode = GM_NONE then GameMode := GM_DM;
5775 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5776 b := 1;
5777 if Length(P) >= 4 then
5778 b := StrToIntDef(P[3], 1);
5779 g_Game_StartCustom(s, GameMode, TimeLimit,
5780 GoalLimit, MaxLives, Options, b);
5781 end;
5782 end
5783 else
5784 if P[2] = '' then
5785 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5786 else
5787 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5788 end else
5789 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5790 end
5791 else if cmd = 'host' then
5792 begin
5793 if gGameSettings.GameType <> GT_NONE then
5794 begin
5795 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5796 Exit;
5797 end;
5798 if Length(P) < 4 then
5799 begin
5800 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5801 Exit;
5802 end;
5803 if not StrToIp(P[1], listen) then
5804 Exit;
5805 prt := StrToIntDef(P[2], 25666);
5807 P[3] := addWadExtension(P[3]);
5808 if FileExists(MapsDir + P[3]) then
5809 begin
5810 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5811 if Length(P) < 5 then
5812 begin
5813 SetLength(P, 5);
5814 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5815 end;
5817 s := P[3] + ':\' + UpperCase(P[4]);
5819 if g_Map_Exist(MapsDir + s) then
5820 begin
5821 // Çàïóñêàåì ñâîþ èãðó
5822 g_Game_Free();
5823 with gGameSettings do
5824 begin
5825 GameMode := g_Game_TextToMode(gcGameMode);
5826 if gSwitchGameMode <> GM_NONE then
5827 GameMode := gSwitchGameMode;
5828 if GameMode = GM_NONE then GameMode := GM_DM;
5829 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5830 b := 0;
5831 if Length(P) >= 6 then
5832 b := StrToIntDef(P[5], 0);
5833 g_Game_StartServer(s, GameMode, TimeLimit,
5834 GoalLimit, MaxLives, Options, b, listen, prt);
5835 end;
5836 end
5837 else
5838 if P[4] = '' then
5839 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5840 else
5841 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5842 end else
5843 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5844 end
5845 else if cmd = 'map' then
5846 begin
5847 if Length(P) = 1 then
5848 begin
5849 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5850 begin
5851 g_Console_Add(cmd + ' <MAP>');
5852 g_Console_Add(cmd + ' <WAD> [MAP]');
5853 end else
5854 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5855 end else
5856 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5857 begin
5858 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5859 if Length(P) < 3 then
5860 begin
5861 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5862 s := UpperCase(P[1]);
5863 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5864 begin // Êàðòà íàøëàñü
5865 gExitByTrigger := False;
5866 if gGameOn then
5867 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5868 gNextMap := s;
5869 gExit := EXIT_ENDLEVELCUSTOM;
5870 end
5871 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5872 g_Game_ChangeMap(s);
5873 end else
5874 begin
5875 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5876 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5877 P[1] := addWadExtension(P[1]);
5878 if FileExists(MapsDir + P[1]) then
5879 begin
5880 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5881 SetLength(P, 3);
5882 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5884 s := P[1] + ':\' + P[2];
5886 if g_Map_Exist(MapsDir + s) then
5887 begin
5888 gExitByTrigger := False;
5889 if gGameOn then
5890 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5891 gNextMap := s;
5892 gExit := EXIT_ENDLEVELCUSTOM;
5893 end
5894 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5895 g_Game_ChangeMap(s);
5896 end else
5897 if P[2] = '' then
5898 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5899 else
5900 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5901 end else
5902 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5903 end;
5904 end else
5905 begin
5906 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5907 P[1] := addWadExtension(P[1]);
5908 if FileExists(MapsDir + P[1]) then
5909 begin
5910 // Íàøëè WAD ôàéë
5911 P[2] := UpperCase(P[2]);
5912 s := P[1] + ':\' + P[2];
5914 if g_Map_Exist(MapsDir + s) then
5915 begin // Íàøëè êàðòó
5916 gExitByTrigger := False;
5917 if gGameOn then
5918 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5919 gNextMap := s;
5920 gExit := EXIT_ENDLEVELCUSTOM;
5921 end
5922 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5923 g_Game_ChangeMap(s);
5924 end else
5925 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5926 end else
5927 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5928 end;
5929 end else
5930 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5931 end
5932 else if cmd = 'nextmap' then
5933 begin
5934 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5935 g_Console_Add(_lc[I_MSG_NOT_GAME])
5936 else begin
5937 nm := True;
5938 if Length(P) = 1 then
5939 begin
5940 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5941 begin
5942 g_Console_Add(cmd + ' <MAP>');
5943 g_Console_Add(cmd + ' <WAD> [MAP]');
5944 end else begin
5945 nm := False;
5946 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5947 end;
5948 end else
5949 begin
5950 nm := False;
5951 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5952 begin
5953 if Length(P) < 3 then
5954 begin
5955 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5956 s := UpperCase(P[1]);
5957 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5958 begin // Êàðòà íàøëàñü
5959 gExitByTrigger := False;
5960 gNextMap := s;
5961 nm := True;
5962 end else
5963 begin
5964 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5965 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5966 P[1] := addWadExtension(P[1]);
5967 if FileExists(MapsDir + P[1]) then
5968 begin
5969 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5970 SetLength(P, 3);
5971 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5973 s := P[1] + ':\' + P[2];
5975 if g_Map_Exist(MapsDir + s) then
5976 begin // Óñòàíàâëèâàåì êàðòó
5977 gExitByTrigger := False;
5978 gNextMap := s;
5979 nm := True;
5980 end else
5981 if P[2] = '' then
5982 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5983 else
5984 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5985 end else
5986 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5987 end;
5988 end else
5989 begin
5990 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5991 P[1] := addWadExtension(P[1]);
5992 if FileExists(MapsDir + P[1]) then
5993 begin
5994 // Íàøëè WAD ôàéë
5995 P[2] := UpperCase(P[2]);
5996 s := P[1] + ':\' + P[2];
5998 if g_Map_Exist(MapsDir + s) then
5999 begin // Íàøëè êàðòó
6000 gExitByTrigger := False;
6001 gNextMap := s;
6002 nm := True;
6003 end else
6004 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6005 end else
6006 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6007 end;
6008 end else
6009 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6010 end;
6011 if nm then
6012 if gNextMap = '' then
6013 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6014 else
6015 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6016 end;
6017 end
6018 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6019 begin
6020 if not gGameOn then
6021 g_Console_Add(_lc[I_MSG_NOT_GAME])
6022 else
6023 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6024 begin
6025 gExitByTrigger := False;
6026 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6027 if (gNextMap = '') and (gTriggers <> nil) then
6028 for a := 0 to High(gTriggers) do
6029 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6030 begin
6031 gExitByTrigger := True;
6032 gNextMap := gTriggers[a].Data.MapName;
6033 Break;
6034 end;
6035 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6036 if gNextMap = '' then
6037 gNextMap := g_Game_GetNextMap();
6038 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6039 if Pos(':\', gNextMap) = 0 then
6040 s := gGameSettings.WAD + ':\' + gNextMap
6041 else
6042 s := gNextMap;
6043 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6044 if g_Map_Exist(MapsDir + s) then
6045 gExit := EXIT_ENDLEVELCUSTOM
6046 else
6047 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6048 end else
6049 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6050 end
6051 else if (cmd = 'event') then
6052 begin
6053 if (Length(P) <= 1) then
6054 begin
6055 for a := 0 to High(gEvents) do
6056 if gEvents[a].Command = '' then
6057 g_Console_Add(gEvents[a].Name + ' <none>')
6058 else
6059 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6060 Exit;
6061 end;
6062 if (Length(P) = 2) then
6063 begin
6064 for a := 0 to High(gEvents) do
6065 if gEvents[a].Name = P[1] then
6066 if gEvents[a].Command = '' then
6067 g_Console_Add(gEvents[a].Name + ' <none>')
6068 else
6069 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6070 Exit;
6071 end;
6072 for a := 0 to High(gEvents) do
6073 if gEvents[a].Name = P[1] then
6074 begin
6075 gEvents[a].Command := '';
6076 for b := 2 to High(P) do
6077 if Pos(' ', P[b]) = 0 then
6078 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6079 else
6080 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6081 gEvents[a].Command := Trim(gEvents[a].Command);
6082 Exit;
6083 end;
6084 end
6085 // Êîìàíäû Ñâîåé èãðû:
6086 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6087 begin
6088 if cmd = 'bot_addred' then
6089 begin
6090 if Length(P) > 1 then
6091 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6092 else
6093 g_Bot_Add(TEAM_RED, 2);
6094 end
6095 else if cmd = 'bot_addblue' then
6096 begin
6097 if Length(P) > 1 then
6098 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6099 else
6100 g_Bot_Add(TEAM_BLUE, 2);
6101 end
6102 else if cmd = 'suicide' then
6103 begin
6104 if gGameOn then
6105 begin
6106 if g_Game_IsClient then
6107 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6108 else
6109 begin
6110 if gPlayer1 <> nil then
6111 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6112 if gPlayer2 <> nil then
6113 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6114 end;
6115 end;
6116 end
6117 else if cmd = 'spectate' then
6118 begin
6119 if not gGameOn then
6120 Exit;
6121 g_Game_Spectate();
6122 end
6123 else if cmd = 'say' then
6124 begin
6125 if g_Game_IsServer and g_Game_IsNet then
6126 begin
6127 if Length(P) > 1 then
6128 begin
6129 chstr := '';
6130 for a := 1 to High(P) do
6131 chstr := chstr + P[a] + ' ';
6133 if Length(chstr) > 200 then SetLength(chstr, 200);
6135 if Length(chstr) < 1 then
6136 begin
6137 g_Console_Add('say <text>');
6138 Exit;
6139 end;
6141 chstr := b_Text_Format(chstr);
6142 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6143 end
6144 else g_Console_Add('say <text>');
6145 end else
6146 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6147 end
6148 else if cmd = 'tell' then
6149 begin
6150 if g_Game_IsServer and g_Game_IsNet then
6151 begin
6152 if (Length(P) > 2) and (P[1] <> '') then
6153 begin
6154 chstr := '';
6155 for a := 2 to High(P) do
6156 chstr := chstr + P[a] + ' ';
6158 if Length(chstr) > 200 then SetLength(chstr, 200);
6160 if Length(chstr) < 1 then
6161 begin
6162 g_Console_Add('tell <playername> <text>');
6163 Exit;
6164 end;
6166 pl := g_Net_Client_ByName(P[1]);
6167 if pl <> nil then
6168 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6169 else
6170 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6171 end
6172 else g_Console_Add('tell <playername> <text>');
6173 end else
6174 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6175 end
6176 else if (cmd = 'overtime') and not g_Game_IsClient then
6177 begin
6178 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6179 Exit;
6180 // Äîïîëíèòåëüíîå âðåìÿ:
6181 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6183 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6184 [gGameSettings.TimeLimit div 3600,
6185 (gGameSettings.TimeLimit div 60) mod 60,
6186 gGameSettings.TimeLimit mod 60]));
6187 if g_Game_IsNet then MH_SEND_GameSettings;
6188 end
6189 else if (cmd = 'rcon_password') and g_Game_IsClient then
6190 begin
6191 if (Length(P) <= 1) then
6192 g_Console_Add('rcon_password <password>')
6193 else
6194 MC_SEND_RCONPassword(P[1]);
6195 end
6196 else if cmd = 'rcon' then
6197 begin
6198 if g_Game_IsClient then
6199 begin
6200 if Length(P) > 1 then
6201 begin
6202 chstr := '';
6203 for a := 1 to High(P) do
6204 chstr := chstr + P[a] + ' ';
6206 if Length(chstr) > 200 then SetLength(chstr, 200);
6208 if Length(chstr) < 1 then
6209 begin
6210 g_Console_Add('rcon <command>');
6211 Exit;
6212 end;
6214 MC_SEND_RCONCommand(chstr);
6215 end
6216 else g_Console_Add('rcon <command>');
6217 end;
6218 end
6219 else if cmd = 'ready' then
6220 begin
6221 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6222 gLMSRespawnTime := gTime + 100;
6223 end
6224 else if (cmd = 'callvote') and g_Game_IsNet then
6225 begin
6226 if Length(P) > 1 then
6227 begin
6228 chstr := '';
6229 for a := 1 to High(P) do begin
6230 if a > 1 then chstr := chstr + ' ';
6231 chstr := chstr + P[a];
6232 end;
6234 if Length(chstr) > 200 then SetLength(chstr, 200);
6236 if Length(chstr) < 1 then
6237 begin
6238 g_Console_Add('callvote <command>');
6239 Exit;
6240 end;
6242 if g_Game_IsClient then
6243 MC_SEND_Vote(True, chstr)
6244 else
6245 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6246 g_Console_Process('vote', True);
6247 end
6248 else
6249 g_Console_Add('callvote <command>');
6250 end
6251 else if (cmd = 'vote') and g_Game_IsNet then
6252 begin
6253 if g_Game_IsClient then
6254 MC_SEND_Vote(False)
6255 else if gVoteInProgress then
6256 begin
6257 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6258 a := Floor((NetClientCount+1)/2.0) + 1
6259 else
6260 a := Floor(NetClientCount/2.0) + 1;
6261 if gVoted then
6262 begin
6263 Dec(gVoteCount);
6264 gVoted := False;
6265 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6266 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6267 end
6268 else
6269 begin
6270 Inc(gVoteCount);
6271 gVoted := True;
6272 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6273 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6274 g_Game_CheckVote;
6275 end;
6276 end;
6277 end
6278 end;
6279 end;
6281 procedure g_TakeScreenShot();
6282 var
6283 a: Word;
6284 FileName: string;
6285 ssdir, t: string;
6286 st: TStream;
6287 ok: Boolean;
6288 begin
6289 if e_NoGraphics then Exit;
6290 ssdir := GameDir+'/screenshots';
6291 if not findFileCI(ssdir, true) then
6292 begin
6293 // try to create dir
6294 try
6295 CreateDir(ssdir);
6296 except
6297 end;
6298 if not findFileCI(ssdir, true) then exit; // alas
6299 end;
6300 try
6301 for a := 1 to High(Word) do
6302 begin
6303 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6304 t := FileName;
6305 if findFileCI(t, true) then continue;
6306 if not findFileCI(FileName) then
6307 begin
6308 ok := false;
6309 st := createDiskFile(FileName);
6310 try
6311 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6312 ok := true;
6313 finally
6314 st.Free();
6315 end;
6316 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6317 break;
6318 end;
6319 end;
6320 except
6321 end;
6322 end;
6324 procedure g_Game_InGameMenu(Show: Boolean);
6325 begin
6326 if (g_ActiveWindow = nil) and Show then
6327 begin
6328 if gGameSettings.GameType = GT_SINGLE then
6329 g_GUI_ShowWindow('GameSingleMenu')
6330 else
6331 begin
6332 if g_Game_IsClient then
6333 g_GUI_ShowWindow('GameClientMenu')
6334 else
6335 if g_Game_IsNet then
6336 g_GUI_ShowWindow('GameServerMenu')
6337 else
6338 g_GUI_ShowWindow('GameCustomMenu');
6339 end;
6340 g_Sound_PlayEx('MENU_OPEN');
6342 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6343 if (not g_Game_IsNet) then
6344 g_Game_Pause(True);
6345 end
6346 else
6347 if (g_ActiveWindow <> nil) and (not Show) then
6348 begin
6349 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6350 if (not g_Game_IsNet) then
6351 g_Game_Pause(False);
6352 end;
6353 end;
6355 procedure g_Game_Pause(Enable: Boolean);
6356 begin
6357 if not gGameOn then
6358 Exit;
6360 if gPause = Enable then
6361 Exit;
6363 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6364 Exit;
6366 gPause := Enable;
6367 g_Game_PauseAllSounds(Enable);
6368 end;
6370 procedure g_Game_PauseAllSounds(Enable: Boolean);
6371 var
6372 i: Integer;
6373 begin
6374 // Òðèããåðû:
6375 if gTriggers <> nil then
6376 for i := 0 to High(gTriggers) do
6377 with gTriggers[i] do
6378 if (TriggerType = TRIGGER_SOUND) and
6379 (Sound <> nil) and
6380 Sound.IsPlaying() then
6381 begin
6382 Sound.Pause(Enable);
6383 end;
6385 // Çâóêè èãðîêîâ:
6386 if gPlayers <> nil then
6387 for i := 0 to High(gPlayers) do
6388 if gPlayers[i] <> nil then
6389 gPlayers[i].PauseSounds(Enable);
6391 // Ìóçûêà:
6392 if gMusic <> nil then
6393 gMusic.Pause(Enable);
6394 end;
6396 procedure g_Game_StopAllSounds(all: Boolean);
6397 var
6398 i: Integer;
6399 begin
6400 if gTriggers <> nil then
6401 for i := 0 to High(gTriggers) do
6402 with gTriggers[i] do
6403 if (TriggerType = TRIGGER_SOUND) and
6404 (Sound <> nil) then
6405 Sound.Stop();
6407 if gMusic <> nil then
6408 gMusic.Stop();
6410 if all then
6411 e_StopChannels();
6412 end;
6414 procedure g_Game_UpdateTriggerSounds();
6415 var
6416 i: Integer;
6417 begin
6418 if gTriggers <> nil then
6419 for i := 0 to High(gTriggers) do
6420 with gTriggers[i] do
6421 if (TriggerType = TRIGGER_SOUND) and
6422 (Sound <> nil) and
6423 (Data.Local) and
6424 Sound.IsPlaying() then
6425 begin
6426 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6427 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6428 begin
6429 Sound.SetPan(0.5 - Data.Pan/255.0);
6430 Sound.SetVolume(Data.Volume/255.0);
6431 end
6432 else
6433 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6434 end;
6435 end;
6437 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6438 begin
6439 Result := False;
6440 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6441 begin
6442 Result := True;
6443 Exit;
6444 end;
6445 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6446 begin
6447 Result := True;
6448 Exit;
6449 end;
6450 if gSpectMode <> SPECT_PLAYERS then
6451 Exit;
6452 if gSpectPID1 = UID then
6453 begin
6454 Result := True;
6455 Exit;
6456 end;
6457 if gSpectViewTwo and (gSpectPID2 = UID) then
6458 begin
6459 Result := True;
6460 Exit;
6461 end;
6462 end;
6464 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6465 var
6466 Pl: TPlayer;
6467 begin
6468 Result := False;
6469 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6470 begin
6471 Result := True;
6472 Exit;
6473 end;
6474 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6475 begin
6476 Result := True;
6477 Exit;
6478 end;
6479 if gSpectMode <> SPECT_PLAYERS then
6480 Exit;
6481 Pl := g_Player_Get(gSpectPID1);
6482 if (Pl <> nil) and (Pl.Team = Team) then
6483 begin
6484 Result := True;
6485 Exit;
6486 end;
6487 if gSpectViewTwo then
6488 begin
6489 Pl := g_Player_Get(gSpectPID2);
6490 if (Pl <> nil) and (Pl.Team = Team) then
6491 begin
6492 Result := True;
6493 Exit;
6494 end;
6495 end;
6496 end;
6498 procedure g_Game_Message(Msg: string; Time: Word);
6499 begin
6500 MessageText := b_Text_Format(Msg);
6501 MessageTime := Time;
6502 end;
6504 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6505 var
6506 a: Integer;
6507 begin
6508 case gAnnouncer of
6509 ANNOUNCE_NONE:
6510 Exit;
6511 ANNOUNCE_ME,
6512 ANNOUNCE_MEPLUS:
6513 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6514 Exit;
6515 end;
6516 for a := 0 to 3 do
6517 if goodsnd[a].IsPlaying() then
6518 Exit;
6520 goodsnd[Random(4)].Play();
6521 end;
6523 procedure g_Game_Announce_KillCombo(Param: Integer);
6524 var
6525 UID: Word;
6526 c, n: Byte;
6527 Pl: TPlayer;
6528 Name: String;
6529 begin
6530 UID := Param and $FFFF;
6531 c := Param shr 16;
6532 if c < 2 then
6533 Exit;
6535 Pl := g_Player_Get(UID);
6536 if Pl = nil then
6537 Name := '?'
6538 else
6539 Name := Pl.Name;
6541 case c of
6542 2: begin
6543 n := 0;
6544 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6545 end;
6546 3: begin
6547 n := 1;
6548 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6549 end;
6550 4: begin
6551 n := 2;
6552 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6553 end;
6554 else begin
6555 n := 3;
6556 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6557 end;
6558 end;
6560 case gAnnouncer of
6561 ANNOUNCE_NONE:
6562 Exit;
6563 ANNOUNCE_ME:
6564 if not g_Game_IsWatchedPlayer(UID) then
6565 Exit;
6566 ANNOUNCE_MEPLUS:
6567 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6568 Exit;
6569 end;
6571 if killsnd[n].IsPlaying() then
6572 killsnd[n].Stop();
6573 killsnd[n].Play();
6574 end;
6576 procedure g_Game_StartVote(Command, Initiator: string);
6577 var
6578 Need: Integer;
6579 begin
6580 if not gVotesEnabled then Exit;
6581 if gGameSettings.GameType <> GT_SERVER then Exit;
6582 if gVoteInProgress or gVotePassed then
6583 begin
6584 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6585 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6586 Exit;
6587 end;
6588 gVoteInProgress := True;
6589 gVotePassed := False;
6590 gVoteTimer := gTime + gVoteTimeout * 1000;
6591 gVoteCount := 0;
6592 gVoted := False;
6593 gVoteCommand := Command;
6595 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6596 Need := Floor((NetClientCount+1)/2.0)+1
6597 else
6598 Need := Floor(NetClientCount/2.0)+1;
6599 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6600 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6601 end;
6603 procedure g_Game_CheckVote;
6604 var
6605 I, Need: Integer;
6606 begin
6607 if gGameSettings.GameType <> GT_SERVER then Exit;
6608 if not gVoteInProgress then Exit;
6610 if (gTime >= gVoteTimer) then
6611 begin
6612 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6613 Need := Floor((NetClientCount+1)/2.0) + 1
6614 else
6615 Need := Floor(NetClientCount/2.0) + 1;
6616 if gVoteCount >= Need then
6617 begin
6618 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6619 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6620 gVotePassed := True;
6621 gVoteCmdTimer := gTime + 5000;
6622 end
6623 else
6624 begin
6625 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6626 MH_SEND_VoteEvent(NET_VE_FAILED);
6627 end;
6628 if NetClients <> nil then
6629 for I := Low(NetClients) to High(NetClients) do
6630 if NetClients[i].Used then
6631 NetClients[i].Voted := False;
6632 gVoteInProgress := False;
6633 gVoted := False;
6634 gVoteCount := 0;
6635 end
6636 else
6637 begin
6638 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6639 Need := Floor((NetClientCount+1)/2.0) + 1
6640 else
6641 Need := Floor(NetClientCount/2.0) + 1;
6642 if gVoteCount >= Need then
6643 begin
6644 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6645 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6646 gVoteInProgress := False;
6647 gVotePassed := True;
6648 gVoteCmdTimer := gTime + 5000;
6649 gVoted := False;
6650 gVoteCount := 0;
6651 if NetClients <> nil then
6652 for I := Low(NetClients) to High(NetClients) do
6653 if NetClients[i].Used then
6654 NetClients[i].Voted := False;
6655 end;
6656 end;
6657 end;
6659 procedure g_Game_LoadMapList(FileName: string);
6660 var
6661 ListFile: TextFile;
6662 s: string;
6663 begin
6664 MapList := nil;
6665 MapIndex := -1;
6667 if not FileExists(FileName) then Exit;
6669 AssignFile(ListFile, FileName);
6670 Reset(ListFile);
6671 while not EOF(ListFile) do
6672 begin
6673 ReadLn(ListFile, s);
6675 s := Trim(s);
6676 if s = '' then Continue;
6678 SetLength(MapList, Length(MapList)+1);
6679 MapList[High(MapList)] := s;
6680 end;
6681 CloseFile(ListFile);
6682 end;
6684 procedure g_Game_SetDebugMode();
6685 begin
6686 gDebugMode := True;
6687 // ×èòû (äàæå â ñâîåé èãðå):
6688 gCheats := True;
6689 end;
6691 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6692 var
6693 i: Word;
6694 begin
6695 if Length(LoadingStat.Msgs) = 0 then
6696 Exit;
6698 with LoadingStat do
6699 begin
6700 if not reWrite then
6701 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6702 if NextMsg = Length(Msgs) then
6703 begin // scroll
6704 for i := 0 to High(Msgs)-1 do
6705 Msgs[i] := Msgs[i+1];
6706 end
6707 else
6708 Inc(NextMsg);
6709 end else
6710 if NextMsg = 0 then
6711 Inc(NextMsg);
6713 Msgs[NextMsg-1] := Text;
6714 CurValue := 0;
6715 MaxValue := Max;
6716 ShowCount := 0;
6717 end;
6719 g_ActiveWindow := nil;
6721 ProcessLoading;
6722 end;
6724 procedure g_Game_StepLoading();
6725 begin
6726 with LoadingStat do
6727 begin
6728 Inc(CurValue);
6729 Inc(ShowCount);
6730 if (ShowCount > LOADING_SHOW_STEP) then
6731 begin
6732 ShowCount := 0;
6733 ProcessLoading;
6734 end;
6735 end;
6736 end;
6738 procedure g_Game_ClearLoading();
6739 var
6740 len: Word;
6741 begin
6742 with LoadingStat do
6743 begin
6744 CurValue := 0;
6745 MaxValue := 0;
6746 ShowCount := 0;
6747 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6748 if len < 1 then len := 1;
6749 SetLength(Msgs, len);
6750 for len := Low(Msgs) to High(Msgs) do
6751 Msgs[len] := '';
6752 NextMsg := 0;
6753 end;
6754 end;
6756 procedure Parse_Params(var pars: TParamStrValues);
6757 var
6758 i: Integer;
6759 s: String;
6760 begin
6761 SetLength(pars, 0);
6762 i := 1;
6763 while i <= ParamCount do
6764 begin
6765 s := ParamStr(i);
6766 if (s[1] = '-') and (Length(s) > 1) then
6767 begin
6768 if (s[2] = '-') and (Length(s) > 2) then
6769 begin // Îäèíî÷íûé ïàðàìåòð
6770 SetLength(pars, Length(pars) + 1);
6771 with pars[High(pars)] do
6772 begin
6773 Name := LowerCase(s);
6774 Value := '+';
6775 end;
6776 end
6777 else
6778 if (i < ParamCount) then
6779 begin // Ïàðàìåòð ñî çíà÷åíèåì
6780 Inc(i);
6781 SetLength(pars, Length(pars) + 1);
6782 with pars[High(pars)] do
6783 begin
6784 Name := LowerCase(s);
6785 Value := LowerCase(ParamStr(i));
6786 end;
6787 end;
6788 end;
6790 Inc(i);
6791 end;
6792 end;
6794 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6795 var
6796 i: Integer;
6797 begin
6798 Result := '';
6799 for i := 0 to High(pars) do
6800 if pars[i].Name = aName then
6801 begin
6802 Result := pars[i].Value;
6803 Break;
6804 end;
6805 end;
6807 procedure g_Game_Process_Params();
6808 var
6809 pars: TParamStrValues;
6810 map: String;
6811 GMode, n: Byte;
6812 LimT, LimS: Integer;
6813 Opt: LongWord;
6814 Lives: Integer;
6815 s: String;
6816 Port: Integer;
6817 ip: String;
6818 F: TextFile;
6819 begin
6820 Parse_Params(pars);
6822 // Debug mode:
6823 s := Find_Param_Value(pars, '--debug');
6824 if (s <> '') then
6825 begin
6826 g_Game_SetDebugMode();
6827 s := Find_Param_Value(pars, '--netdump');
6828 if (s <> '') then
6829 NetDump := True;
6830 end;
6832 // Connect when game loads
6833 ip := Find_Param_Value(pars, '-connect');
6835 if ip <> '' then
6836 begin
6837 s := Find_Param_Value(pars, '-port');
6838 if (s = '') or not TryStrToInt(s, Port) then
6839 Port := 25666;
6841 s := Find_Param_Value(pars, '-pw');
6843 g_Game_StartClient(ip, Port, s);
6844 Exit;
6845 end;
6847 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
6848 if (s <> '') then
6849 begin
6850 gDefaultMegawadStart := s;
6851 end;
6853 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
6854 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
6855 begin
6856 gDefaultMegawadStart := DF_Default_Megawad_Start;
6857 end;
6859 // Start map when game loads:
6860 map := LowerCase(Find_Param_Value(pars, '-map'));
6861 if isWadPath(map) then
6862 begin
6863 // Game mode:
6864 s := Find_Param_Value(pars, '-gm');
6865 GMode := g_Game_TextToMode(s);
6866 if GMode = GM_NONE then GMode := GM_DM;
6867 if GMode = GM_SINGLE then GMode := GM_COOP;
6869 // Time limit:
6870 s := Find_Param_Value(pars, '-limt');
6871 if (s = '') or (not TryStrToInt(s, LimT)) then
6872 LimT := 0;
6873 if LimT < 0 then
6874 LimT := 0;
6876 // Goal limit:
6877 s := Find_Param_Value(pars, '-lims');
6878 if (s = '') or (not TryStrToInt(s, LimS)) then
6879 LimS := 0;
6880 if LimS < 0 then
6881 LimS := 0;
6883 // Lives limit:
6884 s := Find_Param_Value(pars, '-lives');
6885 if (s = '') or (not TryStrToInt(s, Lives)) then
6886 Lives := 0;
6887 if Lives < 0 then
6888 Lives := 0;
6890 // Options:
6891 s := Find_Param_Value(pars, '-opt');
6892 if (s = '') then
6893 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6894 else
6895 Opt := StrToIntDef(s, 0);
6896 if Opt = 0 then
6897 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6899 // Close after map:
6900 s := Find_Param_Value(pars, '--close');
6901 if (s <> '') then
6902 gMapOnce := True;
6904 // Delete test map after play:
6905 s := Find_Param_Value(pars, '--testdelete');
6906 if (s <> '') then
6907 begin
6908 gMapToDelete := MapsDir + map;
6909 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6910 Halt(1);
6911 end;
6913 // Delete temporary WAD after play:
6914 s := Find_Param_Value(pars, '--tempdelete');
6915 if (s <> '') then
6916 begin
6917 gMapToDelete := MapsDir + map;
6918 gTempDelete := True;
6919 end;
6921 // Number of players:
6922 s := Find_Param_Value(pars, '-pl');
6923 if (s = '') then
6924 n := 1
6925 else
6926 n := StrToIntDef(s, 1);
6928 // Start:
6929 s := Find_Param_Value(pars, '-port');
6930 if (s = '') or not TryStrToInt(s, Port) then
6931 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6932 else
6933 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6934 end;
6936 // Execute script when game loads:
6937 s := Find_Param_Value(pars, '-exec');
6938 if s <> '' then
6939 begin
6940 if Pos(':\', s) = 0 then
6941 s := GameDir + '/' + s;
6943 {$I-}
6944 AssignFile(F, s);
6945 Reset(F);
6946 if IOResult <> 0 then
6947 begin
6948 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6949 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6950 CloseFile(F);
6951 Exit;
6952 end;
6953 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6954 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6956 while not EOF(F) do
6957 begin
6958 ReadLn(F, s);
6959 if IOResult <> 0 then
6960 begin
6961 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6962 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6963 CloseFile(F);
6964 Exit;
6965 end;
6966 if Pos('#', s) <> 1 then // script comment
6967 g_Console_Process(s, True);
6968 end;
6970 CloseFile(F);
6971 {$I+}
6972 end;
6974 SetLength(pars, 0);
6975 end;
6977 end.