DEADSOFTWARE

more code for tracing and other shit; NOTHING IS WORKING YET
[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;
769 end;
771 procedure g_Game_ExecuteEvent(Name: String);
772 var
773 a: Integer;
774 begin
775 if Name = '' then
776 Exit;
777 if gEvents = nil then
778 Exit;
779 for a := 0 to High(gEvents) do
780 if gEvents[a].Name = Name then
781 begin
782 if gEvents[a].Command <> '' then
783 g_Console_Process(gEvents[a].Command, True);
784 break;
785 end;
786 end;
788 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
789 var
790 a, n: Integer;
791 begin
792 n := -1;
793 if gDelayedEvents <> nil then
794 for a := 0 to High(gDelayedEvents) do
795 if not gDelayedEvents[a].Pending then
796 begin
797 n := a;
798 break;
799 end;
800 if n = -1 then
801 begin
802 SetLength(gDelayedEvents, Length(gDelayedEvents) + 1);
803 n := High(gDelayedEvents);
804 end;
805 gDelayedEvents[n].Pending := True;
806 gDelayedEvents[n].DEType := DEType;
807 gDelayedEvents[n].DENum := Num;
808 gDelayedEvents[n].DEStr := Str;
809 if DEType = DE_GLOBEVENT then
810 gDelayedEvents[n].Time := (GetTimer() {div 1000}) + Time
811 else
812 gDelayedEvents[n].Time := gTime + Time;
813 Result := n;
814 end;
816 procedure EndGame();
817 var
818 a: Integer;
819 FileName: string;
820 begin
821 if g_Game_IsNet and g_Game_IsServer then
822 MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed));
824 // Ñòîï èãðà:
825 gPause := False;
826 gGameOn := False;
828 g_Game_StopAllSounds(False);
830 MessageTime := 0;
831 MessageText := '';
833 EndingGameCounter := 0;
834 g_ActiveWindow := nil;
836 gLMSRespawn := LMS_RESPAWN_NONE;
837 gLMSRespawnTime := 0;
839 case gExit of
840 EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
841 begin
842 g_Game_Free();
844 if gMapOnce then
845 begin // Ýòî áûë òåñò
846 g_Game_Quit();
847 end
848 else
849 begin // Âûõîä â ãëàâíîå ìåíþ
850 gMusic.SetByName('MUSIC_MENU');
851 gMusic.Play();
852 if gState <> STATE_SLIST then
853 begin
854 g_GUI_ShowWindow('MainMenu');
855 gState := STATE_MENU;
856 end else
857 begin
858 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
859 slReturnPressed := True;
860 if g_Net_Slist_Fetch(slCurrent) then
861 begin
862 if slCurrent = nil then
863 slWaitStr := _lc[I_NET_SLIST_NOSERVERS];
864 end
865 else
866 slWaitStr := _lc[I_NET_SLIST_ERROR];
867 end;
869 g_Game_ExecuteEvent('ongameend');
870 end;
871 end;
873 EXIT_RESTART: // Íà÷àòü óðîâåíü ñíà÷àëà
874 begin
875 if not g_Game_IsClient then g_Game_Restart();
876 end;
878 EXIT_ENDLEVELCUSTOM: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
879 begin
880 // Ñòàòèñòèêà Ñâîåé èãðû:
881 FileName := g_ExtractWadName(gMapInfo.Map);
883 CustomStat.GameTime := gTime;
884 CustomStat.Map := ExtractFileName(FileName)+':'+g_ExtractFileName(gMapInfo.Map); //ResName;
885 CustomStat.MapName := gMapInfo.Name;
886 CustomStat.GameMode := gGameSettings.GameMode;
887 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
888 CustomStat.TeamStat := gTeamStat;
890 CustomStat.PlayerStat := nil;
892 // Ñòàòèñòèêà èãðîêîâ:
893 if gPlayers <> nil then
894 begin
895 for a := 0 to High(gPlayers) do
896 if gPlayers[a] <> nil then
897 begin
898 SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
899 with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
900 begin
901 Name := gPlayers[a].Name;
902 Frags := gPlayers[a].Frags;
903 Deaths := gPlayers[a].Death;
904 Kills := gPlayers[a].Kills;
905 Team := gPlayers[a].Team;
906 Color := gPlayers[a].Model.Color;
907 Spectator := gPlayers[a].FSpectator;
908 end;
909 end;
911 SortGameStat(CustomStat.PlayerStat);
912 end;
914 g_Game_ExecuteEvent('onmapend');
916 // Çàòóõàþùèé ýêðàí:
917 EndingGameCounter := 255;
918 gState := STATE_FOLD;
919 gInterTime := 0;
920 if gDefInterTime < 0 then
921 gInterEndTime := IfThen((gGameSettings.GameType = GT_SERVER) and (gPlayer1 = nil), 15000, 25000)
922 else
923 gInterEndTime := gDefInterTime * 1000;
924 end;
926 EXIT_ENDLEVELSINGLE: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
927 begin
928 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
929 SingleStat.GameTime := gTime;
930 SingleStat.TwoPlayers := gPlayer2 <> nil;
931 SingleStat.TotalSecrets := gSecretsCount;
932 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
933 SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills;
934 SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets;
935 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
936 if SingleStat.TwoPlayers then
937 begin
938 SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills;
939 SingleStat.PlayerStat[1].Secrets := gPlayer2.Secrets;
940 end;
942 g_Game_ExecuteEvent('onmapend');
944 // Åñòü åùå êàðòû:
945 if gNextMap <> '' then
946 begin
947 gMusic.SetByName('MUSIC_INTERMUS');
948 gMusic.Play();
949 gState := STATE_INTERSINGLE;
951 g_Game_ExecuteEvent('oninter');
952 end
953 else // Áîëüøå íåò êàðò
954 begin
955 // Çàòóõàþùèé ýêðàí:
956 EndingGameCounter := 255;
957 gState := STATE_FOLD;
958 end;
959 end;
960 end;
962 // Îêîí÷àíèå îáðàáîòàíî:
963 if gExit <> EXIT_QUIT then
964 gExit := 0;
965 end;
967 procedure DrawStat();
968 var
969 pc, x, y, w, h: Integer;
970 w1, w2, w3, w4: Integer;
971 a, aa: Integer;
972 cw, ch, r, g, b, rr, gg, bb: Byte;
973 s1, s2, s3: String;
974 _y: Integer;
975 stat: TPlayerStatArray;
976 wad, map: string;
977 mapstr: string;
978 begin
979 s1 := '';
980 s2 := '';
981 s3 := '';
982 pc := g_Player_GetCount;
983 e_TextureFontGetSize(gStdFont, cw, ch);
985 w := gScreenWidth-(gScreenWidth div 5);
986 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
987 h := 32+ch*(11+pc)
988 else
989 h := 40+ch*5+(ch+8)*pc;
990 x := (gScreenWidth div 2)-(w div 2);
991 y := (gScreenHeight div 2)-(h div 2);
993 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
994 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
996 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
997 map := g_ExtractFileName(gMapInfo.Map);
998 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
1000 case gGameSettings.GameMode of
1001 GM_DM:
1002 begin
1003 if gGameSettings.MaxLives = 0 then
1004 s1 := _lc[I_GAME_DM]
1005 else
1006 s1 := _lc[I_GAME_LMS];
1007 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
1008 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1009 end;
1011 GM_TDM:
1012 begin
1013 if gGameSettings.MaxLives = 0 then
1014 s1 := _lc[I_GAME_TDM]
1015 else
1016 s1 := _lc[I_GAME_TLMS];
1017 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
1018 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1019 end;
1021 GM_CTF:
1022 begin
1023 s1 := _lc[I_GAME_CTF];
1024 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]);
1025 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
1026 end;
1028 GM_COOP:
1029 begin
1030 if gGameSettings.MaxLives = 0 then
1031 s1 := _lc[I_GAME_COOP]
1032 else
1033 s1 := _lc[I_GAME_SURV];
1034 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
1035 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
1036 end;
1038 else
1039 begin
1040 s1 := '';
1041 s2 := '';
1042 end;
1043 end;
1045 _y := y+8;
1046 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
1047 _y := _y+ch+8;
1048 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
1049 _y := _y+ch+8;
1050 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
1052 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3,
1053 gStdFont, 200, 200, 200, 1);
1055 if NetMode = NET_SERVER then
1056 e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1)
1057 else
1058 if NetMode = NET_CLIENT then
1059 e_TextureFontPrintEx(x+8, y + 8,
1060 NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1);
1062 if pc = 0 then
1063 Exit;
1064 stat := g_Player_GetStats();
1065 SortGameStat(stat);
1067 w2 := (w-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1068 w3 := (w-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1069 w4 := w3;
1070 w1 := w-16-w2-w3-w4; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1072 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1073 begin
1074 _y := _y+ch+ch;
1076 for a := TEAM_RED to TEAM_BLUE do
1077 begin
1078 if a = TEAM_RED then
1079 begin
1080 s1 := _lc[I_GAME_TEAM_RED];
1081 r := 255;
1082 g := 0;
1083 b := 0;
1084 end
1085 else
1086 begin
1087 s1 := _lc[I_GAME_TEAM_BLUE];
1088 r := 0;
1089 g := 0;
1090 b := 255;
1091 end;
1093 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
1094 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals),
1095 gStdFont, r, g, b, 1);
1097 _y := _y+ch+(ch div 4);
1098 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
1099 _y := _y+(ch div 4);
1101 for aa := 0 to High(stat) do
1102 if stat[aa].Team = a then
1103 with stat[aa] do
1104 begin
1105 if Spectator then
1106 begin
1107 rr := r div 2;
1108 gg := g div 2;
1109 bb := b div 2;
1110 end
1111 else
1112 begin
1113 rr := r;
1114 gg := g;
1115 bb := b;
1116 end;
1117 // Èìÿ
1118 e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
1119 // Ïèíã/ïîòåðè
1120 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
1121 // Ôðàãè
1122 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
1123 // Ñìåðòè
1124 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
1125 _y := _y+ch;
1126 end;
1128 _y := _y+ch;
1129 end;
1130 end
1131 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
1132 begin
1133 _y := _y+ch+ch;
1134 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
1135 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
1136 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
1137 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
1139 _y := _y+ch+8;
1140 for aa := 0 to High(stat) do
1141 with stat[aa] do
1142 begin
1143 if Spectator then
1144 begin
1145 r := 127;
1146 g := 64;
1147 end
1148 else
1149 begin
1150 r := 255;
1151 g := 127;
1152 end;
1153 // Öâåò èãðîêà
1154 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
1155 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
1156 // Èìÿ
1157 e_TextureFontPrintEx(x+16+16+8, _y+4, Name, gStdFont, r, g, 0, 1);
1158 // Ïèíã/ïîòåðè
1159 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
1160 // Ôðàãè
1161 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
1162 // Ñìåðòè
1163 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
1164 _y := _y+ch+8;
1165 end;
1166 end
1167 end;
1169 procedure g_Game_Init();
1170 var
1171 SR: TSearchRec;
1172 begin
1173 gExit := 0;
1174 gMapToDelete := '';
1175 gTempDelete := False;
1177 sfsGCDisable(); // temporary disable removing of temporary volumes
1179 try
1180 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
1181 g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
1182 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
1183 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
1185 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
1186 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
1187 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
1189 g_Game_ClearLoading();
1190 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
1191 g_Game_SetLoadingText('', 0, False);
1193 g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
1194 g_Console_Init();
1196 g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
1197 g_PlayerModel_LoadData();
1199 if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then
1200 repeat
1201 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1202 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1203 until FindNext(SR) <> 0;
1204 FindClose(SR);
1206 if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then
1207 repeat
1208 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1209 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1210 until FindNext(SR) <> 0;
1211 FindClose(SR);
1213 if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then
1214 repeat
1215 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1216 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1217 until FindNext(SR) <> 0;
1218 FindClose(SR);
1220 gGameOn := False;
1221 gPause := False;
1222 gTime := 0;
1223 LastScreenShot := 0;
1225 {e_MouseInfo.Accel := 1.0;}
1227 g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
1228 g_Game_LoadData();
1230 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1231 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
1232 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
1233 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True);
1234 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
1236 g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
1237 g_Menu_Init();
1239 gMusic := TMusic.Create();
1240 gMusic.SetByName('MUSIC_MENU');
1241 gMusic.Play();
1243 gGameSettings.WarmupTime := 30;
1245 gState := STATE_MENU;
1247 SetLength(gEvents, 6);
1248 gEvents[0].Name := 'ongamestart';
1249 gEvents[1].Name := 'ongameend';
1250 gEvents[2].Name := 'onmapstart';
1251 gEvents[3].Name := 'onmapend';
1252 gEvents[4].Name := 'oninter';
1253 gEvents[5].Name := 'onwadend';
1254 finally
1255 sfsGCEnable(); // enable releasing unused volumes
1256 end;
1257 end;
1259 procedure g_Game_Free();
1260 begin
1261 if NetMode = NET_CLIENT then g_Net_Disconnect();
1262 if NetMode = NET_SERVER then g_Net_Host_Die();
1264 g_Map_Free();
1265 g_Player_Free();
1266 g_Player_RemoveAllCorpses();
1268 gGameSettings.GameType := GT_NONE;
1269 if gGameSettings.GameMode = GM_SINGLE then
1270 gGameSettings.GameMode := GM_DM;
1271 gSwitchGameMode := gGameSettings.GameMode;
1273 gChatShow := False;
1274 gExitByTrigger := False;
1275 end;
1277 function IsActivePlayer(p: TPlayer): Boolean;
1278 begin
1279 Result := False;
1280 if p = nil then
1281 Exit;
1282 Result := (not p.FDummy) and (not p.FSpectator);
1283 end;
1285 function GetActivePlayer_ByID(ID: Integer): TPlayer;
1286 var
1287 a: Integer;
1288 begin
1289 Result := nil;
1290 if ID < 0 then
1291 Exit;
1292 if gPlayers = nil then
1293 Exit;
1294 for a := Low(gPlayers) to High(gPlayers) do
1295 if IsActivePlayer(gPlayers[a]) then
1296 begin
1297 if gPlayers[a].UID <> ID then
1298 continue;
1299 Result := gPlayers[a];
1300 break;
1301 end;
1302 end;
1304 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1305 var
1306 a, idx: Integer;
1307 ids: Array of Word;
1308 begin
1309 Result := -1;
1310 if gPlayers = nil then
1311 Exit;
1312 SetLength(ids, 0);
1313 idx := -1;
1314 for a := Low(gPlayers) to High(gPlayers) do
1315 if IsActivePlayer(gPlayers[a]) then
1316 begin
1317 SetLength(ids, Length(ids) + 1);
1318 ids[High(ids)] := gPlayers[a].UID;
1319 if gPlayers[a].UID = Skip then
1320 idx := High(ids);
1321 end;
1322 if Length(ids) = 0 then
1323 Exit;
1324 if idx = -1 then
1325 Result := ids[0]
1326 else
1327 Result := ids[(idx + 1) mod Length(ids)];
1328 end;
1330 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1331 var
1332 a, idx: Integer;
1333 ids: Array of Word;
1334 begin
1335 Result := -1;
1336 if gPlayers = nil then
1337 Exit;
1338 SetLength(ids, 0);
1339 idx := -1;
1340 for a := Low(gPlayers) to High(gPlayers) do
1341 if IsActivePlayer(gPlayers[a]) then
1342 begin
1343 SetLength(ids, Length(ids) + 1);
1344 ids[High(ids)] := gPlayers[a].UID;
1345 if gPlayers[a].UID = Skip then
1346 idx := High(ids);
1347 end;
1348 if Length(ids) = 0 then
1349 Exit;
1350 if idx = -1 then
1351 Result := ids[Length(ids) - 1]
1352 else
1353 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1354 end;
1356 function isKeyPressed (key1: Word; key2: Word): Boolean;
1357 begin
1358 if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
1359 if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
1360 result := false;
1361 end;
1363 procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false);
1364 var
1365 time: Word;
1366 strafeDir: Byte;
1367 i: Integer;
1368 begin
1369 if (plr = nil) then exit;
1370 if (p2hack) then time := 1000 else time := 1;
1371 strafeDir := MoveButton shr 4;
1372 MoveButton := MoveButton and $0F;
1373 with ctrl do
1374 begin
1375 if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1376 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1377 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1379 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1380 if MoveButton = 1 then plr.PressKey(KEY_LEFT, time)
1381 else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time);
1383 // if we have "strafe" key, turn off old strafe mechanics
1384 if isKeyPressed(KeyStrafe, KeyStrafe2) then
1385 begin
1386 // new strafe mechanics
1387 if (strafeDir = 0) then strafeDir := MoveButton; // start strafing
1388 // now set direction according to strafe (reversed)
1389 if (strafeDir = 2) then plr.SetDirection(D_LEFT)
1390 else if (strafeDir = 1) then plr.SetDirection(D_RIGHT);
1391 end
1392 else
1393 begin
1394 strafeDir := 0; // not strafing anymore
1395 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1396 if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(D_LEFT)
1397 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1398 else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(D_RIGHT)
1399 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1400 else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1));
1401 end;
1403 // fix movebutton state
1404 MoveButton := MoveButton or (strafeDir shl 4);
1406 // Îñòàëüíûå êëàâèøè:
1407 if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time);
1408 if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time);
1409 if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time);
1410 if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE);
1411 if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON);
1412 if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON);
1413 if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN);
1415 for i := 0 to High(KeyWeapon) do
1416 if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then
1417 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1418 end;
1420 // HACK: add dynlight here
1421 if gwin_k8_enable_light_experiments then
1422 begin
1423 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1424 begin
1425 g_playerLight := true;
1426 end;
1427 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1428 begin
1429 g_playerLight := false;
1430 end;
1431 end;
1433 if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1434 end;
1436 procedure g_Game_Update();
1437 var
1438 Msg: g_gui.TMessage;
1439 Time: Int64;
1440 a: Byte;
1441 w: Word;
1442 i, b: Integer;
1444 function sendMonsPos (mon: TMonster): Boolean;
1445 begin
1446 result := false; // don't stop
1447 if (mon.MonsterType = MONSTER_BARREL) then
1448 begin
1449 if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID);
1450 end
1451 else
1452 if (mon.MonsterState <> MONSTATE_SLEEP) then
1453 begin
1454 if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then
1455 begin
1456 MH_SEND_MonsterPos(mon.UID);
1457 end;
1458 end;
1459 end;
1461 begin
1462 g_ResetDynlights();
1463 // Ïîðà âûêëþ÷àòü èãðó:
1464 if gExit = EXIT_QUIT then
1465 Exit;
1466 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1467 if gExit <> 0 then
1468 begin
1469 EndGame();
1470 if gExit = EXIT_QUIT then
1471 Exit;
1472 end;
1474 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1475 e_PollInput();
1477 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1478 g_Console_Update();
1480 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1481 begin
1482 gExit := EXIT_SIMPLE;
1483 EndGame();
1484 Exit;
1485 end;
1487 case gState of
1488 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1489 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1490 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1491 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1492 begin
1493 if g_Game_IsNet and g_Game_IsServer then
1494 begin
1495 gInterTime := gInterTime + GAME_TICK;
1496 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1497 if a <> gServInterTime then
1498 begin
1499 gServInterTime := a;
1500 MH_SEND_TimeSync(gServInterTime);
1501 end;
1502 end;
1504 if (not g_Game_IsClient) and
1507 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1508 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1509 and (g_ActiveWindow = nil)
1511 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1513 then
1514 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1515 g_Game_StopAllSounds(True);
1517 if gMapOnce then // Ýòî áûë òåñò
1518 gExit := EXIT_SIMPLE
1519 else
1520 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1521 g_Game_ChangeMap(gNextMap)
1522 else // Ñëåäóþùåé êàðòû íåò
1523 begin
1524 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1525 begin
1526 // Âûõîä â ãëàâíîå ìåíþ:
1527 g_Game_Free;
1528 g_GUI_ShowWindow('MainMenu');
1529 gMusic.SetByName('MUSIC_MENU');
1530 gMusic.Play();
1531 gState := STATE_MENU;
1532 end else
1533 begin
1534 // Ôèíàëüíàÿ êàðòèíêà:
1535 g_Game_ExecuteEvent('onwadend');
1536 g_Game_Free();
1537 if not gMusic.SetByName('MUSIC_endmus') then
1538 gMusic.SetByName('MUSIC_STDENDMUS');
1539 gMusic.Play();
1540 gState := STATE_ENDPIC;
1541 end;
1542 g_Game_ExecuteEvent('ongameend');
1543 end;
1545 Exit;
1546 end;
1548 if gState = STATE_INTERTEXT then
1549 if InterText.counter > 0 then
1550 InterText.counter := InterText.counter - 1;
1551 end;
1553 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1554 begin
1555 if EndingGameCounter = 0 then
1556 begin
1557 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1558 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1559 begin
1560 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1561 begin
1562 g_Game_ExecuteEvent('onwadend');
1563 if not gMusic.SetByName('MUSIC_endmus') then
1564 gMusic.SetByName('MUSIC_STDENDMUS');
1565 end
1566 else
1567 gMusic.SetByName('MUSIC_ROUNDMUS');
1569 gMusic.Play();
1570 gState := STATE_INTERCUSTOM;
1571 end
1572 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1573 begin
1574 gMusic.SetByName('MUSIC_INTERMUS');
1575 gMusic.Play();
1576 gState := STATE_INTERSINGLE;
1577 end;
1578 g_Game_ExecuteEvent('oninter');
1579 end
1580 else
1581 DecMin(EndingGameCounter, 6, 0);
1582 end;
1584 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1585 begin
1586 if gMapOnce then // Ýòî áûë òåñò
1587 begin
1588 gExit := EXIT_SIMPLE;
1589 Exit;
1590 end;
1591 end;
1593 STATE_SLIST:
1594 g_Serverlist_Control(slCurrent);
1595 end;
1597 if g_Game_IsNet then
1598 if not gConsoleShow then
1599 if not gChatShow then
1600 begin
1601 if g_ActiveWindow = nil then
1602 begin
1603 if e_KeyPressed(gGameControls.GameControls.Chat) then
1604 g_Console_Chat_Switch(False)
1605 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1606 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1607 g_Console_Chat_Switch(True);
1608 end;
1609 end else
1610 if not gChatEnter then
1611 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1612 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1613 gChatEnter := True;
1615 // Ñòàòèñòèêà ïî Tab:
1616 if gGameOn then
1617 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1618 (gGameSettings.GameType <> GT_SINGLE) and
1619 e_KeyPressed(gGameControls.GameControls.Stat);
1621 // Èãðà èäåò:
1622 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1623 begin
1624 // Âðåìÿ += 28 ìèëëèñåêóíä:
1625 gTime := gTime + GAME_TICK;
1627 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1628 if MessageTime = 0 then
1629 MessageText := '';
1630 if MessageTime > 0 then
1631 MessageTime := MessageTime - 1;
1633 if (g_Game_IsServer) then
1634 begin
1635 // Áûë çàäàí ëèìèò âðåìåíè:
1636 if (gGameSettings.TimeLimit > 0) then
1637 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1638 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1639 g_Game_NextLevel();
1640 Exit;
1641 end;
1643 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1644 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1645 g_Game_RestartRound(gLMSSoftSpawn);
1647 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1648 if gVoteInProgress and (gVoteTimer < gTime) then
1649 g_Game_CheckVote
1650 else if gVotePassed and (gVoteCmdTimer < gTime) then
1651 begin
1652 g_Console_Process(gVoteCommand);
1653 gVoteCommand := '';
1654 gVotePassed := False;
1655 end;
1657 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1658 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1659 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1660 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1661 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1663 // Áûë çàäàí ëèìèò ïîáåä:
1664 if (gGameSettings.GoalLimit > 0) then
1665 begin
1666 b := 0;
1668 if gGameSettings.GameMode = GM_DM then
1669 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1670 for i := 0 to High(gPlayers) do
1671 if gPlayers[i] <> nil then
1672 if gPlayers[i].Frags > b then
1673 b := gPlayers[i].Frags;
1674 end
1675 else
1676 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1677 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1678 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1679 end;
1681 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1682 if b >= gGameSettings.GoalLimit then
1683 begin
1684 g_Game_NextLevel();
1685 Exit;
1686 end;
1687 end;
1689 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1690 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1691 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1692 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1693 begin
1694 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1695 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1696 end // if not console
1697 else
1698 begin
1699 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1700 end;
1701 // process weapon switch queue
1702 end; // if server
1704 // Íàáëþäàòåëü
1705 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1706 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1707 begin
1708 if not gSpectKeyPress then
1709 begin
1710 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1711 begin
1712 // switch spect mode
1713 case gSpectMode of
1714 SPECT_NONE: ; // not spectator
1715 SPECT_STATS,
1716 SPECT_MAPVIEW: Inc(gSpectMode);
1717 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1718 end;
1719 gSpectKeyPress := True;
1720 end;
1721 if gSpectMode = SPECT_MAPVIEW then
1722 begin
1723 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1724 gSpectX := Max(gSpectX - gSpectStep, 0);
1725 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1726 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1727 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1728 gSpectY := Max(gSpectY - gSpectStep, 0);
1729 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1730 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1731 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1732 begin
1733 // decrease step
1734 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1735 gSpectKeyPress := True;
1736 end;
1737 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1738 begin
1739 // increase step
1740 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1741 gSpectKeyPress := True;
1742 end;
1743 end;
1744 if gSpectMode = SPECT_PLAYERS then
1745 begin
1746 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1747 begin
1748 // add second view
1749 gSpectViewTwo := True;
1750 gSpectKeyPress := True;
1751 end;
1752 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1753 begin
1754 // remove second view
1755 gSpectViewTwo := False;
1756 gSpectKeyPress := True;
1757 end;
1758 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1759 begin
1760 // prev player (view 1)
1761 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1762 gSpectKeyPress := True;
1763 end;
1764 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1765 begin
1766 // next player (view 1)
1767 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1768 gSpectKeyPress := True;
1769 end;
1770 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1771 begin
1772 // prev player (view 2)
1773 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1774 gSpectKeyPress := True;
1775 end;
1776 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1777 begin
1778 // next player (view 2)
1779 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1780 gSpectKeyPress := True;
1781 end;
1782 end;
1783 end
1784 else
1785 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1786 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1787 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1788 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1789 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1790 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1791 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1792 gSpectKeyPress := False;
1793 end;
1795 // Îáíîâëÿåì âñå îñòàëüíîå:
1796 g_Map_Update();
1797 g_Items_Update();
1798 g_Triggers_Update();
1799 g_Weapon_Update();
1800 g_Monsters_Update();
1801 g_GFX_Update();
1802 g_Player_UpdateAll();
1803 g_Player_UpdatePhysicalObjects();
1804 if gGameSettings.GameType = GT_SERVER then
1805 if Length(gMonstersSpawned) > 0 then
1806 begin
1807 for I := 0 to High(gMonstersSpawned) do
1808 MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1809 SetLength(gMonstersSpawned, 0);
1810 end;
1812 if (gSoundTriggerTime > 8) then
1813 begin
1814 g_Game_UpdateTriggerSounds();
1815 gSoundTriggerTime := 0;
1816 end
1817 else
1818 Inc(gSoundTriggerTime);
1820 if (NetMode = NET_SERVER) then
1821 begin
1822 Inc(NetTimeToUpdate);
1823 Inc(NetTimeToReliable);
1824 if NetTimeToReliable >= NetRelupdRate then
1825 begin
1826 for I := 0 to High(gPlayers) do
1827 if gPlayers[I] <> nil then
1828 MH_SEND_PlayerPos(True, gPlayers[I].UID);
1830 g_Mons_ForEach(sendMonsPos);
1832 NetTimeToReliable := 0;
1833 NetTimeToUpdate := NetUpdateRate;
1834 end
1835 else if NetTimeToUpdate >= NetUpdateRate then
1836 begin
1837 if gPlayers <> nil then
1838 for I := 0 to High(gPlayers) do
1839 if gPlayers[I] <> nil then
1840 MH_SEND_PlayerPos(False, gPlayers[I].UID);
1842 g_Mons_ForEach(sendMonsPos);
1844 NetTimeToUpdate := 0;
1845 end;
1847 if NetUseMaster then
1848 if gTime >= NetTimeToMaster then
1849 begin
1850 if (NetMHost = nil) or (NetMPeer = nil) then
1851 if not g_Net_Slist_Connect then
1852 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1854 g_Net_Slist_Update;
1855 NetTimeToMaster := gTime + NetMasterRate;
1856 end;
1857 end
1858 else
1859 if NetMode = NET_CLIENT then
1860 MC_SEND_PlayerPos();
1861 end; // if gameOn ...
1863 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1864 if g_ActiveWindow <> nil then
1865 begin
1866 w := e_GetFirstKeyPressed();
1868 if (w <> IK_INVALID) then
1869 begin
1870 Msg.Msg := MESSAGE_DIKEY;
1871 Msg.wParam := w;
1872 g_ActiveWindow.OnMessage(Msg);
1873 end;
1875 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1876 if g_ActiveWindow <> nil then
1877 g_ActiveWindow.Update();
1879 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1880 if gResolutionChange then
1881 begin
1882 e_WriteLog('Changing resolution', MSG_NOTIFY);
1883 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1884 gResolutionChange := False;
1885 end;
1887 // Íóæíî ñìåíèòü ÿçûê:
1888 if gLanguageChange then
1889 begin
1890 //e_WriteLog('Read language file', MSG_NOTIFY);
1891 //g_Language_Load(DataDir + gLanguage + '.txt');
1892 g_Language_Set(gLanguage);
1893 g_Menu_Reset();
1894 gLanguageChange := False;
1895 end;
1896 end;
1898 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
1899 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
1900 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
1901 begin
1902 g_TakeScreenShot();
1903 LastScreenShot := GetTimer();
1904 end;
1906 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
1907 if e_KeyPressed(IK_F10) and
1908 gGameOn and
1909 (not gConsoleShow) and
1910 (g_ActiveWindow = nil) then
1911 begin
1912 KeyPress(IK_F10);
1913 end;
1915 Time := GetTimer() {div 1000};
1917 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
1918 if gDelayedEvents <> nil then
1919 for a := 0 to High(gDelayedEvents) do
1920 if gDelayedEvents[a].Pending and
1922 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
1923 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
1924 ) then
1925 begin
1926 case gDelayedEvents[a].DEType of
1927 DE_GLOBEVENT:
1928 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
1929 DE_BFGHIT:
1930 if gGameOn then
1931 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
1932 DE_KILLCOMBO:
1933 if gGameOn then
1934 begin
1935 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
1936 if g_Game_IsNet and g_Game_IsServer then
1937 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
1938 end;
1939 end;
1940 gDelayedEvents[a].Pending := False;
1941 end;
1943 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
1944 UPSCounter := UPSCounter + 1;
1945 if Time - UPSTime >= 1000 then
1946 begin
1947 UPS := UPSCounter;
1948 UPSCounter := 0;
1949 UPSTime := Time;
1950 end;
1952 if gGameOn then g_Weapon_AddDynLights();
1953 end;
1955 procedure g_Game_LoadData();
1956 begin
1957 if DataLoaded then Exit;
1959 e_WriteLog('Loading game data...', MSG_NOTIFY);
1961 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
1962 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
1963 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
1964 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
1965 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
1966 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
1967 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB');
1968 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS');
1969 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD');
1970 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB');
1971 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS');
1972 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD');
1973 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
1974 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
1975 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
1976 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
1977 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
1978 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
1979 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
1980 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
1981 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
1982 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
1983 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
1984 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
1985 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
1986 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
1987 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
1988 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
1989 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
1990 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
1991 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
1992 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
1993 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
1994 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
1996 goodsnd[0] := TPlayableSound.Create();
1997 goodsnd[1] := TPlayableSound.Create();
1998 goodsnd[2] := TPlayableSound.Create();
1999 goodsnd[3] := TPlayableSound.Create();
2001 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2002 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2003 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2004 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2006 killsnd[0] := TPlayableSound.Create();
2007 killsnd[1] := TPlayableSound.Create();
2008 killsnd[2] := TPlayableSound.Create();
2009 killsnd[3] := TPlayableSound.Create();
2011 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2012 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2013 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2014 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2016 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2017 g_Items_LoadData();
2019 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2020 g_Weapon_LoadData();
2022 g_Monsters_LoadData();
2024 DataLoaded := True;
2025 end;
2027 procedure g_Game_FreeData();
2028 begin
2029 if not DataLoaded then Exit;
2031 g_Items_FreeData();
2032 g_Weapon_FreeData();
2033 g_Monsters_FreeData();
2035 e_WriteLog('Releasing game data...', MSG_NOTIFY);
2037 g_Texture_Delete('NOTEXTURE');
2038 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2039 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2040 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2041 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2042 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2043 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2044 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2045 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2046 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2047 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2048 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2049 g_Frames_DeleteByName('FRAMES_TELEPORT');
2050 g_Sound_Delete('SOUND_GAME_TELEPORT');
2051 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2052 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2053 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2054 g_Sound_Delete('SOUND_GAME_BULK1');
2055 g_Sound_Delete('SOUND_GAME_BULK2');
2056 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2057 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2058 g_Sound_Delete('SOUND_GAME_SWITCH1');
2059 g_Sound_Delete('SOUND_GAME_SWITCH0');
2061 goodsnd[0].Free();
2062 goodsnd[1].Free();
2063 goodsnd[2].Free();
2064 goodsnd[3].Free();
2066 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2067 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2068 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2069 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2071 killsnd[0].Free();
2072 killsnd[1].Free();
2073 killsnd[2].Free();
2074 killsnd[3].Free();
2076 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2077 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2078 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2079 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2081 DataLoaded := False;
2082 end;
2084 procedure DrawCustomStat();
2085 var
2086 pc, x, y, w, _y,
2087 w1, w2, w3,
2088 t, p, m: Integer;
2089 ww1, hh1: Word;
2090 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2091 s1, s2, topstr: String;
2092 begin
2093 e_TextureFontGetSize(gStdFont, ww2, hh2);
2095 e_PollInput();
2096 if e_KeyPressed(IK_TAB) then
2097 begin
2098 if not gStatsPressed then
2099 begin
2100 gStatsOff := not gStatsOff;
2101 gStatsPressed := True;
2102 end;
2103 end
2104 else
2105 gStatsPressed := False;
2107 if gStatsOff then
2108 begin
2109 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2110 w := (Length(s1) * ww2) div 2;
2111 x := gScreenWidth div 2 - w;
2112 y := 8;
2113 e_TextureFontPrint(x, y, s1, gStdFont);
2114 Exit;
2115 end;
2117 if (gGameSettings.GameMode = GM_COOP) then
2118 begin
2119 if gMissionFailed then
2120 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2121 else
2122 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2123 end
2124 else
2125 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2127 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2128 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2130 if g_Game_IsNet then
2131 begin
2132 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2133 if not gChatShow then
2134 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2135 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2136 end;
2138 if g_Game_IsClient then
2139 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2140 else
2141 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2142 if not gChatShow then
2143 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2144 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2146 x := 32;
2147 y := 16+hh1+16;
2149 w := gScreenWidth-x*2;
2151 w2 := (w-16) div 6;
2152 w3 := w2;
2153 w1 := w-16-w2-w3;
2155 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2156 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2158 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2160 case CustomStat.GameMode of
2161 GM_DM:
2162 begin
2163 if gGameSettings.MaxLives = 0 then
2164 s1 := _lc[I_GAME_DM]
2165 else
2166 s1 := _lc[I_GAME_LMS];
2167 end;
2168 GM_TDM:
2169 begin
2170 if gGameSettings.MaxLives = 0 then
2171 s1 := _lc[I_GAME_TDM]
2172 else
2173 s1 := _lc[I_GAME_TLMS];
2174 end;
2175 GM_CTF: s1 := _lc[I_GAME_CTF];
2176 GM_COOP:
2177 begin
2178 if gGameSettings.MaxLives = 0 then
2179 s1 := _lc[I_GAME_COOP]
2180 else
2181 s1 := _lc[I_GAME_SURV];
2182 end;
2183 else s1 := '';
2184 end;
2186 _y := y+16;
2187 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2188 _y := _y+8;
2190 _y := _y+16;
2191 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2192 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2194 _y := _y+16;
2195 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2196 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2197 (CustomStat.GameTime div 1000 div 60) mod 60,
2198 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2200 pc := Length(CustomStat.PlayerStat);
2201 if pc = 0 then Exit;
2203 if CustomStat.GameMode = GM_COOP then
2204 begin
2205 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2206 _y := _y+32;
2207 s2 := _lc[I_GAME_MONSTERS];
2208 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2209 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2210 _y := _y+16;
2211 s2 := _lc[I_GAME_SECRETS];
2212 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2213 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2214 if gLastMap then
2215 begin
2216 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2217 _y := _y-16;
2218 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2219 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2220 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2221 _y := _y+16;
2222 s2 := _lc[I_GAME_SECRETS_TOTAL];
2223 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2224 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2225 end;
2226 end;
2228 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2229 begin
2230 _y := _y+16+16;
2232 with CustomStat do
2233 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2234 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2235 else s1 := _lc[I_GAME_WIN_DRAW];
2237 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2238 _y := _y+40;
2240 for t := TEAM_RED to TEAM_BLUE do
2241 begin
2242 if t = TEAM_RED then
2243 begin
2244 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2245 gStdFont, 255, 0, 0, 1);
2246 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2247 gStdFont, 255, 0, 0, 1);
2248 r := 255;
2249 g := 0;
2250 b := 0;
2251 end
2252 else
2253 begin
2254 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2255 gStdFont, 0, 0, 255, 1);
2256 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2257 gStdFont, 0, 0, 255, 1);
2258 r := 0;
2259 g := 0;
2260 b := 255;
2261 end;
2263 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2264 _y := _y+24;
2266 for p := 0 to High(CustomStat.PlayerStat) do
2267 if CustomStat.PlayerStat[p].Team = t then
2268 with CustomStat.PlayerStat[p] do
2269 begin
2270 if Spectator then
2271 begin
2272 rr := r div 2;
2273 gg := g div 2;
2274 bb := b div 2;
2275 end
2276 else
2277 begin
2278 rr := r;
2279 gg := g;
2280 bb := b;
2281 end;
2282 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2283 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2284 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2285 _y := _y+24;
2286 end;
2288 _y := _y+16+16;
2289 end;
2290 end
2291 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2292 begin
2293 _y := _y+40;
2294 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2295 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2296 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2298 _y := _y+24;
2299 for p := 0 to High(CustomStat.PlayerStat) do
2300 with CustomStat.PlayerStat[p] do
2301 begin
2302 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2304 if Spectator then
2305 r := 127
2306 else
2307 r := 255;
2309 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2310 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2311 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2312 _y := _y+24;
2313 end;
2314 end;
2315 end;
2317 procedure DrawSingleStat();
2318 var
2319 tm, key_x, val_x, y: Integer;
2320 w1, w2, h: Word;
2321 s1, s2: String;
2323 procedure player_stat(n: Integer);
2324 var
2325 kpm: Real;
2327 begin
2328 // "Kills: # / #":
2329 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2330 s2 := Format(' %d', [gTotalMonsters]);
2332 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2333 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2334 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2335 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2336 s1 := s1 + '/';
2337 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2338 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2340 // "Kills-per-minute: ##.#":
2341 s1 := _lc[I_MENU_INTER_KPM];
2342 if tm > 0 then
2343 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2344 else
2345 kpm := SingleStat.PlayerStat[n].Kills;
2346 s2 := Format(' %.1f', [kpm]);
2348 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2349 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2351 // "Secrets found: # / #":
2352 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2353 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2355 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2356 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2357 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2358 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2359 s1 := s1 + '/';
2360 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2361 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2362 end;
2364 begin
2365 // "Level Complete":
2366 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2367 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2369 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2370 s1 := _lc[I_MENU_INTER_KPM];
2371 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2372 Inc(w1, 16);
2373 s1 := ' 9999.9';
2374 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2376 key_x := (gScreenWidth-w1-w2) div 2;
2377 val_x := key_x + w1;
2379 // "Time: #:##:##":
2380 tm := SingleStat.GameTime div 1000;
2381 s1 := _lc[I_MENU_INTER_TIME];
2382 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2384 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2385 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2387 if SingleStat.TwoPlayers then
2388 begin
2389 // "Player 1":
2390 s1 := _lc[I_MENU_PLAYER_1];
2391 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2392 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2394 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2395 y := 176;
2396 player_stat(0);
2398 // "Player 2":
2399 s1 := _lc[I_MENU_PLAYER_2];
2400 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2401 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2403 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2404 y := 336;
2405 player_stat(1);
2406 end
2407 else
2408 begin
2409 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2410 y := 128;
2411 player_stat(0);
2412 end;
2413 end;
2415 procedure DrawLoadingStat();
2416 var
2417 ww, hh: Word;
2418 xx, yy, i: Integer;
2419 s: String;
2420 begin
2421 if Length(LoadingStat.Msgs) = 0 then
2422 Exit;
2424 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2425 yy := (gScreenHeight div 3);
2426 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2427 xx := (gScreenWidth div 3);
2429 with LoadingStat do
2430 for i := 0 to NextMsg-1 do
2431 begin
2432 if (i = (NextMsg-1)) and (MaxValue > 0) then
2433 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2434 else
2435 s := Msgs[i];
2437 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2438 yy := yy + LOADING_INTERLINE;
2439 end;
2440 end;
2442 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2443 var
2444 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2446 function monDraw (mon: TMonster): Boolean;
2447 begin
2448 result := false; // don't stop
2449 with mon do
2450 begin
2451 if Live then
2452 begin
2453 // Ëåâûé âåðõíèé óãîë
2454 aX := Obj.X div ScaleSz + 1;
2455 aY := Obj.Y div ScaleSz + 1;
2456 // Ðàçìåðû
2457 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2458 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2459 // Ïðàâûé íèæíèé óãîë
2460 aX2 := aX + aX2 - 1;
2461 aY2 := aY + aY2 - 1;
2462 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2463 end;
2464 end;
2465 end;
2467 begin
2468 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2469 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2470 begin
2471 Scale := 1;
2472 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2473 ScaleSz := 16 div Scale;
2474 // Ðàçìåðû ìèíè-êàðòû:
2475 aX := max(gMapInfo.Width div ScaleSz, 1);
2476 aY := max(gMapInfo.Height div ScaleSz, 1);
2477 // Ðàìêà êàðòû:
2478 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2480 if gWalls <> nil then
2481 begin
2482 // Ðèñóåì ñòåíû:
2483 for a := 0 to High(gWalls) do
2484 with gWalls[a] do
2485 if PanelType <> 0 then
2486 begin
2487 // Ëåâûé âåðõíèé óãîë:
2488 aX := X div ScaleSz;
2489 aY := Y div ScaleSz;
2490 // Ðàçìåðû:
2491 aX2 := max(Width div ScaleSz, 1);
2492 aY2 := max(Height div ScaleSz, 1);
2493 // Ïðàâûé íèæíèé óãîë:
2494 aX2 := aX + aX2 - 1;
2495 aY2 := aY + aY2 - 1;
2497 case PanelType of
2498 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2499 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2500 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2501 end;
2502 end;
2503 end;
2504 if gSteps <> nil then
2505 begin
2506 // Ðèñóåì ñòóïåíè:
2507 for a := 0 to High(gSteps) do
2508 with gSteps[a] do
2509 if PanelType <> 0 then
2510 begin
2511 // Ëåâûé âåðõíèé óãîë:
2512 aX := X div ScaleSz;
2513 aY := Y div ScaleSz;
2514 // Ðàçìåðû:
2515 aX2 := max(Width div ScaleSz, 1);
2516 aY2 := max(Height div ScaleSz, 1);
2517 // Ïðàâûé íèæíèé óãîë:
2518 aX2 := aX + aX2 - 1;
2519 aY2 := aY + aY2 - 1;
2521 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2522 end;
2523 end;
2524 if gLifts <> nil then
2525 begin
2526 // Ðèñóåì ëèôòû:
2527 for a := 0 to High(gLifts) do
2528 with gLifts[a] do
2529 if PanelType <> 0 then
2530 begin
2531 // Ëåâûé âåðõíèé óãîë:
2532 aX := X div ScaleSz;
2533 aY := Y div ScaleSz;
2534 // Ðàçìåðû:
2535 aX2 := max(Width div ScaleSz, 1);
2536 aY2 := max(Height div ScaleSz, 1);
2537 // Ïðàâûé íèæíèé óãîë:
2538 aX2 := aX + aX2 - 1;
2539 aY2 := aY + aY2 - 1;
2541 case LiftType of
2542 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2543 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2544 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2545 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2546 end;
2547 end;
2548 end;
2549 if gWater <> nil then
2550 begin
2551 // Ðèñóåì âîäó:
2552 for a := 0 to High(gWater) do
2553 with gWater[a] do
2554 if PanelType <> 0 then
2555 begin
2556 // Ëåâûé âåðõíèé óãîë:
2557 aX := X div ScaleSz;
2558 aY := Y div ScaleSz;
2559 // Ðàçìåðû:
2560 aX2 := max(Width div ScaleSz, 1);
2561 aY2 := max(Height div ScaleSz, 1);
2562 // Ïðàâûé íèæíèé óãîë:
2563 aX2 := aX + aX2 - 1;
2564 aY2 := aY + aY2 - 1;
2566 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2567 end;
2568 end;
2569 if gAcid1 <> nil then
2570 begin
2571 // Ðèñóåì êèñëîòó 1:
2572 for a := 0 to High(gAcid1) do
2573 with gAcid1[a] do
2574 if PanelType <> 0 then
2575 begin
2576 // Ëåâûé âåðõíèé óãîë:
2577 aX := X div ScaleSz;
2578 aY := Y div ScaleSz;
2579 // Ðàçìåðû:
2580 aX2 := max(Width div ScaleSz, 1);
2581 aY2 := max(Height div ScaleSz, 1);
2582 // Ïðàâûé íèæíèé óãîë:
2583 aX2 := aX + aX2 - 1;
2584 aY2 := aY + aY2 - 1;
2586 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2587 end;
2588 end;
2589 if gAcid2 <> nil then
2590 begin
2591 // Ðèñóåì êèñëîòó 2:
2592 for a := 0 to High(gAcid2) do
2593 with gAcid2[a] do
2594 if PanelType <> 0 then
2595 begin
2596 // Ëåâûé âåðõíèé óãîë:
2597 aX := X div ScaleSz;
2598 aY := Y div ScaleSz;
2599 // Ðàçìåðû:
2600 aX2 := max(Width div ScaleSz, 1);
2601 aY2 := max(Height div ScaleSz, 1);
2602 // Ïðàâûé íèæíèé óãîë:
2603 aX2 := aX + aX2 - 1;
2604 aY2 := aY + aY2 - 1;
2606 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2607 end;
2608 end;
2609 if gPlayers <> nil then
2610 begin
2611 // Ðèñóåì èãðîêîâ:
2612 for a := 0 to High(gPlayers) do
2613 if gPlayers[a] <> nil then with gPlayers[a] do
2614 if Live then begin
2615 // Ëåâûé âåðõíèé óãîë:
2616 aX := Obj.X div ScaleSz + 1;
2617 aY := Obj.Y div ScaleSz + 1;
2618 // Ðàçìåðû:
2619 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2620 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2621 // Ïðàâûé íèæíèé óãîë:
2622 aX2 := aX + aX2 - 1;
2623 aY2 := aY + aY2 - 1;
2625 if gPlayers[a] = p then
2626 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2627 else
2628 case Team of
2629 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2630 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2631 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2632 end;
2633 end;
2634 end;
2635 // Ðèñóåì ìîíñòðîâ
2636 g_Mons_ForEach(monDraw);
2637 end;
2638 end;
2641 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2642 procedure renderDynLightsInternal ();
2643 var
2644 lln: Integer;
2645 lx, ly, lrad: Integer;
2646 begin
2647 //TODO: lights should be in separate grid, i think
2648 // but on the other side: grid may be slower for dynlights, as their lifetime is short
2649 if not gwin_has_stencil or (g_dynLightCount < 1) then exit;
2651 // setup OpenGL parameters
2652 glStencilMask($FFFFFFFF);
2653 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2654 glEnable(GL_STENCIL_TEST);
2655 glEnable(GL_SCISSOR_TEST);
2656 glClear(GL_STENCIL_BUFFER_BIT);
2657 glStencilFunc(GL_EQUAL, 0, $ff);
2659 for lln := 0 to g_dynLightCount-1 do
2660 begin
2661 lx := g_dynLights[lln].x;
2662 ly := g_dynLights[lln].y;
2663 lrad := g_dynLights[lln].radius;
2664 if lrad < 3 then continue;
2666 if lx-sX+lrad < 0 then continue;
2667 if ly-sY+lrad < 0 then continue;
2668 if lx-sX-lrad >= gPlayerScreenSize.X then continue;
2669 if ly-sY-lrad >= gPlayerScreenSize.Y then continue;
2671 // set scissor to optimize drawing
2672 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2673 // no need to clear stencil buffer, light blitting will do it for us
2674 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2675 // draw extruded panels
2676 glDisable(GL_TEXTURE_2D);
2677 glDisable(GL_BLEND);
2678 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2679 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2680 // render light texture
2681 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2682 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2683 // blend it
2684 glEnable(GL_BLEND);
2685 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2686 glEnable(GL_TEXTURE_2D);
2687 // color and opacity
2688 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2689 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2690 glBegin(GL_QUADS);
2691 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2692 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2693 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2694 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2695 glEnd();
2696 end;
2698 // done
2699 glDisable(GL_STENCIL_TEST);
2700 glDisable(GL_BLEND);
2701 glDisable(GL_SCISSOR_TEST);
2702 glScissor(0, 0, sWidth, sHeight);
2703 end;
2706 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2707 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
2708 procedure renderMapInternal (backXOfs, backYOfs: Integer; transX, transY: Integer; setTransMatrix: Boolean);
2709 type
2710 TDrawCB = procedure ();
2712 procedure drawPanelType (profname: AnsiString; panType: DWord);
2713 var
2714 tagmask: Integer;
2715 pan: TPanel;
2716 begin
2717 profileFrameDraw.sectionBegin(profname);
2718 if gdbg_map_use_accel_render then
2719 begin
2720 tagmask := panelTypeToTag(panType);
2721 {$IF TRUE}
2722 while (gDrawPanelList.count > 0) do
2723 begin
2724 pan := TPanel(gDrawPanelList.front());
2725 if ((pan.tag and tagmask) = 0) then break;
2726 pan.Draw();
2727 gDrawPanelList.popFront();
2728 end;
2729 {$ELSE}
2730 e_WriteLog(Format('=== PANELS: %d ===', [gDrawPanelList.count]), MSG_NOTIFY);
2731 while (gDrawPanelList.count > 0) do
2732 begin
2733 pan := TPanel(gDrawPanelList.front());
2734 e_WriteLog(Format('tagmask: 0x%04x; pan.tag: 0x%04x; pan.arrIdx: %d', [tagmask, pan.tag, pan.arrIdx]), MSG_NOTIFY);
2735 pan.Draw();
2736 gDrawPanelList.popFront();
2737 end;
2738 {$ENDIF}
2739 end
2740 else
2741 begin
2742 g_Map_DrawPanels(panType);
2743 end;
2744 profileFrameDraw.sectionEnd();
2745 end;
2747 procedure drawOther (profname: AnsiString; cb: TDrawCB);
2748 begin
2749 profileFrameDraw.sectionBegin(profname);
2750 if assigned(cb) then cb();
2751 profileFrameDraw.sectionEnd();
2752 end;
2754 begin
2755 profileFrameDraw.sectionBegin('total');
2757 // our accelerated renderer will collect all panels to gDrawPanelList
2758 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
2759 profileFrameDraw.sectionBegin('collect');
2760 if gdbg_map_use_accel_render then
2761 begin
2762 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
2763 end;
2764 profileFrameDraw.sectionEnd();
2766 profileFrameDraw.sectionBegin('skyback');
2767 g_Map_DrawBack(backXOfs, backYOfs);
2768 profileFrameDraw.sectionEnd();
2770 if (setTransMatrix) then glTranslatef(transX, transY, 0);
2772 drawPanelType('*back', PANEL_BACK);
2773 drawPanelType('*step', PANEL_STEP);
2774 drawOther('items', @g_Items_Draw);
2775 drawOther('weapons', @g_Weapon_Draw);
2776 drawOther('shells', @g_Player_DrawShells);
2777 drawOther('drawall', @g_Player_DrawAll);
2778 drawOther('corpses', @g_Player_DrawCorpses);
2779 drawPanelType('*wall', PANEL_WALL);
2780 drawOther('monsters', @g_Monsters_Draw);
2781 drawPanelType('*door', PANEL_CLOSEDOOR);
2782 drawOther('gfx', @g_GFX_Draw);
2783 drawOther('flags', @g_Map_DrawFlags);
2784 drawPanelType('*acid1', PANEL_ACID1);
2785 drawPanelType('*acid2', PANEL_ACID2);
2786 drawPanelType('*water', PANEL_WATER);
2787 drawOther('dynlights', @renderDynLightsInternal);
2788 drawPanelType('*fore', PANEL_FORE);
2790 if g_debug_HealthBar then
2791 begin
2792 g_Monsters_DrawHealth();
2793 g_Player_DrawHealth();
2794 end;
2796 profileFrameDraw.mainEnd(); // map rendering
2797 end;
2800 procedure DrawMapView(x, y, w, h: Integer);
2802 var
2803 bx, by: Integer;
2804 begin
2805 glPushMatrix();
2807 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2808 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2810 sX := x;
2811 sY := y;
2812 sWidth := w;
2813 sHeight := h;
2815 renderMapInternal(-bx, -by, -x, -y, true);
2817 glPopMatrix();
2818 end;
2821 procedure DrawPlayer(p: TPlayer);
2822 var
2823 px, py, a, b, c, d: Integer;
2824 //R: TRect;
2826 begin
2827 if (p = nil) or (p.FDummy) then
2828 begin
2829 glPushMatrix();
2830 g_Map_DrawBack(0, 0);
2831 glPopMatrix();
2832 Exit;
2833 end;
2835 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
2836 profileFrameDraw.mainBegin(g_profile_frame_draw);
2838 gPlayerDrawn := p;
2840 glPushMatrix();
2842 px := p.GameX + PLAYER_RECT_CX;
2843 py := p.GameY + PLAYER_RECT_CY;
2845 if px > (gPlayerScreenSize.X div 2) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
2846 if py > (gPlayerScreenSize.Y div 2) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
2848 if px > gMapInfo.Width-(gPlayerScreenSize.X div 2) then a := -gMapInfo.Width+gPlayerScreenSize.X;
2849 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
2851 if gMapInfo.Width <= gPlayerScreenSize.X then a := 0;
2852 if gMapInfo.Height <= gPlayerScreenSize.Y then b := 0;
2854 if p.IncCam <> 0 then
2855 begin
2856 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
2857 begin
2858 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2859 begin
2860 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2861 end;
2862 end;
2864 if py < gPlayerScreenSize.Y div 2 then
2865 begin
2866 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2867 begin
2868 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2869 end;
2870 end;
2872 if p.IncCam < 0 then
2873 begin
2874 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
2875 end;
2877 if p.IncCam > 0 then
2878 begin
2879 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
2880 end;
2881 end;
2883 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
2884 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
2885 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2887 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
2888 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
2889 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
2891 sX := -a;
2892 sY := -(b+p.IncCam);
2893 sWidth := gPlayerScreenSize.X;
2894 sHeight := gPlayerScreenSize.Y;
2896 //glTranslatef(a, b+p.IncCam, 0);
2898 renderMapInternal(-c, -d, a, b+p.IncCam, true);
2900 if p.FSpectator then
2901 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2902 p.GameY + PLAYER_RECT_CY - 4,
2903 'X', gStdFont, 255, 255, 255, 1, True);
2905 for a := 0 to High(gCollideMap) do
2906 for b := 0 to High(gCollideMap[a]) do
2907 begin
2908 d := 0;
2909 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2910 d := d + 1;
2911 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2912 d := d + 2;
2914 case d of
2915 1: e_DrawPoint(1, b, a, 200, 200, 200);
2916 2: e_DrawPoint(1, b, a, 64, 64, 255);
2917 3: e_DrawPoint(1, b, a, 255, 0, 255);
2918 end;
2919 end;
2922 glPopMatrix();
2924 p.DrawPain();
2925 p.DrawPickup();
2926 p.DrawRulez();
2927 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
2928 if g_Debug_Player then
2929 g_Player_DrawDebug(p);
2930 p.DrawGUI();
2931 end;
2933 procedure drawProfilers ();
2934 var
2935 px: Integer = -1;
2936 py: Integer = -1;
2937 begin
2938 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
2939 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
2940 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
2941 end;
2943 procedure g_Game_Draw();
2944 var
2945 ID: DWORD;
2946 w, h: Word;
2947 ww, hh: Byte;
2948 Time: Int64;
2949 back: string;
2950 plView1, plView2: TPlayer;
2951 Split: Boolean;
2952 begin
2953 if gExit = EXIT_QUIT then Exit;
2955 Time := GetTimer() {div 1000};
2956 FPSCounter := FPSCounter+1;
2957 if Time - FPSTime >= 1000 then
2958 begin
2959 FPS := FPSCounter;
2960 FPSCounter := 0;
2961 FPSTime := Time;
2962 end;
2964 if gGameOn or (gState = STATE_FOLD) then
2965 begin
2966 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
2967 begin
2968 gSpectMode := SPECT_NONE;
2969 if not gRevertPlayers then
2970 begin
2971 plView1 := gPlayer1;
2972 plView2 := gPlayer2;
2973 end
2974 else
2975 begin
2976 plView1 := gPlayer2;
2977 plView2 := gPlayer1;
2978 end;
2979 end
2980 else
2981 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
2982 begin
2983 gSpectMode := SPECT_NONE;
2984 if gPlayer2 = nil then
2985 plView1 := gPlayer1
2986 else
2987 plView1 := gPlayer2;
2988 plView2 := nil;
2989 end
2990 else
2991 begin
2992 plView1 := nil;
2993 plView2 := nil;
2994 end;
2996 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
2997 gSpectMode := SPECT_STATS;
2999 if gSpectMode = SPECT_PLAYERS then
3000 if gPlayers <> nil then
3001 begin
3002 plView1 := GetActivePlayer_ByID(gSpectPID1);
3003 if plView1 = nil then
3004 begin
3005 gSpectPID1 := GetActivePlayerID_Next();
3006 plView1 := GetActivePlayer_ByID(gSpectPID1);
3007 end;
3008 if gSpectViewTwo then
3009 begin
3010 plView2 := GetActivePlayer_ByID(gSpectPID2);
3011 if plView2 = nil then
3012 begin
3013 gSpectPID2 := GetActivePlayerID_Next();
3014 plView2 := GetActivePlayer_ByID(gSpectPID2);
3015 end;
3016 end;
3017 end;
3019 if gSpectMode = SPECT_MAPVIEW then
3020 begin
3021 // Ðåæèì ïðîñìîòðà êàðòû
3022 Split := False;
3023 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3024 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3025 gHearPoint1.Active := True;
3026 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3027 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3028 gHearPoint2.Active := False;
3029 end
3030 else
3031 begin
3032 Split := (plView1 <> nil) and (plView2 <> nil);
3034 // Òî÷êè ñëóõà èãðîêîâ
3035 if plView1 <> nil then
3036 begin
3037 gHearPoint1.Active := True;
3038 gHearPoint1.Coords.X := plView1.GameX;
3039 gHearPoint1.Coords.Y := plView1.GameY;
3040 end else
3041 gHearPoint1.Active := False;
3042 if plView2 <> nil then
3043 begin
3044 gHearPoint2.Active := True;
3045 gHearPoint2.Coords.X := plView2.GameX;
3046 gHearPoint2.Coords.Y := plView2.GameY;
3047 end else
3048 gHearPoint2.Active := False;
3050 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3051 gPlayerScreenSize.X := gScreenWidth-196;
3052 if Split then
3053 begin
3054 gPlayerScreenSize.Y := gScreenHeight div 2;
3055 if gScreenHeight mod 2 = 0 then
3056 Dec(gPlayerScreenSize.Y);
3057 end
3058 else
3059 gPlayerScreenSize.Y := gScreenHeight;
3061 if Split then
3062 if gScreenHeight mod 2 = 0 then
3063 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3064 else
3065 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3067 DrawPlayer(plView1);
3068 gPlayer1ScreenCoord.X := sX;
3069 gPlayer1ScreenCoord.Y := sY;
3071 if Split then
3072 begin
3073 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3075 DrawPlayer(plView2);
3076 gPlayer2ScreenCoord.X := sX;
3077 gPlayer2ScreenCoord.Y := sY;
3078 end;
3080 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3082 if Split then
3083 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3084 end;
3086 if MessageText <> '' then
3087 begin
3088 w := 0;
3089 h := 0;
3090 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3091 if Split then
3092 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3093 (gScreenHeight div 2)-(h div 2), MessageText)
3094 else
3095 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3096 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3097 end;
3099 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3101 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3102 begin
3103 // Draw spectator GUI
3104 ww := 0;
3105 hh := 0;
3106 e_TextureFontGetSize(gStdFont, ww, hh);
3107 case gSpectMode of
3108 SPECT_STATS:
3109 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3110 SPECT_MAPVIEW:
3111 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3112 SPECT_PLAYERS:
3113 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3114 end;
3115 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3116 if gSpectMode = SPECT_MAPVIEW then
3117 begin
3118 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3119 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3120 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3121 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3122 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3123 end;
3124 if gSpectMode = SPECT_PLAYERS then
3125 begin
3126 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3127 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3128 if gSpectViewTwo then
3129 begin
3130 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3131 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3132 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3133 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3134 end
3135 else
3136 begin
3137 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3138 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3139 end;
3140 end;
3141 end;
3142 end;
3144 if gPause and gGameOn and (g_ActiveWindow = nil) then
3145 begin
3146 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3148 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3149 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3150 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3151 end;
3153 if not gGameOn then
3154 begin
3155 if (gState = STATE_MENU) then
3156 begin
3157 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
3158 begin
3159 if g_Texture_Get('MENU_BACKGROUND', ID) then
3160 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3161 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3162 end;
3163 if g_ActiveWindow <> nil then
3164 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3165 end;
3167 if gState = STATE_FOLD then
3168 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3170 if gState = STATE_INTERCUSTOM then
3171 begin
3172 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3173 begin
3174 back := 'TEXTURE_endpic';
3175 if not g_Texture_Get(back, ID) then
3176 back := _lc[I_TEXTURE_ENDPIC];
3177 end
3178 else
3179 back := 'INTER';
3181 if g_Texture_Get(back, ID) then
3182 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3183 else
3184 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3186 DrawCustomStat();
3188 if g_ActiveWindow <> nil then
3189 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3190 end;
3192 if gState = STATE_INTERSINGLE then
3193 begin
3194 if EndingGameCounter > 0 then
3195 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter)
3196 else
3197 begin
3198 back := 'INTER';
3200 if g_Texture_Get(back, ID) then
3201 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3202 else
3203 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3205 DrawSingleStat();
3207 if g_ActiveWindow <> nil then
3208 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3209 end;
3210 end;
3212 if gState = STATE_ENDPIC then
3213 begin
3214 ID := DWORD(-1);
3215 if not g_Texture_Get('TEXTURE_endpic', ID) then
3216 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3218 if ID <> DWORD(-1) then
3219 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3220 else
3221 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3223 if g_ActiveWindow <> nil then
3224 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3225 end;
3227 if gState = STATE_SLIST then
3228 begin
3229 if g_Texture_Get('MENU_BACKGROUND', ID) then
3230 begin
3231 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3232 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3233 end;
3234 g_Serverlist_Draw(slCurrent);
3235 end;
3236 end;
3238 if g_ActiveWindow <> nil then
3239 begin
3240 if gGameOn then
3241 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3242 g_ActiveWindow.Draw();
3243 end;
3245 g_Console_Draw();
3247 if g_debug_Sounds and gGameOn then
3248 begin
3249 for w := 0 to High(e_SoundsArray) do
3250 for h := 0 to e_SoundsArray[w].nRefs do
3251 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3252 end;
3254 if gShowFPS then
3255 begin
3256 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3257 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3258 end;
3260 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3261 e_TextureFontPrint(gScreenWidth-72, 0,
3262 Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]),
3263 gStdFont);
3265 if gGameOn then drawProfilers();
3266 end;
3268 procedure g_Game_Quit();
3269 begin
3270 g_Game_StopAllSounds(True);
3271 gMusic.Free();
3272 g_Game_SaveOptions();
3273 g_Game_FreeData();
3274 g_PlayerModel_FreeData();
3275 g_Texture_DeleteAll();
3276 g_Frames_DeleteAll();
3277 g_Menu_Free();
3279 if NetInitDone then g_Net_Free;
3281 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3282 if gMapToDelete <> '' then
3283 g_Game_DeleteTestMap();
3285 gExit := EXIT_QUIT;
3286 PushExitEvent();
3287 end;
3289 procedure g_FatalError(Text: String);
3290 begin
3291 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3292 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3294 gExit := EXIT_SIMPLE;
3295 end;
3297 procedure g_SimpleError(Text: String);
3298 begin
3299 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3300 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3301 end;
3303 procedure g_Game_SetupScreenSize();
3304 const
3305 RES_FACTOR = 4.0 / 3.0;
3306 var
3307 s: Single;
3308 rf: Single;
3309 bw, bh: Word;
3310 begin
3311 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3312 gPlayerScreenSize.X := gScreenWidth-196;
3313 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3314 gPlayerScreenSize.Y := gScreenHeight div 2
3315 else
3316 gPlayerScreenSize.Y := gScreenHeight;
3318 // Ðàçìåð çàäíåãî ïëàíà:
3319 if BackID <> DWORD(-1) then
3320 begin
3321 s := SKY_STRETCH;
3322 if (gScreenWidth*s > gMapInfo.Width) or
3323 (gScreenHeight*s > gMapInfo.Height) then
3324 begin
3325 gBackSize.X := gScreenWidth;
3326 gBackSize.Y := gScreenHeight;
3327 end
3328 else
3329 begin
3330 e_GetTextureSize(BackID, @bw, @bh);
3331 rf := Single(bw) / Single(bh);
3332 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3333 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3334 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3335 if (s < 1.0) then s := 1.0;
3336 gBackSize.X := Round(bw*s);
3337 gBackSize.Y := Round(bh*s);
3338 end;
3339 end;
3340 end;
3342 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3343 begin
3344 g_Window_SetSize(newWidth, newHeight, nowFull);
3345 end;
3347 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3348 begin
3349 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3350 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3351 Exit;
3352 if gPlayer1 = nil then
3353 begin
3354 if g_Game_IsClient then
3355 begin
3356 if NetPlrUID1 > -1 then
3357 begin
3358 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3359 gPlayer1 := g_Player_Get(NetPlrUID1);
3360 end;
3361 Exit;
3362 end;
3364 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3365 Team := gPlayer1Settings.Team;
3367 // Ñîçäàíèå ïåðâîãî èãðîêà:
3368 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3369 gPlayer1Settings.Color,
3370 Team, False));
3371 if gPlayer1 = nil then
3372 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3373 else
3374 begin
3375 gPlayer1.Name := gPlayer1Settings.Name;
3376 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3377 if g_Game_IsServer and g_Game_IsNet then
3378 MH_SEND_PlayerCreate(gPlayer1.UID);
3379 gPlayer1.Respawn(False, True);
3381 if g_Game_IsNet and NetUseMaster then
3382 g_Net_Slist_Update;
3383 end;
3385 Exit;
3386 end;
3387 if gPlayer2 = nil then
3388 begin
3389 if g_Game_IsClient then
3390 begin
3391 if NetPlrUID2 > -1 then
3392 gPlayer2 := g_Player_Get(NetPlrUID2);
3393 Exit;
3394 end;
3396 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3397 Team := gPlayer2Settings.Team;
3399 // Ñîçäàíèå âòîðîãî èãðîêà:
3400 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3401 gPlayer2Settings.Color,
3402 Team, False));
3403 if gPlayer2 = nil then
3404 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3405 else
3406 begin
3407 gPlayer2.Name := gPlayer2Settings.Name;
3408 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3409 if g_Game_IsServer and g_Game_IsNet then
3410 MH_SEND_PlayerCreate(gPlayer2.UID);
3411 gPlayer2.Respawn(False, True);
3413 if g_Game_IsNet and NetUseMaster then
3414 g_Net_Slist_Update;
3415 end;
3417 Exit;
3418 end;
3419 end;
3421 procedure g_Game_RemovePlayer();
3422 var
3423 Pl: TPlayer;
3424 begin
3425 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3426 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3427 Exit;
3428 Pl := gPlayer2;
3429 if Pl <> nil then
3430 begin
3431 if g_Game_IsServer then
3432 begin
3433 Pl.Lives := 0;
3434 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3435 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3436 g_Player_Remove(Pl.UID);
3438 if g_Game_IsNet and NetUseMaster then
3439 g_Net_Slist_Update;
3440 end else
3441 gPlayer2 := nil;
3442 Exit;
3443 end;
3444 Pl := gPlayer1;
3445 if Pl <> nil then
3446 begin
3447 if g_Game_IsServer then
3448 begin
3449 Pl.Lives := 0;
3450 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3451 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3452 g_Player_Remove(Pl.UID);
3454 if g_Game_IsNet and NetUseMaster then
3455 g_Net_Slist_Update;
3456 end else
3457 begin
3458 gPlayer1 := nil;
3459 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3460 end;
3461 Exit;
3462 end;
3463 end;
3465 procedure g_Game_Spectate();
3466 begin
3467 g_Game_RemovePlayer();
3468 if gPlayer1 <> nil then
3469 g_Game_RemovePlayer();
3470 end;
3472 procedure g_Game_SpectateCenterView();
3473 begin
3474 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3475 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3476 end;
3478 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3479 var
3480 i, nPl: Integer;
3481 begin
3482 g_Game_Free();
3484 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3486 g_Game_ClearLoading();
3488 // Íàñòðîéêè èãðû:
3489 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3490 gAimLine := False;
3491 gShowMap := False;
3492 gGameSettings.GameType := GT_SINGLE;
3493 gGameSettings.MaxLives := 0;
3494 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3495 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3496 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3497 gSwitchGameMode := GM_SINGLE;
3499 g_Game_ExecuteEvent('ongamestart');
3501 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3502 g_Game_SetupScreenSize();
3504 // Ñîçäàíèå ïåðâîãî èãðîêà:
3505 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3506 gPlayer1Settings.Color,
3507 gPlayer1Settings.Team, False));
3508 if gPlayer1 = nil then
3509 begin
3510 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3511 Exit;
3512 end;
3514 gPlayer1.Name := gPlayer1Settings.Name;
3515 nPl := 1;
3517 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3518 if TwoPlayers then
3519 begin
3520 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3521 gPlayer2Settings.Color,
3522 gPlayer2Settings.Team, False));
3523 if gPlayer2 = nil then
3524 begin
3525 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3526 Exit;
3527 end;
3529 gPlayer2.Name := gPlayer2Settings.Name;
3530 Inc(nPl);
3531 end;
3533 // Çàãðóçêà è çàïóñê êàðòû:
3534 if not g_Game_StartMap(MAP, True) then
3535 begin
3536 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3537 Exit;
3538 end;
3540 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3541 g_Player_Init();
3543 // Ñîçäàåì áîòîâ:
3544 for i := nPl+1 to nPlayers do
3545 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3546 end;
3548 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3549 TimeLimit, GoalLimit: Word;
3550 MaxLives: Byte;
3551 Options: LongWord; nPlayers: Byte);
3552 var
3553 i, nPl: Integer;
3554 begin
3555 g_Game_Free();
3557 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3559 g_Game_ClearLoading();
3561 // Íàñòðîéêè èãðû:
3562 gGameSettings.GameType := GT_CUSTOM;
3563 gGameSettings.GameMode := GameMode;
3564 gSwitchGameMode := GameMode;
3565 gGameSettings.TimeLimit := TimeLimit;
3566 gGameSettings.GoalLimit := GoalLimit;
3567 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3568 gGameSettings.Options := Options;
3570 gCoopTotalMonstersKilled := 0;
3571 gCoopTotalSecretsFound := 0;
3572 gCoopTotalMonsters := 0;
3573 gCoopTotalSecrets := 0;
3574 gAimLine := False;
3575 gShowMap := False;
3577 g_Game_ExecuteEvent('ongamestart');
3579 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3580 g_Game_SetupScreenSize();
3582 // Ðåæèì íàáëþäàòåëÿ:
3583 if nPlayers = 0 then
3584 begin
3585 gPlayer1 := nil;
3586 gPlayer2 := nil;
3587 end;
3589 nPl := 0;
3590 if nPlayers >= 1 then
3591 begin
3592 // Ñîçäàíèå ïåðâîãî èãðîêà:
3593 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3594 gPlayer1Settings.Color,
3595 gPlayer1Settings.Team, False));
3596 if gPlayer1 = nil then
3597 begin
3598 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3599 Exit;
3600 end;
3602 gPlayer1.Name := gPlayer1Settings.Name;
3603 Inc(nPl);
3604 end;
3606 if nPlayers >= 2 then
3607 begin
3608 // Ñîçäàíèå âòîðîãî èãðîêà:
3609 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3610 gPlayer2Settings.Color,
3611 gPlayer2Settings.Team, False));
3612 if gPlayer2 = nil then
3613 begin
3614 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3615 Exit;
3616 end;
3618 gPlayer2.Name := gPlayer2Settings.Name;
3619 Inc(nPl);
3620 end;
3622 // Çàãðóçêà è çàïóñê êàðòû:
3623 if not g_Game_StartMap(Map, True) then
3624 begin
3625 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3626 Exit;
3627 end;
3629 // Íåò òî÷åê ïîÿâëåíèÿ:
3630 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3631 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3632 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3633 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3634 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3635 begin
3636 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3637 Exit;
3638 end;
3640 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3641 g_Player_Init();
3643 // Ñîçäàåì áîòîâ:
3644 for i := nPl+1 to nPlayers do
3645 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3646 end;
3648 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3649 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3650 Options: LongWord; nPlayers: Byte;
3651 IPAddr: LongWord; Port: Word);
3652 begin
3653 g_Game_Free();
3655 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3657 g_Game_ClearLoading();
3659 // Íàñòðîéêè èãðû:
3660 gGameSettings.GameType := GT_SERVER;
3661 gGameSettings.GameMode := GameMode;
3662 gSwitchGameMode := GameMode;
3663 gGameSettings.TimeLimit := TimeLimit;
3664 gGameSettings.GoalLimit := GoalLimit;
3665 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3666 gGameSettings.Options := Options;
3668 gCoopTotalMonstersKilled := 0;
3669 gCoopTotalSecretsFound := 0;
3670 gCoopTotalMonsters := 0;
3671 gCoopTotalSecrets := 0;
3672 gAimLine := False;
3673 gShowMap := False;
3675 g_Game_ExecuteEvent('ongamestart');
3677 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3678 g_Game_SetupScreenSize();
3680 // Ðåæèì íàáëþäàòåëÿ:
3681 if nPlayers = 0 then
3682 begin
3683 gPlayer1 := nil;
3684 gPlayer2 := nil;
3685 end;
3687 if nPlayers >= 1 then
3688 begin
3689 // Ñîçäàíèå ïåðâîãî èãðîêà:
3690 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3691 gPlayer1Settings.Color,
3692 gPlayer1Settings.Team, False));
3693 if gPlayer1 = nil then
3694 begin
3695 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3696 Exit;
3697 end;
3699 gPlayer1.Name := gPlayer1Settings.Name;
3700 end;
3702 if nPlayers >= 2 then
3703 begin
3704 // Ñîçäàíèå âòîðîãî èãðîêà:
3705 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3706 gPlayer2Settings.Color,
3707 gPlayer2Settings.Team, False));
3708 if gPlayer2 = nil then
3709 begin
3710 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3711 Exit;
3712 end;
3714 gPlayer2.Name := gPlayer2Settings.Name;
3715 end;
3717 // Ñòàðòóåì ñåðâåð
3718 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3719 begin
3720 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3721 Exit;
3722 end;
3724 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3726 // Çàãðóçêà è çàïóñê êàðòû:
3727 if not g_Game_StartMap(Map, True) then
3728 begin
3729 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3730 Exit;
3731 end;
3733 // Íåò òî÷åê ïîÿâëåíèÿ:
3734 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3735 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3736 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3737 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3738 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3739 begin
3740 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3741 Exit;
3742 end;
3744 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3745 g_Player_Init();
3747 NetState := NET_STATE_GAME;
3748 end;
3750 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3751 var
3752 Map: String;
3753 WadName: string;
3754 Ptr: Pointer;
3755 T: Cardinal;
3756 MID: Byte;
3757 State: Byte;
3758 OuterLoop: Boolean;
3759 newResPath: string;
3760 InMsg: TMsg;
3761 begin
3762 g_Game_Free();
3764 State := 0;
3765 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3766 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3768 g_Game_ClearLoading();
3770 // Íàñòðîéêè èãðû:
3771 gGameSettings.GameType := GT_CLIENT;
3773 gCoopTotalMonstersKilled := 0;
3774 gCoopTotalSecretsFound := 0;
3775 gCoopTotalMonsters := 0;
3776 gCoopTotalSecrets := 0;
3777 gAimLine := False;
3778 gShowMap := False;
3780 g_Game_ExecuteEvent('ongamestart');
3782 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3783 g_Game_SetupScreenSize();
3785 NetState := NET_STATE_AUTH;
3787 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3788 // Ñòàðòóåì êëèåíò
3789 if not g_Net_Connect(Addr, Port) then
3790 begin
3791 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3792 NetState := NET_STATE_NONE;
3793 Exit;
3794 end;
3796 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3797 MC_SEND_Info(PW);
3798 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3800 OuterLoop := True;
3801 while OuterLoop do
3802 begin
3803 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3804 begin
3805 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3806 begin
3807 Ptr := NetEvent.packet^.data;
3808 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
3809 continue;
3811 MID := InMsg.ReadByte();
3813 if (MID = NET_MSG_INFO) and (State = 0) then
3814 begin
3815 NetMyID := InMsg.ReadByte();
3816 NetPlrUID1 := InMsg.ReadWord();
3818 WadName := InMsg.ReadString();
3819 Map := InMsg.ReadString();
3821 gWADHash := InMsg.ReadMD5();
3823 gGameSettings.GameMode := InMsg.ReadByte();
3824 gSwitchGameMode := gGameSettings.GameMode;
3825 gGameSettings.GoalLimit := InMsg.ReadWord();
3826 gGameSettings.TimeLimit := InMsg.ReadWord();
3827 gGameSettings.MaxLives := InMsg.ReadByte();
3828 gGameSettings.Options := InMsg.ReadLongWord();
3829 T := InMsg.ReadLongWord();
3831 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3832 if newResPath = '' then
3833 begin
3834 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3835 newResPath := g_Res_DownloadWAD(WadName);
3836 if newResPath = '' then
3837 begin
3838 g_FatalError(_lc[I_NET_ERR_HASH]);
3839 enet_packet_destroy(NetEvent.packet);
3840 NetState := NET_STATE_NONE;
3841 Exit;
3842 end;
3843 end;
3844 newResPath := ExtractRelativePath(MapsDir, newResPath);
3846 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3847 gPlayer1Settings.Color,
3848 gPlayer1Settings.Team, False));
3850 if gPlayer1 = nil then
3851 begin
3852 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3854 enet_packet_destroy(NetEvent.packet);
3855 NetState := NET_STATE_NONE;
3856 Exit;
3857 end;
3859 gPlayer1.Name := gPlayer1Settings.Name;
3860 gPlayer1.UID := NetPlrUID1;
3861 gPlayer1.Reset(True);
3863 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3864 begin
3865 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3867 enet_packet_destroy(NetEvent.packet);
3868 NetState := NET_STATE_NONE;
3869 Exit;
3870 end;
3872 gTime := T;
3874 State := 1;
3875 OuterLoop := False;
3876 enet_packet_destroy(NetEvent.packet);
3877 break;
3878 end
3879 else
3880 enet_packet_destroy(NetEvent.packet);
3881 end
3882 else
3883 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3884 begin
3885 State := 0;
3886 if (NetEvent.data <= NET_DISC_MAX) then
3887 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3888 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3889 OuterLoop := False;
3890 Break;
3891 end;
3892 end;
3894 ProcessLoading();
3896 e_PollInput();
3898 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3899 begin
3900 State := 0;
3901 break;
3902 end;
3903 end;
3905 if State <> 1 then
3906 begin
3907 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3908 NetState := NET_STATE_NONE;
3909 Exit;
3910 end;
3912 gLMSRespawn := LMS_RESPAWN_NONE;
3913 gLMSRespawnTime := 0;
3915 g_Player_Init();
3916 NetState := NET_STATE_GAME;
3917 MC_SEND_FullStateRequest;
3918 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3919 end;
3921 procedure g_Game_SaveOptions();
3922 begin
3923 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3924 end;
3926 procedure g_Game_ChangeMap(MapPath: String);
3927 var
3928 Force: Boolean;
3929 begin
3930 g_Game_ClearLoading();
3932 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3933 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3934 if gExitByTrigger then
3935 begin
3936 Force := False;
3937 gExitByTrigger := False;
3938 end;
3939 if not g_Game_StartMap(MapPath, Force) then
3940 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3941 end;
3943 procedure g_Game_Restart();
3944 var
3945 Map: string;
3946 begin
3947 if g_Game_IsClient then
3948 Exit;
3949 map := g_ExtractFileName(gMapInfo.Map);
3951 MessageTime := 0;
3952 gGameOn := False;
3953 g_Game_ClearLoading();
3954 g_Game_StartMap(Map, True);
3955 end;
3957 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
3958 var
3959 NewWAD, ResName: String;
3960 I: Integer;
3961 begin
3962 g_Map_Free();
3963 g_Player_RemoveAllCorpses();
3965 if (not g_Game_IsClient) and
3966 (gSwitchGameMode <> gGameSettings.GameMode) and
3967 (gGameSettings.GameMode <> GM_SINGLE) then
3968 begin
3969 if gSwitchGameMode = GM_CTF then
3970 gGameSettings.MaxLives := 0;
3971 gGameSettings.GameMode := gSwitchGameMode;
3972 Force := True;
3973 end else
3974 gSwitchGameMode := gGameSettings.GameMode;
3976 g_Player_ResetTeams();
3978 if Pos(':\', Map) > 0 then
3979 begin
3980 NewWAD := g_ExtractWadName(Map);
3981 ResName := g_ExtractFileName(Map);
3982 if g_Game_IsServer then
3983 begin
3984 gWADHash := MD5File(MapsDir + NewWAD);
3985 g_Game_LoadWAD(NewWAD);
3986 end else
3987 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
3988 g_Game_ClientWAD(NewWAD, gWADHash);
3989 end else
3990 ResName := Map;
3992 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
3993 if Result then
3994 begin
3995 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
3997 gState := STATE_NONE;
3998 g_ActiveWindow := nil;
3999 gGameOn := True;
4001 DisableCheats();
4002 ResetTimer();
4004 if gGameSettings.GameMode = GM_CTF then
4005 begin
4006 g_Map_ResetFlag(FLAG_RED);
4007 g_Map_ResetFlag(FLAG_BLUE);
4008 // CTF, à ôëàãîâ íåò:
4009 if not g_Map_HaveFlagPoints() then
4010 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4011 end;
4012 end
4013 else
4014 begin
4015 gState := STATE_MENU;
4016 gGameOn := False;
4017 end;
4019 gExit := 0;
4020 gPause := False;
4021 gTime := 0;
4022 NetTimeToUpdate := 1;
4023 NetTimeToReliable := 0;
4024 NetTimeToMaster := NetMasterRate;
4025 gLMSRespawn := LMS_RESPAWN_NONE;
4026 gLMSRespawnTime := 0;
4027 gMissionFailed := False;
4028 gNextMap := '';
4030 gCoopMonstersKilled := 0;
4031 gCoopSecretsFound := 0;
4033 gVoteInProgress := False;
4034 gVotePassed := False;
4035 gVoteCount := 0;
4036 gVoted := False;
4038 gStatsOff := False;
4040 if not gGameOn then Exit;
4042 g_Game_SpectateCenterView();
4044 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4045 begin
4046 gLMSRespawn := LMS_RESPAWN_WARMUP;
4047 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4048 gLMSSoftSpawn := True;
4049 if NetMode = NET_SERVER then
4050 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4051 else
4052 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4053 end;
4055 if NetMode = NET_SERVER then
4056 begin
4057 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4059 // Ìàñòåðñåðâåð
4060 if NetUseMaster then
4061 begin
4062 if (NetMHost = nil) or (NetMPeer = nil) then
4063 if not g_Net_Slist_Connect then
4064 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4066 g_Net_Slist_Update;
4067 end;
4069 if NetClients <> nil then
4070 for I := 0 to High(NetClients) do
4071 if NetClients[I].Used then
4072 begin
4073 NetClients[I].Voted := False;
4074 if NetClients[I].RequestedFullUpdate then
4075 begin
4076 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4077 NetClients[I].RequestedFullUpdate := False;
4078 end;
4079 end;
4081 g_Net_UnbanNonPermHosts();
4082 end;
4084 if gLastMap then
4085 begin
4086 gCoopTotalMonstersKilled := 0;
4087 gCoopTotalSecretsFound := 0;
4088 gCoopTotalMonsters := 0;
4089 gCoopTotalSecrets := 0;
4090 gLastMap := False;
4091 end;
4093 g_Game_ExecuteEvent('onmapstart');
4094 end;
4096 procedure SetFirstLevel();
4097 begin
4098 gNextMap := '';
4100 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4101 if MapList = nil then
4102 Exit;
4104 SortSArray(MapList);
4105 gNextMap := MapList[Low(MapList)];
4107 MapList := nil;
4108 end;
4110 procedure g_Game_ExitLevel(Map: Char16);
4111 begin
4112 gNextMap := Map;
4114 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4115 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4116 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4117 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4119 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4120 if gGameSettings.GameType = GT_SINGLE then
4121 gExit := EXIT_ENDLEVELSINGLE
4122 else // Âûøëè â âûõîä â Ñâîåé èãðå
4123 begin
4124 gExit := EXIT_ENDLEVELCUSTOM;
4125 if gGameSettings.GameMode = GM_COOP then
4126 g_Player_RememberAll;
4128 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4129 begin
4130 gLastMap := True;
4131 if gGameSettings.GameMode = GM_COOP then
4132 gStatsOff := True;
4134 gStatsPressed := True;
4135 gNextMap := 'MAP01';
4137 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4138 g_Game_NextLevel;
4140 if g_Game_IsNet then
4141 begin
4142 MH_SEND_GameStats();
4143 MH_SEND_CoopStats();
4144 end;
4145 end;
4146 end;
4147 end;
4149 procedure g_Game_RestartLevel();
4150 var
4151 Map: string;
4152 begin
4153 if gGameSettings.GameMode = GM_SINGLE then
4154 begin
4155 g_Game_Restart();
4156 Exit;
4157 end;
4158 gExit := EXIT_ENDLEVELCUSTOM;
4159 Map := g_ExtractFileName(gMapInfo.Map);
4160 gNextMap := Map;
4161 end;
4163 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4164 var
4165 gWAD: String;
4166 begin
4167 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4168 Exit;
4169 if not g_Game_IsClient then
4170 Exit;
4171 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4172 if gWAD = '' then
4173 begin
4174 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4175 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4176 if gWAD = '' then
4177 begin
4178 g_Game_Free();
4179 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4180 Exit;
4181 end;
4182 end;
4183 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4184 g_Game_LoadWAD(NewWAD);
4185 end;
4187 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4188 var
4189 i, n, nb, nr: Integer;
4191 function monRespawn (mon: TMonster): Boolean;
4192 begin
4193 result := false; // don't stop
4194 if not mon.FNoRespawn then mon.Respawn();
4195 end;
4197 begin
4198 if not g_Game_IsServer then Exit;
4199 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4200 gLMSRespawn := LMS_RESPAWN_NONE;
4201 gLMSRespawnTime := 0;
4202 MessageTime := 0;
4204 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4205 begin
4206 gMissionFailed := True;
4207 g_Game_RestartLevel;
4208 Exit;
4209 end;
4211 n := 0; nb := 0; nr := 0;
4212 for i := Low(gPlayers) to High(gPlayers) do
4213 if (gPlayers[i] <> nil) and
4214 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4215 (gPlayers[i] is TBot)) then
4216 begin
4217 Inc(n);
4218 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4219 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4220 end;
4222 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4223 begin
4224 // wait a second until the fuckers finally decide to join
4225 gLMSRespawn := LMS_RESPAWN_WARMUP;
4226 gLMSRespawnTime := gTime + 1000;
4227 gLMSSoftSpawn := NoMapRestart;
4228 Exit;
4229 end;
4231 g_Player_RemoveAllCorpses;
4232 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4233 if g_Game_IsNet then
4234 MH_SEND_GameEvent(NET_EV_LMS_START);
4236 for i := Low(gPlayers) to High(gPlayers) do
4237 begin
4238 if gPlayers[i] = nil then continue;
4239 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4240 // don't touch normal spectators
4241 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4242 begin
4243 gPlayers[i].FNoRespawn := True;
4244 gPlayers[i].Lives := 0;
4245 if g_Game_IsNet then
4246 MH_SEND_PlayerStats(gPlayers[I].UID);
4247 continue;
4248 end;
4249 gPlayers[i].FNoRespawn := False;
4250 gPlayers[i].Lives := gGameSettings.MaxLives;
4251 gPlayers[i].Respawn(False, True);
4252 if gGameSettings.GameMode = GM_COOP then
4253 begin
4254 gPlayers[i].Frags := 0;
4255 gPlayers[i].RecallState;
4256 end;
4257 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4258 gPlayer1 := g_Player_Get(gLMSPID1);
4259 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4260 gPlayer2 := g_Player_Get(gLMSPID2);
4261 end;
4263 g_Items_RestartRound();
4266 g_Mons_ForEach(monRespawn);
4268 gLMSSoftSpawn := False;
4269 end;
4271 function g_Game_GetFirstMap(WAD: String): String;
4272 begin
4273 Result := '';
4275 MapList := g_Map_GetMapsList(WAD);
4276 if MapList = nil then
4277 Exit;
4279 SortSArray(MapList);
4280 Result := MapList[Low(MapList)];
4282 if not g_Map_Exist(WAD + ':\' + Result) then
4283 Result := '';
4285 MapList := nil;
4286 end;
4288 function g_Game_GetNextMap(): String;
4289 var
4290 I: Integer;
4291 Map: string;
4292 begin
4293 Result := '';
4295 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4296 if MapList = nil then
4297 Exit;
4299 Map := g_ExtractFileName(gMapInfo.Map);
4301 SortSArray(MapList);
4302 MapIndex := -255;
4303 for I := Low(MapList) to High(MapList) do
4304 if Map = MapList[I] then
4305 begin
4306 MapIndex := I;
4307 Break;
4308 end;
4310 if MapIndex <> -255 then
4311 begin
4312 if MapIndex = High(MapList) then
4313 Result := MapList[Low(MapList)]
4314 else
4315 Result := MapList[MapIndex + 1];
4317 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4318 end;
4320 MapList := nil;
4321 end;
4323 procedure g_Game_NextLevel();
4324 begin
4325 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4326 gExit := EXIT_ENDLEVELCUSTOM
4327 else
4328 begin
4329 gExit := EXIT_ENDLEVELSINGLE;
4330 Exit;
4331 end;
4333 if gNextMap <> '' then Exit;
4334 gNextMap := g_Game_GetNextMap();
4335 end;
4337 function g_Game_IsTestMap(): Boolean;
4338 begin
4339 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4340 end;
4342 procedure g_Game_DeleteTestMap();
4343 var
4344 a: Integer;
4345 MapName: Char16;
4346 WadName: string;
4348 WAD: TWADFile;
4349 MapList: SArray;
4350 time: Integer;
4352 begin
4353 a := Pos('.wad:\', gMapToDelete);
4354 if a = 0 then
4355 Exit;
4357 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4358 WadName := Copy(gMapToDelete, 1, a + 3);
4359 Delete(gMapToDelete, 1, a + 5);
4360 gMapToDelete := UpperCase(gMapToDelete);
4361 MapName := '';
4362 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4365 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4366 if MapName <> TEST_MAP_NAME then
4367 Exit;
4369 if not gTempDelete then
4370 begin
4371 time := g_GetFileTime(WadName);
4372 WAD := TWADFile.Create();
4374 // ×èòàåì Wad-ôàéë:
4375 if not WAD.ReadFile(WadName) then
4376 begin // Íåò òàêîãî WAD-ôàéëà
4377 WAD.Free();
4378 Exit;
4379 end;
4381 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4382 WAD.CreateImage();
4383 MapList := WAD.GetResourcesList('');
4385 if MapList <> nil then
4386 for a := 0 to High(MapList) do
4387 if MapList[a] = MapName then
4388 begin
4389 // Óäàëÿåì è ñîõðàíÿåì:
4390 WAD.RemoveResource('', MapName);
4391 WAD.SaveTo(WadName);
4392 Break;
4393 end;
4395 WAD.Free();
4396 g_SetFileTime(WadName, time);
4397 end else
4399 if gTempDelete then DeleteFile(WadName);
4400 end;
4402 procedure GameCVars(P: SArray);
4403 var
4404 a, b: Integer;
4405 stat: TPlayerStatArray;
4406 cmd, s: string;
4407 config: TConfig;
4408 begin
4409 stat := nil;
4410 cmd := LowerCase(P[0]);
4411 if cmd = 'r_showfps' then
4412 begin
4413 if (Length(P) > 1) and
4414 ((P[1] = '1') or (P[1] = '0')) then
4415 gShowFPS := (P[1][1] = '1');
4417 if gShowFPS then
4418 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4419 else
4420 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4421 end
4422 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4423 begin
4424 with gGameSettings do
4425 begin
4426 if (Length(P) > 1) and
4427 ((P[1] = '1') or (P[1] = '0')) then
4428 begin
4429 if (P[1][1] = '1') then
4430 Options := Options or GAME_OPTION_TEAMDAMAGE
4431 else
4432 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4433 end;
4435 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4436 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4437 else
4438 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4440 if g_Game_IsNet then MH_SEND_GameSettings;
4441 end;
4442 end
4443 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4444 begin
4445 with gGameSettings do
4446 begin
4447 if (Length(P) > 1) and
4448 ((P[1] = '1') or (P[1] = '0')) then
4449 begin
4450 if (P[1][1] = '1') then
4451 Options := Options or GAME_OPTION_WEAPONSTAY
4452 else
4453 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4454 end;
4456 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4457 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4458 else
4459 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4461 if g_Game_IsNet then MH_SEND_GameSettings;
4462 end;
4463 end
4464 else if cmd = 'g_gamemode' then
4465 begin
4466 a := g_Game_TextToMode(P[1]);
4467 if a = GM_SINGLE then a := GM_COOP;
4468 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4469 begin
4470 gSwitchGameMode := a;
4471 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4472 (gState = STATE_INTERSINGLE) then
4473 gSwitchGameMode := GM_SINGLE;
4474 if not gGameOn then
4475 gGameSettings.GameMode := gSwitchGameMode;
4476 end;
4477 if gSwitchGameMode = gGameSettings.GameMode then
4478 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4479 [g_Game_ModeToText(gGameSettings.GameMode)]))
4480 else
4481 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4482 [g_Game_ModeToText(gGameSettings.GameMode),
4483 g_Game_ModeToText(gSwitchGameMode)]));
4484 end
4485 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4486 begin
4487 with gGameSettings do
4488 begin
4489 if (Length(P) > 1) and
4490 ((P[1] = '1') or (P[1] = '0')) then
4491 begin
4492 if (P[1][1] = '1') then
4493 Options := Options or GAME_OPTION_ALLOWEXIT
4494 else
4495 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4496 end;
4498 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4499 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4500 else
4501 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4502 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4504 if g_Game_IsNet then MH_SEND_GameSettings;
4505 end;
4506 end
4507 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4508 begin
4509 with gGameSettings do
4510 begin
4511 if (Length(P) > 1) and
4512 ((P[1] = '1') or (P[1] = '0')) then
4513 begin
4514 if (P[1][1] = '1') then
4515 Options := Options or GAME_OPTION_MONSTERS
4516 else
4517 Options := Options and (not GAME_OPTION_MONSTERS);
4518 end;
4520 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4521 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4522 else
4523 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4524 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4526 if g_Game_IsNet then MH_SEND_GameSettings;
4527 end;
4528 end
4529 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4530 begin
4531 with gGameSettings do
4532 begin
4533 if (Length(P) > 1) and
4534 ((P[1] = '1') or (P[1] = '0')) then
4535 begin
4536 if (P[1][1] = '1') then
4537 Options := Options or GAME_OPTION_BOTVSPLAYER
4538 else
4539 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4540 end;
4542 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4543 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4544 else
4545 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4547 if g_Game_IsNet then MH_SEND_GameSettings;
4548 end;
4549 end
4550 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4551 begin
4552 with gGameSettings do
4553 begin
4554 if (Length(P) > 1) and
4555 ((P[1] = '1') or (P[1] = '0')) then
4556 begin
4557 if (P[1][1] = '1') then
4558 Options := Options or GAME_OPTION_BOTVSMONSTER
4559 else
4560 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4561 end;
4563 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4564 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4565 else
4566 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4568 if g_Game_IsNet then MH_SEND_GameSettings;
4569 end;
4570 end
4571 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4572 begin
4573 if Length(P) > 1 then
4574 begin
4575 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4576 gGameSettings.WarmupTime := 30
4577 else
4578 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4579 end;
4581 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4582 [gGameSettings.WarmupTime]));
4583 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4584 end
4585 else if cmd = 'net_interp' then
4586 begin
4587 if (Length(P) > 1) then
4588 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4590 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4591 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4592 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4593 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4594 config.Free();
4595 end
4596 else if cmd = 'net_forceplayerupdate' then
4597 begin
4598 if (Length(P) > 1) and
4599 ((P[1] = '1') or (P[1] = '0')) then
4600 NetForcePlayerUpdate := (P[1][1] = '1');
4602 if NetForcePlayerUpdate then
4603 g_Console_Add('net_forceplayerupdate = 1')
4604 else
4605 g_Console_Add('net_forceplayerupdate = 0');
4606 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4607 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4608 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4609 config.Free();
4610 end
4611 else if cmd = 'net_predictself' then
4612 begin
4613 if (Length(P) > 1) and
4614 ((P[1] = '1') or (P[1] = '0')) then
4615 NetPredictSelf := (P[1][1] = '1');
4617 if NetPredictSelf then
4618 g_Console_Add('net_predictself = 1')
4619 else
4620 g_Console_Add('net_predictself = 0');
4621 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4622 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4623 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4624 config.Free();
4625 end
4626 else if cmd = 'sv_name' then
4627 begin
4628 if (Length(P) > 1) and (Length(P[1]) > 0) then
4629 begin
4630 NetServerName := P[1];
4631 if Length(NetServerName) > 64 then
4632 SetLength(NetServerName, 64);
4633 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4634 g_Net_Slist_Update;
4635 end;
4637 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4638 end
4639 else if cmd = 'sv_passwd' then
4640 begin
4641 if (Length(P) > 1) and (Length(P[1]) > 0) then
4642 begin
4643 NetPassword := P[1];
4644 if Length(NetPassword) > 24 then
4645 SetLength(NetPassword, 24);
4646 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4647 g_Net_Slist_Update;
4648 end;
4650 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4651 end
4652 else if cmd = 'sv_maxplrs' then
4653 begin
4654 if (Length(P) > 1) then
4655 begin
4656 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4657 if g_Game_IsServer and g_Game_IsNet then
4658 begin
4659 b := 0;
4660 for a := 0 to High(NetClients) do
4661 if NetClients[a].Used then
4662 begin
4663 Inc(b);
4664 if b > NetMaxClients then
4665 begin
4666 s := g_Player_Get(NetClients[a].Player).Name;
4667 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4668 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4669 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4670 end;
4671 end;
4672 if NetUseMaster then
4673 g_Net_Slist_Update;
4674 end;
4675 end;
4677 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4678 end
4679 else if cmd = 'sv_public' then
4680 begin
4681 if (Length(P) > 1) then
4682 begin
4683 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4684 if g_Game_IsServer and g_Game_IsNet then
4685 if NetUseMaster then
4686 begin
4687 if NetMPeer = nil then
4688 if not g_Net_Slist_Connect() then
4689 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4690 g_Net_Slist_Update();
4691 end
4692 else
4693 if NetMPeer <> nil then
4694 g_Net_Slist_Disconnect();
4695 end;
4697 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4698 end
4699 else if cmd = 'sv_intertime' then
4700 begin
4701 if (Length(P) > 1) then
4702 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4704 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4705 end
4706 else if cmd = 'p1_name' then
4707 begin
4708 if (Length(P) > 1) and gGameOn then
4709 begin
4710 if g_Game_IsClient then
4711 begin
4712 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4713 MC_SEND_PlayerSettings;
4714 end
4715 else
4716 if gPlayer1 <> nil then
4717 begin
4718 gPlayer1.Name := b_Text_Unformat(P[1]);
4719 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4720 end
4721 else
4722 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4723 end;
4724 end
4725 else if cmd = 'p2_name' then
4726 begin
4727 if (Length(P) > 1) and gGameOn then
4728 begin
4729 if g_Game_IsClient then
4730 begin
4731 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4732 MC_SEND_PlayerSettings;
4733 end
4734 else
4735 if gPlayer2 <> nil then
4736 begin
4737 gPlayer2.Name := b_Text_Unformat(P[1]);
4738 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4739 end
4740 else
4741 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4742 end;
4743 end
4744 else if cmd = 'p1_color' then
4745 begin
4746 if Length(P) > 3 then
4747 if g_Game_IsClient then
4748 begin
4749 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4750 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4751 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4752 MC_SEND_PlayerSettings;
4753 end
4754 else
4755 if gPlayer1 <> nil then
4756 begin
4757 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4758 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4759 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4760 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4761 end
4762 else
4763 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4764 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4765 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4766 end
4767 else if (cmd = 'p2_color') and not g_Game_IsNet then
4768 begin
4769 if Length(P) > 3 then
4770 if g_Game_IsClient then
4771 begin
4772 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4773 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4774 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4775 MC_SEND_PlayerSettings;
4776 end
4777 else
4778 if gPlayer2 <> nil then
4779 begin
4780 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4781 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4782 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4783 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4784 end
4785 else
4786 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4787 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4788 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4789 end
4790 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4791 begin
4792 if cmd = 'r_showtime' then
4793 begin
4794 if (Length(P) > 1) and
4795 ((P[1] = '1') or (P[1] = '0')) then
4796 gShowTime := (P[1][1] = '1');
4798 if gShowTime then
4799 g_Console_Add(_lc[I_MSG_TIME_ON])
4800 else
4801 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4802 end
4803 else if cmd = 'r_showscore' then
4804 begin
4805 if (Length(P) > 1) and
4806 ((P[1] = '1') or (P[1] = '0')) then
4807 gShowGoals := (P[1][1] = '1');
4809 if gShowGoals then
4810 g_Console_Add(_lc[I_MSG_SCORE_ON])
4811 else
4812 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4813 end
4814 else if cmd = 'r_showstat' then
4815 begin
4816 if (Length(P) > 1) and
4817 ((P[1] = '1') or (P[1] = '0')) then
4818 gShowStat := (P[1][1] = '1');
4820 if gShowStat then
4821 g_Console_Add(_lc[I_MSG_STATS_ON])
4822 else
4823 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4824 end
4825 else if cmd = 'r_showkillmsg' then
4826 begin
4827 if (Length(P) > 1) and
4828 ((P[1] = '1') or (P[1] = '0')) then
4829 gShowKillMsg := (P[1][1] = '1');
4831 if gShowKillMsg then
4832 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4833 else
4834 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4835 end
4836 else if cmd = 'r_showlives' then
4837 begin
4838 if (Length(P) > 1) and
4839 ((P[1] = '1') or (P[1] = '0')) then
4840 gShowLives := (P[1][1] = '1');
4842 if gShowLives then
4843 g_Console_Add(_lc[I_MSG_LIVES_ON])
4844 else
4845 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4846 end
4847 else if cmd = 'r_showspect' then
4848 begin
4849 if (Length(P) > 1) and
4850 ((P[1] = '1') or (P[1] = '0')) then
4851 gSpectHUD := (P[1][1] = '1');
4853 if gSpectHUD then
4854 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4855 else
4856 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4857 end
4858 else if cmd = 'r_showping' then
4859 begin
4860 if (Length(P) > 1) and
4861 ((P[1] = '1') or (P[1] = '0')) then
4862 gShowPing := (P[1][1] = '1');
4864 if gShowPing then
4865 g_Console_Add(_lc[I_MSG_PING_ON])
4866 else
4867 g_Console_Add(_lc[I_MSG_PING_OFF]);
4868 end
4869 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4870 begin
4871 if Length(P) > 1 then
4872 begin
4873 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4874 gGameSettings.GoalLimit := 0
4875 else
4876 begin
4877 b := 0;
4879 if gGameSettings.GameMode = GM_DM then
4880 begin // DM
4881 stat := g_Player_GetStats();
4882 if stat <> nil then
4883 for a := 0 to High(stat) do
4884 if stat[a].Frags > b then
4885 b := stat[a].Frags;
4886 end
4887 else // TDM/CTF
4888 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4890 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4891 end;
4893 if g_Game_IsNet then MH_SEND_GameSettings;
4894 end;
4896 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4897 end
4898 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4899 begin
4900 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4901 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4903 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4904 [gGameSettings.TimeLimit div 3600,
4905 (gGameSettings.TimeLimit div 60) mod 60,
4906 gGameSettings.TimeLimit mod 60]));
4907 if g_Game_IsNet then MH_SEND_GameSettings;
4908 end
4909 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4910 begin
4911 if Length(P) > 1 then
4912 begin
4913 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4914 gGameSettings.MaxLives := 0
4915 else
4916 begin
4917 b := 0;
4918 stat := g_Player_GetStats();
4919 if stat <> nil then
4920 for a := 0 to High(stat) do
4921 if stat[a].Lives > b then
4922 b := stat[a].Lives;
4923 gGameSettings.MaxLives :=
4924 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4925 end;
4926 end;
4928 g_Console_Add(Format(_lc[I_MSG_LIVES],
4929 [gGameSettings.MaxLives]));
4930 if g_Game_IsNet then MH_SEND_GameSettings;
4931 end;
4932 end;
4933 end;
4935 // profiler console commands
4936 procedure ProfilerCommands (P: SArray);
4937 var
4938 cmd: string;
4940 function getBool (idx: Integer): Integer;
4941 begin
4942 if (idx < 0) or (idx > High(P)) then begin result := -1; exit; end;
4943 result := 0;
4944 if (P[idx] = '1') or (P[idx] = 'on') or (P[idx] = 'true') or (P[idx] = 'tan') then result := 1;
4945 end;
4947 procedure binaryFlag (var flag: Boolean; msg: string);
4948 begin
4949 if (Length(p) > 2) then
4950 begin
4951 g_Console_Add('too many arguments to '''+P[0]+'''');
4952 end
4953 else
4954 begin
4955 case getBool(1) of
4956 -1: begin end;
4957 0: flag := false;
4958 1: flag := true;
4959 end;
4960 if flag then g_Console_Add(msg+': tan') else g_Console_Add(msg+': ona');
4961 end;
4962 end;
4964 begin
4965 //if not gDebugMode then exit;
4966 cmd := LowerCase(P[0]);
4968 if (cmd = 'pf_draw_frame') then begin binaryFlag(g_profile_frame_draw, 'render profiles'); exit; end;
4969 if (cmd = 'pf_update_frame') then begin binaryFlag(g_profile_frame_update, 'update profiles (not yet)'); exit; end;
4970 if (cmd = 'pf_coldet') then begin binaryFlag(g_profile_collision, 'coldet profiles'); exit; end;
4971 if (cmd = 'pf_los') then begin binaryFlag(g_profile_los, 'monster LOS profiles'); exit; end;
4972 if (cmd = 'r_sq_draw') then begin binaryFlag(gdbg_map_use_accel_render, 'accelerated rendering'); exit; end;
4973 if (cmd = 'cd_sq_enabled') then begin binaryFlag(gdbg_map_use_accel_coldet, 'accelerated map coldet'); exit; end;
4974 if (cmd = 'mon_sq_enabled') then begin binaryFlag(gmon_debug_use_sqaccel, 'accelerated monster coldet'); exit; end;
4975 if (cmd = 'wtrace_sq_enabled') then begin binaryFlag(gwep_debug_fast_trace, 'accelerated weapon hitscan'); exit; end;
4976 if (cmd = 'pr_enabled') then begin binaryFlag(gpart_dbg_enabled, 'particles'); exit; end;
4977 if (cmd = 'pr_phys_enabled') then begin binaryFlag(gpart_dbg_phys_enabled, 'particle physics'); exit; end;
4978 if (cmd = 'los_enabled') then begin binaryFlag(gmon_dbg_los_enabled, 'LOS calculations'); exit; end;
4979 end;
4982 procedure DebugCommands(P: SArray);
4983 var
4984 a, b: Integer;
4985 cmd: string;
4986 //pt: TPoint;
4987 mon: TMonster;
4988 begin
4989 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
4990 if gDebugMode then
4991 begin
4992 cmd := LowerCase(P[0]);
4993 if cmd = 'd_window' then
4994 begin
4995 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
4996 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
4997 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
4998 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
4999 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5000 end
5001 else if cmd = 'd_sounds' then
5002 begin
5003 if (Length(P) > 1) and
5004 ((P[1] = '1') or (P[1] = '0')) then
5005 g_Debug_Sounds := (P[1][1] = '1');
5007 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5008 end
5009 else if cmd = 'd_frames' then
5010 begin
5011 if (Length(P) > 1) and
5012 ((P[1] = '1') or (P[1] = '0')) then
5013 g_Debug_Frames := (P[1][1] = '1');
5015 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5016 end
5017 else if cmd = 'd_winmsg' then
5018 begin
5019 if (Length(P) > 1) and
5020 ((P[1] = '1') or (P[1] = '0')) then
5021 g_Debug_WinMsgs := (P[1][1] = '1');
5023 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5024 end
5025 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5026 begin
5027 if (Length(P) > 1) and
5028 ((P[1] = '1') or (P[1] = '0')) then
5029 g_Debug_MonsterOff := (P[1][1] = '1');
5031 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5032 end
5033 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5034 begin
5035 if Length(P) > 1 then
5036 case P[1][1] of
5037 '0': g_debug_BotAIOff := 0;
5038 '1': g_debug_BotAIOff := 1;
5039 '2': g_debug_BotAIOff := 2;
5040 '3': g_debug_BotAIOff := 3;
5041 end;
5043 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5044 end
5045 else if cmd = 'd_monster' then
5046 begin
5047 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
5048 if Length(P) < 2 then
5049 begin
5050 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5051 g_Console_Add('ID | Name');
5052 for b := MONSTER_DEMON to MONSTER_MAN do
5053 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
5054 end else
5055 begin
5056 a := StrToIntDef(P[1], 0);
5057 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5058 a := g_Monsters_GetIDByName(P[1]);
5060 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5061 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5062 else
5063 begin
5064 with gPlayer1.Obj do
5065 begin
5066 mon := g_Monsters_Create(a,
5067 X + Rect.X + (Rect.Width div 2),
5068 Y + Rect.Y + Rect.Height,
5069 gPlayer1.Direction, True);
5070 end;
5071 if (Length(P) > 2) and (mon <> nil) then
5072 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5073 end;
5074 end;
5075 end
5076 else if (cmd = 'd_health') then
5077 begin
5078 if (Length(P) > 1) and
5079 ((P[1] = '1') or (P[1] = '0')) then
5080 g_debug_HealthBar := (P[1][1] = '1');
5082 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5083 end
5084 else if (cmd = 'd_player') then
5085 begin
5086 if (Length(P) > 1) and
5087 ((P[1] = '1') or (P[1] = '0')) then
5088 g_debug_Player := (P[1][1] = '1');
5090 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5091 end
5092 else if (cmd = 'd_joy') then
5093 begin
5094 for a := 1 to 8 do
5095 g_Console_Add(e_JoystickStateToString(a));
5096 end;
5097 end
5098 else
5099 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5100 end;
5103 procedure GameCheats(P: SArray);
5104 var
5105 cmd: string;
5106 f, a: Integer;
5107 plr: TPlayer;
5108 begin
5109 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5110 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5111 begin
5112 g_Console_Add('not available');
5113 exit;
5114 end;
5115 plr := gPlayer1;
5116 if plr = nil then
5117 begin
5118 g_Console_Add('where is the player?!');
5119 exit;
5120 end;
5121 cmd := LowerCase(P[0]);
5122 // god
5123 if cmd = 'god' then
5124 begin
5125 plr.GodMode := not plr.GodMode;
5126 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5127 exit;
5128 end;
5129 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5130 if cmd = 'give' then
5131 begin
5132 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5133 for f := 1 to High(P) do
5134 begin
5135 cmd := LowerCase(P[f]);
5136 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5137 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5138 if cmd = 'exit' then
5139 begin
5140 if gTriggers <> nil then
5141 begin
5142 for a := 0 to High(gTriggers) do
5143 begin
5144 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5145 begin
5146 g_Console_Add('player left the map');
5147 gExitByTrigger := True;
5148 g_Game_ExitLevel(gTriggers[a].Data.MapName);
5149 break;
5150 end;
5151 end;
5152 end;
5153 continue;
5154 end;
5156 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5157 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5158 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5159 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5160 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5162 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5163 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5165 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5166 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;
5168 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5169 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5171 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5172 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5174 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5175 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5177 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5178 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5179 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5181 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5182 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5183 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5184 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;
5185 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5186 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5188 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;
5189 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;
5190 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;
5191 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;
5192 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;
5193 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;
5195 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5196 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;
5198 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;
5199 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;
5201 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5203 if cmd = 'ammo' then
5204 begin
5205 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5206 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5207 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5208 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5209 plr.GiveItem(ITEM_AMMO_FUELCAN);
5210 g_Console_Add('player got some ammo');
5211 continue;
5212 end;
5214 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5215 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5217 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5218 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5220 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5221 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5223 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5224 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5226 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5228 if cmd = 'weapons' then
5229 begin
5230 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5231 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5232 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5233 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5234 plr.GiveItem(ITEM_WEAPON_PLASMA);
5235 plr.GiveItem(ITEM_WEAPON_BFG);
5236 g_Console_Add('player got weapons');
5237 continue;
5238 end;
5240 if cmd = 'keys' then
5241 begin
5242 plr.GiveItem(ITEM_KEY_RED);
5243 plr.GiveItem(ITEM_KEY_GREEN);
5244 plr.GiveItem(ITEM_KEY_BLUE);
5245 g_Console_Add('player got all keys');
5246 continue;
5247 end;
5249 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5250 end;
5251 exit;
5252 end;
5253 // open
5254 if cmd = 'open' then
5255 begin
5256 g_Console_Add('player activated sesame');
5257 g_Triggers_OpenAll();
5258 exit;
5259 end;
5260 // fly
5261 if cmd = 'fly' then
5262 begin
5263 gFly := not gFly;
5264 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5265 exit;
5266 end;
5267 // noclip
5268 if cmd = 'noclip' then
5269 begin
5270 plr.SwitchNoClip;
5271 g_Console_Add('wall hardeness adjusted');
5272 exit;
5273 end;
5274 // notarget
5275 if cmd = 'notarget' then
5276 begin
5277 plr.NoTarget := not plr.NoTarget;
5278 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5279 exit;
5280 end;
5281 // noreload
5282 if cmd = 'noreload' then
5283 begin
5284 plr.NoReload := not plr.NoReload;
5285 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5286 exit;
5287 end;
5288 // speedy
5289 if cmd = 'speedy' then
5290 begin
5291 MAX_RUNVEL := 32-MAX_RUNVEL;
5292 g_Console_Add('speed adjusted');
5293 exit;
5294 end;
5295 // jumpy
5296 if cmd = 'jumpy' then
5297 begin
5298 VEL_JUMP := 30-VEL_JUMP;
5299 g_Console_Add('jump height adjusted');
5300 exit;
5301 end;
5302 // automap
5303 if cmd = 'automap' then
5304 begin
5305 gShowMap := not gShowMap;
5306 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5307 exit;
5308 end;
5309 // aimline
5310 if cmd = 'aimline' then
5311 begin
5312 gAimLine := not gAimLine;
5313 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5314 exit;
5315 end;
5316 end;
5318 procedure GameCommands(P: SArray);
5319 var
5320 a, b: Integer;
5321 s, pw: String;
5322 chstr: string;
5323 cmd: string;
5324 pl: pTNetClient = nil;
5325 plr: TPlayer;
5326 prt: Word;
5327 nm: Boolean;
5328 listen: LongWord;
5329 begin
5330 // Îáùèå êîìàíäû:
5331 cmd := LowerCase(P[0]);
5332 chstr := '';
5333 if (cmd = 'quit') or
5334 (cmd = 'exit') then
5335 begin
5336 g_Game_Free();
5337 g_Game_Quit();
5338 Exit;
5339 end
5340 else if cmd = 'pause' then
5341 begin
5342 if (g_ActiveWindow = nil) then
5343 g_Game_Pause(not gPause);
5344 end
5345 else if cmd = 'endgame' then
5346 gExit := EXIT_SIMPLE
5347 else if cmd = 'restart' then
5348 begin
5349 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5350 begin
5351 if g_Game_IsClient then
5352 begin
5353 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5354 Exit;
5355 end;
5356 g_Game_Restart();
5357 end else
5358 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5359 end
5360 else if cmd = 'kick' then
5361 begin
5362 if g_Game_IsServer then
5363 begin
5364 if Length(P) < 2 then
5365 begin
5366 g_Console_Add('kick <name>');
5367 Exit;
5368 end;
5369 if P[1] = '' then
5370 begin
5371 g_Console_Add('kick <name>');
5372 Exit;
5373 end;
5375 if g_Game_IsNet then
5376 pl := g_Net_Client_ByName(P[1]);
5377 if (pl <> nil) then
5378 begin
5379 s := g_Net_ClientName_ByID(pl^.ID);
5380 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5381 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5382 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5383 if NetUseMaster then
5384 g_Net_Slist_Update;
5385 end else if gPlayers <> nil then
5386 for a := Low(gPlayers) to High(gPlayers) do
5387 if gPlayers[a] <> nil then
5388 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5389 begin
5390 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5391 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5392 continue;
5393 gPlayers[a].Lives := 0;
5394 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5395 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5396 g_Player_Remove(gPlayers[a].UID);
5397 if NetUseMaster then
5398 g_Net_Slist_Update;
5399 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5400 g_Bot_MixNames();
5401 end;
5402 end else
5403 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5404 end
5405 else if cmd = 'kick_id' then
5406 begin
5407 if g_Game_IsServer and g_Game_IsNet then
5408 begin
5409 if Length(P) < 2 then
5410 begin
5411 g_Console_Add('kick_id <client ID>');
5412 Exit;
5413 end;
5414 if P[1] = '' then
5415 begin
5416 g_Console_Add('kick_id <client ID>');
5417 Exit;
5418 end;
5420 a := StrToIntDef(P[1], 0);
5421 if (NetClients <> nil) and (a <= High(NetClients)) then
5422 begin
5423 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5424 begin
5425 s := g_Net_ClientName_ByID(NetClients[a].ID);
5426 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5427 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5428 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5429 if NetUseMaster then
5430 g_Net_Slist_Update;
5431 end;
5432 end;
5433 end else
5434 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5435 end
5436 else if cmd = 'ban' then
5437 begin
5438 if g_Game_IsServer and g_Game_IsNet then
5439 begin
5440 if Length(P) < 2 then
5441 begin
5442 g_Console_Add('ban <name>');
5443 Exit;
5444 end;
5445 if P[1] = '' then
5446 begin
5447 g_Console_Add('ban <name>');
5448 Exit;
5449 end;
5451 pl := g_Net_Client_ByName(P[1]);
5452 if (pl <> nil) then
5453 begin
5454 s := g_Net_ClientName_ByID(pl^.ID);
5455 g_Net_BanHost(pl^.Peer^.address.host, False);
5456 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5457 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5458 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5459 if NetUseMaster then
5460 g_Net_Slist_Update;
5461 end else
5462 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5463 end else
5464 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5465 end
5466 else if cmd = 'ban_id' then
5467 begin
5468 if g_Game_IsServer and g_Game_IsNet then
5469 begin
5470 if Length(P) < 2 then
5471 begin
5472 g_Console_Add('ban_id <client ID>');
5473 Exit;
5474 end;
5475 if P[1] = '' then
5476 begin
5477 g_Console_Add('ban_id <client ID>');
5478 Exit;
5479 end;
5481 a := StrToIntDef(P[1], 0);
5482 if (NetClients <> nil) and (a <= High(NetClients)) then
5483 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5484 begin
5485 s := g_Net_ClientName_ByID(NetClients[a].ID);
5486 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5487 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5488 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5489 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5490 if NetUseMaster then
5491 g_Net_Slist_Update;
5492 end;
5493 end else
5494 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5495 end
5496 else if cmd = 'permban' then
5497 begin
5498 if g_Game_IsServer and g_Game_IsNet then
5499 begin
5500 if Length(P) < 2 then
5501 begin
5502 g_Console_Add('permban <name>');
5503 Exit;
5504 end;
5505 if P[1] = '' then
5506 begin
5507 g_Console_Add('permban <name>');
5508 Exit;
5509 end;
5511 pl := g_Net_Client_ByName(P[1]);
5512 if (pl <> nil) then
5513 begin
5514 s := g_Net_ClientName_ByID(pl^.ID);
5515 g_Net_BanHost(pl^.Peer^.address.host);
5516 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5517 g_Net_SaveBanList();
5518 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5519 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5520 if NetUseMaster then
5521 g_Net_Slist_Update;
5522 end else
5523 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5524 end else
5525 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5526 end
5527 else if cmd = 'permban_id' then
5528 begin
5529 if g_Game_IsServer and g_Game_IsNet then
5530 begin
5531 if Length(P) < 2 then
5532 begin
5533 g_Console_Add('permban_id <client ID>');
5534 Exit;
5535 end;
5536 if P[1] = '' then
5537 begin
5538 g_Console_Add('permban_id <client ID>');
5539 Exit;
5540 end;
5542 a := StrToIntDef(P[1], 0);
5543 if (NetClients <> nil) and (a <= High(NetClients)) then
5544 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5545 begin
5546 s := g_Net_ClientName_ByID(NetClients[a].ID);
5547 g_Net_BanHost(NetClients[a].Peer^.address.host);
5548 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5549 g_Net_SaveBanList();
5550 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5551 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5552 if NetUseMaster then
5553 g_Net_Slist_Update;
5554 end;
5555 end else
5556 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5557 end
5558 else if cmd = 'unban' then
5559 begin
5560 if g_Game_IsServer and g_Game_IsNet then
5561 begin
5562 if Length(P) < 2 then
5563 begin
5564 g_Console_Add('unban <IP Address>');
5565 Exit;
5566 end;
5567 if P[1] = '' then
5568 begin
5569 g_Console_Add('unban <IP Address>');
5570 Exit;
5571 end;
5573 if g_Net_UnbanHost(P[1]) then
5574 begin
5575 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5576 g_Net_SaveBanList();
5577 end else
5578 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5579 end else
5580 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5581 end
5582 else if cmd = 'clientlist' then
5583 begin
5584 if g_Game_IsServer and g_Game_IsNet then
5585 begin
5586 b := 0;
5587 if NetClients <> nil then
5588 for a := Low(NetClients) to High(NetClients) do
5589 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5590 begin
5591 plr := g_Player_Get(NetClients[a].Player);
5592 if plr = nil then continue;
5593 Inc(b);
5594 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5595 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5596 end;
5597 if b = 0 then
5598 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5599 end else
5600 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5601 end
5602 else if cmd = 'connect' then
5603 begin
5604 if (NetMode = NET_NONE) then
5605 begin
5606 if Length(P) < 2 then
5607 begin
5608 g_Console_Add('connect <IP> [port] [password]');
5609 Exit;
5610 end;
5611 if P[1] = '' then
5612 begin
5613 g_Console_Add('connect <IP> [port] [password]');
5614 Exit;
5615 end;
5617 if Length(P) > 2 then
5618 prt := StrToIntDef(P[2], 25666)
5619 else
5620 prt := 25666;
5622 if Length(P) > 3 then
5623 pw := P[3]
5624 else
5625 pw := '';
5627 g_Game_StartClient(P[1], prt, pw);
5628 end;
5629 end
5630 else if cmd = 'disconnect' then
5631 begin
5632 if (NetMode = NET_CLIENT) then
5633 g_Net_Disconnect();
5634 end
5635 else if cmd = 'reconnect' then
5636 begin
5637 if (NetMode = NET_SERVER) then
5638 Exit;
5640 if (NetMode = NET_CLIENT) then
5641 begin
5642 g_Net_Disconnect();
5643 gExit := EXIT_SIMPLE;
5644 EndGame;
5645 end;
5647 //TODO: Use last successful password to reconnect, instead of ''
5648 g_Game_StartClient(NetClientIP, NetClientPort, '');
5649 end
5650 else if (cmd = 'addbot') or
5651 (cmd = 'bot_add') then
5652 begin
5653 if Length(P) > 1 then
5654 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5655 else
5656 g_Bot_Add(TEAM_NONE, 2);
5657 end
5658 else if cmd = 'bot_addlist' then
5659 begin
5660 if Length(P) > 1 then
5661 if Length(P) = 2 then
5662 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5663 else
5664 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5665 end
5666 else if cmd = 'bot_removeall' then
5667 g_Bot_RemoveAll()
5668 else if cmd = 'chat' then
5669 begin
5670 if g_Game_IsNet then
5671 begin
5672 if Length(P) > 1 then
5673 begin
5674 for a := 1 to High(P) do
5675 chstr := chstr + P[a] + ' ';
5677 if Length(chstr) > 200 then SetLength(chstr, 200);
5679 if Length(chstr) < 1 then
5680 begin
5681 g_Console_Add('chat <text>');
5682 Exit;
5683 end;
5685 chstr := b_Text_Format(chstr);
5686 if g_Game_IsClient then
5687 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5688 else
5689 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5690 end
5691 else
5692 g_Console_Add('chat <text>');
5693 end else
5694 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5695 end
5696 else if cmd = 'teamchat' then
5697 begin
5698 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5699 begin
5700 if Length(P) > 1 then
5701 begin
5702 for a := 1 to High(P) do
5703 chstr := chstr + P[a] + ' ';
5705 if Length(chstr) > 200 then SetLength(chstr, 200);
5707 if Length(chstr) < 1 then
5708 begin
5709 g_Console_Add('teamchat <text>');
5710 Exit;
5711 end;
5713 chstr := b_Text_Format(chstr);
5714 if g_Game_IsClient then
5715 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5716 else
5717 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5718 gPlayer1Settings.Team);
5719 end
5720 else
5721 g_Console_Add('teamchat <text>');
5722 end else
5723 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5724 end
5725 else if cmd = 'game' then
5726 begin
5727 if gGameSettings.GameType <> GT_NONE then
5728 begin
5729 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5730 Exit;
5731 end;
5732 if Length(P) = 1 then
5733 begin
5734 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5735 Exit;
5736 end;
5737 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5738 P[1] := addWadExtension(P[1]);
5739 if FileExists(MapsDir + P[1]) then
5740 begin
5741 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5742 if Length(P) < 3 then
5743 begin
5744 SetLength(P, 3);
5745 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5746 end;
5748 s := P[1] + ':\' + UpperCase(P[2]);
5750 if g_Map_Exist(MapsDir + s) then
5751 begin
5752 // Çàïóñêàåì ñâîþ èãðó
5753 g_Game_Free();
5754 with gGameSettings do
5755 begin
5756 GameMode := g_Game_TextToMode(gcGameMode);
5757 if gSwitchGameMode <> GM_NONE then
5758 GameMode := gSwitchGameMode;
5759 if GameMode = GM_NONE then GameMode := GM_DM;
5760 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5761 b := 1;
5762 if Length(P) >= 4 then
5763 b := StrToIntDef(P[3], 1);
5764 g_Game_StartCustom(s, GameMode, TimeLimit,
5765 GoalLimit, MaxLives, Options, b);
5766 end;
5767 end
5768 else
5769 if P[2] = '' then
5770 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5771 else
5772 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5773 end else
5774 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5775 end
5776 else if cmd = 'host' then
5777 begin
5778 if gGameSettings.GameType <> GT_NONE then
5779 begin
5780 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5781 Exit;
5782 end;
5783 if Length(P) < 4 then
5784 begin
5785 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5786 Exit;
5787 end;
5788 if not StrToIp(P[1], listen) then
5789 Exit;
5790 prt := StrToIntDef(P[2], 25666);
5792 P[3] := addWadExtension(P[3]);
5793 if FileExists(MapsDir + P[3]) then
5794 begin
5795 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5796 if Length(P) < 5 then
5797 begin
5798 SetLength(P, 5);
5799 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5800 end;
5802 s := P[3] + ':\' + UpperCase(P[4]);
5804 if g_Map_Exist(MapsDir + s) then
5805 begin
5806 // Çàïóñêàåì ñâîþ èãðó
5807 g_Game_Free();
5808 with gGameSettings do
5809 begin
5810 GameMode := g_Game_TextToMode(gcGameMode);
5811 if gSwitchGameMode <> GM_NONE then
5812 GameMode := gSwitchGameMode;
5813 if GameMode = GM_NONE then GameMode := GM_DM;
5814 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5815 b := 0;
5816 if Length(P) >= 6 then
5817 b := StrToIntDef(P[5], 0);
5818 g_Game_StartServer(s, GameMode, TimeLimit,
5819 GoalLimit, MaxLives, Options, b, listen, prt);
5820 end;
5821 end
5822 else
5823 if P[4] = '' then
5824 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5825 else
5826 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5827 end else
5828 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5829 end
5830 else if cmd = 'map' then
5831 begin
5832 if Length(P) = 1 then
5833 begin
5834 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5835 begin
5836 g_Console_Add(cmd + ' <MAP>');
5837 g_Console_Add(cmd + ' <WAD> [MAP]');
5838 end else
5839 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5840 end else
5841 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5842 begin
5843 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5844 if Length(P) < 3 then
5845 begin
5846 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5847 s := UpperCase(P[1]);
5848 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5849 begin // Êàðòà íàøëàñü
5850 gExitByTrigger := False;
5851 if gGameOn then
5852 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5853 gNextMap := s;
5854 gExit := EXIT_ENDLEVELCUSTOM;
5855 end
5856 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5857 g_Game_ChangeMap(s);
5858 end else
5859 begin
5860 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5861 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5862 P[1] := addWadExtension(P[1]);
5863 if FileExists(MapsDir + P[1]) then
5864 begin
5865 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5866 SetLength(P, 3);
5867 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5869 s := P[1] + ':\' + P[2];
5871 if g_Map_Exist(MapsDir + s) then
5872 begin
5873 gExitByTrigger := False;
5874 if gGameOn then
5875 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5876 gNextMap := s;
5877 gExit := EXIT_ENDLEVELCUSTOM;
5878 end
5879 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5880 g_Game_ChangeMap(s);
5881 end else
5882 if P[2] = '' then
5883 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5884 else
5885 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5886 end else
5887 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5888 end;
5889 end else
5890 begin
5891 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5892 P[1] := addWadExtension(P[1]);
5893 if FileExists(MapsDir + P[1]) then
5894 begin
5895 // Íàøëè WAD ôàéë
5896 P[2] := UpperCase(P[2]);
5897 s := P[1] + ':\' + P[2];
5899 if g_Map_Exist(MapsDir + s) then
5900 begin // Íàøëè êàðòó
5901 gExitByTrigger := False;
5902 if gGameOn then
5903 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5904 gNextMap := s;
5905 gExit := EXIT_ENDLEVELCUSTOM;
5906 end
5907 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5908 g_Game_ChangeMap(s);
5909 end else
5910 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5911 end else
5912 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5913 end;
5914 end else
5915 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5916 end
5917 else if cmd = 'nextmap' then
5918 begin
5919 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5920 g_Console_Add(_lc[I_MSG_NOT_GAME])
5921 else begin
5922 nm := True;
5923 if Length(P) = 1 then
5924 begin
5925 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5926 begin
5927 g_Console_Add(cmd + ' <MAP>');
5928 g_Console_Add(cmd + ' <WAD> [MAP]');
5929 end else begin
5930 nm := False;
5931 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5932 end;
5933 end else
5934 begin
5935 nm := False;
5936 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5937 begin
5938 if Length(P) < 3 then
5939 begin
5940 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5941 s := UpperCase(P[1]);
5942 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5943 begin // Êàðòà íàøëàñü
5944 gExitByTrigger := False;
5945 gNextMap := s;
5946 nm := True;
5947 end else
5948 begin
5949 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5950 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5951 P[1] := addWadExtension(P[1]);
5952 if FileExists(MapsDir + P[1]) then
5953 begin
5954 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5955 SetLength(P, 3);
5956 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5958 s := P[1] + ':\' + P[2];
5960 if g_Map_Exist(MapsDir + s) then
5961 begin // Óñòàíàâëèâàåì êàðòó
5962 gExitByTrigger := False;
5963 gNextMap := s;
5964 nm := True;
5965 end else
5966 if P[2] = '' then
5967 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5968 else
5969 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5970 end else
5971 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5972 end;
5973 end else
5974 begin
5975 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5976 P[1] := addWadExtension(P[1]);
5977 if FileExists(MapsDir + P[1]) then
5978 begin
5979 // Íàøëè WAD ôàéë
5980 P[2] := UpperCase(P[2]);
5981 s := P[1] + ':\' + P[2];
5983 if g_Map_Exist(MapsDir + s) then
5984 begin // Íàøëè êàðòó
5985 gExitByTrigger := False;
5986 gNextMap := s;
5987 nm := True;
5988 end else
5989 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5990 end else
5991 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5992 end;
5993 end else
5994 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5995 end;
5996 if nm then
5997 if gNextMap = '' then
5998 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
5999 else
6000 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6001 end;
6002 end
6003 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6004 begin
6005 if not gGameOn then
6006 g_Console_Add(_lc[I_MSG_NOT_GAME])
6007 else
6008 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6009 begin
6010 gExitByTrigger := False;
6011 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6012 if (gNextMap = '') and (gTriggers <> nil) then
6013 for a := 0 to High(gTriggers) do
6014 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6015 begin
6016 gExitByTrigger := True;
6017 gNextMap := gTriggers[a].Data.MapName;
6018 Break;
6019 end;
6020 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6021 if gNextMap = '' then
6022 gNextMap := g_Game_GetNextMap();
6023 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6024 if Pos(':\', gNextMap) = 0 then
6025 s := gGameSettings.WAD + ':\' + gNextMap
6026 else
6027 s := gNextMap;
6028 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6029 if g_Map_Exist(MapsDir + s) then
6030 gExit := EXIT_ENDLEVELCUSTOM
6031 else
6032 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6033 end else
6034 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6035 end
6036 else if (cmd = 'event') then
6037 begin
6038 if (Length(P) <= 1) then
6039 begin
6040 for a := 0 to High(gEvents) do
6041 if gEvents[a].Command = '' then
6042 g_Console_Add(gEvents[a].Name + ' <none>')
6043 else
6044 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6045 Exit;
6046 end;
6047 if (Length(P) = 2) then
6048 begin
6049 for a := 0 to High(gEvents) do
6050 if gEvents[a].Name = P[1] then
6051 if gEvents[a].Command = '' then
6052 g_Console_Add(gEvents[a].Name + ' <none>')
6053 else
6054 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6055 Exit;
6056 end;
6057 for a := 0 to High(gEvents) do
6058 if gEvents[a].Name = P[1] then
6059 begin
6060 gEvents[a].Command := '';
6061 for b := 2 to High(P) do
6062 if Pos(' ', P[b]) = 0 then
6063 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6064 else
6065 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6066 gEvents[a].Command := Trim(gEvents[a].Command);
6067 Exit;
6068 end;
6069 end
6070 // Êîìàíäû Ñâîåé èãðû:
6071 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6072 begin
6073 if cmd = 'bot_addred' then
6074 begin
6075 if Length(P) > 1 then
6076 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6077 else
6078 g_Bot_Add(TEAM_RED, 2);
6079 end
6080 else if cmd = 'bot_addblue' then
6081 begin
6082 if Length(P) > 1 then
6083 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6084 else
6085 g_Bot_Add(TEAM_BLUE, 2);
6086 end
6087 else if cmd = 'suicide' then
6088 begin
6089 if gGameOn then
6090 begin
6091 if g_Game_IsClient then
6092 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6093 else
6094 begin
6095 if gPlayer1 <> nil then
6096 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6097 if gPlayer2 <> nil then
6098 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6099 end;
6100 end;
6101 end
6102 else if cmd = 'spectate' then
6103 begin
6104 if not gGameOn then
6105 Exit;
6106 g_Game_Spectate();
6107 end
6108 else if cmd = 'say' then
6109 begin
6110 if g_Game_IsServer and g_Game_IsNet then
6111 begin
6112 if Length(P) > 1 then
6113 begin
6114 chstr := '';
6115 for a := 1 to High(P) do
6116 chstr := chstr + P[a] + ' ';
6118 if Length(chstr) > 200 then SetLength(chstr, 200);
6120 if Length(chstr) < 1 then
6121 begin
6122 g_Console_Add('say <text>');
6123 Exit;
6124 end;
6126 chstr := b_Text_Format(chstr);
6127 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6128 end
6129 else g_Console_Add('say <text>');
6130 end else
6131 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6132 end
6133 else if cmd = 'tell' then
6134 begin
6135 if g_Game_IsServer and g_Game_IsNet then
6136 begin
6137 if (Length(P) > 2) and (P[1] <> '') then
6138 begin
6139 chstr := '';
6140 for a := 2 to High(P) do
6141 chstr := chstr + P[a] + ' ';
6143 if Length(chstr) > 200 then SetLength(chstr, 200);
6145 if Length(chstr) < 1 then
6146 begin
6147 g_Console_Add('tell <playername> <text>');
6148 Exit;
6149 end;
6151 pl := g_Net_Client_ByName(P[1]);
6152 if pl <> nil then
6153 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6154 else
6155 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6156 end
6157 else g_Console_Add('tell <playername> <text>');
6158 end else
6159 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6160 end
6161 else if (cmd = 'overtime') and not g_Game_IsClient then
6162 begin
6163 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6164 Exit;
6165 // Äîïîëíèòåëüíîå âðåìÿ:
6166 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6168 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6169 [gGameSettings.TimeLimit div 3600,
6170 (gGameSettings.TimeLimit div 60) mod 60,
6171 gGameSettings.TimeLimit mod 60]));
6172 if g_Game_IsNet then MH_SEND_GameSettings;
6173 end
6174 else if (cmd = 'rcon_password') and g_Game_IsClient then
6175 begin
6176 if (Length(P) <= 1) then
6177 g_Console_Add('rcon_password <password>')
6178 else
6179 MC_SEND_RCONPassword(P[1]);
6180 end
6181 else if cmd = 'rcon' then
6182 begin
6183 if g_Game_IsClient then
6184 begin
6185 if Length(P) > 1 then
6186 begin
6187 chstr := '';
6188 for a := 1 to High(P) do
6189 chstr := chstr + P[a] + ' ';
6191 if Length(chstr) > 200 then SetLength(chstr, 200);
6193 if Length(chstr) < 1 then
6194 begin
6195 g_Console_Add('rcon <command>');
6196 Exit;
6197 end;
6199 MC_SEND_RCONCommand(chstr);
6200 end
6201 else g_Console_Add('rcon <command>');
6202 end;
6203 end
6204 else if cmd = 'ready' then
6205 begin
6206 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6207 gLMSRespawnTime := gTime + 100;
6208 end
6209 else if (cmd = 'callvote') and g_Game_IsNet then
6210 begin
6211 if Length(P) > 1 then
6212 begin
6213 chstr := '';
6214 for a := 1 to High(P) do begin
6215 if a > 1 then chstr := chstr + ' ';
6216 chstr := chstr + P[a];
6217 end;
6219 if Length(chstr) > 200 then SetLength(chstr, 200);
6221 if Length(chstr) < 1 then
6222 begin
6223 g_Console_Add('callvote <command>');
6224 Exit;
6225 end;
6227 if g_Game_IsClient then
6228 MC_SEND_Vote(True, chstr)
6229 else
6230 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6231 g_Console_Process('vote', True);
6232 end
6233 else
6234 g_Console_Add('callvote <command>');
6235 end
6236 else if (cmd = 'vote') and g_Game_IsNet then
6237 begin
6238 if g_Game_IsClient then
6239 MC_SEND_Vote(False)
6240 else if gVoteInProgress then
6241 begin
6242 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6243 a := Floor((NetClientCount+1)/2.0) + 1
6244 else
6245 a := Floor(NetClientCount/2.0) + 1;
6246 if gVoted then
6247 begin
6248 Dec(gVoteCount);
6249 gVoted := False;
6250 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6251 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6252 end
6253 else
6254 begin
6255 Inc(gVoteCount);
6256 gVoted := True;
6257 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6258 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6259 g_Game_CheckVote;
6260 end;
6261 end;
6262 end
6263 end;
6264 end;
6266 procedure g_TakeScreenShot();
6267 var
6268 a: Word;
6269 FileName: string;
6270 ssdir, t: string;
6271 st: TStream;
6272 ok: Boolean;
6273 begin
6274 if e_NoGraphics then Exit;
6275 ssdir := GameDir+'/screenshots';
6276 if not findFileCI(ssdir, true) then
6277 begin
6278 // try to create dir
6279 try
6280 CreateDir(ssdir);
6281 except
6282 end;
6283 if not findFileCI(ssdir, true) then exit; // alas
6284 end;
6285 try
6286 for a := 1 to High(Word) do
6287 begin
6288 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6289 t := FileName;
6290 if findFileCI(t, true) then continue;
6291 if not findFileCI(FileName) then
6292 begin
6293 ok := false;
6294 st := createDiskFile(FileName);
6295 try
6296 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6297 ok := true;
6298 finally
6299 st.Free();
6300 end;
6301 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6302 break;
6303 end;
6304 end;
6305 except
6306 end;
6307 end;
6309 procedure g_Game_InGameMenu(Show: Boolean);
6310 begin
6311 if (g_ActiveWindow = nil) and Show then
6312 begin
6313 if gGameSettings.GameType = GT_SINGLE then
6314 g_GUI_ShowWindow('GameSingleMenu')
6315 else
6316 begin
6317 if g_Game_IsClient then
6318 g_GUI_ShowWindow('GameClientMenu')
6319 else
6320 if g_Game_IsNet then
6321 g_GUI_ShowWindow('GameServerMenu')
6322 else
6323 g_GUI_ShowWindow('GameCustomMenu');
6324 end;
6325 g_Sound_PlayEx('MENU_OPEN');
6327 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6328 if (not g_Game_IsNet) then
6329 g_Game_Pause(True);
6330 end
6331 else
6332 if (g_ActiveWindow <> nil) and (not Show) then
6333 begin
6334 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6335 if (not g_Game_IsNet) then
6336 g_Game_Pause(False);
6337 end;
6338 end;
6340 procedure g_Game_Pause(Enable: Boolean);
6341 begin
6342 if not gGameOn then
6343 Exit;
6345 if gPause = Enable then
6346 Exit;
6348 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6349 Exit;
6351 gPause := Enable;
6352 g_Game_PauseAllSounds(Enable);
6353 end;
6355 procedure g_Game_PauseAllSounds(Enable: Boolean);
6356 var
6357 i: Integer;
6358 begin
6359 // Òðèããåðû:
6360 if gTriggers <> nil then
6361 for i := 0 to High(gTriggers) do
6362 with gTriggers[i] do
6363 if (TriggerType = TRIGGER_SOUND) and
6364 (Sound <> nil) and
6365 Sound.IsPlaying() then
6366 begin
6367 Sound.Pause(Enable);
6368 end;
6370 // Çâóêè èãðîêîâ:
6371 if gPlayers <> nil then
6372 for i := 0 to High(gPlayers) do
6373 if gPlayers[i] <> nil then
6374 gPlayers[i].PauseSounds(Enable);
6376 // Ìóçûêà:
6377 if gMusic <> nil then
6378 gMusic.Pause(Enable);
6379 end;
6381 procedure g_Game_StopAllSounds(all: Boolean);
6382 var
6383 i: Integer;
6384 begin
6385 if gTriggers <> nil then
6386 for i := 0 to High(gTriggers) do
6387 with gTriggers[i] do
6388 if (TriggerType = TRIGGER_SOUND) and
6389 (Sound <> nil) then
6390 Sound.Stop();
6392 if gMusic <> nil then
6393 gMusic.Stop();
6395 if all then
6396 e_StopChannels();
6397 end;
6399 procedure g_Game_UpdateTriggerSounds();
6400 var
6401 i: Integer;
6402 begin
6403 if gTriggers <> nil then
6404 for i := 0 to High(gTriggers) do
6405 with gTriggers[i] do
6406 if (TriggerType = TRIGGER_SOUND) and
6407 (Sound <> nil) and
6408 (Data.Local) and
6409 Sound.IsPlaying() then
6410 begin
6411 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6412 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6413 begin
6414 Sound.SetPan(0.5 - Data.Pan/255.0);
6415 Sound.SetVolume(Data.Volume/255.0);
6416 end
6417 else
6418 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6419 end;
6420 end;
6422 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6423 begin
6424 Result := False;
6425 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6426 begin
6427 Result := True;
6428 Exit;
6429 end;
6430 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6431 begin
6432 Result := True;
6433 Exit;
6434 end;
6435 if gSpectMode <> SPECT_PLAYERS then
6436 Exit;
6437 if gSpectPID1 = UID then
6438 begin
6439 Result := True;
6440 Exit;
6441 end;
6442 if gSpectViewTwo and (gSpectPID2 = UID) then
6443 begin
6444 Result := True;
6445 Exit;
6446 end;
6447 end;
6449 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6450 var
6451 Pl: TPlayer;
6452 begin
6453 Result := False;
6454 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6455 begin
6456 Result := True;
6457 Exit;
6458 end;
6459 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6460 begin
6461 Result := True;
6462 Exit;
6463 end;
6464 if gSpectMode <> SPECT_PLAYERS then
6465 Exit;
6466 Pl := g_Player_Get(gSpectPID1);
6467 if (Pl <> nil) and (Pl.Team = Team) then
6468 begin
6469 Result := True;
6470 Exit;
6471 end;
6472 if gSpectViewTwo then
6473 begin
6474 Pl := g_Player_Get(gSpectPID2);
6475 if (Pl <> nil) and (Pl.Team = Team) then
6476 begin
6477 Result := True;
6478 Exit;
6479 end;
6480 end;
6481 end;
6483 procedure g_Game_Message(Msg: string; Time: Word);
6484 begin
6485 MessageText := b_Text_Format(Msg);
6486 MessageTime := Time;
6487 end;
6489 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6490 var
6491 a: Integer;
6492 begin
6493 case gAnnouncer of
6494 ANNOUNCE_NONE:
6495 Exit;
6496 ANNOUNCE_ME,
6497 ANNOUNCE_MEPLUS:
6498 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6499 Exit;
6500 end;
6501 for a := 0 to 3 do
6502 if goodsnd[a].IsPlaying() then
6503 Exit;
6505 goodsnd[Random(4)].Play();
6506 end;
6508 procedure g_Game_Announce_KillCombo(Param: Integer);
6509 var
6510 UID: Word;
6511 c, n: Byte;
6512 Pl: TPlayer;
6513 Name: String;
6514 begin
6515 UID := Param and $FFFF;
6516 c := Param shr 16;
6517 if c < 2 then
6518 Exit;
6520 Pl := g_Player_Get(UID);
6521 if Pl = nil then
6522 Name := '?'
6523 else
6524 Name := Pl.Name;
6526 case c of
6527 2: begin
6528 n := 0;
6529 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6530 end;
6531 3: begin
6532 n := 1;
6533 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6534 end;
6535 4: begin
6536 n := 2;
6537 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6538 end;
6539 else begin
6540 n := 3;
6541 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6542 end;
6543 end;
6545 case gAnnouncer of
6546 ANNOUNCE_NONE:
6547 Exit;
6548 ANNOUNCE_ME:
6549 if not g_Game_IsWatchedPlayer(UID) then
6550 Exit;
6551 ANNOUNCE_MEPLUS:
6552 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6553 Exit;
6554 end;
6556 if killsnd[n].IsPlaying() then
6557 killsnd[n].Stop();
6558 killsnd[n].Play();
6559 end;
6561 procedure g_Game_StartVote(Command, Initiator: string);
6562 var
6563 Need: Integer;
6564 begin
6565 if not gVotesEnabled then Exit;
6566 if gGameSettings.GameType <> GT_SERVER then Exit;
6567 if gVoteInProgress or gVotePassed then
6568 begin
6569 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6570 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6571 Exit;
6572 end;
6573 gVoteInProgress := True;
6574 gVotePassed := False;
6575 gVoteTimer := gTime + gVoteTimeout * 1000;
6576 gVoteCount := 0;
6577 gVoted := False;
6578 gVoteCommand := Command;
6580 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6581 Need := Floor((NetClientCount+1)/2.0)+1
6582 else
6583 Need := Floor(NetClientCount/2.0)+1;
6584 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6585 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6586 end;
6588 procedure g_Game_CheckVote;
6589 var
6590 I, Need: Integer;
6591 begin
6592 if gGameSettings.GameType <> GT_SERVER then Exit;
6593 if not gVoteInProgress then Exit;
6595 if (gTime >= gVoteTimer) then
6596 begin
6597 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6598 Need := Floor((NetClientCount+1)/2.0) + 1
6599 else
6600 Need := Floor(NetClientCount/2.0) + 1;
6601 if gVoteCount >= Need then
6602 begin
6603 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6604 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6605 gVotePassed := True;
6606 gVoteCmdTimer := gTime + 5000;
6607 end
6608 else
6609 begin
6610 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6611 MH_SEND_VoteEvent(NET_VE_FAILED);
6612 end;
6613 if NetClients <> nil then
6614 for I := Low(NetClients) to High(NetClients) do
6615 if NetClients[i].Used then
6616 NetClients[i].Voted := False;
6617 gVoteInProgress := False;
6618 gVoted := False;
6619 gVoteCount := 0;
6620 end
6621 else
6622 begin
6623 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6624 Need := Floor((NetClientCount+1)/2.0) + 1
6625 else
6626 Need := Floor(NetClientCount/2.0) + 1;
6627 if gVoteCount >= Need then
6628 begin
6629 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6630 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6631 gVoteInProgress := False;
6632 gVotePassed := True;
6633 gVoteCmdTimer := gTime + 5000;
6634 gVoted := False;
6635 gVoteCount := 0;
6636 if NetClients <> nil then
6637 for I := Low(NetClients) to High(NetClients) do
6638 if NetClients[i].Used then
6639 NetClients[i].Voted := False;
6640 end;
6641 end;
6642 end;
6644 procedure g_Game_LoadMapList(FileName: string);
6645 var
6646 ListFile: TextFile;
6647 s: string;
6648 begin
6649 MapList := nil;
6650 MapIndex := -1;
6652 if not FileExists(FileName) then Exit;
6654 AssignFile(ListFile, FileName);
6655 Reset(ListFile);
6656 while not EOF(ListFile) do
6657 begin
6658 ReadLn(ListFile, s);
6660 s := Trim(s);
6661 if s = '' then Continue;
6663 SetLength(MapList, Length(MapList)+1);
6664 MapList[High(MapList)] := s;
6665 end;
6666 CloseFile(ListFile);
6667 end;
6669 procedure g_Game_SetDebugMode();
6670 begin
6671 gDebugMode := True;
6672 // ×èòû (äàæå â ñâîåé èãðå):
6673 gCheats := True;
6674 end;
6676 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6677 var
6678 i: Word;
6679 begin
6680 if Length(LoadingStat.Msgs) = 0 then
6681 Exit;
6683 with LoadingStat do
6684 begin
6685 if not reWrite then
6686 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6687 if NextMsg = Length(Msgs) then
6688 begin // scroll
6689 for i := 0 to High(Msgs)-1 do
6690 Msgs[i] := Msgs[i+1];
6691 end
6692 else
6693 Inc(NextMsg);
6694 end else
6695 if NextMsg = 0 then
6696 Inc(NextMsg);
6698 Msgs[NextMsg-1] := Text;
6699 CurValue := 0;
6700 MaxValue := Max;
6701 ShowCount := 0;
6702 end;
6704 g_ActiveWindow := nil;
6706 ProcessLoading;
6707 end;
6709 procedure g_Game_StepLoading();
6710 begin
6711 with LoadingStat do
6712 begin
6713 Inc(CurValue);
6714 Inc(ShowCount);
6715 if (ShowCount > LOADING_SHOW_STEP) then
6716 begin
6717 ShowCount := 0;
6718 ProcessLoading;
6719 end;
6720 end;
6721 end;
6723 procedure g_Game_ClearLoading();
6724 var
6725 len: Word;
6726 begin
6727 with LoadingStat do
6728 begin
6729 CurValue := 0;
6730 MaxValue := 0;
6731 ShowCount := 0;
6732 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6733 if len < 1 then len := 1;
6734 SetLength(Msgs, len);
6735 for len := Low(Msgs) to High(Msgs) do
6736 Msgs[len] := '';
6737 NextMsg := 0;
6738 end;
6739 end;
6741 procedure Parse_Params(var pars: TParamStrValues);
6742 var
6743 i: Integer;
6744 s: String;
6745 begin
6746 SetLength(pars, 0);
6747 i := 1;
6748 while i <= ParamCount do
6749 begin
6750 s := ParamStr(i);
6751 if (s[1] = '-') and (Length(s) > 1) then
6752 begin
6753 if (s[2] = '-') and (Length(s) > 2) then
6754 begin // Îäèíî÷íûé ïàðàìåòð
6755 SetLength(pars, Length(pars) + 1);
6756 with pars[High(pars)] do
6757 begin
6758 Name := LowerCase(s);
6759 Value := '+';
6760 end;
6761 end
6762 else
6763 if (i < ParamCount) then
6764 begin // Ïàðàìåòð ñî çíà÷åíèåì
6765 Inc(i);
6766 SetLength(pars, Length(pars) + 1);
6767 with pars[High(pars)] do
6768 begin
6769 Name := LowerCase(s);
6770 Value := LowerCase(ParamStr(i));
6771 end;
6772 end;
6773 end;
6775 Inc(i);
6776 end;
6777 end;
6779 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6780 var
6781 i: Integer;
6782 begin
6783 Result := '';
6784 for i := 0 to High(pars) do
6785 if pars[i].Name = aName then
6786 begin
6787 Result := pars[i].Value;
6788 Break;
6789 end;
6790 end;
6792 procedure g_Game_Process_Params();
6793 var
6794 pars: TParamStrValues;
6795 map: String;
6796 GMode, n: Byte;
6797 LimT, LimS: Integer;
6798 Opt: LongWord;
6799 Lives: Integer;
6800 s: String;
6801 Port: Integer;
6802 ip: String;
6803 F: TextFile;
6804 begin
6805 Parse_Params(pars);
6807 // Debug mode:
6808 s := Find_Param_Value(pars, '--debug');
6809 if (s <> '') then
6810 begin
6811 g_Game_SetDebugMode();
6812 s := Find_Param_Value(pars, '--netdump');
6813 if (s <> '') then
6814 NetDump := True;
6815 end;
6817 // Connect when game loads
6818 ip := Find_Param_Value(pars, '-connect');
6820 if ip <> '' then
6821 begin
6822 s := Find_Param_Value(pars, '-port');
6823 if (s = '') or not TryStrToInt(s, Port) then
6824 Port := 25666;
6826 s := Find_Param_Value(pars, '-pw');
6828 g_Game_StartClient(ip, Port, s);
6829 Exit;
6830 end;
6832 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
6833 if (s <> '') then
6834 begin
6835 gDefaultMegawadStart := s;
6836 end;
6838 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
6839 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
6840 begin
6841 gDefaultMegawadStart := DF_Default_Megawad_Start;
6842 end;
6844 // Start map when game loads:
6845 map := LowerCase(Find_Param_Value(pars, '-map'));
6846 if isWadPath(map) then
6847 begin
6848 // Game mode:
6849 s := Find_Param_Value(pars, '-gm');
6850 GMode := g_Game_TextToMode(s);
6851 if GMode = GM_NONE then GMode := GM_DM;
6852 if GMode = GM_SINGLE then GMode := GM_COOP;
6854 // Time limit:
6855 s := Find_Param_Value(pars, '-limt');
6856 if (s = '') or (not TryStrToInt(s, LimT)) then
6857 LimT := 0;
6858 if LimT < 0 then
6859 LimT := 0;
6861 // Goal limit:
6862 s := Find_Param_Value(pars, '-lims');
6863 if (s = '') or (not TryStrToInt(s, LimS)) then
6864 LimS := 0;
6865 if LimS < 0 then
6866 LimS := 0;
6868 // Lives limit:
6869 s := Find_Param_Value(pars, '-lives');
6870 if (s = '') or (not TryStrToInt(s, Lives)) then
6871 Lives := 0;
6872 if Lives < 0 then
6873 Lives := 0;
6875 // Options:
6876 s := Find_Param_Value(pars, '-opt');
6877 if (s = '') then
6878 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6879 else
6880 Opt := StrToIntDef(s, 0);
6881 if Opt = 0 then
6882 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6884 // Close after map:
6885 s := Find_Param_Value(pars, '--close');
6886 if (s <> '') then
6887 gMapOnce := True;
6889 // Delete test map after play:
6890 s := Find_Param_Value(pars, '--testdelete');
6891 if (s <> '') then
6892 begin
6893 gMapToDelete := MapsDir + map;
6894 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6895 Halt(1);
6896 end;
6898 // Delete temporary WAD after play:
6899 s := Find_Param_Value(pars, '--tempdelete');
6900 if (s <> '') then
6901 begin
6902 gMapToDelete := MapsDir + map;
6903 gTempDelete := True;
6904 end;
6906 // Number of players:
6907 s := Find_Param_Value(pars, '-pl');
6908 if (s = '') then
6909 n := 1
6910 else
6911 n := StrToIntDef(s, 1);
6913 // Start:
6914 s := Find_Param_Value(pars, '-port');
6915 if (s = '') or not TryStrToInt(s, Port) then
6916 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6917 else
6918 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6919 end;
6921 // Execute script when game loads:
6922 s := Find_Param_Value(pars, '-exec');
6923 if s <> '' then
6924 begin
6925 if Pos(':\', s) = 0 then
6926 s := GameDir + '/' + s;
6928 {$I-}
6929 AssignFile(F, s);
6930 Reset(F);
6931 if IOResult <> 0 then
6932 begin
6933 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6934 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6935 CloseFile(F);
6936 Exit;
6937 end;
6938 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6939 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6941 while not EOF(F) do
6942 begin
6943 ReadLn(F, s);
6944 if IOResult <> 0 then
6945 begin
6946 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6947 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6948 CloseFile(F);
6949 Exit;
6950 end;
6951 if Pos('#', s) <> 1 then // script comment
6952 g_Console_Process(s, True);
6953 end;
6955 CloseFile(F);
6956 {$I+}
6957 end;
6959 SetLength(pars, 0);
6960 end;
6962 end.