DEADSOFTWARE

36b1016abb6720dab7e7006448c8ef2ad7e966c5
[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 Len: Word;
3643 begin
3644 g_Game_Free();
3646 State := 0;
3647 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3648 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3650 g_Game_ClearLoading();
3652 // Íàñòðîéêè èãðû:
3653 gGameSettings.GameType := GT_CLIENT;
3655 gCoopTotalMonstersKilled := 0;
3656 gCoopTotalSecretsFound := 0;
3657 gCoopTotalMonsters := 0;
3658 gCoopTotalSecrets := 0;
3659 gAimLine := False;
3660 gShowMap := False;
3662 g_Game_ExecuteEvent('ongamestart');
3664 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3665 g_Game_SetupScreenSize();
3667 NetState := NET_STATE_AUTH;
3669 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3670 // Ñòàðòóåì êëèåíò
3671 if not g_Net_Connect(Addr, Port) then
3672 begin
3673 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3674 NetState := NET_STATE_NONE;
3675 Exit;
3676 end;
3678 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3679 MC_SEND_Info(PW);
3680 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3682 OuterLoop := True;
3683 while OuterLoop do
3684 begin
3685 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3686 begin
3687 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3688 begin
3689 Ptr := NetEvent.packet^.data;
3690 e_Raw_Seek(0);
3692 Len := e_Raw_Read_Word(Ptr);
3693 MID := e_Raw_Read_Byte(Ptr);
3695 if (MID = NET_MSG_INFO) and (State = 0) then
3696 begin
3697 NetMyID := e_Raw_Read_Byte(Ptr);
3698 NetPlrUID1 := e_Raw_Read_Word(Ptr);
3700 WadName := e_Raw_Read_String(Ptr);
3701 Map := e_Raw_Read_String(Ptr);
3703 gWADHash := e_Raw_Read_MD5(Ptr);
3705 gGameSettings.GameMode := e_Raw_Read_Byte(Ptr);
3706 gSwitchGameMode := gGameSettings.GameMode;
3707 gGameSettings.GoalLimit := e_Raw_Read_Word(Ptr);
3708 gGameSettings.TimeLimit := e_Raw_Read_Word(Ptr);
3709 gGameSettings.MaxLives := e_Raw_Read_Byte(Ptr);
3710 gGameSettings.Options := e_Raw_Read_LongWord(Ptr);
3711 T := e_Raw_Read_LongWord(Ptr);
3713 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3714 if newResPath = '' then
3715 begin
3716 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3717 newResPath := g_Res_DownloadWAD(WadName);
3718 if newResPath = '' then
3719 begin
3720 g_FatalError(_lc[I_NET_ERR_HASH]);
3721 enet_packet_destroy(NetEvent.packet);
3722 NetState := NET_STATE_NONE;
3723 Exit;
3724 end;
3725 end;
3726 newResPath := ExtractRelativePath(MapsDir, newResPath);
3728 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3729 gPlayer1Settings.Color,
3730 gPlayer1Settings.Team, False));
3732 if gPlayer1 = nil then
3733 begin
3734 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3736 enet_packet_destroy(NetEvent.packet);
3737 NetState := NET_STATE_NONE;
3738 Exit;
3739 end;
3741 gPlayer1.Name := gPlayer1Settings.Name;
3742 gPlayer1.UID := NetPlrUID1;
3743 gPlayer1.Reset(True);
3745 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3746 begin
3747 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3749 enet_packet_destroy(NetEvent.packet);
3750 NetState := NET_STATE_NONE;
3751 Exit;
3752 end;
3754 gTime := T;
3756 State := 1;
3757 OuterLoop := False;
3758 enet_packet_destroy(NetEvent.packet);
3759 break;
3760 end
3761 else
3762 enet_packet_destroy(NetEvent.packet);
3763 end
3764 else
3765 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3766 begin
3767 State := 0;
3768 if (NetEvent.data <= NET_DISC_MAX) then
3769 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3770 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3771 OuterLoop := False;
3772 Break;
3773 end;
3774 end;
3776 ProcessLoading();
3778 e_PollInput();
3780 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3781 begin
3782 State := 0;
3783 break;
3784 end;
3785 end;
3787 if State <> 1 then
3788 begin
3789 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3790 NetState := NET_STATE_NONE;
3791 Exit;
3792 end;
3794 gLMSRespawn := LMS_RESPAWN_NONE;
3795 gLMSRespawnTime := 0;
3797 g_Player_Init();
3798 NetState := NET_STATE_GAME;
3799 MC_SEND_FullStateRequest();
3800 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3801 end;
3803 procedure g_Game_SaveOptions();
3804 begin
3805 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3806 end;
3808 procedure g_Game_ChangeMap(MapPath: String);
3809 var
3810 Force: Boolean;
3811 begin
3812 g_Game_ClearLoading();
3814 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3815 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3816 if gExitByTrigger then
3817 begin
3818 Force := False;
3819 gExitByTrigger := False;
3820 end;
3821 if not g_Game_StartMap(MapPath, Force) then
3822 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3823 end;
3825 procedure g_Game_Restart();
3826 var
3827 Map: string;
3828 begin
3829 if g_Game_IsClient then
3830 Exit;
3831 map := g_ExtractFileName(gMapInfo.Map);
3833 MessageTime := 0;
3834 gGameOn := False;
3835 g_Game_ClearLoading();
3836 g_Game_StartMap(Map, True);
3837 end;
3839 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
3840 var
3841 NewWAD, ResName: String;
3842 I: Integer;
3843 begin
3844 g_Map_Free();
3845 g_Player_RemoveAllCorpses();
3847 if (not g_Game_IsClient) and
3848 (gSwitchGameMode <> gGameSettings.GameMode) and
3849 (gGameSettings.GameMode <> GM_SINGLE) then
3850 begin
3851 if gSwitchGameMode = GM_CTF then
3852 gGameSettings.MaxLives := 0;
3853 gGameSettings.GameMode := gSwitchGameMode;
3854 Force := True;
3855 end else
3856 gSwitchGameMode := gGameSettings.GameMode;
3858 g_Player_ResetTeams();
3860 if Pos(':\', Map) > 0 then
3861 begin
3862 NewWAD := g_ExtractWadName(Map);
3863 ResName := g_ExtractFileName(Map);
3864 if g_Game_IsServer then
3865 begin
3866 gWADHash := MD5File(MapsDir + NewWAD);
3867 g_Game_LoadWAD(NewWAD);
3868 end else
3869 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
3870 g_Game_ClientWAD(NewWAD, gWADHash);
3871 end else
3872 ResName := Map;
3874 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
3875 if Result then
3876 begin
3877 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
3879 gState := STATE_NONE;
3880 g_ActiveWindow := nil;
3881 gGameOn := True;
3883 DisableCheats();
3884 ResetTimer();
3886 if gGameSettings.GameMode = GM_CTF then
3887 begin
3888 g_Map_ResetFlag(FLAG_RED);
3889 g_Map_ResetFlag(FLAG_BLUE);
3890 // CTF, à ôëàãîâ íåò:
3891 if not g_Map_HaveFlagPoints() then
3892 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
3893 end;
3894 end
3895 else
3896 begin
3897 gState := STATE_MENU;
3898 gGameOn := False;
3899 end;
3901 gExit := 0;
3902 gPause := False;
3903 gTime := 0;
3904 NetTimeToUpdate := 1;
3905 NetTimeToReliable := 0;
3906 NetTimeToMaster := NetMasterRate;
3907 gLMSRespawn := LMS_RESPAWN_NONE;
3908 gLMSRespawnTime := 0;
3909 gMissionFailed := False;
3910 gNextMap := '';
3912 gCoopMonstersKilled := 0;
3913 gCoopSecretsFound := 0;
3915 gVoteInProgress := False;
3916 gVotePassed := False;
3917 gVoteCount := 0;
3918 gVoted := False;
3920 gStatsOff := False;
3922 if not gGameOn then Exit;
3924 g_Game_SpectateCenterView();
3926 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
3927 begin
3928 gLMSRespawn := LMS_RESPAWN_WARMUP;
3929 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
3930 gLMSSoftSpawn := True;
3931 if NetMode = NET_SERVER then
3932 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
3933 else
3934 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
3935 end;
3937 if NetMode = NET_SERVER then
3938 begin
3939 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
3941 // Ìàñòåðñåðâåð
3942 if NetUseMaster then
3943 begin
3944 if (NetMHost = nil) or (NetMPeer = nil) then
3945 if not g_Net_Slist_Connect then
3946 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
3948 g_Net_Slist_Update;
3949 end;
3951 if NetClients <> nil then
3952 for I := 0 to High(NetClients) do
3953 if NetClients[I].Used then
3954 begin
3955 NetClients[I].Voted := False;
3956 if NetClients[I].RequestedFullUpdate then
3957 begin
3958 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
3959 NetClients[I].RequestedFullUpdate := False;
3960 end;
3961 end;
3963 g_Net_UnbanNonPermHosts();
3964 end;
3966 if gLastMap then
3967 begin
3968 gCoopTotalMonstersKilled := 0;
3969 gCoopTotalSecretsFound := 0;
3970 gCoopTotalMonsters := 0;
3971 gCoopTotalSecrets := 0;
3972 gLastMap := False;
3973 end;
3975 g_Game_ExecuteEvent('onmapstart');
3976 end;
3978 procedure SetFirstLevel();
3979 begin
3980 gNextMap := '';
3982 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
3983 if MapList = nil then
3984 Exit;
3986 SortSArray(MapList);
3987 gNextMap := MapList[Low(MapList)];
3989 MapList := nil;
3990 end;
3992 procedure g_Game_ExitLevel(Map: Char16);
3993 begin
3994 gNextMap := Map;
3996 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
3997 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
3998 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
3999 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4001 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4002 if gGameSettings.GameType = GT_SINGLE then
4003 gExit := EXIT_ENDLEVELSINGLE
4004 else // Âûøëè â âûõîä â Ñâîåé èãðå
4005 begin
4006 gExit := EXIT_ENDLEVELCUSTOM;
4007 if gGameSettings.GameMode = GM_COOP then
4008 g_Player_RememberAll;
4010 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4011 begin
4012 gLastMap := True;
4013 if gGameSettings.GameMode = GM_COOP then
4014 gStatsOff := True;
4016 gStatsPressed := True;
4017 gNextMap := 'MAP01';
4019 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4020 g_Game_NextLevel;
4022 if g_Game_IsNet then
4023 begin
4024 MH_SEND_GameStats();
4025 MH_SEND_CoopStats();
4026 end;
4027 end;
4028 end;
4029 end;
4031 procedure g_Game_RestartLevel();
4032 var
4033 Map: string;
4034 begin
4035 if gGameSettings.GameMode = GM_SINGLE then
4036 begin
4037 g_Game_Restart();
4038 Exit;
4039 end;
4040 gExit := EXIT_ENDLEVELCUSTOM;
4041 Map := g_ExtractFileName(gMapInfo.Map);
4042 gNextMap := Map;
4043 end;
4045 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4046 var
4047 gWAD: String;
4048 begin
4049 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4050 Exit;
4051 if not g_Game_IsClient then
4052 Exit;
4053 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4054 if gWAD = '' then
4055 begin
4056 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4057 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4058 if gWAD = '' then
4059 begin
4060 g_Game_Free();
4061 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4062 Exit;
4063 end;
4064 end;
4065 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4066 g_Game_LoadWAD(NewWAD);
4067 end;
4069 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4070 var
4071 i, n, nb, nr: Integer;
4072 begin
4073 if not g_Game_IsServer then Exit;
4074 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4075 gLMSRespawn := LMS_RESPAWN_NONE;
4076 gLMSRespawnTime := 0;
4077 MessageTime := 0;
4079 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4080 begin
4081 gMissionFailed := True;
4082 g_Game_RestartLevel;
4083 Exit;
4084 end;
4086 n := 0; nb := 0; nr := 0;
4087 for i := Low(gPlayers) to High(gPlayers) do
4088 if (gPlayers[i] <> nil) and
4089 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4090 (gPlayers[i] is TBot)) then
4091 begin
4092 Inc(n);
4093 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4094 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4095 end;
4097 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4098 begin
4099 // wait a second until the fuckers finally decide to join
4100 gLMSRespawn := LMS_RESPAWN_WARMUP;
4101 gLMSRespawnTime := gTime + 1000;
4102 gLMSSoftSpawn := NoMapRestart;
4103 Exit;
4104 end;
4106 g_Player_RemoveAllCorpses;
4107 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4108 if g_Game_IsNet then
4109 MH_SEND_GameEvent(NET_EV_LMS_START);
4111 for i := Low(gPlayers) to High(gPlayers) do
4112 begin
4113 if gPlayers[i] = nil then continue;
4114 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4115 // don't touch normal spectators
4116 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4117 begin
4118 gPlayers[i].FNoRespawn := True;
4119 gPlayers[i].Lives := 0;
4120 if g_Game_IsNet then
4121 MH_SEND_PlayerStats(gPlayers[I].UID);
4122 continue;
4123 end;
4124 gPlayers[i].FNoRespawn := False;
4125 gPlayers[i].Lives := gGameSettings.MaxLives;
4126 gPlayers[i].Respawn(False, True);
4127 if gGameSettings.GameMode = GM_COOP then
4128 begin
4129 gPlayers[i].Frags := 0;
4130 gPlayers[i].RecallState;
4131 end;
4132 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4133 gPlayer1 := g_Player_Get(gLMSPID1);
4134 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4135 gPlayer2 := g_Player_Get(gLMSPID2);
4136 end;
4138 for i := Low(gItems) to High(gItems) do
4139 begin
4140 if gItems[i].Respawnable then
4141 begin
4142 gItems[i].QuietRespawn := True;
4143 gItems[i].RespawnTime := 0;
4144 end
4145 else
4146 begin
4147 g_Items_Remove(i);
4148 if g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
4149 end;
4150 end;
4152 for i := Low(gMonsters) to High(gMonsters) do
4153 begin
4154 if (gMonsters[i] <> nil) and not gMonsters[i].FNoRespawn then
4155 gMonsters[i].Respawn;
4156 end;
4158 gLMSSoftSpawn := False;
4159 end;
4161 function g_Game_GetFirstMap(WAD: String): String;
4162 begin
4163 Result := '';
4165 MapList := g_Map_GetMapsList(WAD);
4166 if MapList = nil then
4167 Exit;
4169 SortSArray(MapList);
4170 Result := MapList[Low(MapList)];
4172 if not g_Map_Exist(WAD + ':\' + Result) then
4173 Result := '';
4175 MapList := nil;
4176 end;
4178 function g_Game_GetNextMap(): String;
4179 var
4180 I: Integer;
4181 Map: string;
4182 begin
4183 Result := '';
4185 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4186 if MapList = nil then
4187 Exit;
4189 Map := g_ExtractFileName(gMapInfo.Map);
4191 SortSArray(MapList);
4192 MapIndex := -255;
4193 for I := Low(MapList) to High(MapList) do
4194 if Map = MapList[I] then
4195 begin
4196 MapIndex := I;
4197 Break;
4198 end;
4200 if MapIndex <> -255 then
4201 begin
4202 if MapIndex = High(MapList) then
4203 Result := MapList[Low(MapList)]
4204 else
4205 Result := MapList[MapIndex + 1];
4207 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4208 end;
4210 MapList := nil;
4211 end;
4213 procedure g_Game_NextLevel();
4214 begin
4215 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4216 gExit := EXIT_ENDLEVELCUSTOM
4217 else
4218 begin
4219 gExit := EXIT_ENDLEVELSINGLE;
4220 Exit;
4221 end;
4223 if gNextMap <> '' then Exit;
4224 gNextMap := g_Game_GetNextMap();
4225 end;
4227 function g_Game_IsTestMap(): Boolean;
4228 begin
4229 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4230 end;
4232 procedure g_Game_DeleteTestMap();
4233 var
4234 a: Integer;
4235 MapName: Char16;
4236 WadName: string;
4238 WAD: TWADFile;
4239 MapList: SArray;
4240 time: Integer;
4242 begin
4243 a := Pos('.wad:\', gMapToDelete);
4244 if a = 0 then
4245 Exit;
4247 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4248 WadName := Copy(gMapToDelete, 1, a + 3);
4249 Delete(gMapToDelete, 1, a + 5);
4250 gMapToDelete := UpperCase(gMapToDelete);
4251 MapName := '';
4252 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4255 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4256 if MapName <> TEST_MAP_NAME then
4257 Exit;
4259 if not gTempDelete then
4260 begin
4261 time := g_GetFileTime(WadName);
4262 WAD := TWADFile.Create();
4264 // ×èòàåì Wad-ôàéë:
4265 if not WAD.ReadFile(WadName) then
4266 begin // Íåò òàêîãî WAD-ôàéëà
4267 WAD.Free();
4268 Exit;
4269 end;
4271 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4272 WAD.CreateImage();
4273 MapList := WAD.GetResourcesList('');
4275 if MapList <> nil then
4276 for a := 0 to High(MapList) do
4277 if MapList[a] = MapName then
4278 begin
4279 // Óäàëÿåì è ñîõðàíÿåì:
4280 WAD.RemoveResource('', MapName);
4281 WAD.SaveTo(WadName);
4282 Break;
4283 end;
4285 WAD.Free();
4286 g_SetFileTime(WadName, time);
4287 end else
4289 if gTempDelete then DeleteFile(WadName);
4290 end;
4292 procedure GameCVars(P: SArray);
4293 var
4294 a, b: Integer;
4295 stat: TPlayerStatArray;
4296 cmd, s: string;
4297 config: TConfig;
4298 begin
4299 stat := nil;
4300 cmd := LowerCase(P[0]);
4301 if cmd = 'r_showfps' then
4302 begin
4303 if (Length(P) > 1) and
4304 ((P[1] = '1') or (P[1] = '0')) then
4305 gShowFPS := (P[1][1] = '1');
4307 if gShowFPS then
4308 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4309 else
4310 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4311 end
4312 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4313 begin
4314 with gGameSettings do
4315 begin
4316 if (Length(P) > 1) and
4317 ((P[1] = '1') or (P[1] = '0')) then
4318 begin
4319 if (P[1][1] = '1') then
4320 Options := Options or GAME_OPTION_TEAMDAMAGE
4321 else
4322 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4323 end;
4325 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4326 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4327 else
4328 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4330 if g_Game_IsNet then MH_SEND_GameSettings;
4331 end;
4332 end
4333 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4334 begin
4335 with gGameSettings do
4336 begin
4337 if (Length(P) > 1) and
4338 ((P[1] = '1') or (P[1] = '0')) then
4339 begin
4340 if (P[1][1] = '1') then
4341 Options := Options or GAME_OPTION_WEAPONSTAY
4342 else
4343 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4344 end;
4346 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4347 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4348 else
4349 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4351 if g_Game_IsNet then MH_SEND_GameSettings;
4352 end;
4353 end
4354 else if cmd = 'g_gamemode' then
4355 begin
4356 a := g_Game_TextToMode(P[1]);
4357 if a = GM_SINGLE then a := GM_COOP;
4358 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4359 begin
4360 gSwitchGameMode := a;
4361 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4362 (gState = STATE_INTERSINGLE) then
4363 gSwitchGameMode := GM_SINGLE;
4364 if not gGameOn then
4365 gGameSettings.GameMode := gSwitchGameMode;
4366 end;
4367 if gSwitchGameMode = gGameSettings.GameMode then
4368 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4369 [g_Game_ModeToText(gGameSettings.GameMode)]))
4370 else
4371 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4372 [g_Game_ModeToText(gGameSettings.GameMode),
4373 g_Game_ModeToText(gSwitchGameMode)]));
4374 end
4375 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4376 begin
4377 with gGameSettings do
4378 begin
4379 if (Length(P) > 1) and
4380 ((P[1] = '1') or (P[1] = '0')) then
4381 begin
4382 if (P[1][1] = '1') then
4383 Options := Options or GAME_OPTION_ALLOWEXIT
4384 else
4385 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4386 end;
4388 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4389 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4390 else
4391 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4392 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4394 if g_Game_IsNet then MH_SEND_GameSettings;
4395 end;
4396 end
4397 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4398 begin
4399 with gGameSettings do
4400 begin
4401 if (Length(P) > 1) and
4402 ((P[1] = '1') or (P[1] = '0')) then
4403 begin
4404 if (P[1][1] = '1') then
4405 Options := Options or GAME_OPTION_MONSTERS
4406 else
4407 Options := Options and (not GAME_OPTION_MONSTERS);
4408 end;
4410 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4411 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4412 else
4413 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4414 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4416 if g_Game_IsNet then MH_SEND_GameSettings;
4417 end;
4418 end
4419 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4420 begin
4421 with gGameSettings do
4422 begin
4423 if (Length(P) > 1) and
4424 ((P[1] = '1') or (P[1] = '0')) then
4425 begin
4426 if (P[1][1] = '1') then
4427 Options := Options or GAME_OPTION_BOTVSPLAYER
4428 else
4429 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4430 end;
4432 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4433 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4434 else
4435 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4437 if g_Game_IsNet then MH_SEND_GameSettings;
4438 end;
4439 end
4440 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4441 begin
4442 with gGameSettings do
4443 begin
4444 if (Length(P) > 1) and
4445 ((P[1] = '1') or (P[1] = '0')) then
4446 begin
4447 if (P[1][1] = '1') then
4448 Options := Options or GAME_OPTION_BOTVSMONSTER
4449 else
4450 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4451 end;
4453 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4454 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4455 else
4456 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4458 if g_Game_IsNet then MH_SEND_GameSettings;
4459 end;
4460 end
4461 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4462 begin
4463 if Length(P) > 1 then
4464 begin
4465 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4466 gGameSettings.WarmupTime := 30
4467 else
4468 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4469 end;
4471 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4472 [gGameSettings.WarmupTime]));
4473 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4474 end
4475 else if cmd = 'net_interp' then
4476 begin
4477 if (Length(P) > 1) then
4478 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4480 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4481 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4482 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4483 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4484 config.Free();
4485 end
4486 else if cmd = 'net_forceplayerupdate' then
4487 begin
4488 if (Length(P) > 1) and
4489 ((P[1] = '1') or (P[1] = '0')) then
4490 NetForcePlayerUpdate := (P[1][1] = '1');
4492 if NetForcePlayerUpdate then
4493 g_Console_Add('net_forceplayerupdate = 1')
4494 else
4495 g_Console_Add('net_forceplayerupdate = 0');
4496 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4497 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4498 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4499 config.Free();
4500 end
4501 else if cmd = 'net_predictself' then
4502 begin
4503 if (Length(P) > 1) and
4504 ((P[1] = '1') or (P[1] = '0')) then
4505 NetPredictSelf := (P[1][1] = '1');
4507 if NetPredictSelf then
4508 g_Console_Add('net_predictself = 1')
4509 else
4510 g_Console_Add('net_predictself = 0');
4511 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4512 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4513 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4514 config.Free();
4515 end
4516 else if cmd = 'sv_name' then
4517 begin
4518 if (Length(P) > 1) and (Length(P[1]) > 0) then
4519 begin
4520 NetServerName := P[1];
4521 if Length(NetServerName) > 64 then
4522 SetLength(NetServerName, 64);
4523 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4524 g_Net_Slist_Update;
4525 end;
4527 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4528 end
4529 else if cmd = 'sv_passwd' then
4530 begin
4531 if (Length(P) > 1) and (Length(P[1]) > 0) then
4532 begin
4533 NetPassword := P[1];
4534 if Length(NetPassword) > 24 then
4535 SetLength(NetPassword, 24);
4536 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4537 g_Net_Slist_Update;
4538 end;
4540 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4541 end
4542 else if cmd = 'sv_maxplrs' then
4543 begin
4544 if (Length(P) > 1) then
4545 begin
4546 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4547 if g_Game_IsServer and g_Game_IsNet then
4548 begin
4549 b := 0;
4550 for a := 0 to High(NetClients) do
4551 if NetClients[a].Used then
4552 begin
4553 Inc(b);
4554 if b > NetMaxClients then
4555 begin
4556 s := g_Player_Get(NetClients[a].Player).Name;
4557 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4558 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4559 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4560 end;
4561 end;
4562 if NetUseMaster then
4563 g_Net_Slist_Update;
4564 end;
4565 end;
4567 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4568 end
4569 else if cmd = 'sv_public' then
4570 begin
4571 if (Length(P) > 1) then
4572 begin
4573 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4574 if g_Game_IsServer and g_Game_IsNet then
4575 if NetUseMaster then
4576 begin
4577 if NetMPeer = nil then
4578 if not g_Net_Slist_Connect() then
4579 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4580 g_Net_Slist_Update();
4581 end
4582 else
4583 if NetMPeer <> nil then
4584 g_Net_Slist_Disconnect();
4585 end;
4587 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4588 end
4589 else if cmd = 'sv_intertime' then
4590 begin
4591 if (Length(P) > 1) then
4592 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4594 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4595 end
4596 else if cmd = 'p1_name' then
4597 begin
4598 if (Length(P) > 1) and gGameOn then
4599 begin
4600 if g_Game_IsClient then
4601 begin
4602 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4603 MC_SEND_PlayerSettings;
4604 end
4605 else
4606 if gPlayer1 <> nil then
4607 begin
4608 gPlayer1.Name := b_Text_Unformat(P[1]);
4609 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4610 end
4611 else
4612 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4613 end;
4614 end
4615 else if cmd = 'p2_name' then
4616 begin
4617 if (Length(P) > 1) and gGameOn then
4618 begin
4619 if g_Game_IsClient then
4620 begin
4621 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4622 MC_SEND_PlayerSettings;
4623 end
4624 else
4625 if gPlayer2 <> nil then
4626 begin
4627 gPlayer2.Name := b_Text_Unformat(P[1]);
4628 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4629 end
4630 else
4631 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4632 end;
4633 end
4634 else if cmd = 'p1_color' then
4635 begin
4636 if Length(P) > 3 then
4637 if g_Game_IsClient then
4638 begin
4639 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4640 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4641 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4642 MC_SEND_PlayerSettings;
4643 end
4644 else
4645 if gPlayer1 <> nil then
4646 begin
4647 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4648 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4649 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4650 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4651 end
4652 else
4653 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4654 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4655 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4656 end
4657 else if (cmd = 'p2_color') and not g_Game_IsNet then
4658 begin
4659 if Length(P) > 3 then
4660 if g_Game_IsClient then
4661 begin
4662 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4663 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4664 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4665 MC_SEND_PlayerSettings;
4666 end
4667 else
4668 if gPlayer2 <> nil then
4669 begin
4670 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4671 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4672 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4673 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4674 end
4675 else
4676 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4677 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4678 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4679 end
4680 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4681 begin
4682 if cmd = 'r_showtime' then
4683 begin
4684 if (Length(P) > 1) and
4685 ((P[1] = '1') or (P[1] = '0')) then
4686 gShowTime := (P[1][1] = '1');
4688 if gShowTime then
4689 g_Console_Add(_lc[I_MSG_TIME_ON])
4690 else
4691 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4692 end
4693 else if cmd = 'r_showscore' then
4694 begin
4695 if (Length(P) > 1) and
4696 ((P[1] = '1') or (P[1] = '0')) then
4697 gShowGoals := (P[1][1] = '1');
4699 if gShowGoals then
4700 g_Console_Add(_lc[I_MSG_SCORE_ON])
4701 else
4702 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4703 end
4704 else if cmd = 'r_showstat' then
4705 begin
4706 if (Length(P) > 1) and
4707 ((P[1] = '1') or (P[1] = '0')) then
4708 gShowStat := (P[1][1] = '1');
4710 if gShowStat then
4711 g_Console_Add(_lc[I_MSG_STATS_ON])
4712 else
4713 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4714 end
4715 else if cmd = 'r_showkillmsg' then
4716 begin
4717 if (Length(P) > 1) and
4718 ((P[1] = '1') or (P[1] = '0')) then
4719 gShowKillMsg := (P[1][1] = '1');
4721 if gShowKillMsg then
4722 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4723 else
4724 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4725 end
4726 else if cmd = 'r_showlives' then
4727 begin
4728 if (Length(P) > 1) and
4729 ((P[1] = '1') or (P[1] = '0')) then
4730 gShowLives := (P[1][1] = '1');
4732 if gShowLives then
4733 g_Console_Add(_lc[I_MSG_LIVES_ON])
4734 else
4735 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4736 end
4737 else if cmd = 'r_showspect' then
4738 begin
4739 if (Length(P) > 1) and
4740 ((P[1] = '1') or (P[1] = '0')) then
4741 gSpectHUD := (P[1][1] = '1');
4743 if gSpectHUD then
4744 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4745 else
4746 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4747 end
4748 else if cmd = 'r_showping' then
4749 begin
4750 if (Length(P) > 1) and
4751 ((P[1] = '1') or (P[1] = '0')) then
4752 gShowPing := (P[1][1] = '1');
4754 if gShowPing then
4755 g_Console_Add(_lc[I_MSG_PING_ON])
4756 else
4757 g_Console_Add(_lc[I_MSG_PING_OFF]);
4758 end
4759 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4760 begin
4761 if Length(P) > 1 then
4762 begin
4763 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4764 gGameSettings.GoalLimit := 0
4765 else
4766 begin
4767 b := 0;
4769 if gGameSettings.GameMode = GM_DM then
4770 begin // DM
4771 stat := g_Player_GetStats();
4772 if stat <> nil then
4773 for a := 0 to High(stat) do
4774 if stat[a].Frags > b then
4775 b := stat[a].Frags;
4776 end
4777 else // TDM/CTF
4778 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4780 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4781 end;
4783 if g_Game_IsNet then MH_SEND_GameSettings;
4784 end;
4786 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4787 end
4788 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4789 begin
4790 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4791 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4793 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4794 [gGameSettings.TimeLimit div 3600,
4795 (gGameSettings.TimeLimit div 60) mod 60,
4796 gGameSettings.TimeLimit mod 60]));
4797 if g_Game_IsNet then MH_SEND_GameSettings;
4798 end
4799 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4800 begin
4801 if Length(P) > 1 then
4802 begin
4803 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4804 gGameSettings.MaxLives := 0
4805 else
4806 begin
4807 b := 0;
4808 stat := g_Player_GetStats();
4809 if stat <> nil then
4810 for a := 0 to High(stat) do
4811 if stat[a].Lives > b then
4812 b := stat[a].Lives;
4813 gGameSettings.MaxLives :=
4814 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4815 end;
4816 end;
4818 g_Console_Add(Format(_lc[I_MSG_LIVES],
4819 [gGameSettings.MaxLives]));
4820 if g_Game_IsNet then MH_SEND_GameSettings;
4821 end;
4822 end;
4823 end;
4825 procedure DebugCommands(P: SArray);
4826 var
4827 a, b: Integer;
4828 cmd: string;
4829 //pt: TPoint;
4830 begin
4831 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
4832 if gDebugMode then
4833 begin
4834 cmd := LowerCase(P[0]);
4835 if cmd = 'd_window' then
4836 begin
4837 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
4838 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
4839 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
4840 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
4841 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
4842 end
4843 else if cmd = 'd_sounds' then
4844 begin
4845 if (Length(P) > 1) and
4846 ((P[1] = '1') or (P[1] = '0')) then
4847 g_Debug_Sounds := (P[1][1] = '1');
4849 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
4850 end
4851 else if cmd = 'd_frames' then
4852 begin
4853 if (Length(P) > 1) and
4854 ((P[1] = '1') or (P[1] = '0')) then
4855 g_Debug_Frames := (P[1][1] = '1');
4857 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
4858 end
4859 else if cmd = 'd_winmsg' then
4860 begin
4861 if (Length(P) > 1) and
4862 ((P[1] = '1') or (P[1] = '0')) then
4863 g_Debug_WinMsgs := (P[1][1] = '1');
4865 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
4866 end
4867 else if (cmd = 'd_monoff') and not g_Game_IsNet then
4868 begin
4869 if (Length(P) > 1) and
4870 ((P[1] = '1') or (P[1] = '0')) then
4871 g_Debug_MonsterOff := (P[1][1] = '1');
4873 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
4874 end
4875 else if (cmd = 'd_botoff') and not g_Game_IsNet then
4876 begin
4877 if Length(P) > 1 then
4878 case P[1][1] of
4879 '0': g_debug_BotAIOff := 0;
4880 '1': g_debug_BotAIOff := 1;
4881 '2': g_debug_BotAIOff := 2;
4882 '3': g_debug_BotAIOff := 3;
4883 end;
4885 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
4886 end
4887 else if cmd = 'd_monster' then
4888 begin
4889 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
4890 if Length(P) < 2 then
4891 begin
4892 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
4893 g_Console_Add('ID | Name');
4894 for b := MONSTER_DEMON to MONSTER_MAN do
4895 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
4896 end else
4897 begin
4898 a := StrToIntDef(P[1], 0);
4899 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4900 a := g_Monsters_GetIDByName(P[1]);
4902 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4903 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
4904 else
4905 begin
4906 with gPlayer1.Obj do
4907 b := g_Monsters_Create(a,
4908 X + Rect.X + (Rect.Width div 2),
4909 Y + Rect.Y + Rect.Height,
4910 gPlayer1.Direction, True);
4911 if (Length(P) > 2) and (b >= 0) then
4912 gMonsters[b].MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
4913 end;
4914 end;
4915 end
4916 else if (cmd = 'd_health') then
4917 begin
4918 if (Length(P) > 1) and
4919 ((P[1] = '1') or (P[1] = '0')) then
4920 g_debug_HealthBar := (P[1][1] = '1');
4922 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
4923 end
4924 else if (cmd = 'd_player') then
4925 begin
4926 if (Length(P) > 1) and
4927 ((P[1] = '1') or (P[1] = '0')) then
4928 g_debug_Player := (P[1][1] = '1');
4930 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
4931 end
4932 else if (cmd = 'd_joy') then
4933 begin
4934 for a := 1 to 8 do
4935 g_Console_Add(e_JoystickStateToString(a));
4936 end;
4937 end
4938 else
4939 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
4940 end;
4943 procedure GameCheats(P: SArray);
4944 var
4945 cmd: string;
4946 f, a: Integer;
4947 plr: TPlayer;
4948 begin
4949 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
4950 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
4951 begin
4952 g_Console_Add('not available');
4953 exit;
4954 end;
4955 plr := gPlayer1;
4956 if plr = nil then
4957 begin
4958 g_Console_Add('where is the player?!');
4959 exit;
4960 end;
4961 cmd := LowerCase(P[0]);
4962 // god
4963 if cmd = 'god' then
4964 begin
4965 plr.GodMode := not plr.GodMode;
4966 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
4967 exit;
4968 end;
4969 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
4970 if cmd = 'give' then
4971 begin
4972 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
4973 for f := 1 to High(P) do
4974 begin
4975 cmd := LowerCase(P[f]);
4976 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
4977 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
4978 if cmd = 'exit' then
4979 begin
4980 if gTriggers <> nil then
4981 begin
4982 for a := 0 to High(gTriggers) do
4983 begin
4984 if gTriggers[a].TriggerType = TRIGGER_EXIT then
4985 begin
4986 g_Console_Add('player left the map');
4987 gExitByTrigger := True;
4988 g_Game_ExitLevel(gTriggers[a].Data.MapName);
4989 break;
4990 end;
4991 end;
4992 end;
4993 continue;
4994 end;
4996 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
4997 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
4998 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
4999 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5000 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5002 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5003 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5005 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5006 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;
5008 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5009 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5011 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5012 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5014 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5015 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5017 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5018 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5019 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5021 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5022 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5023 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5024 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;
5025 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5026 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5028 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;
5029 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;
5030 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;
5031 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;
5032 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;
5033 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;
5035 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5036 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;
5038 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;
5039 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;
5041 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5043 if cmd = 'ammo' then
5044 begin
5045 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5046 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5047 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5048 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5049 plr.GiveItem(ITEM_AMMO_FUELCAN);
5050 g_Console_Add('player got some ammo');
5051 continue;
5052 end;
5054 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5055 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5057 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5058 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5060 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5061 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5063 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5064 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5066 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5068 if cmd = 'weapons' then
5069 begin
5070 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5071 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5072 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5073 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5074 plr.GiveItem(ITEM_WEAPON_PLASMA);
5075 plr.GiveItem(ITEM_WEAPON_BFG);
5076 g_Console_Add('player got weapons');
5077 continue;
5078 end;
5080 if cmd = 'keys' then
5081 begin
5082 plr.GiveItem(ITEM_KEY_RED);
5083 plr.GiveItem(ITEM_KEY_GREEN);
5084 plr.GiveItem(ITEM_KEY_BLUE);
5085 g_Console_Add('player got all keys');
5086 continue;
5087 end;
5089 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5090 end;
5091 exit;
5092 end;
5093 // open
5094 if cmd = 'open' then
5095 begin
5096 g_Console_Add('player activated sesame');
5097 g_Triggers_OpenAll();
5098 exit;
5099 end;
5100 // fly
5101 if cmd = 'fly' then
5102 begin
5103 gFly := not gFly;
5104 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5105 exit;
5106 end;
5107 // noclip
5108 if cmd = 'noclip' then
5109 begin
5110 plr.SwitchNoClip;
5111 g_Console_Add('wall hardeness adjusted');
5112 exit;
5113 end;
5114 // notarget
5115 if cmd = 'notarget' then
5116 begin
5117 plr.NoTarget := not plr.NoTarget;
5118 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5119 exit;
5120 end;
5121 // noreload
5122 if cmd = 'noreload' then
5123 begin
5124 plr.NoReload := not plr.NoReload;
5125 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5126 exit;
5127 end;
5128 // speedy
5129 if cmd = 'speedy' then
5130 begin
5131 MAX_RUNVEL := 32-MAX_RUNVEL;
5132 g_Console_Add('speed adjusted');
5133 exit;
5134 end;
5135 // jumpy
5136 if cmd = 'jumpy' then
5137 begin
5138 VEL_JUMP := 30-VEL_JUMP;
5139 g_Console_Add('jump height adjusted');
5140 exit;
5141 end;
5142 // automap
5143 if cmd = 'automap' then
5144 begin
5145 gShowMap := not gShowMap;
5146 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5147 exit;
5148 end;
5149 // aimline
5150 if cmd = 'aimline' then
5151 begin
5152 gAimLine := not gAimLine;
5153 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5154 exit;
5155 end;
5156 end;
5158 procedure GameCommands(P: SArray);
5159 var
5160 a, b: Integer;
5161 s, pw: String;
5162 chstr: string;
5163 cmd: string;
5164 pl: pTNetClient = nil;
5165 plr: TPlayer;
5166 prt: Word;
5167 nm: Boolean;
5168 listen: LongWord;
5169 begin
5170 // Îáùèå êîìàíäû:
5171 cmd := LowerCase(P[0]);
5172 chstr := '';
5173 if (cmd = 'quit') or
5174 (cmd = 'exit') then
5175 begin
5176 g_Game_Free();
5177 g_Game_Quit();
5178 Exit;
5179 end
5180 else if cmd = 'pause' then
5181 begin
5182 if (g_ActiveWindow = nil) then
5183 g_Game_Pause(not gPause);
5184 end
5185 else if cmd = 'endgame' then
5186 gExit := EXIT_SIMPLE
5187 else if cmd = 'restart' then
5188 begin
5189 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5190 begin
5191 if g_Game_IsClient then
5192 begin
5193 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5194 Exit;
5195 end;
5196 g_Game_Restart();
5197 end else
5198 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5199 end
5200 else if cmd = 'kick' then
5201 begin
5202 if g_Game_IsServer then
5203 begin
5204 if Length(P) < 2 then
5205 begin
5206 g_Console_Add('kick <name>');
5207 Exit;
5208 end;
5209 if P[1] = '' then
5210 begin
5211 g_Console_Add('kick <name>');
5212 Exit;
5213 end;
5215 if g_Game_IsNet then
5216 pl := g_Net_Client_ByName(P[1]);
5217 if (pl <> nil) then
5218 begin
5219 s := g_Net_ClientName_ByID(pl^.ID);
5220 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5221 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5222 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5223 if NetUseMaster then
5224 g_Net_Slist_Update;
5225 end else if gPlayers <> nil then
5226 for a := Low(gPlayers) to High(gPlayers) do
5227 if gPlayers[a] <> nil then
5228 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5229 begin
5230 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5231 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5232 continue;
5233 gPlayers[a].Lives := 0;
5234 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5235 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5236 g_Player_Remove(gPlayers[a].UID);
5237 if NetUseMaster then
5238 g_Net_Slist_Update;
5239 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5240 g_Bot_MixNames();
5241 end;
5242 end else
5243 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5244 end
5245 else if cmd = 'kick_id' then
5246 begin
5247 if g_Game_IsServer and g_Game_IsNet then
5248 begin
5249 if Length(P) < 2 then
5250 begin
5251 g_Console_Add('kick_id <client ID>');
5252 Exit;
5253 end;
5254 if P[1] = '' then
5255 begin
5256 g_Console_Add('kick_id <client ID>');
5257 Exit;
5258 end;
5260 a := StrToIntDef(P[1], 0);
5261 if (NetClients <> nil) and (a <= High(NetClients)) then
5262 begin
5263 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5264 begin
5265 s := g_Net_ClientName_ByID(NetClients[a].ID);
5266 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5267 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5268 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5269 if NetUseMaster then
5270 g_Net_Slist_Update;
5271 end;
5272 end;
5273 end else
5274 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5275 end
5276 else if cmd = 'ban' then
5277 begin
5278 if g_Game_IsServer and g_Game_IsNet then
5279 begin
5280 if Length(P) < 2 then
5281 begin
5282 g_Console_Add('ban <name>');
5283 Exit;
5284 end;
5285 if P[1] = '' then
5286 begin
5287 g_Console_Add('ban <name>');
5288 Exit;
5289 end;
5291 pl := g_Net_Client_ByName(P[1]);
5292 if (pl <> nil) then
5293 begin
5294 s := g_Net_ClientName_ByID(pl^.ID);
5295 g_Net_BanHost(pl^.Peer^.address.host, False);
5296 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5297 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5298 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5299 if NetUseMaster then
5300 g_Net_Slist_Update;
5301 end else
5302 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5303 end else
5304 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5305 end
5306 else if cmd = 'ban_id' then
5307 begin
5308 if g_Game_IsServer and g_Game_IsNet then
5309 begin
5310 if Length(P) < 2 then
5311 begin
5312 g_Console_Add('ban_id <client ID>');
5313 Exit;
5314 end;
5315 if P[1] = '' then
5316 begin
5317 g_Console_Add('ban_id <client ID>');
5318 Exit;
5319 end;
5321 a := StrToIntDef(P[1], 0);
5322 if (NetClients <> nil) and (a <= High(NetClients)) then
5323 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5324 begin
5325 s := g_Net_ClientName_ByID(NetClients[a].ID);
5326 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5327 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5328 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5329 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5330 if NetUseMaster then
5331 g_Net_Slist_Update;
5332 end;
5333 end else
5334 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5335 end
5336 else if cmd = 'permban' then
5337 begin
5338 if g_Game_IsServer and g_Game_IsNet then
5339 begin
5340 if Length(P) < 2 then
5341 begin
5342 g_Console_Add('permban <name>');
5343 Exit;
5344 end;
5345 if P[1] = '' then
5346 begin
5347 g_Console_Add('permban <name>');
5348 Exit;
5349 end;
5351 pl := g_Net_Client_ByName(P[1]);
5352 if (pl <> nil) then
5353 begin
5354 s := g_Net_ClientName_ByID(pl^.ID);
5355 g_Net_BanHost(pl^.Peer^.address.host);
5356 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5357 g_Net_SaveBanList();
5358 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5359 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5360 if NetUseMaster then
5361 g_Net_Slist_Update;
5362 end else
5363 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5364 end else
5365 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5366 end
5367 else if cmd = 'permban_id' then
5368 begin
5369 if g_Game_IsServer and g_Game_IsNet then
5370 begin
5371 if Length(P) < 2 then
5372 begin
5373 g_Console_Add('permban_id <client ID>');
5374 Exit;
5375 end;
5376 if P[1] = '' then
5377 begin
5378 g_Console_Add('permban_id <client ID>');
5379 Exit;
5380 end;
5382 a := StrToIntDef(P[1], 0);
5383 if (NetClients <> nil) and (a <= High(NetClients)) then
5384 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5385 begin
5386 s := g_Net_ClientName_ByID(NetClients[a].ID);
5387 g_Net_BanHost(NetClients[a].Peer^.address.host);
5388 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5389 g_Net_SaveBanList();
5390 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5391 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5392 if NetUseMaster then
5393 g_Net_Slist_Update;
5394 end;
5395 end else
5396 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5397 end
5398 else if cmd = 'unban' then
5399 begin
5400 if g_Game_IsServer and g_Game_IsNet then
5401 begin
5402 if Length(P) < 2 then
5403 begin
5404 g_Console_Add('unban <IP Address>');
5405 Exit;
5406 end;
5407 if P[1] = '' then
5408 begin
5409 g_Console_Add('unban <IP Address>');
5410 Exit;
5411 end;
5413 if g_Net_UnbanHost(P[1]) then
5414 begin
5415 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5416 g_Net_SaveBanList();
5417 end else
5418 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5419 end else
5420 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5421 end
5422 else if cmd = 'clientlist' then
5423 begin
5424 if g_Game_IsServer and g_Game_IsNet then
5425 begin
5426 b := 0;
5427 if NetClients <> nil then
5428 for a := Low(NetClients) to High(NetClients) do
5429 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5430 begin
5431 plr := g_Player_Get(NetClients[a].Player);
5432 if plr = nil then continue;
5433 Inc(b);
5434 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5435 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5436 end;
5437 if b = 0 then
5438 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5439 end else
5440 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5441 end
5442 else if cmd = 'connect' then
5443 begin
5444 if (NetMode = NET_NONE) then
5445 begin
5446 if Length(P) < 2 then
5447 begin
5448 g_Console_Add('connect <IP> [port] [password]');
5449 Exit;
5450 end;
5451 if P[1] = '' then
5452 begin
5453 g_Console_Add('connect <IP> [port] [password]');
5454 Exit;
5455 end;
5457 if Length(P) > 2 then
5458 prt := StrToIntDef(P[2], 25666)
5459 else
5460 prt := 25666;
5462 if Length(P) > 3 then
5463 pw := P[3]
5464 else
5465 pw := '';
5467 g_Game_StartClient(P[1], prt, pw);
5468 end;
5469 end
5470 else if cmd = 'disconnect' then
5471 begin
5472 if (NetMode = NET_CLIENT) then
5473 g_Net_Disconnect();
5474 end
5475 else if cmd = 'reconnect' then
5476 begin
5477 if (NetMode = NET_SERVER) then
5478 Exit;
5480 if (NetMode = NET_CLIENT) then
5481 begin
5482 g_Net_Disconnect();
5483 gExit := EXIT_SIMPLE;
5484 EndGame;
5485 end;
5487 //TODO: Use last successful password to reconnect, instead of ''
5488 g_Game_StartClient(NetClientIP, NetClientPort, '');
5489 end
5490 else if (cmd = 'addbot') or
5491 (cmd = 'bot_add') then
5492 begin
5493 if Length(P) > 1 then
5494 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5495 else
5496 g_Bot_Add(TEAM_NONE, 2);
5497 end
5498 else if cmd = 'bot_addlist' then
5499 begin
5500 if Length(P) > 1 then
5501 if Length(P) = 2 then
5502 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5503 else
5504 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5505 end
5506 else if cmd = 'bot_removeall' then
5507 g_Bot_RemoveAll()
5508 else if cmd = 'chat' then
5509 begin
5510 if g_Game_IsNet then
5511 begin
5512 if Length(P) > 1 then
5513 begin
5514 for a := 1 to High(P) do
5515 chstr := chstr + P[a] + ' ';
5517 if Length(chstr) > 200 then SetLength(chstr, 200);
5519 if Length(chstr) < 1 then
5520 begin
5521 g_Console_Add('chat <text>');
5522 Exit;
5523 end;
5525 chstr := b_Text_Format(chstr);
5526 if g_Game_IsClient then
5527 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5528 else
5529 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5530 end
5531 else
5532 g_Console_Add('chat <text>');
5533 end else
5534 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5535 end
5536 else if cmd = 'teamchat' then
5537 begin
5538 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5539 begin
5540 if Length(P) > 1 then
5541 begin
5542 for a := 1 to High(P) do
5543 chstr := chstr + P[a] + ' ';
5545 if Length(chstr) > 200 then SetLength(chstr, 200);
5547 if Length(chstr) < 1 then
5548 begin
5549 g_Console_Add('teamchat <text>');
5550 Exit;
5551 end;
5553 chstr := b_Text_Format(chstr);
5554 if g_Game_IsClient then
5555 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5556 else
5557 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5558 gPlayer1Settings.Team);
5559 end
5560 else
5561 g_Console_Add('teamchat <text>');
5562 end else
5563 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5564 end
5565 else if cmd = 'game' then
5566 begin
5567 if gGameSettings.GameType <> GT_NONE then
5568 begin
5569 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5570 Exit;
5571 end;
5572 if Length(P) = 1 then
5573 begin
5574 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5575 Exit;
5576 end;
5577 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5578 P[1] := addWadExtension(P[1]);
5579 if FileExists(MapsDir + P[1]) then
5580 begin
5581 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5582 if Length(P) < 3 then
5583 begin
5584 SetLength(P, 3);
5585 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5586 end;
5588 s := P[1] + ':\' + UpperCase(P[2]);
5590 if g_Map_Exist(MapsDir + s) then
5591 begin
5592 // Çàïóñêàåì ñâîþ èãðó
5593 g_Game_Free();
5594 with gGameSettings do
5595 begin
5596 GameMode := g_Game_TextToMode(gcGameMode);
5597 if gSwitchGameMode <> GM_NONE then
5598 GameMode := gSwitchGameMode;
5599 if GameMode = GM_NONE then GameMode := GM_DM;
5600 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5601 b := 1;
5602 if Length(P) >= 4 then
5603 b := StrToIntDef(P[3], 1);
5604 g_Game_StartCustom(s, GameMode, TimeLimit,
5605 GoalLimit, MaxLives, Options, b);
5606 end;
5607 end
5608 else
5609 if P[2] = '' then
5610 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5611 else
5612 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5613 end else
5614 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5615 end
5616 else if cmd = 'host' then
5617 begin
5618 if gGameSettings.GameType <> GT_NONE then
5619 begin
5620 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5621 Exit;
5622 end;
5623 if Length(P) < 4 then
5624 begin
5625 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5626 Exit;
5627 end;
5628 if not StrToIp(P[1], listen) then
5629 Exit;
5630 prt := StrToIntDef(P[2], 25666);
5632 P[3] := addWadExtension(P[3]);
5633 if FileExists(MapsDir + P[3]) then
5634 begin
5635 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5636 if Length(P) < 5 then
5637 begin
5638 SetLength(P, 5);
5639 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5640 end;
5642 s := P[3] + ':\' + UpperCase(P[4]);
5644 if g_Map_Exist(MapsDir + s) then
5645 begin
5646 // Çàïóñêàåì ñâîþ èãðó
5647 g_Game_Free();
5648 with gGameSettings do
5649 begin
5650 GameMode := g_Game_TextToMode(gcGameMode);
5651 if gSwitchGameMode <> GM_NONE then
5652 GameMode := gSwitchGameMode;
5653 if GameMode = GM_NONE then GameMode := GM_DM;
5654 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5655 b := 0;
5656 if Length(P) >= 6 then
5657 b := StrToIntDef(P[5], 0);
5658 g_Game_StartServer(s, GameMode, TimeLimit,
5659 GoalLimit, MaxLives, Options, b, listen, prt);
5660 end;
5661 end
5662 else
5663 if P[4] = '' then
5664 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5665 else
5666 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5667 end else
5668 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5669 end
5670 else if cmd = 'map' then
5671 begin
5672 if Length(P) = 1 then
5673 begin
5674 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5675 begin
5676 g_Console_Add(cmd + ' <MAP>');
5677 g_Console_Add(cmd + ' <WAD> [MAP]');
5678 end else
5679 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5680 end else
5681 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5682 begin
5683 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5684 if Length(P) < 3 then
5685 begin
5686 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5687 s := UpperCase(P[1]);
5688 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5689 begin // Êàðòà íàøëàñü
5690 gExitByTrigger := False;
5691 if gGameOn then
5692 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5693 gNextMap := s;
5694 gExit := EXIT_ENDLEVELCUSTOM;
5695 end
5696 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5697 g_Game_ChangeMap(s);
5698 end else
5699 begin
5700 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5701 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5702 P[1] := addWadExtension(P[1]);
5703 if FileExists(MapsDir + P[1]) then
5704 begin
5705 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5706 SetLength(P, 3);
5707 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5709 s := P[1] + ':\' + P[2];
5711 if g_Map_Exist(MapsDir + s) then
5712 begin
5713 gExitByTrigger := False;
5714 if gGameOn then
5715 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5716 gNextMap := s;
5717 gExit := EXIT_ENDLEVELCUSTOM;
5718 end
5719 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5720 g_Game_ChangeMap(s);
5721 end else
5722 if P[2] = '' then
5723 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5724 else
5725 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5726 end else
5727 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5728 end;
5729 end else
5730 begin
5731 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5732 P[1] := addWadExtension(P[1]);
5733 if FileExists(MapsDir + P[1]) then
5734 begin
5735 // Íàøëè WAD ôàéë
5736 P[2] := UpperCase(P[2]);
5737 s := P[1] + ':\' + P[2];
5739 if g_Map_Exist(MapsDir + s) then
5740 begin // Íàøëè êàðòó
5741 gExitByTrigger := False;
5742 if gGameOn then
5743 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5744 gNextMap := s;
5745 gExit := EXIT_ENDLEVELCUSTOM;
5746 end
5747 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5748 g_Game_ChangeMap(s);
5749 end else
5750 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5751 end else
5752 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5753 end;
5754 end else
5755 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5756 end
5757 else if cmd = 'nextmap' then
5758 begin
5759 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5760 g_Console_Add(_lc[I_MSG_NOT_GAME])
5761 else begin
5762 nm := True;
5763 if Length(P) = 1 then
5764 begin
5765 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5766 begin
5767 g_Console_Add(cmd + ' <MAP>');
5768 g_Console_Add(cmd + ' <WAD> [MAP]');
5769 end else begin
5770 nm := False;
5771 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5772 end;
5773 end else
5774 begin
5775 nm := False;
5776 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5777 begin
5778 if Length(P) < 3 then
5779 begin
5780 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5781 s := UpperCase(P[1]);
5782 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5783 begin // Êàðòà íàøëàñü
5784 gExitByTrigger := False;
5785 gNextMap := s;
5786 nm := True;
5787 end else
5788 begin
5789 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5790 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5791 P[1] := addWadExtension(P[1]);
5792 if FileExists(MapsDir + P[1]) then
5793 begin
5794 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5795 SetLength(P, 3);
5796 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5798 s := P[1] + ':\' + P[2];
5800 if g_Map_Exist(MapsDir + s) then
5801 begin // Óñòàíàâëèâàåì êàðòó
5802 gExitByTrigger := False;
5803 gNextMap := s;
5804 nm := True;
5805 end else
5806 if P[2] = '' then
5807 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5808 else
5809 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5810 end else
5811 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5812 end;
5813 end else
5814 begin
5815 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5816 P[1] := addWadExtension(P[1]);
5817 if FileExists(MapsDir + P[1]) then
5818 begin
5819 // Íàøëè WAD ôàéë
5820 P[2] := UpperCase(P[2]);
5821 s := P[1] + ':\' + P[2];
5823 if g_Map_Exist(MapsDir + s) then
5824 begin // Íàøëè êàðòó
5825 gExitByTrigger := False;
5826 gNextMap := s;
5827 nm := True;
5828 end else
5829 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5830 end else
5831 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5832 end;
5833 end else
5834 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5835 end;
5836 if nm then
5837 if gNextMap = '' then
5838 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
5839 else
5840 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
5841 end;
5842 end
5843 else if (cmd = 'endmap') or (cmd = 'goodbye') then
5844 begin
5845 if not gGameOn then
5846 g_Console_Add(_lc[I_MSG_NOT_GAME])
5847 else
5848 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5849 begin
5850 gExitByTrigger := False;
5851 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
5852 if (gNextMap = '') and (gTriggers <> nil) then
5853 for a := 0 to High(gTriggers) do
5854 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5855 begin
5856 gExitByTrigger := True;
5857 gNextMap := gTriggers[a].Data.MapName;
5858 Break;
5859 end;
5860 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
5861 if gNextMap = '' then
5862 gNextMap := g_Game_GetNextMap();
5863 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
5864 if Pos(':\', gNextMap) = 0 then
5865 s := gGameSettings.WAD + ':\' + gNextMap
5866 else
5867 s := gNextMap;
5868 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
5869 if g_Map_Exist(MapsDir + s) then
5870 gExit := EXIT_ENDLEVELCUSTOM
5871 else
5872 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
5873 end else
5874 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5875 end
5876 else if (cmd = 'event') then
5877 begin
5878 if (Length(P) <= 1) then
5879 begin
5880 for a := 0 to High(gEvents) do
5881 if gEvents[a].Command = '' then
5882 g_Console_Add(gEvents[a].Name + ' <none>')
5883 else
5884 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5885 Exit;
5886 end;
5887 if (Length(P) = 2) then
5888 begin
5889 for a := 0 to High(gEvents) do
5890 if gEvents[a].Name = P[1] then
5891 if gEvents[a].Command = '' then
5892 g_Console_Add(gEvents[a].Name + ' <none>')
5893 else
5894 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5895 Exit;
5896 end;
5897 for a := 0 to High(gEvents) do
5898 if gEvents[a].Name = P[1] then
5899 begin
5900 gEvents[a].Command := '';
5901 for b := 2 to High(P) do
5902 if Pos(' ', P[b]) = 0 then
5903 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
5904 else
5905 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
5906 gEvents[a].Command := Trim(gEvents[a].Command);
5907 Exit;
5908 end;
5909 end
5910 // Êîìàíäû Ñâîåé èãðû:
5911 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5912 begin
5913 if cmd = 'bot_addred' then
5914 begin
5915 if Length(P) > 1 then
5916 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
5917 else
5918 g_Bot_Add(TEAM_RED, 2);
5919 end
5920 else if cmd = 'bot_addblue' then
5921 begin
5922 if Length(P) > 1 then
5923 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
5924 else
5925 g_Bot_Add(TEAM_BLUE, 2);
5926 end
5927 else if cmd = 'suicide' then
5928 begin
5929 if gGameOn then
5930 begin
5931 if g_Game_IsClient then
5932 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
5933 else
5934 begin
5935 if gPlayer1 <> nil then
5936 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
5937 if gPlayer2 <> nil then
5938 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
5939 end;
5940 end;
5941 end
5942 else if cmd = 'spectate' then
5943 begin
5944 if not gGameOn then
5945 Exit;
5946 g_Game_Spectate();
5947 end
5948 else if cmd = 'say' then
5949 begin
5950 if g_Game_IsServer and g_Game_IsNet then
5951 begin
5952 if Length(P) > 1 then
5953 begin
5954 chstr := '';
5955 for a := 1 to High(P) do
5956 chstr := chstr + P[a] + ' ';
5958 if Length(chstr) > 200 then SetLength(chstr, 200);
5960 if Length(chstr) < 1 then
5961 begin
5962 g_Console_Add('say <text>');
5963 Exit;
5964 end;
5966 chstr := b_Text_Format(chstr);
5967 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
5968 end
5969 else g_Console_Add('say <text>');
5970 end else
5971 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5972 end
5973 else if cmd = 'tell' then
5974 begin
5975 if g_Game_IsServer and g_Game_IsNet then
5976 begin
5977 if (Length(P) > 2) and (P[1] <> '') then
5978 begin
5979 chstr := '';
5980 for a := 2 to High(P) do
5981 chstr := chstr + P[a] + ' ';
5983 if Length(chstr) > 200 then SetLength(chstr, 200);
5985 if Length(chstr) < 1 then
5986 begin
5987 g_Console_Add('tell <playername> <text>');
5988 Exit;
5989 end;
5991 pl := g_Net_Client_ByName(P[1]);
5992 if pl <> nil then
5993 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
5994 else
5995 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5996 end
5997 else g_Console_Add('tell <playername> <text>');
5998 end else
5999 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6000 end
6001 else if (cmd = 'overtime') and not g_Game_IsClient then
6002 begin
6003 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6004 Exit;
6005 // Äîïîëíèòåëüíîå âðåìÿ:
6006 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6008 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6009 [gGameSettings.TimeLimit div 3600,
6010 (gGameSettings.TimeLimit div 60) mod 60,
6011 gGameSettings.TimeLimit mod 60]));
6012 if g_Game_IsNet then MH_SEND_GameSettings;
6013 end
6014 else if (cmd = 'rcon_password') and g_Game_IsClient then
6015 begin
6016 if (Length(P) <= 1) then
6017 g_Console_Add('rcon_password <password>')
6018 else
6019 MC_SEND_RCONPassword(P[1]);
6020 end
6021 else if cmd = 'rcon' then
6022 begin
6023 if g_Game_IsClient then
6024 begin
6025 if Length(P) > 1 then
6026 begin
6027 chstr := '';
6028 for a := 1 to High(P) do
6029 chstr := chstr + P[a] + ' ';
6031 if Length(chstr) > 200 then SetLength(chstr, 200);
6033 if Length(chstr) < 1 then
6034 begin
6035 g_Console_Add('rcon <command>');
6036 Exit;
6037 end;
6039 MC_SEND_RCONCommand(chstr);
6040 end
6041 else g_Console_Add('rcon <command>');
6042 end;
6043 end
6044 else if cmd = 'ready' then
6045 begin
6046 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6047 gLMSRespawnTime := gTime + 100;
6048 end
6049 else if (cmd = 'callvote') and g_Game_IsNet then
6050 begin
6051 if Length(P) > 1 then
6052 begin
6053 chstr := '';
6054 for a := 1 to High(P) do begin
6055 if a > 1 then chstr := chstr + ' ';
6056 chstr := chstr + P[a];
6057 end;
6059 if Length(chstr) > 200 then SetLength(chstr, 200);
6061 if Length(chstr) < 1 then
6062 begin
6063 g_Console_Add('callvote <command>');
6064 Exit;
6065 end;
6067 if g_Game_IsClient then
6068 MC_SEND_Vote(True, chstr)
6069 else
6070 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6071 g_Console_Process('vote', True);
6072 end
6073 else
6074 g_Console_Add('callvote <command>');
6075 end
6076 else if (cmd = 'vote') and g_Game_IsNet then
6077 begin
6078 if g_Game_IsClient then
6079 MC_SEND_Vote(False)
6080 else if gVoteInProgress then
6081 begin
6082 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6083 a := Floor((NetClientCount+1)/2.0) + 1
6084 else
6085 a := Floor(NetClientCount/2.0) + 1;
6086 if gVoted then
6087 begin
6088 Dec(gVoteCount);
6089 gVoted := False;
6090 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6091 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6092 end
6093 else
6094 begin
6095 Inc(gVoteCount);
6096 gVoted := True;
6097 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6098 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6099 g_Game_CheckVote;
6100 end;
6101 end;
6102 end
6103 end;
6104 end;
6106 procedure g_TakeScreenShot();
6107 var
6108 a: Word;
6109 FileName: string;
6110 ssdir, t: string;
6111 st: TStream;
6112 ok: Boolean;
6113 begin
6114 if e_NoGraphics then Exit;
6115 ssdir := GameDir+'/screenshots';
6116 if not findFileCI(ssdir, true) then
6117 begin
6118 // try to create dir
6119 try
6120 CreateDir(ssdir);
6121 except
6122 end;
6123 if not findFileCI(ssdir, true) then exit; // alas
6124 end;
6125 try
6126 for a := 1 to High(Word) do
6127 begin
6128 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6129 t := FileName;
6130 if findFileCI(t, true) then continue;
6131 if not findFileCI(FileName) then
6132 begin
6133 ok := false;
6134 st := createDiskFile(FileName);
6135 try
6136 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6137 ok := true;
6138 finally
6139 st.Free();
6140 end;
6141 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6142 break;
6143 end;
6144 end;
6145 except
6146 end;
6147 end;
6149 procedure g_Game_InGameMenu(Show: Boolean);
6150 begin
6151 if (g_ActiveWindow = nil) and Show then
6152 begin
6153 if gGameSettings.GameType = GT_SINGLE then
6154 g_GUI_ShowWindow('GameSingleMenu')
6155 else
6156 begin
6157 if g_Game_IsClient then
6158 g_GUI_ShowWindow('GameClientMenu')
6159 else
6160 if g_Game_IsNet then
6161 g_GUI_ShowWindow('GameServerMenu')
6162 else
6163 g_GUI_ShowWindow('GameCustomMenu');
6164 end;
6165 g_Sound_PlayEx('MENU_OPEN');
6167 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6168 if (not g_Game_IsNet) then
6169 g_Game_Pause(True);
6170 end
6171 else
6172 if (g_ActiveWindow <> nil) and (not Show) then
6173 begin
6174 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6175 if (not g_Game_IsNet) then
6176 g_Game_Pause(False);
6177 end;
6178 end;
6180 procedure g_Game_Pause(Enable: Boolean);
6181 begin
6182 if not gGameOn then
6183 Exit;
6185 if gPause = Enable then
6186 Exit;
6188 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6189 Exit;
6191 gPause := Enable;
6192 g_Game_PauseAllSounds(Enable);
6193 end;
6195 procedure g_Game_PauseAllSounds(Enable: Boolean);
6196 var
6197 i: Integer;
6198 begin
6199 // Òðèããåðû:
6200 if gTriggers <> nil then
6201 for i := 0 to High(gTriggers) do
6202 with gTriggers[i] do
6203 if (TriggerType = TRIGGER_SOUND) and
6204 (Sound <> nil) and
6205 Sound.IsPlaying() then
6206 begin
6207 Sound.Pause(Enable);
6208 end;
6210 // Çâóêè èãðîêîâ:
6211 if gPlayers <> nil then
6212 for i := 0 to High(gPlayers) do
6213 if gPlayers[i] <> nil then
6214 gPlayers[i].PauseSounds(Enable);
6216 // Ìóçûêà:
6217 if gMusic <> nil then
6218 gMusic.Pause(Enable);
6219 end;
6221 procedure g_Game_StopAllSounds(all: Boolean);
6222 var
6223 i: Integer;
6224 begin
6225 if gTriggers <> nil then
6226 for i := 0 to High(gTriggers) do
6227 with gTriggers[i] do
6228 if (TriggerType = TRIGGER_SOUND) and
6229 (Sound <> nil) then
6230 Sound.Stop();
6232 if gMusic <> nil then
6233 gMusic.Stop();
6235 if all then
6236 e_StopChannels();
6237 end;
6239 procedure g_Game_UpdateTriggerSounds();
6240 var
6241 i: Integer;
6242 begin
6243 if gTriggers <> nil then
6244 for i := 0 to High(gTriggers) do
6245 with gTriggers[i] do
6246 if (TriggerType = TRIGGER_SOUND) and
6247 (Sound <> nil) and
6248 (Data.Local) and
6249 Sound.IsPlaying() then
6250 begin
6251 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6252 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6253 begin
6254 Sound.SetPan(0.5 - Data.Pan/255.0);
6255 Sound.SetVolume(Data.Volume/255.0);
6256 end
6257 else
6258 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6259 end;
6260 end;
6262 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6263 begin
6264 Result := False;
6265 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6266 begin
6267 Result := True;
6268 Exit;
6269 end;
6270 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6271 begin
6272 Result := True;
6273 Exit;
6274 end;
6275 if gSpectMode <> SPECT_PLAYERS then
6276 Exit;
6277 if gSpectPID1 = UID then
6278 begin
6279 Result := True;
6280 Exit;
6281 end;
6282 if gSpectViewTwo and (gSpectPID2 = UID) then
6283 begin
6284 Result := True;
6285 Exit;
6286 end;
6287 end;
6289 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6290 var
6291 Pl: TPlayer;
6292 begin
6293 Result := False;
6294 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6295 begin
6296 Result := True;
6297 Exit;
6298 end;
6299 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6300 begin
6301 Result := True;
6302 Exit;
6303 end;
6304 if gSpectMode <> SPECT_PLAYERS then
6305 Exit;
6306 Pl := g_Player_Get(gSpectPID1);
6307 if (Pl <> nil) and (Pl.Team = Team) then
6308 begin
6309 Result := True;
6310 Exit;
6311 end;
6312 if gSpectViewTwo then
6313 begin
6314 Pl := g_Player_Get(gSpectPID2);
6315 if (Pl <> nil) and (Pl.Team = Team) then
6316 begin
6317 Result := True;
6318 Exit;
6319 end;
6320 end;
6321 end;
6323 procedure g_Game_Message(Msg: string; Time: Word);
6324 begin
6325 MessageText := b_Text_Format(Msg);
6326 MessageTime := Time;
6327 end;
6329 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6330 var
6331 a: Integer;
6332 begin
6333 case gAnnouncer of
6334 ANNOUNCE_NONE:
6335 Exit;
6336 ANNOUNCE_ME,
6337 ANNOUNCE_MEPLUS:
6338 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6339 Exit;
6340 end;
6341 for a := 0 to 3 do
6342 if goodsnd[a].IsPlaying() then
6343 Exit;
6345 goodsnd[Random(4)].Play();
6346 end;
6348 procedure g_Game_Announce_KillCombo(Param: Integer);
6349 var
6350 UID: Word;
6351 c, n: Byte;
6352 Pl: TPlayer;
6353 Name: String;
6354 begin
6355 UID := Param and $FFFF;
6356 c := Param shr 16;
6357 if c < 2 then
6358 Exit;
6360 Pl := g_Player_Get(UID);
6361 if Pl = nil then
6362 Name := '?'
6363 else
6364 Name := Pl.Name;
6366 case c of
6367 2: begin
6368 n := 0;
6369 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6370 end;
6371 3: begin
6372 n := 1;
6373 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6374 end;
6375 4: begin
6376 n := 2;
6377 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6378 end;
6379 else begin
6380 n := 3;
6381 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6382 end;
6383 end;
6385 case gAnnouncer of
6386 ANNOUNCE_NONE:
6387 Exit;
6388 ANNOUNCE_ME:
6389 if not g_Game_IsWatchedPlayer(UID) then
6390 Exit;
6391 ANNOUNCE_MEPLUS:
6392 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6393 Exit;
6394 end;
6396 if killsnd[n].IsPlaying() then
6397 killsnd[n].Stop();
6398 killsnd[n].Play();
6399 end;
6401 procedure g_Game_StartVote(Command, Initiator: string);
6402 var
6403 Need: Integer;
6404 begin
6405 if not gVotesEnabled then Exit;
6406 if gGameSettings.GameType <> GT_SERVER then Exit;
6407 if gVoteInProgress or gVotePassed then
6408 begin
6409 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6410 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6411 Exit;
6412 end;
6413 gVoteInProgress := True;
6414 gVotePassed := False;
6415 gVoteTimer := gTime + gVoteTimeout * 1000;
6416 gVoteCount := 0;
6417 gVoted := False;
6418 gVoteCommand := Command;
6420 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6421 Need := Floor((NetClientCount+1)/2.0)+1
6422 else
6423 Need := Floor(NetClientCount/2.0)+1;
6424 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6425 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6426 end;
6428 procedure g_Game_CheckVote;
6429 var
6430 I, Need: Integer;
6431 begin
6432 if gGameSettings.GameType <> GT_SERVER then Exit;
6433 if not gVoteInProgress then Exit;
6435 if (gTime >= gVoteTimer) then
6436 begin
6437 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6438 Need := Floor((NetClientCount+1)/2.0) + 1
6439 else
6440 Need := Floor(NetClientCount/2.0) + 1;
6441 if gVoteCount >= Need then
6442 begin
6443 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6444 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6445 gVotePassed := True;
6446 gVoteCmdTimer := gTime + 5000;
6447 end
6448 else
6449 begin
6450 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6451 MH_SEND_VoteEvent(NET_VE_FAILED);
6452 end;
6453 if NetClients <> nil then
6454 for I := Low(NetClients) to High(NetClients) do
6455 if NetClients[i].Used then
6456 NetClients[i].Voted := False;
6457 gVoteInProgress := False;
6458 gVoted := False;
6459 gVoteCount := 0;
6460 end
6461 else
6462 begin
6463 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6464 Need := Floor((NetClientCount+1)/2.0) + 1
6465 else
6466 Need := Floor(NetClientCount/2.0) + 1;
6467 if gVoteCount >= Need then
6468 begin
6469 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6470 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6471 gVoteInProgress := False;
6472 gVotePassed := True;
6473 gVoteCmdTimer := gTime + 5000;
6474 gVoted := False;
6475 gVoteCount := 0;
6476 if NetClients <> nil then
6477 for I := Low(NetClients) to High(NetClients) do
6478 if NetClients[i].Used then
6479 NetClients[i].Voted := False;
6480 end;
6481 end;
6482 end;
6484 procedure g_Game_LoadMapList(FileName: string);
6485 var
6486 ListFile: TextFile;
6487 s: string;
6488 begin
6489 MapList := nil;
6490 MapIndex := -1;
6492 if not FileExists(FileName) then Exit;
6494 AssignFile(ListFile, FileName);
6495 Reset(ListFile);
6496 while not EOF(ListFile) do
6497 begin
6498 ReadLn(ListFile, s);
6500 s := Trim(s);
6501 if s = '' then Continue;
6503 SetLength(MapList, Length(MapList)+1);
6504 MapList[High(MapList)] := s;
6505 end;
6506 CloseFile(ListFile);
6507 end;
6509 procedure g_Game_SetDebugMode();
6510 begin
6511 gDebugMode := True;
6512 // ×èòû (äàæå â ñâîåé èãðå):
6513 gCheats := True;
6514 end;
6516 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6517 var
6518 i: Word;
6519 begin
6520 if Length(LoadingStat.Msgs) = 0 then
6521 Exit;
6523 with LoadingStat do
6524 begin
6525 if not reWrite then
6526 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6527 if NextMsg = Length(Msgs) then
6528 begin // scroll
6529 for i := 0 to High(Msgs)-1 do
6530 Msgs[i] := Msgs[i+1];
6531 end
6532 else
6533 Inc(NextMsg);
6534 end else
6535 if NextMsg = 0 then
6536 Inc(NextMsg);
6538 Msgs[NextMsg-1] := Text;
6539 CurValue := 0;
6540 MaxValue := Max;
6541 ShowCount := 0;
6542 end;
6544 g_ActiveWindow := nil;
6546 ProcessLoading;
6547 end;
6549 procedure g_Game_StepLoading();
6550 begin
6551 with LoadingStat do
6552 begin
6553 Inc(CurValue);
6554 Inc(ShowCount);
6555 if (ShowCount > LOADING_SHOW_STEP) then
6556 begin
6557 ShowCount := 0;
6558 ProcessLoading;
6559 end;
6560 end;
6561 end;
6563 procedure g_Game_ClearLoading();
6564 var
6565 len: Word;
6566 begin
6567 with LoadingStat do
6568 begin
6569 CurValue := 0;
6570 MaxValue := 0;
6571 ShowCount := 0;
6572 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6573 if len < 1 then len := 1;
6574 SetLength(Msgs, len);
6575 for len := Low(Msgs) to High(Msgs) do
6576 Msgs[len] := '';
6577 NextMsg := 0;
6578 end;
6579 end;
6581 procedure Parse_Params(var pars: TParamStrValues);
6582 var
6583 i: Integer;
6584 s: String;
6585 begin
6586 SetLength(pars, 0);
6587 i := 1;
6588 while i <= ParamCount do
6589 begin
6590 s := ParamStr(i);
6591 if (s[1] = '-') and (Length(s) > 1) then
6592 begin
6593 if (s[2] = '-') and (Length(s) > 2) then
6594 begin // Îäèíî÷íûé ïàðàìåòð
6595 SetLength(pars, Length(pars) + 1);
6596 with pars[High(pars)] do
6597 begin
6598 Name := LowerCase(s);
6599 Value := '+';
6600 end;
6601 end
6602 else
6603 if (i < ParamCount) then
6604 begin // Ïàðàìåòð ñî çíà÷åíèåì
6605 Inc(i);
6606 SetLength(pars, Length(pars) + 1);
6607 with pars[High(pars)] do
6608 begin
6609 Name := LowerCase(s);
6610 Value := LowerCase(ParamStr(i));
6611 end;
6612 end;
6613 end;
6615 Inc(i);
6616 end;
6617 end;
6619 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6620 var
6621 i: Integer;
6622 begin
6623 Result := '';
6624 for i := 0 to High(pars) do
6625 if pars[i].Name = aName then
6626 begin
6627 Result := pars[i].Value;
6628 Break;
6629 end;
6630 end;
6632 procedure g_Game_Process_Params();
6633 var
6634 pars: TParamStrValues;
6635 map: String;
6636 GMode, n: Byte;
6637 LimT, LimS: Integer;
6638 Opt: LongWord;
6639 Lives: Integer;
6640 s: String;
6641 Port: Integer;
6642 ip: String;
6643 F: TextFile;
6644 begin
6645 Parse_Params(pars);
6647 // Debug mode:
6648 s := Find_Param_Value(pars, '--debug');
6649 if (s <> '') then
6650 begin
6651 g_Game_SetDebugMode();
6652 s := Find_Param_Value(pars, '--netdump');
6653 if (s <> '') then
6654 NetDump := True;
6655 end;
6657 // Connect when game loads
6658 ip := Find_Param_Value(pars, '-connect');
6660 if ip <> '' then
6661 begin
6662 s := Find_Param_Value(pars, '-port');
6663 if (s = '') or not TryStrToInt(s, Port) then
6664 Port := 25666;
6666 s := Find_Param_Value(pars, '-pw');
6668 g_Game_StartClient(ip, Port, s);
6669 Exit;
6670 end;
6672 // Start map when game loads:
6673 map := LowerCase(Find_Param_Value(pars, '-map'));
6674 if isWadPath(map) then
6675 begin
6676 // Game mode:
6677 s := Find_Param_Value(pars, '-gm');
6678 GMode := g_Game_TextToMode(s);
6679 if GMode = GM_NONE then GMode := GM_DM;
6680 if GMode = GM_SINGLE then GMode := GM_COOP;
6682 // Time limit:
6683 s := Find_Param_Value(pars, '-limt');
6684 if (s = '') or (not TryStrToInt(s, LimT)) then
6685 LimT := 0;
6686 if LimT < 0 then
6687 LimT := 0;
6689 // Goal limit:
6690 s := Find_Param_Value(pars, '-lims');
6691 if (s = '') or (not TryStrToInt(s, LimS)) then
6692 LimS := 0;
6693 if LimS < 0 then
6694 LimS := 0;
6696 // Lives limit:
6697 s := Find_Param_Value(pars, '-lives');
6698 if (s = '') or (not TryStrToInt(s, Lives)) then
6699 Lives := 0;
6700 if Lives < 0 then
6701 Lives := 0;
6703 // Options:
6704 s := Find_Param_Value(pars, '-opt');
6705 if (s = '') then
6706 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6707 else
6708 Opt := StrToIntDef(s, 0);
6709 if Opt = 0 then
6710 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6712 // Close after map:
6713 s := Find_Param_Value(pars, '--close');
6714 if (s <> '') then
6715 gMapOnce := True;
6717 // Delete test map after play:
6718 s := Find_Param_Value(pars, '--testdelete');
6719 if (s <> '') then
6720 begin
6721 gMapToDelete := MapsDir + map;
6722 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6723 Halt(1);
6724 end;
6726 // Delete temporary WAD after play:
6727 s := Find_Param_Value(pars, '--tempdelete');
6728 if (s <> '') then
6729 begin
6730 gMapToDelete := MapsDir + map;
6731 gTempDelete := True;
6732 end;
6734 // Number of players:
6735 s := Find_Param_Value(pars, '-pl');
6736 if (s = '') then
6737 n := 1
6738 else
6739 n := StrToIntDef(s, 1);
6741 // Start:
6742 s := Find_Param_Value(pars, '-port');
6743 if (s = '') or not TryStrToInt(s, Port) then
6744 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6745 else
6746 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6747 end;
6749 // Execute script when game loads:
6750 s := Find_Param_Value(pars, '-exec');
6751 if s <> '' then
6752 begin
6753 if Pos(':\', s) = 0 then
6754 s := GameDir + '/' + s;
6756 {$I-}
6757 AssignFile(F, s);
6758 Reset(F);
6759 if IOResult <> 0 then
6760 begin
6761 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6762 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6763 CloseFile(F);
6764 Exit;
6765 end;
6766 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6767 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6769 while not EOF(F) do
6770 begin
6771 ReadLn(F, s);
6772 if IOResult <> 0 then
6773 begin
6774 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6775 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6776 CloseFile(F);
6777 Exit;
6778 end;
6779 if Pos('#', s) <> 1 then // script comment
6780 g_Console_Process(s, True);
6781 end;
6783 CloseFile(F);
6784 {$I+}
6785 end;
6787 SetLength(pars, 0);
6788 end;
6790 end.