DEADSOFTWARE

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