DEADSOFTWARE

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