DEADSOFTWARE

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