DEADSOFTWARE

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