DEADSOFTWARE

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