DEADSOFTWARE

removed "potentially visible panels" code in favor of grid
[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 {$MODE DELPHI}
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;
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 procedure g_ResetDynlights ();
313 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
314 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
316 implementation
318 uses
319 g_textures, g_main, g_window, g_menu,
320 e_input, e_log, g_console, g_items, g_map,
321 g_playermodel, g_gfx, g_options, g_weapons, Math,
322 g_triggers, MAPDEF, g_monsters, e_sound, CONFIG,
323 BinEditor, g_language, g_net, SDL,
324 ENet, e_fixedbuffer, g_netmsg, g_netmaster, GL, GLExt,
325 utils, sfs;
327 type
328 TDynLight = record
329 x, y, radius: Integer;
330 r, g, b, a: Single;
331 exploCount: Integer;
332 exploRadius: Integer;
333 end;
335 var
336 g_dynLights: array of TDynLight = nil;
337 g_dynLightCount: Integer = 0;
338 g_playerLight: Boolean = false;
340 procedure g_ResetDynlights ();
341 var
342 lnum, idx: Integer;
343 begin
344 lnum := 0;
345 for idx := 0 to g_dynLightCount-1 do
346 begin
347 if g_dynLights[idx].exploCount = -666 then
348 begin
349 // skip it
350 end
351 else
352 begin
353 // explosion
354 Inc(g_dynLights[idx].exploCount);
355 if (g_dynLights[idx].exploCount < 10) then
356 begin
357 g_dynLights[idx].radius := g_dynLights[idx].exploRadius+g_dynLights[idx].exploCount*8;
358 g_dynLights[idx].a := 0.4+g_dynLights[idx].exploCount/10;
359 if (g_dynLights[idx].a > 0.8) then g_dynLights[idx].a := 0.8;
360 if lnum <> idx then g_dynLights[lnum] := g_dynLights[idx];
361 Inc(lnum);
362 end;
363 end;
364 end;
365 g_dynLightCount := lnum;
366 end;
368 procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single);
369 begin
370 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
371 g_dynLights[g_dynLightCount].x := x;
372 g_dynLights[g_dynLightCount].y := y;
373 g_dynLights[g_dynLightCount].radius := radius;
374 g_dynLights[g_dynLightCount].r := r;
375 g_dynLights[g_dynLightCount].g := g;
376 g_dynLights[g_dynLightCount].b := b;
377 g_dynLights[g_dynLightCount].a := a;
378 g_dynLights[g_dynLightCount].exploCount := -666;
379 Inc(g_dynLightCount);
380 end;
382 procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single);
383 begin
384 if g_dynLightCount = length(g_dynLights) then SetLength(g_dynLights, g_dynLightCount+1024);
385 g_dynLights[g_dynLightCount].x := x;
386 g_dynLights[g_dynLightCount].y := y;
387 g_dynLights[g_dynLightCount].radius := 0;
388 g_dynLights[g_dynLightCount].exploRadius := radius;
389 g_dynLights[g_dynLightCount].r := r;
390 g_dynLights[g_dynLightCount].g := g;
391 g_dynLights[g_dynLightCount].b := b;
392 g_dynLights[g_dynLightCount].a := 0;
393 g_dynLights[g_dynLightCount].exploCount := 0;
394 Inc(g_dynLightCount);
395 end;
397 type
398 TEndCustomGameStat = record
399 PlayerStat: TPlayerStatArray;
400 TeamStat: TTeamStat;
401 GameTime: LongWord;
402 GameMode: Byte;
403 Map, MapName: String;
404 end;
406 TEndSingleGameStat = record
407 PlayerStat: Array [0..1] of record
408 Kills: Integer;
409 Secrets: Integer;
410 end;
411 GameTime: LongWord;
412 TwoPlayers: Boolean;
413 TotalSecrets: Integer;
414 end;
416 TLoadingStat = record
417 CurValue: Integer;
418 MaxValue: Integer;
419 ShowCount: Integer;
420 Msgs: Array of String;
421 NextMsg: Word;
422 end;
424 TParamStrValue = record
425 Name: String;
426 Value: String;
427 end;
429 TParamStrValues = Array of TParamStrValue;
431 const
432 INTER_ACTION_TEXT = 1;
433 INTER_ACTION_PIC = 2;
434 INTER_ACTION_MUSIC = 3;
436 var
437 FPS, UPS: Word;
438 FPSCounter, UPSCounter: Word;
439 FPSTime, UPSTime: LongWord;
440 DataLoaded: Boolean = False;
441 LastScreenShot: Int64;
442 IsDrawStat: Boolean = False;
443 CustomStat: TEndCustomGameStat;
444 SingleStat: TEndSingleGameStat;
445 LoadingStat: TLoadingStat;
446 EndingGameCounter: Byte = 0;
447 MessageText: String;
448 MessageTime: Word;
449 MapList: SArray = nil;
450 MapIndex: Integer = -1;
451 MegaWAD: record
452 info: TMegaWADInfo;
453 endpic: String;
454 endmus: String;
455 res: record
456 text: Array of ShortString;
457 anim: Array of ShortString;
458 pic: Array of ShortString;
459 mus: Array of ShortString;
460 end;
461 triggers: Array of record
462 event: ShortString;
463 actions: Array of record
464 action, p1, p2: Integer;
465 end;
466 end;
467 cur_trigger: Integer;
468 cur_action: Integer;
469 end;
470 //InterPic: String;
471 InterText: record
472 lines: SArray;
473 img: String;
474 cur_line: Integer;
475 cur_char: Integer;
476 counter: Integer;
477 endtext: Boolean;
478 end;
480 function Compare(a, b: TPlayerStat): Integer;
481 begin
482 if a.Spectator then Result := 1
483 else if b.Spectator then Result := -1
484 else if a.Frags < b.Frags then Result := 1
485 else if a.Frags > b.Frags then Result := -1
486 else if a.Deaths < b.Deaths then Result := -1
487 else if a.Deaths > b.Deaths then Result := 1
488 else if a.Kills < b.Kills then Result := -1
489 else Result := 1;
490 end;
492 procedure SortGameStat(var stat: TPlayerStatArray);
493 var
494 I, J: Integer;
495 T: TPlayerStat;
496 begin
497 if stat = nil then Exit;
499 for I := High(stat) downto Low(stat) do
500 for J := Low(stat) to High(stat) - 1 do
501 if Compare(stat[J], stat[J + 1]) = 1 then
502 begin
503 T := stat[J];
504 stat[J] := stat[J + 1];
505 stat[J + 1] := T;
506 end;
507 end;
509 function g_Game_ModeToText(Mode: Byte): string;
510 begin
511 Result := '';
512 case Mode of
513 GM_DM: Result := _lc[I_MENU_GAME_TYPE_DM];
514 GM_TDM: Result := _lc[I_MENU_GAME_TYPE_TDM];
515 GM_CTF: Result := _lc[I_MENU_GAME_TYPE_CTF];
516 GM_COOP: Result := _lc[I_MENU_GAME_TYPE_COOP];
517 GM_SINGLE: Result := _lc[I_MENU_GAME_TYPE_SINGLE];
518 end;
519 end;
521 function g_Game_TextToMode(Mode: string): Byte;
522 begin
523 Result := GM_NONE;
524 Mode := UpperCase(Mode);
525 if Mode = _lc[I_MENU_GAME_TYPE_DM] then
526 begin
527 Result := GM_DM;
528 Exit;
529 end;
530 if Mode = _lc[I_MENU_GAME_TYPE_TDM] then
531 begin
532 Result := GM_TDM;
533 Exit;
534 end;
535 if Mode = _lc[I_MENU_GAME_TYPE_CTF] then
536 begin
537 Result := GM_CTF;
538 Exit;
539 end;
540 if Mode = _lc[I_MENU_GAME_TYPE_COOP] then
541 begin
542 Result := GM_COOP;
543 Exit;
544 end;
545 if Mode = _lc[I_MENU_GAME_TYPE_SINGLE] then
546 begin
547 Result := GM_SINGLE;
548 Exit;
549 end;
550 end;
552 function g_Game_IsNet(): Boolean;
553 begin
554 Result := (gGameSettings.GameType in [GT_SERVER, GT_CLIENT]);
555 end;
557 function g_Game_IsServer(): Boolean;
558 begin
559 Result := (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM, GT_SERVER]);
560 end;
562 function g_Game_IsClient(): Boolean;
563 begin
564 Result := (gGameSettings.GameType = GT_CLIENT);
565 end;
567 function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo;
568 var
569 w: TWADFile;
570 cfg: TConfig;
571 p: Pointer;
572 len: Integer;
573 begin
574 Result.name := ExtractFileName(WAD);
575 Result.description := '';
576 Result.author := '';
578 w := TWADFile.Create();
579 w.ReadFile(WAD);
581 if not w.GetResource('INTERSCRIPT', p, len) then
582 begin
583 w.Free();
584 Exit;
585 end;
587 cfg := TConfig.CreateMem(p, len);
588 Result.name := cfg.ReadStr('megawad', 'name', ExtractFileName(WAD));
589 Result.description := cfg.ReadStr('megawad', 'description', '');
590 Result.author := cfg.ReadStr('megawad', 'author', '');
591 Result.pic := cfg.ReadStr('megawad', 'pic', '');
592 cfg.Free();
594 FreeMem(p);
595 end;
597 procedure g_Game_FreeWAD();
598 var
599 a: Integer;
600 begin
601 for a := 0 to High(MegaWAD.res.pic) do
602 if MegaWAD.res.pic[a] <> '' then
603 g_Texture_Delete(MegaWAD.res.pic[a]);
605 for a := 0 to High(MegaWAD.res.mus) do
606 if MegaWAD.res.mus[a] <> '' then
607 g_Sound_Delete(MegaWAD.res.mus[a]);
609 MegaWAD.res.pic := nil;
610 MegaWAD.res.text := nil;
611 MegaWAD.res.anim := nil;
612 MegaWAD.res.mus := nil;
613 MegaWAD.triggers := nil;
615 g_Texture_Delete('TEXTURE_endpic');
616 g_Sound_Delete('MUSIC_endmus');
618 ZeroMemory(@MegaWAD, SizeOf(MegaWAD));
619 gGameSettings.WAD := '';
620 end;
622 procedure g_Game_LoadWAD(WAD: string);
623 var
624 w: TWADFile;
625 cfg: TConfig;
626 p: Pointer;
627 {b, }len: Integer;
628 s: string;
629 begin
630 g_Game_FreeWAD();
631 gGameSettings.WAD := WAD;
632 if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
633 Exit;
635 MegaWAD.info := g_Game_GetMegaWADInfo(MapsDir + WAD);
637 w := TWADFile.Create();
638 w.ReadFile(MapsDir + WAD);
640 if not w.GetResource('INTERSCRIPT', p, len) then
641 begin
642 w.Free();
643 Exit;
644 end;
646 cfg := TConfig.CreateMem(p, len);
648 {b := 1;
649 while True do
650 begin
651 s := cfg.ReadStr('pic', 'pic'+IntToStr(b), '');
652 if s = '' then Break;
653 b := b+1;
655 SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1);
656 MegaWAD.res.pic[High(MegaWAD.res.pic)] := s;
658 g_Texture_CreateWADEx(s, s);
659 end;
661 b := 1;
662 while True do
663 begin
664 s := cfg.ReadStr('mus', 'mus'+IntToStr(b), '');
665 if s = '' then Break;
666 b := b+1;
668 SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1);
669 MegaWAD.res.mus[High(MegaWAD.res.mus)] := s;
671 g_Music_CreateWADEx(s, s);
672 end;}
674 MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
675 if MegaWAD.endpic <> '' then
676 begin
677 s := g_ExtractWadName(MegaWAD.endpic);
678 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
679 g_Texture_CreateWADEx('TEXTURE_endpic', s+MegaWAD.endpic);
680 end;
681 MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
682 if MegaWAD.endmus <> '' then
683 begin
684 s := g_ExtractWadName(MegaWAD.endmus);
685 if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
686 g_Sound_CreateWADEx('MUSIC_endmus', s+MegaWAD.endmus, True);
687 end;
689 cfg.Free();
690 FreeMem(p);
691 w.Free();
692 end;
694 {procedure start_trigger(t: string);
695 begin
696 end;
698 function next_trigger(): Boolean;
699 begin
700 end;}
702 procedure DisableCheats();
703 begin
704 MAX_RUNVEL := 8;
705 VEL_JUMP := 10;
706 gFly := False;
708 if gPlayer1 <> nil then gPlayer1.GodMode := False;
709 if gPlayer2 <> nil then gPlayer2.GodMode := False;
710 if gPlayer1 <> nil then gPlayer1.NoTarget := False;
711 if gPlayer2 <> nil then gPlayer2.NoTarget := False;
712 end;
714 procedure g_Game_ExecuteEvent(Name: String);
715 var
716 a: Integer;
717 begin
718 if Name = '' then
719 Exit;
720 if gEvents = nil then
721 Exit;
722 for a := 0 to High(gEvents) do
723 if gEvents[a].Name = Name then
724 begin
725 if gEvents[a].Command <> '' then
726 g_Console_Process(gEvents[a].Command, True);
727 break;
728 end;
729 end;
731 function g_Game_DelayEvent(DEType: Byte; Time: LongWord; Num: Integer = 0; Str: String = ''): Integer;
732 var
733 a, n: Integer;
734 begin
735 n := -1;
736 if gDelayedEvents <> nil then
737 for a := 0 to High(gDelayedEvents) do
738 if not gDelayedEvents[a].Pending then
739 begin
740 n := a;
741 break;
742 end;
743 if n = -1 then
744 begin
745 SetLength(gDelayedEvents, Length(gDelayedEvents) + 1);
746 n := High(gDelayedEvents);
747 end;
748 gDelayedEvents[n].Pending := True;
749 gDelayedEvents[n].DEType := DEType;
750 gDelayedEvents[n].DENum := Num;
751 gDelayedEvents[n].DEStr := Str;
752 if DEType = DE_GLOBEVENT then
753 gDelayedEvents[n].Time := (GetTimer() {div 1000}) + Time
754 else
755 gDelayedEvents[n].Time := gTime + Time;
756 Result := n;
757 end;
759 procedure EndGame();
760 var
761 a: Integer;
762 FileName: string;
763 begin
764 if g_Game_IsNet and g_Game_IsServer then
765 MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed));
767 // Ñòîï èãðà:
768 gPause := False;
769 gGameOn := False;
771 g_Game_StopAllSounds(False);
773 MessageTime := 0;
774 MessageText := '';
776 EndingGameCounter := 0;
777 g_ActiveWindow := nil;
779 gLMSRespawn := LMS_RESPAWN_NONE;
780 gLMSRespawnTime := 0;
782 case gExit of
783 EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà
784 begin
785 g_Game_Free();
787 if gMapOnce then
788 begin // Ýòî áûë òåñò
789 g_Game_Quit();
790 end
791 else
792 begin // Âûõîä â ãëàâíîå ìåíþ
793 gMusic.SetByName('MUSIC_MENU');
794 gMusic.Play();
795 if gState <> STATE_SLIST then
796 begin
797 g_GUI_ShowWindow('MainMenu');
798 gState := STATE_MENU;
799 end else
800 begin
801 // Îáíîâëÿåì ñïèñîê ñåðâåðîâ
802 slReturnPressed := True;
803 if g_Net_Slist_Fetch(slCurrent) then
804 begin
805 if slCurrent = nil then
806 slWaitStr := _lc[I_NET_SLIST_NOSERVERS];
807 end
808 else
809 slWaitStr := _lc[I_NET_SLIST_ERROR];
810 end;
812 g_Game_ExecuteEvent('ongameend');
813 end;
814 end;
816 EXIT_RESTART: // Íà÷àòü óðîâåíü ñíà÷àëà
817 begin
818 if not g_Game_IsClient then g_Game_Restart();
819 end;
821 EXIT_ENDLEVELCUSTOM: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå
822 begin
823 // Ñòàòèñòèêà Ñâîåé èãðû:
824 FileName := g_ExtractWadName(gMapInfo.Map);
826 CustomStat.GameTime := gTime;
827 CustomStat.Map := ExtractFileName(FileName)+':'+g_ExtractFileName(gMapInfo.Map); //ResName;
828 CustomStat.MapName := gMapInfo.Name;
829 CustomStat.GameMode := gGameSettings.GameMode;
830 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
831 CustomStat.TeamStat := gTeamStat;
833 CustomStat.PlayerStat := nil;
835 // Ñòàòèñòèêà èãðîêîâ:
836 if gPlayers <> nil then
837 begin
838 for a := 0 to High(gPlayers) do
839 if gPlayers[a] <> nil then
840 begin
841 SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1);
842 with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do
843 begin
844 Name := gPlayers[a].Name;
845 Frags := gPlayers[a].Frags;
846 Deaths := gPlayers[a].Death;
847 Kills := gPlayers[a].Kills;
848 Team := gPlayers[a].Team;
849 Color := gPlayers[a].Model.Color;
850 Spectator := gPlayers[a].FSpectator;
851 end;
852 end;
854 SortGameStat(CustomStat.PlayerStat);
855 end;
857 g_Game_ExecuteEvent('onmapend');
859 // Çàòóõàþùèé ýêðàí:
860 EndingGameCounter := 255;
861 gState := STATE_FOLD;
862 gInterTime := 0;
863 if gDefInterTime < 0 then
864 gInterEndTime := IfThen((gGameSettings.GameType = GT_SERVER) and (gPlayer1 = nil), 15000, 25000)
865 else
866 gInterEndTime := gDefInterTime * 1000;
867 end;
869 EXIT_ENDLEVELSINGLE: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå
870 begin
871 // Ñòàòèñòèêà Îäèíî÷íîé èãðû:
872 SingleStat.GameTime := gTime;
873 SingleStat.TwoPlayers := gPlayer2 <> nil;
874 SingleStat.TotalSecrets := gSecretsCount;
875 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
876 SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills;
877 SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets;
878 // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü):
879 if SingleStat.TwoPlayers then
880 begin
881 SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills;
882 SingleStat.PlayerStat[1].Secrets := gPlayer2.Secrets;
883 end;
885 g_Game_ExecuteEvent('onmapend');
887 // Åñòü åùå êàðòû:
888 if gNextMap <> '' then
889 begin
890 gMusic.SetByName('MUSIC_INTERMUS');
891 gMusic.Play();
892 gState := STATE_INTERSINGLE;
894 g_Game_ExecuteEvent('oninter');
895 end
896 else // Áîëüøå íåò êàðò
897 begin
898 // Çàòóõàþùèé ýêðàí:
899 EndingGameCounter := 255;
900 gState := STATE_FOLD;
901 end;
902 end;
903 end;
905 // Îêîí÷àíèå îáðàáîòàíî:
906 if gExit <> EXIT_QUIT then
907 gExit := 0;
908 end;
910 procedure DrawStat();
911 var
912 pc, x, y, w, h: Integer;
913 w1, w2, w3, w4: Integer;
914 a, aa: Integer;
915 cw, ch, r, g, b, rr, gg, bb: Byte;
916 s1, s2, s3: String;
917 _y: Integer;
918 stat: TPlayerStatArray;
919 wad, map: string;
920 mapstr: string;
921 begin
922 s1 := '';
923 s2 := '';
924 s3 := '';
925 pc := g_Player_GetCount;
926 e_TextureFontGetSize(gStdFont, cw, ch);
928 w := gScreenWidth-(gScreenWidth div 5);
929 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
930 h := 32+ch*(11+pc)
931 else
932 h := 40+ch*5+(ch+8)*pc;
933 x := (gScreenWidth div 2)-(w div 2);
934 y := (gScreenHeight div 2)-(h div 2);
936 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
937 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
939 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
940 map := g_ExtractFileName(gMapInfo.Map);
941 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
943 case gGameSettings.GameMode of
944 GM_DM:
945 begin
946 if gGameSettings.MaxLives = 0 then
947 s1 := _lc[I_GAME_DM]
948 else
949 s1 := _lc[I_GAME_LMS];
950 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
951 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
952 end;
954 GM_TDM:
955 begin
956 if gGameSettings.MaxLives = 0 then
957 s1 := _lc[I_GAME_TDM]
958 else
959 s1 := _lc[I_GAME_TLMS];
960 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
961 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
962 end;
964 GM_CTF:
965 begin
966 s1 := _lc[I_GAME_CTF];
967 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]);
968 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
969 end;
971 GM_COOP:
972 begin
973 if gGameSettings.MaxLives = 0 then
974 s1 := _lc[I_GAME_COOP]
975 else
976 s1 := _lc[I_GAME_SURV];
977 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
978 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
979 end;
981 else
982 begin
983 s1 := '';
984 s2 := '';
985 end;
986 end;
988 _y := y+8;
989 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
990 _y := _y+ch+8;
991 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
992 _y := _y+ch+8;
993 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
995 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3,
996 gStdFont, 200, 200, 200, 1);
998 if NetMode = NET_SERVER then
999 e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1)
1000 else
1001 if NetMode = NET_CLIENT then
1002 e_TextureFontPrintEx(x+8, y + 8,
1003 NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1);
1005 if pc = 0 then
1006 Exit;
1007 stat := g_Player_GetStats();
1008 SortGameStat(stat);
1010 w2 := (w-16) div 6 + 48; // øèðèíà 2 ñòîëáöà
1011 w3 := (w-16) div 6; // øèðèíà 3 è 4 ñòîëáöîâ
1012 w4 := w3;
1013 w1 := w-16-w2-w3-w4; // îñòàâøååñÿ ïðîñòðàíñòâî - äëÿ öâåòà è èìåíè èãðîêà
1015 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1016 begin
1017 _y := _y+ch+ch;
1019 for a := TEAM_RED to TEAM_BLUE do
1020 begin
1021 if a = TEAM_RED then
1022 begin
1023 s1 := _lc[I_GAME_TEAM_RED];
1024 r := 255;
1025 g := 0;
1026 b := 0;
1027 end
1028 else
1029 begin
1030 s1 := _lc[I_GAME_TEAM_BLUE];
1031 r := 0;
1032 g := 0;
1033 b := 255;
1034 end;
1036 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
1037 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals),
1038 gStdFont, r, g, b, 1);
1040 _y := _y+ch+(ch div 4);
1041 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
1042 _y := _y+(ch div 4);
1044 for aa := 0 to High(stat) do
1045 if stat[aa].Team = a then
1046 with stat[aa] do
1047 begin
1048 if Spectator then
1049 begin
1050 rr := r div 2;
1051 gg := g div 2;
1052 bb := b div 2;
1053 end
1054 else
1055 begin
1056 rr := r;
1057 gg := g;
1058 bb := b;
1059 end;
1060 // Èìÿ
1061 e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
1062 // Ïèíã/ïîòåðè
1063 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
1064 // Ôðàãè
1065 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
1066 // Ñìåðòè
1067 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
1068 _y := _y+ch;
1069 end;
1071 _y := _y+ch;
1072 end;
1073 end
1074 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
1075 begin
1076 _y := _y+ch+ch;
1077 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
1078 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
1079 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
1080 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
1082 _y := _y+ch+8;
1083 for aa := 0 to High(stat) do
1084 with stat[aa] do
1085 begin
1086 if Spectator then
1087 begin
1088 r := 127;
1089 g := 64;
1090 end
1091 else
1092 begin
1093 r := 255;
1094 g := 127;
1095 end;
1096 // Öâåò èãðîêà
1097 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
1098 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
1099 // Èìÿ
1100 e_TextureFontPrintEx(x+16+16+8, _y+4, Name, gStdFont, r, g, 0, 1);
1101 // Ïèíã/ïîòåðè
1102 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
1103 // Ôðàãè
1104 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
1105 // Ñìåðòè
1106 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
1107 _y := _y+ch+8;
1108 end;
1109 end
1110 end;
1112 procedure g_Game_Init();
1113 var
1114 SR: TSearchRec;
1115 begin
1116 gExit := 0;
1117 gMapToDelete := '';
1118 gTempDelete := False;
1120 sfsGCDisable(); // temporary disable removing of temporary volumes
1122 try
1123 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
1124 g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
1125 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
1126 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
1128 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
1129 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
1130 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
1132 g_Game_ClearLoading();
1133 g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
1134 g_Game_SetLoadingText('', 0, False);
1136 g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
1137 g_Console_Init();
1139 g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
1140 g_PlayerModel_LoadData();
1142 if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then
1143 repeat
1144 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1145 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1146 until FindNext(SR) <> 0;
1147 FindClose(SR);
1149 if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then
1150 repeat
1151 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1152 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1153 until FindNext(SR) <> 0;
1154 FindClose(SR);
1156 if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then
1157 repeat
1158 if not g_PlayerModel_Load(ModelsDir+SR.Name) then
1159 e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
1160 until FindNext(SR) <> 0;
1161 FindClose(SR);
1163 gGameOn := False;
1164 gPause := False;
1165 gTime := 0;
1166 LastScreenShot := 0;
1168 {e_MouseInfo.Accel := 1.0;}
1170 g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
1171 g_Game_LoadData();
1173 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1174 g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
1175 g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
1176 g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True);
1177 g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
1179 g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
1180 g_Menu_Init();
1182 gMusic := TMusic.Create();
1183 gMusic.SetByName('MUSIC_MENU');
1184 gMusic.Play();
1186 gGameSettings.WarmupTime := 30;
1188 gState := STATE_MENU;
1190 SetLength(gEvents, 6);
1191 gEvents[0].Name := 'ongamestart';
1192 gEvents[1].Name := 'ongameend';
1193 gEvents[2].Name := 'onmapstart';
1194 gEvents[3].Name := 'onmapend';
1195 gEvents[4].Name := 'oninter';
1196 gEvents[5].Name := 'onwadend';
1197 finally
1198 sfsGCEnable(); // enable releasing unused volumes
1199 end;
1200 end;
1202 procedure g_Game_Free();
1203 begin
1204 if NetMode = NET_CLIENT then g_Net_Disconnect();
1205 if NetMode = NET_SERVER then g_Net_Host_Die();
1207 g_Map_Free();
1208 g_Player_Free();
1209 g_Player_RemoveAllCorpses();
1211 gGameSettings.GameType := GT_NONE;
1212 if gGameSettings.GameMode = GM_SINGLE then
1213 gGameSettings.GameMode := GM_DM;
1214 gSwitchGameMode := gGameSettings.GameMode;
1216 gChatShow := False;
1217 gExitByTrigger := False;
1218 end;
1220 function IsActivePlayer(p: TPlayer): Boolean;
1221 begin
1222 Result := False;
1223 if p = nil then
1224 Exit;
1225 Result := (not p.FDummy) and (not p.FSpectator);
1226 end;
1228 function GetActivePlayer_ByID(ID: Integer): TPlayer;
1229 var
1230 a: Integer;
1231 begin
1232 Result := nil;
1233 if ID < 0 then
1234 Exit;
1235 if gPlayers = nil then
1236 Exit;
1237 for a := Low(gPlayers) to High(gPlayers) do
1238 if IsActivePlayer(gPlayers[a]) then
1239 begin
1240 if gPlayers[a].UID <> ID then
1241 continue;
1242 Result := gPlayers[a];
1243 break;
1244 end;
1245 end;
1247 function GetActivePlayerID_Next(Skip: Integer = -1): Integer;
1248 var
1249 a, idx: Integer;
1250 ids: Array of Word;
1251 begin
1252 Result := -1;
1253 if gPlayers = nil then
1254 Exit;
1255 SetLength(ids, 0);
1256 idx := -1;
1257 for a := Low(gPlayers) to High(gPlayers) do
1258 if IsActivePlayer(gPlayers[a]) then
1259 begin
1260 SetLength(ids, Length(ids) + 1);
1261 ids[High(ids)] := gPlayers[a].UID;
1262 if gPlayers[a].UID = Skip then
1263 idx := High(ids);
1264 end;
1265 if Length(ids) = 0 then
1266 Exit;
1267 if idx = -1 then
1268 Result := ids[0]
1269 else
1270 Result := ids[(idx + 1) mod Length(ids)];
1271 end;
1273 function GetActivePlayerID_Prev(Skip: Integer = -1): Integer;
1274 var
1275 a, idx: Integer;
1276 ids: Array of Word;
1277 begin
1278 Result := -1;
1279 if gPlayers = nil then
1280 Exit;
1281 SetLength(ids, 0);
1282 idx := -1;
1283 for a := Low(gPlayers) to High(gPlayers) do
1284 if IsActivePlayer(gPlayers[a]) then
1285 begin
1286 SetLength(ids, Length(ids) + 1);
1287 ids[High(ids)] := gPlayers[a].UID;
1288 if gPlayers[a].UID = Skip then
1289 idx := High(ids);
1290 end;
1291 if Length(ids) = 0 then
1292 Exit;
1293 if idx = -1 then
1294 Result := ids[Length(ids) - 1]
1295 else
1296 Result := ids[(Length(ids) - 1 + idx) mod Length(ids)];
1297 end;
1299 function isKeyPressed (key1: Word; key2: Word): Boolean;
1300 begin
1301 if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
1302 if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
1303 result := false;
1304 end;
1306 procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false);
1307 var
1308 time: Word;
1309 strafeDir: Byte;
1310 i: Integer;
1311 begin
1312 if (plr = nil) then exit;
1313 if (p2hack) then time := 1000 else time := 1;
1314 strafeDir := MoveButton shr 4;
1315 MoveButton := MoveButton and $0F;
1316 with ctrl do
1317 begin
1318 if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî"
1319 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî"
1320 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî"
1322 // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó:
1323 if MoveButton = 1 then plr.PressKey(KEY_LEFT, time)
1324 else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time);
1326 // if we have "strafe" key, turn off old strafe mechanics
1327 if isKeyPressed(KeyStrafe, KeyStrafe2) then
1328 begin
1329 // new strafe mechanics
1330 if (strafeDir = 0) then strafeDir := MoveButton; // start strafing
1331 // now set direction according to strafe (reversed)
1332 if (strafeDir = 2) then plr.SetDirection(D_LEFT)
1333 else if (strafeDir = 1) then plr.SetDirection(D_RIGHT);
1334 end
1335 else
1336 begin
1337 strafeDir := 0; // not strafing anymore
1338 // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî:
1339 if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(D_LEFT)
1340 // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî:
1341 else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(D_RIGHT)
1342 // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì:
1343 else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1));
1344 end;
1346 // fix movebutton state
1347 MoveButton := MoveButton or (strafeDir shl 4);
1349 // Îñòàëüíûå êëàâèøè:
1350 if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time);
1351 if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time);
1352 if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time);
1353 if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE);
1354 if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON);
1355 if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON);
1356 if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN);
1358 for i := 0 to High(KeyWeapon) do
1359 if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then
1360 plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best
1361 end;
1363 // HACK: add dynlight here
1364 if e_KeyPressed(IK_F8) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1365 begin
1366 g_playerLight := true;
1367 end;
1368 if e_KeyPressed(IK_F9) and gGameOn and (not gConsoleShow) and (g_ActiveWindow = nil) then
1369 begin
1370 g_playerLight := false;
1371 end;
1373 if (g_playerLight) then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6);
1374 end;
1376 procedure g_Game_Update();
1377 var
1378 Msg: g_gui.TMessage;
1379 Time: Int64;
1380 a: Byte;
1381 w: Word;
1382 i, b: Integer;
1383 begin
1384 g_ResetDynlights();
1385 // Ïîðà âûêëþ÷àòü èãðó:
1386 if gExit = EXIT_QUIT then
1387 Exit;
1388 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1389 if gExit <> 0 then
1390 begin
1391 EndGame();
1392 if gExit = EXIT_QUIT then
1393 Exit;
1394 end;
1396 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1397 e_PollInput();
1399 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1400 g_Console_Update();
1402 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1403 begin
1404 gExit := EXIT_SIMPLE;
1405 EndGame();
1406 Exit;
1407 end;
1409 case gState of
1410 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1411 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1412 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1413 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1414 begin
1415 if g_Game_IsNet and g_Game_IsServer then
1416 begin
1417 gInterTime := gInterTime + GAME_TICK;
1418 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1419 if a <> gServInterTime then
1420 begin
1421 gServInterTime := a;
1422 MH_SEND_TimeSync(gServInterTime);
1423 end;
1424 end;
1426 if (not g_Game_IsClient) and
1429 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1430 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1431 and (g_ActiveWindow = nil)
1433 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1435 then
1436 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1437 g_Game_StopAllSounds(True);
1439 if gMapOnce then // Ýòî áûë òåñò
1440 gExit := EXIT_SIMPLE
1441 else
1442 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1443 g_Game_ChangeMap(gNextMap)
1444 else // Ñëåäóþùåé êàðòû íåò
1445 begin
1446 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1447 begin
1448 // Âûõîä â ãëàâíîå ìåíþ:
1449 g_Game_Free;
1450 g_GUI_ShowWindow('MainMenu');
1451 gMusic.SetByName('MUSIC_MENU');
1452 gMusic.Play();
1453 gState := STATE_MENU;
1454 end else
1455 begin
1456 // Ôèíàëüíàÿ êàðòèíêà:
1457 g_Game_ExecuteEvent('onwadend');
1458 g_Game_Free();
1459 if not gMusic.SetByName('MUSIC_endmus') then
1460 gMusic.SetByName('MUSIC_STDENDMUS');
1461 gMusic.Play();
1462 gState := STATE_ENDPIC;
1463 end;
1464 g_Game_ExecuteEvent('ongameend');
1465 end;
1467 Exit;
1468 end;
1470 if gState = STATE_INTERTEXT then
1471 if InterText.counter > 0 then
1472 InterText.counter := InterText.counter - 1;
1473 end;
1475 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1476 begin
1477 if EndingGameCounter = 0 then
1478 begin
1479 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1480 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1481 begin
1482 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1483 begin
1484 g_Game_ExecuteEvent('onwadend');
1485 if not gMusic.SetByName('MUSIC_endmus') then
1486 gMusic.SetByName('MUSIC_STDENDMUS');
1487 end
1488 else
1489 gMusic.SetByName('MUSIC_ROUNDMUS');
1491 gMusic.Play();
1492 gState := STATE_INTERCUSTOM;
1493 end
1494 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1495 begin
1496 gMusic.SetByName('MUSIC_INTERMUS');
1497 gMusic.Play();
1498 gState := STATE_INTERSINGLE;
1499 end;
1500 g_Game_ExecuteEvent('oninter');
1501 end
1502 else
1503 DecMin(EndingGameCounter, 6, 0);
1504 end;
1506 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1507 begin
1508 if gMapOnce then // Ýòî áûë òåñò
1509 begin
1510 gExit := EXIT_SIMPLE;
1511 Exit;
1512 end;
1513 end;
1515 STATE_SLIST:
1516 g_Serverlist_Control(slCurrent);
1517 end;
1519 if g_Game_IsNet then
1520 if not gConsoleShow then
1521 if not gChatShow then
1522 begin
1523 if g_ActiveWindow = nil then
1524 begin
1525 if e_KeyPressed(gGameControls.GameControls.Chat) then
1526 g_Console_Chat_Switch(False)
1527 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1528 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1529 g_Console_Chat_Switch(True);
1530 end;
1531 end else
1532 if not gChatEnter then
1533 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1534 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1535 gChatEnter := True;
1537 // Ñòàòèñòèêà ïî Tab:
1538 if gGameOn then
1539 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1540 (gGameSettings.GameType <> GT_SINGLE) and
1541 e_KeyPressed(gGameControls.GameControls.Stat);
1543 // Èãðà èäåò:
1544 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1545 begin
1546 // Âðåìÿ += 28 ìèëëèñåêóíä:
1547 gTime := gTime + GAME_TICK;
1549 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1550 if MessageTime = 0 then
1551 MessageText := '';
1552 if MessageTime > 0 then
1553 MessageTime := MessageTime - 1;
1555 if (g_Game_IsServer) then
1556 begin
1557 // Áûë çàäàí ëèìèò âðåìåíè:
1558 if (gGameSettings.TimeLimit > 0) then
1559 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1560 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1561 g_Game_NextLevel();
1562 Exit;
1563 end;
1565 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1566 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1567 g_Game_RestartRound(gLMSSoftSpawn);
1569 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1570 if gVoteInProgress and (gVoteTimer < gTime) then
1571 g_Game_CheckVote
1572 else if gVotePassed and (gVoteCmdTimer < gTime) then
1573 begin
1574 g_Console_Process(gVoteCommand);
1575 gVoteCommand := '';
1576 gVotePassed := False;
1577 end;
1579 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1580 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1581 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1582 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1583 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1585 // Áûë çàäàí ëèìèò ïîáåä:
1586 if (gGameSettings.GoalLimit > 0) then
1587 begin
1588 b := 0;
1590 if gGameSettings.GameMode = GM_DM then
1591 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1592 for i := 0 to High(gPlayers) do
1593 if gPlayers[i] <> nil then
1594 if gPlayers[i].Frags > b then
1595 b := gPlayers[i].Frags;
1596 end
1597 else
1598 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1599 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1600 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1601 end;
1603 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1604 if b >= gGameSettings.GoalLimit then
1605 begin
1606 g_Game_NextLevel();
1607 Exit;
1608 end;
1609 end;
1611 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1612 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1613 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1614 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1615 begin
1616 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1617 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1618 end // if not console
1619 else
1620 begin
1621 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1622 end;
1623 // process weapon switch queue
1624 end; // if server
1626 // Íàáëþäàòåëü
1627 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1628 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1629 begin
1630 if not gSpectKeyPress then
1631 begin
1632 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1633 begin
1634 // switch spect mode
1635 case gSpectMode of
1636 SPECT_NONE: ; // not spectator
1637 SPECT_STATS,
1638 SPECT_MAPVIEW: Inc(gSpectMode);
1639 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1640 end;
1641 gSpectKeyPress := True;
1642 end;
1643 if gSpectMode = SPECT_MAPVIEW then
1644 begin
1645 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1646 gSpectX := Max(gSpectX - gSpectStep, 0);
1647 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1648 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1649 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1650 gSpectY := Max(gSpectY - gSpectStep, 0);
1651 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1652 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1653 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1654 begin
1655 // decrease step
1656 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1657 gSpectKeyPress := True;
1658 end;
1659 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1660 begin
1661 // increase step
1662 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1663 gSpectKeyPress := True;
1664 end;
1665 end;
1666 if gSpectMode = SPECT_PLAYERS then
1667 begin
1668 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1669 begin
1670 // add second view
1671 gSpectViewTwo := True;
1672 gSpectKeyPress := True;
1673 end;
1674 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1675 begin
1676 // remove second view
1677 gSpectViewTwo := False;
1678 gSpectKeyPress := True;
1679 end;
1680 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1681 begin
1682 // prev player (view 1)
1683 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1684 gSpectKeyPress := True;
1685 end;
1686 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1687 begin
1688 // next player (view 1)
1689 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1690 gSpectKeyPress := True;
1691 end;
1692 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1693 begin
1694 // prev player (view 2)
1695 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1696 gSpectKeyPress := True;
1697 end;
1698 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1699 begin
1700 // next player (view 2)
1701 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1702 gSpectKeyPress := True;
1703 end;
1704 end;
1705 end
1706 else
1707 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1708 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1709 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1710 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1711 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1712 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1713 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1714 gSpectKeyPress := False;
1715 end;
1717 // Îáíîâëÿåì âñå îñòàëüíîå:
1718 g_Map_Update();
1719 g_Items_Update();
1720 g_Triggers_Update();
1721 g_Weapon_Update();
1722 g_Monsters_Update();
1723 g_GFX_Update();
1724 g_Player_UpdateAll();
1725 g_Player_UpdatePhysicalObjects();
1726 if gGameSettings.GameType = GT_SERVER then
1727 if Length(gMonstersSpawned) > 0 then
1728 begin
1729 for I := 0 to High(gMonstersSpawned) do
1730 MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1731 SetLength(gMonstersSpawned, 0);
1732 end;
1734 if (gSoundTriggerTime > 8) then
1735 begin
1736 g_Game_UpdateTriggerSounds();
1737 gSoundTriggerTime := 0;
1738 end
1739 else
1740 Inc(gSoundTriggerTime);
1742 if (NetMode = NET_SERVER) then
1743 begin
1744 Inc(NetTimeToUpdate);
1745 Inc(NetTimeToReliable);
1746 if NetTimeToReliable >= NetRelupdRate then
1747 begin
1748 for I := 0 to High(gPlayers) do
1749 if gPlayers[I] <> nil then
1750 MH_SEND_PlayerPos(True, gPlayers[I].UID);
1752 if gMonsters <> nil then
1753 for I := 0 to High(gMonsters) do
1754 if gMonsters[I] <> nil then
1755 begin
1756 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1757 begin
1758 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1759 MH_SEND_MonsterPos(gMonsters[I].UID);
1760 end
1761 else
1762 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1763 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1764 (gMonsters[I].GameVelX <> 0) or
1765 (gMonsters[I].GameVelY <> 0) then
1766 MH_SEND_MonsterPos(gMonsters[I].UID);
1767 end;
1769 NetTimeToReliable := 0;
1770 NetTimeToUpdate := NetUpdateRate;
1771 end
1772 else if NetTimeToUpdate >= NetUpdateRate then
1773 begin
1774 if gPlayers <> nil then
1775 for I := 0 to High(gPlayers) do
1776 if gPlayers[I] <> nil then
1777 MH_SEND_PlayerPos(False, gPlayers[I].UID);
1779 if gMonsters <> nil then
1780 for I := 0 to High(gMonsters) do
1781 if gMonsters[I] <> nil then
1782 begin
1783 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1784 begin
1785 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1786 MH_SEND_MonsterPos(gMonsters[I].UID);
1787 end
1788 else
1789 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1790 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1791 (gMonsters[I].GameVelX <> 0) or
1792 (gMonsters[I].GameVelY <> 0) then
1793 MH_SEND_MonsterPos(gMonsters[I].UID);
1794 end;
1796 NetTimeToUpdate := 0;
1797 end;
1799 if NetUseMaster then
1800 if gTime >= NetTimeToMaster then
1801 begin
1802 if (NetMHost = nil) or (NetMPeer = nil) then
1803 if not g_Net_Slist_Connect then
1804 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1806 g_Net_Slist_Update;
1807 NetTimeToMaster := gTime + NetMasterRate;
1808 end;
1809 end
1810 else
1811 if NetMode = NET_CLIENT then
1812 MC_SEND_PlayerPos();
1813 end; // if gameOn ...
1815 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1816 if g_ActiveWindow <> nil then
1817 begin
1818 w := e_GetFirstKeyPressed();
1820 if (w <> IK_INVALID) then
1821 begin
1822 Msg.Msg := MESSAGE_DIKEY;
1823 Msg.wParam := w;
1824 g_ActiveWindow.OnMessage(Msg);
1825 end;
1827 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1828 if g_ActiveWindow <> nil then
1829 g_ActiveWindow.Update();
1831 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1832 if gResolutionChange then
1833 begin
1834 e_WriteLog('Changing resolution', MSG_NOTIFY);
1835 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1836 gResolutionChange := False;
1837 end;
1839 // Íóæíî ñìåíèòü ÿçûê:
1840 if gLanguageChange then
1841 begin
1842 //e_WriteLog('Read language file', MSG_NOTIFY);
1843 //g_Language_Load(DataDir + gLanguage + '.txt');
1844 g_Language_Set(gLanguage);
1845 g_Menu_Reset();
1846 gLanguageChange := False;
1847 end;
1848 end;
1850 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
1851 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
1852 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
1853 begin
1854 g_TakeScreenShot();
1855 LastScreenShot := GetTimer();
1856 end;
1858 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
1859 if e_KeyPressed(IK_F10) and
1860 gGameOn and
1861 (not gConsoleShow) and
1862 (g_ActiveWindow = nil) then
1863 begin
1864 KeyPress(IK_F10);
1865 end;
1867 Time := GetTimer() {div 1000};
1869 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
1870 if gDelayedEvents <> nil then
1871 for a := 0 to High(gDelayedEvents) do
1872 if gDelayedEvents[a].Pending and
1874 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
1875 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
1876 ) then
1877 begin
1878 case gDelayedEvents[a].DEType of
1879 DE_GLOBEVENT:
1880 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
1881 DE_BFGHIT:
1882 if gGameOn then
1883 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
1884 DE_KILLCOMBO:
1885 if gGameOn then
1886 begin
1887 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
1888 if g_Game_IsNet and g_Game_IsServer then
1889 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
1890 end;
1891 end;
1892 gDelayedEvents[a].Pending := False;
1893 end;
1895 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
1896 UPSCounter := UPSCounter + 1;
1897 if Time - UPSTime >= 1000 then
1898 begin
1899 UPS := UPSCounter;
1900 UPSCounter := 0;
1901 UPSTime := Time;
1902 end;
1904 if gGameOn then g_Weapon_AddDynLights();
1905 end;
1907 procedure g_Game_LoadData();
1908 begin
1909 if DataLoaded then Exit;
1911 e_WriteLog('Loading game data...', MSG_NOTIFY);
1913 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
1914 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
1915 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
1916 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
1917 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
1918 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
1919 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB');
1920 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS');
1921 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD');
1922 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB');
1923 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS');
1924 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD');
1925 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
1926 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
1927 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
1928 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
1929 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
1930 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
1931 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
1932 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
1933 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
1934 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
1935 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
1936 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
1937 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
1938 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
1939 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
1940 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
1941 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
1942 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
1943 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
1944 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
1945 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
1946 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
1948 goodsnd[0] := TPlayableSound.Create();
1949 goodsnd[1] := TPlayableSound.Create();
1950 goodsnd[2] := TPlayableSound.Create();
1951 goodsnd[3] := TPlayableSound.Create();
1953 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
1954 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
1955 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
1956 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
1958 killsnd[0] := TPlayableSound.Create();
1959 killsnd[1] := TPlayableSound.Create();
1960 killsnd[2] := TPlayableSound.Create();
1961 killsnd[3] := TPlayableSound.Create();
1963 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
1964 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
1965 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
1966 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
1968 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
1969 g_Items_LoadData();
1971 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
1972 g_Weapon_LoadData();
1974 g_Monsters_LoadData();
1976 DataLoaded := True;
1977 end;
1979 procedure g_Game_FreeData();
1980 begin
1981 if not DataLoaded then Exit;
1983 g_Items_FreeData();
1984 g_Weapon_FreeData();
1985 g_Monsters_FreeData();
1987 e_WriteLog('Releasing game data...', MSG_NOTIFY);
1989 g_Texture_Delete('NOTEXTURE');
1990 g_Texture_Delete('TEXTURE_PLAYER_HUD');
1991 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
1992 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
1993 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
1994 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
1995 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
1996 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
1997 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
1998 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
1999 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2000 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2001 g_Frames_DeleteByName('FRAMES_TELEPORT');
2002 g_Sound_Delete('SOUND_GAME_TELEPORT');
2003 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2004 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2005 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2006 g_Sound_Delete('SOUND_GAME_BULK1');
2007 g_Sound_Delete('SOUND_GAME_BULK2');
2008 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2009 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2010 g_Sound_Delete('SOUND_GAME_SWITCH1');
2011 g_Sound_Delete('SOUND_GAME_SWITCH0');
2013 goodsnd[0].Free();
2014 goodsnd[1].Free();
2015 goodsnd[2].Free();
2016 goodsnd[3].Free();
2018 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2019 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2020 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2021 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2023 killsnd[0].Free();
2024 killsnd[1].Free();
2025 killsnd[2].Free();
2026 killsnd[3].Free();
2028 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2029 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2030 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2031 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2033 DataLoaded := False;
2034 end;
2036 procedure DrawCustomStat();
2037 var
2038 pc, x, y, w, _y,
2039 w1, w2, w3,
2040 t, p, m: Integer;
2041 ww1, hh1: Word;
2042 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2043 s1, s2, topstr: String;
2044 begin
2045 e_TextureFontGetSize(gStdFont, ww2, hh2);
2047 e_PollInput();
2048 if e_KeyPressed(IK_TAB) then
2049 begin
2050 if not gStatsPressed then
2051 begin
2052 gStatsOff := not gStatsOff;
2053 gStatsPressed := True;
2054 end;
2055 end
2056 else
2057 gStatsPressed := False;
2059 if gStatsOff then
2060 begin
2061 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2062 w := (Length(s1) * ww2) div 2;
2063 x := gScreenWidth div 2 - w;
2064 y := 8;
2065 e_TextureFontPrint(x, y, s1, gStdFont);
2066 Exit;
2067 end;
2069 if (gGameSettings.GameMode = GM_COOP) then
2070 begin
2071 if gMissionFailed then
2072 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2073 else
2074 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2075 end
2076 else
2077 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2079 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2080 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2082 if g_Game_IsNet then
2083 begin
2084 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2085 if not gChatShow then
2086 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2087 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2088 end;
2090 if g_Game_IsClient then
2091 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2092 else
2093 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2094 if not gChatShow then
2095 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2096 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2098 x := 32;
2099 y := 16+hh1+16;
2101 w := gScreenWidth-x*2;
2103 w2 := (w-16) div 6;
2104 w3 := w2;
2105 w1 := w-16-w2-w3;
2107 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2108 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2110 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2112 case CustomStat.GameMode of
2113 GM_DM:
2114 begin
2115 if gGameSettings.MaxLives = 0 then
2116 s1 := _lc[I_GAME_DM]
2117 else
2118 s1 := _lc[I_GAME_LMS];
2119 end;
2120 GM_TDM:
2121 begin
2122 if gGameSettings.MaxLives = 0 then
2123 s1 := _lc[I_GAME_TDM]
2124 else
2125 s1 := _lc[I_GAME_TLMS];
2126 end;
2127 GM_CTF: s1 := _lc[I_GAME_CTF];
2128 GM_COOP:
2129 begin
2130 if gGameSettings.MaxLives = 0 then
2131 s1 := _lc[I_GAME_COOP]
2132 else
2133 s1 := _lc[I_GAME_SURV];
2134 end;
2135 else s1 := '';
2136 end;
2138 _y := y+16;
2139 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2140 _y := _y+8;
2142 _y := _y+16;
2143 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2144 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2146 _y := _y+16;
2147 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2148 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2149 (CustomStat.GameTime div 1000 div 60) mod 60,
2150 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2152 pc := Length(CustomStat.PlayerStat);
2153 if pc = 0 then Exit;
2155 if CustomStat.GameMode = GM_COOP then
2156 begin
2157 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2158 _y := _y+32;
2159 s2 := _lc[I_GAME_MONSTERS];
2160 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2161 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2162 _y := _y+16;
2163 s2 := _lc[I_GAME_SECRETS];
2164 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2165 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2166 if gLastMap then
2167 begin
2168 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2169 _y := _y-16;
2170 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2171 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2172 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2173 _y := _y+16;
2174 s2 := _lc[I_GAME_SECRETS_TOTAL];
2175 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2176 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2177 end;
2178 end;
2180 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2181 begin
2182 _y := _y+16+16;
2184 with CustomStat do
2185 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2186 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2187 else s1 := _lc[I_GAME_WIN_DRAW];
2189 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2190 _y := _y+40;
2192 for t := TEAM_RED to TEAM_BLUE do
2193 begin
2194 if t = TEAM_RED then
2195 begin
2196 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2197 gStdFont, 255, 0, 0, 1);
2198 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2199 gStdFont, 255, 0, 0, 1);
2200 r := 255;
2201 g := 0;
2202 b := 0;
2203 end
2204 else
2205 begin
2206 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2207 gStdFont, 0, 0, 255, 1);
2208 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2209 gStdFont, 0, 0, 255, 1);
2210 r := 0;
2211 g := 0;
2212 b := 255;
2213 end;
2215 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2216 _y := _y+24;
2218 for p := 0 to High(CustomStat.PlayerStat) do
2219 if CustomStat.PlayerStat[p].Team = t then
2220 with CustomStat.PlayerStat[p] do
2221 begin
2222 if Spectator then
2223 begin
2224 rr := r div 2;
2225 gg := g div 2;
2226 bb := b div 2;
2227 end
2228 else
2229 begin
2230 rr := r;
2231 gg := g;
2232 bb := b;
2233 end;
2234 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2235 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2236 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2237 _y := _y+24;
2238 end;
2240 _y := _y+16+16;
2241 end;
2242 end
2243 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2244 begin
2245 _y := _y+40;
2246 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2247 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2248 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2250 _y := _y+24;
2251 for p := 0 to High(CustomStat.PlayerStat) do
2252 with CustomStat.PlayerStat[p] do
2253 begin
2254 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2256 if Spectator then
2257 r := 127
2258 else
2259 r := 255;
2261 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2262 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2263 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2264 _y := _y+24;
2265 end;
2266 end;
2267 end;
2269 procedure DrawSingleStat();
2270 var
2271 tm, key_x, val_x, y: Integer;
2272 w1, w2, h: Word;
2273 s1, s2: String;
2275 procedure player_stat(n: Integer);
2276 var
2277 kpm: Real;
2279 begin
2280 // "Kills: # / #":
2281 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2282 s2 := Format(' %d', [gTotalMonsters]);
2284 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2285 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2286 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2287 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2288 s1 := s1 + '/';
2289 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2290 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2292 // "Kills-per-minute: ##.#":
2293 s1 := _lc[I_MENU_INTER_KPM];
2294 if tm > 0 then
2295 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2296 else
2297 kpm := SingleStat.PlayerStat[n].Kills;
2298 s2 := Format(' %.1f', [kpm]);
2300 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2301 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2303 // "Secrets found: # / #":
2304 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2305 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2307 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2308 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2309 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2310 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2311 s1 := s1 + '/';
2312 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2313 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2314 end;
2316 begin
2317 // "Level Complete":
2318 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2319 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2321 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2322 s1 := _lc[I_MENU_INTER_KPM];
2323 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2324 Inc(w1, 16);
2325 s1 := ' 9999.9';
2326 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2328 key_x := (gScreenWidth-w1-w2) div 2;
2329 val_x := key_x + w1;
2331 // "Time: #:##:##":
2332 tm := SingleStat.GameTime div 1000;
2333 s1 := _lc[I_MENU_INTER_TIME];
2334 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2336 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2337 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2339 if SingleStat.TwoPlayers then
2340 begin
2341 // "Player 1":
2342 s1 := _lc[I_MENU_PLAYER_1];
2343 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2344 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2346 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2347 y := 176;
2348 player_stat(0);
2350 // "Player 2":
2351 s1 := _lc[I_MENU_PLAYER_2];
2352 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2353 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2355 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2356 y := 336;
2357 player_stat(1);
2358 end
2359 else
2360 begin
2361 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2362 y := 128;
2363 player_stat(0);
2364 end;
2365 end;
2367 procedure DrawLoadingStat();
2368 var
2369 ww, hh: Word;
2370 xx, yy, i: Integer;
2371 s: String;
2372 begin
2373 if Length(LoadingStat.Msgs) = 0 then
2374 Exit;
2376 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2377 yy := (gScreenHeight div 3);
2378 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2379 xx := (gScreenWidth div 3);
2381 with LoadingStat do
2382 for i := 0 to NextMsg-1 do
2383 begin
2384 if (i = (NextMsg-1)) and (MaxValue > 0) then
2385 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2386 else
2387 s := Msgs[i];
2389 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2390 yy := yy + LOADING_INTERLINE;
2391 end;
2392 end;
2394 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2395 var
2396 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2397 begin
2398 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2399 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2400 begin
2401 Scale := 1;
2402 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2403 ScaleSz := 16 div Scale;
2404 // Ðàçìåðû ìèíè-êàðòû:
2405 aX := max(gMapInfo.Width div ScaleSz, 1);
2406 aY := max(gMapInfo.Height div ScaleSz, 1);
2407 // Ðàìêà êàðòû:
2408 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2410 if gWalls <> nil then
2411 begin
2412 // Ðèñóåì ñòåíû:
2413 for a := 0 to High(gWalls) do
2414 with gWalls[a] do
2415 if PanelType <> 0 then
2416 begin
2417 // Ëåâûé âåðõíèé óãîë:
2418 aX := X div ScaleSz;
2419 aY := Y div ScaleSz;
2420 // Ðàçìåðû:
2421 aX2 := max(Width div ScaleSz, 1);
2422 aY2 := max(Height div ScaleSz, 1);
2423 // Ïðàâûé íèæíèé óãîë:
2424 aX2 := aX + aX2 - 1;
2425 aY2 := aY + aY2 - 1;
2427 case PanelType of
2428 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2429 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2430 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2431 end;
2432 end;
2433 end;
2434 if gSteps <> nil then
2435 begin
2436 // Ðèñóåì ñòóïåíè:
2437 for a := 0 to High(gSteps) do
2438 with gSteps[a] do
2439 if PanelType <> 0 then
2440 begin
2441 // Ëåâûé âåðõíèé óãîë:
2442 aX := X div ScaleSz;
2443 aY := Y div ScaleSz;
2444 // Ðàçìåðû:
2445 aX2 := max(Width div ScaleSz, 1);
2446 aY2 := max(Height div ScaleSz, 1);
2447 // Ïðàâûé íèæíèé óãîë:
2448 aX2 := aX + aX2 - 1;
2449 aY2 := aY + aY2 - 1;
2451 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2452 end;
2453 end;
2454 if gLifts <> nil then
2455 begin
2456 // Ðèñóåì ëèôòû:
2457 for a := 0 to High(gLifts) do
2458 with gLifts[a] do
2459 if PanelType <> 0 then
2460 begin
2461 // Ëåâûé âåðõíèé óãîë:
2462 aX := X div ScaleSz;
2463 aY := Y div ScaleSz;
2464 // Ðàçìåðû:
2465 aX2 := max(Width div ScaleSz, 1);
2466 aY2 := max(Height div ScaleSz, 1);
2467 // Ïðàâûé íèæíèé óãîë:
2468 aX2 := aX + aX2 - 1;
2469 aY2 := aY + aY2 - 1;
2471 case LiftType of
2472 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2473 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2474 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2475 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2476 end;
2477 end;
2478 end;
2479 if gWater <> nil then
2480 begin
2481 // Ðèñóåì âîäó:
2482 for a := 0 to High(gWater) do
2483 with gWater[a] do
2484 if PanelType <> 0 then
2485 begin
2486 // Ëåâûé âåðõíèé óãîë:
2487 aX := X div ScaleSz;
2488 aY := Y div ScaleSz;
2489 // Ðàçìåðû:
2490 aX2 := max(Width div ScaleSz, 1);
2491 aY2 := max(Height div ScaleSz, 1);
2492 // Ïðàâûé íèæíèé óãîë:
2493 aX2 := aX + aX2 - 1;
2494 aY2 := aY + aY2 - 1;
2496 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2497 end;
2498 end;
2499 if gAcid1 <> nil then
2500 begin
2501 // Ðèñóåì êèñëîòó 1:
2502 for a := 0 to High(gAcid1) do
2503 with gAcid1[a] do
2504 if PanelType <> 0 then
2505 begin
2506 // Ëåâûé âåðõíèé óãîë:
2507 aX := X div ScaleSz;
2508 aY := Y div ScaleSz;
2509 // Ðàçìåðû:
2510 aX2 := max(Width div ScaleSz, 1);
2511 aY2 := max(Height div ScaleSz, 1);
2512 // Ïðàâûé íèæíèé óãîë:
2513 aX2 := aX + aX2 - 1;
2514 aY2 := aY + aY2 - 1;
2516 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2517 end;
2518 end;
2519 if gAcid2 <> nil then
2520 begin
2521 // Ðèñóåì êèñëîòó 2:
2522 for a := 0 to High(gAcid2) do
2523 with gAcid2[a] do
2524 if PanelType <> 0 then
2525 begin
2526 // Ëåâûé âåðõíèé óãîë:
2527 aX := X div ScaleSz;
2528 aY := Y div ScaleSz;
2529 // Ðàçìåðû:
2530 aX2 := max(Width div ScaleSz, 1);
2531 aY2 := max(Height div ScaleSz, 1);
2532 // Ïðàâûé íèæíèé óãîë:
2533 aX2 := aX + aX2 - 1;
2534 aY2 := aY + aY2 - 1;
2536 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2537 end;
2538 end;
2539 if gPlayers <> nil then
2540 begin
2541 // Ðèñóåì èãðîêîâ:
2542 for a := 0 to High(gPlayers) do
2543 if gPlayers[a] <> nil then with gPlayers[a] do
2544 if Live then begin
2545 // Ëåâûé âåðõíèé óãîë:
2546 aX := Obj.X div ScaleSz + 1;
2547 aY := Obj.Y div ScaleSz + 1;
2548 // Ðàçìåðû:
2549 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2550 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2551 // Ïðàâûé íèæíèé óãîë:
2552 aX2 := aX + aX2 - 1;
2553 aY2 := aY + aY2 - 1;
2555 if gPlayers[a] = p then
2556 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2557 else
2558 case Team of
2559 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2560 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2561 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2562 end;
2563 end;
2564 end;
2565 if gMonsters <> nil then
2566 begin
2567 // Ðèñóåì ìîíñòðîâ:
2568 for a := 0 to High(gMonsters) do
2569 if gMonsters[a] <> nil then with gMonsters[a] do
2570 if Live then begin
2571 // Ëåâûé âåðõíèé óãîë:
2572 aX := Obj.X div ScaleSz + 1;
2573 aY := Obj.Y div ScaleSz + 1;
2574 // Ðàçìåðû:
2575 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2576 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2577 // Ïðàâûé íèæíèé óãîë:
2578 aX2 := aX + aX2 - 1;
2579 aY2 := aY + aY2 - 1;
2581 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2582 end;
2583 end;
2584 end;
2585 end;
2587 procedure DrawMapView(x, y, w, h: Integer);
2588 var
2589 bx, by: Integer;
2590 begin
2591 glPushMatrix();
2593 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2594 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2595 g_Map_DrawBack(-bx, -by);
2597 sX := x;
2598 sY := y;
2599 sWidth := w;
2600 sHeight := h;
2602 glTranslatef(-x, -y, 0);
2604 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_BACK);
2605 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_STEP);
2606 g_Items_Draw();
2607 g_Weapon_Draw();
2608 g_Player_DrawShells();
2609 g_Player_DrawAll();
2610 g_Player_DrawCorpses();
2611 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WALL);
2612 g_Monsters_Draw();
2613 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_CLOSEDOOR);
2614 g_GFX_Draw();
2615 g_Map_DrawFlags();
2616 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID1);
2617 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID2);
2618 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WATER);
2619 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_FORE);
2620 if g_debug_HealthBar then
2621 begin
2622 g_Monsters_DrawHealth();
2623 g_Player_DrawHealth();
2624 end;
2626 glPopMatrix();
2627 end;
2629 procedure DrawPlayer(p: TPlayer);
2630 var
2631 px, py, a, b, c, d: Integer;
2632 //R: TRect;
2633 lln: Integer;
2634 lx, ly, lrad: Integer;
2635 ltminx, ltminy, ltmaxx, ltmaxy: Integer;
2636 begin
2637 if (p = nil) or (p.FDummy) then
2638 begin
2639 glPushMatrix();
2640 g_Map_DrawBack(0, 0);
2641 glPopMatrix();
2642 Exit;
2643 end;
2645 gPlayerDrawn := p;
2647 glPushMatrix();
2649 px := p.GameX + PLAYER_RECT_CX;
2650 py := p.GameY + PLAYER_RECT_CY;
2652 if px > (gPlayerScreenSize.X div 2) then
2653 a := -px + (gPlayerScreenSize.X div 2)
2654 else
2655 a := 0;
2656 if py > (gPlayerScreenSize.Y div 2) then
2657 b := -py + (gPlayerScreenSize.Y div 2)
2658 else
2659 b := 0;
2660 if px > (gMapInfo.Width - (gPlayerScreenSize.X div 2)) then
2661 a := -gMapInfo.Width + gPlayerScreenSize.X;
2662 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2663 b := -gMapInfo.Height + gPlayerScreenSize.Y;
2664 if gMapInfo.Width <= gPlayerScreenSize.X then
2665 a := 0;
2666 if gMapInfo.Height <= gPlayerScreenSize.Y then
2667 b := 0;
2669 if p.IncCam <> 0 then
2670 begin
2671 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2672 begin
2673 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2674 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2675 end;
2677 if py < (gPlayerScreenSize.Y div 2) then
2678 begin
2679 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2680 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2681 end;
2683 if p.IncCam < 0 then
2684 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and
2685 (p.IncCam < 0) do
2686 p.IncCam := p.IncCam + 1;
2688 if p.IncCam > 0 then
2689 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and
2690 (p.IncCam > 0) do
2691 p.IncCam := p.IncCam - 1;
2692 end;
2694 if (px< gPlayerScreenSize.X div 2) or
2695 (gMapInfo.Width-gPlayerScreenSize.X <= 256) then
2696 c := 0
2697 else
2698 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then
2699 c := gBackSize.X - gPlayerScreenSize.X
2700 else
2701 c := Round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2703 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or
2704 (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then
2705 d := 0
2706 else
2707 if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then
2708 d := gBackSize.Y - gPlayerScreenSize.Y
2709 else
2710 d := Round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
2712 g_Map_DrawBack(-c, -d);
2714 sX := -a;
2715 sY := -(b+p.IncCam);
2716 sWidth := gPlayerScreenSize.X;
2717 sHeight := gPlayerScreenSize.Y;
2719 glTranslatef(a, b+p.IncCam, 0);
2721 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_BACK);
2722 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_STEP);
2723 g_Items_Draw();
2724 g_Weapon_Draw();
2725 g_Player_DrawShells();
2726 g_Player_DrawAll();
2727 g_Player_DrawCorpses();
2728 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WALL);
2729 g_Monsters_Draw();
2730 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_CLOSEDOOR);
2731 g_GFX_Draw();
2732 g_Map_DrawFlags();
2733 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID1);
2734 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID2);
2735 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WATER);
2737 if gwin_has_stencil and (g_dynLightCount > 0) then
2738 begin
2739 // get light bounds
2740 ltminx := $3fffffff;
2741 ltminy := $3fffffff;
2742 ltmaxx := -$3fffffff;
2743 ltmaxy := -$3fffffff;
2745 for lln := 0 to g_dynLightCount-1 do
2746 begin
2747 lx := g_dynLights[lln].x-sX;
2748 ly := g_dynLights[lln].y-sY;
2749 lrad := g_dynLights[lln].radius;
2750 if lrad < 3 then continue;
2752 if lx+lrad < 0 then continue;
2753 if ly+lrad < 0 then continue;
2754 if lx-lrad >= gPlayerScreenSize.X then continue;
2755 if ly-lrad >= gPlayerScreenSize.Y then continue;
2757 lx := lx+sX;
2758 ly := ly+sY;
2760 if ltminx > lx-lrad then ltminx := lx-lrad;
2761 if ltminy > ly-lrad then ltminy := ly-lrad;
2762 if ltmaxx < lx+lrad then ltmaxx := lx+lrad;
2763 if ltmaxy < ly+lrad then ltmaxy := ly+lrad;
2764 end;
2766 if g_Map_BuildPLP(ltminx, ltminy, ltmaxx, ltmaxy) then
2767 begin
2768 // setup OpenGL parameters
2769 glStencilMask($FFFFFFFF);
2770 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2771 glEnable(GL_STENCIL_TEST);
2772 glEnable(GL_SCISSOR_TEST);
2773 glClear(GL_STENCIL_BUFFER_BIT);
2774 glStencilFunc(GL_EQUAL, 0, $ff);
2776 for lln := 0 to g_dynLightCount-1 do
2777 begin
2778 lx := g_dynLights[lln].x;
2779 ly := g_dynLights[lln].y;
2780 lrad := g_dynLights[lln].radius;
2781 if lrad < 3 then continue;
2782 // set scissor to optimize drawing
2783 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2784 // no need to clear stencil buffer, light blitting will do it for us
2785 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2786 // draw extruded panels
2787 glDisable(GL_TEXTURE_2D);
2788 glDisable(GL_BLEND);
2789 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2790 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2791 // render light texture
2792 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2793 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2794 // blend it
2795 glEnable(GL_BLEND);
2796 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2797 glEnable(GL_TEXTURE_2D);
2798 // color and opacity
2799 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2800 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2801 glBegin(GL_QUADS);
2802 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2803 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2804 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2805 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2806 glEnd();
2807 end;
2809 // done
2810 glDisable(GL_STENCIL_TEST);
2811 glDisable(GL_BLEND);
2812 glDisable(GL_SCISSOR_TEST);
2813 glScissor(0, 0, sWidth, sHeight);
2814 end;
2815 end;
2817 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_FORE);
2818 if g_debug_HealthBar then
2819 begin
2820 g_Monsters_DrawHealth();
2821 g_Player_DrawHealth();
2822 end;
2824 if p.FSpectator then
2825 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2826 p.GameY + PLAYER_RECT_CY - 4,
2827 'X', gStdFont, 255, 255, 255, 1, True);
2829 for a := 0 to High(gCollideMap) do
2830 for b := 0 to High(gCollideMap[a]) do
2831 begin
2832 d := 0;
2833 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2834 d := d + 1;
2835 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2836 d := d + 2;
2838 case d of
2839 1: e_DrawPoint(1, b, a, 200, 200, 200);
2840 2: e_DrawPoint(1, b, a, 64, 64, 255);
2841 3: e_DrawPoint(1, b, a, 255, 0, 255);
2842 end;
2843 end;
2846 glPopMatrix();
2848 p.DrawPain();
2849 p.DrawPickup();
2850 p.DrawRulez();
2851 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
2852 if g_Debug_Player then
2853 g_Player_DrawDebug(p);
2854 p.DrawGUI();
2855 end;
2857 procedure g_Game_Draw();
2858 var
2859 ID: DWORD;
2860 w, h: Word;
2861 ww, hh: Byte;
2862 Time: Int64;
2863 back: string;
2864 plView1, plView2: TPlayer;
2865 Split: Boolean;
2866 begin
2867 if gExit = EXIT_QUIT then Exit;
2869 Time := GetTimer() {div 1000};
2870 FPSCounter := FPSCounter+1;
2871 if Time - FPSTime >= 1000 then
2872 begin
2873 FPS := FPSCounter;
2874 FPSCounter := 0;
2875 FPSTime := Time;
2876 end;
2878 if gGameOn or (gState = STATE_FOLD) then
2879 begin
2880 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
2881 begin
2882 gSpectMode := SPECT_NONE;
2883 if not gRevertPlayers then
2884 begin
2885 plView1 := gPlayer1;
2886 plView2 := gPlayer2;
2887 end
2888 else
2889 begin
2890 plView1 := gPlayer2;
2891 plView2 := gPlayer1;
2892 end;
2893 end
2894 else
2895 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
2896 begin
2897 gSpectMode := SPECT_NONE;
2898 if gPlayer2 = nil then
2899 plView1 := gPlayer1
2900 else
2901 plView1 := gPlayer2;
2902 plView2 := nil;
2903 end
2904 else
2905 begin
2906 plView1 := nil;
2907 plView2 := nil;
2908 end;
2910 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
2911 gSpectMode := SPECT_STATS;
2913 if gSpectMode = SPECT_PLAYERS then
2914 if gPlayers <> nil then
2915 begin
2916 plView1 := GetActivePlayer_ByID(gSpectPID1);
2917 if plView1 = nil then
2918 begin
2919 gSpectPID1 := GetActivePlayerID_Next();
2920 plView1 := GetActivePlayer_ByID(gSpectPID1);
2921 end;
2922 if gSpectViewTwo then
2923 begin
2924 plView2 := GetActivePlayer_ByID(gSpectPID2);
2925 if plView2 = nil then
2926 begin
2927 gSpectPID2 := GetActivePlayerID_Next();
2928 plView2 := GetActivePlayer_ByID(gSpectPID2);
2929 end;
2930 end;
2931 end;
2933 if gSpectMode = SPECT_MAPVIEW then
2934 begin
2935 // Ðåæèì ïðîñìîòðà êàðòû
2936 Split := False;
2937 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
2938 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
2939 gHearPoint1.Active := True;
2940 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
2941 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
2942 gHearPoint2.Active := False;
2943 end
2944 else
2945 begin
2946 Split := (plView1 <> nil) and (plView2 <> nil);
2948 // Òî÷êè ñëóõà èãðîêîâ
2949 if plView1 <> nil then
2950 begin
2951 gHearPoint1.Active := True;
2952 gHearPoint1.Coords.X := plView1.GameX;
2953 gHearPoint1.Coords.Y := plView1.GameY;
2954 end else
2955 gHearPoint1.Active := False;
2956 if plView2 <> nil then
2957 begin
2958 gHearPoint2.Active := True;
2959 gHearPoint2.Coords.X := plView2.GameX;
2960 gHearPoint2.Coords.Y := plView2.GameY;
2961 end else
2962 gHearPoint2.Active := False;
2964 // Ðàçìåð ýêðàíîâ èãðîêîâ:
2965 gPlayerScreenSize.X := gScreenWidth-196;
2966 if Split then
2967 begin
2968 gPlayerScreenSize.Y := gScreenHeight div 2;
2969 if gScreenHeight mod 2 = 0 then
2970 Dec(gPlayerScreenSize.Y);
2971 end
2972 else
2973 gPlayerScreenSize.Y := gScreenHeight;
2975 if Split then
2976 if gScreenHeight mod 2 = 0 then
2977 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
2978 else
2979 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
2981 DrawPlayer(plView1);
2982 gPlayer1ScreenCoord.X := sX;
2983 gPlayer1ScreenCoord.Y := sY;
2985 if Split then
2986 begin
2987 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
2989 DrawPlayer(plView2);
2990 gPlayer2ScreenCoord.X := sX;
2991 gPlayer2ScreenCoord.Y := sY;
2992 end;
2994 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
2996 if Split then
2997 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
2998 end;
3000 if MessageText <> '' then
3001 begin
3002 w := 0;
3003 h := 0;
3004 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3005 if Split then
3006 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3007 (gScreenHeight div 2)-(h div 2), MessageText)
3008 else
3009 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3010 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3011 end;
3013 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3015 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3016 begin
3017 // Draw spectator GUI
3018 ww := 0;
3019 hh := 0;
3020 e_TextureFontGetSize(gStdFont, ww, hh);
3021 case gSpectMode of
3022 SPECT_STATS:
3023 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3024 SPECT_MAPVIEW:
3025 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3026 SPECT_PLAYERS:
3027 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3028 end;
3029 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3030 if gSpectMode = SPECT_MAPVIEW then
3031 begin
3032 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3033 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3034 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3035 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3036 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3037 end;
3038 if gSpectMode = SPECT_PLAYERS then
3039 begin
3040 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3041 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3042 if gSpectViewTwo then
3043 begin
3044 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3045 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3046 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3047 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3048 end
3049 else
3050 begin
3051 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3052 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3053 end;
3054 end;
3055 end;
3056 end;
3058 if gPause and gGameOn and (g_ActiveWindow = nil) then
3059 begin
3060 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3062 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3063 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3064 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3065 end;
3067 if not gGameOn then
3068 begin
3069 if (gState = STATE_MENU) then
3070 begin
3071 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
3072 begin
3073 if g_Texture_Get('MENU_BACKGROUND', ID) then
3074 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3075 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3076 end;
3077 if g_ActiveWindow <> nil then
3078 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3079 end;
3081 if gState = STATE_FOLD then
3082 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3084 if gState = STATE_INTERCUSTOM then
3085 begin
3086 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3087 begin
3088 back := 'TEXTURE_endpic';
3089 if not g_Texture_Get(back, ID) then
3090 back := _lc[I_TEXTURE_ENDPIC];
3091 end
3092 else
3093 back := 'INTER';
3095 if g_Texture_Get(back, ID) then
3096 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3097 else
3098 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3100 DrawCustomStat();
3102 if g_ActiveWindow <> nil then
3103 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3104 end;
3106 if gState = STATE_INTERSINGLE then
3107 begin
3108 if EndingGameCounter > 0 then
3109 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter)
3110 else
3111 begin
3112 back := 'INTER';
3114 if g_Texture_Get(back, ID) then
3115 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3116 else
3117 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3119 DrawSingleStat();
3121 if g_ActiveWindow <> nil then
3122 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3123 end;
3124 end;
3126 if gState = STATE_ENDPIC then
3127 begin
3128 ID := DWORD(-1);
3129 if not g_Texture_Get('TEXTURE_endpic', ID) then
3130 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3132 if ID <> DWORD(-1) then
3133 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3134 else
3135 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3137 if g_ActiveWindow <> nil then
3138 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3139 end;
3141 if gState = STATE_SLIST then
3142 begin
3143 if g_Texture_Get('MENU_BACKGROUND', ID) then
3144 begin
3145 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3146 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3147 end;
3148 g_Serverlist_Draw(slCurrent);
3149 end;
3150 end;
3152 if g_ActiveWindow <> nil then
3153 begin
3154 if gGameOn then
3155 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3156 g_ActiveWindow.Draw();
3157 end;
3159 g_Console_Draw();
3161 if g_debug_Sounds and gGameOn then
3162 begin
3163 for w := 0 to High(e_SoundsArray) do
3164 for h := 0 to e_SoundsArray[w].nRefs do
3165 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3166 end;
3168 if gShowFPS then
3169 begin
3170 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3171 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3172 end;
3174 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3175 e_TextureFontPrint(gScreenWidth-72, 0,
3176 Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]),
3177 gStdFont);
3178 end;
3180 procedure g_Game_Quit();
3181 begin
3182 g_Game_StopAllSounds(True);
3183 gMusic.Free();
3184 g_Game_SaveOptions();
3185 g_Game_FreeData();
3186 g_PlayerModel_FreeData();
3187 g_Texture_DeleteAll();
3188 g_Frames_DeleteAll();
3189 g_Menu_Free();
3191 if NetInitDone then g_Net_Free;
3193 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3194 if gMapToDelete <> '' then
3195 g_Game_DeleteTestMap();
3197 gExit := EXIT_QUIT;
3198 PushExitEvent();
3199 end;
3201 procedure g_FatalError(Text: String);
3202 begin
3203 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3204 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3206 gExit := EXIT_SIMPLE;
3207 end;
3209 procedure g_SimpleError(Text: String);
3210 begin
3211 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3212 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3213 end;
3215 procedure g_Game_SetupScreenSize();
3216 var
3217 d: Single;
3218 begin
3219 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3220 gPlayerScreenSize.X := gScreenWidth-196;
3221 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3222 gPlayerScreenSize.Y := gScreenHeight div 2
3223 else
3224 gPlayerScreenSize.Y := gScreenHeight;
3226 // Ðàçìåð çàäíåãî ïëàíà:
3227 if BackID <> DWORD(-1) then
3228 begin
3229 d := SKY_STRETCH;
3231 if (gScreenWidth*d > gMapInfo.Width) or
3232 (gScreenHeight*d > gMapInfo.Height) then
3233 d := 1.0;
3235 gBackSize.X := Round(gScreenWidth*d);
3236 gBackSize.Y := Round(gScreenHeight*d);
3237 end;
3238 end;
3240 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3241 begin
3242 g_Window_SetSize(newWidth, newHeight, nowFull);
3243 end;
3245 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3246 begin
3247 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3248 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3249 Exit;
3250 if gPlayer1 = nil then
3251 begin
3252 if g_Game_IsClient then
3253 begin
3254 if NetPlrUID1 > -1 then
3255 begin
3256 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3257 gPlayer1 := g_Player_Get(NetPlrUID1);
3258 end;
3259 Exit;
3260 end;
3262 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3263 Team := gPlayer1Settings.Team;
3265 // Ñîçäàíèå ïåðâîãî èãðîêà:
3266 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3267 gPlayer1Settings.Color,
3268 Team, False));
3269 if gPlayer1 = nil then
3270 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3271 else
3272 begin
3273 gPlayer1.Name := gPlayer1Settings.Name;
3274 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3275 if g_Game_IsServer and g_Game_IsNet then
3276 MH_SEND_PlayerCreate(gPlayer1.UID);
3277 gPlayer1.Respawn(False, True);
3279 if g_Game_IsNet and NetUseMaster then
3280 g_Net_Slist_Update;
3281 end;
3283 Exit;
3284 end;
3285 if gPlayer2 = nil then
3286 begin
3287 if g_Game_IsClient then
3288 begin
3289 if NetPlrUID2 > -1 then
3290 gPlayer2 := g_Player_Get(NetPlrUID2);
3291 Exit;
3292 end;
3294 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3295 Team := gPlayer2Settings.Team;
3297 // Ñîçäàíèå âòîðîãî èãðîêà:
3298 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3299 gPlayer2Settings.Color,
3300 Team, False));
3301 if gPlayer2 = nil then
3302 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3303 else
3304 begin
3305 gPlayer2.Name := gPlayer2Settings.Name;
3306 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3307 if g_Game_IsServer and g_Game_IsNet then
3308 MH_SEND_PlayerCreate(gPlayer2.UID);
3309 gPlayer2.Respawn(False, True);
3311 if g_Game_IsNet and NetUseMaster then
3312 g_Net_Slist_Update;
3313 end;
3315 Exit;
3316 end;
3317 end;
3319 procedure g_Game_RemovePlayer();
3320 var
3321 Pl: TPlayer;
3322 begin
3323 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3324 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3325 Exit;
3326 Pl := gPlayer2;
3327 if Pl <> nil then
3328 begin
3329 if g_Game_IsServer then
3330 begin
3331 Pl.Lives := 0;
3332 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3333 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3334 g_Player_Remove(Pl.UID);
3336 if g_Game_IsNet and NetUseMaster then
3337 g_Net_Slist_Update;
3338 end else
3339 gPlayer2 := nil;
3340 Exit;
3341 end;
3342 Pl := gPlayer1;
3343 if Pl <> nil then
3344 begin
3345 if g_Game_IsServer then
3346 begin
3347 Pl.Lives := 0;
3348 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3349 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3350 g_Player_Remove(Pl.UID);
3352 if g_Game_IsNet and NetUseMaster then
3353 g_Net_Slist_Update;
3354 end else
3355 begin
3356 gPlayer1 := nil;
3357 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3358 end;
3359 Exit;
3360 end;
3361 end;
3363 procedure g_Game_Spectate();
3364 begin
3365 g_Game_RemovePlayer();
3366 if gPlayer1 <> nil then
3367 g_Game_RemovePlayer();
3368 end;
3370 procedure g_Game_SpectateCenterView();
3371 begin
3372 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3373 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3374 end;
3376 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3377 var
3378 i, nPl: Integer;
3379 begin
3380 g_Game_Free();
3382 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3384 g_Game_ClearLoading();
3386 // Íàñòðîéêè èãðû:
3387 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3388 gAimLine := False;
3389 gShowMap := False;
3390 gGameSettings.GameType := GT_SINGLE;
3391 gGameSettings.MaxLives := 0;
3392 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3393 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3394 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3395 gSwitchGameMode := GM_SINGLE;
3397 g_Game_ExecuteEvent('ongamestart');
3399 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3400 g_Game_SetupScreenSize();
3402 // Ñîçäàíèå ïåðâîãî èãðîêà:
3403 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3404 gPlayer1Settings.Color,
3405 gPlayer1Settings.Team, False));
3406 if gPlayer1 = nil then
3407 begin
3408 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3409 Exit;
3410 end;
3412 gPlayer1.Name := gPlayer1Settings.Name;
3413 nPl := 1;
3415 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3416 if TwoPlayers then
3417 begin
3418 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3419 gPlayer2Settings.Color,
3420 gPlayer2Settings.Team, False));
3421 if gPlayer2 = nil then
3422 begin
3423 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3424 Exit;
3425 end;
3427 gPlayer2.Name := gPlayer2Settings.Name;
3428 Inc(nPl);
3429 end;
3431 // Çàãðóçêà è çàïóñê êàðòû:
3432 if not g_Game_StartMap(MAP, True) then
3433 begin
3434 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3435 Exit;
3436 end;
3438 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3439 g_Player_Init();
3441 // Ñîçäàåì áîòîâ:
3442 for i := nPl+1 to nPlayers do
3443 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3444 end;
3446 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3447 TimeLimit, GoalLimit: Word;
3448 MaxLives: Byte;
3449 Options: LongWord; nPlayers: Byte);
3450 var
3451 i, nPl: Integer;
3452 begin
3453 g_Game_Free();
3455 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3457 g_Game_ClearLoading();
3459 // Íàñòðîéêè èãðû:
3460 gGameSettings.GameType := GT_CUSTOM;
3461 gGameSettings.GameMode := GameMode;
3462 gSwitchGameMode := GameMode;
3463 gGameSettings.TimeLimit := TimeLimit;
3464 gGameSettings.GoalLimit := GoalLimit;
3465 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3466 gGameSettings.Options := Options;
3468 gCoopTotalMonstersKilled := 0;
3469 gCoopTotalSecretsFound := 0;
3470 gCoopTotalMonsters := 0;
3471 gCoopTotalSecrets := 0;
3472 gAimLine := False;
3473 gShowMap := False;
3475 g_Game_ExecuteEvent('ongamestart');
3477 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3478 g_Game_SetupScreenSize();
3480 // Ðåæèì íàáëþäàòåëÿ:
3481 if nPlayers = 0 then
3482 begin
3483 gPlayer1 := nil;
3484 gPlayer2 := nil;
3485 end;
3487 nPl := 0;
3488 if nPlayers >= 1 then
3489 begin
3490 // Ñîçäàíèå ïåðâîãî èãðîêà:
3491 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3492 gPlayer1Settings.Color,
3493 gPlayer1Settings.Team, False));
3494 if gPlayer1 = nil then
3495 begin
3496 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3497 Exit;
3498 end;
3500 gPlayer1.Name := gPlayer1Settings.Name;
3501 Inc(nPl);
3502 end;
3504 if nPlayers >= 2 then
3505 begin
3506 // Ñîçäàíèå âòîðîãî èãðîêà:
3507 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3508 gPlayer2Settings.Color,
3509 gPlayer2Settings.Team, False));
3510 if gPlayer2 = nil then
3511 begin
3512 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3513 Exit;
3514 end;
3516 gPlayer2.Name := gPlayer2Settings.Name;
3517 Inc(nPl);
3518 end;
3520 // Çàãðóçêà è çàïóñê êàðòû:
3521 if not g_Game_StartMap(Map, True) then
3522 begin
3523 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3524 Exit;
3525 end;
3527 // Íåò òî÷åê ïîÿâëåíèÿ:
3528 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3529 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3530 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3531 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3532 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3533 begin
3534 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3535 Exit;
3536 end;
3538 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3539 g_Player_Init();
3541 // Ñîçäàåì áîòîâ:
3542 for i := nPl+1 to nPlayers do
3543 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3544 end;
3546 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3547 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3548 Options: LongWord; nPlayers: Byte;
3549 IPAddr: LongWord; Port: Word);
3550 begin
3551 g_Game_Free();
3553 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3555 g_Game_ClearLoading();
3557 // Íàñòðîéêè èãðû:
3558 gGameSettings.GameType := GT_SERVER;
3559 gGameSettings.GameMode := GameMode;
3560 gSwitchGameMode := GameMode;
3561 gGameSettings.TimeLimit := TimeLimit;
3562 gGameSettings.GoalLimit := GoalLimit;
3563 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3564 gGameSettings.Options := Options;
3566 gCoopTotalMonstersKilled := 0;
3567 gCoopTotalSecretsFound := 0;
3568 gCoopTotalMonsters := 0;
3569 gCoopTotalSecrets := 0;
3570 gAimLine := False;
3571 gShowMap := False;
3573 g_Game_ExecuteEvent('ongamestart');
3575 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3576 g_Game_SetupScreenSize();
3578 // Ðåæèì íàáëþäàòåëÿ:
3579 if nPlayers = 0 then
3580 begin
3581 gPlayer1 := nil;
3582 gPlayer2 := nil;
3583 end;
3585 if nPlayers >= 1 then
3586 begin
3587 // Ñîçäàíèå ïåðâîãî èãðîêà:
3588 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3589 gPlayer1Settings.Color,
3590 gPlayer1Settings.Team, False));
3591 if gPlayer1 = nil then
3592 begin
3593 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3594 Exit;
3595 end;
3597 gPlayer1.Name := gPlayer1Settings.Name;
3598 end;
3600 if nPlayers >= 2 then
3601 begin
3602 // Ñîçäàíèå âòîðîãî èãðîêà:
3603 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3604 gPlayer2Settings.Color,
3605 gPlayer2Settings.Team, False));
3606 if gPlayer2 = nil then
3607 begin
3608 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3609 Exit;
3610 end;
3612 gPlayer2.Name := gPlayer2Settings.Name;
3613 end;
3615 // Ñòàðòóåì ñåðâåð
3616 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3617 begin
3618 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3619 Exit;
3620 end;
3622 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3624 // Çàãðóçêà è çàïóñê êàðòû:
3625 if not g_Game_StartMap(Map, True) then
3626 begin
3627 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3628 Exit;
3629 end;
3631 // Íåò òî÷åê ïîÿâëåíèÿ:
3632 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3633 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3634 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3635 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3636 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3637 begin
3638 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3639 Exit;
3640 end;
3642 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3643 g_Player_Init();
3645 NetState := NET_STATE_GAME;
3646 end;
3648 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3649 var
3650 Map: String;
3651 WadName: string;
3652 Ptr: Pointer;
3653 T: Cardinal;
3654 MID: Byte;
3655 State: Byte;
3656 OuterLoop: Boolean;
3657 newResPath: string;
3658 begin
3659 g_Game_Free();
3661 State := 0;
3662 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3663 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3665 g_Game_ClearLoading();
3667 // Íàñòðîéêè èãðû:
3668 gGameSettings.GameType := GT_CLIENT;
3670 gCoopTotalMonstersKilled := 0;
3671 gCoopTotalSecretsFound := 0;
3672 gCoopTotalMonsters := 0;
3673 gCoopTotalSecrets := 0;
3674 gAimLine := False;
3675 gShowMap := False;
3677 g_Game_ExecuteEvent('ongamestart');
3679 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3680 g_Game_SetupScreenSize();
3682 NetState := NET_STATE_AUTH;
3684 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3685 // Ñòàðòóåì êëèåíò
3686 if not g_Net_Connect(Addr, Port) then
3687 begin
3688 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3689 NetState := NET_STATE_NONE;
3690 Exit;
3691 end;
3693 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3694 MC_SEND_Info(PW);
3695 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3697 OuterLoop := True;
3698 while OuterLoop do
3699 begin
3700 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3701 begin
3702 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3703 begin
3704 Ptr := NetEvent.packet^.data;
3705 e_Raw_Seek(0);
3707 MID := e_Raw_Read_Byte(Ptr);
3709 if (MID = NET_MSG_INFO) and (State = 0) then
3710 begin
3711 NetMyID := e_Raw_Read_Byte(Ptr);
3712 NetPlrUID1 := e_Raw_Read_Word(Ptr);
3714 WadName := e_Raw_Read_String(Ptr);
3715 Map := e_Raw_Read_String(Ptr);
3717 gWADHash := e_Raw_Read_MD5(Ptr);
3719 gGameSettings.GameMode := e_Raw_Read_Byte(Ptr);
3720 gSwitchGameMode := gGameSettings.GameMode;
3721 gGameSettings.GoalLimit := e_Raw_Read_Word(Ptr);
3722 gGameSettings.TimeLimit := e_Raw_Read_Word(Ptr);
3723 gGameSettings.MaxLives := e_Raw_Read_Byte(Ptr);
3724 gGameSettings.Options := e_Raw_Read_LongWord(Ptr);
3725 T := e_Raw_Read_LongWord(Ptr);
3727 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3728 if newResPath = '' then
3729 begin
3730 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3731 newResPath := g_Res_DownloadWAD(WadName);
3732 if newResPath = '' then
3733 begin
3734 g_FatalError(_lc[I_NET_ERR_HASH]);
3735 enet_packet_destroy(NetEvent.packet);
3736 NetState := NET_STATE_NONE;
3737 Exit;
3738 end;
3739 end;
3740 newResPath := ExtractRelativePath(MapsDir, newResPath);
3742 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3743 gPlayer1Settings.Color,
3744 gPlayer1Settings.Team, False));
3746 if gPlayer1 = nil then
3747 begin
3748 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3750 enet_packet_destroy(NetEvent.packet);
3751 NetState := NET_STATE_NONE;
3752 Exit;
3753 end;
3755 gPlayer1.Name := gPlayer1Settings.Name;
3756 gPlayer1.UID := NetPlrUID1;
3757 gPlayer1.Reset(True);
3759 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3760 begin
3761 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3763 enet_packet_destroy(NetEvent.packet);
3764 NetState := NET_STATE_NONE;
3765 Exit;
3766 end;
3768 gTime := T;
3770 State := 1;
3771 OuterLoop := False;
3772 enet_packet_destroy(NetEvent.packet);
3773 break;
3774 end
3775 else
3776 enet_packet_destroy(NetEvent.packet);
3777 end
3778 else
3779 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3780 begin
3781 State := 0;
3782 if (NetEvent.data <= NET_DISC_MAX) then
3783 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3784 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3785 OuterLoop := False;
3786 Break;
3787 end;
3788 end;
3790 ProcessLoading();
3792 e_PollInput();
3794 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3795 begin
3796 State := 0;
3797 break;
3798 end;
3799 end;
3801 if State <> 1 then
3802 begin
3803 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3804 NetState := NET_STATE_NONE;
3805 Exit;
3806 end;
3808 gLMSRespawn := LMS_RESPAWN_NONE;
3809 gLMSRespawnTime := 0;
3811 g_Player_Init();
3812 NetState := NET_STATE_GAME;
3813 MC_SEND_FullStateRequest;
3814 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3815 end;
3817 procedure g_Game_SaveOptions();
3818 begin
3819 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3820 end;
3822 procedure g_Game_ChangeMap(MapPath: String);
3823 var
3824 Force: Boolean;
3825 begin
3826 g_Game_ClearLoading();
3828 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3829 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3830 if gExitByTrigger then
3831 begin
3832 Force := False;
3833 gExitByTrigger := False;
3834 end;
3835 if not g_Game_StartMap(MapPath, Force) then
3836 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3837 end;
3839 procedure g_Game_Restart();
3840 var
3841 Map: string;
3842 begin
3843 if g_Game_IsClient then
3844 Exit;
3845 map := g_ExtractFileName(gMapInfo.Map);
3847 MessageTime := 0;
3848 gGameOn := False;
3849 g_Game_ClearLoading();
3850 g_Game_StartMap(Map, True);
3851 end;
3853 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
3854 var
3855 NewWAD, ResName: String;
3856 I: Integer;
3857 begin
3858 g_Map_Free();
3859 g_Player_RemoveAllCorpses();
3861 if (not g_Game_IsClient) and
3862 (gSwitchGameMode <> gGameSettings.GameMode) and
3863 (gGameSettings.GameMode <> GM_SINGLE) then
3864 begin
3865 if gSwitchGameMode = GM_CTF then
3866 gGameSettings.MaxLives := 0;
3867 gGameSettings.GameMode := gSwitchGameMode;
3868 Force := True;
3869 end else
3870 gSwitchGameMode := gGameSettings.GameMode;
3872 g_Player_ResetTeams();
3874 if Pos(':\', Map) > 0 then
3875 begin
3876 NewWAD := g_ExtractWadName(Map);
3877 ResName := g_ExtractFileName(Map);
3878 if g_Game_IsServer then
3879 begin
3880 gWADHash := MD5File(MapsDir + NewWAD);
3881 g_Game_LoadWAD(NewWAD);
3882 end else
3883 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
3884 g_Game_ClientWAD(NewWAD, gWADHash);
3885 end else
3886 ResName := Map;
3888 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
3889 if Result then
3890 begin
3891 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
3893 gState := STATE_NONE;
3894 g_ActiveWindow := nil;
3895 gGameOn := True;
3897 DisableCheats();
3898 ResetTimer();
3900 if gGameSettings.GameMode = GM_CTF then
3901 begin
3902 g_Map_ResetFlag(FLAG_RED);
3903 g_Map_ResetFlag(FLAG_BLUE);
3904 // CTF, à ôëàãîâ íåò:
3905 if not g_Map_HaveFlagPoints() then
3906 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
3907 end;
3908 end
3909 else
3910 begin
3911 gState := STATE_MENU;
3912 gGameOn := False;
3913 end;
3915 gExit := 0;
3916 gPause := False;
3917 gTime := 0;
3918 NetTimeToUpdate := 1;
3919 NetTimeToReliable := 0;
3920 NetTimeToMaster := NetMasterRate;
3921 gLMSRespawn := LMS_RESPAWN_NONE;
3922 gLMSRespawnTime := 0;
3923 gMissionFailed := False;
3924 gNextMap := '';
3926 gCoopMonstersKilled := 0;
3927 gCoopSecretsFound := 0;
3929 gVoteInProgress := False;
3930 gVotePassed := False;
3931 gVoteCount := 0;
3932 gVoted := False;
3934 gStatsOff := False;
3936 if not gGameOn then Exit;
3938 g_Game_SpectateCenterView();
3940 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
3941 begin
3942 gLMSRespawn := LMS_RESPAWN_WARMUP;
3943 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
3944 gLMSSoftSpawn := True;
3945 if NetMode = NET_SERVER then
3946 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
3947 else
3948 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
3949 end;
3951 if NetMode = NET_SERVER then
3952 begin
3953 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
3955 // Ìàñòåðñåðâåð
3956 if NetUseMaster then
3957 begin
3958 if (NetMHost = nil) or (NetMPeer = nil) then
3959 if not g_Net_Slist_Connect then
3960 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
3962 g_Net_Slist_Update;
3963 end;
3965 if NetClients <> nil then
3966 for I := 0 to High(NetClients) do
3967 if NetClients[I].Used then
3968 begin
3969 NetClients[I].Voted := False;
3970 if NetClients[I].RequestedFullUpdate then
3971 begin
3972 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
3973 NetClients[I].RequestedFullUpdate := False;
3974 end;
3975 end;
3977 g_Net_UnbanNonPermHosts();
3978 end;
3980 if gLastMap then
3981 begin
3982 gCoopTotalMonstersKilled := 0;
3983 gCoopTotalSecretsFound := 0;
3984 gCoopTotalMonsters := 0;
3985 gCoopTotalSecrets := 0;
3986 gLastMap := False;
3987 end;
3989 g_Game_ExecuteEvent('onmapstart');
3990 end;
3992 procedure SetFirstLevel();
3993 begin
3994 gNextMap := '';
3996 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
3997 if MapList = nil then
3998 Exit;
4000 SortSArray(MapList);
4001 gNextMap := MapList[Low(MapList)];
4003 MapList := nil;
4004 end;
4006 procedure g_Game_ExitLevel(Map: Char16);
4007 begin
4008 gNextMap := Map;
4010 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4011 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4012 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4013 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4015 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4016 if gGameSettings.GameType = GT_SINGLE then
4017 gExit := EXIT_ENDLEVELSINGLE
4018 else // Âûøëè â âûõîä â Ñâîåé èãðå
4019 begin
4020 gExit := EXIT_ENDLEVELCUSTOM;
4021 if gGameSettings.GameMode = GM_COOP then
4022 g_Player_RememberAll;
4024 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4025 begin
4026 gLastMap := True;
4027 if gGameSettings.GameMode = GM_COOP then
4028 gStatsOff := True;
4030 gStatsPressed := True;
4031 gNextMap := 'MAP01';
4033 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4034 g_Game_NextLevel;
4036 if g_Game_IsNet then
4037 begin
4038 MH_SEND_GameStats();
4039 MH_SEND_CoopStats();
4040 end;
4041 end;
4042 end;
4043 end;
4045 procedure g_Game_RestartLevel();
4046 var
4047 Map: string;
4048 begin
4049 if gGameSettings.GameMode = GM_SINGLE then
4050 begin
4051 g_Game_Restart();
4052 Exit;
4053 end;
4054 gExit := EXIT_ENDLEVELCUSTOM;
4055 Map := g_ExtractFileName(gMapInfo.Map);
4056 gNextMap := Map;
4057 end;
4059 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4060 var
4061 gWAD: String;
4062 begin
4063 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4064 Exit;
4065 if not g_Game_IsClient then
4066 Exit;
4067 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4068 if gWAD = '' then
4069 begin
4070 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4071 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4072 if gWAD = '' then
4073 begin
4074 g_Game_Free();
4075 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4076 Exit;
4077 end;
4078 end;
4079 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4080 g_Game_LoadWAD(NewWAD);
4081 end;
4083 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4084 var
4085 i, n, nb, nr: Integer;
4086 begin
4087 if not g_Game_IsServer then Exit;
4088 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4089 gLMSRespawn := LMS_RESPAWN_NONE;
4090 gLMSRespawnTime := 0;
4091 MessageTime := 0;
4093 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4094 begin
4095 gMissionFailed := True;
4096 g_Game_RestartLevel;
4097 Exit;
4098 end;
4100 n := 0; nb := 0; nr := 0;
4101 for i := Low(gPlayers) to High(gPlayers) do
4102 if (gPlayers[i] <> nil) and
4103 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4104 (gPlayers[i] is TBot)) then
4105 begin
4106 Inc(n);
4107 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4108 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4109 end;
4111 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4112 begin
4113 // wait a second until the fuckers finally decide to join
4114 gLMSRespawn := LMS_RESPAWN_WARMUP;
4115 gLMSRespawnTime := gTime + 1000;
4116 gLMSSoftSpawn := NoMapRestart;
4117 Exit;
4118 end;
4120 g_Player_RemoveAllCorpses;
4121 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4122 if g_Game_IsNet then
4123 MH_SEND_GameEvent(NET_EV_LMS_START);
4125 for i := Low(gPlayers) to High(gPlayers) do
4126 begin
4127 if gPlayers[i] = nil then continue;
4128 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4129 // don't touch normal spectators
4130 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4131 begin
4132 gPlayers[i].FNoRespawn := True;
4133 gPlayers[i].Lives := 0;
4134 if g_Game_IsNet then
4135 MH_SEND_PlayerStats(gPlayers[I].UID);
4136 continue;
4137 end;
4138 gPlayers[i].FNoRespawn := False;
4139 gPlayers[i].Lives := gGameSettings.MaxLives;
4140 gPlayers[i].Respawn(False, True);
4141 if gGameSettings.GameMode = GM_COOP then
4142 begin
4143 gPlayers[i].Frags := 0;
4144 gPlayers[i].RecallState;
4145 end;
4146 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4147 gPlayer1 := g_Player_Get(gLMSPID1);
4148 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4149 gPlayer2 := g_Player_Get(gLMSPID2);
4150 end;
4152 for i := Low(gItems) to High(gItems) do
4153 begin
4154 if gItems[i].Respawnable then
4155 begin
4156 gItems[i].QuietRespawn := True;
4157 gItems[i].RespawnTime := 0;
4158 end
4159 else
4160 begin
4161 g_Items_Remove(i);
4162 if g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
4163 end;
4164 end;
4166 for i := Low(gMonsters) to High(gMonsters) do
4167 begin
4168 if (gMonsters[i] <> nil) and not gMonsters[i].FNoRespawn then
4169 gMonsters[i].Respawn;
4170 end;
4172 gLMSSoftSpawn := False;
4173 end;
4175 function g_Game_GetFirstMap(WAD: String): String;
4176 begin
4177 Result := '';
4179 MapList := g_Map_GetMapsList(WAD);
4180 if MapList = nil then
4181 Exit;
4183 SortSArray(MapList);
4184 Result := MapList[Low(MapList)];
4186 if not g_Map_Exist(WAD + ':\' + Result) then
4187 Result := '';
4189 MapList := nil;
4190 end;
4192 function g_Game_GetNextMap(): String;
4193 var
4194 I: Integer;
4195 Map: string;
4196 begin
4197 Result := '';
4199 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4200 if MapList = nil then
4201 Exit;
4203 Map := g_ExtractFileName(gMapInfo.Map);
4205 SortSArray(MapList);
4206 MapIndex := -255;
4207 for I := Low(MapList) to High(MapList) do
4208 if Map = MapList[I] then
4209 begin
4210 MapIndex := I;
4211 Break;
4212 end;
4214 if MapIndex <> -255 then
4215 begin
4216 if MapIndex = High(MapList) then
4217 Result := MapList[Low(MapList)]
4218 else
4219 Result := MapList[MapIndex + 1];
4221 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4222 end;
4224 MapList := nil;
4225 end;
4227 procedure g_Game_NextLevel();
4228 begin
4229 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4230 gExit := EXIT_ENDLEVELCUSTOM
4231 else
4232 begin
4233 gExit := EXIT_ENDLEVELSINGLE;
4234 Exit;
4235 end;
4237 if gNextMap <> '' then Exit;
4238 gNextMap := g_Game_GetNextMap();
4239 end;
4241 function g_Game_IsTestMap(): Boolean;
4242 begin
4243 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4244 end;
4246 procedure g_Game_DeleteTestMap();
4247 var
4248 a: Integer;
4249 MapName: Char16;
4250 WadName: string;
4252 WAD: TWADFile;
4253 MapList: SArray;
4254 time: Integer;
4256 begin
4257 a := Pos('.wad:\', gMapToDelete);
4258 if a = 0 then
4259 Exit;
4261 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4262 WadName := Copy(gMapToDelete, 1, a + 3);
4263 Delete(gMapToDelete, 1, a + 5);
4264 gMapToDelete := UpperCase(gMapToDelete);
4265 MapName := '';
4266 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4269 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4270 if MapName <> TEST_MAP_NAME then
4271 Exit;
4273 if not gTempDelete then
4274 begin
4275 time := g_GetFileTime(WadName);
4276 WAD := TWADFile.Create();
4278 // ×èòàåì Wad-ôàéë:
4279 if not WAD.ReadFile(WadName) then
4280 begin // Íåò òàêîãî WAD-ôàéëà
4281 WAD.Free();
4282 Exit;
4283 end;
4285 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4286 WAD.CreateImage();
4287 MapList := WAD.GetResourcesList('');
4289 if MapList <> nil then
4290 for a := 0 to High(MapList) do
4291 if MapList[a] = MapName then
4292 begin
4293 // Óäàëÿåì è ñîõðàíÿåì:
4294 WAD.RemoveResource('', MapName);
4295 WAD.SaveTo(WadName);
4296 Break;
4297 end;
4299 WAD.Free();
4300 g_SetFileTime(WadName, time);
4301 end else
4303 if gTempDelete then DeleteFile(WadName);
4304 end;
4306 procedure GameCVars(P: SArray);
4307 var
4308 a, b: Integer;
4309 stat: TPlayerStatArray;
4310 cmd, s: string;
4311 config: TConfig;
4312 begin
4313 stat := nil;
4314 cmd := LowerCase(P[0]);
4315 if cmd = 'r_showfps' then
4316 begin
4317 if (Length(P) > 1) and
4318 ((P[1] = '1') or (P[1] = '0')) then
4319 gShowFPS := (P[1][1] = '1');
4321 if gShowFPS then
4322 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4323 else
4324 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4325 end
4326 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4327 begin
4328 with gGameSettings do
4329 begin
4330 if (Length(P) > 1) and
4331 ((P[1] = '1') or (P[1] = '0')) then
4332 begin
4333 if (P[1][1] = '1') then
4334 Options := Options or GAME_OPTION_TEAMDAMAGE
4335 else
4336 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4337 end;
4339 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4340 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4341 else
4342 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4344 if g_Game_IsNet then MH_SEND_GameSettings;
4345 end;
4346 end
4347 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4348 begin
4349 with gGameSettings do
4350 begin
4351 if (Length(P) > 1) and
4352 ((P[1] = '1') or (P[1] = '0')) then
4353 begin
4354 if (P[1][1] = '1') then
4355 Options := Options or GAME_OPTION_WEAPONSTAY
4356 else
4357 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4358 end;
4360 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4361 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4362 else
4363 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4365 if g_Game_IsNet then MH_SEND_GameSettings;
4366 end;
4367 end
4368 else if cmd = 'g_gamemode' then
4369 begin
4370 a := g_Game_TextToMode(P[1]);
4371 if a = GM_SINGLE then a := GM_COOP;
4372 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4373 begin
4374 gSwitchGameMode := a;
4375 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4376 (gState = STATE_INTERSINGLE) then
4377 gSwitchGameMode := GM_SINGLE;
4378 if not gGameOn then
4379 gGameSettings.GameMode := gSwitchGameMode;
4380 end;
4381 if gSwitchGameMode = gGameSettings.GameMode then
4382 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4383 [g_Game_ModeToText(gGameSettings.GameMode)]))
4384 else
4385 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4386 [g_Game_ModeToText(gGameSettings.GameMode),
4387 g_Game_ModeToText(gSwitchGameMode)]));
4388 end
4389 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4390 begin
4391 with gGameSettings do
4392 begin
4393 if (Length(P) > 1) and
4394 ((P[1] = '1') or (P[1] = '0')) then
4395 begin
4396 if (P[1][1] = '1') then
4397 Options := Options or GAME_OPTION_ALLOWEXIT
4398 else
4399 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4400 end;
4402 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4403 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4404 else
4405 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4406 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4408 if g_Game_IsNet then MH_SEND_GameSettings;
4409 end;
4410 end
4411 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4412 begin
4413 with gGameSettings do
4414 begin
4415 if (Length(P) > 1) and
4416 ((P[1] = '1') or (P[1] = '0')) then
4417 begin
4418 if (P[1][1] = '1') then
4419 Options := Options or GAME_OPTION_MONSTERS
4420 else
4421 Options := Options and (not GAME_OPTION_MONSTERS);
4422 end;
4424 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4425 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4426 else
4427 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4428 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4430 if g_Game_IsNet then MH_SEND_GameSettings;
4431 end;
4432 end
4433 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4434 begin
4435 with gGameSettings do
4436 begin
4437 if (Length(P) > 1) and
4438 ((P[1] = '1') or (P[1] = '0')) then
4439 begin
4440 if (P[1][1] = '1') then
4441 Options := Options or GAME_OPTION_BOTVSPLAYER
4442 else
4443 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4444 end;
4446 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4447 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4448 else
4449 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4451 if g_Game_IsNet then MH_SEND_GameSettings;
4452 end;
4453 end
4454 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4455 begin
4456 with gGameSettings do
4457 begin
4458 if (Length(P) > 1) and
4459 ((P[1] = '1') or (P[1] = '0')) then
4460 begin
4461 if (P[1][1] = '1') then
4462 Options := Options or GAME_OPTION_BOTVSMONSTER
4463 else
4464 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4465 end;
4467 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4468 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4469 else
4470 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4472 if g_Game_IsNet then MH_SEND_GameSettings;
4473 end;
4474 end
4475 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4476 begin
4477 if Length(P) > 1 then
4478 begin
4479 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4480 gGameSettings.WarmupTime := 30
4481 else
4482 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4483 end;
4485 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4486 [gGameSettings.WarmupTime]));
4487 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4488 end
4489 else if cmd = 'net_interp' then
4490 begin
4491 if (Length(P) > 1) then
4492 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4494 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4495 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4496 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4497 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4498 config.Free();
4499 end
4500 else if cmd = 'net_forceplayerupdate' then
4501 begin
4502 if (Length(P) > 1) and
4503 ((P[1] = '1') or (P[1] = '0')) then
4504 NetForcePlayerUpdate := (P[1][1] = '1');
4506 if NetForcePlayerUpdate then
4507 g_Console_Add('net_forceplayerupdate = 1')
4508 else
4509 g_Console_Add('net_forceplayerupdate = 0');
4510 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4511 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4512 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4513 config.Free();
4514 end
4515 else if cmd = 'net_predictself' then
4516 begin
4517 if (Length(P) > 1) and
4518 ((P[1] = '1') or (P[1] = '0')) then
4519 NetPredictSelf := (P[1][1] = '1');
4521 if NetPredictSelf then
4522 g_Console_Add('net_predictself = 1')
4523 else
4524 g_Console_Add('net_predictself = 0');
4525 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4526 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4527 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4528 config.Free();
4529 end
4530 else if cmd = 'sv_name' then
4531 begin
4532 if (Length(P) > 1) and (Length(P[1]) > 0) then
4533 begin
4534 NetServerName := P[1];
4535 if Length(NetServerName) > 64 then
4536 SetLength(NetServerName, 64);
4537 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4538 g_Net_Slist_Update;
4539 end;
4541 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4542 end
4543 else if cmd = 'sv_passwd' then
4544 begin
4545 if (Length(P) > 1) and (Length(P[1]) > 0) then
4546 begin
4547 NetPassword := P[1];
4548 if Length(NetPassword) > 24 then
4549 SetLength(NetPassword, 24);
4550 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4551 g_Net_Slist_Update;
4552 end;
4554 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4555 end
4556 else if cmd = 'sv_maxplrs' then
4557 begin
4558 if (Length(P) > 1) then
4559 begin
4560 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4561 if g_Game_IsServer and g_Game_IsNet then
4562 begin
4563 b := 0;
4564 for a := 0 to High(NetClients) do
4565 if NetClients[a].Used then
4566 begin
4567 Inc(b);
4568 if b > NetMaxClients then
4569 begin
4570 s := g_Player_Get(NetClients[a].Player).Name;
4571 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4572 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4573 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4574 end;
4575 end;
4576 if NetUseMaster then
4577 g_Net_Slist_Update;
4578 end;
4579 end;
4581 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4582 end
4583 else if cmd = 'sv_public' then
4584 begin
4585 if (Length(P) > 1) then
4586 begin
4587 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4588 if g_Game_IsServer and g_Game_IsNet then
4589 if NetUseMaster then
4590 begin
4591 if NetMPeer = nil then
4592 if not g_Net_Slist_Connect() then
4593 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4594 g_Net_Slist_Update();
4595 end
4596 else
4597 if NetMPeer <> nil then
4598 g_Net_Slist_Disconnect();
4599 end;
4601 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4602 end
4603 else if cmd = 'sv_intertime' then
4604 begin
4605 if (Length(P) > 1) then
4606 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4608 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4609 end
4610 else if cmd = 'p1_name' then
4611 begin
4612 if (Length(P) > 1) and gGameOn then
4613 begin
4614 if g_Game_IsClient then
4615 begin
4616 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4617 MC_SEND_PlayerSettings;
4618 end
4619 else
4620 if gPlayer1 <> nil then
4621 begin
4622 gPlayer1.Name := b_Text_Unformat(P[1]);
4623 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4624 end
4625 else
4626 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4627 end;
4628 end
4629 else if cmd = 'p2_name' then
4630 begin
4631 if (Length(P) > 1) and gGameOn then
4632 begin
4633 if g_Game_IsClient then
4634 begin
4635 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4636 MC_SEND_PlayerSettings;
4637 end
4638 else
4639 if gPlayer2 <> nil then
4640 begin
4641 gPlayer2.Name := b_Text_Unformat(P[1]);
4642 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4643 end
4644 else
4645 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4646 end;
4647 end
4648 else if cmd = 'p1_color' then
4649 begin
4650 if Length(P) > 3 then
4651 if g_Game_IsClient then
4652 begin
4653 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4654 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4655 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4656 MC_SEND_PlayerSettings;
4657 end
4658 else
4659 if gPlayer1 <> nil then
4660 begin
4661 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4662 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4663 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4664 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4665 end
4666 else
4667 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4668 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4669 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4670 end
4671 else if (cmd = 'p2_color') and not g_Game_IsNet then
4672 begin
4673 if Length(P) > 3 then
4674 if g_Game_IsClient then
4675 begin
4676 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4677 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4678 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4679 MC_SEND_PlayerSettings;
4680 end
4681 else
4682 if gPlayer2 <> nil then
4683 begin
4684 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4685 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4686 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4687 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4688 end
4689 else
4690 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4691 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4692 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4693 end
4694 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4695 begin
4696 if cmd = 'r_showtime' then
4697 begin
4698 if (Length(P) > 1) and
4699 ((P[1] = '1') or (P[1] = '0')) then
4700 gShowTime := (P[1][1] = '1');
4702 if gShowTime then
4703 g_Console_Add(_lc[I_MSG_TIME_ON])
4704 else
4705 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4706 end
4707 else if cmd = 'r_showscore' then
4708 begin
4709 if (Length(P) > 1) and
4710 ((P[1] = '1') or (P[1] = '0')) then
4711 gShowGoals := (P[1][1] = '1');
4713 if gShowGoals then
4714 g_Console_Add(_lc[I_MSG_SCORE_ON])
4715 else
4716 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4717 end
4718 else if cmd = 'r_showstat' then
4719 begin
4720 if (Length(P) > 1) and
4721 ((P[1] = '1') or (P[1] = '0')) then
4722 gShowStat := (P[1][1] = '1');
4724 if gShowStat then
4725 g_Console_Add(_lc[I_MSG_STATS_ON])
4726 else
4727 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4728 end
4729 else if cmd = 'r_showkillmsg' then
4730 begin
4731 if (Length(P) > 1) and
4732 ((P[1] = '1') or (P[1] = '0')) then
4733 gShowKillMsg := (P[1][1] = '1');
4735 if gShowKillMsg then
4736 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4737 else
4738 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4739 end
4740 else if cmd = 'r_showlives' then
4741 begin
4742 if (Length(P) > 1) and
4743 ((P[1] = '1') or (P[1] = '0')) then
4744 gShowLives := (P[1][1] = '1');
4746 if gShowLives then
4747 g_Console_Add(_lc[I_MSG_LIVES_ON])
4748 else
4749 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4750 end
4751 else if cmd = 'r_showspect' then
4752 begin
4753 if (Length(P) > 1) and
4754 ((P[1] = '1') or (P[1] = '0')) then
4755 gSpectHUD := (P[1][1] = '1');
4757 if gSpectHUD then
4758 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4759 else
4760 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4761 end
4762 else if cmd = 'r_showping' then
4763 begin
4764 if (Length(P) > 1) and
4765 ((P[1] = '1') or (P[1] = '0')) then
4766 gShowPing := (P[1][1] = '1');
4768 if gShowPing then
4769 g_Console_Add(_lc[I_MSG_PING_ON])
4770 else
4771 g_Console_Add(_lc[I_MSG_PING_OFF]);
4772 end
4773 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4774 begin
4775 if Length(P) > 1 then
4776 begin
4777 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4778 gGameSettings.GoalLimit := 0
4779 else
4780 begin
4781 b := 0;
4783 if gGameSettings.GameMode = GM_DM then
4784 begin // DM
4785 stat := g_Player_GetStats();
4786 if stat <> nil then
4787 for a := 0 to High(stat) do
4788 if stat[a].Frags > b then
4789 b := stat[a].Frags;
4790 end
4791 else // TDM/CTF
4792 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4794 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4795 end;
4797 if g_Game_IsNet then MH_SEND_GameSettings;
4798 end;
4800 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4801 end
4802 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4803 begin
4804 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4805 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4807 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4808 [gGameSettings.TimeLimit div 3600,
4809 (gGameSettings.TimeLimit div 60) mod 60,
4810 gGameSettings.TimeLimit mod 60]));
4811 if g_Game_IsNet then MH_SEND_GameSettings;
4812 end
4813 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4814 begin
4815 if Length(P) > 1 then
4816 begin
4817 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4818 gGameSettings.MaxLives := 0
4819 else
4820 begin
4821 b := 0;
4822 stat := g_Player_GetStats();
4823 if stat <> nil then
4824 for a := 0 to High(stat) do
4825 if stat[a].Lives > b then
4826 b := stat[a].Lives;
4827 gGameSettings.MaxLives :=
4828 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4829 end;
4830 end;
4832 g_Console_Add(Format(_lc[I_MSG_LIVES],
4833 [gGameSettings.MaxLives]));
4834 if g_Game_IsNet then MH_SEND_GameSettings;
4835 end;
4836 end;
4837 end;
4839 procedure DebugCommands(P: SArray);
4840 var
4841 a, b: Integer;
4842 cmd: string;
4843 //pt: TPoint;
4844 begin
4845 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
4846 if gDebugMode then
4847 begin
4848 cmd := LowerCase(P[0]);
4849 if cmd = 'd_window' then
4850 begin
4851 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
4852 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
4853 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
4854 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
4855 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
4856 end
4857 else if cmd = 'd_sounds' then
4858 begin
4859 if (Length(P) > 1) and
4860 ((P[1] = '1') or (P[1] = '0')) then
4861 g_Debug_Sounds := (P[1][1] = '1');
4863 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
4864 end
4865 else if cmd = 'd_frames' then
4866 begin
4867 if (Length(P) > 1) and
4868 ((P[1] = '1') or (P[1] = '0')) then
4869 g_Debug_Frames := (P[1][1] = '1');
4871 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
4872 end
4873 else if cmd = 'd_winmsg' then
4874 begin
4875 if (Length(P) > 1) and
4876 ((P[1] = '1') or (P[1] = '0')) then
4877 g_Debug_WinMsgs := (P[1][1] = '1');
4879 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
4880 end
4881 else if (cmd = 'd_monoff') and not g_Game_IsNet then
4882 begin
4883 if (Length(P) > 1) and
4884 ((P[1] = '1') or (P[1] = '0')) then
4885 g_Debug_MonsterOff := (P[1][1] = '1');
4887 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
4888 end
4889 else if (cmd = 'd_botoff') and not g_Game_IsNet then
4890 begin
4891 if Length(P) > 1 then
4892 case P[1][1] of
4893 '0': g_debug_BotAIOff := 0;
4894 '1': g_debug_BotAIOff := 1;
4895 '2': g_debug_BotAIOff := 2;
4896 '3': g_debug_BotAIOff := 3;
4897 end;
4899 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
4900 end
4901 else if cmd = 'd_monster' then
4902 begin
4903 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
4904 if Length(P) < 2 then
4905 begin
4906 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
4907 g_Console_Add('ID | Name');
4908 for b := MONSTER_DEMON to MONSTER_MAN do
4909 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
4910 end else
4911 begin
4912 a := StrToIntDef(P[1], 0);
4913 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4914 a := g_Monsters_GetIDByName(P[1]);
4916 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4917 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
4918 else
4919 begin
4920 with gPlayer1.Obj do
4921 b := g_Monsters_Create(a,
4922 X + Rect.X + (Rect.Width div 2),
4923 Y + Rect.Y + Rect.Height,
4924 gPlayer1.Direction, True);
4925 if (Length(P) > 2) and (b >= 0) then
4926 gMonsters[b].MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
4927 end;
4928 end;
4929 end
4930 else if (cmd = 'd_health') then
4931 begin
4932 if (Length(P) > 1) and
4933 ((P[1] = '1') or (P[1] = '0')) then
4934 g_debug_HealthBar := (P[1][1] = '1');
4936 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
4937 end
4938 else if (cmd = 'd_player') then
4939 begin
4940 if (Length(P) > 1) and
4941 ((P[1] = '1') or (P[1] = '0')) then
4942 g_debug_Player := (P[1][1] = '1');
4944 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
4945 end
4946 else if (cmd = 'd_joy') then
4947 begin
4948 for a := 1 to 8 do
4949 g_Console_Add(e_JoystickStateToString(a));
4950 end;
4951 end
4952 else
4953 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
4954 end;
4957 procedure GameCheats(P: SArray);
4958 var
4959 cmd: string;
4960 f, a: Integer;
4961 plr: TPlayer;
4962 begin
4963 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
4964 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
4965 begin
4966 g_Console_Add('not available');
4967 exit;
4968 end;
4969 plr := gPlayer1;
4970 if plr = nil then
4971 begin
4972 g_Console_Add('where is the player?!');
4973 exit;
4974 end;
4975 cmd := LowerCase(P[0]);
4976 // god
4977 if cmd = 'god' then
4978 begin
4979 plr.GodMode := not plr.GodMode;
4980 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
4981 exit;
4982 end;
4983 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
4984 if cmd = 'give' then
4985 begin
4986 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
4987 for f := 1 to High(P) do
4988 begin
4989 cmd := LowerCase(P[f]);
4990 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
4991 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
4992 if cmd = 'exit' then
4993 begin
4994 if gTriggers <> nil then
4995 begin
4996 for a := 0 to High(gTriggers) do
4997 begin
4998 if gTriggers[a].TriggerType = TRIGGER_EXIT then
4999 begin
5000 g_Console_Add('player left the map');
5001 gExitByTrigger := True;
5002 g_Game_ExitLevel(gTriggers[a].Data.MapName);
5003 break;
5004 end;
5005 end;
5006 end;
5007 continue;
5008 end;
5010 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5011 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5012 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5013 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5014 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5016 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5017 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5019 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5020 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;
5022 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5023 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5025 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5026 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5028 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5029 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5031 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5032 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5033 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5035 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5036 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5037 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5038 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;
5039 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5040 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5042 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;
5043 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;
5044 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;
5045 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;
5046 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;
5047 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;
5049 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5050 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;
5052 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;
5053 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;
5055 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5057 if cmd = 'ammo' then
5058 begin
5059 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5060 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5061 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5062 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5063 plr.GiveItem(ITEM_AMMO_FUELCAN);
5064 g_Console_Add('player got some ammo');
5065 continue;
5066 end;
5068 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5069 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5071 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5072 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5074 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5075 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5077 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5078 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5080 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5082 if cmd = 'weapons' then
5083 begin
5084 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5085 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5086 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5087 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5088 plr.GiveItem(ITEM_WEAPON_PLASMA);
5089 plr.GiveItem(ITEM_WEAPON_BFG);
5090 g_Console_Add('player got weapons');
5091 continue;
5092 end;
5094 if cmd = 'keys' then
5095 begin
5096 plr.GiveItem(ITEM_KEY_RED);
5097 plr.GiveItem(ITEM_KEY_GREEN);
5098 plr.GiveItem(ITEM_KEY_BLUE);
5099 g_Console_Add('player got all keys');
5100 continue;
5101 end;
5103 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5104 end;
5105 exit;
5106 end;
5107 // open
5108 if cmd = 'open' then
5109 begin
5110 g_Console_Add('player activated sesame');
5111 g_Triggers_OpenAll();
5112 exit;
5113 end;
5114 // fly
5115 if cmd = 'fly' then
5116 begin
5117 gFly := not gFly;
5118 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5119 exit;
5120 end;
5121 // noclip
5122 if cmd = 'noclip' then
5123 begin
5124 plr.SwitchNoClip;
5125 g_Console_Add('wall hardeness adjusted');
5126 exit;
5127 end;
5128 // notarget
5129 if cmd = 'notarget' then
5130 begin
5131 plr.NoTarget := not plr.NoTarget;
5132 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5133 exit;
5134 end;
5135 // noreload
5136 if cmd = 'noreload' then
5137 begin
5138 plr.NoReload := not plr.NoReload;
5139 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5140 exit;
5141 end;
5142 // speedy
5143 if cmd = 'speedy' then
5144 begin
5145 MAX_RUNVEL := 32-MAX_RUNVEL;
5146 g_Console_Add('speed adjusted');
5147 exit;
5148 end;
5149 // jumpy
5150 if cmd = 'jumpy' then
5151 begin
5152 VEL_JUMP := 30-VEL_JUMP;
5153 g_Console_Add('jump height adjusted');
5154 exit;
5155 end;
5156 // automap
5157 if cmd = 'automap' then
5158 begin
5159 gShowMap := not gShowMap;
5160 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5161 exit;
5162 end;
5163 // aimline
5164 if cmd = 'aimline' then
5165 begin
5166 gAimLine := not gAimLine;
5167 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5168 exit;
5169 end;
5170 end;
5172 procedure GameCommands(P: SArray);
5173 var
5174 a, b: Integer;
5175 s, pw: String;
5176 chstr: string;
5177 cmd: string;
5178 pl: pTNetClient = nil;
5179 plr: TPlayer;
5180 prt: Word;
5181 nm: Boolean;
5182 listen: LongWord;
5183 begin
5184 // Îáùèå êîìàíäû:
5185 cmd := LowerCase(P[0]);
5186 chstr := '';
5187 if (cmd = 'quit') or
5188 (cmd = 'exit') then
5189 begin
5190 g_Game_Free();
5191 g_Game_Quit();
5192 Exit;
5193 end
5194 else if cmd = 'pause' then
5195 begin
5196 if (g_ActiveWindow = nil) then
5197 g_Game_Pause(not gPause);
5198 end
5199 else if cmd = 'endgame' then
5200 gExit := EXIT_SIMPLE
5201 else if cmd = 'restart' then
5202 begin
5203 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5204 begin
5205 if g_Game_IsClient then
5206 begin
5207 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5208 Exit;
5209 end;
5210 g_Game_Restart();
5211 end else
5212 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5213 end
5214 else if cmd = 'kick' then
5215 begin
5216 if g_Game_IsServer then
5217 begin
5218 if Length(P) < 2 then
5219 begin
5220 g_Console_Add('kick <name>');
5221 Exit;
5222 end;
5223 if P[1] = '' then
5224 begin
5225 g_Console_Add('kick <name>');
5226 Exit;
5227 end;
5229 if g_Game_IsNet then
5230 pl := g_Net_Client_ByName(P[1]);
5231 if (pl <> nil) then
5232 begin
5233 s := g_Net_ClientName_ByID(pl^.ID);
5234 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5235 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5236 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5237 if NetUseMaster then
5238 g_Net_Slist_Update;
5239 end else if gPlayers <> nil then
5240 for a := Low(gPlayers) to High(gPlayers) do
5241 if gPlayers[a] <> nil then
5242 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5243 begin
5244 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5245 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5246 continue;
5247 gPlayers[a].Lives := 0;
5248 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5249 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5250 g_Player_Remove(gPlayers[a].UID);
5251 if NetUseMaster then
5252 g_Net_Slist_Update;
5253 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5254 g_Bot_MixNames();
5255 end;
5256 end else
5257 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5258 end
5259 else if cmd = 'kick_id' then
5260 begin
5261 if g_Game_IsServer and g_Game_IsNet then
5262 begin
5263 if Length(P) < 2 then
5264 begin
5265 g_Console_Add('kick_id <client ID>');
5266 Exit;
5267 end;
5268 if P[1] = '' then
5269 begin
5270 g_Console_Add('kick_id <client ID>');
5271 Exit;
5272 end;
5274 a := StrToIntDef(P[1], 0);
5275 if (NetClients <> nil) and (a <= High(NetClients)) then
5276 begin
5277 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5278 begin
5279 s := g_Net_ClientName_ByID(NetClients[a].ID);
5280 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5281 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5282 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5283 if NetUseMaster then
5284 g_Net_Slist_Update;
5285 end;
5286 end;
5287 end else
5288 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5289 end
5290 else if cmd = 'ban' then
5291 begin
5292 if g_Game_IsServer and g_Game_IsNet then
5293 begin
5294 if Length(P) < 2 then
5295 begin
5296 g_Console_Add('ban <name>');
5297 Exit;
5298 end;
5299 if P[1] = '' then
5300 begin
5301 g_Console_Add('ban <name>');
5302 Exit;
5303 end;
5305 pl := g_Net_Client_ByName(P[1]);
5306 if (pl <> nil) then
5307 begin
5308 s := g_Net_ClientName_ByID(pl^.ID);
5309 g_Net_BanHost(pl^.Peer^.address.host, False);
5310 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5311 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5312 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5313 if NetUseMaster then
5314 g_Net_Slist_Update;
5315 end else
5316 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5317 end else
5318 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5319 end
5320 else if cmd = 'ban_id' then
5321 begin
5322 if g_Game_IsServer and g_Game_IsNet then
5323 begin
5324 if Length(P) < 2 then
5325 begin
5326 g_Console_Add('ban_id <client ID>');
5327 Exit;
5328 end;
5329 if P[1] = '' then
5330 begin
5331 g_Console_Add('ban_id <client ID>');
5332 Exit;
5333 end;
5335 a := StrToIntDef(P[1], 0);
5336 if (NetClients <> nil) and (a <= High(NetClients)) then
5337 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5338 begin
5339 s := g_Net_ClientName_ByID(NetClients[a].ID);
5340 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5341 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5342 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5343 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5344 if NetUseMaster then
5345 g_Net_Slist_Update;
5346 end;
5347 end else
5348 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5349 end
5350 else if cmd = 'permban' then
5351 begin
5352 if g_Game_IsServer and g_Game_IsNet then
5353 begin
5354 if Length(P) < 2 then
5355 begin
5356 g_Console_Add('permban <name>');
5357 Exit;
5358 end;
5359 if P[1] = '' then
5360 begin
5361 g_Console_Add('permban <name>');
5362 Exit;
5363 end;
5365 pl := g_Net_Client_ByName(P[1]);
5366 if (pl <> nil) then
5367 begin
5368 s := g_Net_ClientName_ByID(pl^.ID);
5369 g_Net_BanHost(pl^.Peer^.address.host);
5370 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5371 g_Net_SaveBanList();
5372 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5373 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5374 if NetUseMaster then
5375 g_Net_Slist_Update;
5376 end else
5377 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5378 end else
5379 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5380 end
5381 else if cmd = 'permban_id' then
5382 begin
5383 if g_Game_IsServer and g_Game_IsNet then
5384 begin
5385 if Length(P) < 2 then
5386 begin
5387 g_Console_Add('permban_id <client ID>');
5388 Exit;
5389 end;
5390 if P[1] = '' then
5391 begin
5392 g_Console_Add('permban_id <client ID>');
5393 Exit;
5394 end;
5396 a := StrToIntDef(P[1], 0);
5397 if (NetClients <> nil) and (a <= High(NetClients)) then
5398 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5399 begin
5400 s := g_Net_ClientName_ByID(NetClients[a].ID);
5401 g_Net_BanHost(NetClients[a].Peer^.address.host);
5402 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5403 g_Net_SaveBanList();
5404 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5405 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5406 if NetUseMaster then
5407 g_Net_Slist_Update;
5408 end;
5409 end else
5410 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5411 end
5412 else if cmd = 'unban' then
5413 begin
5414 if g_Game_IsServer and g_Game_IsNet then
5415 begin
5416 if Length(P) < 2 then
5417 begin
5418 g_Console_Add('unban <IP Address>');
5419 Exit;
5420 end;
5421 if P[1] = '' then
5422 begin
5423 g_Console_Add('unban <IP Address>');
5424 Exit;
5425 end;
5427 if g_Net_UnbanHost(P[1]) then
5428 begin
5429 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5430 g_Net_SaveBanList();
5431 end else
5432 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5433 end else
5434 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5435 end
5436 else if cmd = 'clientlist' then
5437 begin
5438 if g_Game_IsServer and g_Game_IsNet then
5439 begin
5440 b := 0;
5441 if NetClients <> nil then
5442 for a := Low(NetClients) to High(NetClients) do
5443 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5444 begin
5445 plr := g_Player_Get(NetClients[a].Player);
5446 if plr = nil then continue;
5447 Inc(b);
5448 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5449 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5450 end;
5451 if b = 0 then
5452 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5453 end else
5454 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5455 end
5456 else if cmd = 'connect' then
5457 begin
5458 if (NetMode = NET_NONE) then
5459 begin
5460 if Length(P) < 2 then
5461 begin
5462 g_Console_Add('connect <IP> [port] [password]');
5463 Exit;
5464 end;
5465 if P[1] = '' then
5466 begin
5467 g_Console_Add('connect <IP> [port] [password]');
5468 Exit;
5469 end;
5471 if Length(P) > 2 then
5472 prt := StrToIntDef(P[2], 25666)
5473 else
5474 prt := 25666;
5476 if Length(P) > 3 then
5477 pw := P[3]
5478 else
5479 pw := '';
5481 g_Game_StartClient(P[1], prt, pw);
5482 end;
5483 end
5484 else if cmd = 'disconnect' then
5485 begin
5486 if (NetMode = NET_CLIENT) then
5487 g_Net_Disconnect();
5488 end
5489 else if cmd = 'reconnect' then
5490 begin
5491 if (NetMode = NET_SERVER) then
5492 Exit;
5494 if (NetMode = NET_CLIENT) then
5495 begin
5496 g_Net_Disconnect();
5497 gExit := EXIT_SIMPLE;
5498 EndGame;
5499 end;
5501 //TODO: Use last successful password to reconnect, instead of ''
5502 g_Game_StartClient(NetClientIP, NetClientPort, '');
5503 end
5504 else if (cmd = 'addbot') or
5505 (cmd = 'bot_add') then
5506 begin
5507 if Length(P) > 1 then
5508 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5509 else
5510 g_Bot_Add(TEAM_NONE, 2);
5511 end
5512 else if cmd = 'bot_addlist' then
5513 begin
5514 if Length(P) > 1 then
5515 if Length(P) = 2 then
5516 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5517 else
5518 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5519 end
5520 else if cmd = 'bot_removeall' then
5521 g_Bot_RemoveAll()
5522 else if cmd = 'chat' then
5523 begin
5524 if g_Game_IsNet then
5525 begin
5526 if Length(P) > 1 then
5527 begin
5528 for a := 1 to High(P) do
5529 chstr := chstr + P[a] + ' ';
5531 if Length(chstr) > 200 then SetLength(chstr, 200);
5533 if Length(chstr) < 1 then
5534 begin
5535 g_Console_Add('chat <text>');
5536 Exit;
5537 end;
5539 chstr := b_Text_Format(chstr);
5540 if g_Game_IsClient then
5541 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5542 else
5543 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5544 end
5545 else
5546 g_Console_Add('chat <text>');
5547 end else
5548 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5549 end
5550 else if cmd = 'teamchat' then
5551 begin
5552 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5553 begin
5554 if Length(P) > 1 then
5555 begin
5556 for a := 1 to High(P) do
5557 chstr := chstr + P[a] + ' ';
5559 if Length(chstr) > 200 then SetLength(chstr, 200);
5561 if Length(chstr) < 1 then
5562 begin
5563 g_Console_Add('teamchat <text>');
5564 Exit;
5565 end;
5567 chstr := b_Text_Format(chstr);
5568 if g_Game_IsClient then
5569 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5570 else
5571 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5572 gPlayer1Settings.Team);
5573 end
5574 else
5575 g_Console_Add('teamchat <text>');
5576 end else
5577 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5578 end
5579 else if cmd = 'game' then
5580 begin
5581 if gGameSettings.GameType <> GT_NONE then
5582 begin
5583 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5584 Exit;
5585 end;
5586 if Length(P) = 1 then
5587 begin
5588 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5589 Exit;
5590 end;
5591 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5592 P[1] := addWadExtension(P[1]);
5593 if FileExists(MapsDir + P[1]) then
5594 begin
5595 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5596 if Length(P) < 3 then
5597 begin
5598 SetLength(P, 3);
5599 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5600 end;
5602 s := P[1] + ':\' + UpperCase(P[2]);
5604 if g_Map_Exist(MapsDir + s) then
5605 begin
5606 // Çàïóñêàåì ñâîþ èãðó
5607 g_Game_Free();
5608 with gGameSettings do
5609 begin
5610 GameMode := g_Game_TextToMode(gcGameMode);
5611 if gSwitchGameMode <> GM_NONE then
5612 GameMode := gSwitchGameMode;
5613 if GameMode = GM_NONE then GameMode := GM_DM;
5614 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5615 b := 1;
5616 if Length(P) >= 4 then
5617 b := StrToIntDef(P[3], 1);
5618 g_Game_StartCustom(s, GameMode, TimeLimit,
5619 GoalLimit, MaxLives, Options, b);
5620 end;
5621 end
5622 else
5623 if P[2] = '' then
5624 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5625 else
5626 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5627 end else
5628 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5629 end
5630 else if cmd = 'host' then
5631 begin
5632 if gGameSettings.GameType <> GT_NONE then
5633 begin
5634 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5635 Exit;
5636 end;
5637 if Length(P) < 4 then
5638 begin
5639 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5640 Exit;
5641 end;
5642 if not StrToIp(P[1], listen) then
5643 Exit;
5644 prt := StrToIntDef(P[2], 25666);
5646 P[3] := addWadExtension(P[3]);
5647 if FileExists(MapsDir + P[3]) then
5648 begin
5649 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5650 if Length(P) < 5 then
5651 begin
5652 SetLength(P, 5);
5653 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5654 end;
5656 s := P[3] + ':\' + UpperCase(P[4]);
5658 if g_Map_Exist(MapsDir + s) then
5659 begin
5660 // Çàïóñêàåì ñâîþ èãðó
5661 g_Game_Free();
5662 with gGameSettings do
5663 begin
5664 GameMode := g_Game_TextToMode(gcGameMode);
5665 if gSwitchGameMode <> GM_NONE then
5666 GameMode := gSwitchGameMode;
5667 if GameMode = GM_NONE then GameMode := GM_DM;
5668 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5669 b := 0;
5670 if Length(P) >= 6 then
5671 b := StrToIntDef(P[5], 0);
5672 g_Game_StartServer(s, GameMode, TimeLimit,
5673 GoalLimit, MaxLives, Options, b, listen, prt);
5674 end;
5675 end
5676 else
5677 if P[4] = '' then
5678 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5679 else
5680 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5681 end else
5682 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5683 end
5684 else if cmd = 'map' then
5685 begin
5686 if Length(P) = 1 then
5687 begin
5688 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5689 begin
5690 g_Console_Add(cmd + ' <MAP>');
5691 g_Console_Add(cmd + ' <WAD> [MAP]');
5692 end else
5693 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5694 end else
5695 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5696 begin
5697 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5698 if Length(P) < 3 then
5699 begin
5700 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5701 s := UpperCase(P[1]);
5702 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5703 begin // Êàðòà íàøëàñü
5704 gExitByTrigger := False;
5705 if gGameOn then
5706 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5707 gNextMap := s;
5708 gExit := EXIT_ENDLEVELCUSTOM;
5709 end
5710 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5711 g_Game_ChangeMap(s);
5712 end else
5713 begin
5714 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5715 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5716 P[1] := addWadExtension(P[1]);
5717 if FileExists(MapsDir + P[1]) then
5718 begin
5719 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5720 SetLength(P, 3);
5721 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5723 s := P[1] + ':\' + P[2];
5725 if g_Map_Exist(MapsDir + s) then
5726 begin
5727 gExitByTrigger := False;
5728 if gGameOn then
5729 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5730 gNextMap := s;
5731 gExit := EXIT_ENDLEVELCUSTOM;
5732 end
5733 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5734 g_Game_ChangeMap(s);
5735 end else
5736 if P[2] = '' then
5737 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5738 else
5739 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5740 end else
5741 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5742 end;
5743 end else
5744 begin
5745 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5746 P[1] := addWadExtension(P[1]);
5747 if FileExists(MapsDir + P[1]) then
5748 begin
5749 // Íàøëè WAD ôàéë
5750 P[2] := UpperCase(P[2]);
5751 s := P[1] + ':\' + P[2];
5753 if g_Map_Exist(MapsDir + s) then
5754 begin // Íàøëè êàðòó
5755 gExitByTrigger := False;
5756 if gGameOn then
5757 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5758 gNextMap := s;
5759 gExit := EXIT_ENDLEVELCUSTOM;
5760 end
5761 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5762 g_Game_ChangeMap(s);
5763 end else
5764 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5765 end else
5766 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5767 end;
5768 end else
5769 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5770 end
5771 else if cmd = 'nextmap' then
5772 begin
5773 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5774 g_Console_Add(_lc[I_MSG_NOT_GAME])
5775 else begin
5776 nm := True;
5777 if Length(P) = 1 then
5778 begin
5779 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5780 begin
5781 g_Console_Add(cmd + ' <MAP>');
5782 g_Console_Add(cmd + ' <WAD> [MAP]');
5783 end else begin
5784 nm := False;
5785 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5786 end;
5787 end else
5788 begin
5789 nm := False;
5790 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5791 begin
5792 if Length(P) < 3 then
5793 begin
5794 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5795 s := UpperCase(P[1]);
5796 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5797 begin // Êàðòà íàøëàñü
5798 gExitByTrigger := False;
5799 gNextMap := s;
5800 nm := True;
5801 end else
5802 begin
5803 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5804 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5805 P[1] := addWadExtension(P[1]);
5806 if FileExists(MapsDir + P[1]) then
5807 begin
5808 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5809 SetLength(P, 3);
5810 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5812 s := P[1] + ':\' + P[2];
5814 if g_Map_Exist(MapsDir + s) then
5815 begin // Óñòàíàâëèâàåì êàðòó
5816 gExitByTrigger := False;
5817 gNextMap := s;
5818 nm := True;
5819 end else
5820 if P[2] = '' then
5821 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5822 else
5823 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5824 end else
5825 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5826 end;
5827 end else
5828 begin
5829 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5830 P[1] := addWadExtension(P[1]);
5831 if FileExists(MapsDir + P[1]) then
5832 begin
5833 // Íàøëè WAD ôàéë
5834 P[2] := UpperCase(P[2]);
5835 s := P[1] + ':\' + P[2];
5837 if g_Map_Exist(MapsDir + s) then
5838 begin // Íàøëè êàðòó
5839 gExitByTrigger := False;
5840 gNextMap := s;
5841 nm := True;
5842 end else
5843 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5844 end else
5845 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5846 end;
5847 end else
5848 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5849 end;
5850 if nm then
5851 if gNextMap = '' then
5852 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
5853 else
5854 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
5855 end;
5856 end
5857 else if (cmd = 'endmap') or (cmd = 'goodbye') then
5858 begin
5859 if not gGameOn then
5860 g_Console_Add(_lc[I_MSG_NOT_GAME])
5861 else
5862 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5863 begin
5864 gExitByTrigger := False;
5865 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
5866 if (gNextMap = '') and (gTriggers <> nil) then
5867 for a := 0 to High(gTriggers) do
5868 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5869 begin
5870 gExitByTrigger := True;
5871 gNextMap := gTriggers[a].Data.MapName;
5872 Break;
5873 end;
5874 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
5875 if gNextMap = '' then
5876 gNextMap := g_Game_GetNextMap();
5877 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
5878 if Pos(':\', gNextMap) = 0 then
5879 s := gGameSettings.WAD + ':\' + gNextMap
5880 else
5881 s := gNextMap;
5882 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
5883 if g_Map_Exist(MapsDir + s) then
5884 gExit := EXIT_ENDLEVELCUSTOM
5885 else
5886 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
5887 end else
5888 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5889 end
5890 else if (cmd = 'event') then
5891 begin
5892 if (Length(P) <= 1) then
5893 begin
5894 for a := 0 to High(gEvents) do
5895 if gEvents[a].Command = '' then
5896 g_Console_Add(gEvents[a].Name + ' <none>')
5897 else
5898 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5899 Exit;
5900 end;
5901 if (Length(P) = 2) then
5902 begin
5903 for a := 0 to High(gEvents) do
5904 if gEvents[a].Name = P[1] then
5905 if gEvents[a].Command = '' then
5906 g_Console_Add(gEvents[a].Name + ' <none>')
5907 else
5908 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5909 Exit;
5910 end;
5911 for a := 0 to High(gEvents) do
5912 if gEvents[a].Name = P[1] then
5913 begin
5914 gEvents[a].Command := '';
5915 for b := 2 to High(P) do
5916 if Pos(' ', P[b]) = 0 then
5917 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
5918 else
5919 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
5920 gEvents[a].Command := Trim(gEvents[a].Command);
5921 Exit;
5922 end;
5923 end
5924 // Êîìàíäû Ñâîåé èãðû:
5925 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5926 begin
5927 if cmd = 'bot_addred' then
5928 begin
5929 if Length(P) > 1 then
5930 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
5931 else
5932 g_Bot_Add(TEAM_RED, 2);
5933 end
5934 else if cmd = 'bot_addblue' then
5935 begin
5936 if Length(P) > 1 then
5937 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
5938 else
5939 g_Bot_Add(TEAM_BLUE, 2);
5940 end
5941 else if cmd = 'suicide' then
5942 begin
5943 if gGameOn then
5944 begin
5945 if g_Game_IsClient then
5946 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
5947 else
5948 begin
5949 if gPlayer1 <> nil then
5950 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
5951 if gPlayer2 <> nil then
5952 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
5953 end;
5954 end;
5955 end
5956 else if cmd = 'spectate' then
5957 begin
5958 if not gGameOn then
5959 Exit;
5960 g_Game_Spectate();
5961 end
5962 else if cmd = 'say' then
5963 begin
5964 if g_Game_IsServer and g_Game_IsNet then
5965 begin
5966 if Length(P) > 1 then
5967 begin
5968 chstr := '';
5969 for a := 1 to High(P) do
5970 chstr := chstr + P[a] + ' ';
5972 if Length(chstr) > 200 then SetLength(chstr, 200);
5974 if Length(chstr) < 1 then
5975 begin
5976 g_Console_Add('say <text>');
5977 Exit;
5978 end;
5980 chstr := b_Text_Format(chstr);
5981 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
5982 end
5983 else g_Console_Add('say <text>');
5984 end else
5985 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5986 end
5987 else if cmd = 'tell' then
5988 begin
5989 if g_Game_IsServer and g_Game_IsNet then
5990 begin
5991 if (Length(P) > 2) and (P[1] <> '') then
5992 begin
5993 chstr := '';
5994 for a := 2 to High(P) do
5995 chstr := chstr + P[a] + ' ';
5997 if Length(chstr) > 200 then SetLength(chstr, 200);
5999 if Length(chstr) < 1 then
6000 begin
6001 g_Console_Add('tell <playername> <text>');
6002 Exit;
6003 end;
6005 pl := g_Net_Client_ByName(P[1]);
6006 if pl <> nil then
6007 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6008 else
6009 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6010 end
6011 else g_Console_Add('tell <playername> <text>');
6012 end else
6013 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6014 end
6015 else if (cmd = 'overtime') and not g_Game_IsClient then
6016 begin
6017 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6018 Exit;
6019 // Äîïîëíèòåëüíîå âðåìÿ:
6020 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6022 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6023 [gGameSettings.TimeLimit div 3600,
6024 (gGameSettings.TimeLimit div 60) mod 60,
6025 gGameSettings.TimeLimit mod 60]));
6026 if g_Game_IsNet then MH_SEND_GameSettings;
6027 end
6028 else if (cmd = 'rcon_password') and g_Game_IsClient then
6029 begin
6030 if (Length(P) <= 1) then
6031 g_Console_Add('rcon_password <password>')
6032 else
6033 MC_SEND_RCONPassword(P[1]);
6034 end
6035 else if cmd = 'rcon' then
6036 begin
6037 if g_Game_IsClient then
6038 begin
6039 if Length(P) > 1 then
6040 begin
6041 chstr := '';
6042 for a := 1 to High(P) do
6043 chstr := chstr + P[a] + ' ';
6045 if Length(chstr) > 200 then SetLength(chstr, 200);
6047 if Length(chstr) < 1 then
6048 begin
6049 g_Console_Add('rcon <command>');
6050 Exit;
6051 end;
6053 MC_SEND_RCONCommand(chstr);
6054 end
6055 else g_Console_Add('rcon <command>');
6056 end;
6057 end
6058 else if cmd = 'ready' then
6059 begin
6060 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6061 gLMSRespawnTime := gTime + 100;
6062 end
6063 else if (cmd = 'callvote') and g_Game_IsNet then
6064 begin
6065 if Length(P) > 1 then
6066 begin
6067 chstr := '';
6068 for a := 1 to High(P) do begin
6069 if a > 1 then chstr := chstr + ' ';
6070 chstr := chstr + P[a];
6071 end;
6073 if Length(chstr) > 200 then SetLength(chstr, 200);
6075 if Length(chstr) < 1 then
6076 begin
6077 g_Console_Add('callvote <command>');
6078 Exit;
6079 end;
6081 if g_Game_IsClient then
6082 MC_SEND_Vote(True, chstr)
6083 else
6084 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6085 g_Console_Process('vote', True);
6086 end
6087 else
6088 g_Console_Add('callvote <command>');
6089 end
6090 else if (cmd = 'vote') and g_Game_IsNet then
6091 begin
6092 if g_Game_IsClient then
6093 MC_SEND_Vote(False)
6094 else if gVoteInProgress then
6095 begin
6096 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6097 a := Floor((NetClientCount+1)/2.0) + 1
6098 else
6099 a := Floor(NetClientCount/2.0) + 1;
6100 if gVoted then
6101 begin
6102 Dec(gVoteCount);
6103 gVoted := False;
6104 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6105 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6106 end
6107 else
6108 begin
6109 Inc(gVoteCount);
6110 gVoted := True;
6111 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6112 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6113 g_Game_CheckVote;
6114 end;
6115 end;
6116 end
6117 end;
6118 end;
6120 procedure g_TakeScreenShot();
6121 var
6122 a: Word;
6123 FileName: string;
6124 ssdir, t: string;
6125 st: TStream;
6126 ok: Boolean;
6127 begin
6128 if e_NoGraphics then Exit;
6129 ssdir := GameDir+'/screenshots';
6130 if not findFileCI(ssdir, true) then
6131 begin
6132 // try to create dir
6133 try
6134 CreateDir(ssdir);
6135 except
6136 end;
6137 if not findFileCI(ssdir, true) then exit; // alas
6138 end;
6139 try
6140 for a := 1 to High(Word) do
6141 begin
6142 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6143 t := FileName;
6144 if findFileCI(t, true) then continue;
6145 if not findFileCI(FileName) then
6146 begin
6147 ok := false;
6148 st := createDiskFile(FileName);
6149 try
6150 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6151 ok := true;
6152 finally
6153 st.Free();
6154 end;
6155 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6156 break;
6157 end;
6158 end;
6159 except
6160 end;
6161 end;
6163 procedure g_Game_InGameMenu(Show: Boolean);
6164 begin
6165 if (g_ActiveWindow = nil) and Show then
6166 begin
6167 if gGameSettings.GameType = GT_SINGLE then
6168 g_GUI_ShowWindow('GameSingleMenu')
6169 else
6170 begin
6171 if g_Game_IsClient then
6172 g_GUI_ShowWindow('GameClientMenu')
6173 else
6174 if g_Game_IsNet then
6175 g_GUI_ShowWindow('GameServerMenu')
6176 else
6177 g_GUI_ShowWindow('GameCustomMenu');
6178 end;
6179 g_Sound_PlayEx('MENU_OPEN');
6181 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6182 if (not g_Game_IsNet) then
6183 g_Game_Pause(True);
6184 end
6185 else
6186 if (g_ActiveWindow <> nil) and (not Show) then
6187 begin
6188 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6189 if (not g_Game_IsNet) then
6190 g_Game_Pause(False);
6191 end;
6192 end;
6194 procedure g_Game_Pause(Enable: Boolean);
6195 begin
6196 if not gGameOn then
6197 Exit;
6199 if gPause = Enable then
6200 Exit;
6202 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6203 Exit;
6205 gPause := Enable;
6206 g_Game_PauseAllSounds(Enable);
6207 end;
6209 procedure g_Game_PauseAllSounds(Enable: Boolean);
6210 var
6211 i: Integer;
6212 begin
6213 // Òðèããåðû:
6214 if gTriggers <> nil then
6215 for i := 0 to High(gTriggers) do
6216 with gTriggers[i] do
6217 if (TriggerType = TRIGGER_SOUND) and
6218 (Sound <> nil) and
6219 Sound.IsPlaying() then
6220 begin
6221 Sound.Pause(Enable);
6222 end;
6224 // Çâóêè èãðîêîâ:
6225 if gPlayers <> nil then
6226 for i := 0 to High(gPlayers) do
6227 if gPlayers[i] <> nil then
6228 gPlayers[i].PauseSounds(Enable);
6230 // Ìóçûêà:
6231 if gMusic <> nil then
6232 gMusic.Pause(Enable);
6233 end;
6235 procedure g_Game_StopAllSounds(all: Boolean);
6236 var
6237 i: Integer;
6238 begin
6239 if gTriggers <> nil then
6240 for i := 0 to High(gTriggers) do
6241 with gTriggers[i] do
6242 if (TriggerType = TRIGGER_SOUND) and
6243 (Sound <> nil) then
6244 Sound.Stop();
6246 if gMusic <> nil then
6247 gMusic.Stop();
6249 if all then
6250 e_StopChannels();
6251 end;
6253 procedure g_Game_UpdateTriggerSounds();
6254 var
6255 i: Integer;
6256 begin
6257 if gTriggers <> nil then
6258 for i := 0 to High(gTriggers) do
6259 with gTriggers[i] do
6260 if (TriggerType = TRIGGER_SOUND) and
6261 (Sound <> nil) and
6262 (Data.Local) and
6263 Sound.IsPlaying() then
6264 begin
6265 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6266 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6267 begin
6268 Sound.SetPan(0.5 - Data.Pan/255.0);
6269 Sound.SetVolume(Data.Volume/255.0);
6270 end
6271 else
6272 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6273 end;
6274 end;
6276 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6277 begin
6278 Result := False;
6279 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6280 begin
6281 Result := True;
6282 Exit;
6283 end;
6284 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6285 begin
6286 Result := True;
6287 Exit;
6288 end;
6289 if gSpectMode <> SPECT_PLAYERS then
6290 Exit;
6291 if gSpectPID1 = UID then
6292 begin
6293 Result := True;
6294 Exit;
6295 end;
6296 if gSpectViewTwo and (gSpectPID2 = UID) then
6297 begin
6298 Result := True;
6299 Exit;
6300 end;
6301 end;
6303 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6304 var
6305 Pl: TPlayer;
6306 begin
6307 Result := False;
6308 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6309 begin
6310 Result := True;
6311 Exit;
6312 end;
6313 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6314 begin
6315 Result := True;
6316 Exit;
6317 end;
6318 if gSpectMode <> SPECT_PLAYERS then
6319 Exit;
6320 Pl := g_Player_Get(gSpectPID1);
6321 if (Pl <> nil) and (Pl.Team = Team) then
6322 begin
6323 Result := True;
6324 Exit;
6325 end;
6326 if gSpectViewTwo then
6327 begin
6328 Pl := g_Player_Get(gSpectPID2);
6329 if (Pl <> nil) and (Pl.Team = Team) then
6330 begin
6331 Result := True;
6332 Exit;
6333 end;
6334 end;
6335 end;
6337 procedure g_Game_Message(Msg: string; Time: Word);
6338 begin
6339 MessageText := b_Text_Format(Msg);
6340 MessageTime := Time;
6341 end;
6343 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6344 var
6345 a: Integer;
6346 begin
6347 case gAnnouncer of
6348 ANNOUNCE_NONE:
6349 Exit;
6350 ANNOUNCE_ME,
6351 ANNOUNCE_MEPLUS:
6352 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6353 Exit;
6354 end;
6355 for a := 0 to 3 do
6356 if goodsnd[a].IsPlaying() then
6357 Exit;
6359 goodsnd[Random(4)].Play();
6360 end;
6362 procedure g_Game_Announce_KillCombo(Param: Integer);
6363 var
6364 UID: Word;
6365 c, n: Byte;
6366 Pl: TPlayer;
6367 Name: String;
6368 begin
6369 UID := Param and $FFFF;
6370 c := Param shr 16;
6371 if c < 2 then
6372 Exit;
6374 Pl := g_Player_Get(UID);
6375 if Pl = nil then
6376 Name := '?'
6377 else
6378 Name := Pl.Name;
6380 case c of
6381 2: begin
6382 n := 0;
6383 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6384 end;
6385 3: begin
6386 n := 1;
6387 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6388 end;
6389 4: begin
6390 n := 2;
6391 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6392 end;
6393 else begin
6394 n := 3;
6395 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6396 end;
6397 end;
6399 case gAnnouncer of
6400 ANNOUNCE_NONE:
6401 Exit;
6402 ANNOUNCE_ME:
6403 if not g_Game_IsWatchedPlayer(UID) then
6404 Exit;
6405 ANNOUNCE_MEPLUS:
6406 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6407 Exit;
6408 end;
6410 if killsnd[n].IsPlaying() then
6411 killsnd[n].Stop();
6412 killsnd[n].Play();
6413 end;
6415 procedure g_Game_StartVote(Command, Initiator: string);
6416 var
6417 Need: Integer;
6418 begin
6419 if not gVotesEnabled then Exit;
6420 if gGameSettings.GameType <> GT_SERVER then Exit;
6421 if gVoteInProgress or gVotePassed then
6422 begin
6423 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6424 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6425 Exit;
6426 end;
6427 gVoteInProgress := True;
6428 gVotePassed := False;
6429 gVoteTimer := gTime + gVoteTimeout * 1000;
6430 gVoteCount := 0;
6431 gVoted := False;
6432 gVoteCommand := Command;
6434 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6435 Need := Floor((NetClientCount+1)/2.0)+1
6436 else
6437 Need := Floor(NetClientCount/2.0)+1;
6438 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6439 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6440 end;
6442 procedure g_Game_CheckVote;
6443 var
6444 I, Need: Integer;
6445 begin
6446 if gGameSettings.GameType <> GT_SERVER then Exit;
6447 if not gVoteInProgress then Exit;
6449 if (gTime >= gVoteTimer) then
6450 begin
6451 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6452 Need := Floor((NetClientCount+1)/2.0) + 1
6453 else
6454 Need := Floor(NetClientCount/2.0) + 1;
6455 if gVoteCount >= Need then
6456 begin
6457 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6458 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6459 gVotePassed := True;
6460 gVoteCmdTimer := gTime + 5000;
6461 end
6462 else
6463 begin
6464 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6465 MH_SEND_VoteEvent(NET_VE_FAILED);
6466 end;
6467 if NetClients <> nil then
6468 for I := Low(NetClients) to High(NetClients) do
6469 if NetClients[i].Used then
6470 NetClients[i].Voted := False;
6471 gVoteInProgress := False;
6472 gVoted := False;
6473 gVoteCount := 0;
6474 end
6475 else
6476 begin
6477 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6478 Need := Floor((NetClientCount+1)/2.0) + 1
6479 else
6480 Need := Floor(NetClientCount/2.0) + 1;
6481 if gVoteCount >= Need then
6482 begin
6483 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6484 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6485 gVoteInProgress := False;
6486 gVotePassed := True;
6487 gVoteCmdTimer := gTime + 5000;
6488 gVoted := False;
6489 gVoteCount := 0;
6490 if NetClients <> nil then
6491 for I := Low(NetClients) to High(NetClients) do
6492 if NetClients[i].Used then
6493 NetClients[i].Voted := False;
6494 end;
6495 end;
6496 end;
6498 procedure g_Game_LoadMapList(FileName: string);
6499 var
6500 ListFile: TextFile;
6501 s: string;
6502 begin
6503 MapList := nil;
6504 MapIndex := -1;
6506 if not FileExists(FileName) then Exit;
6508 AssignFile(ListFile, FileName);
6509 Reset(ListFile);
6510 while not EOF(ListFile) do
6511 begin
6512 ReadLn(ListFile, s);
6514 s := Trim(s);
6515 if s = '' then Continue;
6517 SetLength(MapList, Length(MapList)+1);
6518 MapList[High(MapList)] := s;
6519 end;
6520 CloseFile(ListFile);
6521 end;
6523 procedure g_Game_SetDebugMode();
6524 begin
6525 gDebugMode := True;
6526 // ×èòû (äàæå â ñâîåé èãðå):
6527 gCheats := True;
6528 end;
6530 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6531 var
6532 i: Word;
6533 begin
6534 if Length(LoadingStat.Msgs) = 0 then
6535 Exit;
6537 with LoadingStat do
6538 begin
6539 if not reWrite then
6540 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6541 if NextMsg = Length(Msgs) then
6542 begin // scroll
6543 for i := 0 to High(Msgs)-1 do
6544 Msgs[i] := Msgs[i+1];
6545 end
6546 else
6547 Inc(NextMsg);
6548 end else
6549 if NextMsg = 0 then
6550 Inc(NextMsg);
6552 Msgs[NextMsg-1] := Text;
6553 CurValue := 0;
6554 MaxValue := Max;
6555 ShowCount := 0;
6556 end;
6558 g_ActiveWindow := nil;
6560 ProcessLoading;
6561 end;
6563 procedure g_Game_StepLoading();
6564 begin
6565 with LoadingStat do
6566 begin
6567 Inc(CurValue);
6568 Inc(ShowCount);
6569 if (ShowCount > LOADING_SHOW_STEP) then
6570 begin
6571 ShowCount := 0;
6572 ProcessLoading;
6573 end;
6574 end;
6575 end;
6577 procedure g_Game_ClearLoading();
6578 var
6579 len: Word;
6580 begin
6581 with LoadingStat do
6582 begin
6583 CurValue := 0;
6584 MaxValue := 0;
6585 ShowCount := 0;
6586 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6587 if len < 1 then len := 1;
6588 SetLength(Msgs, len);
6589 for len := Low(Msgs) to High(Msgs) do
6590 Msgs[len] := '';
6591 NextMsg := 0;
6592 end;
6593 end;
6595 procedure Parse_Params(var pars: TParamStrValues);
6596 var
6597 i: Integer;
6598 s: String;
6599 begin
6600 SetLength(pars, 0);
6601 i := 1;
6602 while i <= ParamCount do
6603 begin
6604 s := ParamStr(i);
6605 if (s[1] = '-') and (Length(s) > 1) then
6606 begin
6607 if (s[2] = '-') and (Length(s) > 2) then
6608 begin // Îäèíî÷íûé ïàðàìåòð
6609 SetLength(pars, Length(pars) + 1);
6610 with pars[High(pars)] do
6611 begin
6612 Name := LowerCase(s);
6613 Value := '+';
6614 end;
6615 end
6616 else
6617 if (i < ParamCount) then
6618 begin // Ïàðàìåòð ñî çíà÷åíèåì
6619 Inc(i);
6620 SetLength(pars, Length(pars) + 1);
6621 with pars[High(pars)] do
6622 begin
6623 Name := LowerCase(s);
6624 Value := LowerCase(ParamStr(i));
6625 end;
6626 end;
6627 end;
6629 Inc(i);
6630 end;
6631 end;
6633 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6634 var
6635 i: Integer;
6636 begin
6637 Result := '';
6638 for i := 0 to High(pars) do
6639 if pars[i].Name = aName then
6640 begin
6641 Result := pars[i].Value;
6642 Break;
6643 end;
6644 end;
6646 procedure g_Game_Process_Params();
6647 var
6648 pars: TParamStrValues;
6649 map: String;
6650 GMode, n: Byte;
6651 LimT, LimS: Integer;
6652 Opt: LongWord;
6653 Lives: Integer;
6654 s: String;
6655 Port: Integer;
6656 ip: String;
6657 F: TextFile;
6658 begin
6659 Parse_Params(pars);
6661 // Debug mode:
6662 s := Find_Param_Value(pars, '--debug');
6663 if (s <> '') then
6664 begin
6665 g_Game_SetDebugMode();
6666 s := Find_Param_Value(pars, '--netdump');
6667 if (s <> '') then
6668 NetDump := True;
6669 end;
6671 // Connect when game loads
6672 ip := Find_Param_Value(pars, '-connect');
6674 if ip <> '' then
6675 begin
6676 s := Find_Param_Value(pars, '-port');
6677 if (s = '') or not TryStrToInt(s, Port) then
6678 Port := 25666;
6680 s := Find_Param_Value(pars, '-pw');
6682 g_Game_StartClient(ip, Port, s);
6683 Exit;
6684 end;
6686 // Start map when game loads:
6687 map := LowerCase(Find_Param_Value(pars, '-map'));
6688 if isWadPath(map) then
6689 begin
6690 // Game mode:
6691 s := Find_Param_Value(pars, '-gm');
6692 GMode := g_Game_TextToMode(s);
6693 if GMode = GM_NONE then GMode := GM_DM;
6694 if GMode = GM_SINGLE then GMode := GM_COOP;
6696 // Time limit:
6697 s := Find_Param_Value(pars, '-limt');
6698 if (s = '') or (not TryStrToInt(s, LimT)) then
6699 LimT := 0;
6700 if LimT < 0 then
6701 LimT := 0;
6703 // Goal limit:
6704 s := Find_Param_Value(pars, '-lims');
6705 if (s = '') or (not TryStrToInt(s, LimS)) then
6706 LimS := 0;
6707 if LimS < 0 then
6708 LimS := 0;
6710 // Lives limit:
6711 s := Find_Param_Value(pars, '-lives');
6712 if (s = '') or (not TryStrToInt(s, Lives)) then
6713 Lives := 0;
6714 if Lives < 0 then
6715 Lives := 0;
6717 // Options:
6718 s := Find_Param_Value(pars, '-opt');
6719 if (s = '') then
6720 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6721 else
6722 Opt := StrToIntDef(s, 0);
6723 if Opt = 0 then
6724 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6726 // Close after map:
6727 s := Find_Param_Value(pars, '--close');
6728 if (s <> '') then
6729 gMapOnce := True;
6731 // Delete test map after play:
6732 s := Find_Param_Value(pars, '--testdelete');
6733 if (s <> '') then
6734 begin
6735 gMapToDelete := MapsDir + map;
6736 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6737 Halt(1);
6738 end;
6740 // Delete temporary WAD after play:
6741 s := Find_Param_Value(pars, '--tempdelete');
6742 if (s <> '') then
6743 begin
6744 gMapToDelete := MapsDir + map;
6745 gTempDelete := True;
6746 end;
6748 // Number of players:
6749 s := Find_Param_Value(pars, '-pl');
6750 if (s = '') then
6751 n := 1
6752 else
6753 n := StrToIntDef(s, 1);
6755 // Start:
6756 s := Find_Param_Value(pars, '-port');
6757 if (s = '') or not TryStrToInt(s, Port) then
6758 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6759 else
6760 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6761 end;
6763 // Execute script when game loads:
6764 s := Find_Param_Value(pars, '-exec');
6765 if s <> '' then
6766 begin
6767 if Pos(':\', s) = 0 then
6768 s := GameDir + '/' + s;
6770 {$I-}
6771 AssignFile(F, s);
6772 Reset(F);
6773 if IOResult <> 0 then
6774 begin
6775 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6776 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6777 CloseFile(F);
6778 Exit;
6779 end;
6780 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6781 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6783 while not EOF(F) do
6784 begin
6785 ReadLn(F, s);
6786 if IOResult <> 0 then
6787 begin
6788 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6789 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6790 CloseFile(F);
6791 Exit;
6792 end;
6793 if Pos('#', s) <> 1 then // script comment
6794 g_Console_Process(s, True);
6795 end;
6797 CloseFile(F);
6798 {$I+}
6799 end;
6801 SetLength(pars, 0);
6802 end;
6804 end.