DEADSOFTWARE

light: use proper scissoring instead of clearing the whole stencil buffer for each...
[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);
2740 if gwin_has_stencil and (g_dynLightCount > 0) then
2741 begin
2742 // setup OpenGL parameters
2743 glStencilMask($FFFFFFFF);
2744 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2745 glEnable(GL_STENCIL_TEST);
2746 glEnable(GL_SCISSOR_TEST);
2747 glClear(GL_STENCIL_BUFFER_BIT);
2748 glStencilFunc(GL_EQUAL, 0, $ff);
2750 for lln := 0 to g_dynLightCount-1 do
2751 begin
2752 lx := g_dynLights[lln].x;
2753 ly := g_dynLights[lln].y;
2754 lrad := g_dynLights[lln].radius;
2755 if lrad < 3 then continue;
2756 // set scissor to optimize drawing
2757 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2758 // clear stencil buffer
2759 //glClear(GL_STENCIL_BUFFER_BIT); //!!!
2760 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2761 // draw extruded panels
2762 glDisable(GL_TEXTURE_2D);
2763 glDisable(GL_BLEND);
2764 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2765 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2766 // render light texture
2767 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2768 //glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2769 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2770 // blend it
2771 glEnable(GL_BLEND);
2772 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2773 glEnable(GL_TEXTURE_2D);
2774 // color and opacity
2775 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2776 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2777 glBegin(GL_QUADS);
2778 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2779 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2780 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2781 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2782 glEnd();
2783 end;
2785 // done
2786 glDisable(GL_STENCIL_TEST);
2787 glDisable(GL_BLEND);
2788 glDisable(GL_SCISSOR_TEST);
2789 glScissor(0, 0, sWidth, sHeight);
2790 end;
2792 g_Map_DrawPanels(PANEL_FORE);
2793 if g_debug_HealthBar then
2794 begin
2795 g_Monsters_DrawHealth();
2796 g_Player_DrawHealth();
2797 end;
2799 if p.FSpectator then
2800 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2801 p.GameY + PLAYER_RECT_CY - 4,
2802 'X', gStdFont, 255, 255, 255, 1, True);
2804 for a := 0 to High(gCollideMap) do
2805 for b := 0 to High(gCollideMap[a]) do
2806 begin
2807 d := 0;
2808 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2809 d := d + 1;
2810 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2811 d := d + 2;
2813 case d of
2814 1: e_DrawPoint(1, b, a, 200, 200, 200);
2815 2: e_DrawPoint(1, b, a, 64, 64, 255);
2816 3: e_DrawPoint(1, b, a, 255, 0, 255);
2817 end;
2818 end;
2821 glPopMatrix();
2822 g_Map_ResetPVP();
2824 p.DrawPain();
2825 p.DrawPickup();
2826 p.DrawRulez();
2827 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
2828 if g_Debug_Player then
2829 g_Player_DrawDebug(p);
2830 p.DrawGUI();
2831 end;
2833 procedure g_Game_Draw();
2834 var
2835 ID: DWORD;
2836 w, h: Word;
2837 ww, hh: Byte;
2838 Time: Int64;
2839 back: string;
2840 plView1, plView2: TPlayer;
2841 Split: Boolean;
2842 begin
2843 if gExit = EXIT_QUIT then Exit;
2845 Time := GetTimer() {div 1000};
2846 FPSCounter := FPSCounter+1;
2847 if Time - FPSTime >= 1000 then
2848 begin
2849 FPS := FPSCounter;
2850 FPSCounter := 0;
2851 FPSTime := Time;
2852 end;
2854 if gGameOn or (gState = STATE_FOLD) then
2855 begin
2856 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
2857 begin
2858 gSpectMode := SPECT_NONE;
2859 if not gRevertPlayers then
2860 begin
2861 plView1 := gPlayer1;
2862 plView2 := gPlayer2;
2863 end
2864 else
2865 begin
2866 plView1 := gPlayer2;
2867 plView2 := gPlayer1;
2868 end;
2869 end
2870 else
2871 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
2872 begin
2873 gSpectMode := SPECT_NONE;
2874 if gPlayer2 = nil then
2875 plView1 := gPlayer1
2876 else
2877 plView1 := gPlayer2;
2878 plView2 := nil;
2879 end
2880 else
2881 begin
2882 plView1 := nil;
2883 plView2 := nil;
2884 end;
2886 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
2887 gSpectMode := SPECT_STATS;
2889 if gSpectMode = SPECT_PLAYERS then
2890 if gPlayers <> nil then
2891 begin
2892 plView1 := GetActivePlayer_ByID(gSpectPID1);
2893 if plView1 = nil then
2894 begin
2895 gSpectPID1 := GetActivePlayerID_Next();
2896 plView1 := GetActivePlayer_ByID(gSpectPID1);
2897 end;
2898 if gSpectViewTwo then
2899 begin
2900 plView2 := GetActivePlayer_ByID(gSpectPID2);
2901 if plView2 = nil then
2902 begin
2903 gSpectPID2 := GetActivePlayerID_Next();
2904 plView2 := GetActivePlayer_ByID(gSpectPID2);
2905 end;
2906 end;
2907 end;
2909 if gSpectMode = SPECT_MAPVIEW then
2910 begin
2911 // Ðåæèì ïðîñìîòðà êàðòû
2912 Split := False;
2913 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
2914 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
2915 gHearPoint1.Active := True;
2916 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
2917 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
2918 gHearPoint2.Active := False;
2919 end
2920 else
2921 begin
2922 Split := (plView1 <> nil) and (plView2 <> nil);
2924 // Òî÷êè ñëóõà èãðîêîâ
2925 if plView1 <> nil then
2926 begin
2927 gHearPoint1.Active := True;
2928 gHearPoint1.Coords.X := plView1.GameX;
2929 gHearPoint1.Coords.Y := plView1.GameY;
2930 end else
2931 gHearPoint1.Active := False;
2932 if plView2 <> nil then
2933 begin
2934 gHearPoint2.Active := True;
2935 gHearPoint2.Coords.X := plView2.GameX;
2936 gHearPoint2.Coords.Y := plView2.GameY;
2937 end else
2938 gHearPoint2.Active := False;
2940 // Ðàçìåð ýêðàíîâ èãðîêîâ:
2941 gPlayerScreenSize.X := gScreenWidth-196;
2942 if Split then
2943 begin
2944 gPlayerScreenSize.Y := gScreenHeight div 2;
2945 if gScreenHeight mod 2 = 0 then
2946 Dec(gPlayerScreenSize.Y);
2947 end
2948 else
2949 gPlayerScreenSize.Y := gScreenHeight;
2951 if Split then
2952 if gScreenHeight mod 2 = 0 then
2953 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
2954 else
2955 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
2957 DrawPlayer(plView1);
2958 gPlayer1ScreenCoord.X := sX;
2959 gPlayer1ScreenCoord.Y := sY;
2961 if Split then
2962 begin
2963 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
2965 DrawPlayer(plView2);
2966 gPlayer2ScreenCoord.X := sX;
2967 gPlayer2ScreenCoord.Y := sY;
2968 end;
2970 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
2972 if Split then
2973 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
2974 end;
2976 if MessageText <> '' then
2977 begin
2978 w := 0;
2979 h := 0;
2980 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
2981 if Split then
2982 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
2983 (gScreenHeight div 2)-(h div 2), MessageText)
2984 else
2985 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
2986 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
2987 end;
2989 if IsDrawStat or (gSpectMode = 1) then DrawStat();
2991 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
2992 begin
2993 // Draw spectator GUI
2994 ww := 0;
2995 hh := 0;
2996 e_TextureFontGetSize(gStdFont, ww, hh);
2997 case gSpectMode of
2998 SPECT_STATS:
2999 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3000 SPECT_MAPVIEW:
3001 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3002 SPECT_PLAYERS:
3003 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3004 end;
3005 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3006 if gSpectMode = SPECT_MAPVIEW then
3007 begin
3008 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3009 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3010 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3011 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3012 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3013 end;
3014 if gSpectMode = SPECT_PLAYERS then
3015 begin
3016 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3017 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3018 if gSpectViewTwo then
3019 begin
3020 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3021 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3022 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3023 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3024 end
3025 else
3026 begin
3027 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3028 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3029 end;
3030 end;
3031 end;
3032 end;
3034 if gPause and gGameOn and (g_ActiveWindow = nil) then
3035 begin
3036 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3038 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3039 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3040 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3041 end;
3043 if not gGameOn then
3044 begin
3045 if (gState = STATE_MENU) then
3046 begin
3047 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
3048 begin
3049 if g_Texture_Get('MENU_BACKGROUND', ID) then
3050 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3051 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3052 end;
3053 if g_ActiveWindow <> nil then
3054 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3055 end;
3057 if gState = STATE_FOLD then
3058 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3060 if gState = STATE_INTERCUSTOM then
3061 begin
3062 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3063 begin
3064 back := 'TEXTURE_endpic';
3065 if not g_Texture_Get(back, ID) then
3066 back := _lc[I_TEXTURE_ENDPIC];
3067 end
3068 else
3069 back := 'INTER';
3071 if g_Texture_Get(back, ID) then
3072 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3073 else
3074 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3076 DrawCustomStat();
3078 if g_ActiveWindow <> nil then
3079 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3080 end;
3082 if gState = STATE_INTERSINGLE then
3083 begin
3084 if EndingGameCounter > 0 then
3085 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter)
3086 else
3087 begin
3088 back := 'INTER';
3090 if g_Texture_Get(back, ID) then
3091 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3092 else
3093 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3095 DrawSingleStat();
3097 if g_ActiveWindow <> nil then
3098 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3099 end;
3100 end;
3102 if gState = STATE_ENDPIC then
3103 begin
3104 ID := DWORD(-1);
3105 if not g_Texture_Get('TEXTURE_endpic', ID) then
3106 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3108 if ID <> DWORD(-1) then
3109 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3110 else
3111 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3113 if g_ActiveWindow <> nil then
3114 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3115 end;
3117 if gState = STATE_SLIST then
3118 begin
3119 if g_Texture_Get('MENU_BACKGROUND', ID) then
3120 begin
3121 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3122 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3123 end;
3124 g_Serverlist_Draw(slCurrent);
3125 end;
3126 end;
3128 if g_ActiveWindow <> nil then
3129 begin
3130 if gGameOn then
3131 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3132 g_ActiveWindow.Draw();
3133 end;
3135 g_Console_Draw();
3137 if g_debug_Sounds and gGameOn then
3138 begin
3139 for w := 0 to High(e_SoundsArray) do
3140 for h := 0 to e_SoundsArray[w].nRefs do
3141 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3142 end;
3144 if gShowFPS then
3145 begin
3146 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3147 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3148 end;
3150 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3151 e_TextureFontPrint(gScreenWidth-72, 0,
3152 Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]),
3153 gStdFont);
3154 end;
3156 procedure g_Game_Quit();
3157 begin
3158 g_Game_StopAllSounds(True);
3159 gMusic.Free();
3160 g_Game_SaveOptions();
3161 g_Game_FreeData();
3162 g_PlayerModel_FreeData();
3163 g_Texture_DeleteAll();
3164 g_Frames_DeleteAll();
3165 g_Menu_Free();
3167 if NetInitDone then g_Net_Free;
3169 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3170 if gMapToDelete <> '' then
3171 g_Game_DeleteTestMap();
3173 gExit := EXIT_QUIT;
3174 PushExitEvent();
3175 end;
3177 procedure g_FatalError(Text: String);
3178 begin
3179 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3180 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3182 gExit := EXIT_SIMPLE;
3183 end;
3185 procedure g_SimpleError(Text: String);
3186 begin
3187 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3188 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3189 end;
3191 procedure g_Game_SetupScreenSize();
3192 var
3193 d: Single;
3194 begin
3195 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3196 gPlayerScreenSize.X := gScreenWidth-196;
3197 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3198 gPlayerScreenSize.Y := gScreenHeight div 2
3199 else
3200 gPlayerScreenSize.Y := gScreenHeight;
3202 // Ðàçìåð çàäíåãî ïëàíà:
3203 if BackID <> DWORD(-1) then
3204 begin
3205 d := SKY_STRETCH;
3207 if (gScreenWidth*d > gMapInfo.Width) or
3208 (gScreenHeight*d > gMapInfo.Height) then
3209 d := 1.0;
3211 gBackSize.X := Round(gScreenWidth*d);
3212 gBackSize.Y := Round(gScreenHeight*d);
3213 end;
3214 end;
3216 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3217 begin
3218 g_Window_SetSize(newWidth, newHeight, nowFull);
3219 end;
3221 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3222 begin
3223 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3224 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3225 Exit;
3226 if gPlayer1 = nil then
3227 begin
3228 if g_Game_IsClient then
3229 begin
3230 if NetPlrUID1 > -1 then
3231 begin
3232 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3233 gPlayer1 := g_Player_Get(NetPlrUID1);
3234 end;
3235 Exit;
3236 end;
3238 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3239 Team := gPlayer1Settings.Team;
3241 // Ñîçäàíèå ïåðâîãî èãðîêà:
3242 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3243 gPlayer1Settings.Color,
3244 Team, False));
3245 if gPlayer1 = nil then
3246 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3247 else
3248 begin
3249 gPlayer1.Name := gPlayer1Settings.Name;
3250 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3251 if g_Game_IsServer and g_Game_IsNet then
3252 MH_SEND_PlayerCreate(gPlayer1.UID);
3253 gPlayer1.Respawn(False, True);
3255 if g_Game_IsNet and NetUseMaster then
3256 g_Net_Slist_Update;
3257 end;
3259 Exit;
3260 end;
3261 if gPlayer2 = nil then
3262 begin
3263 if g_Game_IsClient then
3264 begin
3265 if NetPlrUID2 > -1 then
3266 gPlayer2 := g_Player_Get(NetPlrUID2);
3267 Exit;
3268 end;
3270 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3271 Team := gPlayer2Settings.Team;
3273 // Ñîçäàíèå âòîðîãî èãðîêà:
3274 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3275 gPlayer2Settings.Color,
3276 Team, False));
3277 if gPlayer2 = nil then
3278 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3279 else
3280 begin
3281 gPlayer2.Name := gPlayer2Settings.Name;
3282 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3283 if g_Game_IsServer and g_Game_IsNet then
3284 MH_SEND_PlayerCreate(gPlayer2.UID);
3285 gPlayer2.Respawn(False, True);
3287 if g_Game_IsNet and NetUseMaster then
3288 g_Net_Slist_Update;
3289 end;
3291 Exit;
3292 end;
3293 end;
3295 procedure g_Game_RemovePlayer();
3296 var
3297 Pl: TPlayer;
3298 begin
3299 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3300 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3301 Exit;
3302 Pl := gPlayer2;
3303 if Pl <> nil then
3304 begin
3305 if g_Game_IsServer then
3306 begin
3307 Pl.Lives := 0;
3308 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3309 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3310 g_Player_Remove(Pl.UID);
3312 if g_Game_IsNet and NetUseMaster then
3313 g_Net_Slist_Update;
3314 end else
3315 gPlayer2 := nil;
3316 Exit;
3317 end;
3318 Pl := gPlayer1;
3319 if Pl <> nil then
3320 begin
3321 if g_Game_IsServer then
3322 begin
3323 Pl.Lives := 0;
3324 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3325 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3326 g_Player_Remove(Pl.UID);
3328 if g_Game_IsNet and NetUseMaster then
3329 g_Net_Slist_Update;
3330 end else
3331 begin
3332 gPlayer1 := nil;
3333 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3334 end;
3335 Exit;
3336 end;
3337 end;
3339 procedure g_Game_Spectate();
3340 begin
3341 g_Game_RemovePlayer();
3342 if gPlayer1 <> nil then
3343 g_Game_RemovePlayer();
3344 end;
3346 procedure g_Game_SpectateCenterView();
3347 begin
3348 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3349 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3350 end;
3352 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3353 var
3354 i, nPl: Integer;
3355 begin
3356 g_Game_Free();
3358 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3360 g_Game_ClearLoading();
3362 // Íàñòðîéêè èãðû:
3363 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3364 gAimLine := False;
3365 gShowMap := False;
3366 gGameSettings.GameType := GT_SINGLE;
3367 gGameSettings.MaxLives := 0;
3368 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3369 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3370 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3371 gSwitchGameMode := GM_SINGLE;
3373 g_Game_ExecuteEvent('ongamestart');
3375 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3376 g_Game_SetupScreenSize();
3378 // Ñîçäàíèå ïåðâîãî èãðîêà:
3379 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3380 gPlayer1Settings.Color,
3381 gPlayer1Settings.Team, False));
3382 if gPlayer1 = nil then
3383 begin
3384 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3385 Exit;
3386 end;
3388 gPlayer1.Name := gPlayer1Settings.Name;
3389 nPl := 1;
3391 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3392 if TwoPlayers then
3393 begin
3394 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3395 gPlayer2Settings.Color,
3396 gPlayer2Settings.Team, False));
3397 if gPlayer2 = nil then
3398 begin
3399 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3400 Exit;
3401 end;
3403 gPlayer2.Name := gPlayer2Settings.Name;
3404 Inc(nPl);
3405 end;
3407 // Çàãðóçêà è çàïóñê êàðòû:
3408 if not g_Game_StartMap(MAP, True) then
3409 begin
3410 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3411 Exit;
3412 end;
3414 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3415 g_Player_Init();
3417 // Ñîçäàåì áîòîâ:
3418 for i := nPl+1 to nPlayers do
3419 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3420 end;
3422 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3423 TimeLimit, GoalLimit: Word;
3424 MaxLives: Byte;
3425 Options: LongWord; nPlayers: Byte);
3426 var
3427 i, nPl: Integer;
3428 begin
3429 g_Game_Free();
3431 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3433 g_Game_ClearLoading();
3435 // Íàñòðîéêè èãðû:
3436 gGameSettings.GameType := GT_CUSTOM;
3437 gGameSettings.GameMode := GameMode;
3438 gSwitchGameMode := GameMode;
3439 gGameSettings.TimeLimit := TimeLimit;
3440 gGameSettings.GoalLimit := GoalLimit;
3441 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3442 gGameSettings.Options := Options;
3444 gCoopTotalMonstersKilled := 0;
3445 gCoopTotalSecretsFound := 0;
3446 gCoopTotalMonsters := 0;
3447 gCoopTotalSecrets := 0;
3448 gAimLine := False;
3449 gShowMap := False;
3451 g_Game_ExecuteEvent('ongamestart');
3453 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3454 g_Game_SetupScreenSize();
3456 // Ðåæèì íàáëþäàòåëÿ:
3457 if nPlayers = 0 then
3458 begin
3459 gPlayer1 := nil;
3460 gPlayer2 := nil;
3461 end;
3463 nPl := 0;
3464 if nPlayers >= 1 then
3465 begin
3466 // Ñîçäàíèå ïåðâîãî èãðîêà:
3467 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3468 gPlayer1Settings.Color,
3469 gPlayer1Settings.Team, False));
3470 if gPlayer1 = nil then
3471 begin
3472 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3473 Exit;
3474 end;
3476 gPlayer1.Name := gPlayer1Settings.Name;
3477 Inc(nPl);
3478 end;
3480 if nPlayers >= 2 then
3481 begin
3482 // Ñîçäàíèå âòîðîãî èãðîêà:
3483 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3484 gPlayer2Settings.Color,
3485 gPlayer2Settings.Team, False));
3486 if gPlayer2 = nil then
3487 begin
3488 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3489 Exit;
3490 end;
3492 gPlayer2.Name := gPlayer2Settings.Name;
3493 Inc(nPl);
3494 end;
3496 // Çàãðóçêà è çàïóñê êàðòû:
3497 if not g_Game_StartMap(Map, True) then
3498 begin
3499 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3500 Exit;
3501 end;
3503 // Íåò òî÷åê ïîÿâëåíèÿ:
3504 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3505 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3506 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3507 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3508 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3509 begin
3510 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3511 Exit;
3512 end;
3514 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3515 g_Player_Init();
3517 // Ñîçäàåì áîòîâ:
3518 for i := nPl+1 to nPlayers do
3519 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3520 end;
3522 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3523 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3524 Options: LongWord; nPlayers: Byte;
3525 IPAddr: LongWord; Port: Word);
3526 begin
3527 g_Game_Free();
3529 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3531 g_Game_ClearLoading();
3533 // Íàñòðîéêè èãðû:
3534 gGameSettings.GameType := GT_SERVER;
3535 gGameSettings.GameMode := GameMode;
3536 gSwitchGameMode := GameMode;
3537 gGameSettings.TimeLimit := TimeLimit;
3538 gGameSettings.GoalLimit := GoalLimit;
3539 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3540 gGameSettings.Options := Options;
3542 gCoopTotalMonstersKilled := 0;
3543 gCoopTotalSecretsFound := 0;
3544 gCoopTotalMonsters := 0;
3545 gCoopTotalSecrets := 0;
3546 gAimLine := False;
3547 gShowMap := False;
3549 g_Game_ExecuteEvent('ongamestart');
3551 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3552 g_Game_SetupScreenSize();
3554 // Ðåæèì íàáëþäàòåëÿ:
3555 if nPlayers = 0 then
3556 begin
3557 gPlayer1 := nil;
3558 gPlayer2 := nil;
3559 end;
3561 if nPlayers >= 1 then
3562 begin
3563 // Ñîçäàíèå ïåðâîãî èãðîêà:
3564 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3565 gPlayer1Settings.Color,
3566 gPlayer1Settings.Team, False));
3567 if gPlayer1 = nil then
3568 begin
3569 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3570 Exit;
3571 end;
3573 gPlayer1.Name := gPlayer1Settings.Name;
3574 end;
3576 if nPlayers >= 2 then
3577 begin
3578 // Ñîçäàíèå âòîðîãî èãðîêà:
3579 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3580 gPlayer2Settings.Color,
3581 gPlayer2Settings.Team, False));
3582 if gPlayer2 = nil then
3583 begin
3584 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3585 Exit;
3586 end;
3588 gPlayer2.Name := gPlayer2Settings.Name;
3589 end;
3591 // Ñòàðòóåì ñåðâåð
3592 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3593 begin
3594 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3595 Exit;
3596 end;
3598 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3600 // Çàãðóçêà è çàïóñê êàðòû:
3601 if not g_Game_StartMap(Map, True) then
3602 begin
3603 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3604 Exit;
3605 end;
3607 // Íåò òî÷åê ïîÿâëåíèÿ:
3608 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3609 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3610 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3611 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3612 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3613 begin
3614 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3615 Exit;
3616 end;
3618 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3619 g_Player_Init();
3621 NetState := NET_STATE_GAME;
3622 end;
3624 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3625 var
3626 Map: String;
3627 WadName: string;
3628 Ptr: Pointer;
3629 T: Cardinal;
3630 MID: Byte;
3631 State: Byte;
3632 OuterLoop: Boolean;
3633 newResPath: string;
3634 begin
3635 g_Game_Free();
3637 State := 0;
3638 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3639 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3641 g_Game_ClearLoading();
3643 // Íàñòðîéêè èãðû:
3644 gGameSettings.GameType := GT_CLIENT;
3646 gCoopTotalMonstersKilled := 0;
3647 gCoopTotalSecretsFound := 0;
3648 gCoopTotalMonsters := 0;
3649 gCoopTotalSecrets := 0;
3650 gAimLine := False;
3651 gShowMap := False;
3653 g_Game_ExecuteEvent('ongamestart');
3655 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3656 g_Game_SetupScreenSize();
3658 NetState := NET_STATE_AUTH;
3660 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3661 // Ñòàðòóåì êëèåíò
3662 if not g_Net_Connect(Addr, Port) then
3663 begin
3664 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3665 NetState := NET_STATE_NONE;
3666 Exit;
3667 end;
3669 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3670 MC_SEND_Info(PW);
3671 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3673 OuterLoop := True;
3674 while OuterLoop do
3675 begin
3676 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3677 begin
3678 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3679 begin
3680 Ptr := NetEvent.packet^.data;
3681 e_Raw_Seek(0);
3683 MID := e_Raw_Read_Byte(Ptr);
3685 if (MID = NET_MSG_INFO) and (State = 0) then
3686 begin
3687 NetMyID := e_Raw_Read_Byte(Ptr);
3688 NetPlrUID1 := e_Raw_Read_Word(Ptr);
3690 WadName := e_Raw_Read_String(Ptr);
3691 Map := e_Raw_Read_String(Ptr);
3693 gWADHash := e_Raw_Read_MD5(Ptr);
3695 gGameSettings.GameMode := e_Raw_Read_Byte(Ptr);
3696 gSwitchGameMode := gGameSettings.GameMode;
3697 gGameSettings.GoalLimit := e_Raw_Read_Word(Ptr);
3698 gGameSettings.TimeLimit := e_Raw_Read_Word(Ptr);
3699 gGameSettings.MaxLives := e_Raw_Read_Byte(Ptr);
3700 gGameSettings.Options := e_Raw_Read_LongWord(Ptr);
3701 T := e_Raw_Read_LongWord(Ptr);
3703 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3704 if newResPath = '' then
3705 begin
3706 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3707 newResPath := g_Res_DownloadWAD(WadName);
3708 if newResPath = '' then
3709 begin
3710 g_FatalError(_lc[I_NET_ERR_HASH]);
3711 enet_packet_destroy(NetEvent.packet);
3712 NetState := NET_STATE_NONE;
3713 Exit;
3714 end;
3715 end;
3716 newResPath := ExtractRelativePath(MapsDir, newResPath);
3718 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3719 gPlayer1Settings.Color,
3720 gPlayer1Settings.Team, False));
3722 if gPlayer1 = nil then
3723 begin
3724 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3726 enet_packet_destroy(NetEvent.packet);
3727 NetState := NET_STATE_NONE;
3728 Exit;
3729 end;
3731 gPlayer1.Name := gPlayer1Settings.Name;
3732 gPlayer1.UID := NetPlrUID1;
3733 gPlayer1.Reset(True);
3735 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3736 begin
3737 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3739 enet_packet_destroy(NetEvent.packet);
3740 NetState := NET_STATE_NONE;
3741 Exit;
3742 end;
3744 gTime := T;
3746 State := 1;
3747 OuterLoop := False;
3748 enet_packet_destroy(NetEvent.packet);
3749 break;
3750 end
3751 else
3752 enet_packet_destroy(NetEvent.packet);
3753 end
3754 else
3755 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3756 begin
3757 State := 0;
3758 if (NetEvent.data <= NET_DISC_MAX) then
3759 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3760 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3761 OuterLoop := False;
3762 Break;
3763 end;
3764 end;
3766 ProcessLoading();
3768 e_PollInput();
3770 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3771 begin
3772 State := 0;
3773 break;
3774 end;
3775 end;
3777 if State <> 1 then
3778 begin
3779 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3780 NetState := NET_STATE_NONE;
3781 Exit;
3782 end;
3784 gLMSRespawn := LMS_RESPAWN_NONE;
3785 gLMSRespawnTime := 0;
3787 g_Player_Init();
3788 NetState := NET_STATE_GAME;
3789 MC_SEND_FullStateRequest;
3790 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3791 end;
3793 procedure g_Game_SaveOptions();
3794 begin
3795 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3796 end;
3798 procedure g_Game_ChangeMap(MapPath: String);
3799 var
3800 Force: Boolean;
3801 begin
3802 g_Game_ClearLoading();
3804 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3805 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3806 if gExitByTrigger then
3807 begin
3808 Force := False;
3809 gExitByTrigger := False;
3810 end;
3811 if not g_Game_StartMap(MapPath, Force) then
3812 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
3813 end;
3815 procedure g_Game_Restart();
3816 var
3817 Map: string;
3818 begin
3819 if g_Game_IsClient then
3820 Exit;
3821 map := g_ExtractFileName(gMapInfo.Map);
3823 MessageTime := 0;
3824 gGameOn := False;
3825 g_Game_ClearLoading();
3826 g_Game_StartMap(Map, True);
3827 end;
3829 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
3830 var
3831 NewWAD, ResName: String;
3832 I: Integer;
3833 begin
3834 g_Map_Free();
3835 g_Player_RemoveAllCorpses();
3837 if (not g_Game_IsClient) and
3838 (gSwitchGameMode <> gGameSettings.GameMode) and
3839 (gGameSettings.GameMode <> GM_SINGLE) then
3840 begin
3841 if gSwitchGameMode = GM_CTF then
3842 gGameSettings.MaxLives := 0;
3843 gGameSettings.GameMode := gSwitchGameMode;
3844 Force := True;
3845 end else
3846 gSwitchGameMode := gGameSettings.GameMode;
3848 g_Player_ResetTeams();
3850 if Pos(':\', Map) > 0 then
3851 begin
3852 NewWAD := g_ExtractWadName(Map);
3853 ResName := g_ExtractFileName(Map);
3854 if g_Game_IsServer then
3855 begin
3856 gWADHash := MD5File(MapsDir + NewWAD);
3857 g_Game_LoadWAD(NewWAD);
3858 end else
3859 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
3860 g_Game_ClientWAD(NewWAD, gWADHash);
3861 end else
3862 ResName := Map;
3864 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
3865 if Result then
3866 begin
3867 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
3869 gState := STATE_NONE;
3870 g_ActiveWindow := nil;
3871 gGameOn := True;
3873 DisableCheats();
3874 ResetTimer();
3876 if gGameSettings.GameMode = GM_CTF then
3877 begin
3878 g_Map_ResetFlag(FLAG_RED);
3879 g_Map_ResetFlag(FLAG_BLUE);
3880 // CTF, à ôëàãîâ íåò:
3881 if not g_Map_HaveFlagPoints() then
3882 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
3883 end;
3884 end
3885 else
3886 begin
3887 gState := STATE_MENU;
3888 gGameOn := False;
3889 end;
3891 gExit := 0;
3892 gPause := False;
3893 gTime := 0;
3894 NetTimeToUpdate := 1;
3895 NetTimeToReliable := 0;
3896 NetTimeToMaster := NetMasterRate;
3897 gLMSRespawn := LMS_RESPAWN_NONE;
3898 gLMSRespawnTime := 0;
3899 gMissionFailed := False;
3900 gNextMap := '';
3902 gCoopMonstersKilled := 0;
3903 gCoopSecretsFound := 0;
3905 gVoteInProgress := False;
3906 gVotePassed := False;
3907 gVoteCount := 0;
3908 gVoted := False;
3910 gStatsOff := False;
3912 if not gGameOn then Exit;
3914 g_Game_SpectateCenterView();
3916 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
3917 begin
3918 gLMSRespawn := LMS_RESPAWN_WARMUP;
3919 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
3920 gLMSSoftSpawn := True;
3921 if NetMode = NET_SERVER then
3922 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
3923 else
3924 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
3925 end;
3927 if NetMode = NET_SERVER then
3928 begin
3929 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
3931 // Ìàñòåðñåðâåð
3932 if NetUseMaster then
3933 begin
3934 if (NetMHost = nil) or (NetMPeer = nil) then
3935 if not g_Net_Slist_Connect then
3936 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
3938 g_Net_Slist_Update;
3939 end;
3941 if NetClients <> nil then
3942 for I := 0 to High(NetClients) do
3943 if NetClients[I].Used then
3944 begin
3945 NetClients[I].Voted := False;
3946 if NetClients[I].RequestedFullUpdate then
3947 begin
3948 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
3949 NetClients[I].RequestedFullUpdate := False;
3950 end;
3951 end;
3953 g_Net_UnbanNonPermHosts();
3954 end;
3956 if gLastMap then
3957 begin
3958 gCoopTotalMonstersKilled := 0;
3959 gCoopTotalSecretsFound := 0;
3960 gCoopTotalMonsters := 0;
3961 gCoopTotalSecrets := 0;
3962 gLastMap := False;
3963 end;
3965 g_Game_ExecuteEvent('onmapstart');
3966 end;
3968 procedure SetFirstLevel();
3969 begin
3970 gNextMap := '';
3972 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
3973 if MapList = nil then
3974 Exit;
3976 SortSArray(MapList);
3977 gNextMap := MapList[Low(MapList)];
3979 MapList := nil;
3980 end;
3982 procedure g_Game_ExitLevel(Map: Char16);
3983 begin
3984 gNextMap := Map;
3986 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
3987 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
3988 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
3989 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
3991 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
3992 if gGameSettings.GameType = GT_SINGLE then
3993 gExit := EXIT_ENDLEVELSINGLE
3994 else // Âûøëè â âûõîä â Ñâîåé èãðå
3995 begin
3996 gExit := EXIT_ENDLEVELCUSTOM;
3997 if gGameSettings.GameMode = GM_COOP then
3998 g_Player_RememberAll;
4000 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4001 begin
4002 gLastMap := True;
4003 if gGameSettings.GameMode = GM_COOP then
4004 gStatsOff := True;
4006 gStatsPressed := True;
4007 gNextMap := 'MAP01';
4009 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4010 g_Game_NextLevel;
4012 if g_Game_IsNet then
4013 begin
4014 MH_SEND_GameStats();
4015 MH_SEND_CoopStats();
4016 end;
4017 end;
4018 end;
4019 end;
4021 procedure g_Game_RestartLevel();
4022 var
4023 Map: string;
4024 begin
4025 if gGameSettings.GameMode = GM_SINGLE then
4026 begin
4027 g_Game_Restart();
4028 Exit;
4029 end;
4030 gExit := EXIT_ENDLEVELCUSTOM;
4031 Map := g_ExtractFileName(gMapInfo.Map);
4032 gNextMap := Map;
4033 end;
4035 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4036 var
4037 gWAD: String;
4038 begin
4039 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4040 Exit;
4041 if not g_Game_IsClient then
4042 Exit;
4043 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4044 if gWAD = '' then
4045 begin
4046 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4047 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4048 if gWAD = '' then
4049 begin
4050 g_Game_Free();
4051 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4052 Exit;
4053 end;
4054 end;
4055 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4056 g_Game_LoadWAD(NewWAD);
4057 end;
4059 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4060 var
4061 i, n, nb, nr: Integer;
4062 begin
4063 if not g_Game_IsServer then Exit;
4064 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4065 gLMSRespawn := LMS_RESPAWN_NONE;
4066 gLMSRespawnTime := 0;
4067 MessageTime := 0;
4069 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4070 begin
4071 gMissionFailed := True;
4072 g_Game_RestartLevel;
4073 Exit;
4074 end;
4076 n := 0; nb := 0; nr := 0;
4077 for i := Low(gPlayers) to High(gPlayers) do
4078 if (gPlayers[i] <> nil) and
4079 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4080 (gPlayers[i] is TBot)) then
4081 begin
4082 Inc(n);
4083 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4084 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4085 end;
4087 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4088 begin
4089 // wait a second until the fuckers finally decide to join
4090 gLMSRespawn := LMS_RESPAWN_WARMUP;
4091 gLMSRespawnTime := gTime + 1000;
4092 gLMSSoftSpawn := NoMapRestart;
4093 Exit;
4094 end;
4096 g_Player_RemoveAllCorpses;
4097 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4098 if g_Game_IsNet then
4099 MH_SEND_GameEvent(NET_EV_LMS_START);
4101 for i := Low(gPlayers) to High(gPlayers) do
4102 begin
4103 if gPlayers[i] = nil then continue;
4104 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4105 // don't touch normal spectators
4106 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4107 begin
4108 gPlayers[i].FNoRespawn := True;
4109 gPlayers[i].Lives := 0;
4110 if g_Game_IsNet then
4111 MH_SEND_PlayerStats(gPlayers[I].UID);
4112 continue;
4113 end;
4114 gPlayers[i].FNoRespawn := False;
4115 gPlayers[i].Lives := gGameSettings.MaxLives;
4116 gPlayers[i].Respawn(False, True);
4117 if gGameSettings.GameMode = GM_COOP then
4118 begin
4119 gPlayers[i].Frags := 0;
4120 gPlayers[i].RecallState;
4121 end;
4122 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4123 gPlayer1 := g_Player_Get(gLMSPID1);
4124 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4125 gPlayer2 := g_Player_Get(gLMSPID2);
4126 end;
4128 for i := Low(gItems) to High(gItems) do
4129 begin
4130 if gItems[i].Respawnable then
4131 begin
4132 gItems[i].QuietRespawn := True;
4133 gItems[i].RespawnTime := 0;
4134 end
4135 else
4136 begin
4137 g_Items_Remove(i);
4138 if g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
4139 end;
4140 end;
4142 for i := Low(gMonsters) to High(gMonsters) do
4143 begin
4144 if (gMonsters[i] <> nil) and not gMonsters[i].FNoRespawn then
4145 gMonsters[i].Respawn;
4146 end;
4148 gLMSSoftSpawn := False;
4149 end;
4151 function g_Game_GetFirstMap(WAD: String): String;
4152 begin
4153 Result := '';
4155 MapList := g_Map_GetMapsList(WAD);
4156 if MapList = nil then
4157 Exit;
4159 SortSArray(MapList);
4160 Result := MapList[Low(MapList)];
4162 if not g_Map_Exist(WAD + ':\' + Result) then
4163 Result := '';
4165 MapList := nil;
4166 end;
4168 function g_Game_GetNextMap(): String;
4169 var
4170 I: Integer;
4171 Map: string;
4172 begin
4173 Result := '';
4175 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4176 if MapList = nil then
4177 Exit;
4179 Map := g_ExtractFileName(gMapInfo.Map);
4181 SortSArray(MapList);
4182 MapIndex := -255;
4183 for I := Low(MapList) to High(MapList) do
4184 if Map = MapList[I] then
4185 begin
4186 MapIndex := I;
4187 Break;
4188 end;
4190 if MapIndex <> -255 then
4191 begin
4192 if MapIndex = High(MapList) then
4193 Result := MapList[Low(MapList)]
4194 else
4195 Result := MapList[MapIndex + 1];
4197 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4198 end;
4200 MapList := nil;
4201 end;
4203 procedure g_Game_NextLevel();
4204 begin
4205 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4206 gExit := EXIT_ENDLEVELCUSTOM
4207 else
4208 begin
4209 gExit := EXIT_ENDLEVELSINGLE;
4210 Exit;
4211 end;
4213 if gNextMap <> '' then Exit;
4214 gNextMap := g_Game_GetNextMap();
4215 end;
4217 function g_Game_IsTestMap(): Boolean;
4218 begin
4219 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4220 end;
4222 procedure g_Game_DeleteTestMap();
4223 var
4224 a: Integer;
4225 MapName: Char16;
4226 WadName: string;
4228 WAD: TWADFile;
4229 MapList: SArray;
4230 time: Integer;
4232 begin
4233 a := Pos('.wad:\', gMapToDelete);
4234 if a = 0 then
4235 Exit;
4237 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4238 WadName := Copy(gMapToDelete, 1, a + 3);
4239 Delete(gMapToDelete, 1, a + 5);
4240 gMapToDelete := UpperCase(gMapToDelete);
4241 MapName := '';
4242 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4245 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4246 if MapName <> TEST_MAP_NAME then
4247 Exit;
4249 if not gTempDelete then
4250 begin
4251 time := g_GetFileTime(WadName);
4252 WAD := TWADFile.Create();
4254 // ×èòàåì Wad-ôàéë:
4255 if not WAD.ReadFile(WadName) then
4256 begin // Íåò òàêîãî WAD-ôàéëà
4257 WAD.Free();
4258 Exit;
4259 end;
4261 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4262 WAD.CreateImage();
4263 MapList := WAD.GetResourcesList('');
4265 if MapList <> nil then
4266 for a := 0 to High(MapList) do
4267 if MapList[a] = MapName then
4268 begin
4269 // Óäàëÿåì è ñîõðàíÿåì:
4270 WAD.RemoveResource('', MapName);
4271 WAD.SaveTo(WadName);
4272 Break;
4273 end;
4275 WAD.Free();
4276 g_SetFileTime(WadName, time);
4277 end else
4279 if gTempDelete then DeleteFile(WadName);
4280 end;
4282 procedure GameCVars(P: SArray);
4283 var
4284 a, b: Integer;
4285 stat: TPlayerStatArray;
4286 cmd, s: string;
4287 config: TConfig;
4288 begin
4289 stat := nil;
4290 cmd := LowerCase(P[0]);
4291 if cmd = 'r_showfps' then
4292 begin
4293 if (Length(P) > 1) and
4294 ((P[1] = '1') or (P[1] = '0')) then
4295 gShowFPS := (P[1][1] = '1');
4297 if gShowFPS then
4298 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4299 else
4300 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4301 end
4302 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4303 begin
4304 with gGameSettings do
4305 begin
4306 if (Length(P) > 1) and
4307 ((P[1] = '1') or (P[1] = '0')) then
4308 begin
4309 if (P[1][1] = '1') then
4310 Options := Options or GAME_OPTION_TEAMDAMAGE
4311 else
4312 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4313 end;
4315 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4316 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4317 else
4318 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4320 if g_Game_IsNet then MH_SEND_GameSettings;
4321 end;
4322 end
4323 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4324 begin
4325 with gGameSettings do
4326 begin
4327 if (Length(P) > 1) and
4328 ((P[1] = '1') or (P[1] = '0')) then
4329 begin
4330 if (P[1][1] = '1') then
4331 Options := Options or GAME_OPTION_WEAPONSTAY
4332 else
4333 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4334 end;
4336 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4337 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4338 else
4339 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4341 if g_Game_IsNet then MH_SEND_GameSettings;
4342 end;
4343 end
4344 else if cmd = 'g_gamemode' then
4345 begin
4346 a := g_Game_TextToMode(P[1]);
4347 if a = GM_SINGLE then a := GM_COOP;
4348 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4349 begin
4350 gSwitchGameMode := a;
4351 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4352 (gState = STATE_INTERSINGLE) then
4353 gSwitchGameMode := GM_SINGLE;
4354 if not gGameOn then
4355 gGameSettings.GameMode := gSwitchGameMode;
4356 end;
4357 if gSwitchGameMode = gGameSettings.GameMode then
4358 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4359 [g_Game_ModeToText(gGameSettings.GameMode)]))
4360 else
4361 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4362 [g_Game_ModeToText(gGameSettings.GameMode),
4363 g_Game_ModeToText(gSwitchGameMode)]));
4364 end
4365 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4366 begin
4367 with gGameSettings do
4368 begin
4369 if (Length(P) > 1) and
4370 ((P[1] = '1') or (P[1] = '0')) then
4371 begin
4372 if (P[1][1] = '1') then
4373 Options := Options or GAME_OPTION_ALLOWEXIT
4374 else
4375 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4376 end;
4378 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4379 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4380 else
4381 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4382 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4384 if g_Game_IsNet then MH_SEND_GameSettings;
4385 end;
4386 end
4387 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4388 begin
4389 with gGameSettings do
4390 begin
4391 if (Length(P) > 1) and
4392 ((P[1] = '1') or (P[1] = '0')) then
4393 begin
4394 if (P[1][1] = '1') then
4395 Options := Options or GAME_OPTION_MONSTERS
4396 else
4397 Options := Options and (not GAME_OPTION_MONSTERS);
4398 end;
4400 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4401 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4402 else
4403 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4404 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4406 if g_Game_IsNet then MH_SEND_GameSettings;
4407 end;
4408 end
4409 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4410 begin
4411 with gGameSettings do
4412 begin
4413 if (Length(P) > 1) and
4414 ((P[1] = '1') or (P[1] = '0')) then
4415 begin
4416 if (P[1][1] = '1') then
4417 Options := Options or GAME_OPTION_BOTVSPLAYER
4418 else
4419 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4420 end;
4422 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4423 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4424 else
4425 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4427 if g_Game_IsNet then MH_SEND_GameSettings;
4428 end;
4429 end
4430 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4431 begin
4432 with gGameSettings do
4433 begin
4434 if (Length(P) > 1) and
4435 ((P[1] = '1') or (P[1] = '0')) then
4436 begin
4437 if (P[1][1] = '1') then
4438 Options := Options or GAME_OPTION_BOTVSMONSTER
4439 else
4440 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4441 end;
4443 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4444 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4445 else
4446 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4448 if g_Game_IsNet then MH_SEND_GameSettings;
4449 end;
4450 end
4451 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4452 begin
4453 if Length(P) > 1 then
4454 begin
4455 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4456 gGameSettings.WarmupTime := 30
4457 else
4458 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4459 end;
4461 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4462 [gGameSettings.WarmupTime]));
4463 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4464 end
4465 else if cmd = 'net_interp' then
4466 begin
4467 if (Length(P) > 1) then
4468 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4470 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4471 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4472 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4473 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4474 config.Free();
4475 end
4476 else if cmd = 'net_forceplayerupdate' then
4477 begin
4478 if (Length(P) > 1) and
4479 ((P[1] = '1') or (P[1] = '0')) then
4480 NetForcePlayerUpdate := (P[1][1] = '1');
4482 if NetForcePlayerUpdate then
4483 g_Console_Add('net_forceplayerupdate = 1')
4484 else
4485 g_Console_Add('net_forceplayerupdate = 0');
4486 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4487 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4488 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4489 config.Free();
4490 end
4491 else if cmd = 'net_predictself' then
4492 begin
4493 if (Length(P) > 1) and
4494 ((P[1] = '1') or (P[1] = '0')) then
4495 NetPredictSelf := (P[1][1] = '1');
4497 if NetPredictSelf then
4498 g_Console_Add('net_predictself = 1')
4499 else
4500 g_Console_Add('net_predictself = 0');
4501 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4502 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4503 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4504 config.Free();
4505 end
4506 else if cmd = 'sv_name' then
4507 begin
4508 if (Length(P) > 1) and (Length(P[1]) > 0) then
4509 begin
4510 NetServerName := P[1];
4511 if Length(NetServerName) > 64 then
4512 SetLength(NetServerName, 64);
4513 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4514 g_Net_Slist_Update;
4515 end;
4517 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4518 end
4519 else if cmd = 'sv_passwd' then
4520 begin
4521 if (Length(P) > 1) and (Length(P[1]) > 0) then
4522 begin
4523 NetPassword := P[1];
4524 if Length(NetPassword) > 24 then
4525 SetLength(NetPassword, 24);
4526 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4527 g_Net_Slist_Update;
4528 end;
4530 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4531 end
4532 else if cmd = 'sv_maxplrs' then
4533 begin
4534 if (Length(P) > 1) then
4535 begin
4536 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4537 if g_Game_IsServer and g_Game_IsNet then
4538 begin
4539 b := 0;
4540 for a := 0 to High(NetClients) do
4541 if NetClients[a].Used then
4542 begin
4543 Inc(b);
4544 if b > NetMaxClients then
4545 begin
4546 s := g_Player_Get(NetClients[a].Player).Name;
4547 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4548 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4549 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4550 end;
4551 end;
4552 if NetUseMaster then
4553 g_Net_Slist_Update;
4554 end;
4555 end;
4557 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4558 end
4559 else if cmd = 'sv_public' then
4560 begin
4561 if (Length(P) > 1) then
4562 begin
4563 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4564 if g_Game_IsServer and g_Game_IsNet then
4565 if NetUseMaster then
4566 begin
4567 if NetMPeer = nil then
4568 if not g_Net_Slist_Connect() then
4569 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4570 g_Net_Slist_Update();
4571 end
4572 else
4573 if NetMPeer <> nil then
4574 g_Net_Slist_Disconnect();
4575 end;
4577 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4578 end
4579 else if cmd = 'sv_intertime' then
4580 begin
4581 if (Length(P) > 1) then
4582 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4584 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4585 end
4586 else if cmd = 'p1_name' then
4587 begin
4588 if (Length(P) > 1) and gGameOn then
4589 begin
4590 if g_Game_IsClient then
4591 begin
4592 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4593 MC_SEND_PlayerSettings;
4594 end
4595 else
4596 if gPlayer1 <> nil then
4597 begin
4598 gPlayer1.Name := b_Text_Unformat(P[1]);
4599 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4600 end
4601 else
4602 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4603 end;
4604 end
4605 else if cmd = 'p2_name' then
4606 begin
4607 if (Length(P) > 1) and gGameOn then
4608 begin
4609 if g_Game_IsClient then
4610 begin
4611 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4612 MC_SEND_PlayerSettings;
4613 end
4614 else
4615 if gPlayer2 <> nil then
4616 begin
4617 gPlayer2.Name := b_Text_Unformat(P[1]);
4618 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4619 end
4620 else
4621 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4622 end;
4623 end
4624 else if cmd = 'p1_color' then
4625 begin
4626 if Length(P) > 3 then
4627 if g_Game_IsClient then
4628 begin
4629 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4630 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4631 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4632 MC_SEND_PlayerSettings;
4633 end
4634 else
4635 if gPlayer1 <> nil then
4636 begin
4637 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4638 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4639 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4640 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4641 end
4642 else
4643 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4644 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4645 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4646 end
4647 else if (cmd = 'p2_color') and not g_Game_IsNet then
4648 begin
4649 if Length(P) > 3 then
4650 if g_Game_IsClient then
4651 begin
4652 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4653 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4654 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4655 MC_SEND_PlayerSettings;
4656 end
4657 else
4658 if gPlayer2 <> nil then
4659 begin
4660 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4661 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4662 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4663 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4664 end
4665 else
4666 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4667 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4668 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4669 end
4670 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4671 begin
4672 if cmd = 'r_showtime' then
4673 begin
4674 if (Length(P) > 1) and
4675 ((P[1] = '1') or (P[1] = '0')) then
4676 gShowTime := (P[1][1] = '1');
4678 if gShowTime then
4679 g_Console_Add(_lc[I_MSG_TIME_ON])
4680 else
4681 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4682 end
4683 else if cmd = 'r_showscore' then
4684 begin
4685 if (Length(P) > 1) and
4686 ((P[1] = '1') or (P[1] = '0')) then
4687 gShowGoals := (P[1][1] = '1');
4689 if gShowGoals then
4690 g_Console_Add(_lc[I_MSG_SCORE_ON])
4691 else
4692 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4693 end
4694 else if cmd = 'r_showstat' then
4695 begin
4696 if (Length(P) > 1) and
4697 ((P[1] = '1') or (P[1] = '0')) then
4698 gShowStat := (P[1][1] = '1');
4700 if gShowStat then
4701 g_Console_Add(_lc[I_MSG_STATS_ON])
4702 else
4703 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4704 end
4705 else if cmd = 'r_showkillmsg' then
4706 begin
4707 if (Length(P) > 1) and
4708 ((P[1] = '1') or (P[1] = '0')) then
4709 gShowKillMsg := (P[1][1] = '1');
4711 if gShowKillMsg then
4712 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4713 else
4714 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4715 end
4716 else if cmd = 'r_showlives' then
4717 begin
4718 if (Length(P) > 1) and
4719 ((P[1] = '1') or (P[1] = '0')) then
4720 gShowLives := (P[1][1] = '1');
4722 if gShowLives then
4723 g_Console_Add(_lc[I_MSG_LIVES_ON])
4724 else
4725 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4726 end
4727 else if cmd = 'r_showspect' then
4728 begin
4729 if (Length(P) > 1) and
4730 ((P[1] = '1') or (P[1] = '0')) then
4731 gSpectHUD := (P[1][1] = '1');
4733 if gSpectHUD then
4734 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4735 else
4736 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4737 end
4738 else if cmd = 'r_showping' then
4739 begin
4740 if (Length(P) > 1) and
4741 ((P[1] = '1') or (P[1] = '0')) then
4742 gShowPing := (P[1][1] = '1');
4744 if gShowPing then
4745 g_Console_Add(_lc[I_MSG_PING_ON])
4746 else
4747 g_Console_Add(_lc[I_MSG_PING_OFF]);
4748 end
4749 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4750 begin
4751 if Length(P) > 1 then
4752 begin
4753 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4754 gGameSettings.GoalLimit := 0
4755 else
4756 begin
4757 b := 0;
4759 if gGameSettings.GameMode = GM_DM then
4760 begin // DM
4761 stat := g_Player_GetStats();
4762 if stat <> nil then
4763 for a := 0 to High(stat) do
4764 if stat[a].Frags > b then
4765 b := stat[a].Frags;
4766 end
4767 else // TDM/CTF
4768 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4770 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4771 end;
4773 if g_Game_IsNet then MH_SEND_GameSettings;
4774 end;
4776 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4777 end
4778 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4779 begin
4780 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4781 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4783 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4784 [gGameSettings.TimeLimit div 3600,
4785 (gGameSettings.TimeLimit div 60) mod 60,
4786 gGameSettings.TimeLimit mod 60]));
4787 if g_Game_IsNet then MH_SEND_GameSettings;
4788 end
4789 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4790 begin
4791 if Length(P) > 1 then
4792 begin
4793 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4794 gGameSettings.MaxLives := 0
4795 else
4796 begin
4797 b := 0;
4798 stat := g_Player_GetStats();
4799 if stat <> nil then
4800 for a := 0 to High(stat) do
4801 if stat[a].Lives > b then
4802 b := stat[a].Lives;
4803 gGameSettings.MaxLives :=
4804 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4805 end;
4806 end;
4808 g_Console_Add(Format(_lc[I_MSG_LIVES],
4809 [gGameSettings.MaxLives]));
4810 if g_Game_IsNet then MH_SEND_GameSettings;
4811 end;
4812 end;
4813 end;
4815 procedure DebugCommands(P: SArray);
4816 var
4817 a, b: Integer;
4818 cmd: string;
4819 //pt: TPoint;
4820 begin
4821 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
4822 if gDebugMode then
4823 begin
4824 cmd := LowerCase(P[0]);
4825 if cmd = 'd_window' then
4826 begin
4827 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
4828 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
4829 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
4830 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
4831 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
4832 end
4833 else if cmd = 'd_sounds' then
4834 begin
4835 if (Length(P) > 1) and
4836 ((P[1] = '1') or (P[1] = '0')) then
4837 g_Debug_Sounds := (P[1][1] = '1');
4839 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
4840 end
4841 else if cmd = 'd_frames' then
4842 begin
4843 if (Length(P) > 1) and
4844 ((P[1] = '1') or (P[1] = '0')) then
4845 g_Debug_Frames := (P[1][1] = '1');
4847 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
4848 end
4849 else if cmd = 'd_winmsg' then
4850 begin
4851 if (Length(P) > 1) and
4852 ((P[1] = '1') or (P[1] = '0')) then
4853 g_Debug_WinMsgs := (P[1][1] = '1');
4855 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
4856 end
4857 else if (cmd = 'd_monoff') and not g_Game_IsNet then
4858 begin
4859 if (Length(P) > 1) and
4860 ((P[1] = '1') or (P[1] = '0')) then
4861 g_Debug_MonsterOff := (P[1][1] = '1');
4863 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
4864 end
4865 else if (cmd = 'd_botoff') and not g_Game_IsNet then
4866 begin
4867 if Length(P) > 1 then
4868 case P[1][1] of
4869 '0': g_debug_BotAIOff := 0;
4870 '1': g_debug_BotAIOff := 1;
4871 '2': g_debug_BotAIOff := 2;
4872 '3': g_debug_BotAIOff := 3;
4873 end;
4875 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
4876 end
4877 else if cmd = 'd_monster' then
4878 begin
4879 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
4880 if Length(P) < 2 then
4881 begin
4882 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
4883 g_Console_Add('ID | Name');
4884 for b := MONSTER_DEMON to MONSTER_MAN do
4885 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
4886 end else
4887 begin
4888 a := StrToIntDef(P[1], 0);
4889 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4890 a := g_Monsters_GetIDByName(P[1]);
4892 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
4893 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
4894 else
4895 begin
4896 with gPlayer1.Obj do
4897 b := g_Monsters_Create(a,
4898 X + Rect.X + (Rect.Width div 2),
4899 Y + Rect.Y + Rect.Height,
4900 gPlayer1.Direction, True);
4901 if (Length(P) > 2) and (b >= 0) then
4902 gMonsters[b].MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
4903 end;
4904 end;
4905 end
4906 else if (cmd = 'd_health') then
4907 begin
4908 if (Length(P) > 1) and
4909 ((P[1] = '1') or (P[1] = '0')) then
4910 g_debug_HealthBar := (P[1][1] = '1');
4912 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
4913 end
4914 else if (cmd = 'd_player') then
4915 begin
4916 if (Length(P) > 1) and
4917 ((P[1] = '1') or (P[1] = '0')) then
4918 g_debug_Player := (P[1][1] = '1');
4920 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
4921 end
4922 else if (cmd = 'd_joy') then
4923 begin
4924 for a := 1 to 8 do
4925 g_Console_Add(e_JoystickStateToString(a));
4926 end;
4927 end
4928 else
4929 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
4930 end;
4933 procedure GameCheats(P: SArray);
4934 var
4935 cmd: string;
4936 f, a: Integer;
4937 plr: TPlayer;
4938 begin
4939 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
4940 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
4941 begin
4942 g_Console_Add('not available');
4943 exit;
4944 end;
4945 plr := gPlayer1;
4946 if plr = nil then
4947 begin
4948 g_Console_Add('where is the player?!');
4949 exit;
4950 end;
4951 cmd := LowerCase(P[0]);
4952 // god
4953 if cmd = 'god' then
4954 begin
4955 plr.GodMode := not plr.GodMode;
4956 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
4957 exit;
4958 end;
4959 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
4960 if cmd = 'give' then
4961 begin
4962 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
4963 for f := 1 to High(P) do
4964 begin
4965 cmd := LowerCase(P[f]);
4966 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
4967 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
4968 if cmd = 'exit' then
4969 begin
4970 if gTriggers <> nil then
4971 begin
4972 for a := 0 to High(gTriggers) do
4973 begin
4974 if gTriggers[a].TriggerType = TRIGGER_EXIT then
4975 begin
4976 g_Console_Add('player left the map');
4977 gExitByTrigger := True;
4978 g_Game_ExitLevel(gTriggers[a].Data.MapName);
4979 break;
4980 end;
4981 end;
4982 end;
4983 continue;
4984 end;
4986 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
4987 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
4988 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
4989 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
4990 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
4992 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
4993 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
4995 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
4996 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;
4998 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
4999 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5001 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5002 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5004 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5005 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5007 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5008 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5009 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5011 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5012 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5013 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5014 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;
5015 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5016 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5018 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;
5019 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;
5020 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;
5021 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;
5022 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;
5023 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;
5025 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5026 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;
5028 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;
5029 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;
5031 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5033 if cmd = 'ammo' then
5034 begin
5035 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5036 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5037 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5038 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5039 plr.GiveItem(ITEM_AMMO_FUELCAN);
5040 g_Console_Add('player got some ammo');
5041 continue;
5042 end;
5044 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5045 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5047 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5048 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5050 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5051 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5053 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5054 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5056 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5058 if cmd = 'weapons' then
5059 begin
5060 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5061 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5062 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5063 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5064 plr.GiveItem(ITEM_WEAPON_PLASMA);
5065 plr.GiveItem(ITEM_WEAPON_BFG);
5066 g_Console_Add('player got weapons');
5067 continue;
5068 end;
5070 if cmd = 'keys' then
5071 begin
5072 plr.GiveItem(ITEM_KEY_RED);
5073 plr.GiveItem(ITEM_KEY_GREEN);
5074 plr.GiveItem(ITEM_KEY_BLUE);
5075 g_Console_Add('player got all keys');
5076 continue;
5077 end;
5079 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5080 end;
5081 exit;
5082 end;
5083 // open
5084 if cmd = 'open' then
5085 begin
5086 g_Console_Add('player activated sesame');
5087 g_Triggers_OpenAll();
5088 exit;
5089 end;
5090 // fly
5091 if cmd = 'fly' then
5092 begin
5093 gFly := not gFly;
5094 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5095 exit;
5096 end;
5097 // noclip
5098 if cmd = 'noclip' then
5099 begin
5100 plr.SwitchNoClip;
5101 g_Console_Add('wall hardeness adjusted');
5102 exit;
5103 end;
5104 // notarget
5105 if cmd = 'notarget' then
5106 begin
5107 plr.NoTarget := not plr.NoTarget;
5108 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5109 exit;
5110 end;
5111 // noreload
5112 if cmd = 'noreload' then
5113 begin
5114 plr.NoReload := not plr.NoReload;
5115 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5116 exit;
5117 end;
5118 // speedy
5119 if cmd = 'speedy' then
5120 begin
5121 MAX_RUNVEL := 32-MAX_RUNVEL;
5122 g_Console_Add('speed adjusted');
5123 exit;
5124 end;
5125 // jumpy
5126 if cmd = 'jumpy' then
5127 begin
5128 VEL_JUMP := 30-VEL_JUMP;
5129 g_Console_Add('jump height adjusted');
5130 exit;
5131 end;
5132 // automap
5133 if cmd = 'automap' then
5134 begin
5135 gShowMap := not gShowMap;
5136 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5137 exit;
5138 end;
5139 // aimline
5140 if cmd = 'aimline' then
5141 begin
5142 gAimLine := not gAimLine;
5143 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5144 exit;
5145 end;
5146 end;
5148 procedure GameCommands(P: SArray);
5149 var
5150 a, b: Integer;
5151 s, pw: String;
5152 chstr: string;
5153 cmd: string;
5154 pl: pTNetClient = nil;
5155 plr: TPlayer;
5156 prt: Word;
5157 nm: Boolean;
5158 listen: LongWord;
5159 begin
5160 // Îáùèå êîìàíäû:
5161 cmd := LowerCase(P[0]);
5162 chstr := '';
5163 if (cmd = 'quit') or
5164 (cmd = 'exit') then
5165 begin
5166 g_Game_Free();
5167 g_Game_Quit();
5168 Exit;
5169 end
5170 else if cmd = 'pause' then
5171 begin
5172 if (g_ActiveWindow = nil) then
5173 g_Game_Pause(not gPause);
5174 end
5175 else if cmd = 'endgame' then
5176 gExit := EXIT_SIMPLE
5177 else if cmd = 'restart' then
5178 begin
5179 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5180 begin
5181 if g_Game_IsClient then
5182 begin
5183 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5184 Exit;
5185 end;
5186 g_Game_Restart();
5187 end else
5188 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5189 end
5190 else if cmd = 'kick' then
5191 begin
5192 if g_Game_IsServer then
5193 begin
5194 if Length(P) < 2 then
5195 begin
5196 g_Console_Add('kick <name>');
5197 Exit;
5198 end;
5199 if P[1] = '' then
5200 begin
5201 g_Console_Add('kick <name>');
5202 Exit;
5203 end;
5205 if g_Game_IsNet then
5206 pl := g_Net_Client_ByName(P[1]);
5207 if (pl <> nil) then
5208 begin
5209 s := g_Net_ClientName_ByID(pl^.ID);
5210 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5211 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5212 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5213 if NetUseMaster then
5214 g_Net_Slist_Update;
5215 end else if gPlayers <> nil then
5216 for a := Low(gPlayers) to High(gPlayers) do
5217 if gPlayers[a] <> nil then
5218 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5219 begin
5220 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5221 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5222 continue;
5223 gPlayers[a].Lives := 0;
5224 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5225 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5226 g_Player_Remove(gPlayers[a].UID);
5227 if NetUseMaster then
5228 g_Net_Slist_Update;
5229 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5230 g_Bot_MixNames();
5231 end;
5232 end else
5233 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5234 end
5235 else if cmd = 'kick_id' then
5236 begin
5237 if g_Game_IsServer and g_Game_IsNet then
5238 begin
5239 if Length(P) < 2 then
5240 begin
5241 g_Console_Add('kick_id <client ID>');
5242 Exit;
5243 end;
5244 if P[1] = '' then
5245 begin
5246 g_Console_Add('kick_id <client ID>');
5247 Exit;
5248 end;
5250 a := StrToIntDef(P[1], 0);
5251 if (NetClients <> nil) and (a <= High(NetClients)) then
5252 begin
5253 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5254 begin
5255 s := g_Net_ClientName_ByID(NetClients[a].ID);
5256 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5257 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5258 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5259 if NetUseMaster then
5260 g_Net_Slist_Update;
5261 end;
5262 end;
5263 end else
5264 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5265 end
5266 else if cmd = 'ban' then
5267 begin
5268 if g_Game_IsServer and g_Game_IsNet then
5269 begin
5270 if Length(P) < 2 then
5271 begin
5272 g_Console_Add('ban <name>');
5273 Exit;
5274 end;
5275 if P[1] = '' then
5276 begin
5277 g_Console_Add('ban <name>');
5278 Exit;
5279 end;
5281 pl := g_Net_Client_ByName(P[1]);
5282 if (pl <> nil) then
5283 begin
5284 s := g_Net_ClientName_ByID(pl^.ID);
5285 g_Net_BanHost(pl^.Peer^.address.host, False);
5286 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5287 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5288 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5289 if NetUseMaster then
5290 g_Net_Slist_Update;
5291 end else
5292 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5293 end else
5294 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5295 end
5296 else if cmd = 'ban_id' then
5297 begin
5298 if g_Game_IsServer and g_Game_IsNet then
5299 begin
5300 if Length(P) < 2 then
5301 begin
5302 g_Console_Add('ban_id <client ID>');
5303 Exit;
5304 end;
5305 if P[1] = '' then
5306 begin
5307 g_Console_Add('ban_id <client ID>');
5308 Exit;
5309 end;
5311 a := StrToIntDef(P[1], 0);
5312 if (NetClients <> nil) and (a <= High(NetClients)) then
5313 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5314 begin
5315 s := g_Net_ClientName_ByID(NetClients[a].ID);
5316 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5317 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5318 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5319 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5320 if NetUseMaster then
5321 g_Net_Slist_Update;
5322 end;
5323 end else
5324 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5325 end
5326 else if cmd = 'permban' then
5327 begin
5328 if g_Game_IsServer and g_Game_IsNet then
5329 begin
5330 if Length(P) < 2 then
5331 begin
5332 g_Console_Add('permban <name>');
5333 Exit;
5334 end;
5335 if P[1] = '' then
5336 begin
5337 g_Console_Add('permban <name>');
5338 Exit;
5339 end;
5341 pl := g_Net_Client_ByName(P[1]);
5342 if (pl <> nil) then
5343 begin
5344 s := g_Net_ClientName_ByID(pl^.ID);
5345 g_Net_BanHost(pl^.Peer^.address.host);
5346 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5347 g_Net_SaveBanList();
5348 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5349 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5350 if NetUseMaster then
5351 g_Net_Slist_Update;
5352 end else
5353 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5354 end else
5355 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5356 end
5357 else if cmd = 'permban_id' then
5358 begin
5359 if g_Game_IsServer and g_Game_IsNet then
5360 begin
5361 if Length(P) < 2 then
5362 begin
5363 g_Console_Add('permban_id <client ID>');
5364 Exit;
5365 end;
5366 if P[1] = '' then
5367 begin
5368 g_Console_Add('permban_id <client ID>');
5369 Exit;
5370 end;
5372 a := StrToIntDef(P[1], 0);
5373 if (NetClients <> nil) and (a <= High(NetClients)) then
5374 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5375 begin
5376 s := g_Net_ClientName_ByID(NetClients[a].ID);
5377 g_Net_BanHost(NetClients[a].Peer^.address.host);
5378 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5379 g_Net_SaveBanList();
5380 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5381 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5382 if NetUseMaster then
5383 g_Net_Slist_Update;
5384 end;
5385 end else
5386 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5387 end
5388 else if cmd = 'unban' then
5389 begin
5390 if g_Game_IsServer and g_Game_IsNet then
5391 begin
5392 if Length(P) < 2 then
5393 begin
5394 g_Console_Add('unban <IP Address>');
5395 Exit;
5396 end;
5397 if P[1] = '' then
5398 begin
5399 g_Console_Add('unban <IP Address>');
5400 Exit;
5401 end;
5403 if g_Net_UnbanHost(P[1]) then
5404 begin
5405 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5406 g_Net_SaveBanList();
5407 end else
5408 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5409 end else
5410 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5411 end
5412 else if cmd = 'clientlist' then
5413 begin
5414 if g_Game_IsServer and g_Game_IsNet then
5415 begin
5416 b := 0;
5417 if NetClients <> nil then
5418 for a := Low(NetClients) to High(NetClients) do
5419 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5420 begin
5421 plr := g_Player_Get(NetClients[a].Player);
5422 if plr = nil then continue;
5423 Inc(b);
5424 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5425 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5426 end;
5427 if b = 0 then
5428 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5429 end else
5430 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5431 end
5432 else if cmd = 'connect' then
5433 begin
5434 if (NetMode = NET_NONE) then
5435 begin
5436 if Length(P) < 2 then
5437 begin
5438 g_Console_Add('connect <IP> [port] [password]');
5439 Exit;
5440 end;
5441 if P[1] = '' then
5442 begin
5443 g_Console_Add('connect <IP> [port] [password]');
5444 Exit;
5445 end;
5447 if Length(P) > 2 then
5448 prt := StrToIntDef(P[2], 25666)
5449 else
5450 prt := 25666;
5452 if Length(P) > 3 then
5453 pw := P[3]
5454 else
5455 pw := '';
5457 g_Game_StartClient(P[1], prt, pw);
5458 end;
5459 end
5460 else if cmd = 'disconnect' then
5461 begin
5462 if (NetMode = NET_CLIENT) then
5463 g_Net_Disconnect();
5464 end
5465 else if cmd = 'reconnect' then
5466 begin
5467 if (NetMode = NET_SERVER) then
5468 Exit;
5470 if (NetMode = NET_CLIENT) then
5471 begin
5472 g_Net_Disconnect();
5473 gExit := EXIT_SIMPLE;
5474 EndGame;
5475 end;
5477 //TODO: Use last successful password to reconnect, instead of ''
5478 g_Game_StartClient(NetClientIP, NetClientPort, '');
5479 end
5480 else if (cmd = 'addbot') or
5481 (cmd = 'bot_add') then
5482 begin
5483 if Length(P) > 1 then
5484 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5485 else
5486 g_Bot_Add(TEAM_NONE, 2);
5487 end
5488 else if cmd = 'bot_addlist' then
5489 begin
5490 if Length(P) > 1 then
5491 if Length(P) = 2 then
5492 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5493 else
5494 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5495 end
5496 else if cmd = 'bot_removeall' then
5497 g_Bot_RemoveAll()
5498 else if cmd = 'chat' then
5499 begin
5500 if g_Game_IsNet then
5501 begin
5502 if Length(P) > 1 then
5503 begin
5504 for a := 1 to High(P) do
5505 chstr := chstr + P[a] + ' ';
5507 if Length(chstr) > 200 then SetLength(chstr, 200);
5509 if Length(chstr) < 1 then
5510 begin
5511 g_Console_Add('chat <text>');
5512 Exit;
5513 end;
5515 chstr := b_Text_Format(chstr);
5516 if g_Game_IsClient then
5517 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5518 else
5519 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5520 end
5521 else
5522 g_Console_Add('chat <text>');
5523 end else
5524 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5525 end
5526 else if cmd = 'teamchat' then
5527 begin
5528 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5529 begin
5530 if Length(P) > 1 then
5531 begin
5532 for a := 1 to High(P) do
5533 chstr := chstr + P[a] + ' ';
5535 if Length(chstr) > 200 then SetLength(chstr, 200);
5537 if Length(chstr) < 1 then
5538 begin
5539 g_Console_Add('teamchat <text>');
5540 Exit;
5541 end;
5543 chstr := b_Text_Format(chstr);
5544 if g_Game_IsClient then
5545 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5546 else
5547 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5548 gPlayer1Settings.Team);
5549 end
5550 else
5551 g_Console_Add('teamchat <text>');
5552 end else
5553 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5554 end
5555 else if cmd = 'game' then
5556 begin
5557 if gGameSettings.GameType <> GT_NONE then
5558 begin
5559 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5560 Exit;
5561 end;
5562 if Length(P) = 1 then
5563 begin
5564 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5565 Exit;
5566 end;
5567 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5568 P[1] := addWadExtension(P[1]);
5569 if FileExists(MapsDir + P[1]) then
5570 begin
5571 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5572 if Length(P) < 3 then
5573 begin
5574 SetLength(P, 3);
5575 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5576 end;
5578 s := P[1] + ':\' + UpperCase(P[2]);
5580 if g_Map_Exist(MapsDir + s) then
5581 begin
5582 // Çàïóñêàåì ñâîþ èãðó
5583 g_Game_Free();
5584 with gGameSettings do
5585 begin
5586 GameMode := g_Game_TextToMode(gcGameMode);
5587 if gSwitchGameMode <> GM_NONE then
5588 GameMode := gSwitchGameMode;
5589 if GameMode = GM_NONE then GameMode := GM_DM;
5590 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5591 b := 1;
5592 if Length(P) >= 4 then
5593 b := StrToIntDef(P[3], 1);
5594 g_Game_StartCustom(s, GameMode, TimeLimit,
5595 GoalLimit, MaxLives, Options, b);
5596 end;
5597 end
5598 else
5599 if P[2] = '' then
5600 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5601 else
5602 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5603 end else
5604 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5605 end
5606 else if cmd = 'host' then
5607 begin
5608 if gGameSettings.GameType <> GT_NONE then
5609 begin
5610 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5611 Exit;
5612 end;
5613 if Length(P) < 4 then
5614 begin
5615 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5616 Exit;
5617 end;
5618 if not StrToIp(P[1], listen) then
5619 Exit;
5620 prt := StrToIntDef(P[2], 25666);
5622 P[3] := addWadExtension(P[3]);
5623 if FileExists(MapsDir + P[3]) then
5624 begin
5625 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5626 if Length(P) < 5 then
5627 begin
5628 SetLength(P, 5);
5629 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5630 end;
5632 s := P[3] + ':\' + UpperCase(P[4]);
5634 if g_Map_Exist(MapsDir + s) then
5635 begin
5636 // Çàïóñêàåì ñâîþ èãðó
5637 g_Game_Free();
5638 with gGameSettings do
5639 begin
5640 GameMode := g_Game_TextToMode(gcGameMode);
5641 if gSwitchGameMode <> GM_NONE then
5642 GameMode := gSwitchGameMode;
5643 if GameMode = GM_NONE then GameMode := GM_DM;
5644 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5645 b := 0;
5646 if Length(P) >= 6 then
5647 b := StrToIntDef(P[5], 0);
5648 g_Game_StartServer(s, GameMode, TimeLimit,
5649 GoalLimit, MaxLives, Options, b, listen, prt);
5650 end;
5651 end
5652 else
5653 if P[4] = '' then
5654 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5655 else
5656 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5657 end else
5658 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5659 end
5660 else if cmd = 'map' then
5661 begin
5662 if Length(P) = 1 then
5663 begin
5664 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5665 begin
5666 g_Console_Add(cmd + ' <MAP>');
5667 g_Console_Add(cmd + ' <WAD> [MAP]');
5668 end else
5669 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5670 end else
5671 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5672 begin
5673 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5674 if Length(P) < 3 then
5675 begin
5676 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5677 s := UpperCase(P[1]);
5678 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5679 begin // Êàðòà íàøëàñü
5680 gExitByTrigger := False;
5681 if gGameOn then
5682 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5683 gNextMap := s;
5684 gExit := EXIT_ENDLEVELCUSTOM;
5685 end
5686 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5687 g_Game_ChangeMap(s);
5688 end else
5689 begin
5690 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5691 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5692 P[1] := addWadExtension(P[1]);
5693 if FileExists(MapsDir + P[1]) then
5694 begin
5695 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5696 SetLength(P, 3);
5697 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5699 s := P[1] + ':\' + P[2];
5701 if g_Map_Exist(MapsDir + s) then
5702 begin
5703 gExitByTrigger := False;
5704 if gGameOn then
5705 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5706 gNextMap := s;
5707 gExit := EXIT_ENDLEVELCUSTOM;
5708 end
5709 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5710 g_Game_ChangeMap(s);
5711 end else
5712 if P[2] = '' then
5713 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5714 else
5715 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5716 end else
5717 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5718 end;
5719 end else
5720 begin
5721 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5722 P[1] := addWadExtension(P[1]);
5723 if FileExists(MapsDir + P[1]) then
5724 begin
5725 // Íàøëè WAD ôàéë
5726 P[2] := UpperCase(P[2]);
5727 s := P[1] + ':\' + P[2];
5729 if g_Map_Exist(MapsDir + s) then
5730 begin // Íàøëè êàðòó
5731 gExitByTrigger := False;
5732 if gGameOn then
5733 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5734 gNextMap := s;
5735 gExit := EXIT_ENDLEVELCUSTOM;
5736 end
5737 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5738 g_Game_ChangeMap(s);
5739 end else
5740 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5741 end else
5742 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5743 end;
5744 end else
5745 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5746 end
5747 else if cmd = 'nextmap' then
5748 begin
5749 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5750 g_Console_Add(_lc[I_MSG_NOT_GAME])
5751 else begin
5752 nm := True;
5753 if Length(P) = 1 then
5754 begin
5755 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5756 begin
5757 g_Console_Add(cmd + ' <MAP>');
5758 g_Console_Add(cmd + ' <WAD> [MAP]');
5759 end else begin
5760 nm := False;
5761 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5762 end;
5763 end else
5764 begin
5765 nm := False;
5766 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5767 begin
5768 if Length(P) < 3 then
5769 begin
5770 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5771 s := UpperCase(P[1]);
5772 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5773 begin // Êàðòà íàøëàñü
5774 gExitByTrigger := False;
5775 gNextMap := s;
5776 nm := True;
5777 end else
5778 begin
5779 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5780 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5781 P[1] := addWadExtension(P[1]);
5782 if FileExists(MapsDir + P[1]) then
5783 begin
5784 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5785 SetLength(P, 3);
5786 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5788 s := P[1] + ':\' + P[2];
5790 if g_Map_Exist(MapsDir + s) then
5791 begin // Óñòàíàâëèâàåì êàðòó
5792 gExitByTrigger := False;
5793 gNextMap := s;
5794 nm := True;
5795 end else
5796 if P[2] = '' then
5797 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5798 else
5799 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5800 end else
5801 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5802 end;
5803 end else
5804 begin
5805 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5806 P[1] := addWadExtension(P[1]);
5807 if FileExists(MapsDir + P[1]) then
5808 begin
5809 // Íàøëè WAD ôàéë
5810 P[2] := UpperCase(P[2]);
5811 s := P[1] + ':\' + P[2];
5813 if g_Map_Exist(MapsDir + s) then
5814 begin // Íàøëè êàðòó
5815 gExitByTrigger := False;
5816 gNextMap := s;
5817 nm := True;
5818 end else
5819 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5820 end else
5821 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5822 end;
5823 end else
5824 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5825 end;
5826 if nm then
5827 if gNextMap = '' then
5828 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
5829 else
5830 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
5831 end;
5832 end
5833 else if (cmd = 'endmap') or (cmd = 'goodbye') then
5834 begin
5835 if not gGameOn then
5836 g_Console_Add(_lc[I_MSG_NOT_GAME])
5837 else
5838 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5839 begin
5840 gExitByTrigger := False;
5841 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
5842 if (gNextMap = '') and (gTriggers <> nil) then
5843 for a := 0 to High(gTriggers) do
5844 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5845 begin
5846 gExitByTrigger := True;
5847 gNextMap := gTriggers[a].Data.MapName;
5848 Break;
5849 end;
5850 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
5851 if gNextMap = '' then
5852 gNextMap := g_Game_GetNextMap();
5853 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
5854 if Pos(':\', gNextMap) = 0 then
5855 s := gGameSettings.WAD + ':\' + gNextMap
5856 else
5857 s := gNextMap;
5858 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
5859 if g_Map_Exist(MapsDir + s) then
5860 gExit := EXIT_ENDLEVELCUSTOM
5861 else
5862 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
5863 end else
5864 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5865 end
5866 else if (cmd = 'event') then
5867 begin
5868 if (Length(P) <= 1) then
5869 begin
5870 for a := 0 to High(gEvents) do
5871 if gEvents[a].Command = '' then
5872 g_Console_Add(gEvents[a].Name + ' <none>')
5873 else
5874 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5875 Exit;
5876 end;
5877 if (Length(P) = 2) then
5878 begin
5879 for a := 0 to High(gEvents) do
5880 if gEvents[a].Name = P[1] then
5881 if gEvents[a].Command = '' then
5882 g_Console_Add(gEvents[a].Name + ' <none>')
5883 else
5884 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
5885 Exit;
5886 end;
5887 for a := 0 to High(gEvents) do
5888 if gEvents[a].Name = P[1] then
5889 begin
5890 gEvents[a].Command := '';
5891 for b := 2 to High(P) do
5892 if Pos(' ', P[b]) = 0 then
5893 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
5894 else
5895 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
5896 gEvents[a].Command := Trim(gEvents[a].Command);
5897 Exit;
5898 end;
5899 end
5900 // Êîìàíäû Ñâîåé èãðû:
5901 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
5902 begin
5903 if cmd = 'bot_addred' then
5904 begin
5905 if Length(P) > 1 then
5906 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
5907 else
5908 g_Bot_Add(TEAM_RED, 2);
5909 end
5910 else if cmd = 'bot_addblue' then
5911 begin
5912 if Length(P) > 1 then
5913 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
5914 else
5915 g_Bot_Add(TEAM_BLUE, 2);
5916 end
5917 else if cmd = 'suicide' then
5918 begin
5919 if gGameOn then
5920 begin
5921 if g_Game_IsClient then
5922 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
5923 else
5924 begin
5925 if gPlayer1 <> nil then
5926 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
5927 if gPlayer2 <> nil then
5928 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
5929 end;
5930 end;
5931 end
5932 else if cmd = 'spectate' then
5933 begin
5934 if not gGameOn then
5935 Exit;
5936 g_Game_Spectate();
5937 end
5938 else if cmd = 'say' then
5939 begin
5940 if g_Game_IsServer and g_Game_IsNet then
5941 begin
5942 if Length(P) > 1 then
5943 begin
5944 chstr := '';
5945 for a := 1 to High(P) do
5946 chstr := chstr + P[a] + ' ';
5948 if Length(chstr) > 200 then SetLength(chstr, 200);
5950 if Length(chstr) < 1 then
5951 begin
5952 g_Console_Add('say <text>');
5953 Exit;
5954 end;
5956 chstr := b_Text_Format(chstr);
5957 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
5958 end
5959 else g_Console_Add('say <text>');
5960 end else
5961 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5962 end
5963 else if cmd = 'tell' then
5964 begin
5965 if g_Game_IsServer and g_Game_IsNet then
5966 begin
5967 if (Length(P) > 2) and (P[1] <> '') then
5968 begin
5969 chstr := '';
5970 for a := 2 to High(P) do
5971 chstr := chstr + P[a] + ' ';
5973 if Length(chstr) > 200 then SetLength(chstr, 200);
5975 if Length(chstr) < 1 then
5976 begin
5977 g_Console_Add('tell <playername> <text>');
5978 Exit;
5979 end;
5981 pl := g_Net_Client_ByName(P[1]);
5982 if pl <> nil then
5983 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
5984 else
5985 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5986 end
5987 else g_Console_Add('tell <playername> <text>');
5988 end else
5989 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5990 end
5991 else if (cmd = 'overtime') and not g_Game_IsClient then
5992 begin
5993 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
5994 Exit;
5995 // Äîïîëíèòåëüíîå âðåìÿ:
5996 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
5998 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
5999 [gGameSettings.TimeLimit div 3600,
6000 (gGameSettings.TimeLimit div 60) mod 60,
6001 gGameSettings.TimeLimit mod 60]));
6002 if g_Game_IsNet then MH_SEND_GameSettings;
6003 end
6004 else if (cmd = 'rcon_password') and g_Game_IsClient then
6005 begin
6006 if (Length(P) <= 1) then
6007 g_Console_Add('rcon_password <password>')
6008 else
6009 MC_SEND_RCONPassword(P[1]);
6010 end
6011 else if cmd = 'rcon' then
6012 begin
6013 if g_Game_IsClient then
6014 begin
6015 if Length(P) > 1 then
6016 begin
6017 chstr := '';
6018 for a := 1 to High(P) do
6019 chstr := chstr + P[a] + ' ';
6021 if Length(chstr) > 200 then SetLength(chstr, 200);
6023 if Length(chstr) < 1 then
6024 begin
6025 g_Console_Add('rcon <command>');
6026 Exit;
6027 end;
6029 MC_SEND_RCONCommand(chstr);
6030 end
6031 else g_Console_Add('rcon <command>');
6032 end;
6033 end
6034 else if cmd = 'ready' then
6035 begin
6036 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6037 gLMSRespawnTime := gTime + 100;
6038 end
6039 else if (cmd = 'callvote') and g_Game_IsNet then
6040 begin
6041 if Length(P) > 1 then
6042 begin
6043 chstr := '';
6044 for a := 1 to High(P) do begin
6045 if a > 1 then chstr := chstr + ' ';
6046 chstr := chstr + P[a];
6047 end;
6049 if Length(chstr) > 200 then SetLength(chstr, 200);
6051 if Length(chstr) < 1 then
6052 begin
6053 g_Console_Add('callvote <command>');
6054 Exit;
6055 end;
6057 if g_Game_IsClient then
6058 MC_SEND_Vote(True, chstr)
6059 else
6060 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6061 g_Console_Process('vote', True);
6062 end
6063 else
6064 g_Console_Add('callvote <command>');
6065 end
6066 else if (cmd = 'vote') and g_Game_IsNet then
6067 begin
6068 if g_Game_IsClient then
6069 MC_SEND_Vote(False)
6070 else if gVoteInProgress then
6071 begin
6072 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6073 a := Floor((NetClientCount+1)/2.0) + 1
6074 else
6075 a := Floor(NetClientCount/2.0) + 1;
6076 if gVoted then
6077 begin
6078 Dec(gVoteCount);
6079 gVoted := False;
6080 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6081 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6082 end
6083 else
6084 begin
6085 Inc(gVoteCount);
6086 gVoted := True;
6087 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6088 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6089 g_Game_CheckVote;
6090 end;
6091 end;
6092 end
6093 end;
6094 end;
6096 procedure g_TakeScreenShot();
6097 var
6098 a: Word;
6099 FileName: string;
6100 ssdir, t: string;
6101 st: TStream;
6102 ok: Boolean;
6103 begin
6104 if e_NoGraphics then Exit;
6105 ssdir := GameDir+'/screenshots';
6106 if not findFileCI(ssdir, true) then
6107 begin
6108 // try to create dir
6109 try
6110 CreateDir(ssdir);
6111 except
6112 end;
6113 if not findFileCI(ssdir, true) then exit; // alas
6114 end;
6115 try
6116 for a := 1 to High(Word) do
6117 begin
6118 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6119 t := FileName;
6120 if findFileCI(t, true) then continue;
6121 if not findFileCI(FileName) then
6122 begin
6123 ok := false;
6124 st := createDiskFile(FileName);
6125 try
6126 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6127 ok := true;
6128 finally
6129 st.Free();
6130 end;
6131 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6132 break;
6133 end;
6134 end;
6135 except
6136 end;
6137 end;
6139 procedure g_Game_InGameMenu(Show: Boolean);
6140 begin
6141 if (g_ActiveWindow = nil) and Show then
6142 begin
6143 if gGameSettings.GameType = GT_SINGLE then
6144 g_GUI_ShowWindow('GameSingleMenu')
6145 else
6146 begin
6147 if g_Game_IsClient then
6148 g_GUI_ShowWindow('GameClientMenu')
6149 else
6150 if g_Game_IsNet then
6151 g_GUI_ShowWindow('GameServerMenu')
6152 else
6153 g_GUI_ShowWindow('GameCustomMenu');
6154 end;
6155 g_Sound_PlayEx('MENU_OPEN');
6157 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6158 if (not g_Game_IsNet) then
6159 g_Game_Pause(True);
6160 end
6161 else
6162 if (g_ActiveWindow <> nil) and (not Show) then
6163 begin
6164 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6165 if (not g_Game_IsNet) then
6166 g_Game_Pause(False);
6167 end;
6168 end;
6170 procedure g_Game_Pause(Enable: Boolean);
6171 begin
6172 if not gGameOn then
6173 Exit;
6175 if gPause = Enable then
6176 Exit;
6178 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6179 Exit;
6181 gPause := Enable;
6182 g_Game_PauseAllSounds(Enable);
6183 end;
6185 procedure g_Game_PauseAllSounds(Enable: Boolean);
6186 var
6187 i: Integer;
6188 begin
6189 // Òðèããåðû:
6190 if gTriggers <> nil then
6191 for i := 0 to High(gTriggers) do
6192 with gTriggers[i] do
6193 if (TriggerType = TRIGGER_SOUND) and
6194 (Sound <> nil) and
6195 Sound.IsPlaying() then
6196 begin
6197 Sound.Pause(Enable);
6198 end;
6200 // Çâóêè èãðîêîâ:
6201 if gPlayers <> nil then
6202 for i := 0 to High(gPlayers) do
6203 if gPlayers[i] <> nil then
6204 gPlayers[i].PauseSounds(Enable);
6206 // Ìóçûêà:
6207 if gMusic <> nil then
6208 gMusic.Pause(Enable);
6209 end;
6211 procedure g_Game_StopAllSounds(all: Boolean);
6212 var
6213 i: Integer;
6214 begin
6215 if gTriggers <> nil then
6216 for i := 0 to High(gTriggers) do
6217 with gTriggers[i] do
6218 if (TriggerType = TRIGGER_SOUND) and
6219 (Sound <> nil) then
6220 Sound.Stop();
6222 if gMusic <> nil then
6223 gMusic.Stop();
6225 if all then
6226 e_StopChannels();
6227 end;
6229 procedure g_Game_UpdateTriggerSounds();
6230 var
6231 i: Integer;
6232 begin
6233 if gTriggers <> nil then
6234 for i := 0 to High(gTriggers) do
6235 with gTriggers[i] do
6236 if (TriggerType = TRIGGER_SOUND) and
6237 (Sound <> nil) and
6238 (Data.Local) and
6239 Sound.IsPlaying() then
6240 begin
6241 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6242 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6243 begin
6244 Sound.SetPan(0.5 - Data.Pan/255.0);
6245 Sound.SetVolume(Data.Volume/255.0);
6246 end
6247 else
6248 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6249 end;
6250 end;
6252 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6253 begin
6254 Result := False;
6255 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6256 begin
6257 Result := True;
6258 Exit;
6259 end;
6260 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6261 begin
6262 Result := True;
6263 Exit;
6264 end;
6265 if gSpectMode <> SPECT_PLAYERS then
6266 Exit;
6267 if gSpectPID1 = UID then
6268 begin
6269 Result := True;
6270 Exit;
6271 end;
6272 if gSpectViewTwo and (gSpectPID2 = UID) then
6273 begin
6274 Result := True;
6275 Exit;
6276 end;
6277 end;
6279 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6280 var
6281 Pl: TPlayer;
6282 begin
6283 Result := False;
6284 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6285 begin
6286 Result := True;
6287 Exit;
6288 end;
6289 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6290 begin
6291 Result := True;
6292 Exit;
6293 end;
6294 if gSpectMode <> SPECT_PLAYERS then
6295 Exit;
6296 Pl := g_Player_Get(gSpectPID1);
6297 if (Pl <> nil) and (Pl.Team = Team) then
6298 begin
6299 Result := True;
6300 Exit;
6301 end;
6302 if gSpectViewTwo then
6303 begin
6304 Pl := g_Player_Get(gSpectPID2);
6305 if (Pl <> nil) and (Pl.Team = Team) then
6306 begin
6307 Result := True;
6308 Exit;
6309 end;
6310 end;
6311 end;
6313 procedure g_Game_Message(Msg: string; Time: Word);
6314 begin
6315 MessageText := b_Text_Format(Msg);
6316 MessageTime := Time;
6317 end;
6319 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6320 var
6321 a: Integer;
6322 begin
6323 case gAnnouncer of
6324 ANNOUNCE_NONE:
6325 Exit;
6326 ANNOUNCE_ME,
6327 ANNOUNCE_MEPLUS:
6328 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6329 Exit;
6330 end;
6331 for a := 0 to 3 do
6332 if goodsnd[a].IsPlaying() then
6333 Exit;
6335 goodsnd[Random(4)].Play();
6336 end;
6338 procedure g_Game_Announce_KillCombo(Param: Integer);
6339 var
6340 UID: Word;
6341 c, n: Byte;
6342 Pl: TPlayer;
6343 Name: String;
6344 begin
6345 UID := Param and $FFFF;
6346 c := Param shr 16;
6347 if c < 2 then
6348 Exit;
6350 Pl := g_Player_Get(UID);
6351 if Pl = nil then
6352 Name := '?'
6353 else
6354 Name := Pl.Name;
6356 case c of
6357 2: begin
6358 n := 0;
6359 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6360 end;
6361 3: begin
6362 n := 1;
6363 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6364 end;
6365 4: begin
6366 n := 2;
6367 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6368 end;
6369 else begin
6370 n := 3;
6371 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6372 end;
6373 end;
6375 case gAnnouncer of
6376 ANNOUNCE_NONE:
6377 Exit;
6378 ANNOUNCE_ME:
6379 if not g_Game_IsWatchedPlayer(UID) then
6380 Exit;
6381 ANNOUNCE_MEPLUS:
6382 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6383 Exit;
6384 end;
6386 if killsnd[n].IsPlaying() then
6387 killsnd[n].Stop();
6388 killsnd[n].Play();
6389 end;
6391 procedure g_Game_StartVote(Command, Initiator: string);
6392 var
6393 Need: Integer;
6394 begin
6395 if not gVotesEnabled then Exit;
6396 if gGameSettings.GameType <> GT_SERVER then Exit;
6397 if gVoteInProgress or gVotePassed then
6398 begin
6399 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6400 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6401 Exit;
6402 end;
6403 gVoteInProgress := True;
6404 gVotePassed := False;
6405 gVoteTimer := gTime + gVoteTimeout * 1000;
6406 gVoteCount := 0;
6407 gVoted := False;
6408 gVoteCommand := Command;
6410 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6411 Need := Floor((NetClientCount+1)/2.0)+1
6412 else
6413 Need := Floor(NetClientCount/2.0)+1;
6414 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6415 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6416 end;
6418 procedure g_Game_CheckVote;
6419 var
6420 I, Need: Integer;
6421 begin
6422 if gGameSettings.GameType <> GT_SERVER then Exit;
6423 if not gVoteInProgress then Exit;
6425 if (gTime >= gVoteTimer) then
6426 begin
6427 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6428 Need := Floor((NetClientCount+1)/2.0) + 1
6429 else
6430 Need := Floor(NetClientCount/2.0) + 1;
6431 if gVoteCount >= Need then
6432 begin
6433 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6434 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6435 gVotePassed := True;
6436 gVoteCmdTimer := gTime + 5000;
6437 end
6438 else
6439 begin
6440 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6441 MH_SEND_VoteEvent(NET_VE_FAILED);
6442 end;
6443 if NetClients <> nil then
6444 for I := Low(NetClients) to High(NetClients) do
6445 if NetClients[i].Used then
6446 NetClients[i].Voted := False;
6447 gVoteInProgress := False;
6448 gVoted := False;
6449 gVoteCount := 0;
6450 end
6451 else
6452 begin
6453 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6454 Need := Floor((NetClientCount+1)/2.0) + 1
6455 else
6456 Need := Floor(NetClientCount/2.0) + 1;
6457 if gVoteCount >= Need then
6458 begin
6459 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6460 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6461 gVoteInProgress := False;
6462 gVotePassed := True;
6463 gVoteCmdTimer := gTime + 5000;
6464 gVoted := False;
6465 gVoteCount := 0;
6466 if NetClients <> nil then
6467 for I := Low(NetClients) to High(NetClients) do
6468 if NetClients[i].Used then
6469 NetClients[i].Voted := False;
6470 end;
6471 end;
6472 end;
6474 procedure g_Game_LoadMapList(FileName: string);
6475 var
6476 ListFile: TextFile;
6477 s: string;
6478 begin
6479 MapList := nil;
6480 MapIndex := -1;
6482 if not FileExists(FileName) then Exit;
6484 AssignFile(ListFile, FileName);
6485 Reset(ListFile);
6486 while not EOF(ListFile) do
6487 begin
6488 ReadLn(ListFile, s);
6490 s := Trim(s);
6491 if s = '' then Continue;
6493 SetLength(MapList, Length(MapList)+1);
6494 MapList[High(MapList)] := s;
6495 end;
6496 CloseFile(ListFile);
6497 end;
6499 procedure g_Game_SetDebugMode();
6500 begin
6501 gDebugMode := True;
6502 // ×èòû (äàæå â ñâîåé èãðå):
6503 gCheats := True;
6504 end;
6506 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6507 var
6508 i: Word;
6509 begin
6510 if Length(LoadingStat.Msgs) = 0 then
6511 Exit;
6513 with LoadingStat do
6514 begin
6515 if not reWrite then
6516 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6517 if NextMsg = Length(Msgs) then
6518 begin // scroll
6519 for i := 0 to High(Msgs)-1 do
6520 Msgs[i] := Msgs[i+1];
6521 end
6522 else
6523 Inc(NextMsg);
6524 end else
6525 if NextMsg = 0 then
6526 Inc(NextMsg);
6528 Msgs[NextMsg-1] := Text;
6529 CurValue := 0;
6530 MaxValue := Max;
6531 ShowCount := 0;
6532 end;
6534 g_ActiveWindow := nil;
6536 ProcessLoading;
6537 end;
6539 procedure g_Game_StepLoading();
6540 begin
6541 with LoadingStat do
6542 begin
6543 Inc(CurValue);
6544 Inc(ShowCount);
6545 if (ShowCount > LOADING_SHOW_STEP) then
6546 begin
6547 ShowCount := 0;
6548 ProcessLoading;
6549 end;
6550 end;
6551 end;
6553 procedure g_Game_ClearLoading();
6554 var
6555 len: Word;
6556 begin
6557 with LoadingStat do
6558 begin
6559 CurValue := 0;
6560 MaxValue := 0;
6561 ShowCount := 0;
6562 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6563 if len < 1 then len := 1;
6564 SetLength(Msgs, len);
6565 for len := Low(Msgs) to High(Msgs) do
6566 Msgs[len] := '';
6567 NextMsg := 0;
6568 end;
6569 end;
6571 procedure Parse_Params(var pars: TParamStrValues);
6572 var
6573 i: Integer;
6574 s: String;
6575 begin
6576 SetLength(pars, 0);
6577 i := 1;
6578 while i <= ParamCount do
6579 begin
6580 s := ParamStr(i);
6581 if (s[1] = '-') and (Length(s) > 1) then
6582 begin
6583 if (s[2] = '-') and (Length(s) > 2) then
6584 begin // Îäèíî÷íûé ïàðàìåòð
6585 SetLength(pars, Length(pars) + 1);
6586 with pars[High(pars)] do
6587 begin
6588 Name := LowerCase(s);
6589 Value := '+';
6590 end;
6591 end
6592 else
6593 if (i < ParamCount) then
6594 begin // Ïàðàìåòð ñî çíà÷åíèåì
6595 Inc(i);
6596 SetLength(pars, Length(pars) + 1);
6597 with pars[High(pars)] do
6598 begin
6599 Name := LowerCase(s);
6600 Value := LowerCase(ParamStr(i));
6601 end;
6602 end;
6603 end;
6605 Inc(i);
6606 end;
6607 end;
6609 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6610 var
6611 i: Integer;
6612 begin
6613 Result := '';
6614 for i := 0 to High(pars) do
6615 if pars[i].Name = aName then
6616 begin
6617 Result := pars[i].Value;
6618 Break;
6619 end;
6620 end;
6622 procedure g_Game_Process_Params();
6623 var
6624 pars: TParamStrValues;
6625 map: String;
6626 GMode, n: Byte;
6627 LimT, LimS: Integer;
6628 Opt: LongWord;
6629 Lives: Integer;
6630 s: String;
6631 Port: Integer;
6632 ip: String;
6633 F: TextFile;
6634 begin
6635 Parse_Params(pars);
6637 // Debug mode:
6638 s := Find_Param_Value(pars, '--debug');
6639 if (s <> '') then
6640 begin
6641 g_Game_SetDebugMode();
6642 s := Find_Param_Value(pars, '--netdump');
6643 if (s <> '') then
6644 NetDump := True;
6645 end;
6647 // Connect when game loads
6648 ip := Find_Param_Value(pars, '-connect');
6650 if ip <> '' then
6651 begin
6652 s := Find_Param_Value(pars, '-port');
6653 if (s = '') or not TryStrToInt(s, Port) then
6654 Port := 25666;
6656 s := Find_Param_Value(pars, '-pw');
6658 g_Game_StartClient(ip, Port, s);
6659 Exit;
6660 end;
6662 // Start map when game loads:
6663 map := LowerCase(Find_Param_Value(pars, '-map'));
6664 if isWadPath(map) then
6665 begin
6666 // Game mode:
6667 s := Find_Param_Value(pars, '-gm');
6668 GMode := g_Game_TextToMode(s);
6669 if GMode = GM_NONE then GMode := GM_DM;
6670 if GMode = GM_SINGLE then GMode := GM_COOP;
6672 // Time limit:
6673 s := Find_Param_Value(pars, '-limt');
6674 if (s = '') or (not TryStrToInt(s, LimT)) then
6675 LimT := 0;
6676 if LimT < 0 then
6677 LimT := 0;
6679 // Goal limit:
6680 s := Find_Param_Value(pars, '-lims');
6681 if (s = '') or (not TryStrToInt(s, LimS)) then
6682 LimS := 0;
6683 if LimS < 0 then
6684 LimS := 0;
6686 // Lives limit:
6687 s := Find_Param_Value(pars, '-lives');
6688 if (s = '') or (not TryStrToInt(s, Lives)) then
6689 Lives := 0;
6690 if Lives < 0 then
6691 Lives := 0;
6693 // Options:
6694 s := Find_Param_Value(pars, '-opt');
6695 if (s = '') then
6696 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6697 else
6698 Opt := StrToIntDef(s, 0);
6699 if Opt = 0 then
6700 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6702 // Close after map:
6703 s := Find_Param_Value(pars, '--close');
6704 if (s <> '') then
6705 gMapOnce := True;
6707 // Delete test map after play:
6708 s := Find_Param_Value(pars, '--testdelete');
6709 if (s <> '') then
6710 begin
6711 gMapToDelete := MapsDir + map;
6712 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6713 Halt(1);
6714 end;
6716 // Delete temporary WAD after play:
6717 s := Find_Param_Value(pars, '--tempdelete');
6718 if (s <> '') then
6719 begin
6720 gMapToDelete := MapsDir + map;
6721 gTempDelete := True;
6722 end;
6724 // Number of players:
6725 s := Find_Param_Value(pars, '-pl');
6726 if (s = '') then
6727 n := 1
6728 else
6729 n := StrToIntDef(s, 1);
6731 // Start:
6732 s := Find_Param_Value(pars, '-port');
6733 if (s = '') or not TryStrToInt(s, Port) then
6734 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6735 else
6736 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6737 end;
6739 // Execute script when game loads:
6740 s := Find_Param_Value(pars, '-exec');
6741 if s <> '' then
6742 begin
6743 if Pos(':\', s) = 0 then
6744 s := GameDir + '/' + s;
6746 {$I-}
6747 AssignFile(F, s);
6748 Reset(F);
6749 if IOResult <> 0 then
6750 begin
6751 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6752 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6753 CloseFile(F);
6754 Exit;
6755 end;
6756 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
6757 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
6759 while not EOF(F) do
6760 begin
6761 ReadLn(F, s);
6762 if IOResult <> 0 then
6763 begin
6764 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
6765 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
6766 CloseFile(F);
6767 Exit;
6768 end;
6769 if Pos('#', s) <> 1 then // script comment
6770 g_Console_Process(s, True);
6771 end;
6773 CloseFile(F);
6774 {$I+}
6775 end;
6777 SetLength(pars, 0);
6778 end;
6780 end.