DEADSOFTWARE

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