DEADSOFTWARE

added lights for some items
[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
1994 begin
1995 g_Weapon_AddDynLights();
1996 g_Items_AddDynLights();
1997 end;
1998 end;
2000 procedure g_Game_LoadData();
2001 begin
2002 if DataLoaded then Exit;
2004 e_WriteLog('Loading game data...', MSG_NOTIFY);
2006 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2007 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2008 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2009 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2010 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2011 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2012 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB');
2013 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS');
2014 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD');
2015 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB');
2016 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS');
2017 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD');
2018 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2019 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2020 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2021 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2022 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2023 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2024 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2025 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2026 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2027 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2028 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2029 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2030 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2031 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2032 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2033 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2034 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2035 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2036 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2037 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2038 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2039 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2041 goodsnd[0] := TPlayableSound.Create();
2042 goodsnd[1] := TPlayableSound.Create();
2043 goodsnd[2] := TPlayableSound.Create();
2044 goodsnd[3] := TPlayableSound.Create();
2046 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2047 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2048 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2049 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2051 killsnd[0] := TPlayableSound.Create();
2052 killsnd[1] := TPlayableSound.Create();
2053 killsnd[2] := TPlayableSound.Create();
2054 killsnd[3] := TPlayableSound.Create();
2056 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2057 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2058 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2059 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2061 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2062 g_Items_LoadData();
2064 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2065 g_Weapon_LoadData();
2067 g_Monsters_LoadData();
2069 DataLoaded := True;
2070 end;
2072 procedure g_Game_FreeData();
2073 begin
2074 if not DataLoaded then Exit;
2076 g_Items_FreeData();
2077 g_Weapon_FreeData();
2078 g_Monsters_FreeData();
2080 e_WriteLog('Releasing game data...', MSG_NOTIFY);
2082 g_Texture_Delete('NOTEXTURE');
2083 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2084 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2085 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2086 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2087 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2088 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2089 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2090 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2091 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2092 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2093 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2094 g_Frames_DeleteByName('FRAMES_TELEPORT');
2095 g_Sound_Delete('SOUND_GAME_TELEPORT');
2096 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2097 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2098 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2099 g_Sound_Delete('SOUND_GAME_BULK1');
2100 g_Sound_Delete('SOUND_GAME_BULK2');
2101 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2102 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2103 g_Sound_Delete('SOUND_GAME_SWITCH1');
2104 g_Sound_Delete('SOUND_GAME_SWITCH0');
2106 goodsnd[0].Free();
2107 goodsnd[1].Free();
2108 goodsnd[2].Free();
2109 goodsnd[3].Free();
2111 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2112 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2113 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2114 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2116 killsnd[0].Free();
2117 killsnd[1].Free();
2118 killsnd[2].Free();
2119 killsnd[3].Free();
2121 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2122 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2123 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2124 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2126 DataLoaded := False;
2127 end;
2129 procedure DrawCustomStat();
2130 var
2131 pc, x, y, w, _y,
2132 w1, w2, w3,
2133 t, p, m: Integer;
2134 ww1, hh1: Word;
2135 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2136 s1, s2, topstr: String;
2137 begin
2138 e_TextureFontGetSize(gStdFont, ww2, hh2);
2140 e_PollInput();
2141 if e_KeyPressed(IK_TAB) then
2142 begin
2143 if not gStatsPressed then
2144 begin
2145 gStatsOff := not gStatsOff;
2146 gStatsPressed := True;
2147 end;
2148 end
2149 else
2150 gStatsPressed := False;
2152 if gStatsOff then
2153 begin
2154 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2155 w := (Length(s1) * ww2) div 2;
2156 x := gScreenWidth div 2 - w;
2157 y := 8;
2158 e_TextureFontPrint(x, y, s1, gStdFont);
2159 Exit;
2160 end;
2162 if (gGameSettings.GameMode = GM_COOP) then
2163 begin
2164 if gMissionFailed then
2165 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2166 else
2167 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2168 end
2169 else
2170 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2172 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2173 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2175 if g_Game_IsNet then
2176 begin
2177 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2178 if not gChatShow then
2179 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2180 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2181 end;
2183 if g_Game_IsClient then
2184 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2185 else
2186 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2187 if not gChatShow then
2188 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2189 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2191 x := 32;
2192 y := 16+hh1+16;
2194 w := gScreenWidth-x*2;
2196 w2 := (w-16) div 6;
2197 w3 := w2;
2198 w1 := w-16-w2-w3;
2200 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2201 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2203 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2205 case CustomStat.GameMode of
2206 GM_DM:
2207 begin
2208 if gGameSettings.MaxLives = 0 then
2209 s1 := _lc[I_GAME_DM]
2210 else
2211 s1 := _lc[I_GAME_LMS];
2212 end;
2213 GM_TDM:
2214 begin
2215 if gGameSettings.MaxLives = 0 then
2216 s1 := _lc[I_GAME_TDM]
2217 else
2218 s1 := _lc[I_GAME_TLMS];
2219 end;
2220 GM_CTF: s1 := _lc[I_GAME_CTF];
2221 GM_COOP:
2222 begin
2223 if gGameSettings.MaxLives = 0 then
2224 s1 := _lc[I_GAME_COOP]
2225 else
2226 s1 := _lc[I_GAME_SURV];
2227 end;
2228 else s1 := '';
2229 end;
2231 _y := y+16;
2232 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2233 _y := _y+8;
2235 _y := _y+16;
2236 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2237 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2239 _y := _y+16;
2240 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2241 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2242 (CustomStat.GameTime div 1000 div 60) mod 60,
2243 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2245 pc := Length(CustomStat.PlayerStat);
2246 if pc = 0 then Exit;
2248 if CustomStat.GameMode = GM_COOP then
2249 begin
2250 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2251 _y := _y+32;
2252 s2 := _lc[I_GAME_MONSTERS];
2253 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2254 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2255 _y := _y+16;
2256 s2 := _lc[I_GAME_SECRETS];
2257 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2258 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2259 if gLastMap then
2260 begin
2261 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2262 _y := _y-16;
2263 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2264 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2265 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2266 _y := _y+16;
2267 s2 := _lc[I_GAME_SECRETS_TOTAL];
2268 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2269 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2270 end;
2271 end;
2273 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2274 begin
2275 _y := _y+16+16;
2277 with CustomStat do
2278 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2279 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2280 else s1 := _lc[I_GAME_WIN_DRAW];
2282 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2283 _y := _y+40;
2285 for t := TEAM_RED to TEAM_BLUE do
2286 begin
2287 if t = TEAM_RED then
2288 begin
2289 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2290 gStdFont, 255, 0, 0, 1);
2291 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2292 gStdFont, 255, 0, 0, 1);
2293 r := 255;
2294 g := 0;
2295 b := 0;
2296 end
2297 else
2298 begin
2299 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2300 gStdFont, 0, 0, 255, 1);
2301 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2302 gStdFont, 0, 0, 255, 1);
2303 r := 0;
2304 g := 0;
2305 b := 255;
2306 end;
2308 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2309 _y := _y+24;
2311 for p := 0 to High(CustomStat.PlayerStat) do
2312 if CustomStat.PlayerStat[p].Team = t then
2313 with CustomStat.PlayerStat[p] do
2314 begin
2315 if Spectator then
2316 begin
2317 rr := r div 2;
2318 gg := g div 2;
2319 bb := b div 2;
2320 end
2321 else
2322 begin
2323 rr := r;
2324 gg := g;
2325 bb := b;
2326 end;
2327 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2328 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2329 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2330 _y := _y+24;
2331 end;
2333 _y := _y+16+16;
2334 end;
2335 end
2336 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2337 begin
2338 _y := _y+40;
2339 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2340 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2341 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2343 _y := _y+24;
2344 for p := 0 to High(CustomStat.PlayerStat) do
2345 with CustomStat.PlayerStat[p] do
2346 begin
2347 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2349 if Spectator then
2350 r := 127
2351 else
2352 r := 255;
2354 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2355 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2356 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2357 _y := _y+24;
2358 end;
2359 end;
2360 end;
2362 procedure DrawSingleStat();
2363 var
2364 tm, key_x, val_x, y: Integer;
2365 w1, w2, h: Word;
2366 s1, s2: String;
2368 procedure player_stat(n: Integer);
2369 var
2370 kpm: Real;
2372 begin
2373 // "Kills: # / #":
2374 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2375 s2 := Format(' %d', [gTotalMonsters]);
2377 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2378 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2379 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2380 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2381 s1 := s1 + '/';
2382 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2383 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2385 // "Kills-per-minute: ##.#":
2386 s1 := _lc[I_MENU_INTER_KPM];
2387 if tm > 0 then
2388 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2389 else
2390 kpm := SingleStat.PlayerStat[n].Kills;
2391 s2 := Format(' %.1f', [kpm]);
2393 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2394 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2396 // "Secrets found: # / #":
2397 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2398 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2400 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2401 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2402 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2403 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2404 s1 := s1 + '/';
2405 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2406 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2407 end;
2409 begin
2410 // "Level Complete":
2411 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2412 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2414 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2415 s1 := _lc[I_MENU_INTER_KPM];
2416 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2417 Inc(w1, 16);
2418 s1 := ' 9999.9';
2419 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2421 key_x := (gScreenWidth-w1-w2) div 2;
2422 val_x := key_x + w1;
2424 // "Time: #:##:##":
2425 tm := SingleStat.GameTime div 1000;
2426 s1 := _lc[I_MENU_INTER_TIME];
2427 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2429 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2430 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2432 if SingleStat.TwoPlayers then
2433 begin
2434 // "Player 1":
2435 s1 := _lc[I_MENU_PLAYER_1];
2436 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2437 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2439 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2440 y := 176;
2441 player_stat(0);
2443 // "Player 2":
2444 s1 := _lc[I_MENU_PLAYER_2];
2445 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2446 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2448 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2449 y := 336;
2450 player_stat(1);
2451 end
2452 else
2453 begin
2454 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2455 y := 128;
2456 player_stat(0);
2457 end;
2458 end;
2460 procedure DrawLoadingStat();
2461 var
2462 ww, hh: Word;
2463 xx, yy, i: Integer;
2464 s: String;
2465 begin
2466 if Length(LoadingStat.Msgs) = 0 then
2467 Exit;
2469 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2470 yy := (gScreenHeight div 3);
2471 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2472 xx := (gScreenWidth div 3);
2474 with LoadingStat do
2475 for i := 0 to NextMsg-1 do
2476 begin
2477 if (i = (NextMsg-1)) and (MaxValue > 0) then
2478 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2479 else
2480 s := Msgs[i];
2482 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2483 yy := yy + LOADING_INTERLINE;
2484 end;
2485 end;
2487 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2488 var
2489 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2491 function monDraw (mon: TMonster): Boolean;
2492 begin
2493 result := false; // don't stop
2494 with mon do
2495 begin
2496 if Live then
2497 begin
2498 // Ëåâûé âåðõíèé óãîë
2499 aX := Obj.X div ScaleSz + 1;
2500 aY := Obj.Y div ScaleSz + 1;
2501 // Ðàçìåðû
2502 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2503 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2504 // Ïðàâûé íèæíèé óãîë
2505 aX2 := aX + aX2 - 1;
2506 aY2 := aY + aY2 - 1;
2507 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2508 end;
2509 end;
2510 end;
2512 begin
2513 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2514 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2515 begin
2516 Scale := 1;
2517 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2518 ScaleSz := 16 div Scale;
2519 // Ðàçìåðû ìèíè-êàðòû:
2520 aX := max(gMapInfo.Width div ScaleSz, 1);
2521 aY := max(gMapInfo.Height div ScaleSz, 1);
2522 // Ðàìêà êàðòû:
2523 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2525 if gWalls <> nil then
2526 begin
2527 // Ðèñóåì ñòåíû:
2528 for a := 0 to High(gWalls) do
2529 with gWalls[a] do
2530 if PanelType <> 0 then
2531 begin
2532 // Ëåâûé âåðõíèé óãîë:
2533 aX := X div ScaleSz;
2534 aY := Y div ScaleSz;
2535 // Ðàçìåðû:
2536 aX2 := max(Width div ScaleSz, 1);
2537 aY2 := max(Height div ScaleSz, 1);
2538 // Ïðàâûé íèæíèé óãîë:
2539 aX2 := aX + aX2 - 1;
2540 aY2 := aY + aY2 - 1;
2542 case PanelType of
2543 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2544 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2545 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2546 end;
2547 end;
2548 end;
2549 if gSteps <> nil then
2550 begin
2551 // Ðèñóåì ñòóïåíè:
2552 for a := 0 to High(gSteps) do
2553 with gSteps[a] do
2554 if PanelType <> 0 then
2555 begin
2556 // Ëåâûé âåðõíèé óãîë:
2557 aX := X div ScaleSz;
2558 aY := Y div ScaleSz;
2559 // Ðàçìåðû:
2560 aX2 := max(Width div ScaleSz, 1);
2561 aY2 := max(Height div ScaleSz, 1);
2562 // Ïðàâûé íèæíèé óãîë:
2563 aX2 := aX + aX2 - 1;
2564 aY2 := aY + aY2 - 1;
2566 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2567 end;
2568 end;
2569 if gLifts <> nil then
2570 begin
2571 // Ðèñóåì ëèôòû:
2572 for a := 0 to High(gLifts) do
2573 with gLifts[a] do
2574 if PanelType <> 0 then
2575 begin
2576 // Ëåâûé âåðõíèé óãîë:
2577 aX := X div ScaleSz;
2578 aY := Y div ScaleSz;
2579 // Ðàçìåðû:
2580 aX2 := max(Width div ScaleSz, 1);
2581 aY2 := max(Height div ScaleSz, 1);
2582 // Ïðàâûé íèæíèé óãîë:
2583 aX2 := aX + aX2 - 1;
2584 aY2 := aY + aY2 - 1;
2586 case LiftType of
2587 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2588 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2589 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2590 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2591 end;
2592 end;
2593 end;
2594 if gWater <> nil then
2595 begin
2596 // Ðèñóåì âîäó:
2597 for a := 0 to High(gWater) do
2598 with gWater[a] do
2599 if PanelType <> 0 then
2600 begin
2601 // Ëåâûé âåðõíèé óãîë:
2602 aX := X div ScaleSz;
2603 aY := Y div ScaleSz;
2604 // Ðàçìåðû:
2605 aX2 := max(Width div ScaleSz, 1);
2606 aY2 := max(Height div ScaleSz, 1);
2607 // Ïðàâûé íèæíèé óãîë:
2608 aX2 := aX + aX2 - 1;
2609 aY2 := aY + aY2 - 1;
2611 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2612 end;
2613 end;
2614 if gAcid1 <> nil then
2615 begin
2616 // Ðèñóåì êèñëîòó 1:
2617 for a := 0 to High(gAcid1) do
2618 with gAcid1[a] do
2619 if PanelType <> 0 then
2620 begin
2621 // Ëåâûé âåðõíèé óãîë:
2622 aX := X div ScaleSz;
2623 aY := Y div ScaleSz;
2624 // Ðàçìåðû:
2625 aX2 := max(Width div ScaleSz, 1);
2626 aY2 := max(Height div ScaleSz, 1);
2627 // Ïðàâûé íèæíèé óãîë:
2628 aX2 := aX + aX2 - 1;
2629 aY2 := aY + aY2 - 1;
2631 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2632 end;
2633 end;
2634 if gAcid2 <> nil then
2635 begin
2636 // Ðèñóåì êèñëîòó 2:
2637 for a := 0 to High(gAcid2) do
2638 with gAcid2[a] do
2639 if PanelType <> 0 then
2640 begin
2641 // Ëåâûé âåðõíèé óãîë:
2642 aX := X div ScaleSz;
2643 aY := Y div ScaleSz;
2644 // Ðàçìåðû:
2645 aX2 := max(Width div ScaleSz, 1);
2646 aY2 := max(Height div ScaleSz, 1);
2647 // Ïðàâûé íèæíèé óãîë:
2648 aX2 := aX + aX2 - 1;
2649 aY2 := aY + aY2 - 1;
2651 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2652 end;
2653 end;
2654 if gPlayers <> nil then
2655 begin
2656 // Ðèñóåì èãðîêîâ:
2657 for a := 0 to High(gPlayers) do
2658 if gPlayers[a] <> nil then with gPlayers[a] do
2659 if Live then begin
2660 // Ëåâûé âåðõíèé óãîë:
2661 aX := Obj.X div ScaleSz + 1;
2662 aY := Obj.Y div ScaleSz + 1;
2663 // Ðàçìåðû:
2664 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2665 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2666 // Ïðàâûé íèæíèé óãîë:
2667 aX2 := aX + aX2 - 1;
2668 aY2 := aY + aY2 - 1;
2670 if gPlayers[a] = p then
2671 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2672 else
2673 case Team of
2674 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2675 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2676 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2677 end;
2678 end;
2679 end;
2680 // Ðèñóåì ìîíñòðîâ
2681 g_Mons_ForEach(monDraw);
2682 end;
2683 end;
2686 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2687 procedure renderDynLightsInternal ();
2688 var
2689 lln: Integer;
2690 lx, ly, lrad: Integer;
2691 begin
2692 //TODO: lights should be in separate grid, i think
2693 // but on the other side: grid may be slower for dynlights, as their lifetime is short
2694 if not gwin_has_stencil or (g_dynLightCount < 1) then exit;
2696 // setup OpenGL parameters
2697 glStencilMask($FFFFFFFF);
2698 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2699 glEnable(GL_STENCIL_TEST);
2700 glEnable(GL_SCISSOR_TEST);
2701 glClear(GL_STENCIL_BUFFER_BIT);
2702 glStencilFunc(GL_EQUAL, 0, $ff);
2704 for lln := 0 to g_dynLightCount-1 do
2705 begin
2706 lx := g_dynLights[lln].x;
2707 ly := g_dynLights[lln].y;
2708 lrad := g_dynLights[lln].radius;
2709 if lrad < 3 then continue;
2711 if lx-sX+lrad < 0 then continue;
2712 if ly-sY+lrad < 0 then continue;
2713 if lx-sX-lrad >= gPlayerScreenSize.X then continue;
2714 if ly-sY-lrad >= gPlayerScreenSize.Y then continue;
2716 // set scissor to optimize drawing
2717 //FIXME: broken for splitscreen mode
2718 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2719 // no need to clear stencil buffer, light blitting will do it for us
2720 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2721 // draw extruded panels
2722 glDisable(GL_TEXTURE_2D);
2723 glDisable(GL_BLEND);
2724 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2725 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2726 // render light texture
2727 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2728 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2729 // blend it
2730 glEnable(GL_BLEND);
2731 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2732 glEnable(GL_TEXTURE_2D);
2733 // color and opacity
2734 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2735 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2736 glBegin(GL_QUADS);
2737 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2738 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2739 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2740 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2741 glEnd();
2742 end;
2744 // done
2745 glDisable(GL_STENCIL_TEST);
2746 glDisable(GL_BLEND);
2747 glDisable(GL_SCISSOR_TEST);
2748 glScissor(0, 0, sWidth, sHeight);
2749 end;
2752 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
2753 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
2754 procedure renderMapInternal (backXOfs, backYOfs: Integer; transX, transY: Integer; setTransMatrix: Boolean);
2755 type
2756 TDrawCB = procedure ();
2758 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
2759 var
2760 tagmask: Integer;
2761 pan: TPanel;
2762 begin
2763 profileFrameDraw.sectionBegin(profname);
2764 if gdbg_map_use_accel_render then
2765 begin
2766 tagmask := panelTypeToTag(panType);
2767 while (gDrawPanelList.count > 0) do
2768 begin
2769 pan := TPanel(gDrawPanelList.front());
2770 if ((pan.tag and tagmask) = 0) then break;
2771 if doDraw then pan.Draw();
2772 gDrawPanelList.popFront();
2773 end;
2774 end
2775 else
2776 begin
2777 if doDraw then g_Map_DrawPanels(panType);
2778 end;
2779 profileFrameDraw.sectionEnd();
2780 end;
2782 procedure drawOther (profname: AnsiString; cb: TDrawCB);
2783 begin
2784 profileFrameDraw.sectionBegin(profname);
2785 if assigned(cb) then cb();
2786 profileFrameDraw.sectionEnd();
2787 end;
2789 begin
2790 profileFrameDraw.sectionBegin('total');
2792 // our accelerated renderer will collect all panels to gDrawPanelList
2793 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
2794 profileFrameDraw.sectionBegin('collect');
2795 if gdbg_map_use_accel_render then
2796 begin
2797 g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
2798 end;
2799 profileFrameDraw.sectionEnd();
2801 profileFrameDraw.sectionBegin('skyback');
2802 g_Map_DrawBack(backXOfs, backYOfs);
2803 profileFrameDraw.sectionEnd();
2805 if (setTransMatrix) then glTranslatef(transX, transY, 0);
2807 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
2808 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
2809 drawOther('items', @g_Items_Draw);
2810 drawOther('weapons', @g_Weapon_Draw);
2811 drawOther('shells', @g_Player_DrawShells);
2812 drawOther('drawall', @g_Player_DrawAll);
2813 drawOther('corpses', @g_Player_DrawCorpses);
2814 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
2815 drawOther('monsters', @g_Monsters_Draw);
2816 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
2817 drawOther('gfx', @g_GFX_Draw);
2818 drawOther('flags', @g_Map_DrawFlags);
2819 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
2820 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
2821 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
2822 drawOther('dynlights', @renderDynLightsInternal);
2823 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
2825 if g_debug_HealthBar then
2826 begin
2827 g_Monsters_DrawHealth();
2828 g_Player_DrawHealth();
2829 end;
2831 profileFrameDraw.mainEnd(); // map rendering
2832 end;
2835 procedure DrawMapView(x, y, w, h: Integer);
2837 var
2838 bx, by: Integer;
2839 begin
2840 glPushMatrix();
2842 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2843 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2845 sX := x;
2846 sY := y;
2847 sWidth := w;
2848 sHeight := h;
2850 renderMapInternal(-bx, -by, -x, -y, true);
2852 glPopMatrix();
2853 end;
2856 procedure DrawPlayer(p: TPlayer);
2857 var
2858 px, py, a, b, c, d: Integer;
2859 //R: TRect;
2861 begin
2862 if (p = nil) or (p.FDummy) then
2863 begin
2864 glPushMatrix();
2865 g_Map_DrawBack(0, 0);
2866 glPopMatrix();
2867 Exit;
2868 end;
2870 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
2871 profileFrameDraw.mainBegin(g_profile_frame_draw);
2873 gPlayerDrawn := p;
2875 glPushMatrix();
2877 px := p.GameX + PLAYER_RECT_CX;
2878 py := p.GameY + PLAYER_RECT_CY;
2880 if px > (gPlayerScreenSize.X div 2) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
2881 if py > (gPlayerScreenSize.Y div 2) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
2883 if px > gMapInfo.Width-(gPlayerScreenSize.X div 2) then a := -gMapInfo.Width+gPlayerScreenSize.X;
2884 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
2886 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
2887 else if (gMapInfo.Width < gPlayerScreenSize.X) then
2888 begin
2889 // hcenter
2890 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
2891 end;
2893 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
2894 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
2895 begin
2896 // vcenter
2897 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
2898 end;
2900 if p.IncCam <> 0 then
2901 begin
2902 if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then
2903 begin
2904 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2905 begin
2906 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2907 end;
2908 end;
2910 if py < gPlayerScreenSize.Y div 2 then
2911 begin
2912 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2913 begin
2914 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2915 end;
2916 end;
2918 if p.IncCam < 0 then
2919 begin
2920 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam);
2921 end;
2923 if p.IncCam > 0 then
2924 begin
2925 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam);
2926 end;
2927 end;
2929 if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0
2930 else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X
2931 else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2933 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0
2934 else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y
2935 else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
2937 sX := -a;
2938 sY := -(b+p.IncCam);
2939 sWidth := gPlayerScreenSize.X;
2940 sHeight := gPlayerScreenSize.Y;
2942 //glTranslatef(a, b+p.IncCam, 0);
2944 p.viewPortX := sX;
2945 p.viewPortY := sY;
2946 p.viewPortW := sWidth;
2947 p.viewPortH := sHeight;
2949 if (p = gPlayer1) then
2950 begin
2951 g_Holmes_plrView(p.viewPortX, p.viewPortY, p.viewPortW, p.viewPortH);
2952 end;
2954 renderMapInternal(-c, -d, a, b+p.IncCam, true);
2956 if p.FSpectator then
2957 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2958 p.GameY + PLAYER_RECT_CY - 4,
2959 'X', gStdFont, 255, 255, 255, 1, True);
2961 for a := 0 to High(gCollideMap) do
2962 for b := 0 to High(gCollideMap[a]) do
2963 begin
2964 d := 0;
2965 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2966 d := d + 1;
2967 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2968 d := d + 2;
2970 case d of
2971 1: e_DrawPoint(1, b, a, 200, 200, 200);
2972 2: e_DrawPoint(1, b, a, 64, 64, 255);
2973 3: e_DrawPoint(1, b, a, 255, 0, 255);
2974 end;
2975 end;
2978 glPopMatrix();
2980 p.DrawPain();
2981 p.DrawPickup();
2982 p.DrawRulez();
2983 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
2984 if g_Debug_Player then
2985 g_Player_DrawDebug(p);
2986 p.DrawGUI();
2987 end;
2989 procedure drawProfilers ();
2990 var
2991 px: Integer = -1;
2992 py: Integer = -1;
2993 begin
2994 if g_profile_frame_draw then px := px-drawProfiles(px, py, profileFrameDraw);
2995 if g_profile_collision then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
2996 if g_profile_los then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
2997 end;
2999 procedure g_Game_Draw();
3000 var
3001 ID: DWORD;
3002 w, h: Word;
3003 ww, hh: Byte;
3004 Time: Int64;
3005 back: string;
3006 plView1, plView2: TPlayer;
3007 Split: Boolean;
3008 begin
3009 if gExit = EXIT_QUIT then Exit;
3011 Time := GetTimer() {div 1000};
3012 FPSCounter := FPSCounter+1;
3013 if Time - FPSTime >= 1000 then
3014 begin
3015 FPS := FPSCounter;
3016 FPSCounter := 0;
3017 FPSTime := Time;
3018 end;
3020 if gGameOn or (gState = STATE_FOLD) then
3021 begin
3022 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3023 begin
3024 gSpectMode := SPECT_NONE;
3025 if not gRevertPlayers then
3026 begin
3027 plView1 := gPlayer1;
3028 plView2 := gPlayer2;
3029 end
3030 else
3031 begin
3032 plView1 := gPlayer2;
3033 plView2 := gPlayer1;
3034 end;
3035 end
3036 else
3037 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3038 begin
3039 gSpectMode := SPECT_NONE;
3040 if gPlayer2 = nil then
3041 plView1 := gPlayer1
3042 else
3043 plView1 := gPlayer2;
3044 plView2 := nil;
3045 end
3046 else
3047 begin
3048 plView1 := nil;
3049 plView2 := nil;
3050 end;
3052 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3053 gSpectMode := SPECT_STATS;
3055 if gSpectMode = SPECT_PLAYERS then
3056 if gPlayers <> nil then
3057 begin
3058 plView1 := GetActivePlayer_ByID(gSpectPID1);
3059 if plView1 = nil then
3060 begin
3061 gSpectPID1 := GetActivePlayerID_Next();
3062 plView1 := GetActivePlayer_ByID(gSpectPID1);
3063 end;
3064 if gSpectViewTwo then
3065 begin
3066 plView2 := GetActivePlayer_ByID(gSpectPID2);
3067 if plView2 = nil then
3068 begin
3069 gSpectPID2 := GetActivePlayerID_Next();
3070 plView2 := GetActivePlayer_ByID(gSpectPID2);
3071 end;
3072 end;
3073 end;
3075 if gSpectMode = SPECT_MAPVIEW then
3076 begin
3077 // Ðåæèì ïðîñìîòðà êàðòû
3078 Split := False;
3079 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3080 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3081 gHearPoint1.Active := True;
3082 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3083 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3084 gHearPoint2.Active := False;
3085 end
3086 else
3087 begin
3088 Split := (plView1 <> nil) and (plView2 <> nil);
3090 // Òî÷êè ñëóõà èãðîêîâ
3091 if plView1 <> nil then
3092 begin
3093 gHearPoint1.Active := True;
3094 gHearPoint1.Coords.X := plView1.GameX;
3095 gHearPoint1.Coords.Y := plView1.GameY;
3096 end else
3097 gHearPoint1.Active := False;
3098 if plView2 <> nil then
3099 begin
3100 gHearPoint2.Active := True;
3101 gHearPoint2.Coords.X := plView2.GameX;
3102 gHearPoint2.Coords.Y := plView2.GameY;
3103 end else
3104 gHearPoint2.Active := False;
3106 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3107 gPlayerScreenSize.X := gScreenWidth-196;
3108 if Split then
3109 begin
3110 gPlayerScreenSize.Y := gScreenHeight div 2;
3111 if gScreenHeight mod 2 = 0 then
3112 Dec(gPlayerScreenSize.Y);
3113 end
3114 else
3115 gPlayerScreenSize.Y := gScreenHeight;
3117 if Split then
3118 if gScreenHeight mod 2 = 0 then
3119 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3120 else
3121 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3123 DrawPlayer(plView1);
3124 gPlayer1ScreenCoord.X := sX;
3125 gPlayer1ScreenCoord.Y := sY;
3127 if Split then
3128 begin
3129 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3131 DrawPlayer(plView2);
3132 gPlayer2ScreenCoord.X := sX;
3133 gPlayer2ScreenCoord.Y := sY;
3134 end;
3136 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3138 if Split then
3139 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3140 end;
3142 if MessageText <> '' then
3143 begin
3144 w := 0;
3145 h := 0;
3146 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3147 if Split then
3148 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3149 (gScreenHeight div 2)-(h div 2), MessageText)
3150 else
3151 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3152 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3153 end;
3155 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3157 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3158 begin
3159 // Draw spectator GUI
3160 ww := 0;
3161 hh := 0;
3162 e_TextureFontGetSize(gStdFont, ww, hh);
3163 case gSpectMode of
3164 SPECT_STATS:
3165 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3166 SPECT_MAPVIEW:
3167 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3168 SPECT_PLAYERS:
3169 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3170 end;
3171 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3172 if gSpectMode = SPECT_MAPVIEW then
3173 begin
3174 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3175 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3176 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3177 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3178 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3179 end;
3180 if gSpectMode = SPECT_PLAYERS then
3181 begin
3182 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3183 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3184 if gSpectViewTwo then
3185 begin
3186 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3187 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3188 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3189 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3190 end
3191 else
3192 begin
3193 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3194 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3195 end;
3196 end;
3197 end;
3198 end;
3200 if gPause and gGameOn and (g_ActiveWindow = nil) then
3201 begin
3202 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3203 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3205 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3206 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3207 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3208 end;
3210 if not gGameOn then
3211 begin
3212 if (gState = STATE_MENU) then
3213 begin
3214 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
3215 begin
3216 if g_Texture_Get('MENU_BACKGROUND', ID) then
3217 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3218 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3219 end;
3220 if g_ActiveWindow <> nil then
3221 begin
3222 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3223 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3224 end;
3225 end;
3227 if gState = STATE_FOLD then
3228 begin
3229 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3230 end;
3232 if gState = STATE_INTERCUSTOM then
3233 begin
3234 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3235 begin
3236 back := 'TEXTURE_endpic';
3237 if not g_Texture_Get(back, ID) then
3238 back := _lc[I_TEXTURE_ENDPIC];
3239 end
3240 else
3241 back := 'INTER';
3243 if g_Texture_Get(back, ID) then
3244 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3245 else
3246 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3248 DrawCustomStat();
3250 if g_ActiveWindow <> nil then
3251 begin
3252 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3253 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3254 end;
3255 end;
3257 if gState = STATE_INTERSINGLE then
3258 begin
3259 if EndingGameCounter > 0 then
3260 begin
3261 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3262 end
3263 else
3264 begin
3265 back := 'INTER';
3267 if g_Texture_Get(back, ID) then
3268 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3269 else
3270 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3272 DrawSingleStat();
3274 if g_ActiveWindow <> nil then
3275 begin
3276 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3277 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3278 end;
3279 end;
3280 end;
3282 if gState = STATE_ENDPIC then
3283 begin
3284 ID := DWORD(-1);
3285 if not g_Texture_Get('TEXTURE_endpic', ID) then
3286 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3288 if ID <> DWORD(-1) then
3289 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3290 else
3291 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3293 if g_ActiveWindow <> nil then
3294 begin
3295 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3296 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3297 end;
3298 end;
3300 if gState = STATE_SLIST then
3301 begin
3302 if g_Texture_Get('MENU_BACKGROUND', ID) then
3303 begin
3304 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3305 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3306 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3307 end;
3308 g_Serverlist_Draw(slCurrent);
3309 end;
3310 end;
3312 if g_ActiveWindow <> nil then
3313 begin
3314 if gGameOn then
3315 begin
3316 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3317 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
3318 end;
3319 g_ActiveWindow.Draw();
3320 end;
3322 // draw inspector
3323 if (g_holmes_enabled) then g_Holmes_Draw();
3325 g_Console_Draw();
3327 if g_debug_Sounds and gGameOn then
3328 begin
3329 for w := 0 to High(e_SoundsArray) do
3330 for h := 0 to e_SoundsArray[w].nRefs do
3331 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3332 end;
3334 if gShowFPS then
3335 begin
3336 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3337 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3338 end;
3340 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3341 drawTime(gScreenWidth-72, gScreenHeight-16);
3343 if gGameOn then drawProfilers();
3345 g_Holmes_DrawUI();
3346 end;
3348 procedure g_Game_Quit();
3349 begin
3350 g_Game_StopAllSounds(True);
3351 gMusic.Free();
3352 g_Game_SaveOptions();
3353 g_Game_FreeData();
3354 g_PlayerModel_FreeData();
3355 g_Texture_DeleteAll();
3356 g_Frames_DeleteAll();
3357 g_Menu_Free();
3359 if NetInitDone then g_Net_Free;
3361 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3362 if gMapToDelete <> '' then
3363 g_Game_DeleteTestMap();
3365 gExit := EXIT_QUIT;
3366 PushExitEvent();
3367 end;
3369 procedure g_FatalError(Text: String);
3370 begin
3371 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3372 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3374 gExit := EXIT_SIMPLE;
3375 end;
3377 procedure g_SimpleError(Text: String);
3378 begin
3379 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3380 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3381 end;
3383 procedure g_Game_SetupScreenSize();
3384 const
3385 RES_FACTOR = 4.0 / 3.0;
3386 var
3387 s: Single;
3388 rf: Single;
3389 bw, bh: Word;
3390 begin
3391 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3392 gPlayerScreenSize.X := gScreenWidth-196;
3393 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3394 gPlayerScreenSize.Y := gScreenHeight div 2
3395 else
3396 gPlayerScreenSize.Y := gScreenHeight;
3398 // Ðàçìåð çàäíåãî ïëàíà:
3399 if BackID <> DWORD(-1) then
3400 begin
3401 s := SKY_STRETCH;
3402 if (gScreenWidth*s > gMapInfo.Width) or
3403 (gScreenHeight*s > gMapInfo.Height) then
3404 begin
3405 gBackSize.X := gScreenWidth;
3406 gBackSize.Y := gScreenHeight;
3407 end
3408 else
3409 begin
3410 e_GetTextureSize(BackID, @bw, @bh);
3411 rf := Single(bw) / Single(bh);
3412 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
3413 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
3414 s := Max(gScreenWidth / bw, gScreenHeight / bh);
3415 if (s < 1.0) then s := 1.0;
3416 gBackSize.X := Round(bw*s);
3417 gBackSize.Y := Round(bh*s);
3418 end;
3419 end;
3420 end;
3422 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3423 begin
3424 g_Window_SetSize(newWidth, newHeight, nowFull);
3425 end;
3427 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3428 begin
3429 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3430 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3431 Exit;
3432 if gPlayer1 = nil then
3433 begin
3434 if g_Game_IsClient then
3435 begin
3436 if NetPlrUID1 > -1 then
3437 begin
3438 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3439 gPlayer1 := g_Player_Get(NetPlrUID1);
3440 end;
3441 Exit;
3442 end;
3444 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3445 Team := gPlayer1Settings.Team;
3447 // Ñîçäàíèå ïåðâîãî èãðîêà:
3448 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3449 gPlayer1Settings.Color,
3450 Team, False));
3451 if gPlayer1 = nil then
3452 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3453 else
3454 begin
3455 gPlayer1.Name := gPlayer1Settings.Name;
3456 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3457 if g_Game_IsServer and g_Game_IsNet then
3458 MH_SEND_PlayerCreate(gPlayer1.UID);
3459 gPlayer1.Respawn(False, True);
3461 if g_Game_IsNet and NetUseMaster then
3462 g_Net_Slist_Update;
3463 end;
3465 Exit;
3466 end;
3467 if gPlayer2 = nil then
3468 begin
3469 if g_Game_IsClient then
3470 begin
3471 if NetPlrUID2 > -1 then
3472 gPlayer2 := g_Player_Get(NetPlrUID2);
3473 Exit;
3474 end;
3476 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3477 Team := gPlayer2Settings.Team;
3479 // Ñîçäàíèå âòîðîãî èãðîêà:
3480 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3481 gPlayer2Settings.Color,
3482 Team, False));
3483 if gPlayer2 = nil then
3484 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3485 else
3486 begin
3487 gPlayer2.Name := gPlayer2Settings.Name;
3488 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3489 if g_Game_IsServer and g_Game_IsNet then
3490 MH_SEND_PlayerCreate(gPlayer2.UID);
3491 gPlayer2.Respawn(False, True);
3493 if g_Game_IsNet and NetUseMaster then
3494 g_Net_Slist_Update;
3495 end;
3497 Exit;
3498 end;
3499 end;
3501 procedure g_Game_RemovePlayer();
3502 var
3503 Pl: TPlayer;
3504 begin
3505 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3506 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3507 Exit;
3508 Pl := gPlayer2;
3509 if Pl <> nil then
3510 begin
3511 if g_Game_IsServer then
3512 begin
3513 Pl.Lives := 0;
3514 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3515 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3516 g_Player_Remove(Pl.UID);
3518 if g_Game_IsNet and NetUseMaster then
3519 g_Net_Slist_Update;
3520 end else
3521 gPlayer2 := nil;
3522 Exit;
3523 end;
3524 Pl := gPlayer1;
3525 if Pl <> nil then
3526 begin
3527 if g_Game_IsServer then
3528 begin
3529 Pl.Lives := 0;
3530 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3531 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3532 g_Player_Remove(Pl.UID);
3534 if g_Game_IsNet and NetUseMaster then
3535 g_Net_Slist_Update;
3536 end else
3537 begin
3538 gPlayer1 := nil;
3539 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3540 end;
3541 Exit;
3542 end;
3543 end;
3545 procedure g_Game_Spectate();
3546 begin
3547 g_Game_RemovePlayer();
3548 if gPlayer1 <> nil then
3549 g_Game_RemovePlayer();
3550 end;
3552 procedure g_Game_SpectateCenterView();
3553 begin
3554 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3555 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3556 end;
3558 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3559 var
3560 i, nPl: Integer;
3561 begin
3562 g_Game_Free();
3564 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3566 g_Game_ClearLoading();
3568 // Íàñòðîéêè èãðû:
3569 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3570 gAimLine := False;
3571 gShowMap := False;
3572 gGameSettings.GameType := GT_SINGLE;
3573 gGameSettings.MaxLives := 0;
3574 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3575 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3576 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3577 gSwitchGameMode := GM_SINGLE;
3579 g_Game_ExecuteEvent('ongamestart');
3581 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3582 g_Game_SetupScreenSize();
3584 // Ñîçäàíèå ïåðâîãî èãðîêà:
3585 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3586 gPlayer1Settings.Color,
3587 gPlayer1Settings.Team, False));
3588 if gPlayer1 = nil then
3589 begin
3590 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3591 Exit;
3592 end;
3594 gPlayer1.Name := gPlayer1Settings.Name;
3595 nPl := 1;
3597 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3598 if TwoPlayers then
3599 begin
3600 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3601 gPlayer2Settings.Color,
3602 gPlayer2Settings.Team, False));
3603 if gPlayer2 = nil then
3604 begin
3605 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3606 Exit;
3607 end;
3609 gPlayer2.Name := gPlayer2Settings.Name;
3610 Inc(nPl);
3611 end;
3613 // Çàãðóçêà è çàïóñê êàðòû:
3614 if not g_Game_StartMap(MAP, True) then
3615 begin
3616 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3617 Exit;
3618 end;
3620 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3621 g_Player_Init();
3623 // Ñîçäàåì áîòîâ:
3624 for i := nPl+1 to nPlayers do
3625 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3626 end;
3628 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3629 TimeLimit, GoalLimit: Word;
3630 MaxLives: Byte;
3631 Options: LongWord; nPlayers: Byte);
3632 var
3633 i, nPl: Integer;
3634 begin
3635 g_Game_Free();
3637 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3639 g_Game_ClearLoading();
3641 // Íàñòðîéêè èãðû:
3642 gGameSettings.GameType := GT_CUSTOM;
3643 gGameSettings.GameMode := GameMode;
3644 gSwitchGameMode := GameMode;
3645 gGameSettings.TimeLimit := TimeLimit;
3646 gGameSettings.GoalLimit := GoalLimit;
3647 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3648 gGameSettings.Options := Options;
3650 gCoopTotalMonstersKilled := 0;
3651 gCoopTotalSecretsFound := 0;
3652 gCoopTotalMonsters := 0;
3653 gCoopTotalSecrets := 0;
3654 gAimLine := False;
3655 gShowMap := False;
3657 g_Game_ExecuteEvent('ongamestart');
3659 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3660 g_Game_SetupScreenSize();
3662 // Ðåæèì íàáëþäàòåëÿ:
3663 if nPlayers = 0 then
3664 begin
3665 gPlayer1 := nil;
3666 gPlayer2 := nil;
3667 end;
3669 nPl := 0;
3670 if nPlayers >= 1 then
3671 begin
3672 // Ñîçäàíèå ïåðâîãî èãðîêà:
3673 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3674 gPlayer1Settings.Color,
3675 gPlayer1Settings.Team, False));
3676 if gPlayer1 = nil then
3677 begin
3678 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3679 Exit;
3680 end;
3682 gPlayer1.Name := gPlayer1Settings.Name;
3683 Inc(nPl);
3684 end;
3686 if nPlayers >= 2 then
3687 begin
3688 // Ñîçäàíèå âòîðîãî èãðîêà:
3689 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3690 gPlayer2Settings.Color,
3691 gPlayer2Settings.Team, False));
3692 if gPlayer2 = nil then
3693 begin
3694 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3695 Exit;
3696 end;
3698 gPlayer2.Name := gPlayer2Settings.Name;
3699 Inc(nPl);
3700 end;
3702 // Çàãðóçêà è çàïóñê êàðòû:
3703 if not g_Game_StartMap(Map, True) then
3704 begin
3705 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3706 Exit;
3707 end;
3709 // Íåò òî÷åê ïîÿâëåíèÿ:
3710 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3711 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3712 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3713 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3714 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3715 begin
3716 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3717 Exit;
3718 end;
3720 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3721 g_Player_Init();
3723 // Ñîçäàåì áîòîâ:
3724 for i := nPl+1 to nPlayers do
3725 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3726 end;
3728 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3729 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3730 Options: LongWord; nPlayers: Byte;
3731 IPAddr: LongWord; Port: Word);
3732 begin
3733 g_Game_Free();
3735 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3737 g_Game_ClearLoading();
3739 // Íàñòðîéêè èãðû:
3740 gGameSettings.GameType := GT_SERVER;
3741 gGameSettings.GameMode := GameMode;
3742 gSwitchGameMode := GameMode;
3743 gGameSettings.TimeLimit := TimeLimit;
3744 gGameSettings.GoalLimit := GoalLimit;
3745 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3746 gGameSettings.Options := Options;
3748 gCoopTotalMonstersKilled := 0;
3749 gCoopTotalSecretsFound := 0;
3750 gCoopTotalMonsters := 0;
3751 gCoopTotalSecrets := 0;
3752 gAimLine := False;
3753 gShowMap := False;
3755 g_Game_ExecuteEvent('ongamestart');
3757 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3758 g_Game_SetupScreenSize();
3760 // Ðåæèì íàáëþäàòåëÿ:
3761 if nPlayers = 0 then
3762 begin
3763 gPlayer1 := nil;
3764 gPlayer2 := nil;
3765 end;
3767 if nPlayers >= 1 then
3768 begin
3769 // Ñîçäàíèå ïåðâîãî èãðîêà:
3770 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3771 gPlayer1Settings.Color,
3772 gPlayer1Settings.Team, False));
3773 if gPlayer1 = nil then
3774 begin
3775 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3776 Exit;
3777 end;
3779 gPlayer1.Name := gPlayer1Settings.Name;
3780 end;
3782 if nPlayers >= 2 then
3783 begin
3784 // Ñîçäàíèå âòîðîãî èãðîêà:
3785 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3786 gPlayer2Settings.Color,
3787 gPlayer2Settings.Team, False));
3788 if gPlayer2 = nil then
3789 begin
3790 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3791 Exit;
3792 end;
3794 gPlayer2.Name := gPlayer2Settings.Name;
3795 end;
3797 // Ñòàðòóåì ñåðâåð
3798 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3799 begin
3800 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3801 Exit;
3802 end;
3804 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3806 // Çàãðóçêà è çàïóñê êàðòû:
3807 if not g_Game_StartMap(Map, True) then
3808 begin
3809 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3810 Exit;
3811 end;
3813 // Íåò òî÷åê ïîÿâëåíèÿ:
3814 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3815 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3816 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3817 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3818 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3819 begin
3820 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3821 Exit;
3822 end;
3824 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3825 g_Player_Init();
3827 NetState := NET_STATE_GAME;
3828 end;
3830 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3831 var
3832 Map: String;
3833 WadName: string;
3834 Ptr: Pointer;
3835 T: Cardinal;
3836 MID: Byte;
3837 State: Byte;
3838 OuterLoop: Boolean;
3839 newResPath: string;
3840 InMsg: TMsg;
3841 begin
3842 g_Game_Free();
3844 State := 0;
3845 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3846 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3848 g_Game_ClearLoading();
3850 // Íàñòðîéêè èãðû:
3851 gGameSettings.GameType := GT_CLIENT;
3853 gCoopTotalMonstersKilled := 0;
3854 gCoopTotalSecretsFound := 0;
3855 gCoopTotalMonsters := 0;
3856 gCoopTotalSecrets := 0;
3857 gAimLine := False;
3858 gShowMap := False;
3860 g_Game_ExecuteEvent('ongamestart');
3862 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3863 g_Game_SetupScreenSize();
3865 NetState := NET_STATE_AUTH;
3867 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3868 // Ñòàðòóåì êëèåíò
3869 if not g_Net_Connect(Addr, Port) then
3870 begin
3871 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3872 NetState := NET_STATE_NONE;
3873 Exit;
3874 end;
3876 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3877 MC_SEND_Info(PW);
3878 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3880 OuterLoop := True;
3881 while OuterLoop do
3882 begin
3883 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3884 begin
3885 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3886 begin
3887 Ptr := NetEvent.packet^.data;
3888 if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
3889 continue;
3891 MID := InMsg.ReadByte();
3893 if (MID = NET_MSG_INFO) and (State = 0) then
3894 begin
3895 NetMyID := InMsg.ReadByte();
3896 NetPlrUID1 := InMsg.ReadWord();
3898 WadName := InMsg.ReadString();
3899 Map := InMsg.ReadString();
3901 gWADHash := InMsg.ReadMD5();
3903 gGameSettings.GameMode := InMsg.ReadByte();
3904 gSwitchGameMode := gGameSettings.GameMode;
3905 gGameSettings.GoalLimit := InMsg.ReadWord();
3906 gGameSettings.TimeLimit := InMsg.ReadWord();
3907 gGameSettings.MaxLives := InMsg.ReadByte();
3908 gGameSettings.Options := InMsg.ReadLongWord();
3909 T := InMsg.ReadLongWord();
3911 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3912 if newResPath = '' then
3913 begin
3914 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3915 newResPath := g_Res_DownloadWAD(WadName);
3916 if newResPath = '' then
3917 begin
3918 g_FatalError(_lc[I_NET_ERR_HASH]);
3919 enet_packet_destroy(NetEvent.packet);
3920 NetState := NET_STATE_NONE;
3921 Exit;
3922 end;
3923 end;
3924 newResPath := ExtractRelativePath(MapsDir, newResPath);
3926 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3927 gPlayer1Settings.Color,
3928 gPlayer1Settings.Team, False));
3930 if gPlayer1 = nil then
3931 begin
3932 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3934 enet_packet_destroy(NetEvent.packet);
3935 NetState := NET_STATE_NONE;
3936 Exit;
3937 end;
3939 gPlayer1.Name := gPlayer1Settings.Name;
3940 gPlayer1.UID := NetPlrUID1;
3941 gPlayer1.Reset(True);
3943 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3944 begin
3945 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3947 enet_packet_destroy(NetEvent.packet);
3948 NetState := NET_STATE_NONE;
3949 Exit;
3950 end;
3952 gTime := T;
3954 State := 1;
3955 OuterLoop := False;
3956 enet_packet_destroy(NetEvent.packet);
3957 break;
3958 end
3959 else
3960 enet_packet_destroy(NetEvent.packet);
3961 end
3962 else
3963 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3964 begin
3965 State := 0;
3966 if (NetEvent.data <= NET_DISC_MAX) then
3967 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3968 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3969 OuterLoop := False;
3970 Break;
3971 end;
3972 end;
3974 ProcessLoading();
3976 e_PollInput();
3978 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3979 begin
3980 State := 0;
3981 break;
3982 end;
3983 end;
3985 if State <> 1 then
3986 begin
3987 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3988 NetState := NET_STATE_NONE;
3989 Exit;
3990 end;
3992 gLMSRespawn := LMS_RESPAWN_NONE;
3993 gLMSRespawnTime := 0;
3995 g_Player_Init();
3996 NetState := NET_STATE_GAME;
3997 MC_SEND_FullStateRequest;
3998 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3999 end;
4001 procedure g_Game_SaveOptions();
4002 begin
4003 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
4004 end;
4006 procedure g_Game_ChangeMap(MapPath: String);
4007 var
4008 Force: Boolean;
4009 begin
4010 g_Game_ClearLoading();
4012 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
4013 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
4014 if gExitByTrigger then
4015 begin
4016 Force := False;
4017 gExitByTrigger := False;
4018 end;
4019 if not g_Game_StartMap(MapPath, Force) then
4020 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4021 end;
4023 procedure g_Game_Restart();
4024 var
4025 Map: string;
4026 begin
4027 if g_Game_IsClient then
4028 Exit;
4029 map := g_ExtractFileName(gMapInfo.Map);
4031 MessageTime := 0;
4032 gGameOn := False;
4033 g_Game_ClearLoading();
4034 g_Game_StartMap(Map, True);
4035 end;
4037 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
4038 var
4039 NewWAD, ResName: String;
4040 I: Integer;
4041 begin
4042 g_Map_Free();
4043 g_Player_RemoveAllCorpses();
4045 if (not g_Game_IsClient) and
4046 (gSwitchGameMode <> gGameSettings.GameMode) and
4047 (gGameSettings.GameMode <> GM_SINGLE) then
4048 begin
4049 if gSwitchGameMode = GM_CTF then
4050 gGameSettings.MaxLives := 0;
4051 gGameSettings.GameMode := gSwitchGameMode;
4052 Force := True;
4053 end else
4054 gSwitchGameMode := gGameSettings.GameMode;
4056 g_Player_ResetTeams();
4058 if Pos(':\', Map) > 0 then
4059 begin
4060 NewWAD := g_ExtractWadName(Map);
4061 ResName := g_ExtractFileName(Map);
4062 if g_Game_IsServer then
4063 begin
4064 gWADHash := MD5File(MapsDir + NewWAD);
4065 g_Game_LoadWAD(NewWAD);
4066 end else
4067 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4068 g_Game_ClientWAD(NewWAD, gWADHash);
4069 end else
4070 ResName := Map;
4072 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4073 if Result then
4074 begin
4075 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4077 gState := STATE_NONE;
4078 g_ActiveWindow := nil;
4079 gGameOn := True;
4081 DisableCheats();
4082 ResetTimer();
4084 if gGameSettings.GameMode = GM_CTF then
4085 begin
4086 g_Map_ResetFlag(FLAG_RED);
4087 g_Map_ResetFlag(FLAG_BLUE);
4088 // CTF, à ôëàãîâ íåò:
4089 if not g_Map_HaveFlagPoints() then
4090 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4091 end;
4092 end
4093 else
4094 begin
4095 gState := STATE_MENU;
4096 gGameOn := False;
4097 end;
4099 gExit := 0;
4100 gPause := False;
4101 gTime := 0;
4102 NetTimeToUpdate := 1;
4103 NetTimeToReliable := 0;
4104 NetTimeToMaster := NetMasterRate;
4105 gLMSRespawn := LMS_RESPAWN_NONE;
4106 gLMSRespawnTime := 0;
4107 gMissionFailed := False;
4108 gNextMap := '';
4110 gCoopMonstersKilled := 0;
4111 gCoopSecretsFound := 0;
4113 gVoteInProgress := False;
4114 gVotePassed := False;
4115 gVoteCount := 0;
4116 gVoted := False;
4118 gStatsOff := False;
4120 if not gGameOn then Exit;
4122 g_Game_SpectateCenterView();
4124 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4125 begin
4126 gLMSRespawn := LMS_RESPAWN_WARMUP;
4127 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4128 gLMSSoftSpawn := True;
4129 if NetMode = NET_SERVER then
4130 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4131 else
4132 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4133 end;
4135 if NetMode = NET_SERVER then
4136 begin
4137 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4139 // Ìàñòåðñåðâåð
4140 if NetUseMaster then
4141 begin
4142 if (NetMHost = nil) or (NetMPeer = nil) then
4143 if not g_Net_Slist_Connect then
4144 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4146 g_Net_Slist_Update;
4147 end;
4149 if NetClients <> nil then
4150 for I := 0 to High(NetClients) do
4151 if NetClients[I].Used then
4152 begin
4153 NetClients[I].Voted := False;
4154 if NetClients[I].RequestedFullUpdate then
4155 begin
4156 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4157 NetClients[I].RequestedFullUpdate := False;
4158 end;
4159 end;
4161 g_Net_UnbanNonPermHosts();
4162 end;
4164 if gLastMap then
4165 begin
4166 gCoopTotalMonstersKilled := 0;
4167 gCoopTotalSecretsFound := 0;
4168 gCoopTotalMonsters := 0;
4169 gCoopTotalSecrets := 0;
4170 gLastMap := False;
4171 end;
4173 g_Game_ExecuteEvent('onmapstart');
4174 end;
4176 procedure SetFirstLevel();
4177 begin
4178 gNextMap := '';
4180 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4181 if MapList = nil then
4182 Exit;
4184 SortSArray(MapList);
4185 gNextMap := MapList[Low(MapList)];
4187 MapList := nil;
4188 end;
4190 procedure g_Game_ExitLevel(Map: Char16);
4191 begin
4192 gNextMap := Map;
4194 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4195 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4196 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4197 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4199 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4200 if gGameSettings.GameType = GT_SINGLE then
4201 gExit := EXIT_ENDLEVELSINGLE
4202 else // Âûøëè â âûõîä â Ñâîåé èãðå
4203 begin
4204 gExit := EXIT_ENDLEVELCUSTOM;
4205 if gGameSettings.GameMode = GM_COOP then
4206 g_Player_RememberAll;
4208 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4209 begin
4210 gLastMap := True;
4211 if gGameSettings.GameMode = GM_COOP then
4212 gStatsOff := True;
4214 gStatsPressed := True;
4215 gNextMap := 'MAP01';
4217 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4218 g_Game_NextLevel;
4220 if g_Game_IsNet then
4221 begin
4222 MH_SEND_GameStats();
4223 MH_SEND_CoopStats();
4224 end;
4225 end;
4226 end;
4227 end;
4229 procedure g_Game_RestartLevel();
4230 var
4231 Map: string;
4232 begin
4233 if gGameSettings.GameMode = GM_SINGLE then
4234 begin
4235 g_Game_Restart();
4236 Exit;
4237 end;
4238 gExit := EXIT_ENDLEVELCUSTOM;
4239 Map := g_ExtractFileName(gMapInfo.Map);
4240 gNextMap := Map;
4241 end;
4243 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4244 var
4245 gWAD: String;
4246 begin
4247 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4248 Exit;
4249 if not g_Game_IsClient then
4250 Exit;
4251 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4252 if gWAD = '' then
4253 begin
4254 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4255 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4256 if gWAD = '' then
4257 begin
4258 g_Game_Free();
4259 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4260 Exit;
4261 end;
4262 end;
4263 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4264 g_Game_LoadWAD(NewWAD);
4265 end;
4267 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4268 var
4269 i, n, nb, nr: Integer;
4271 function monRespawn (mon: TMonster): Boolean;
4272 begin
4273 result := false; // don't stop
4274 if not mon.FNoRespawn then mon.Respawn();
4275 end;
4277 begin
4278 if not g_Game_IsServer then Exit;
4279 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4280 gLMSRespawn := LMS_RESPAWN_NONE;
4281 gLMSRespawnTime := 0;
4282 MessageTime := 0;
4284 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4285 begin
4286 gMissionFailed := True;
4287 g_Game_RestartLevel;
4288 Exit;
4289 end;
4291 n := 0; nb := 0; nr := 0;
4292 for i := Low(gPlayers) to High(gPlayers) do
4293 if (gPlayers[i] <> nil) and
4294 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4295 (gPlayers[i] is TBot)) then
4296 begin
4297 Inc(n);
4298 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4299 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4300 end;
4302 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4303 begin
4304 // wait a second until the fuckers finally decide to join
4305 gLMSRespawn := LMS_RESPAWN_WARMUP;
4306 gLMSRespawnTime := gTime + 1000;
4307 gLMSSoftSpawn := NoMapRestart;
4308 Exit;
4309 end;
4311 g_Player_RemoveAllCorpses;
4312 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4313 if g_Game_IsNet then
4314 MH_SEND_GameEvent(NET_EV_LMS_START);
4316 for i := Low(gPlayers) to High(gPlayers) do
4317 begin
4318 if gPlayers[i] = nil then continue;
4319 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4320 // don't touch normal spectators
4321 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4322 begin
4323 gPlayers[i].FNoRespawn := True;
4324 gPlayers[i].Lives := 0;
4325 if g_Game_IsNet then
4326 MH_SEND_PlayerStats(gPlayers[I].UID);
4327 continue;
4328 end;
4329 gPlayers[i].FNoRespawn := False;
4330 gPlayers[i].Lives := gGameSettings.MaxLives;
4331 gPlayers[i].Respawn(False, True);
4332 if gGameSettings.GameMode = GM_COOP then
4333 begin
4334 gPlayers[i].Frags := 0;
4335 gPlayers[i].RecallState;
4336 end;
4337 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4338 gPlayer1 := g_Player_Get(gLMSPID1);
4339 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4340 gPlayer2 := g_Player_Get(gLMSPID2);
4341 end;
4343 g_Items_RestartRound();
4346 g_Mons_ForEach(monRespawn);
4348 gLMSSoftSpawn := False;
4349 end;
4351 function g_Game_GetFirstMap(WAD: String): String;
4352 begin
4353 Result := '';
4355 MapList := g_Map_GetMapsList(WAD);
4356 if MapList = nil then
4357 Exit;
4359 SortSArray(MapList);
4360 Result := MapList[Low(MapList)];
4362 if not g_Map_Exist(WAD + ':\' + Result) then
4363 Result := '';
4365 MapList := nil;
4366 end;
4368 function g_Game_GetNextMap(): String;
4369 var
4370 I: Integer;
4371 Map: string;
4372 begin
4373 Result := '';
4375 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4376 if MapList = nil then
4377 Exit;
4379 Map := g_ExtractFileName(gMapInfo.Map);
4381 SortSArray(MapList);
4382 MapIndex := -255;
4383 for I := Low(MapList) to High(MapList) do
4384 if Map = MapList[I] then
4385 begin
4386 MapIndex := I;
4387 Break;
4388 end;
4390 if MapIndex <> -255 then
4391 begin
4392 if MapIndex = High(MapList) then
4393 Result := MapList[Low(MapList)]
4394 else
4395 Result := MapList[MapIndex + 1];
4397 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4398 end;
4400 MapList := nil;
4401 end;
4403 procedure g_Game_NextLevel();
4404 begin
4405 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4406 gExit := EXIT_ENDLEVELCUSTOM
4407 else
4408 begin
4409 gExit := EXIT_ENDLEVELSINGLE;
4410 Exit;
4411 end;
4413 if gNextMap <> '' then Exit;
4414 gNextMap := g_Game_GetNextMap();
4415 end;
4417 function g_Game_IsTestMap(): Boolean;
4418 begin
4419 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4420 end;
4422 procedure g_Game_DeleteTestMap();
4423 var
4424 a: Integer;
4425 MapName: Char16;
4426 WadName: string;
4428 WAD: TWADFile;
4429 MapList: SArray;
4430 time: Integer;
4432 begin
4433 a := Pos('.wad:\', gMapToDelete);
4434 if a = 0 then
4435 Exit;
4437 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4438 WadName := Copy(gMapToDelete, 1, a + 3);
4439 Delete(gMapToDelete, 1, a + 5);
4440 gMapToDelete := UpperCase(gMapToDelete);
4441 MapName := '';
4442 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4445 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4446 if MapName <> TEST_MAP_NAME then
4447 Exit;
4449 if not gTempDelete then
4450 begin
4451 time := g_GetFileTime(WadName);
4452 WAD := TWADFile.Create();
4454 // ×èòàåì Wad-ôàéë:
4455 if not WAD.ReadFile(WadName) then
4456 begin // Íåò òàêîãî WAD-ôàéëà
4457 WAD.Free();
4458 Exit;
4459 end;
4461 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4462 WAD.CreateImage();
4463 MapList := WAD.GetResourcesList('');
4465 if MapList <> nil then
4466 for a := 0 to High(MapList) do
4467 if MapList[a] = MapName then
4468 begin
4469 // Óäàëÿåì è ñîõðàíÿåì:
4470 WAD.RemoveResource('', MapName);
4471 WAD.SaveTo(WadName);
4472 Break;
4473 end;
4475 WAD.Free();
4476 g_SetFileTime(WadName, time);
4477 end else
4479 if gTempDelete then DeleteFile(WadName);
4480 end;
4482 procedure GameCVars(P: SArray);
4483 var
4484 a, b: Integer;
4485 stat: TPlayerStatArray;
4486 cmd, s: string;
4487 config: TConfig;
4488 begin
4489 stat := nil;
4490 cmd := LowerCase(P[0]);
4491 if cmd = 'r_showfps' then
4492 begin
4493 if (Length(P) > 1) and
4494 ((P[1] = '1') or (P[1] = '0')) then
4495 gShowFPS := (P[1][1] = '1');
4497 if gShowFPS then
4498 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4499 else
4500 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4501 end
4502 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4503 begin
4504 with gGameSettings do
4505 begin
4506 if (Length(P) > 1) and
4507 ((P[1] = '1') or (P[1] = '0')) then
4508 begin
4509 if (P[1][1] = '1') then
4510 Options := Options or GAME_OPTION_TEAMDAMAGE
4511 else
4512 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4513 end;
4515 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4516 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4517 else
4518 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4520 if g_Game_IsNet then MH_SEND_GameSettings;
4521 end;
4522 end
4523 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4524 begin
4525 with gGameSettings do
4526 begin
4527 if (Length(P) > 1) and
4528 ((P[1] = '1') or (P[1] = '0')) then
4529 begin
4530 if (P[1][1] = '1') then
4531 Options := Options or GAME_OPTION_WEAPONSTAY
4532 else
4533 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4534 end;
4536 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4537 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4538 else
4539 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4541 if g_Game_IsNet then MH_SEND_GameSettings;
4542 end;
4543 end
4544 else if cmd = 'g_gamemode' then
4545 begin
4546 a := g_Game_TextToMode(P[1]);
4547 if a = GM_SINGLE then a := GM_COOP;
4548 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4549 begin
4550 gSwitchGameMode := a;
4551 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4552 (gState = STATE_INTERSINGLE) then
4553 gSwitchGameMode := GM_SINGLE;
4554 if not gGameOn then
4555 gGameSettings.GameMode := gSwitchGameMode;
4556 end;
4557 if gSwitchGameMode = gGameSettings.GameMode then
4558 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4559 [g_Game_ModeToText(gGameSettings.GameMode)]))
4560 else
4561 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4562 [g_Game_ModeToText(gGameSettings.GameMode),
4563 g_Game_ModeToText(gSwitchGameMode)]));
4564 end
4565 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4566 begin
4567 with gGameSettings do
4568 begin
4569 if (Length(P) > 1) and
4570 ((P[1] = '1') or (P[1] = '0')) then
4571 begin
4572 if (P[1][1] = '1') then
4573 Options := Options or GAME_OPTION_ALLOWEXIT
4574 else
4575 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4576 end;
4578 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4579 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4580 else
4581 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4582 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4584 if g_Game_IsNet then MH_SEND_GameSettings;
4585 end;
4586 end
4587 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4588 begin
4589 with gGameSettings do
4590 begin
4591 if (Length(P) > 1) and
4592 ((P[1] = '1') or (P[1] = '0')) then
4593 begin
4594 if (P[1][1] = '1') then
4595 Options := Options or GAME_OPTION_MONSTERS
4596 else
4597 Options := Options and (not GAME_OPTION_MONSTERS);
4598 end;
4600 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4601 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4602 else
4603 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4604 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4606 if g_Game_IsNet then MH_SEND_GameSettings;
4607 end;
4608 end
4609 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4610 begin
4611 with gGameSettings do
4612 begin
4613 if (Length(P) > 1) and
4614 ((P[1] = '1') or (P[1] = '0')) then
4615 begin
4616 if (P[1][1] = '1') then
4617 Options := Options or GAME_OPTION_BOTVSPLAYER
4618 else
4619 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4620 end;
4622 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4623 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4624 else
4625 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4627 if g_Game_IsNet then MH_SEND_GameSettings;
4628 end;
4629 end
4630 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4631 begin
4632 with gGameSettings do
4633 begin
4634 if (Length(P) > 1) and
4635 ((P[1] = '1') or (P[1] = '0')) then
4636 begin
4637 if (P[1][1] = '1') then
4638 Options := Options or GAME_OPTION_BOTVSMONSTER
4639 else
4640 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4641 end;
4643 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4644 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4645 else
4646 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4648 if g_Game_IsNet then MH_SEND_GameSettings;
4649 end;
4650 end
4651 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4652 begin
4653 if Length(P) > 1 then
4654 begin
4655 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4656 gGameSettings.WarmupTime := 30
4657 else
4658 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4659 end;
4661 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4662 [gGameSettings.WarmupTime]));
4663 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4664 end
4665 else if cmd = 'net_interp' then
4666 begin
4667 if (Length(P) > 1) then
4668 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4670 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4671 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4672 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4673 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4674 config.Free();
4675 end
4676 else if cmd = 'net_forceplayerupdate' then
4677 begin
4678 if (Length(P) > 1) and
4679 ((P[1] = '1') or (P[1] = '0')) then
4680 NetForcePlayerUpdate := (P[1][1] = '1');
4682 if NetForcePlayerUpdate then
4683 g_Console_Add('net_forceplayerupdate = 1')
4684 else
4685 g_Console_Add('net_forceplayerupdate = 0');
4686 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4687 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4688 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4689 config.Free();
4690 end
4691 else if cmd = 'net_predictself' then
4692 begin
4693 if (Length(P) > 1) and
4694 ((P[1] = '1') or (P[1] = '0')) then
4695 NetPredictSelf := (P[1][1] = '1');
4697 if NetPredictSelf then
4698 g_Console_Add('net_predictself = 1')
4699 else
4700 g_Console_Add('net_predictself = 0');
4701 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4702 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4703 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4704 config.Free();
4705 end
4706 else if cmd = 'sv_name' then
4707 begin
4708 if (Length(P) > 1) and (Length(P[1]) > 0) then
4709 begin
4710 NetServerName := P[1];
4711 if Length(NetServerName) > 64 then
4712 SetLength(NetServerName, 64);
4713 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4714 g_Net_Slist_Update;
4715 end;
4717 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4718 end
4719 else if cmd = 'sv_passwd' then
4720 begin
4721 if (Length(P) > 1) and (Length(P[1]) > 0) then
4722 begin
4723 NetPassword := P[1];
4724 if Length(NetPassword) > 24 then
4725 SetLength(NetPassword, 24);
4726 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4727 g_Net_Slist_Update;
4728 end;
4730 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4731 end
4732 else if cmd = 'sv_maxplrs' then
4733 begin
4734 if (Length(P) > 1) then
4735 begin
4736 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4737 if g_Game_IsServer and g_Game_IsNet then
4738 begin
4739 b := 0;
4740 for a := 0 to High(NetClients) do
4741 if NetClients[a].Used then
4742 begin
4743 Inc(b);
4744 if b > NetMaxClients then
4745 begin
4746 s := g_Player_Get(NetClients[a].Player).Name;
4747 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4748 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4749 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4750 end;
4751 end;
4752 if NetUseMaster then
4753 g_Net_Slist_Update;
4754 end;
4755 end;
4757 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4758 end
4759 else if cmd = 'sv_public' then
4760 begin
4761 if (Length(P) > 1) then
4762 begin
4763 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4764 if g_Game_IsServer and g_Game_IsNet then
4765 if NetUseMaster then
4766 begin
4767 if NetMPeer = nil then
4768 if not g_Net_Slist_Connect() then
4769 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4770 g_Net_Slist_Update();
4771 end
4772 else
4773 if NetMPeer <> nil then
4774 g_Net_Slist_Disconnect();
4775 end;
4777 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4778 end
4779 else if cmd = 'sv_intertime' then
4780 begin
4781 if (Length(P) > 1) then
4782 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4784 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4785 end
4786 else if cmd = 'p1_name' then
4787 begin
4788 if (Length(P) > 1) and gGameOn then
4789 begin
4790 if g_Game_IsClient then
4791 begin
4792 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4793 MC_SEND_PlayerSettings;
4794 end
4795 else
4796 if gPlayer1 <> nil then
4797 begin
4798 gPlayer1.Name := b_Text_Unformat(P[1]);
4799 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4800 end
4801 else
4802 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4803 end;
4804 end
4805 else if cmd = 'p2_name' then
4806 begin
4807 if (Length(P) > 1) and gGameOn then
4808 begin
4809 if g_Game_IsClient then
4810 begin
4811 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4812 MC_SEND_PlayerSettings;
4813 end
4814 else
4815 if gPlayer2 <> nil then
4816 begin
4817 gPlayer2.Name := b_Text_Unformat(P[1]);
4818 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4819 end
4820 else
4821 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4822 end;
4823 end
4824 else if cmd = 'p1_color' then
4825 begin
4826 if Length(P) > 3 then
4827 if g_Game_IsClient then
4828 begin
4829 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4830 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4831 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4832 MC_SEND_PlayerSettings;
4833 end
4834 else
4835 if gPlayer1 <> nil then
4836 begin
4837 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4838 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4839 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4840 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4841 end
4842 else
4843 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4844 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4845 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4846 end
4847 else if (cmd = 'p2_color') and not g_Game_IsNet then
4848 begin
4849 if Length(P) > 3 then
4850 if g_Game_IsClient then
4851 begin
4852 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4853 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4854 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4855 MC_SEND_PlayerSettings;
4856 end
4857 else
4858 if gPlayer2 <> nil then
4859 begin
4860 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4861 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4862 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4863 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4864 end
4865 else
4866 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4867 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4868 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4869 end
4870 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4871 begin
4872 if cmd = 'r_showtime' then
4873 begin
4874 if (Length(P) > 1) and
4875 ((P[1] = '1') or (P[1] = '0')) then
4876 gShowTime := (P[1][1] = '1');
4878 if gShowTime then
4879 g_Console_Add(_lc[I_MSG_TIME_ON])
4880 else
4881 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4882 end
4883 else if cmd = 'r_showscore' then
4884 begin
4885 if (Length(P) > 1) and
4886 ((P[1] = '1') or (P[1] = '0')) then
4887 gShowGoals := (P[1][1] = '1');
4889 if gShowGoals then
4890 g_Console_Add(_lc[I_MSG_SCORE_ON])
4891 else
4892 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4893 end
4894 else if cmd = 'r_showstat' then
4895 begin
4896 if (Length(P) > 1) and
4897 ((P[1] = '1') or (P[1] = '0')) then
4898 gShowStat := (P[1][1] = '1');
4900 if gShowStat then
4901 g_Console_Add(_lc[I_MSG_STATS_ON])
4902 else
4903 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4904 end
4905 else if cmd = 'r_showkillmsg' then
4906 begin
4907 if (Length(P) > 1) and
4908 ((P[1] = '1') or (P[1] = '0')) then
4909 gShowKillMsg := (P[1][1] = '1');
4911 if gShowKillMsg then
4912 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4913 else
4914 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4915 end
4916 else if cmd = 'r_showlives' then
4917 begin
4918 if (Length(P) > 1) and
4919 ((P[1] = '1') or (P[1] = '0')) then
4920 gShowLives := (P[1][1] = '1');
4922 if gShowLives then
4923 g_Console_Add(_lc[I_MSG_LIVES_ON])
4924 else
4925 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4926 end
4927 else if cmd = 'r_showspect' then
4928 begin
4929 if (Length(P) > 1) and
4930 ((P[1] = '1') or (P[1] = '0')) then
4931 gSpectHUD := (P[1][1] = '1');
4933 if gSpectHUD then
4934 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4935 else
4936 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4937 end
4938 else if cmd = 'r_showping' then
4939 begin
4940 if (Length(P) > 1) and
4941 ((P[1] = '1') or (P[1] = '0')) then
4942 gShowPing := (P[1][1] = '1');
4944 if gShowPing then
4945 g_Console_Add(_lc[I_MSG_PING_ON])
4946 else
4947 g_Console_Add(_lc[I_MSG_PING_OFF]);
4948 end
4949 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4950 begin
4951 if Length(P) > 1 then
4952 begin
4953 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4954 gGameSettings.GoalLimit := 0
4955 else
4956 begin
4957 b := 0;
4959 if gGameSettings.GameMode = GM_DM then
4960 begin // DM
4961 stat := g_Player_GetStats();
4962 if stat <> nil then
4963 for a := 0 to High(stat) do
4964 if stat[a].Frags > b then
4965 b := stat[a].Frags;
4966 end
4967 else // TDM/CTF
4968 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4970 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4971 end;
4973 if g_Game_IsNet then MH_SEND_GameSettings;
4974 end;
4976 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4977 end
4978 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4979 begin
4980 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4981 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4983 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4984 [gGameSettings.TimeLimit div 3600,
4985 (gGameSettings.TimeLimit div 60) mod 60,
4986 gGameSettings.TimeLimit mod 60]));
4987 if g_Game_IsNet then MH_SEND_GameSettings;
4988 end
4989 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4990 begin
4991 if Length(P) > 1 then
4992 begin
4993 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4994 gGameSettings.MaxLives := 0
4995 else
4996 begin
4997 b := 0;
4998 stat := g_Player_GetStats();
4999 if stat <> nil then
5000 for a := 0 to High(stat) do
5001 if stat[a].Lives > b then
5002 b := stat[a].Lives;
5003 gGameSettings.MaxLives :=
5004 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
5005 end;
5006 end;
5008 g_Console_Add(Format(_lc[I_MSG_LIVES],
5009 [gGameSettings.MaxLives]));
5010 if g_Game_IsNet then MH_SEND_GameSettings;
5011 end;
5012 end;
5013 end;
5016 procedure DebugCommands(P: SArray);
5017 var
5018 a, b: Integer;
5019 cmd: string;
5020 //pt: TPoint;
5021 mon: TMonster;
5022 begin
5023 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5024 if gDebugMode then
5025 begin
5026 cmd := LowerCase(P[0]);
5027 if cmd = 'd_window' then
5028 begin
5029 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5030 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5031 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5032 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5033 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5034 end
5035 else if cmd = 'd_sounds' then
5036 begin
5037 if (Length(P) > 1) and
5038 ((P[1] = '1') or (P[1] = '0')) then
5039 g_Debug_Sounds := (P[1][1] = '1');
5041 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5042 end
5043 else if cmd = 'd_frames' then
5044 begin
5045 if (Length(P) > 1) and
5046 ((P[1] = '1') or (P[1] = '0')) then
5047 g_Debug_Frames := (P[1][1] = '1');
5049 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5050 end
5051 else if cmd = 'd_winmsg' then
5052 begin
5053 if (Length(P) > 1) and
5054 ((P[1] = '1') or (P[1] = '0')) then
5055 g_Debug_WinMsgs := (P[1][1] = '1');
5057 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5058 end
5059 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5060 begin
5061 if (Length(P) > 1) and
5062 ((P[1] = '1') or (P[1] = '0')) then
5063 g_Debug_MonsterOff := (P[1][1] = '1');
5065 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5066 end
5067 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5068 begin
5069 if Length(P) > 1 then
5070 case P[1][1] of
5071 '0': g_debug_BotAIOff := 0;
5072 '1': g_debug_BotAIOff := 1;
5073 '2': g_debug_BotAIOff := 2;
5074 '3': g_debug_BotAIOff := 3;
5075 end;
5077 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5078 end
5079 else if cmd = 'd_monster' then
5080 begin
5081 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
5082 if Length(P) < 2 then
5083 begin
5084 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5085 g_Console_Add('ID | Name');
5086 for b := MONSTER_DEMON to MONSTER_MAN do
5087 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
5088 end else
5089 begin
5090 a := StrToIntDef(P[1], 0);
5091 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5092 a := g_Monsters_GetIDByName(P[1]);
5094 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5095 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5096 else
5097 begin
5098 with gPlayer1.Obj do
5099 begin
5100 mon := g_Monsters_Create(a,
5101 X + Rect.X + (Rect.Width div 2),
5102 Y + Rect.Y + Rect.Height,
5103 gPlayer1.Direction, True);
5104 end;
5105 if (Length(P) > 2) and (mon <> nil) then
5106 mon.MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5107 end;
5108 end;
5109 end
5110 else if (cmd = 'd_health') then
5111 begin
5112 if (Length(P) > 1) and
5113 ((P[1] = '1') or (P[1] = '0')) then
5114 g_debug_HealthBar := (P[1][1] = '1');
5116 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5117 end
5118 else if (cmd = 'd_player') then
5119 begin
5120 if (Length(P) > 1) and
5121 ((P[1] = '1') or (P[1] = '0')) then
5122 g_debug_Player := (P[1][1] = '1');
5124 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5125 end
5126 else if (cmd = 'd_joy') then
5127 begin
5128 for a := 1 to 8 do
5129 g_Console_Add(e_JoystickStateToString(a));
5130 end;
5131 end
5132 else
5133 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5134 end;
5137 procedure GameCheats(P: SArray);
5138 var
5139 cmd: string;
5140 f, a: Integer;
5141 plr: TPlayer;
5142 begin
5143 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5144 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5145 begin
5146 g_Console_Add('not available');
5147 exit;
5148 end;
5149 plr := gPlayer1;
5150 if plr = nil then
5151 begin
5152 g_Console_Add('where is the player?!');
5153 exit;
5154 end;
5155 cmd := LowerCase(P[0]);
5156 // god
5157 if cmd = 'god' then
5158 begin
5159 plr.GodMode := not plr.GodMode;
5160 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5161 exit;
5162 end;
5163 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5164 if cmd = 'give' then
5165 begin
5166 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5167 for f := 1 to High(P) do
5168 begin
5169 cmd := LowerCase(P[f]);
5170 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5171 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5172 if cmd = 'exit' then
5173 begin
5174 if gTriggers <> nil then
5175 begin
5176 for a := 0 to High(gTriggers) do
5177 begin
5178 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5179 begin
5180 g_Console_Add('player left the map');
5181 gExitByTrigger := True;
5182 g_Game_ExitLevel(gTriggers[a].Data.MapName);
5183 break;
5184 end;
5185 end;
5186 end;
5187 continue;
5188 end;
5190 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5191 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5192 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5193 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5194 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5196 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5197 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5199 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5200 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;
5202 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5203 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5205 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5206 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5208 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5209 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5211 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5212 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5213 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5215 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5216 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5217 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5218 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;
5219 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5220 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5222 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;
5223 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;
5224 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;
5225 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;
5226 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;
5227 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;
5229 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5230 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;
5232 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;
5233 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;
5235 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5237 if cmd = 'ammo' then
5238 begin
5239 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5240 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5241 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5242 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5243 plr.GiveItem(ITEM_AMMO_FUELCAN);
5244 g_Console_Add('player got some ammo');
5245 continue;
5246 end;
5248 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5249 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5251 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5252 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5254 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5255 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5257 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5258 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5260 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5262 if cmd = 'weapons' then
5263 begin
5264 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5265 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5266 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5267 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5268 plr.GiveItem(ITEM_WEAPON_PLASMA);
5269 plr.GiveItem(ITEM_WEAPON_BFG);
5270 g_Console_Add('player got weapons');
5271 continue;
5272 end;
5274 if cmd = 'keys' then
5275 begin
5276 plr.GiveItem(ITEM_KEY_RED);
5277 plr.GiveItem(ITEM_KEY_GREEN);
5278 plr.GiveItem(ITEM_KEY_BLUE);
5279 g_Console_Add('player got all keys');
5280 continue;
5281 end;
5283 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5284 end;
5285 exit;
5286 end;
5287 // open
5288 if cmd = 'open' then
5289 begin
5290 g_Console_Add('player activated sesame');
5291 g_Triggers_OpenAll();
5292 exit;
5293 end;
5294 // fly
5295 if cmd = 'fly' then
5296 begin
5297 gFly := not gFly;
5298 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5299 exit;
5300 end;
5301 // noclip
5302 if cmd = 'noclip' then
5303 begin
5304 plr.SwitchNoClip;
5305 g_Console_Add('wall hardeness adjusted');
5306 exit;
5307 end;
5308 // notarget
5309 if cmd = 'notarget' then
5310 begin
5311 plr.NoTarget := not plr.NoTarget;
5312 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5313 exit;
5314 end;
5315 // noreload
5316 if cmd = 'noreload' then
5317 begin
5318 plr.NoReload := not plr.NoReload;
5319 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5320 exit;
5321 end;
5322 // speedy
5323 if cmd = 'speedy' then
5324 begin
5325 MAX_RUNVEL := 32-MAX_RUNVEL;
5326 g_Console_Add('speed adjusted');
5327 exit;
5328 end;
5329 // jumpy
5330 if cmd = 'jumpy' then
5331 begin
5332 VEL_JUMP := 30-VEL_JUMP;
5333 g_Console_Add('jump height adjusted');
5334 exit;
5335 end;
5336 // automap
5337 if cmd = 'automap' then
5338 begin
5339 gShowMap := not gShowMap;
5340 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5341 exit;
5342 end;
5343 // aimline
5344 if cmd = 'aimline' then
5345 begin
5346 gAimLine := not gAimLine;
5347 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5348 exit;
5349 end;
5350 end;
5352 procedure GameCommands(P: SArray);
5353 var
5354 a, b: Integer;
5355 s, pw: String;
5356 chstr: string;
5357 cmd: string;
5358 pl: pTNetClient = nil;
5359 plr: TPlayer;
5360 prt: Word;
5361 nm: Boolean;
5362 listen: LongWord;
5363 begin
5364 // Îáùèå êîìàíäû:
5365 cmd := LowerCase(P[0]);
5366 chstr := '';
5367 if (cmd = 'quit') or
5368 (cmd = 'exit') then
5369 begin
5370 g_Game_Free();
5371 g_Game_Quit();
5372 Exit;
5373 end
5374 else if cmd = 'pause' then
5375 begin
5376 if (g_ActiveWindow = nil) then
5377 g_Game_Pause(not gPause);
5378 end
5379 else if cmd = 'endgame' then
5380 gExit := EXIT_SIMPLE
5381 else if cmd = 'restart' then
5382 begin
5383 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5384 begin
5385 if g_Game_IsClient then
5386 begin
5387 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5388 Exit;
5389 end;
5390 g_Game_Restart();
5391 end else
5392 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5393 end
5394 else if cmd = 'kick' then
5395 begin
5396 if g_Game_IsServer then
5397 begin
5398 if Length(P) < 2 then
5399 begin
5400 g_Console_Add('kick <name>');
5401 Exit;
5402 end;
5403 if P[1] = '' then
5404 begin
5405 g_Console_Add('kick <name>');
5406 Exit;
5407 end;
5409 if g_Game_IsNet then
5410 pl := g_Net_Client_ByName(P[1]);
5411 if (pl <> nil) then
5412 begin
5413 s := g_Net_ClientName_ByID(pl^.ID);
5414 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5415 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5416 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5417 if NetUseMaster then
5418 g_Net_Slist_Update;
5419 end else if gPlayers <> nil then
5420 for a := Low(gPlayers) to High(gPlayers) do
5421 if gPlayers[a] <> nil then
5422 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5423 begin
5424 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5425 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5426 continue;
5427 gPlayers[a].Lives := 0;
5428 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5429 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5430 g_Player_Remove(gPlayers[a].UID);
5431 if NetUseMaster then
5432 g_Net_Slist_Update;
5433 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5434 g_Bot_MixNames();
5435 end;
5436 end else
5437 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5438 end
5439 else if cmd = 'kick_id' then
5440 begin
5441 if g_Game_IsServer and g_Game_IsNet then
5442 begin
5443 if Length(P) < 2 then
5444 begin
5445 g_Console_Add('kick_id <client ID>');
5446 Exit;
5447 end;
5448 if P[1] = '' then
5449 begin
5450 g_Console_Add('kick_id <client ID>');
5451 Exit;
5452 end;
5454 a := StrToIntDef(P[1], 0);
5455 if (NetClients <> nil) and (a <= High(NetClients)) then
5456 begin
5457 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5458 begin
5459 s := g_Net_ClientName_ByID(NetClients[a].ID);
5460 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5461 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5462 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5463 if NetUseMaster then
5464 g_Net_Slist_Update;
5465 end;
5466 end;
5467 end else
5468 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5469 end
5470 else if cmd = 'ban' then
5471 begin
5472 if g_Game_IsServer and g_Game_IsNet then
5473 begin
5474 if Length(P) < 2 then
5475 begin
5476 g_Console_Add('ban <name>');
5477 Exit;
5478 end;
5479 if P[1] = '' then
5480 begin
5481 g_Console_Add('ban <name>');
5482 Exit;
5483 end;
5485 pl := g_Net_Client_ByName(P[1]);
5486 if (pl <> nil) then
5487 begin
5488 s := g_Net_ClientName_ByID(pl^.ID);
5489 g_Net_BanHost(pl^.Peer^.address.host, False);
5490 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5491 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5492 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5493 if NetUseMaster then
5494 g_Net_Slist_Update;
5495 end else
5496 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5497 end else
5498 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5499 end
5500 else if cmd = 'ban_id' then
5501 begin
5502 if g_Game_IsServer and g_Game_IsNet then
5503 begin
5504 if Length(P) < 2 then
5505 begin
5506 g_Console_Add('ban_id <client ID>');
5507 Exit;
5508 end;
5509 if P[1] = '' then
5510 begin
5511 g_Console_Add('ban_id <client ID>');
5512 Exit;
5513 end;
5515 a := StrToIntDef(P[1], 0);
5516 if (NetClients <> nil) and (a <= High(NetClients)) then
5517 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5518 begin
5519 s := g_Net_ClientName_ByID(NetClients[a].ID);
5520 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5521 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5522 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5523 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5524 if NetUseMaster then
5525 g_Net_Slist_Update;
5526 end;
5527 end else
5528 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5529 end
5530 else if cmd = 'permban' then
5531 begin
5532 if g_Game_IsServer and g_Game_IsNet then
5533 begin
5534 if Length(P) < 2 then
5535 begin
5536 g_Console_Add('permban <name>');
5537 Exit;
5538 end;
5539 if P[1] = '' then
5540 begin
5541 g_Console_Add('permban <name>');
5542 Exit;
5543 end;
5545 pl := g_Net_Client_ByName(P[1]);
5546 if (pl <> nil) then
5547 begin
5548 s := g_Net_ClientName_ByID(pl^.ID);
5549 g_Net_BanHost(pl^.Peer^.address.host);
5550 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5551 g_Net_SaveBanList();
5552 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5553 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5554 if NetUseMaster then
5555 g_Net_Slist_Update;
5556 end else
5557 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5558 end else
5559 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5560 end
5561 else if cmd = 'permban_id' then
5562 begin
5563 if g_Game_IsServer and g_Game_IsNet then
5564 begin
5565 if Length(P) < 2 then
5566 begin
5567 g_Console_Add('permban_id <client ID>');
5568 Exit;
5569 end;
5570 if P[1] = '' then
5571 begin
5572 g_Console_Add('permban_id <client ID>');
5573 Exit;
5574 end;
5576 a := StrToIntDef(P[1], 0);
5577 if (NetClients <> nil) and (a <= High(NetClients)) then
5578 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5579 begin
5580 s := g_Net_ClientName_ByID(NetClients[a].ID);
5581 g_Net_BanHost(NetClients[a].Peer^.address.host);
5582 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5583 g_Net_SaveBanList();
5584 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5585 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5586 if NetUseMaster then
5587 g_Net_Slist_Update;
5588 end;
5589 end else
5590 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5591 end
5592 else if cmd = 'unban' then
5593 begin
5594 if g_Game_IsServer and g_Game_IsNet then
5595 begin
5596 if Length(P) < 2 then
5597 begin
5598 g_Console_Add('unban <IP Address>');
5599 Exit;
5600 end;
5601 if P[1] = '' then
5602 begin
5603 g_Console_Add('unban <IP Address>');
5604 Exit;
5605 end;
5607 if g_Net_UnbanHost(P[1]) then
5608 begin
5609 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5610 g_Net_SaveBanList();
5611 end else
5612 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5613 end else
5614 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5615 end
5616 else if cmd = 'clientlist' then
5617 begin
5618 if g_Game_IsServer and g_Game_IsNet then
5619 begin
5620 b := 0;
5621 if NetClients <> nil then
5622 for a := Low(NetClients) to High(NetClients) do
5623 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5624 begin
5625 plr := g_Player_Get(NetClients[a].Player);
5626 if plr = nil then continue;
5627 Inc(b);
5628 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5629 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5630 end;
5631 if b = 0 then
5632 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5633 end else
5634 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5635 end
5636 else if cmd = 'connect' then
5637 begin
5638 if (NetMode = NET_NONE) then
5639 begin
5640 if Length(P) < 2 then
5641 begin
5642 g_Console_Add('connect <IP> [port] [password]');
5643 Exit;
5644 end;
5645 if P[1] = '' then
5646 begin
5647 g_Console_Add('connect <IP> [port] [password]');
5648 Exit;
5649 end;
5651 if Length(P) > 2 then
5652 prt := StrToIntDef(P[2], 25666)
5653 else
5654 prt := 25666;
5656 if Length(P) > 3 then
5657 pw := P[3]
5658 else
5659 pw := '';
5661 g_Game_StartClient(P[1], prt, pw);
5662 end;
5663 end
5664 else if cmd = 'disconnect' then
5665 begin
5666 if (NetMode = NET_CLIENT) then
5667 g_Net_Disconnect();
5668 end
5669 else if cmd = 'reconnect' then
5670 begin
5671 if (NetMode = NET_SERVER) then
5672 Exit;
5674 if (NetMode = NET_CLIENT) then
5675 begin
5676 g_Net_Disconnect();
5677 gExit := EXIT_SIMPLE;
5678 EndGame;
5679 end;
5681 //TODO: Use last successful password to reconnect, instead of ''
5682 g_Game_StartClient(NetClientIP, NetClientPort, '');
5683 end
5684 else if (cmd = 'addbot') or
5685 (cmd = 'bot_add') then
5686 begin
5687 if Length(P) > 1 then
5688 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5689 else
5690 g_Bot_Add(TEAM_NONE, 2);
5691 end
5692 else if cmd = 'bot_addlist' then
5693 begin
5694 if Length(P) > 1 then
5695 if Length(P) = 2 then
5696 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5697 else
5698 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5699 end
5700 else if cmd = 'bot_removeall' then
5701 g_Bot_RemoveAll()
5702 else if cmd = 'chat' then
5703 begin
5704 if g_Game_IsNet then
5705 begin
5706 if Length(P) > 1 then
5707 begin
5708 for a := 1 to High(P) do
5709 chstr := chstr + P[a] + ' ';
5711 if Length(chstr) > 200 then SetLength(chstr, 200);
5713 if Length(chstr) < 1 then
5714 begin
5715 g_Console_Add('chat <text>');
5716 Exit;
5717 end;
5719 chstr := b_Text_Format(chstr);
5720 if g_Game_IsClient then
5721 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5722 else
5723 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5724 end
5725 else
5726 g_Console_Add('chat <text>');
5727 end else
5728 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5729 end
5730 else if cmd = 'teamchat' then
5731 begin
5732 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5733 begin
5734 if Length(P) > 1 then
5735 begin
5736 for a := 1 to High(P) do
5737 chstr := chstr + P[a] + ' ';
5739 if Length(chstr) > 200 then SetLength(chstr, 200);
5741 if Length(chstr) < 1 then
5742 begin
5743 g_Console_Add('teamchat <text>');
5744 Exit;
5745 end;
5747 chstr := b_Text_Format(chstr);
5748 if g_Game_IsClient then
5749 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5750 else
5751 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5752 gPlayer1Settings.Team);
5753 end
5754 else
5755 g_Console_Add('teamchat <text>');
5756 end else
5757 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5758 end
5759 else if cmd = 'game' then
5760 begin
5761 if gGameSettings.GameType <> GT_NONE then
5762 begin
5763 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5764 Exit;
5765 end;
5766 if Length(P) = 1 then
5767 begin
5768 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5769 Exit;
5770 end;
5771 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5772 P[1] := addWadExtension(P[1]);
5773 if FileExists(MapsDir + P[1]) then
5774 begin
5775 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5776 if Length(P) < 3 then
5777 begin
5778 SetLength(P, 3);
5779 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5780 end;
5782 s := P[1] + ':\' + UpperCase(P[2]);
5784 if g_Map_Exist(MapsDir + s) then
5785 begin
5786 // Çàïóñêàåì ñâîþ èãðó
5787 g_Game_Free();
5788 with gGameSettings do
5789 begin
5790 GameMode := g_Game_TextToMode(gcGameMode);
5791 if gSwitchGameMode <> GM_NONE then
5792 GameMode := gSwitchGameMode;
5793 if GameMode = GM_NONE then GameMode := GM_DM;
5794 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5795 b := 1;
5796 if Length(P) >= 4 then
5797 b := StrToIntDef(P[3], 1);
5798 g_Game_StartCustom(s, GameMode, TimeLimit,
5799 GoalLimit, MaxLives, Options, b);
5800 end;
5801 end
5802 else
5803 if P[2] = '' then
5804 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5805 else
5806 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5807 end else
5808 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5809 end
5810 else if cmd = 'host' then
5811 begin
5812 if gGameSettings.GameType <> GT_NONE then
5813 begin
5814 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5815 Exit;
5816 end;
5817 if Length(P) < 4 then
5818 begin
5819 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5820 Exit;
5821 end;
5822 if not StrToIp(P[1], listen) then
5823 Exit;
5824 prt := StrToIntDef(P[2], 25666);
5826 P[3] := addWadExtension(P[3]);
5827 if FileExists(MapsDir + P[3]) then
5828 begin
5829 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5830 if Length(P) < 5 then
5831 begin
5832 SetLength(P, 5);
5833 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5834 end;
5836 s := P[3] + ':\' + UpperCase(P[4]);
5838 if g_Map_Exist(MapsDir + s) then
5839 begin
5840 // Çàïóñêàåì ñâîþ èãðó
5841 g_Game_Free();
5842 with gGameSettings do
5843 begin
5844 GameMode := g_Game_TextToMode(gcGameMode);
5845 if gSwitchGameMode <> GM_NONE then
5846 GameMode := gSwitchGameMode;
5847 if GameMode = GM_NONE then GameMode := GM_DM;
5848 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5849 b := 0;
5850 if Length(P) >= 6 then
5851 b := StrToIntDef(P[5], 0);
5852 g_Game_StartServer(s, GameMode, TimeLimit,
5853 GoalLimit, MaxLives, Options, b, listen, prt);
5854 end;
5855 end
5856 else
5857 if P[4] = '' then
5858 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5859 else
5860 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5861 end else
5862 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5863 end
5864 else if cmd = 'map' then
5865 begin
5866 if Length(P) = 1 then
5867 begin
5868 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5869 begin
5870 g_Console_Add(cmd + ' <MAP>');
5871 g_Console_Add(cmd + ' <WAD> [MAP]');
5872 end else
5873 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5874 end else
5875 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5876 begin
5877 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5878 if Length(P) < 3 then
5879 begin
5880 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5881 s := UpperCase(P[1]);
5882 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5883 begin // Êàðòà íàøëàñü
5884 gExitByTrigger := False;
5885 if gGameOn then
5886 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5887 gNextMap := s;
5888 gExit := EXIT_ENDLEVELCUSTOM;
5889 end
5890 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5891 g_Game_ChangeMap(s);
5892 end else
5893 begin
5894 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5895 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5896 P[1] := addWadExtension(P[1]);
5897 if FileExists(MapsDir + P[1]) then
5898 begin
5899 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5900 SetLength(P, 3);
5901 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5903 s := P[1] + ':\' + P[2];
5905 if g_Map_Exist(MapsDir + s) then
5906 begin
5907 gExitByTrigger := False;
5908 if gGameOn then
5909 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5910 gNextMap := s;
5911 gExit := EXIT_ENDLEVELCUSTOM;
5912 end
5913 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5914 g_Game_ChangeMap(s);
5915 end else
5916 if P[2] = '' then
5917 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5918 else
5919 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5920 end else
5921 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5922 end;
5923 end else
5924 begin
5925 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5926 P[1] := addWadExtension(P[1]);
5927 if FileExists(MapsDir + P[1]) then
5928 begin
5929 // Íàøëè WAD ôàéë
5930 P[2] := UpperCase(P[2]);
5931 s := P[1] + ':\' + P[2];
5933 if g_Map_Exist(MapsDir + s) then
5934 begin // Íàøëè êàðòó
5935 gExitByTrigger := False;
5936 if gGameOn then
5937 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5938 gNextMap := s;
5939 gExit := EXIT_ENDLEVELCUSTOM;
5940 end
5941 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5942 g_Game_ChangeMap(s);
5943 end else
5944 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5945 end else
5946 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5947 end;
5948 end else
5949 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5950 end
5951 else if cmd = 'nextmap' then
5952 begin
5953 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5954 g_Console_Add(_lc[I_MSG_NOT_GAME])
5955 else begin
5956 nm := True;
5957 if Length(P) = 1 then
5958 begin
5959 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5960 begin
5961 g_Console_Add(cmd + ' <MAP>');
5962 g_Console_Add(cmd + ' <WAD> [MAP]');
5963 end else begin
5964 nm := False;
5965 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5966 end;
5967 end else
5968 begin
5969 nm := False;
5970 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5971 begin
5972 if Length(P) < 3 then
5973 begin
5974 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5975 s := UpperCase(P[1]);
5976 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5977 begin // Êàðòà íàøëàñü
5978 gExitByTrigger := False;
5979 gNextMap := s;
5980 nm := True;
5981 end else
5982 begin
5983 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5984 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5985 P[1] := addWadExtension(P[1]);
5986 if FileExists(MapsDir + P[1]) then
5987 begin
5988 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5989 SetLength(P, 3);
5990 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5992 s := P[1] + ':\' + P[2];
5994 if g_Map_Exist(MapsDir + s) then
5995 begin // Óñòàíàâëèâàåì êàðòó
5996 gExitByTrigger := False;
5997 gNextMap := s;
5998 nm := True;
5999 end else
6000 if P[2] = '' then
6001 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6002 else
6003 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6004 end else
6005 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6006 end;
6007 end else
6008 begin
6009 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6010 P[1] := addWadExtension(P[1]);
6011 if FileExists(MapsDir + P[1]) then
6012 begin
6013 // Íàøëè WAD ôàéë
6014 P[2] := UpperCase(P[2]);
6015 s := P[1] + ':\' + P[2];
6017 if g_Map_Exist(MapsDir + s) then
6018 begin // Íàøëè êàðòó
6019 gExitByTrigger := False;
6020 gNextMap := s;
6021 nm := True;
6022 end else
6023 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6024 end else
6025 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6026 end;
6027 end else
6028 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6029 end;
6030 if nm then
6031 if gNextMap = '' then
6032 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6033 else
6034 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6035 end;
6036 end
6037 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6038 begin
6039 if not gGameOn then
6040 g_Console_Add(_lc[I_MSG_NOT_GAME])
6041 else
6042 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6043 begin
6044 gExitByTrigger := False;
6045 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6046 if (gNextMap = '') and (gTriggers <> nil) then
6047 for a := 0 to High(gTriggers) do
6048 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6049 begin
6050 gExitByTrigger := True;
6051 gNextMap := gTriggers[a].Data.MapName;
6052 Break;
6053 end;
6054 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6055 if gNextMap = '' then
6056 gNextMap := g_Game_GetNextMap();
6057 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6058 if Pos(':\', gNextMap) = 0 then
6059 s := gGameSettings.WAD + ':\' + gNextMap
6060 else
6061 s := gNextMap;
6062 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6063 if g_Map_Exist(MapsDir + s) then
6064 gExit := EXIT_ENDLEVELCUSTOM
6065 else
6066 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6067 end else
6068 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6069 end
6070 else if (cmd = 'event') then
6071 begin
6072 if (Length(P) <= 1) then
6073 begin
6074 for a := 0 to High(gEvents) do
6075 if gEvents[a].Command = '' then
6076 g_Console_Add(gEvents[a].Name + ' <none>')
6077 else
6078 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6079 Exit;
6080 end;
6081 if (Length(P) = 2) then
6082 begin
6083 for a := 0 to High(gEvents) do
6084 if gEvents[a].Name = P[1] then
6085 if gEvents[a].Command = '' then
6086 g_Console_Add(gEvents[a].Name + ' <none>')
6087 else
6088 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6089 Exit;
6090 end;
6091 for a := 0 to High(gEvents) do
6092 if gEvents[a].Name = P[1] then
6093 begin
6094 gEvents[a].Command := '';
6095 for b := 2 to High(P) do
6096 if Pos(' ', P[b]) = 0 then
6097 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6098 else
6099 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6100 gEvents[a].Command := Trim(gEvents[a].Command);
6101 Exit;
6102 end;
6103 end
6104 // Êîìàíäû Ñâîåé èãðû:
6105 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6106 begin
6107 if cmd = 'bot_addred' then
6108 begin
6109 if Length(P) > 1 then
6110 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6111 else
6112 g_Bot_Add(TEAM_RED, 2);
6113 end
6114 else if cmd = 'bot_addblue' then
6115 begin
6116 if Length(P) > 1 then
6117 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6118 else
6119 g_Bot_Add(TEAM_BLUE, 2);
6120 end
6121 else if cmd = 'suicide' then
6122 begin
6123 if gGameOn then
6124 begin
6125 if g_Game_IsClient then
6126 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6127 else
6128 begin
6129 if gPlayer1 <> nil then
6130 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6131 if gPlayer2 <> nil then
6132 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6133 end;
6134 end;
6135 end
6136 else if cmd = 'spectate' then
6137 begin
6138 if not gGameOn then
6139 Exit;
6140 g_Game_Spectate();
6141 end
6142 else if cmd = 'say' then
6143 begin
6144 if g_Game_IsServer and g_Game_IsNet then
6145 begin
6146 if Length(P) > 1 then
6147 begin
6148 chstr := '';
6149 for a := 1 to High(P) do
6150 chstr := chstr + P[a] + ' ';
6152 if Length(chstr) > 200 then SetLength(chstr, 200);
6154 if Length(chstr) < 1 then
6155 begin
6156 g_Console_Add('say <text>');
6157 Exit;
6158 end;
6160 chstr := b_Text_Format(chstr);
6161 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6162 end
6163 else g_Console_Add('say <text>');
6164 end else
6165 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6166 end
6167 else if cmd = 'tell' then
6168 begin
6169 if g_Game_IsServer and g_Game_IsNet then
6170 begin
6171 if (Length(P) > 2) and (P[1] <> '') then
6172 begin
6173 chstr := '';
6174 for a := 2 to High(P) do
6175 chstr := chstr + P[a] + ' ';
6177 if Length(chstr) > 200 then SetLength(chstr, 200);
6179 if Length(chstr) < 1 then
6180 begin
6181 g_Console_Add('tell <playername> <text>');
6182 Exit;
6183 end;
6185 pl := g_Net_Client_ByName(P[1]);
6186 if pl <> nil then
6187 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6188 else
6189 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6190 end
6191 else g_Console_Add('tell <playername> <text>');
6192 end else
6193 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6194 end
6195 else if (cmd = 'overtime') and not g_Game_IsClient then
6196 begin
6197 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6198 Exit;
6199 // Äîïîëíèòåëüíîå âðåìÿ:
6200 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6202 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6203 [gGameSettings.TimeLimit div 3600,
6204 (gGameSettings.TimeLimit div 60) mod 60,
6205 gGameSettings.TimeLimit mod 60]));
6206 if g_Game_IsNet then MH_SEND_GameSettings;
6207 end
6208 else if (cmd = 'rcon_password') and g_Game_IsClient then
6209 begin
6210 if (Length(P) <= 1) then
6211 g_Console_Add('rcon_password <password>')
6212 else
6213 MC_SEND_RCONPassword(P[1]);
6214 end
6215 else if cmd = 'rcon' then
6216 begin
6217 if g_Game_IsClient then
6218 begin
6219 if Length(P) > 1 then
6220 begin
6221 chstr := '';
6222 for a := 1 to High(P) do
6223 chstr := chstr + P[a] + ' ';
6225 if Length(chstr) > 200 then SetLength(chstr, 200);
6227 if Length(chstr) < 1 then
6228 begin
6229 g_Console_Add('rcon <command>');
6230 Exit;
6231 end;
6233 MC_SEND_RCONCommand(chstr);
6234 end
6235 else g_Console_Add('rcon <command>');
6236 end;
6237 end
6238 else if cmd = 'ready' then
6239 begin
6240 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6241 gLMSRespawnTime := gTime + 100;
6242 end
6243 else if (cmd = 'callvote') and g_Game_IsNet then
6244 begin
6245 if Length(P) > 1 then
6246 begin
6247 chstr := '';
6248 for a := 1 to High(P) do begin
6249 if a > 1 then chstr := chstr + ' ';
6250 chstr := chstr + P[a];
6251 end;
6253 if Length(chstr) > 200 then SetLength(chstr, 200);
6255 if Length(chstr) < 1 then
6256 begin
6257 g_Console_Add('callvote <command>');
6258 Exit;
6259 end;
6261 if g_Game_IsClient then
6262 MC_SEND_Vote(True, chstr)
6263 else
6264 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6265 g_Console_Process('vote', True);
6266 end
6267 else
6268 g_Console_Add('callvote <command>');
6269 end
6270 else if (cmd = 'vote') and g_Game_IsNet then
6271 begin
6272 if g_Game_IsClient then
6273 MC_SEND_Vote(False)
6274 else if gVoteInProgress then
6275 begin
6276 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6277 a := Floor((NetClientCount+1)/2.0) + 1
6278 else
6279 a := Floor(NetClientCount/2.0) + 1;
6280 if gVoted then
6281 begin
6282 Dec(gVoteCount);
6283 gVoted := False;
6284 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6285 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6286 end
6287 else
6288 begin
6289 Inc(gVoteCount);
6290 gVoted := True;
6291 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6292 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6293 g_Game_CheckVote;
6294 end;
6295 end;
6296 end
6297 end;
6298 end;
6300 procedure g_TakeScreenShot();
6301 var
6302 a: Word;
6303 FileName: string;
6304 ssdir, t: string;
6305 st: TStream;
6306 ok: Boolean;
6307 begin
6308 if e_NoGraphics then Exit;
6309 ssdir := GameDir+'/screenshots';
6310 if not findFileCI(ssdir, true) then
6311 begin
6312 // try to create dir
6313 try
6314 CreateDir(ssdir);
6315 except
6316 end;
6317 if not findFileCI(ssdir, true) then exit; // alas
6318 end;
6319 try
6320 for a := 1 to High(Word) do
6321 begin
6322 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6323 t := FileName;
6324 if findFileCI(t, true) then continue;
6325 if not findFileCI(FileName) then
6326 begin
6327 ok := false;
6328 st := createDiskFile(FileName);
6329 try
6330 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6331 ok := true;
6332 finally
6333 st.Free();
6334 end;
6335 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6336 break;
6337 end;
6338 end;
6339 except
6340 end;
6341 end;
6343 procedure g_Game_InGameMenu(Show: Boolean);
6344 begin
6345 if (g_ActiveWindow = nil) and Show then
6346 begin
6347 if gGameSettings.GameType = GT_SINGLE then
6348 g_GUI_ShowWindow('GameSingleMenu')
6349 else
6350 begin
6351 if g_Game_IsClient then
6352 g_GUI_ShowWindow('GameClientMenu')
6353 else
6354 if g_Game_IsNet then
6355 g_GUI_ShowWindow('GameServerMenu')
6356 else
6357 g_GUI_ShowWindow('GameCustomMenu');
6358 end;
6359 g_Sound_PlayEx('MENU_OPEN');
6361 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6362 if (not g_Game_IsNet) then
6363 g_Game_Pause(True);
6364 end
6365 else
6366 if (g_ActiveWindow <> nil) and (not Show) then
6367 begin
6368 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6369 if (not g_Game_IsNet) then
6370 g_Game_Pause(False);
6371 end;
6372 end;
6374 procedure g_Game_Pause(Enable: Boolean);
6375 begin
6376 if not gGameOn then
6377 Exit;
6379 if gPause = Enable then
6380 Exit;
6382 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6383 Exit;
6385 gPause := Enable;
6386 g_Game_PauseAllSounds(Enable);
6387 end;
6389 procedure g_Game_PauseAllSounds(Enable: Boolean);
6390 var
6391 i: Integer;
6392 begin
6393 // Òðèããåðû:
6394 if gTriggers <> nil then
6395 for i := 0 to High(gTriggers) do
6396 with gTriggers[i] do
6397 if (TriggerType = TRIGGER_SOUND) and
6398 (Sound <> nil) and
6399 Sound.IsPlaying() then
6400 begin
6401 Sound.Pause(Enable);
6402 end;
6404 // Çâóêè èãðîêîâ:
6405 if gPlayers <> nil then
6406 for i := 0 to High(gPlayers) do
6407 if gPlayers[i] <> nil then
6408 gPlayers[i].PauseSounds(Enable);
6410 // Ìóçûêà:
6411 if gMusic <> nil then
6412 gMusic.Pause(Enable);
6413 end;
6415 procedure g_Game_StopAllSounds(all: Boolean);
6416 var
6417 i: Integer;
6418 begin
6419 if gTriggers <> nil then
6420 for i := 0 to High(gTriggers) do
6421 with gTriggers[i] do
6422 if (TriggerType = TRIGGER_SOUND) and
6423 (Sound <> nil) then
6424 Sound.Stop();
6426 if gMusic <> nil then
6427 gMusic.Stop();
6429 if all then
6430 e_StopChannels();
6431 end;
6433 procedure g_Game_UpdateTriggerSounds();
6434 var
6435 i: Integer;
6436 begin
6437 if gTriggers <> nil then
6438 for i := 0 to High(gTriggers) do
6439 with gTriggers[i] do
6440 if (TriggerType = TRIGGER_SOUND) and
6441 (Sound <> nil) and
6442 (Data.Local) and
6443 Sound.IsPlaying() then
6444 begin
6445 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6446 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6447 begin
6448 Sound.SetPan(0.5 - Data.Pan/255.0);
6449 Sound.SetVolume(Data.Volume/255.0);
6450 end
6451 else
6452 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6453 end;
6454 end;
6456 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6457 begin
6458 Result := False;
6459 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6460 begin
6461 Result := True;
6462 Exit;
6463 end;
6464 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6465 begin
6466 Result := True;
6467 Exit;
6468 end;
6469 if gSpectMode <> SPECT_PLAYERS then
6470 Exit;
6471 if gSpectPID1 = UID then
6472 begin
6473 Result := True;
6474 Exit;
6475 end;
6476 if gSpectViewTwo and (gSpectPID2 = UID) then
6477 begin
6478 Result := True;
6479 Exit;
6480 end;
6481 end;
6483 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6484 var
6485 Pl: TPlayer;
6486 begin
6487 Result := False;
6488 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6489 begin
6490 Result := True;
6491 Exit;
6492 end;
6493 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6494 begin
6495 Result := True;
6496 Exit;
6497 end;
6498 if gSpectMode <> SPECT_PLAYERS then
6499 Exit;
6500 Pl := g_Player_Get(gSpectPID1);
6501 if (Pl <> nil) and (Pl.Team = Team) then
6502 begin
6503 Result := True;
6504 Exit;
6505 end;
6506 if gSpectViewTwo then
6507 begin
6508 Pl := g_Player_Get(gSpectPID2);
6509 if (Pl <> nil) and (Pl.Team = Team) then
6510 begin
6511 Result := True;
6512 Exit;
6513 end;
6514 end;
6515 end;
6517 procedure g_Game_Message(Msg: string; Time: Word);
6518 begin
6519 MessageText := b_Text_Format(Msg);
6520 MessageTime := Time;
6521 end;
6523 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6524 var
6525 a: Integer;
6526 begin
6527 case gAnnouncer of
6528 ANNOUNCE_NONE:
6529 Exit;
6530 ANNOUNCE_ME,
6531 ANNOUNCE_MEPLUS:
6532 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6533 Exit;
6534 end;
6535 for a := 0 to 3 do
6536 if goodsnd[a].IsPlaying() then
6537 Exit;
6539 goodsnd[Random(4)].Play();
6540 end;
6542 procedure g_Game_Announce_KillCombo(Param: Integer);
6543 var
6544 UID: Word;
6545 c, n: Byte;
6546 Pl: TPlayer;
6547 Name: String;
6548 begin
6549 UID := Param and $FFFF;
6550 c := Param shr 16;
6551 if c < 2 then
6552 Exit;
6554 Pl := g_Player_Get(UID);
6555 if Pl = nil then
6556 Name := '?'
6557 else
6558 Name := Pl.Name;
6560 case c of
6561 2: begin
6562 n := 0;
6563 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6564 end;
6565 3: begin
6566 n := 1;
6567 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6568 end;
6569 4: begin
6570 n := 2;
6571 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6572 end;
6573 else begin
6574 n := 3;
6575 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6576 end;
6577 end;
6579 case gAnnouncer of
6580 ANNOUNCE_NONE:
6581 Exit;
6582 ANNOUNCE_ME:
6583 if not g_Game_IsWatchedPlayer(UID) then
6584 Exit;
6585 ANNOUNCE_MEPLUS:
6586 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6587 Exit;
6588 end;
6590 if killsnd[n].IsPlaying() then
6591 killsnd[n].Stop();
6592 killsnd[n].Play();
6593 end;
6595 procedure g_Game_StartVote(Command, Initiator: string);
6596 var
6597 Need: Integer;
6598 begin
6599 if not gVotesEnabled then Exit;
6600 if gGameSettings.GameType <> GT_SERVER then Exit;
6601 if gVoteInProgress or gVotePassed then
6602 begin
6603 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6604 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6605 Exit;
6606 end;
6607 gVoteInProgress := True;
6608 gVotePassed := False;
6609 gVoteTimer := gTime + gVoteTimeout * 1000;
6610 gVoteCount := 0;
6611 gVoted := False;
6612 gVoteCommand := Command;
6614 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6615 Need := Floor((NetClientCount+1)/2.0)+1
6616 else
6617 Need := Floor(NetClientCount/2.0)+1;
6618 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6619 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6620 end;
6622 procedure g_Game_CheckVote;
6623 var
6624 I, Need: Integer;
6625 begin
6626 if gGameSettings.GameType <> GT_SERVER then Exit;
6627 if not gVoteInProgress then Exit;
6629 if (gTime >= gVoteTimer) then
6630 begin
6631 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6632 Need := Floor((NetClientCount+1)/2.0) + 1
6633 else
6634 Need := Floor(NetClientCount/2.0) + 1;
6635 if gVoteCount >= Need then
6636 begin
6637 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6638 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6639 gVotePassed := True;
6640 gVoteCmdTimer := gTime + 5000;
6641 end
6642 else
6643 begin
6644 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6645 MH_SEND_VoteEvent(NET_VE_FAILED);
6646 end;
6647 if NetClients <> nil then
6648 for I := Low(NetClients) to High(NetClients) do
6649 if NetClients[i].Used then
6650 NetClients[i].Voted := False;
6651 gVoteInProgress := False;
6652 gVoted := False;
6653 gVoteCount := 0;
6654 end
6655 else
6656 begin
6657 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6658 Need := Floor((NetClientCount+1)/2.0) + 1
6659 else
6660 Need := Floor(NetClientCount/2.0) + 1;
6661 if gVoteCount >= Need then
6662 begin
6663 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6664 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6665 gVoteInProgress := False;
6666 gVotePassed := True;
6667 gVoteCmdTimer := gTime + 5000;
6668 gVoted := False;
6669 gVoteCount := 0;
6670 if NetClients <> nil then
6671 for I := Low(NetClients) to High(NetClients) do
6672 if NetClients[i].Used then
6673 NetClients[i].Voted := False;
6674 end;
6675 end;
6676 end;
6678 procedure g_Game_LoadMapList(FileName: string);
6679 var
6680 ListFile: TextFile;
6681 s: string;
6682 begin
6683 MapList := nil;
6684 MapIndex := -1;
6686 if not FileExists(FileName) then Exit;
6688 AssignFile(ListFile, FileName);
6689 Reset(ListFile);
6690 while not EOF(ListFile) do
6691 begin
6692 ReadLn(ListFile, s);
6694 s := Trim(s);
6695 if s = '' then Continue;
6697 SetLength(MapList, Length(MapList)+1);
6698 MapList[High(MapList)] := s;
6699 end;
6700 CloseFile(ListFile);
6701 end;
6703 procedure g_Game_SetDebugMode();
6704 begin
6705 gDebugMode := True;
6706 // ×èòû (äàæå â ñâîåé èãðå):
6707 gCheats := True;
6708 end;
6710 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6711 var
6712 i: Word;
6713 begin
6714 if Length(LoadingStat.Msgs) = 0 then
6715 Exit;
6717 with LoadingStat do
6718 begin
6719 if not reWrite then
6720 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6721 if NextMsg = Length(Msgs) then
6722 begin // scroll
6723 for i := 0 to High(Msgs)-1 do
6724 Msgs[i] := Msgs[i+1];
6725 end
6726 else
6727 Inc(NextMsg);
6728 end else
6729 if NextMsg = 0 then
6730 Inc(NextMsg);
6732 Msgs[NextMsg-1] := Text;
6733 CurValue := 0;
6734 MaxValue := Max;
6735 ShowCount := 0;
6736 end;
6738 g_ActiveWindow := nil;
6740 ProcessLoading;
6741 end;
6743 procedure g_Game_StepLoading();
6744 begin
6745 with LoadingStat do
6746 begin
6747 Inc(CurValue);
6748 Inc(ShowCount);
6749 if (ShowCount > LOADING_SHOW_STEP) then
6750 begin
6751 ShowCount := 0;
6752 ProcessLoading;
6753 end;
6754 end;
6755 end;
6757 procedure g_Game_ClearLoading();
6758 var
6759 len: Word;
6760 begin
6761 with LoadingStat do
6762 begin
6763 CurValue := 0;
6764 MaxValue := 0;
6765 ShowCount := 0;
6766 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6767 if len < 1 then len := 1;
6768 SetLength(Msgs, len);
6769 for len := Low(Msgs) to High(Msgs) do
6770 Msgs[len] := '';
6771 NextMsg := 0;
6772 end;
6773 end;
6775 procedure Parse_Params(var pars: TParamStrValues);
6776 var
6777 i: Integer;
6778 s: String;
6779 begin
6780 SetLength(pars, 0);
6781 i := 1;
6782 while i <= ParamCount do
6783 begin
6784 s := ParamStr(i);
6785 if (s[1] = '-') and (Length(s) > 1) then
6786 begin
6787 if (s[2] = '-') and (Length(s) > 2) then
6788 begin // Îäèíî÷íûé ïàðàìåòð
6789 SetLength(pars, Length(pars) + 1);
6790 with pars[High(pars)] do
6791 begin
6792 Name := LowerCase(s);
6793 Value := '+';
6794 end;
6795 end
6796 else
6797 if (i < ParamCount) then
6798 begin // Ïàðàìåòð ñî çíà÷åíèåì
6799 Inc(i);
6800 SetLength(pars, Length(pars) + 1);
6801 with pars[High(pars)] do
6802 begin
6803 Name := LowerCase(s);
6804 Value := LowerCase(ParamStr(i));
6805 end;
6806 end;
6807 end;
6809 Inc(i);
6810 end;
6811 end;
6813 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6814 var
6815 i: Integer;
6816 begin
6817 Result := '';
6818 for i := 0 to High(pars) do
6819 if pars[i].Name = aName then
6820 begin
6821 Result := pars[i].Value;
6822 Break;
6823 end;
6824 end;
6826 procedure g_Game_Process_Params();
6827 var
6828 pars: TParamStrValues;
6829 map: String;
6830 GMode, n: Byte;
6831 LimT, LimS: Integer;
6832 Opt: LongWord;
6833 Lives: Integer;
6834 s: String;
6835 Port: Integer;
6836 ip: String;
6837 F: TextFile;
6838 begin
6839 Parse_Params(pars);
6841 // Debug mode:
6842 s := Find_Param_Value(pars, '--debug');
6843 if (s <> '') then
6844 begin
6845 g_Game_SetDebugMode();
6846 s := Find_Param_Value(pars, '--netdump');
6847 if (s <> '') then
6848 NetDump := True;
6849 end;
6851 // Connect when game loads
6852 ip := Find_Param_Value(pars, '-connect');
6854 if ip <> '' then
6855 begin
6856 s := Find_Param_Value(pars, '-port');
6857 if (s = '') or not TryStrToInt(s, Port) then
6858 Port := 25666;
6860 s := Find_Param_Value(pars, '-pw');
6862 g_Game_StartClient(ip, Port, s);
6863 Exit;
6864 end;
6866 s := LowerCase(Find_Param_Value(pars, '-dbg-mainwad'));
6867 if (s <> '') then
6868 begin
6869 gDefaultMegawadStart := s;
6870 end;
6872 if (Find_Param_Value(pars, '--dbg-mainwad-restore') <> '') or
6873 (Find_Param_Value(pars, '--dbg-mainwad-default') <> '') then
6874 begin
6875 gDefaultMegawadStart := DF_Default_Megawad_Start;
6876 end;
6878 // Start map when game loads:
6879 map := LowerCase(Find_Param_Value(pars, '-map'));
6880 if isWadPath(map) then
6881 begin
6882 // Game mode:
6883 s := Find_Param_Value(pars, '-gm');
6884 GMode := g_Game_TextToMode(s);
6885 if GMode = GM_NONE then GMode := GM_DM;
6886 if GMode = GM_SINGLE then GMode := GM_COOP;
6888 // Time limit:
6889 s := Find_Param_Value(pars, '-limt');
6890 if (s = '') or (not TryStrToInt(s, LimT)) then
6891 LimT := 0;
6892 if LimT < 0 then
6893 LimT := 0;
6895 // Goal limit:
6896 s := Find_Param_Value(pars, '-lims');
6897 if (s = '') or (not TryStrToInt(s, LimS)) then
6898 LimS := 0;
6899 if LimS < 0 then
6900 LimS := 0;
6902 // Lives limit:
6903 s := Find_Param_Value(pars, '-lives');
6904 if (s = '') or (not TryStrToInt(s, Lives)) then
6905 Lives := 0;
6906 if Lives < 0 then
6907 Lives := 0;
6909 // Options:
6910 s := Find_Param_Value(pars, '-opt');
6911 if (s = '') then
6912 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6913 else
6914 Opt := StrToIntDef(s, 0);
6915 if Opt = 0 then
6916 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6918 // Close after map:
6919 s := Find_Param_Value(pars, '--close');
6920 if (s <> '') then
6921 gMapOnce := True;
6923 // Delete test map after play:
6924 s := Find_Param_Value(pars, '--testdelete');
6925 if (s <> '') then
6926 begin
6927 gMapToDelete := MapsDir + map;
6928 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6929 Halt(1);
6930 end;
6932 // Delete temporary WAD after play:
6933 s := Find_Param_Value(pars, '--tempdelete');
6934 if (s <> '') then
6935 begin
6936 gMapToDelete := MapsDir + map;
6937 gTempDelete := True;
6938 end;
6940 // Number of players:
6941 s := Find_Param_Value(pars, '-pl');
6942 if (s = '') then
6943 n := 1
6944 else
6945 n := StrToIntDef(s, 1);
6947 // Start:
6948 s := Find_Param_Value(pars, '-port');
6949 if (s = '') or not TryStrToInt(s, Port) then
6950 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6951 else
6952 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6953 end;
6955 // Execute script when game loads:
6956 s := Find_Param_Value(pars, '-exec');
6957 if s <> '' then
6958 begin
6959 if Pos(':\', s) = 0 then
6960 s := GameDir + '/' + s;
6962 {$I-}
6963 AssignFile(F, s);
6964 Reset(F);
6965 if IOResult <> 0 then
6966 begin
6967 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6968 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6969 CloseFile(F);
6970 Exit;
6971 end;
6972 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6973 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6975 while not EOF(F) do
6976 begin
6977 ReadLn(F, s);
6978 if IOResult <> 0 then
6979 begin
6980 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6981 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6982 CloseFile(F);
6983 Exit;
6984 end;
6985 if Pos('#', s) <> 1 then // script comment
6986 g_Console_Process(s, True);
6987 end;
6989 CloseFile(F);
6990 {$I+}
6991 end;
6993 SetLength(pars, 0);
6994 end;
6996 begin
6997 conRegVar('pf_draw_frame', @g_profile_frame_draw, 'draw frame rendering profiles', 'render profiles');
6998 //conRegVar('pf_update_frame', @g_profile_frame_update, 'draw frame updating profiles', 'update profiles');
6999 conRegVar('pf_coldet', @g_profile_collision, 'draw collision detection profiles', 'coldet profiles');
7000 conRegVar('pf_los', @g_profile_los, 'draw monster LOS profiles', 'monster LOS profiles');
7002 conRegVar('r_sq_draw', @gdbg_map_use_accel_render, 'accelerated spatial queries in rendering', 'accelerated rendering');
7003 conRegVar('cd_sq_enabled', @gdbg_map_use_accel_coldet, 'accelerated spatial queries in map coldet', 'accelerated map coldet');
7004 conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet');
7005 conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan');
7007 conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles');
7008 conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics');
7010 conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true);
7011 conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true);
7013 conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true);
7014 end.