DEADSOFTWARE

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