DEADSOFTWARE

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