DEADSOFTWARE

"dbg_holmes" console command; draw line to monster target
[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 renderMapInternal(-c, -d, a, b+p.IncCam, true);
2912 if p.FSpectator then
2913 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2914 p.GameY + PLAYER_RECT_CY - 4,
2915 'X', gStdFont, 255, 255, 255, 1, True);
2917 for a := 0 to High(gCollideMap) do
2918 for b := 0 to High(gCollideMap[a]) do
2919 begin
2920 d := 0;
2921 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2922 d := d + 1;
2923 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2924 d := d + 2;
2926 case d of
2927 1: e_DrawPoint(1, b, a, 200, 200, 200);
2928 2: e_DrawPoint(1, b, a, 64, 64, 255);
2929 3: e_DrawPoint(1, b, a, 255, 0, 255);
2930 end;
2931 end;
2934 glPopMatrix();
2936 p.DrawPain();
2937 p.DrawPickup();
2938 p.DrawRulez();
2939 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
2940 if g_Debug_Player then
2941 g_Player_DrawDebug(p);
2942 p.DrawGUI();
2943 end;
2945 procedure drawProfilers ();
2946 var
2947 px: Integer = -1;
2948 py: Integer = -1;
2949 begin
2950 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
2951 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
2952 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
2953 end;
2955 procedure g_Game_Draw();
2956 var
2957 ID: DWORD;
2958 w, h: Word;
2959 ww, hh: Byte;
2960 Time: Int64;
2961 back: string;
2962 plView1, plView2: TPlayer;
2963 Split: Boolean;
2964 begin
2965 if gExit = EXIT_QUIT then Exit;
2967 Time := GetTimer() {div 1000};
2968 FPSCounter := FPSCounter+1;
2969 if Time - FPSTime >= 1000 then
2970 begin
2971 FPS := FPSCounter;
2972 FPSCounter := 0;
2973 FPSTime := Time;
2974 end;
2976 if gGameOn or (gState = STATE_FOLD) then
2977 begin
2978 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
2979 begin
2980 gSpectMode := SPECT_NONE;
2981 if not gRevertPlayers then
2982 begin
2983 plView1 := gPlayer1;
2984 plView2 := gPlayer2;
2985 end
2986 else
2987 begin
2988 plView1 := gPlayer2;
2989 plView2 := gPlayer1;
2990 end;
2991 end
2992 else
2993 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
2994 begin
2995 gSpectMode := SPECT_NONE;
2996 if gPlayer2 = nil then
2997 plView1 := gPlayer1
2998 else
2999 plView1 := gPlayer2;
3000 plView2 := nil;
3001 end
3002 else
3003 begin
3004 plView1 := nil;
3005 plView2 := nil;
3006 end;
3008 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3009 gSpectMode := SPECT_STATS;
3011 if gSpectMode = SPECT_PLAYERS then
3012 if gPlayers <> nil then
3013 begin
3014 plView1 := GetActivePlayer_ByID(gSpectPID1);
3015 if plView1 = nil then
3016 begin
3017 gSpectPID1 := GetActivePlayerID_Next();
3018 plView1 := GetActivePlayer_ByID(gSpectPID1);
3019 end;
3020 if gSpectViewTwo then
3021 begin
3022 plView2 := GetActivePlayer_ByID(gSpectPID2);
3023 if plView2 = nil then
3024 begin
3025 gSpectPID2 := GetActivePlayerID_Next();
3026 plView2 := GetActivePlayer_ByID(gSpectPID2);
3027 end;
3028 end;
3029 end;
3031 if gSpectMode = SPECT_MAPVIEW then
3032 begin
3033 // Ðåæèì ïðîñìîòðà êàðòû
3034 Split := False;
3035 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3036 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3037 gHearPoint1.Active := True;
3038 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3039 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3040 gHearPoint2.Active := False;
3041 end
3042 else
3043 begin
3044 Split := (plView1 <> nil) and (plView2 <> nil);
3046 // Òî÷êè ñëóõà èãðîêîâ
3047 if plView1 <> nil then
3048 begin
3049 gHearPoint1.Active := True;
3050 gHearPoint1.Coords.X := plView1.GameX;
3051 gHearPoint1.Coords.Y := plView1.GameY;
3052 end else
3053 gHearPoint1.Active := False;
3054 if plView2 <> nil then
3055 begin
3056 gHearPoint2.Active := True;
3057 gHearPoint2.Coords.X := plView2.GameX;
3058 gHearPoint2.Coords.Y := plView2.GameY;
3059 end else
3060 gHearPoint2.Active := False;
3062 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3063 gPlayerScreenSize.X := gScreenWidth-196;
3064 if Split then
3065 begin
3066 gPlayerScreenSize.Y := gScreenHeight div 2;
3067 if gScreenHeight mod 2 = 0 then
3068 Dec(gPlayerScreenSize.Y);
3069 end
3070 else
3071 gPlayerScreenSize.Y := gScreenHeight;
3073 if Split then
3074 if gScreenHeight mod 2 = 0 then
3075 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3076 else
3077 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3079 DrawPlayer(plView1);
3080 gPlayer1ScreenCoord.X := sX;
3081 gPlayer1ScreenCoord.Y := sY;
3083 if Split then
3084 begin
3085 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3087 DrawPlayer(plView2);
3088 gPlayer2ScreenCoord.X := sX;
3089 gPlayer2ScreenCoord.Y := sY;
3090 end;
3092 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3094 if Split then
3095 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3096 end;
3098 if MessageText <> '' then
3099 begin
3100 w := 0;
3101 h := 0;
3102 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3103 if Split then
3104 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3105 (gScreenHeight div 2)-(h div 2), MessageText)
3106 else
3107 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3108 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3109 end;
3111 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3113 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3114 begin
3115 // Draw spectator GUI
3116 ww := 0;
3117 hh := 0;
3118 e_TextureFontGetSize(gStdFont, ww, hh);
3119 case gSpectMode of
3120 SPECT_STATS:
3121 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3122 SPECT_MAPVIEW:
3123 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3124 SPECT_PLAYERS:
3125 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3126 end;
3127 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3128 if gSpectMode = SPECT_MAPVIEW then
3129 begin
3130 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3131 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3132 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3133 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3134 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3135 end;
3136 if gSpectMode = SPECT_PLAYERS then
3137 begin
3138 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3139 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3140 if gSpectViewTwo then
3141 begin
3142 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3143 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3144 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3145 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3146 end
3147 else
3148 begin
3149 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3150 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3151 end;
3152 end;
3153 end;
3154 end;
3156 if gPause and gGameOn and (g_ActiveWindow = nil) then
3157 begin
3158 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3160 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3161 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3162 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3163 end;
3165 if not gGameOn then
3166 begin
3167 if (gState = STATE_MENU) then
3168 begin
3169 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
3170 begin
3171 if g_Texture_Get('MENU_BACKGROUND', ID) then
3172 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3173 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3174 end;
3175 if g_ActiveWindow <> nil then
3176 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3177 end;
3179 if gState = STATE_FOLD then
3180 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3182 if gState = STATE_INTERCUSTOM then
3183 begin
3184 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3185 begin
3186 back := 'TEXTURE_endpic';
3187 if not g_Texture_Get(back, ID) then
3188 back := _lc[I_TEXTURE_ENDPIC];
3189 end
3190 else
3191 back := 'INTER';
3193 if g_Texture_Get(back, ID) then
3194 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3195 else
3196 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3198 DrawCustomStat();
3200 if g_ActiveWindow <> nil then
3201 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3202 end;
3204 if gState = STATE_INTERSINGLE then
3205 begin
3206 if EndingGameCounter > 0 then
3207 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter)
3208 else
3209 begin
3210 back := 'INTER';
3212 if g_Texture_Get(back, ID) then
3213 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3214 else
3215 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3217 DrawSingleStat();
3219 if g_ActiveWindow <> nil then
3220 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3221 end;
3222 end;
3224 if gState = STATE_ENDPIC then
3225 begin
3226 ID := DWORD(-1);
3227 if not g_Texture_Get('TEXTURE_endpic', ID) then
3228 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3230 if ID <> DWORD(-1) then
3231 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3232 else
3233 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3235 if g_ActiveWindow <> nil then
3236 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3237 end;
3239 if gState = STATE_SLIST then
3240 begin
3241 if g_Texture_Get('MENU_BACKGROUND', ID) then
3242 begin
3243 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3244 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3245 end;
3246 g_Serverlist_Draw(slCurrent);
3247 end;
3248 end;
3250 if g_ActiveWindow <> nil then
3251 begin
3252 if gGameOn then
3253 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3254 g_ActiveWindow.Draw();
3255 end;
3257 // draw inspector
3258 if (g_holmes_enabled) then g_Holmes_Draw();
3260 g_Console_Draw();
3262 if g_debug_Sounds and gGameOn then
3263 begin
3264 for w := 0 to High(e_SoundsArray) do
3265 for h := 0 to e_SoundsArray[w].nRefs do
3266 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3267 end;
3269 if gShowFPS then
3270 begin
3271 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3272 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3273 end;
3275 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3276 e_TextureFontPrint(gScreenWidth-72, 0,
3277 Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]),
3278 gStdFont);
3280 if gGameOn then drawProfilers();
3281 end;
3283 procedure g_Game_Quit();
3284 begin
3285 g_Game_StopAllSounds(True);
3286 gMusic.Free();
3287 g_Game_SaveOptions();
3288 g_Game_FreeData();
3289 g_PlayerModel_FreeData();
3290 g_Texture_DeleteAll();
3291 g_Frames_DeleteAll();
3292 g_Menu_Free();
3294 if NetInitDone then g_Net_Free;
3296 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3297 if gMapToDelete <> '' then
3298 g_Game_DeleteTestMap();
3300 gExit := EXIT_QUIT;
3301 PushExitEvent();
3302 end;
3304 procedure g_FatalError(Text: String);
3305 begin
3306 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3307 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3309 gExit := EXIT_SIMPLE;
3310 end;
3312 procedure g_SimpleError(Text: String);
3313 begin
3314 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3315 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3316 end;
3318 procedure g_Game_SetupScreenSize();
3319 const
3320 RES_FACTOR = 4.0 / 3.0;
3321 var
3322 s: Single;
3323 rf: Single;
3324 bw, bh: Word;
3325 begin
3326 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3327 gPlayerScreenSize.X := gScreenWidth-196;
3328 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3329 gPlayerScreenSize.Y := gScreenHeight div 2
3330 else
3331 gPlayerScreenSize.Y := gScreenHeight;
3333 // Ðàçìåð çàäíåãî ïëàíà:
3334 if BackID <> DWORD(-1) then
3335 begin
3336 s := SKY_STRETCH;
3337 if (gScreenWidth*s > gMapInfo.Width) or
3338 (gScreenHeight*s > gMapInfo.Height) then
3339 begin
3340 gBackSize.X := gScreenWidth;
3341 gBackSize.Y := gScreenHeight;
3342 end
3343 else
3344 begin
3345 e_GetTextureSize(BackID, @bw, @bh);
3346 rf := Single(bw) / Single(bh);
3347 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3348 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3349 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3350 if (s < 1.0) then s := 1.0;
3351 gBackSize.X := Round(bw*s);
3352 gBackSize.Y := Round(bh*s);
3353 end;
3354 end;
3355 end;
3357 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3358 begin
3359 g_Window_SetSize(newWidth, newHeight, nowFull);
3360 end;
3362 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3363 begin
3364 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3365 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3366 Exit;
3367 if gPlayer1 = nil then
3368 begin
3369 if g_Game_IsClient then
3370 begin
3371 if NetPlrUID1 > -1 then
3372 begin
3373 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3374 gPlayer1 := g_Player_Get(NetPlrUID1);
3375 end;
3376 Exit;
3377 end;
3379 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3380 Team := gPlayer1Settings.Team;
3382 // Ñîçäàíèå ïåðâîãî èãðîêà:
3383 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3384 gPlayer1Settings.Color,
3385 Team, False));
3386 if gPlayer1 = nil then
3387 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3388 else
3389 begin
3390 gPlayer1.Name := gPlayer1Settings.Name;
3391 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3392 if g_Game_IsServer and g_Game_IsNet then
3393 MH_SEND_PlayerCreate(gPlayer1.UID);
3394 gPlayer1.Respawn(False, True);
3396 if g_Game_IsNet and NetUseMaster then
3397 g_Net_Slist_Update;
3398 end;
3400 Exit;
3401 end;
3402 if gPlayer2 = nil then
3403 begin
3404 if g_Game_IsClient then
3405 begin
3406 if NetPlrUID2 > -1 then
3407 gPlayer2 := g_Player_Get(NetPlrUID2);
3408 Exit;
3409 end;
3411 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3412 Team := gPlayer2Settings.Team;
3414 // Ñîçäàíèå âòîðîãî èãðîêà:
3415 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3416 gPlayer2Settings.Color,
3417 Team, False));
3418 if gPlayer2 = nil then
3419 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3420 else
3421 begin
3422 gPlayer2.Name := gPlayer2Settings.Name;
3423 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3424 if g_Game_IsServer and g_Game_IsNet then
3425 MH_SEND_PlayerCreate(gPlayer2.UID);
3426 gPlayer2.Respawn(False, True);
3428 if g_Game_IsNet and NetUseMaster then
3429 g_Net_Slist_Update;
3430 end;
3432 Exit;
3433 end;
3434 end;
3436 procedure g_Game_RemovePlayer();
3437 var
3438 Pl: TPlayer;
3439 begin
3440 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3441 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3442 Exit;
3443 Pl := gPlayer2;
3444 if Pl <> nil then
3445 begin
3446 if g_Game_IsServer then
3447 begin
3448 Pl.Lives := 0;
3449 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3450 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3451 g_Player_Remove(Pl.UID);
3453 if g_Game_IsNet and NetUseMaster then
3454 g_Net_Slist_Update;
3455 end else
3456 gPlayer2 := nil;
3457 Exit;
3458 end;
3459 Pl := gPlayer1;
3460 if Pl <> nil then
3461 begin
3462 if g_Game_IsServer then
3463 begin
3464 Pl.Lives := 0;
3465 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3466 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3467 g_Player_Remove(Pl.UID);
3469 if g_Game_IsNet and NetUseMaster then
3470 g_Net_Slist_Update;
3471 end else
3472 begin
3473 gPlayer1 := nil;
3474 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3475 end;
3476 Exit;
3477 end;
3478 end;
3480 procedure g_Game_Spectate();
3481 begin
3482 g_Game_RemovePlayer();
3483 if gPlayer1 <> nil then
3484 g_Game_RemovePlayer();
3485 end;
3487 procedure g_Game_SpectateCenterView();
3488 begin
3489 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3490 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3491 end;
3493 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3494 var
3495 i, nPl: Integer;
3496 begin
3497 g_Game_Free();
3499 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3501 g_Game_ClearLoading();
3503 // Íàñòðîéêè èãðû:
3504 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3505 gAimLine := False;
3506 gShowMap := False;
3507 gGameSettings.GameType := GT_SINGLE;
3508 gGameSettings.MaxLives := 0;
3509 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3510 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3511 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3512 gSwitchGameMode := GM_SINGLE;
3514 g_Game_ExecuteEvent('ongamestart');
3516 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3517 g_Game_SetupScreenSize();
3519 // Ñîçäàíèå ïåðâîãî èãðîêà:
3520 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3521 gPlayer1Settings.Color,
3522 gPlayer1Settings.Team, False));
3523 if gPlayer1 = nil then
3524 begin
3525 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3526 Exit;
3527 end;
3529 gPlayer1.Name := gPlayer1Settings.Name;
3530 nPl := 1;
3532 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3533 if TwoPlayers then
3534 begin
3535 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3536 gPlayer2Settings.Color,
3537 gPlayer2Settings.Team, False));
3538 if gPlayer2 = nil then
3539 begin
3540 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3541 Exit;
3542 end;
3544 gPlayer2.Name := gPlayer2Settings.Name;
3545 Inc(nPl);
3546 end;
3548 // Çàãðóçêà è çàïóñê êàðòû:
3549 if not g_Game_StartMap(MAP, True) then
3550 begin
3551 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3552 Exit;
3553 end;
3555 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3556 g_Player_Init();
3558 // Ñîçäàåì áîòîâ:
3559 for i := nPl+1 to nPlayers do
3560 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3561 end;
3563 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3564 TimeLimit, GoalLimit: Word;
3565 MaxLives: Byte;
3566 Options: LongWord; nPlayers: Byte);
3567 var
3568 i, nPl: Integer;
3569 begin
3570 g_Game_Free();
3572 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3574 g_Game_ClearLoading();
3576 // Íàñòðîéêè èãðû:
3577 gGameSettings.GameType := GT_CUSTOM;
3578 gGameSettings.GameMode := GameMode;
3579 gSwitchGameMode := GameMode;
3580 gGameSettings.TimeLimit := TimeLimit;
3581 gGameSettings.GoalLimit := GoalLimit;
3582 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3583 gGameSettings.Options := Options;
3585 gCoopTotalMonstersKilled := 0;
3586 gCoopTotalSecretsFound := 0;
3587 gCoopTotalMonsters := 0;
3588 gCoopTotalSecrets := 0;
3589 gAimLine := False;
3590 gShowMap := False;
3592 g_Game_ExecuteEvent('ongamestart');
3594 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3595 g_Game_SetupScreenSize();
3597 // Ðåæèì íàáëþäàòåëÿ:
3598 if nPlayers = 0 then
3599 begin
3600 gPlayer1 := nil;
3601 gPlayer2 := nil;
3602 end;
3604 nPl := 0;
3605 if nPlayers >= 1 then
3606 begin
3607 // Ñîçäàíèå ïåðâîãî èãðîêà:
3608 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3609 gPlayer1Settings.Color,
3610 gPlayer1Settings.Team, False));
3611 if gPlayer1 = nil then
3612 begin
3613 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3614 Exit;
3615 end;
3617 gPlayer1.Name := gPlayer1Settings.Name;
3618 Inc(nPl);
3619 end;
3621 if nPlayers >= 2 then
3622 begin
3623 // Ñîçäàíèå âòîðîãî èãðîêà:
3624 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3625 gPlayer2Settings.Color,
3626 gPlayer2Settings.Team, False));
3627 if gPlayer2 = nil then
3628 begin
3629 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3630 Exit;
3631 end;
3633 gPlayer2.Name := gPlayer2Settings.Name;
3634 Inc(nPl);
3635 end;
3637 // Çàãðóçêà è çàïóñê êàðòû:
3638 if not g_Game_StartMap(Map, True) then
3639 begin
3640 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3641 Exit;
3642 end;
3644 // Íåò òî÷åê ïîÿâëåíèÿ:
3645 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3646 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3647 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3648 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3649 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3650 begin
3651 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3652 Exit;
3653 end;
3655 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3656 g_Player_Init();
3658 // Ñîçäàåì áîòîâ:
3659 for i := nPl+1 to nPlayers do
3660 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3661 end;
3663 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3664 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3665 Options: LongWord; nPlayers: Byte;
3666 IPAddr: LongWord; Port: Word);
3667 begin
3668 g_Game_Free();
3670 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3672 g_Game_ClearLoading();
3674 // Íàñòðîéêè èãðû:
3675 gGameSettings.GameType := GT_SERVER;
3676 gGameSettings.GameMode := GameMode;
3677 gSwitchGameMode := GameMode;
3678 gGameSettings.TimeLimit := TimeLimit;
3679 gGameSettings.GoalLimit := GoalLimit;
3680 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3681 gGameSettings.Options := Options;
3683 gCoopTotalMonstersKilled := 0;
3684 gCoopTotalSecretsFound := 0;
3685 gCoopTotalMonsters := 0;
3686 gCoopTotalSecrets := 0;
3687 gAimLine := False;
3688 gShowMap := False;
3690 g_Game_ExecuteEvent('ongamestart');
3692 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3693 g_Game_SetupScreenSize();
3695 // Ðåæèì íàáëþäàòåëÿ:
3696 if nPlayers = 0 then
3697 begin
3698 gPlayer1 := nil;
3699 gPlayer2 := nil;
3700 end;
3702 if nPlayers >= 1 then
3703 begin
3704 // Ñîçäàíèå ïåðâîãî èãðîêà:
3705 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3706 gPlayer1Settings.Color,
3707 gPlayer1Settings.Team, False));
3708 if gPlayer1 = nil then
3709 begin
3710 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3711 Exit;
3712 end;
3714 gPlayer1.Name := gPlayer1Settings.Name;
3715 end;
3717 if nPlayers >= 2 then
3718 begin
3719 // Ñîçäàíèå âòîðîãî èãðîêà:
3720 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3721 gPlayer2Settings.Color,
3722 gPlayer2Settings.Team, False));
3723 if gPlayer2 = nil then
3724 begin
3725 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3726 Exit;
3727 end;
3729 gPlayer2.Name := gPlayer2Settings.Name;
3730 end;
3732 // Ñòàðòóåì ñåðâåð
3733 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3734 begin
3735 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3736 Exit;
3737 end;
3739 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3741 // Çàãðóçêà è çàïóñê êàðòû:
3742 if not g_Game_StartMap(Map, True) then
3743 begin
3744 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3745 Exit;
3746 end;
3748 // Íåò òî÷åê ïîÿâëåíèÿ:
3749 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3750 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3751 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3752 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3753 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3754 begin
3755 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3756 Exit;
3757 end;
3759 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3760 g_Player_Init();
3762 NetState := NET_STATE_GAME;
3763 end;
3765 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3766 var
3767 Map: String;
3768 WadName: string;
3769 Ptr: Pointer;
3770 T: Cardinal;
3771 MID: Byte;
3772 State: Byte;
3773 OuterLoop: Boolean;
3774 newResPath: string;
3775 InMsg: TMsg;
3776 begin
3777 g_Game_Free();
3779 State := 0;
3780 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3781 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3783 g_Game_ClearLoading();
3785 // Íàñòðîéêè èãðû:
3786 gGameSettings.GameType := GT_CLIENT;
3788 gCoopTotalMonstersKilled := 0;
3789 gCoopTotalSecretsFound := 0;
3790 gCoopTotalMonsters := 0;
3791 gCoopTotalSecrets := 0;
3792 gAimLine := False;
3793 gShowMap := False;
3795 g_Game_ExecuteEvent('ongamestart');
3797 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3798 g_Game_SetupScreenSize();
3800 NetState := NET_STATE_AUTH;
3802 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3803 // Ñòàðòóåì êëèåíò
3804 if not g_Net_Connect(Addr, Port) then
3805 begin
3806 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3807 NetState := NET_STATE_NONE;
3808 Exit;
3809 end;
3811 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3812 MC_SEND_Info(PW);
3813 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3815 OuterLoop := True;
3816 while OuterLoop do
3817 begin
3818 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3819 begin
3820 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3821 begin
3822 Ptr := NetEvent.packet^.data;
3823 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
3824 continue;
3826 MID := InMsg.ReadByte();
3828 if (MID = NET_MSG_INFO) and (State = 0) then
3829 begin
3830 NetMyID := InMsg.ReadByte();
3831 NetPlrUID1 := InMsg.ReadWord();
3833 WadName := InMsg.ReadString();
3834 Map := InMsg.ReadString();
3836 gWADHash := InMsg.ReadMD5();
3838 gGameSettings.GameMode := InMsg.ReadByte();
3839 gSwitchGameMode := gGameSettings.GameMode;
3840 gGameSettings.GoalLimit := InMsg.ReadWord();
3841 gGameSettings.TimeLimit := InMsg.ReadWord();
3842 gGameSettings.MaxLives := InMsg.ReadByte();
3843 gGameSettings.Options := InMsg.ReadLongWord();
3844 T := InMsg.ReadLongWord();
3846 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3847 if newResPath = '' then
3848 begin
3849 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3850 newResPath := g_Res_DownloadWAD(WadName);
3851 if newResPath = '' then
3852 begin
3853 g_FatalError(_lc[I_NET_ERR_HASH]);
3854 enet_packet_destroy(NetEvent.packet);
3855 NetState := NET_STATE_NONE;
3856 Exit;
3857 end;
3858 end;
3859 newResPath := ExtractRelativePath(MapsDir, newResPath);
3861 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3862 gPlayer1Settings.Color,
3863 gPlayer1Settings.Team, False));
3865 if gPlayer1 = nil then
3866 begin
3867 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3869 enet_packet_destroy(NetEvent.packet);
3870 NetState := NET_STATE_NONE;
3871 Exit;
3872 end;
3874 gPlayer1.Name := gPlayer1Settings.Name;
3875 gPlayer1.UID := NetPlrUID1;
3876 gPlayer1.Reset(True);
3878 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3879 begin
3880 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3882 enet_packet_destroy(NetEvent.packet);
3883 NetState := NET_STATE_NONE;
3884 Exit;
3885 end;
3887 gTime := T;
3889 State := 1;
3890 OuterLoop := False;
3891 enet_packet_destroy(NetEvent.packet);
3892 break;
3893 end
3894 else
3895 enet_packet_destroy(NetEvent.packet);
3896 end
3897 else
3898 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3899 begin
3900 State := 0;
3901 if (NetEvent.data <= NET_DISC_MAX) then
3902 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3903 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3904 OuterLoop := False;
3905 Break;
3906 end;
3907 end;
3909 ProcessLoading();
3911 e_PollInput();
3913 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3914 begin
3915 State := 0;
3916 break;
3917 end;
3918 end;
3920 if State <> 1 then
3921 begin
3922 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3923 NetState := NET_STATE_NONE;
3924 Exit;
3925 end;
3927 gLMSRespawn := LMS_RESPAWN_NONE;
3928 gLMSRespawnTime := 0;
3930 g_Player_Init();
3931 NetState := NET_STATE_GAME;
3932 MC_SEND_FullStateRequest;
3933 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3934 end;
3936 procedure g_Game_SaveOptions();
3937 begin
3938 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3939 end;
3941 procedure g_Game_ChangeMap(MapPath: String);
3942 var
3943 Force: Boolean;
3944 begin
3945 g_Game_ClearLoading();
3947 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3948 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3949 if gExitByTrigger then
3950 begin
3951 Force := False;
3952 gExitByTrigger := False;
3953 end;
3954 if not g_Game_StartMap(MapPath, Force) then
3955 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3956 end;
3958 procedure g_Game_Restart();
3959 var
3960 Map: string;
3961 begin
3962 if g_Game_IsClient then
3963 Exit;
3964 map := g_ExtractFileName(gMapInfo.Map);
3966 MessageTime := 0;
3967 gGameOn := False;
3968 g_Game_ClearLoading();
3969 g_Game_StartMap(Map, True);
3970 end;
3972 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
3973 var
3974 NewWAD, ResName: String;
3975 I: Integer;
3976 begin
3977 g_Map_Free();
3978 g_Player_RemoveAllCorpses();
3980 if (not g_Game_IsClient) and
3981 (gSwitchGameMode <> gGameSettings.GameMode) and
3982 (gGameSettings.GameMode <> GM_SINGLE) then
3983 begin
3984 if gSwitchGameMode = GM_CTF then
3985 gGameSettings.MaxLives := 0;
3986 gGameSettings.GameMode := gSwitchGameMode;
3987 Force := True;
3988 end else
3989 gSwitchGameMode := gGameSettings.GameMode;
3991 g_Player_ResetTeams();
3993 if Pos(':\', Map) > 0 then
3994 begin
3995 NewWAD := g_ExtractWadName(Map);
3996 ResName := g_ExtractFileName(Map);
3997 if g_Game_IsServer then
3998 begin
3999 gWADHash := MD5File(MapsDir + NewWAD);
4000 g_Game_LoadWAD(NewWAD);
4001 end else
4002 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4003 g_Game_ClientWAD(NewWAD, gWADHash);
4004 end else
4005 ResName := Map;
4007 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4008 if Result then
4009 begin
4010 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4012 gState := STATE_NONE;
4013 g_ActiveWindow := nil;
4014 gGameOn := True;
4016 DisableCheats();
4017 ResetTimer();
4019 if gGameSettings.GameMode = GM_CTF then
4020 begin
4021 g_Map_ResetFlag(FLAG_RED);
4022 g_Map_ResetFlag(FLAG_BLUE);
4023 // CTF, à ôëàãîâ íåò:
4024 if not g_Map_HaveFlagPoints() then
4025 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4026 end;
4027 end
4028 else
4029 begin
4030 gState := STATE_MENU;
4031 gGameOn := False;
4032 end;
4034 gExit := 0;
4035 gPause := False;
4036 gTime := 0;
4037 NetTimeToUpdate := 1;
4038 NetTimeToReliable := 0;
4039 NetTimeToMaster := NetMasterRate;
4040 gLMSRespawn := LMS_RESPAWN_NONE;
4041 gLMSRespawnTime := 0;
4042 gMissionFailed := False;
4043 gNextMap := '';
4045 gCoopMonstersKilled := 0;
4046 gCoopSecretsFound := 0;
4048 gVoteInProgress := False;
4049 gVotePassed := False;
4050 gVoteCount := 0;
4051 gVoted := False;
4053 gStatsOff := False;
4055 if not gGameOn then Exit;
4057 g_Game_SpectateCenterView();
4059 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4060 begin
4061 gLMSRespawn := LMS_RESPAWN_WARMUP;
4062 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4063 gLMSSoftSpawn := True;
4064 if NetMode = NET_SERVER then
4065 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4066 else
4067 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4068 end;
4070 if NetMode = NET_SERVER then
4071 begin
4072 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4074 // Ìàñòåðñåðâåð
4075 if NetUseMaster then
4076 begin
4077 if (NetMHost = nil) or (NetMPeer = nil) then
4078 if not g_Net_Slist_Connect then
4079 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4081 g_Net_Slist_Update;
4082 end;
4084 if NetClients <> nil then
4085 for I := 0 to High(NetClients) do
4086 if NetClients[I].Used then
4087 begin
4088 NetClients[I].Voted := False;
4089 if NetClients[I].RequestedFullUpdate then
4090 begin
4091 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4092 NetClients[I].RequestedFullUpdate := False;
4093 end;
4094 end;
4096 g_Net_UnbanNonPermHosts();
4097 end;
4099 if gLastMap then
4100 begin
4101 gCoopTotalMonstersKilled := 0;
4102 gCoopTotalSecretsFound := 0;
4103 gCoopTotalMonsters := 0;
4104 gCoopTotalSecrets := 0;
4105 gLastMap := False;
4106 end;
4108 g_Game_ExecuteEvent('onmapstart');
4109 end;
4111 procedure SetFirstLevel();
4112 begin
4113 gNextMap := '';
4115 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4116 if MapList = nil then
4117 Exit;
4119 SortSArray(MapList);
4120 gNextMap := MapList[Low(MapList)];
4122 MapList := nil;
4123 end;
4125 procedure g_Game_ExitLevel(Map: Char16);
4126 begin
4127 gNextMap := Map;
4129 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4130 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4131 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4132 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4134 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4135 if gGameSettings.GameType = GT_SINGLE then
4136 gExit := EXIT_ENDLEVELSINGLE
4137 else // Âûøëè â âûõîä â Ñâîåé èãðå
4138 begin
4139 gExit := EXIT_ENDLEVELCUSTOM;
4140 if gGameSettings.GameMode = GM_COOP then
4141 g_Player_RememberAll;
4143 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4144 begin
4145 gLastMap := True;
4146 if gGameSettings.GameMode = GM_COOP then
4147 gStatsOff := True;
4149 gStatsPressed := True;
4150 gNextMap := 'MAP01';
4152 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4153 g_Game_NextLevel;
4155 if g_Game_IsNet then
4156 begin
4157 MH_SEND_GameStats();
4158 MH_SEND_CoopStats();
4159 end;
4160 end;
4161 end;
4162 end;
4164 procedure g_Game_RestartLevel();
4165 var
4166 Map: string;
4167 begin
4168 if gGameSettings.GameMode = GM_SINGLE then
4169 begin
4170 g_Game_Restart();
4171 Exit;
4172 end;
4173 gExit := EXIT_ENDLEVELCUSTOM;
4174 Map := g_ExtractFileName(gMapInfo.Map);
4175 gNextMap := Map;
4176 end;
4178 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4179 var
4180 gWAD: String;
4181 begin
4182 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4183 Exit;
4184 if not g_Game_IsClient then
4185 Exit;
4186 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4187 if gWAD = '' then
4188 begin
4189 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4190 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4191 if gWAD = '' then
4192 begin
4193 g_Game_Free();
4194 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4195 Exit;
4196 end;
4197 end;
4198 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4199 g_Game_LoadWAD(NewWAD);
4200 end;
4202 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4203 var
4204 i, n, nb, nr: Integer;
4206 function monRespawn (mon: TMonster): Boolean;
4207 begin
4208 result := false; // don't stop
4209 if not mon.FNoRespawn then mon.Respawn();
4210 end;
4212 begin
4213 if not g_Game_IsServer then Exit;
4214 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4215 gLMSRespawn := LMS_RESPAWN_NONE;
4216 gLMSRespawnTime := 0;
4217 MessageTime := 0;
4219 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4220 begin
4221 gMissionFailed := True;
4222 g_Game_RestartLevel;
4223 Exit;
4224 end;
4226 n := 0; nb := 0; nr := 0;
4227 for i := Low(gPlayers) to High(gPlayers) do
4228 if (gPlayers[i] <> nil) and
4229 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4230 (gPlayers[i] is TBot)) then
4231 begin
4232 Inc(n);
4233 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4234 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4235 end;
4237 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4238 begin
4239 // wait a second until the fuckers finally decide to join
4240 gLMSRespawn := LMS_RESPAWN_WARMUP;
4241 gLMSRespawnTime := gTime + 1000;
4242 gLMSSoftSpawn := NoMapRestart;
4243 Exit;
4244 end;
4246 g_Player_RemoveAllCorpses;
4247 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4248 if g_Game_IsNet then
4249 MH_SEND_GameEvent(NET_EV_LMS_START);
4251 for i := Low(gPlayers) to High(gPlayers) do
4252 begin
4253 if gPlayers[i] = nil then continue;
4254 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4255 // don't touch normal spectators
4256 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4257 begin
4258 gPlayers[i].FNoRespawn := True;
4259 gPlayers[i].Lives := 0;
4260 if g_Game_IsNet then
4261 MH_SEND_PlayerStats(gPlayers[I].UID);
4262 continue;
4263 end;
4264 gPlayers[i].FNoRespawn := False;
4265 gPlayers[i].Lives := gGameSettings.MaxLives;
4266 gPlayers[i].Respawn(False, True);
4267 if gGameSettings.GameMode = GM_COOP then
4268 begin
4269 gPlayers[i].Frags := 0;
4270 gPlayers[i].RecallState;
4271 end;
4272 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4273 gPlayer1 := g_Player_Get(gLMSPID1);
4274 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4275 gPlayer2 := g_Player_Get(gLMSPID2);
4276 end;
4278 g_Items_RestartRound();
4281 g_Mons_ForEach(monRespawn);
4283 gLMSSoftSpawn := False;
4284 end;
4286 function g_Game_GetFirstMap(WAD: String): String;
4287 begin
4288 Result := '';
4290 MapList := g_Map_GetMapsList(WAD);
4291 if MapList = nil then
4292 Exit;
4294 SortSArray(MapList);
4295 Result := MapList[Low(MapList)];
4297 if not g_Map_Exist(WAD + ':\' + Result) then
4298 Result := '';
4300 MapList := nil;
4301 end;
4303 function g_Game_GetNextMap(): String;
4304 var
4305 I: Integer;
4306 Map: string;
4307 begin
4308 Result := '';
4310 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4311 if MapList = nil then
4312 Exit;
4314 Map := g_ExtractFileName(gMapInfo.Map);
4316 SortSArray(MapList);
4317 MapIndex := -255;
4318 for I := Low(MapList) to High(MapList) do
4319 if Map = MapList[I] then
4320 begin
4321 MapIndex := I;
4322 Break;
4323 end;
4325 if MapIndex <> -255 then
4326 begin
4327 if MapIndex = High(MapList) then
4328 Result := MapList[Low(MapList)]
4329 else
4330 Result := MapList[MapIndex + 1];
4332 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4333 end;
4335 MapList := nil;
4336 end;
4338 procedure g_Game_NextLevel();
4339 begin
4340 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4341 gExit := EXIT_ENDLEVELCUSTOM
4342 else
4343 begin
4344 gExit := EXIT_ENDLEVELSINGLE;
4345 Exit;
4346 end;
4348 if gNextMap <> '' then Exit;
4349 gNextMap := g_Game_GetNextMap();
4350 end;
4352 function g_Game_IsTestMap(): Boolean;
4353 begin
4354 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4355 end;
4357 procedure g_Game_DeleteTestMap();
4358 var
4359 a: Integer;
4360 MapName: Char16;
4361 WadName: string;
4363 WAD: TWADFile;
4364 MapList: SArray;
4365 time: Integer;
4367 begin
4368 a := Pos('.wad:\', gMapToDelete);
4369 if a = 0 then
4370 Exit;
4372 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4373 WadName := Copy(gMapToDelete, 1, a + 3);
4374 Delete(gMapToDelete, 1, a + 5);
4375 gMapToDelete := UpperCase(gMapToDelete);
4376 MapName := '';
4377 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4380 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4381 if MapName <> TEST_MAP_NAME then
4382 Exit;
4384 if not gTempDelete then
4385 begin
4386 time := g_GetFileTime(WadName);
4387 WAD := TWADFile.Create();
4389 // ×èòàåì Wad-ôàéë:
4390 if not WAD.ReadFile(WadName) then
4391 begin // Íåò òàêîãî WAD-ôàéëà
4392 WAD.Free();
4393 Exit;
4394 end;
4396 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4397 WAD.CreateImage();
4398 MapList := WAD.GetResourcesList('');
4400 if MapList <> nil then
4401 for a := 0 to High(MapList) do
4402 if MapList[a] = MapName then
4403 begin
4404 // Óäàëÿåì è ñîõðàíÿåì:
4405 WAD.RemoveResource('', MapName);
4406 WAD.SaveTo(WadName);
4407 Break;
4408 end;
4410 WAD.Free();
4411 g_SetFileTime(WadName, time);
4412 end else
4414 if gTempDelete then DeleteFile(WadName);
4415 end;
4417 procedure GameCVars(P: SArray);
4418 var
4419 a, b: Integer;
4420 stat: TPlayerStatArray;
4421 cmd, s: string;
4422 config: TConfig;
4423 begin
4424 stat := nil;
4425 cmd := LowerCase(P[0]);
4426 if cmd = 'r_showfps' then
4427 begin
4428 if (Length(P) > 1) and
4429 ((P[1] = '1') or (P[1] = '0')) then
4430 gShowFPS := (P[1][1] = '1');
4432 if gShowFPS then
4433 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4434 else
4435 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4436 end
4437 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4438 begin
4439 with gGameSettings do
4440 begin
4441 if (Length(P) > 1) and
4442 ((P[1] = '1') or (P[1] = '0')) then
4443 begin
4444 if (P[1][1] = '1') then
4445 Options := Options or GAME_OPTION_TEAMDAMAGE
4446 else
4447 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4448 end;
4450 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4451 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4452 else
4453 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4455 if g_Game_IsNet then MH_SEND_GameSettings;
4456 end;
4457 end
4458 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4459 begin
4460 with gGameSettings do
4461 begin
4462 if (Length(P) > 1) and
4463 ((P[1] = '1') or (P[1] = '0')) then
4464 begin
4465 if (P[1][1] = '1') then
4466 Options := Options or GAME_OPTION_WEAPONSTAY
4467 else
4468 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4469 end;
4471 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4472 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4473 else
4474 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4476 if g_Game_IsNet then MH_SEND_GameSettings;
4477 end;
4478 end
4479 else if cmd = 'g_gamemode' then
4480 begin
4481 a := g_Game_TextToMode(P[1]);
4482 if a = GM_SINGLE then a := GM_COOP;
4483 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4484 begin
4485 gSwitchGameMode := a;
4486 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4487 (gState = STATE_INTERSINGLE) then
4488 gSwitchGameMode := GM_SINGLE;
4489 if not gGameOn then
4490 gGameSettings.GameMode := gSwitchGameMode;
4491 end;
4492 if gSwitchGameMode = gGameSettings.GameMode then
4493 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4494 [g_Game_ModeToText(gGameSettings.GameMode)]))
4495 else
4496 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4497 [g_Game_ModeToText(gGameSettings.GameMode),
4498 g_Game_ModeToText(gSwitchGameMode)]));
4499 end
4500 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4501 begin
4502 with gGameSettings do
4503 begin
4504 if (Length(P) > 1) and
4505 ((P[1] = '1') or (P[1] = '0')) then
4506 begin
4507 if (P[1][1] = '1') then
4508 Options := Options or GAME_OPTION_ALLOWEXIT
4509 else
4510 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4511 end;
4513 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4514 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4515 else
4516 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4517 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4519 if g_Game_IsNet then MH_SEND_GameSettings;
4520 end;
4521 end
4522 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4523 begin
4524 with gGameSettings do
4525 begin
4526 if (Length(P) > 1) and
4527 ((P[1] = '1') or (P[1] = '0')) then
4528 begin
4529 if (P[1][1] = '1') then
4530 Options := Options or GAME_OPTION_MONSTERS
4531 else
4532 Options := Options and (not GAME_OPTION_MONSTERS);
4533 end;
4535 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4536 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4537 else
4538 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4539 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4541 if g_Game_IsNet then MH_SEND_GameSettings;
4542 end;
4543 end
4544 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4545 begin
4546 with gGameSettings do
4547 begin
4548 if (Length(P) > 1) and
4549 ((P[1] = '1') or (P[1] = '0')) then
4550 begin
4551 if (P[1][1] = '1') then
4552 Options := Options or GAME_OPTION_BOTVSPLAYER
4553 else
4554 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4555 end;
4557 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4558 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4559 else
4560 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4562 if g_Game_IsNet then MH_SEND_GameSettings;
4563 end;
4564 end
4565 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4566 begin
4567 with gGameSettings do
4568 begin
4569 if (Length(P) > 1) and
4570 ((P[1] = '1') or (P[1] = '0')) then
4571 begin
4572 if (P[1][1] = '1') then
4573 Options := Options or GAME_OPTION_BOTVSMONSTER
4574 else
4575 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4576 end;
4578 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4579 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4580 else
4581 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4583 if g_Game_IsNet then MH_SEND_GameSettings;
4584 end;
4585 end
4586 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4587 begin
4588 if Length(P) > 1 then
4589 begin
4590 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4591 gGameSettings.WarmupTime := 30
4592 else
4593 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4594 end;
4596 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4597 [gGameSettings.WarmupTime]));
4598 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4599 end
4600 else if cmd = 'net_interp' then
4601 begin
4602 if (Length(P) > 1) then
4603 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4605 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4606 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4607 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4608 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4609 config.Free();
4610 end
4611 else if cmd = 'net_forceplayerupdate' then
4612 begin
4613 if (Length(P) > 1) and
4614 ((P[1] = '1') or (P[1] = '0')) then
4615 NetForcePlayerUpdate := (P[1][1] = '1');
4617 if NetForcePlayerUpdate then
4618 g_Console_Add('net_forceplayerupdate = 1')
4619 else
4620 g_Console_Add('net_forceplayerupdate = 0');
4621 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4622 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4623 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4624 config.Free();
4625 end
4626 else if cmd = 'net_predictself' then
4627 begin
4628 if (Length(P) > 1) and
4629 ((P[1] = '1') or (P[1] = '0')) then
4630 NetPredictSelf := (P[1][1] = '1');
4632 if NetPredictSelf then
4633 g_Console_Add('net_predictself = 1')
4634 else
4635 g_Console_Add('net_predictself = 0');
4636 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4637 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4638 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4639 config.Free();
4640 end
4641 else if cmd = 'sv_name' then
4642 begin
4643 if (Length(P) > 1) and (Length(P[1]) > 0) then
4644 begin
4645 NetServerName := P[1];
4646 if Length(NetServerName) > 64 then
4647 SetLength(NetServerName, 64);
4648 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4649 g_Net_Slist_Update;
4650 end;
4652 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4653 end
4654 else if cmd = 'sv_passwd' then
4655 begin
4656 if (Length(P) > 1) and (Length(P[1]) > 0) then
4657 begin
4658 NetPassword := P[1];
4659 if Length(NetPassword) > 24 then
4660 SetLength(NetPassword, 24);
4661 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4662 g_Net_Slist_Update;
4663 end;
4665 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4666 end
4667 else if cmd = 'sv_maxplrs' then
4668 begin
4669 if (Length(P) > 1) then
4670 begin
4671 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4672 if g_Game_IsServer and g_Game_IsNet then
4673 begin
4674 b := 0;
4675 for a := 0 to High(NetClients) do
4676 if NetClients[a].Used then
4677 begin
4678 Inc(b);
4679 if b > NetMaxClients then
4680 begin
4681 s := g_Player_Get(NetClients[a].Player).Name;
4682 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4683 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4684 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4685 end;
4686 end;
4687 if NetUseMaster then
4688 g_Net_Slist_Update;
4689 end;
4690 end;
4692 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4693 end
4694 else if cmd = 'sv_public' then
4695 begin
4696 if (Length(P) > 1) then
4697 begin
4698 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4699 if g_Game_IsServer and g_Game_IsNet then
4700 if NetUseMaster then
4701 begin
4702 if NetMPeer = nil then
4703 if not g_Net_Slist_Connect() then
4704 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4705 g_Net_Slist_Update();
4706 end
4707 else
4708 if NetMPeer <> nil then
4709 g_Net_Slist_Disconnect();
4710 end;
4712 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4713 end
4714 else if cmd = 'sv_intertime' then
4715 begin
4716 if (Length(P) > 1) then
4717 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4719 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4720 end
4721 else if cmd = 'p1_name' then
4722 begin
4723 if (Length(P) > 1) and gGameOn then
4724 begin
4725 if g_Game_IsClient then
4726 begin
4727 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4728 MC_SEND_PlayerSettings;
4729 end
4730 else
4731 if gPlayer1 <> nil then
4732 begin
4733 gPlayer1.Name := b_Text_Unformat(P[1]);
4734 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4735 end
4736 else
4737 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4738 end;
4739 end
4740 else if cmd = 'p2_name' then
4741 begin
4742 if (Length(P) > 1) and gGameOn then
4743 begin
4744 if g_Game_IsClient then
4745 begin
4746 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4747 MC_SEND_PlayerSettings;
4748 end
4749 else
4750 if gPlayer2 <> nil then
4751 begin
4752 gPlayer2.Name := b_Text_Unformat(P[1]);
4753 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4754 end
4755 else
4756 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4757 end;
4758 end
4759 else if cmd = 'p1_color' then
4760 begin
4761 if Length(P) > 3 then
4762 if g_Game_IsClient then
4763 begin
4764 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4765 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4766 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4767 MC_SEND_PlayerSettings;
4768 end
4769 else
4770 if gPlayer1 <> nil then
4771 begin
4772 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4773 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4774 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4775 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4776 end
4777 else
4778 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4779 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4780 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4781 end
4782 else if (cmd = 'p2_color') and not g_Game_IsNet then
4783 begin
4784 if Length(P) > 3 then
4785 if g_Game_IsClient then
4786 begin
4787 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4788 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4789 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4790 MC_SEND_PlayerSettings;
4791 end
4792 else
4793 if gPlayer2 <> nil then
4794 begin
4795 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4796 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4797 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4798 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4799 end
4800 else
4801 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4802 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4803 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4804 end
4805 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4806 begin
4807 if cmd = 'r_showtime' then
4808 begin
4809 if (Length(P) > 1) and
4810 ((P[1] = '1') or (P[1] = '0')) then
4811 gShowTime := (P[1][1] = '1');
4813 if gShowTime then
4814 g_Console_Add(_lc[I_MSG_TIME_ON])
4815 else
4816 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4817 end
4818 else if cmd = 'r_showscore' then
4819 begin
4820 if (Length(P) > 1) and
4821 ((P[1] = '1') or (P[1] = '0')) then
4822 gShowGoals := (P[1][1] = '1');
4824 if gShowGoals then
4825 g_Console_Add(_lc[I_MSG_SCORE_ON])
4826 else
4827 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4828 end
4829 else if cmd = 'r_showstat' then
4830 begin
4831 if (Length(P) > 1) and
4832 ((P[1] = '1') or (P[1] = '0')) then
4833 gShowStat := (P[1][1] = '1');
4835 if gShowStat then
4836 g_Console_Add(_lc[I_MSG_STATS_ON])
4837 else
4838 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4839 end
4840 else if cmd = 'r_showkillmsg' then
4841 begin
4842 if (Length(P) > 1) and
4843 ((P[1] = '1') or (P[1] = '0')) then
4844 gShowKillMsg := (P[1][1] = '1');
4846 if gShowKillMsg then
4847 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4848 else
4849 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4850 end
4851 else if cmd = 'r_showlives' then
4852 begin
4853 if (Length(P) > 1) and
4854 ((P[1] = '1') or (P[1] = '0')) then
4855 gShowLives := (P[1][1] = '1');
4857 if gShowLives then
4858 g_Console_Add(_lc[I_MSG_LIVES_ON])
4859 else
4860 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4861 end
4862 else if cmd = 'r_showspect' then
4863 begin
4864 if (Length(P) > 1) and
4865 ((P[1] = '1') or (P[1] = '0')) then
4866 gSpectHUD := (P[1][1] = '1');
4868 if gSpectHUD then
4869 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4870 else
4871 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4872 end
4873 else if cmd = 'r_showping' then
4874 begin
4875 if (Length(P) > 1) and
4876 ((P[1] = '1') or (P[1] = '0')) then
4877 gShowPing := (P[1][1] = '1');
4879 if gShowPing then
4880 g_Console_Add(_lc[I_MSG_PING_ON])
4881 else
4882 g_Console_Add(_lc[I_MSG_PING_OFF]);
4883 end
4884 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4885 begin
4886 if Length(P) > 1 then
4887 begin
4888 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4889 gGameSettings.GoalLimit := 0
4890 else
4891 begin
4892 b := 0;
4894 if gGameSettings.GameMode = GM_DM then
4895 begin // DM
4896 stat := g_Player_GetStats();
4897 if stat <> nil then
4898 for a := 0 to High(stat) do
4899 if stat[a].Frags > b then
4900 b := stat[a].Frags;
4901 end
4902 else // TDM/CTF
4903 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4905 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4906 end;
4908 if g_Game_IsNet then MH_SEND_GameSettings;
4909 end;
4911 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4912 end
4913 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4914 begin
4915 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4916 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4918 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4919 [gGameSettings.TimeLimit div 3600,
4920 (gGameSettings.TimeLimit div 60) mod 60,
4921 gGameSettings.TimeLimit mod 60]));
4922 if g_Game_IsNet then MH_SEND_GameSettings;
4923 end
4924 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4925 begin
4926 if Length(P) > 1 then
4927 begin
4928 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4929 gGameSettings.MaxLives := 0
4930 else
4931 begin
4932 b := 0;
4933 stat := g_Player_GetStats();
4934 if stat <> nil then
4935 for a := 0 to High(stat) do
4936 if stat[a].Lives > b then
4937 b := stat[a].Lives;
4938 gGameSettings.MaxLives :=
4939 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4940 end;
4941 end;
4943 g_Console_Add(Format(_lc[I_MSG_LIVES],
4944 [gGameSettings.MaxLives]));
4945 if g_Game_IsNet then MH_SEND_GameSettings;
4946 end;
4947 end;
4948 end;
4950 // profiler console commands
4951 procedure ProfilerCommands (P: SArray);
4952 var
4953 cmd: string;
4955 function getBool (idx: Integer): Integer;
4956 begin
4957 if (idx < 0) or (idx > High(P)) then begin result := -1; exit; end;
4958 result := 0;
4959 if (P[idx] = '1') or (P[idx] = 'on') or (P[idx] = 'true') or (P[idx] = 'tan') then result := 1;
4960 end;
4962 procedure binaryFlag (var flag: Boolean; msg: string);
4963 begin
4964 if (Length(p) > 2) then
4965 begin
4966 g_Console_Add('too many arguments to '''+P[0]+'''');
4967 end
4968 else
4969 begin
4970 case getBool(1) of
4971 -1: begin end;
4972 0: flag := false;
4973 1: flag := true;
4974 end;
4975 if flag then g_Console_Add(msg+': tan') else g_Console_Add(msg+': ona');
4976 end;
4977 end;
4979 begin
4980 //if not gDebugMode then exit;
4981 cmd := LowerCase(P[0]);
4983 if (cmd = 'pf_draw_frame') then begin binaryFlag(g_profile_frame_draw, 'render profiles'); exit; end;
4984 if (cmd = 'pf_update_frame') then begin binaryFlag(g_profile_frame_update, 'update profiles (not yet)'); exit; end;
4985 if (cmd = 'pf_coldet') then begin binaryFlag(g_profile_collision, 'coldet profiles'); exit; end;
4986 if (cmd = 'pf_los') then begin binaryFlag(g_profile_los, 'monster LOS profiles'); exit; end;
4987 if (cmd = 'r_sq_draw') then begin binaryFlag(gdbg_map_use_accel_render, 'accelerated rendering'); exit; end;
4988 if (cmd = 'cd_sq_enabled') then begin binaryFlag(gdbg_map_use_accel_coldet, 'accelerated map coldet'); exit; end;
4989 if (cmd = 'mon_sq_enabled') then begin binaryFlag(gmon_debug_use_sqaccel, 'accelerated monster coldet'); exit; end;
4990 if (cmd = 'wtrace_sq_enabled') then begin binaryFlag(gwep_debug_fast_trace, 'accelerated weapon hitscan'); exit; end;
4991 if (cmd = 'pr_enabled') then begin binaryFlag(gpart_dbg_enabled, 'particles'); exit; end;
4992 if (cmd = 'pr_phys_enabled') then begin binaryFlag(gpart_dbg_phys_enabled, 'particle physics'); exit; end;
4993 if (cmd = 'los_enabled') then begin binaryFlag(gmon_dbg_los_enabled, 'LOS calculations'); exit; end;
4995 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
4996 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then exit;
4998 if (cmd = 'mon_think') then begin binaryFlag(gmon_debug_think, 'monster thinking'); exit; end;
4999 if (cmd = 'dbg_holmes') then begin binaryFlag(g_holmes_enabled, 'Holmes'); exit; end;
5000 end;
5003 procedure DebugCommands(P: SArray);
5004 var
5005 a, b: Integer;
5006 cmd: string;
5007 //pt: TPoint;
5008 mon: TMonster;
5009 begin
5010 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5011 if gDebugMode then
5012 begin
5013 cmd := LowerCase(P[0]);
5014 if cmd = 'd_window' then
5015 begin
5016 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5017 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5018 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5019 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5020 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5021 end
5022 else if cmd = 'd_sounds' then
5023 begin
5024 if (Length(P) > 1) and
5025 ((P[1] = '1') or (P[1] = '0')) then
5026 g_Debug_Sounds := (P[1][1] = '1');
5028 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5029 end
5030 else if cmd = 'd_frames' then
5031 begin
5032 if (Length(P) > 1) and
5033 ((P[1] = '1') or (P[1] = '0')) then
5034 g_Debug_Frames := (P[1][1] = '1');
5036 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5037 end
5038 else if cmd = 'd_winmsg' then
5039 begin
5040 if (Length(P) > 1) and
5041 ((P[1] = '1') or (P[1] = '0')) then
5042 g_Debug_WinMsgs := (P[1][1] = '1');
5044 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5045 end
5046 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5047 begin
5048 if (Length(P) > 1) and
5049 ((P[1] = '1') or (P[1] = '0')) then
5050 g_Debug_MonsterOff := (P[1][1] = '1');
5052 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5053 end
5054 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5055 begin
5056 if Length(P) > 1 then
5057 case P[1][1] of
5058 '0': g_debug_BotAIOff := 0;
5059 '1': g_debug_BotAIOff := 1;
5060 '2': g_debug_BotAIOff := 2;
5061 '3': g_debug_BotAIOff := 3;
5062 end;
5064 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5065 end
5066 else if cmd = 'd_monster' then
5067 begin
5068 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
5069 if Length(P) < 2 then
5070 begin
5071 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5072 g_Console_Add('ID | Name');
5073 for b := MONSTER_DEMON to MONSTER_MAN do
5074 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
5075 end else
5076 begin
5077 a := StrToIntDef(P[1], 0);
5078 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5079 a := g_Monsters_GetIDByName(P[1]);
5081 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5082 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5083 else
5084 begin
5085 with gPlayer1.Obj do
5086 begin
5087 mon := g_Monsters_Create(a,
5088 X + Rect.X + (Rect.Width div 2),
5089 Y + Rect.Y + Rect.Height,
5090 gPlayer1.Direction, True);
5091 end;
5092 if (Length(P) > 2) and (mon <> nil) then
5093 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5094 end;
5095 end;
5096 end
5097 else if (cmd = 'd_health') then
5098 begin
5099 if (Length(P) > 1) and
5100 ((P[1] = '1') or (P[1] = '0')) then
5101 g_debug_HealthBar := (P[1][1] = '1');
5103 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5104 end
5105 else if (cmd = 'd_player') then
5106 begin
5107 if (Length(P) > 1) and
5108 ((P[1] = '1') or (P[1] = '0')) then
5109 g_debug_Player := (P[1][1] = '1');
5111 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5112 end
5113 else if (cmd = 'd_joy') then
5114 begin
5115 for a := 1 to 8 do
5116 g_Console_Add(e_JoystickStateToString(a));
5117 end;
5118 end
5119 else
5120 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5121 end;
5124 procedure GameCheats(P: SArray);
5125 var
5126 cmd: string;
5127 f, a: Integer;
5128 plr: TPlayer;
5129 begin
5130 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5131 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5132 begin
5133 g_Console_Add('not available');
5134 exit;
5135 end;
5136 plr := gPlayer1;
5137 if plr = nil then
5138 begin
5139 g_Console_Add('where is the player?!');
5140 exit;
5141 end;
5142 cmd := LowerCase(P[0]);
5143 // god
5144 if cmd = 'god' then
5145 begin
5146 plr.GodMode := not plr.GodMode;
5147 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5148 exit;
5149 end;
5150 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5151 if cmd = 'give' then
5152 begin
5153 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5154 for f := 1 to High(P) do
5155 begin
5156 cmd := LowerCase(P[f]);
5157 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5158 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5159 if cmd = 'exit' then
5160 begin
5161 if gTriggers <> nil then
5162 begin
5163 for a := 0 to High(gTriggers) do
5164 begin
5165 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5166 begin
5167 g_Console_Add('player left the map');
5168 gExitByTrigger := True;
5169 g_Game_ExitLevel(gTriggers[a].Data.MapName);
5170 break;
5171 end;
5172 end;
5173 end;
5174 continue;
5175 end;
5177 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5178 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5179 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5180 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5181 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5183 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5184 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5186 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5187 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;
5189 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5190 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5192 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5193 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5195 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5196 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5198 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5199 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5200 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5202 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5203 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5204 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5205 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;
5206 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5207 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5209 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;
5210 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;
5211 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;
5212 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;
5213 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;
5214 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;
5216 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5217 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;
5219 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;
5220 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;
5222 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5224 if cmd = 'ammo' then
5225 begin
5226 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5227 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5228 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5229 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5230 plr.GiveItem(ITEM_AMMO_FUELCAN);
5231 g_Console_Add('player got some ammo');
5232 continue;
5233 end;
5235 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5236 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5238 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5239 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5241 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5242 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5244 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5245 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5247 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5249 if cmd = 'weapons' then
5250 begin
5251 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5252 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5253 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5254 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5255 plr.GiveItem(ITEM_WEAPON_PLASMA);
5256 plr.GiveItem(ITEM_WEAPON_BFG);
5257 g_Console_Add('player got weapons');
5258 continue;
5259 end;
5261 if cmd = 'keys' then
5262 begin
5263 plr.GiveItem(ITEM_KEY_RED);
5264 plr.GiveItem(ITEM_KEY_GREEN);
5265 plr.GiveItem(ITEM_KEY_BLUE);
5266 g_Console_Add('player got all keys');
5267 continue;
5268 end;
5270 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5271 end;
5272 exit;
5273 end;
5274 // open
5275 if cmd = 'open' then
5276 begin
5277 g_Console_Add('player activated sesame');
5278 g_Triggers_OpenAll();
5279 exit;
5280 end;
5281 // fly
5282 if cmd = 'fly' then
5283 begin
5284 gFly := not gFly;
5285 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5286 exit;
5287 end;
5288 // noclip
5289 if cmd = 'noclip' then
5290 begin
5291 plr.SwitchNoClip;
5292 g_Console_Add('wall hardeness adjusted');
5293 exit;
5294 end;
5295 // notarget
5296 if cmd = 'notarget' then
5297 begin
5298 plr.NoTarget := not plr.NoTarget;
5299 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5300 exit;
5301 end;
5302 // noreload
5303 if cmd = 'noreload' then
5304 begin
5305 plr.NoReload := not plr.NoReload;
5306 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5307 exit;
5308 end;
5309 // speedy
5310 if cmd = 'speedy' then
5311 begin
5312 MAX_RUNVEL := 32-MAX_RUNVEL;
5313 g_Console_Add('speed adjusted');
5314 exit;
5315 end;
5316 // jumpy
5317 if cmd = 'jumpy' then
5318 begin
5319 VEL_JUMP := 30-VEL_JUMP;
5320 g_Console_Add('jump height adjusted');
5321 exit;
5322 end;
5323 // automap
5324 if cmd = 'automap' then
5325 begin
5326 gShowMap := not gShowMap;
5327 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5328 exit;
5329 end;
5330 // aimline
5331 if cmd = 'aimline' then
5332 begin
5333 gAimLine := not gAimLine;
5334 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5335 exit;
5336 end;
5337 end;
5339 procedure GameCommands(P: SArray);
5340 var
5341 a, b: Integer;
5342 s, pw: String;
5343 chstr: string;
5344 cmd: string;
5345 pl: pTNetClient = nil;
5346 plr: TPlayer;
5347 prt: Word;
5348 nm: Boolean;
5349 listen: LongWord;
5350 begin
5351 // Îáùèå êîìàíäû:
5352 cmd := LowerCase(P[0]);
5353 chstr := '';
5354 if (cmd = 'quit') or
5355 (cmd = 'exit') then
5356 begin
5357 g_Game_Free();
5358 g_Game_Quit();
5359 Exit;
5360 end
5361 else if cmd = 'pause' then
5362 begin
5363 if (g_ActiveWindow = nil) then
5364 g_Game_Pause(not gPause);
5365 end
5366 else if cmd = 'endgame' then
5367 gExit := EXIT_SIMPLE
5368 else if cmd = 'restart' then
5369 begin
5370 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5371 begin
5372 if g_Game_IsClient then
5373 begin
5374 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5375 Exit;
5376 end;
5377 g_Game_Restart();
5378 end else
5379 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5380 end
5381 else if cmd = 'kick' then
5382 begin
5383 if g_Game_IsServer then
5384 begin
5385 if Length(P) < 2 then
5386 begin
5387 g_Console_Add('kick <name>');
5388 Exit;
5389 end;
5390 if P[1] = '' then
5391 begin
5392 g_Console_Add('kick <name>');
5393 Exit;
5394 end;
5396 if g_Game_IsNet then
5397 pl := g_Net_Client_ByName(P[1]);
5398 if (pl <> nil) then
5399 begin
5400 s := g_Net_ClientName_ByID(pl^.ID);
5401 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5402 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5403 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5404 if NetUseMaster then
5405 g_Net_Slist_Update;
5406 end else if gPlayers <> nil then
5407 for a := Low(gPlayers) to High(gPlayers) do
5408 if gPlayers[a] <> nil then
5409 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5410 begin
5411 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5412 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5413 continue;
5414 gPlayers[a].Lives := 0;
5415 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5416 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5417 g_Player_Remove(gPlayers[a].UID);
5418 if NetUseMaster then
5419 g_Net_Slist_Update;
5420 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5421 g_Bot_MixNames();
5422 end;
5423 end else
5424 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5425 end
5426 else if cmd = 'kick_id' then
5427 begin
5428 if g_Game_IsServer and g_Game_IsNet then
5429 begin
5430 if Length(P) < 2 then
5431 begin
5432 g_Console_Add('kick_id <client ID>');
5433 Exit;
5434 end;
5435 if P[1] = '' then
5436 begin
5437 g_Console_Add('kick_id <client ID>');
5438 Exit;
5439 end;
5441 a := StrToIntDef(P[1], 0);
5442 if (NetClients <> nil) and (a <= High(NetClients)) then
5443 begin
5444 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5445 begin
5446 s := g_Net_ClientName_ByID(NetClients[a].ID);
5447 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5448 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5449 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5450 if NetUseMaster then
5451 g_Net_Slist_Update;
5452 end;
5453 end;
5454 end else
5455 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5456 end
5457 else if cmd = 'ban' then
5458 begin
5459 if g_Game_IsServer and g_Game_IsNet then
5460 begin
5461 if Length(P) < 2 then
5462 begin
5463 g_Console_Add('ban <name>');
5464 Exit;
5465 end;
5466 if P[1] = '' then
5467 begin
5468 g_Console_Add('ban <name>');
5469 Exit;
5470 end;
5472 pl := g_Net_Client_ByName(P[1]);
5473 if (pl <> nil) then
5474 begin
5475 s := g_Net_ClientName_ByID(pl^.ID);
5476 g_Net_BanHost(pl^.Peer^.address.host, False);
5477 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5478 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5479 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5480 if NetUseMaster then
5481 g_Net_Slist_Update;
5482 end else
5483 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5484 end else
5485 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5486 end
5487 else if cmd = 'ban_id' then
5488 begin
5489 if g_Game_IsServer and g_Game_IsNet then
5490 begin
5491 if Length(P) < 2 then
5492 begin
5493 g_Console_Add('ban_id <client ID>');
5494 Exit;
5495 end;
5496 if P[1] = '' then
5497 begin
5498 g_Console_Add('ban_id <client ID>');
5499 Exit;
5500 end;
5502 a := StrToIntDef(P[1], 0);
5503 if (NetClients <> nil) and (a <= High(NetClients)) then
5504 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5505 begin
5506 s := g_Net_ClientName_ByID(NetClients[a].ID);
5507 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5508 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5509 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5510 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5511 if NetUseMaster then
5512 g_Net_Slist_Update;
5513 end;
5514 end else
5515 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5516 end
5517 else if cmd = 'permban' then
5518 begin
5519 if g_Game_IsServer and g_Game_IsNet then
5520 begin
5521 if Length(P) < 2 then
5522 begin
5523 g_Console_Add('permban <name>');
5524 Exit;
5525 end;
5526 if P[1] = '' then
5527 begin
5528 g_Console_Add('permban <name>');
5529 Exit;
5530 end;
5532 pl := g_Net_Client_ByName(P[1]);
5533 if (pl <> nil) then
5534 begin
5535 s := g_Net_ClientName_ByID(pl^.ID);
5536 g_Net_BanHost(pl^.Peer^.address.host);
5537 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5538 g_Net_SaveBanList();
5539 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5540 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5541 if NetUseMaster then
5542 g_Net_Slist_Update;
5543 end else
5544 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5545 end else
5546 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5547 end
5548 else if cmd = 'permban_id' then
5549 begin
5550 if g_Game_IsServer and g_Game_IsNet then
5551 begin
5552 if Length(P) < 2 then
5553 begin
5554 g_Console_Add('permban_id <client ID>');
5555 Exit;
5556 end;
5557 if P[1] = '' then
5558 begin
5559 g_Console_Add('permban_id <client ID>');
5560 Exit;
5561 end;
5563 a := StrToIntDef(P[1], 0);
5564 if (NetClients <> nil) and (a <= High(NetClients)) then
5565 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5566 begin
5567 s := g_Net_ClientName_ByID(NetClients[a].ID);
5568 g_Net_BanHost(NetClients[a].Peer^.address.host);
5569 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5570 g_Net_SaveBanList();
5571 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5572 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5573 if NetUseMaster then
5574 g_Net_Slist_Update;
5575 end;
5576 end else
5577 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5578 end
5579 else if cmd = 'unban' then
5580 begin
5581 if g_Game_IsServer and g_Game_IsNet then
5582 begin
5583 if Length(P) < 2 then
5584 begin
5585 g_Console_Add('unban <IP Address>');
5586 Exit;
5587 end;
5588 if P[1] = '' then
5589 begin
5590 g_Console_Add('unban <IP Address>');
5591 Exit;
5592 end;
5594 if g_Net_UnbanHost(P[1]) then
5595 begin
5596 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5597 g_Net_SaveBanList();
5598 end else
5599 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5600 end else
5601 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5602 end
5603 else if cmd = 'clientlist' then
5604 begin
5605 if g_Game_IsServer and g_Game_IsNet then
5606 begin
5607 b := 0;
5608 if NetClients <> nil then
5609 for a := Low(NetClients) to High(NetClients) do
5610 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5611 begin
5612 plr := g_Player_Get(NetClients[a].Player);
5613 if plr = nil then continue;
5614 Inc(b);
5615 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5616 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5617 end;
5618 if b = 0 then
5619 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5620 end else
5621 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5622 end
5623 else if cmd = 'connect' then
5624 begin
5625 if (NetMode = NET_NONE) then
5626 begin
5627 if Length(P) < 2 then
5628 begin
5629 g_Console_Add('connect <IP> [port] [password]');
5630 Exit;
5631 end;
5632 if P[1] = '' then
5633 begin
5634 g_Console_Add('connect <IP> [port] [password]');
5635 Exit;
5636 end;
5638 if Length(P) > 2 then
5639 prt := StrToIntDef(P[2], 25666)
5640 else
5641 prt := 25666;
5643 if Length(P) > 3 then
5644 pw := P[3]
5645 else
5646 pw := '';
5648 g_Game_StartClient(P[1], prt, pw);
5649 end;
5650 end
5651 else if cmd = 'disconnect' then
5652 begin
5653 if (NetMode = NET_CLIENT) then
5654 g_Net_Disconnect();
5655 end
5656 else if cmd = 'reconnect' then
5657 begin
5658 if (NetMode = NET_SERVER) then
5659 Exit;
5661 if (NetMode = NET_CLIENT) then
5662 begin
5663 g_Net_Disconnect();
5664 gExit := EXIT_SIMPLE;
5665 EndGame;
5666 end;
5668 //TODO: Use last successful password to reconnect, instead of ''
5669 g_Game_StartClient(NetClientIP, NetClientPort, '');
5670 end
5671 else if (cmd = 'addbot') or
5672 (cmd = 'bot_add') then
5673 begin
5674 if Length(P) > 1 then
5675 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5676 else
5677 g_Bot_Add(TEAM_NONE, 2);
5678 end
5679 else if cmd = 'bot_addlist' then
5680 begin
5681 if Length(P) > 1 then
5682 if Length(P) = 2 then
5683 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5684 else
5685 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5686 end
5687 else if cmd = 'bot_removeall' then
5688 g_Bot_RemoveAll()
5689 else if cmd = 'chat' then
5690 begin
5691 if g_Game_IsNet then
5692 begin
5693 if Length(P) > 1 then
5694 begin
5695 for a := 1 to High(P) do
5696 chstr := chstr + P[a] + ' ';
5698 if Length(chstr) > 200 then SetLength(chstr, 200);
5700 if Length(chstr) < 1 then
5701 begin
5702 g_Console_Add('chat <text>');
5703 Exit;
5704 end;
5706 chstr := b_Text_Format(chstr);
5707 if g_Game_IsClient then
5708 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5709 else
5710 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5711 end
5712 else
5713 g_Console_Add('chat <text>');
5714 end else
5715 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5716 end
5717 else if cmd = 'teamchat' then
5718 begin
5719 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5720 begin
5721 if Length(P) > 1 then
5722 begin
5723 for a := 1 to High(P) do
5724 chstr := chstr + P[a] + ' ';
5726 if Length(chstr) > 200 then SetLength(chstr, 200);
5728 if Length(chstr) < 1 then
5729 begin
5730 g_Console_Add('teamchat <text>');
5731 Exit;
5732 end;
5734 chstr := b_Text_Format(chstr);
5735 if g_Game_IsClient then
5736 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5737 else
5738 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5739 gPlayer1Settings.Team);
5740 end
5741 else
5742 g_Console_Add('teamchat <text>');
5743 end else
5744 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5745 end
5746 else if cmd = 'game' then
5747 begin
5748 if gGameSettings.GameType <> GT_NONE then
5749 begin
5750 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5751 Exit;
5752 end;
5753 if Length(P) = 1 then
5754 begin
5755 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5756 Exit;
5757 end;
5758 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5759 P[1] := addWadExtension(P[1]);
5760 if FileExists(MapsDir + P[1]) then
5761 begin
5762 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5763 if Length(P) < 3 then
5764 begin
5765 SetLength(P, 3);
5766 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5767 end;
5769 s := P[1] + ':\' + UpperCase(P[2]);
5771 if g_Map_Exist(MapsDir + s) then
5772 begin
5773 // Çàïóñêàåì ñâîþ èãðó
5774 g_Game_Free();
5775 with gGameSettings do
5776 begin
5777 GameMode := g_Game_TextToMode(gcGameMode);
5778 if gSwitchGameMode <> GM_NONE then
5779 GameMode := gSwitchGameMode;
5780 if GameMode = GM_NONE then GameMode := GM_DM;
5781 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5782 b := 1;
5783 if Length(P) >= 4 then
5784 b := StrToIntDef(P[3], 1);
5785 g_Game_StartCustom(s, GameMode, TimeLimit,
5786 GoalLimit, MaxLives, Options, b);
5787 end;
5788 end
5789 else
5790 if P[2] = '' then
5791 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5792 else
5793 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5794 end else
5795 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5796 end
5797 else if cmd = 'host' then
5798 begin
5799 if gGameSettings.GameType <> GT_NONE then
5800 begin
5801 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5802 Exit;
5803 end;
5804 if Length(P) < 4 then
5805 begin
5806 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5807 Exit;
5808 end;
5809 if not StrToIp(P[1], listen) then
5810 Exit;
5811 prt := StrToIntDef(P[2], 25666);
5813 P[3] := addWadExtension(P[3]);
5814 if FileExists(MapsDir + P[3]) then
5815 begin
5816 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5817 if Length(P) < 5 then
5818 begin
5819 SetLength(P, 5);
5820 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5821 end;
5823 s := P[3] + ':\' + UpperCase(P[4]);
5825 if g_Map_Exist(MapsDir + s) then
5826 begin
5827 // Çàïóñêàåì ñâîþ èãðó
5828 g_Game_Free();
5829 with gGameSettings do
5830 begin
5831 GameMode := g_Game_TextToMode(gcGameMode);
5832 if gSwitchGameMode <> GM_NONE then
5833 GameMode := gSwitchGameMode;
5834 if GameMode = GM_NONE then GameMode := GM_DM;
5835 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5836 b := 0;
5837 if Length(P) >= 6 then
5838 b := StrToIntDef(P[5], 0);
5839 g_Game_StartServer(s, GameMode, TimeLimit,
5840 GoalLimit, MaxLives, Options, b, listen, prt);
5841 end;
5842 end
5843 else
5844 if P[4] = '' then
5845 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5846 else
5847 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5848 end else
5849 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5850 end
5851 else if cmd = 'map' then
5852 begin
5853 if Length(P) = 1 then
5854 begin
5855 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5856 begin
5857 g_Console_Add(cmd + ' <MAP>');
5858 g_Console_Add(cmd + ' <WAD> [MAP]');
5859 end else
5860 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5861 end else
5862 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5863 begin
5864 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5865 if Length(P) < 3 then
5866 begin
5867 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5868 s := UpperCase(P[1]);
5869 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5870 begin // Êàðòà íàøëàñü
5871 gExitByTrigger := False;
5872 if gGameOn then
5873 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5874 gNextMap := s;
5875 gExit := EXIT_ENDLEVELCUSTOM;
5876 end
5877 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5878 g_Game_ChangeMap(s);
5879 end else
5880 begin
5881 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5882 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5883 P[1] := addWadExtension(P[1]);
5884 if FileExists(MapsDir + P[1]) then
5885 begin
5886 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5887 SetLength(P, 3);
5888 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5890 s := P[1] + ':\' + P[2];
5892 if g_Map_Exist(MapsDir + s) then
5893 begin
5894 gExitByTrigger := False;
5895 if gGameOn then
5896 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5897 gNextMap := s;
5898 gExit := EXIT_ENDLEVELCUSTOM;
5899 end
5900 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5901 g_Game_ChangeMap(s);
5902 end else
5903 if P[2] = '' then
5904 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5905 else
5906 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5907 end else
5908 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5909 end;
5910 end else
5911 begin
5912 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5913 P[1] := addWadExtension(P[1]);
5914 if FileExists(MapsDir + P[1]) then
5915 begin
5916 // Íàøëè WAD ôàéë
5917 P[2] := UpperCase(P[2]);
5918 s := P[1] + ':\' + P[2];
5920 if g_Map_Exist(MapsDir + s) then
5921 begin // Íàøëè êàðòó
5922 gExitByTrigger := False;
5923 if gGameOn then
5924 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5925 gNextMap := s;
5926 gExit := EXIT_ENDLEVELCUSTOM;
5927 end
5928 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5929 g_Game_ChangeMap(s);
5930 end else
5931 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5932 end else
5933 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5934 end;
5935 end else
5936 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5937 end
5938 else if cmd = 'nextmap' then
5939 begin
5940 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5941 g_Console_Add(_lc[I_MSG_NOT_GAME])
5942 else begin
5943 nm := True;
5944 if Length(P) = 1 then
5945 begin
5946 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5947 begin
5948 g_Console_Add(cmd + ' <MAP>');
5949 g_Console_Add(cmd + ' <WAD> [MAP]');
5950 end else begin
5951 nm := False;
5952 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5953 end;
5954 end else
5955 begin
5956 nm := False;
5957 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5958 begin
5959 if Length(P) < 3 then
5960 begin
5961 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5962 s := UpperCase(P[1]);
5963 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5964 begin // Êàðòà íàøëàñü
5965 gExitByTrigger := False;
5966 gNextMap := s;
5967 nm := True;
5968 end else
5969 begin
5970 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5971 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5972 P[1] := addWadExtension(P[1]);
5973 if FileExists(MapsDir + P[1]) then
5974 begin
5975 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5976 SetLength(P, 3);
5977 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5979 s := P[1] + ':\' + P[2];
5981 if g_Map_Exist(MapsDir + s) then
5982 begin // Óñòàíàâëèâàåì êàðòó
5983 gExitByTrigger := False;
5984 gNextMap := s;
5985 nm := True;
5986 end else
5987 if P[2] = '' then
5988 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5989 else
5990 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5991 end else
5992 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5993 end;
5994 end else
5995 begin
5996 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5997 P[1] := addWadExtension(P[1]);
5998 if FileExists(MapsDir + P[1]) then
5999 begin
6000 // Íàøëè WAD ôàéë
6001 P[2] := UpperCase(P[2]);
6002 s := P[1] + ':\' + P[2];
6004 if g_Map_Exist(MapsDir + s) then
6005 begin // Íàøëè êàðòó
6006 gExitByTrigger := False;
6007 gNextMap := s;
6008 nm := True;
6009 end else
6010 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6011 end else
6012 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6013 end;
6014 end else
6015 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6016 end;
6017 if nm then
6018 if gNextMap = '' then
6019 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6020 else
6021 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6022 end;
6023 end
6024 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6025 begin
6026 if not gGameOn then
6027 g_Console_Add(_lc[I_MSG_NOT_GAME])
6028 else
6029 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6030 begin
6031 gExitByTrigger := False;
6032 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6033 if (gNextMap = '') and (gTriggers <> nil) then
6034 for a := 0 to High(gTriggers) do
6035 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6036 begin
6037 gExitByTrigger := True;
6038 gNextMap := gTriggers[a].Data.MapName;
6039 Break;
6040 end;
6041 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6042 if gNextMap = '' then
6043 gNextMap := g_Game_GetNextMap();
6044 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6045 if Pos(':\', gNextMap) = 0 then
6046 s := gGameSettings.WAD + ':\' + gNextMap
6047 else
6048 s := gNextMap;
6049 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6050 if g_Map_Exist(MapsDir + s) then
6051 gExit := EXIT_ENDLEVELCUSTOM
6052 else
6053 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6054 end else
6055 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6056 end
6057 else if (cmd = 'event') then
6058 begin
6059 if (Length(P) <= 1) then
6060 begin
6061 for a := 0 to High(gEvents) do
6062 if gEvents[a].Command = '' then
6063 g_Console_Add(gEvents[a].Name + ' <none>')
6064 else
6065 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6066 Exit;
6067 end;
6068 if (Length(P) = 2) then
6069 begin
6070 for a := 0 to High(gEvents) do
6071 if gEvents[a].Name = P[1] then
6072 if gEvents[a].Command = '' then
6073 g_Console_Add(gEvents[a].Name + ' <none>')
6074 else
6075 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6076 Exit;
6077 end;
6078 for a := 0 to High(gEvents) do
6079 if gEvents[a].Name = P[1] then
6080 begin
6081 gEvents[a].Command := '';
6082 for b := 2 to High(P) do
6083 if Pos(' ', P[b]) = 0 then
6084 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6085 else
6086 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6087 gEvents[a].Command := Trim(gEvents[a].Command);
6088 Exit;
6089 end;
6090 end
6091 // Êîìàíäû Ñâîåé èãðû:
6092 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6093 begin
6094 if cmd = 'bot_addred' then
6095 begin
6096 if Length(P) > 1 then
6097 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6098 else
6099 g_Bot_Add(TEAM_RED, 2);
6100 end
6101 else if cmd = 'bot_addblue' then
6102 begin
6103 if Length(P) > 1 then
6104 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6105 else
6106 g_Bot_Add(TEAM_BLUE, 2);
6107 end
6108 else if cmd = 'suicide' then
6109 begin
6110 if gGameOn then
6111 begin
6112 if g_Game_IsClient then
6113 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6114 else
6115 begin
6116 if gPlayer1 <> nil then
6117 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6118 if gPlayer2 <> nil then
6119 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6120 end;
6121 end;
6122 end
6123 else if cmd = 'spectate' then
6124 begin
6125 if not gGameOn then
6126 Exit;
6127 g_Game_Spectate();
6128 end
6129 else if cmd = 'say' then
6130 begin
6131 if g_Game_IsServer and g_Game_IsNet then
6132 begin
6133 if Length(P) > 1 then
6134 begin
6135 chstr := '';
6136 for a := 1 to High(P) do
6137 chstr := chstr + P[a] + ' ';
6139 if Length(chstr) > 200 then SetLength(chstr, 200);
6141 if Length(chstr) < 1 then
6142 begin
6143 g_Console_Add('say <text>');
6144 Exit;
6145 end;
6147 chstr := b_Text_Format(chstr);
6148 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6149 end
6150 else g_Console_Add('say <text>');
6151 end else
6152 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6153 end
6154 else if cmd = 'tell' then
6155 begin
6156 if g_Game_IsServer and g_Game_IsNet then
6157 begin
6158 if (Length(P) > 2) and (P[1] <> '') then
6159 begin
6160 chstr := '';
6161 for a := 2 to High(P) do
6162 chstr := chstr + P[a] + ' ';
6164 if Length(chstr) > 200 then SetLength(chstr, 200);
6166 if Length(chstr) < 1 then
6167 begin
6168 g_Console_Add('tell <playername> <text>');
6169 Exit;
6170 end;
6172 pl := g_Net_Client_ByName(P[1]);
6173 if pl <> nil then
6174 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6175 else
6176 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6177 end
6178 else g_Console_Add('tell <playername> <text>');
6179 end else
6180 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6181 end
6182 else if (cmd = 'overtime') and not g_Game_IsClient then
6183 begin
6184 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6185 Exit;
6186 // Äîïîëíèòåëüíîå âðåìÿ:
6187 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6189 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6190 [gGameSettings.TimeLimit div 3600,
6191 (gGameSettings.TimeLimit div 60) mod 60,
6192 gGameSettings.TimeLimit mod 60]));
6193 if g_Game_IsNet then MH_SEND_GameSettings;
6194 end
6195 else if (cmd = 'rcon_password') and g_Game_IsClient then
6196 begin
6197 if (Length(P) <= 1) then
6198 g_Console_Add('rcon_password <password>')
6199 else
6200 MC_SEND_RCONPassword(P[1]);
6201 end
6202 else if cmd = 'rcon' then
6203 begin
6204 if g_Game_IsClient then
6205 begin
6206 if Length(P) > 1 then
6207 begin
6208 chstr := '';
6209 for a := 1 to High(P) do
6210 chstr := chstr + P[a] + ' ';
6212 if Length(chstr) > 200 then SetLength(chstr, 200);
6214 if Length(chstr) < 1 then
6215 begin
6216 g_Console_Add('rcon <command>');
6217 Exit;
6218 end;
6220 MC_SEND_RCONCommand(chstr);
6221 end
6222 else g_Console_Add('rcon <command>');
6223 end;
6224 end
6225 else if cmd = 'ready' then
6226 begin
6227 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6228 gLMSRespawnTime := gTime + 100;
6229 end
6230 else if (cmd = 'callvote') and g_Game_IsNet then
6231 begin
6232 if Length(P) > 1 then
6233 begin
6234 chstr := '';
6235 for a := 1 to High(P) do begin
6236 if a > 1 then chstr := chstr + ' ';
6237 chstr := chstr + P[a];
6238 end;
6240 if Length(chstr) > 200 then SetLength(chstr, 200);
6242 if Length(chstr) < 1 then
6243 begin
6244 g_Console_Add('callvote <command>');
6245 Exit;
6246 end;
6248 if g_Game_IsClient then
6249 MC_SEND_Vote(True, chstr)
6250 else
6251 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6252 g_Console_Process('vote', True);
6253 end
6254 else
6255 g_Console_Add('callvote <command>');
6256 end
6257 else if (cmd = 'vote') and g_Game_IsNet then
6258 begin
6259 if g_Game_IsClient then
6260 MC_SEND_Vote(False)
6261 else if gVoteInProgress then
6262 begin
6263 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6264 a := Floor((NetClientCount+1)/2.0) + 1
6265 else
6266 a := Floor(NetClientCount/2.0) + 1;
6267 if gVoted then
6268 begin
6269 Dec(gVoteCount);
6270 gVoted := False;
6271 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6272 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6273 end
6274 else
6275 begin
6276 Inc(gVoteCount);
6277 gVoted := True;
6278 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6279 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6280 g_Game_CheckVote;
6281 end;
6282 end;
6283 end
6284 end;
6285 end;
6287 procedure g_TakeScreenShot();
6288 var
6289 a: Word;
6290 FileName: string;
6291 ssdir, t: string;
6292 st: TStream;
6293 ok: Boolean;
6294 begin
6295 if e_NoGraphics then Exit;
6296 ssdir := GameDir+'/screenshots';
6297 if not findFileCI(ssdir, true) then
6298 begin
6299 // try to create dir
6300 try
6301 CreateDir(ssdir);
6302 except
6303 end;
6304 if not findFileCI(ssdir, true) then exit; // alas
6305 end;
6306 try
6307 for a := 1 to High(Word) do
6308 begin
6309 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6310 t := FileName;
6311 if findFileCI(t, true) then continue;
6312 if not findFileCI(FileName) then
6313 begin
6314 ok := false;
6315 st := createDiskFile(FileName);
6316 try
6317 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6318 ok := true;
6319 finally
6320 st.Free();
6321 end;
6322 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6323 break;
6324 end;
6325 end;
6326 except
6327 end;
6328 end;
6330 procedure g_Game_InGameMenu(Show: Boolean);
6331 begin
6332 if (g_ActiveWindow = nil) and Show then
6333 begin
6334 if gGameSettings.GameType = GT_SINGLE then
6335 g_GUI_ShowWindow('GameSingleMenu')
6336 else
6337 begin
6338 if g_Game_IsClient then
6339 g_GUI_ShowWindow('GameClientMenu')
6340 else
6341 if g_Game_IsNet then
6342 g_GUI_ShowWindow('GameServerMenu')
6343 else
6344 g_GUI_ShowWindow('GameCustomMenu');
6345 end;
6346 g_Sound_PlayEx('MENU_OPEN');
6348 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6349 if (not g_Game_IsNet) then
6350 g_Game_Pause(True);
6351 end
6352 else
6353 if (g_ActiveWindow <> nil) and (not Show) then
6354 begin
6355 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6356 if (not g_Game_IsNet) then
6357 g_Game_Pause(False);
6358 end;
6359 end;
6361 procedure g_Game_Pause(Enable: Boolean);
6362 begin
6363 if not gGameOn then
6364 Exit;
6366 if gPause = Enable then
6367 Exit;
6369 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6370 Exit;
6372 gPause := Enable;
6373 g_Game_PauseAllSounds(Enable);
6374 end;
6376 procedure g_Game_PauseAllSounds(Enable: Boolean);
6377 var
6378 i: Integer;
6379 begin
6380 // Òðèããåðû:
6381 if gTriggers <> nil then
6382 for i := 0 to High(gTriggers) do
6383 with gTriggers[i] do
6384 if (TriggerType = TRIGGER_SOUND) and
6385 (Sound <> nil) and
6386 Sound.IsPlaying() then
6387 begin
6388 Sound.Pause(Enable);
6389 end;
6391 // Çâóêè èãðîêîâ:
6392 if gPlayers <> nil then
6393 for i := 0 to High(gPlayers) do
6394 if gPlayers[i] <> nil then
6395 gPlayers[i].PauseSounds(Enable);
6397 // Ìóçûêà:
6398 if gMusic <> nil then
6399 gMusic.Pause(Enable);
6400 end;
6402 procedure g_Game_StopAllSounds(all: Boolean);
6403 var
6404 i: Integer;
6405 begin
6406 if gTriggers <> nil then
6407 for i := 0 to High(gTriggers) do
6408 with gTriggers[i] do
6409 if (TriggerType = TRIGGER_SOUND) and
6410 (Sound <> nil) then
6411 Sound.Stop();
6413 if gMusic <> nil then
6414 gMusic.Stop();
6416 if all then
6417 e_StopChannels();
6418 end;
6420 procedure g_Game_UpdateTriggerSounds();
6421 var
6422 i: Integer;
6423 begin
6424 if gTriggers <> nil then
6425 for i := 0 to High(gTriggers) do
6426 with gTriggers[i] do
6427 if (TriggerType = TRIGGER_SOUND) and
6428 (Sound <> nil) and
6429 (Data.Local) and
6430 Sound.IsPlaying() then
6431 begin
6432 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6433 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6434 begin
6435 Sound.SetPan(0.5 - Data.Pan/255.0);
6436 Sound.SetVolume(Data.Volume/255.0);
6437 end
6438 else
6439 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6440 end;
6441 end;
6443 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6444 begin
6445 Result := False;
6446 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6447 begin
6448 Result := True;
6449 Exit;
6450 end;
6451 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6452 begin
6453 Result := True;
6454 Exit;
6455 end;
6456 if gSpectMode <> SPECT_PLAYERS then
6457 Exit;
6458 if gSpectPID1 = UID then
6459 begin
6460 Result := True;
6461 Exit;
6462 end;
6463 if gSpectViewTwo and (gSpectPID2 = UID) then
6464 begin
6465 Result := True;
6466 Exit;
6467 end;
6468 end;
6470 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6471 var
6472 Pl: TPlayer;
6473 begin
6474 Result := False;
6475 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6476 begin
6477 Result := True;
6478 Exit;
6479 end;
6480 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6481 begin
6482 Result := True;
6483 Exit;
6484 end;
6485 if gSpectMode <> SPECT_PLAYERS then
6486 Exit;
6487 Pl := g_Player_Get(gSpectPID1);
6488 if (Pl <> nil) and (Pl.Team = Team) then
6489 begin
6490 Result := True;
6491 Exit;
6492 end;
6493 if gSpectViewTwo then
6494 begin
6495 Pl := g_Player_Get(gSpectPID2);
6496 if (Pl <> nil) and (Pl.Team = Team) then
6497 begin
6498 Result := True;
6499 Exit;
6500 end;
6501 end;
6502 end;
6504 procedure g_Game_Message(Msg: string; Time: Word);
6505 begin
6506 MessageText := b_Text_Format(Msg);
6507 MessageTime := Time;
6508 end;
6510 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6511 var
6512 a: Integer;
6513 begin
6514 case gAnnouncer of
6515 ANNOUNCE_NONE:
6516 Exit;
6517 ANNOUNCE_ME,
6518 ANNOUNCE_MEPLUS:
6519 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6520 Exit;
6521 end;
6522 for a := 0 to 3 do
6523 if goodsnd[a].IsPlaying() then
6524 Exit;
6526 goodsnd[Random(4)].Play();
6527 end;
6529 procedure g_Game_Announce_KillCombo(Param: Integer);
6530 var
6531 UID: Word;
6532 c, n: Byte;
6533 Pl: TPlayer;
6534 Name: String;
6535 begin
6536 UID := Param and $FFFF;
6537 c := Param shr 16;
6538 if c < 2 then
6539 Exit;
6541 Pl := g_Player_Get(UID);
6542 if Pl = nil then
6543 Name := '?'
6544 else
6545 Name := Pl.Name;
6547 case c of
6548 2: begin
6549 n := 0;
6550 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6551 end;
6552 3: begin
6553 n := 1;
6554 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6555 end;
6556 4: begin
6557 n := 2;
6558 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6559 end;
6560 else begin
6561 n := 3;
6562 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6563 end;
6564 end;
6566 case gAnnouncer of
6567 ANNOUNCE_NONE:
6568 Exit;
6569 ANNOUNCE_ME:
6570 if not g_Game_IsWatchedPlayer(UID) then
6571 Exit;
6572 ANNOUNCE_MEPLUS:
6573 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6574 Exit;
6575 end;
6577 if killsnd[n].IsPlaying() then
6578 killsnd[n].Stop();
6579 killsnd[n].Play();
6580 end;
6582 procedure g_Game_StartVote(Command, Initiator: string);
6583 var
6584 Need: Integer;
6585 begin
6586 if not gVotesEnabled then Exit;
6587 if gGameSettings.GameType <> GT_SERVER then Exit;
6588 if gVoteInProgress or gVotePassed then
6589 begin
6590 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6591 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6592 Exit;
6593 end;
6594 gVoteInProgress := True;
6595 gVotePassed := False;
6596 gVoteTimer := gTime + gVoteTimeout * 1000;
6597 gVoteCount := 0;
6598 gVoted := False;
6599 gVoteCommand := Command;
6601 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6602 Need := Floor((NetClientCount+1)/2.0)+1
6603 else
6604 Need := Floor(NetClientCount/2.0)+1;
6605 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6606 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6607 end;
6609 procedure g_Game_CheckVote;
6610 var
6611 I, Need: Integer;
6612 begin
6613 if gGameSettings.GameType <> GT_SERVER then Exit;
6614 if not gVoteInProgress then Exit;
6616 if (gTime >= gVoteTimer) then
6617 begin
6618 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6619 Need := Floor((NetClientCount+1)/2.0) + 1
6620 else
6621 Need := Floor(NetClientCount/2.0) + 1;
6622 if gVoteCount >= Need then
6623 begin
6624 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6625 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6626 gVotePassed := True;
6627 gVoteCmdTimer := gTime + 5000;
6628 end
6629 else
6630 begin
6631 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6632 MH_SEND_VoteEvent(NET_VE_FAILED);
6633 end;
6634 if NetClients <> nil then
6635 for I := Low(NetClients) to High(NetClients) do
6636 if NetClients[i].Used then
6637 NetClients[i].Voted := False;
6638 gVoteInProgress := False;
6639 gVoted := False;
6640 gVoteCount := 0;
6641 end
6642 else
6643 begin
6644 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6645 Need := Floor((NetClientCount+1)/2.0) + 1
6646 else
6647 Need := Floor(NetClientCount/2.0) + 1;
6648 if gVoteCount >= Need then
6649 begin
6650 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6651 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6652 gVoteInProgress := False;
6653 gVotePassed := True;
6654 gVoteCmdTimer := gTime + 5000;
6655 gVoted := False;
6656 gVoteCount := 0;
6657 if NetClients <> nil then
6658 for I := Low(NetClients) to High(NetClients) do
6659 if NetClients[i].Used then
6660 NetClients[i].Voted := False;
6661 end;
6662 end;
6663 end;
6665 procedure g_Game_LoadMapList(FileName: string);
6666 var
6667 ListFile: TextFile;
6668 s: string;
6669 begin
6670 MapList := nil;
6671 MapIndex := -1;
6673 if not FileExists(FileName) then Exit;
6675 AssignFile(ListFile, FileName);
6676 Reset(ListFile);
6677 while not EOF(ListFile) do
6678 begin
6679 ReadLn(ListFile, s);
6681 s := Trim(s);
6682 if s = '' then Continue;
6684 SetLength(MapList, Length(MapList)+1);
6685 MapList[High(MapList)] := s;
6686 end;
6687 CloseFile(ListFile);
6688 end;
6690 procedure g_Game_SetDebugMode();
6691 begin
6692 gDebugMode := True;
6693 // ×èòû (äàæå â ñâîåé èãðå):
6694 gCheats := True;
6695 end;
6697 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6698 var
6699 i: Word;
6700 begin
6701 if Length(LoadingStat.Msgs) = 0 then
6702 Exit;
6704 with LoadingStat do
6705 begin
6706 if not reWrite then
6707 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6708 if NextMsg = Length(Msgs) then
6709 begin // scroll
6710 for i := 0 to High(Msgs)-1 do
6711 Msgs[i] := Msgs[i+1];
6712 end
6713 else
6714 Inc(NextMsg);
6715 end else
6716 if NextMsg = 0 then
6717 Inc(NextMsg);
6719 Msgs[NextMsg-1] := Text;
6720 CurValue := 0;
6721 MaxValue := Max;
6722 ShowCount := 0;
6723 end;
6725 g_ActiveWindow := nil;
6727 ProcessLoading;
6728 end;
6730 procedure g_Game_StepLoading();
6731 begin
6732 with LoadingStat do
6733 begin
6734 Inc(CurValue);
6735 Inc(ShowCount);
6736 if (ShowCount > LOADING_SHOW_STEP) then
6737 begin
6738 ShowCount := 0;
6739 ProcessLoading;
6740 end;
6741 end;
6742 end;
6744 procedure g_Game_ClearLoading();
6745 var
6746 len: Word;
6747 begin
6748 with LoadingStat do
6749 begin
6750 CurValue := 0;
6751 MaxValue := 0;
6752 ShowCount := 0;
6753 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6754 if len < 1 then len := 1;
6755 SetLength(Msgs, len);
6756 for len := Low(Msgs) to High(Msgs) do
6757 Msgs[len] := '';
6758 NextMsg := 0;
6759 end;
6760 end;
6762 procedure Parse_Params(var pars: TParamStrValues);
6763 var
6764 i: Integer;
6765 s: String;
6766 begin
6767 SetLength(pars, 0);
6768 i := 1;
6769 while i <= ParamCount do
6770 begin
6771 s := ParamStr(i);
6772 if (s[1] = '-') and (Length(s) > 1) then
6773 begin
6774 if (s[2] = '-') and (Length(s) > 2) then
6775 begin // Îäèíî÷íûé ïàðàìåòð
6776 SetLength(pars, Length(pars) + 1);
6777 with pars[High(pars)] do
6778 begin
6779 Name := LowerCase(s);
6780 Value := '+';
6781 end;
6782 end
6783 else
6784 if (i < ParamCount) then
6785 begin // Ïàðàìåòð ñî çíà÷åíèåì
6786 Inc(i);
6787 SetLength(pars, Length(pars) + 1);
6788 with pars[High(pars)] do
6789 begin
6790 Name := LowerCase(s);
6791 Value := LowerCase(ParamStr(i));
6792 end;
6793 end;
6794 end;
6796 Inc(i);
6797 end;
6798 end;
6800 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6801 var
6802 i: Integer;
6803 begin
6804 Result := '';
6805 for i := 0 to High(pars) do
6806 if pars[i].Name = aName then
6807 begin
6808 Result := pars[i].Value;
6809 Break;
6810 end;
6811 end;
6813 procedure g_Game_Process_Params();
6814 var
6815 pars: TParamStrValues;
6816 map: String;
6817 GMode, n: Byte;
6818 LimT, LimS: Integer;
6819 Opt: LongWord;
6820 Lives: Integer;
6821 s: String;
6822 Port: Integer;
6823 ip: String;
6824 F: TextFile;
6825 begin
6826 Parse_Params(pars);
6828 // Debug mode:
6829 s := Find_Param_Value(pars, '--debug');
6830 if (s <> '') then
6831 begin
6832 g_Game_SetDebugMode();
6833 s := Find_Param_Value(pars, '--netdump');
6834 if (s <> '') then
6835 NetDump := True;
6836 end;
6838 // Connect when game loads
6839 ip := Find_Param_Value(pars, '-connect');
6841 if ip <> '' then
6842 begin
6843 s := Find_Param_Value(pars, '-port');
6844 if (s = '') or not TryStrToInt(s, Port) then
6845 Port := 25666;
6847 s := Find_Param_Value(pars, '-pw');
6849 g_Game_StartClient(ip, Port, s);
6850 Exit;
6851 end;
6853 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
6854 if (s <> '') then
6855 begin
6856 gDefaultMegawadStart := s;
6857 end;
6859 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
6860 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
6861 begin
6862 gDefaultMegawadStart := DF_Default_Megawad_Start;
6863 end;
6865 // Start map when game loads:
6866 map := LowerCase(Find_Param_Value(pars, '-map'));
6867 if isWadPath(map) then
6868 begin
6869 // Game mode:
6870 s := Find_Param_Value(pars, '-gm');
6871 GMode := g_Game_TextToMode(s);
6872 if GMode = GM_NONE then GMode := GM_DM;
6873 if GMode = GM_SINGLE then GMode := GM_COOP;
6875 // Time limit:
6876 s := Find_Param_Value(pars, '-limt');
6877 if (s = '') or (not TryStrToInt(s, LimT)) then
6878 LimT := 0;
6879 if LimT < 0 then
6880 LimT := 0;
6882 // Goal limit:
6883 s := Find_Param_Value(pars, '-lims');
6884 if (s = '') or (not TryStrToInt(s, LimS)) then
6885 LimS := 0;
6886 if LimS < 0 then
6887 LimS := 0;
6889 // Lives limit:
6890 s := Find_Param_Value(pars, '-lives');
6891 if (s = '') or (not TryStrToInt(s, Lives)) then
6892 Lives := 0;
6893 if Lives < 0 then
6894 Lives := 0;
6896 // Options:
6897 s := Find_Param_Value(pars, '-opt');
6898 if (s = '') then
6899 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6900 else
6901 Opt := StrToIntDef(s, 0);
6902 if Opt = 0 then
6903 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6905 // Close after map:
6906 s := Find_Param_Value(pars, '--close');
6907 if (s <> '') then
6908 gMapOnce := True;
6910 // Delete test map after play:
6911 s := Find_Param_Value(pars, '--testdelete');
6912 if (s <> '') then
6913 begin
6914 gMapToDelete := MapsDir + map;
6915 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6916 Halt(1);
6917 end;
6919 // Delete temporary WAD after play:
6920 s := Find_Param_Value(pars, '--tempdelete');
6921 if (s <> '') then
6922 begin
6923 gMapToDelete := MapsDir + map;
6924 gTempDelete := True;
6925 end;
6927 // Number of players:
6928 s := Find_Param_Value(pars, '-pl');
6929 if (s = '') then
6930 n := 1
6931 else
6932 n := StrToIntDef(s, 1);
6934 // Start:
6935 s := Find_Param_Value(pars, '-port');
6936 if (s = '') or not TryStrToInt(s, Port) then
6937 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6938 else
6939 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6940 end;
6942 // Execute script when game loads:
6943 s := Find_Param_Value(pars, '-exec');
6944 if s <> '' then
6945 begin
6946 if Pos(':\', s) = 0 then
6947 s := GameDir + '/' + s;
6949 {$I-}
6950 AssignFile(F, s);
6951 Reset(F);
6952 if IOResult <> 0 then
6953 begin
6954 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6955 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6956 CloseFile(F);
6957 Exit;
6958 end;
6959 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6960 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6962 while not EOF(F) do
6963 begin
6964 ReadLn(F, s);
6965 if IOResult <> 0 then
6966 begin
6967 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6968 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6969 CloseFile(F);
6970 Exit;
6971 end;
6972 if Pos('#', s) <> 1 then // script comment
6973 g_Console_Process(s, True);
6974 end;
6976 CloseFile(F);
6977 {$I+}
6978 end;
6980 SetLength(pars, 0);
6981 end;
6983 end.