DEADSOFTWARE

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