DEADSOFTWARE

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