DEADSOFTWARE

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