DEADSOFTWARE

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