DEADSOFTWARE

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