DEADSOFTWARE

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