DEADSOFTWARE

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