DEADSOFTWARE

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