DEADSOFTWARE

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