DEADSOFTWARE

more profiling code
[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_Map_ProfilersBegin();
1494 g_ResetDynlights();
1495 // Ïîðà âûêëþ÷àòü èãðó:
1496 if gExit = EXIT_QUIT then
1497 Exit;
1498 // Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì:
1499 if gExit <> 0 then
1500 begin
1501 EndGame();
1502 if gExit = EXIT_QUIT then
1503 Exit;
1504 end;
1506 // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî:
1507 e_PollInput();
1509 // Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ):
1510 g_Console_Update();
1512 if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then
1513 begin
1514 gExit := EXIT_SIMPLE;
1515 EndGame();
1516 Exit;
1517 end;
1519 case gState of
1520 STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå
1521 STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå
1522 STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè
1523 STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè
1524 begin
1525 if g_Game_IsNet and g_Game_IsServer then
1526 begin
1527 gInterTime := gInterTime + GAME_TICK;
1528 a := Min((gInterEndTime - gInterTime) div 1000 + 1, 255);
1529 if a <> gServInterTime then
1530 begin
1531 gServInterTime := a;
1532 MH_SEND_TimeSync(gServInterTime);
1533 end;
1534 end;
1536 if (not g_Game_IsClient) and
1539 (e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE))
1540 and (not gJustChatted) and (not gConsoleShow) and (not gChatShow)
1541 and (g_ActiveWindow = nil)
1543 or (g_Game_IsNet and (gInterTime > gInterEndTime))
1545 then
1546 begin // Íàæàëè <Enter>/<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè:
1547 g_Game_StopAllSounds(True);
1549 if gMapOnce then // Ýòî áûë òåñò
1550 gExit := EXIT_SIMPLE
1551 else
1552 if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó
1553 g_Game_ChangeMap(gNextMap)
1554 else // Ñëåäóþùåé êàðòû íåò
1555 begin
1556 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then
1557 begin
1558 // Âûõîä â ãëàâíîå ìåíþ:
1559 g_Game_Free;
1560 g_GUI_ShowWindow('MainMenu');
1561 gMusic.SetByName('MUSIC_MENU');
1562 gMusic.Play();
1563 gState := STATE_MENU;
1564 end else
1565 begin
1566 // Ôèíàëüíàÿ êàðòèíêà:
1567 g_Game_ExecuteEvent('onwadend');
1568 g_Game_Free();
1569 if not gMusic.SetByName('MUSIC_endmus') then
1570 gMusic.SetByName('MUSIC_STDENDMUS');
1571 gMusic.Play();
1572 gState := STATE_ENDPIC;
1573 end;
1574 g_Game_ExecuteEvent('ongameend');
1575 end;
1577 Exit;
1578 end;
1580 if gState = STATE_INTERTEXT then
1581 if InterText.counter > 0 then
1582 InterText.counter := InterText.counter - 1;
1583 end;
1585 STATE_FOLD: // Çàòóõàíèå ýêðàíà
1586 begin
1587 if EndingGameCounter = 0 then
1588 begin
1589 // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå:
1590 if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
1591 begin
1592 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1593 begin
1594 g_Game_ExecuteEvent('onwadend');
1595 if not gMusic.SetByName('MUSIC_endmus') then
1596 gMusic.SetByName('MUSIC_STDENDMUS');
1597 end
1598 else
1599 gMusic.SetByName('MUSIC_ROUNDMUS');
1601 gMusic.Play();
1602 gState := STATE_INTERCUSTOM;
1603 end
1604 else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå
1605 begin
1606 gMusic.SetByName('MUSIC_INTERMUS');
1607 gMusic.Play();
1608 gState := STATE_INTERSINGLE;
1609 end;
1610 g_Game_ExecuteEvent('oninter');
1611 end
1612 else
1613 DecMin(EndingGameCounter, 6, 0);
1614 end;
1616 STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà
1617 begin
1618 if gMapOnce then // Ýòî áûë òåñò
1619 begin
1620 gExit := EXIT_SIMPLE;
1621 Exit;
1622 end;
1623 end;
1625 STATE_SLIST:
1626 g_Serverlist_Control(slCurrent);
1627 end;
1629 if g_Game_IsNet then
1630 if not gConsoleShow then
1631 if not gChatShow then
1632 begin
1633 if g_ActiveWindow = nil then
1634 begin
1635 if e_KeyPressed(gGameControls.GameControls.Chat) then
1636 g_Console_Chat_Switch(False)
1637 else if (e_KeyPressed(gGameControls.GameControls.TeamChat)) and
1638 (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1639 g_Console_Chat_Switch(True);
1640 end;
1641 end else
1642 if not gChatEnter then
1643 if (not e_KeyPressed(gGameControls.GameControls.Chat))
1644 and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) then
1645 gChatEnter := True;
1647 // Ñòàòèñòèêà ïî Tab:
1648 if gGameOn then
1649 IsDrawStat := (not gConsoleShow) and (not gChatShow) and
1650 (gGameSettings.GameType <> GT_SINGLE) and
1651 e_KeyPressed(gGameControls.GameControls.Stat);
1653 // Èãðà èäåò:
1654 if gGameOn and not gPause and (gState <> STATE_FOLD) then
1655 begin
1656 // Âðåìÿ += 28 ìèëëèñåêóíä:
1657 gTime := gTime + GAME_TICK;
1659 // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà:
1660 if MessageTime = 0 then
1661 MessageText := '';
1662 if MessageTime > 0 then
1663 MessageTime := MessageTime - 1;
1665 if (g_Game_IsServer) then
1666 begin
1667 // Áûë çàäàí ëèìèò âðåìåíè:
1668 if (gGameSettings.TimeLimit > 0) then
1669 if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then
1670 begin // Îí ïðîøåë => êîíåö óðîâíÿ
1671 g_Game_NextLevel();
1672 Exit;
1673 end;
1675 // Íàäî ðåñïàâíèòü èãðîêîâ â LMS:
1676 if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then
1677 g_Game_RestartRound(gLMSSoftSpawn);
1679 // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî
1680 if gVoteInProgress and (gVoteTimer < gTime) then
1681 g_Game_CheckVote
1682 else if gVotePassed and (gVoteCmdTimer < gTime) then
1683 begin
1684 g_Console_Process(gVoteCommand);
1685 gVoteCommand := '';
1686 gVotePassed := False;
1687 end;
1689 // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ
1690 if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then
1691 gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK;
1692 if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then
1693 gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK;
1695 // Áûë çàäàí ëèìèò ïîáåä:
1696 if (gGameSettings.GoalLimit > 0) then
1697 begin
1698 b := 0;
1700 if gGameSettings.GameMode = GM_DM then
1701 begin // Â DM èùåì èãðîêà ñ max ôðàãàìè
1702 for i := 0 to High(gPlayers) do
1703 if gPlayers[i] <> nil then
1704 if gPlayers[i].Frags > b then
1705 b := gPlayers[i].Frags;
1706 end
1707 else
1708 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1709 begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì
1710 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
1711 end;
1713 // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ:
1714 if b >= gGameSettings.GoalLimit then
1715 begin
1716 g_Game_NextLevel();
1717 Exit;
1718 end;
1719 end;
1721 // Îáðàáàòûâàåì êëàâèøè èãðîêîâ:
1722 if gPlayer1 <> nil then gPlayer1.ReleaseKeys();
1723 if gPlayer2 <> nil then gPlayer2.ReleaseKeys();
1724 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1725 begin
1726 processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton);
1727 processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true);
1728 end // if not console
1729 else
1730 begin
1731 if g_Game_IsNet and (gPlayer1 <> nil) then gPlayer1.PressKey(KEY_CHAT, 10000);
1732 end;
1733 // process weapon switch queue
1734 end; // if server
1736 // Íàáëþäàòåëü
1737 if (gPlayer1 = nil) and (gPlayer2 = nil) and
1738 (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
1739 begin
1740 if not gSpectKeyPress then
1741 begin
1742 if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then
1743 begin
1744 // switch spect mode
1745 case gSpectMode of
1746 SPECT_NONE: ; // not spectator
1747 SPECT_STATS,
1748 SPECT_MAPVIEW: Inc(gSpectMode);
1749 SPECT_PLAYERS: gSpectMode := SPECT_STATS; // reset to 1
1750 end;
1751 gSpectKeyPress := True;
1752 end;
1753 if gSpectMode = SPECT_MAPVIEW then
1754 begin
1755 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1756 gSpectX := Max(gSpectX - gSpectStep, 0);
1757 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1758 gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth);
1759 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1760 gSpectY := Max(gSpectY - gSpectStep, 0);
1761 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1762 gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight);
1763 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1764 begin
1765 // decrease step
1766 if gSpectStep > 4 then gSpectStep := gSpectStep shr 1;
1767 gSpectKeyPress := True;
1768 end;
1769 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1770 begin
1771 // increase step
1772 if gSpectStep < 64 then gSpectStep := gSpectStep shl 1;
1773 gSpectKeyPress := True;
1774 end;
1775 end;
1776 if gSpectMode = SPECT_PLAYERS then
1777 begin
1778 if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then
1779 begin
1780 // add second view
1781 gSpectViewTwo := True;
1782 gSpectKeyPress := True;
1783 end;
1784 if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then
1785 begin
1786 // remove second view
1787 gSpectViewTwo := False;
1788 gSpectKeyPress := True;
1789 end;
1790 if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then
1791 begin
1792 // prev player (view 1)
1793 gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1);
1794 gSpectKeyPress := True;
1795 end;
1796 if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then
1797 begin
1798 // next player (view 1)
1799 gSpectPID1 := GetActivePlayerID_Next(gSpectPID1);
1800 gSpectKeyPress := True;
1801 end;
1802 if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then
1803 begin
1804 // prev player (view 2)
1805 gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2);
1806 gSpectKeyPress := True;
1807 end;
1808 if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then
1809 begin
1810 // next player (view 2)
1811 gSpectPID2 := GetActivePlayerID_Next(gSpectPID2);
1812 gSpectKeyPress := True;
1813 end;
1814 end;
1815 end
1816 else
1817 if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and
1818 (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and
1819 (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and
1820 (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and
1821 (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and
1822 (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and
1823 (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then
1824 gSpectKeyPress := False;
1825 end;
1827 // Îáíîâëÿåì âñå îñòàëüíîå:
1828 g_Map_Update();
1829 g_Items_Update();
1830 g_Triggers_Update();
1831 g_Weapon_Update();
1832 g_Monsters_Update();
1833 g_GFX_Update();
1834 g_Player_UpdateAll();
1835 g_Player_UpdatePhysicalObjects();
1836 if gGameSettings.GameType = GT_SERVER then
1837 if Length(gMonstersSpawned) > 0 then
1838 begin
1839 for I := 0 to High(gMonstersSpawned) do
1840 MH_SEND_MonsterSpawn(gMonstersSpawned[I]);
1841 SetLength(gMonstersSpawned, 0);
1842 end;
1844 if (gSoundTriggerTime > 8) then
1845 begin
1846 g_Game_UpdateTriggerSounds();
1847 gSoundTriggerTime := 0;
1848 end
1849 else
1850 Inc(gSoundTriggerTime);
1852 if (NetMode = NET_SERVER) then
1853 begin
1854 Inc(NetTimeToUpdate);
1855 Inc(NetTimeToReliable);
1856 if NetTimeToReliable >= NetRelupdRate then
1857 begin
1858 for I := 0 to High(gPlayers) do
1859 if gPlayers[I] <> nil then
1860 MH_SEND_PlayerPos(True, gPlayers[I].UID);
1862 if gMonsters <> nil then
1863 for I := 0 to High(gMonsters) do
1864 if gMonsters[I] <> nil then
1865 begin
1866 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1867 begin
1868 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1869 MH_SEND_MonsterPos(gMonsters[I].UID);
1870 end
1871 else
1872 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1873 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1874 (gMonsters[I].GameVelX <> 0) or
1875 (gMonsters[I].GameVelY <> 0) then
1876 MH_SEND_MonsterPos(gMonsters[I].UID);
1877 end;
1879 NetTimeToReliable := 0;
1880 NetTimeToUpdate := NetUpdateRate;
1881 end
1882 else if NetTimeToUpdate >= NetUpdateRate then
1883 begin
1884 if gPlayers <> nil then
1885 for I := 0 to High(gPlayers) do
1886 if gPlayers[I] <> nil then
1887 MH_SEND_PlayerPos(False, gPlayers[I].UID);
1889 if gMonsters <> nil then
1890 for I := 0 to High(gMonsters) do
1891 if gMonsters[I] <> nil then
1892 begin
1893 if (gMonsters[I].MonsterType = MONSTER_BARREL) then
1894 begin
1895 if (gMonsters[I].GameVelX <> 0) or (gMonsters[I].GameVelY <> 0) then
1896 MH_SEND_MonsterPos(gMonsters[I].UID);
1897 end
1898 else
1899 if (gMonsters[I].MonsterState <> MONSTATE_SLEEP) then
1900 if (gMonsters[I].MonsterState <> MONSTATE_DEAD) or
1901 (gMonsters[I].GameVelX <> 0) or
1902 (gMonsters[I].GameVelY <> 0) then
1903 MH_SEND_MonsterPos(gMonsters[I].UID);
1904 end;
1906 NetTimeToUpdate := 0;
1907 end;
1909 if NetUseMaster then
1910 if gTime >= NetTimeToMaster then
1911 begin
1912 if (NetMHost = nil) or (NetMPeer = nil) then
1913 if not g_Net_Slist_Connect then
1914 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
1916 g_Net_Slist_Update;
1917 NetTimeToMaster := gTime + NetMasterRate;
1918 end;
1919 end
1920 else
1921 if NetMode = NET_CLIENT then
1922 MC_SEND_PlayerPos();
1923 end; // if gameOn ...
1925 // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó:
1926 if g_ActiveWindow <> nil then
1927 begin
1928 w := e_GetFirstKeyPressed();
1930 if (w <> IK_INVALID) then
1931 begin
1932 Msg.Msg := MESSAGE_DIKEY;
1933 Msg.wParam := w;
1934 g_ActiveWindow.OnMessage(Msg);
1935 end;
1937 // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì:
1938 if g_ActiveWindow <> nil then
1939 g_ActiveWindow.Update();
1941 // Íóæíî ñìåíèòü ðàçðåøåíèå:
1942 if gResolutionChange then
1943 begin
1944 e_WriteLog('Changing resolution', MSG_NOTIFY);
1945 g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized);
1946 gResolutionChange := False;
1947 end;
1949 // Íóæíî ñìåíèòü ÿçûê:
1950 if gLanguageChange then
1951 begin
1952 //e_WriteLog('Read language file', MSG_NOTIFY);
1953 //g_Language_Load(DataDir + gLanguage + '.txt');
1954 g_Language_Set(gLanguage);
1955 g_Menu_Reset();
1956 gLanguageChange := False;
1957 end;
1958 end;
1960 // Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä):
1961 if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) then
1962 if (GetTimer()-LastScreenShot) > 200000 div 1000 then
1963 begin
1964 g_TakeScreenShot();
1965 LastScreenShot := GetTimer();
1966 end;
1968 // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10):
1969 if e_KeyPressed(IK_F10) and
1970 gGameOn and
1971 (not gConsoleShow) and
1972 (g_ActiveWindow = nil) then
1973 begin
1974 KeyPress(IK_F10);
1975 end;
1977 Time := GetTimer() {div 1000};
1979 // Îáðàáîòêà îòëîæåííûõ ñîáûòèé:
1980 if gDelayedEvents <> nil then
1981 for a := 0 to High(gDelayedEvents) do
1982 if gDelayedEvents[a].Pending and
1984 ((gDelayedEvents[a].DEType = DE_GLOBEVENT) and (gDelayedEvents[a].Time <= Time)) or
1985 ((gDelayedEvents[a].DEType > DE_GLOBEVENT) and (gDelayedEvents[a].Time <= gTime))
1986 ) then
1987 begin
1988 case gDelayedEvents[a].DEType of
1989 DE_GLOBEVENT:
1990 g_Game_ExecuteEvent(gDelayedEvents[a].DEStr);
1991 DE_BFGHIT:
1992 if gGameOn then
1993 g_Game_Announce_GoodShot(gDelayedEvents[a].DENum);
1994 DE_KILLCOMBO:
1995 if gGameOn then
1996 begin
1997 g_Game_Announce_KillCombo(gDelayedEvents[a].DENum);
1998 if g_Game_IsNet and g_Game_IsServer then
1999 MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum);
2000 end;
2001 end;
2002 gDelayedEvents[a].Pending := False;
2003 end;
2005 // Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé:
2006 UPSCounter := UPSCounter + 1;
2007 if Time - UPSTime >= 1000 then
2008 begin
2009 UPS := UPSCounter;
2010 UPSCounter := 0;
2011 UPSTime := Time;
2012 end;
2014 if gGameOn then g_Weapon_AddDynLights();
2016 g_Map_ProfilersEnd();
2017 end;
2019 procedure g_Game_LoadData();
2020 begin
2021 if DataLoaded then Exit;
2023 e_WriteLog('Loading game data...', MSG_NOTIFY);
2025 g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE');
2026 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD');
2027 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR');
2028 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR');
2029 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG');
2030 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD');
2031 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_RB');
2032 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_RS');
2033 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_RD');
2034 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_BB');
2035 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_BS');
2036 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_BD');
2037 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE');
2038 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA');
2039 g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False);
2040 g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT');
2041 g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT');
2042 g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN');
2043 g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE');
2044 g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1');
2045 g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2');
2046 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1');
2047 g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2');
2048 g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1');
2049 g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0');
2050 g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO');
2051 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD1', GameWAD+':SOUNDS\GOOD1');
2052 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD2', GameWAD+':SOUNDS\GOOD2');
2053 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD3', GameWAD+':SOUNDS\GOOD3');
2054 g_Sound_CreateWADEx('SOUND_ANNOUNCER_GOOD4', GameWAD+':SOUNDS\GOOD4');
2055 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL2X', GameWAD+':SOUNDS\KILL2X');
2056 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X');
2057 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X');
2058 g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX');
2060 goodsnd[0] := TPlayableSound.Create();
2061 goodsnd[1] := TPlayableSound.Create();
2062 goodsnd[2] := TPlayableSound.Create();
2063 goodsnd[3] := TPlayableSound.Create();
2065 goodsnd[0].SetByName('SOUND_ANNOUNCER_GOOD1');
2066 goodsnd[1].SetByName('SOUND_ANNOUNCER_GOOD2');
2067 goodsnd[2].SetByName('SOUND_ANNOUNCER_GOOD3');
2068 goodsnd[3].SetByName('SOUND_ANNOUNCER_GOOD4');
2070 killsnd[0] := TPlayableSound.Create();
2071 killsnd[1] := TPlayableSound.Create();
2072 killsnd[2] := TPlayableSound.Create();
2073 killsnd[3] := TPlayableSound.Create();
2075 killsnd[0].SetByName('SOUND_ANNOUNCER_KILL2X');
2076 killsnd[1].SetByName('SOUND_ANNOUNCER_KILL3X');
2077 killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X');
2078 killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX');
2080 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False);
2081 g_Items_LoadData();
2083 g_Game_SetLoadingText(_lc[I_LOAD_WEAPONS_DATA], 0, False);
2084 g_Weapon_LoadData();
2086 g_Monsters_LoadData();
2088 DataLoaded := True;
2089 end;
2091 procedure g_Game_FreeData();
2092 begin
2093 if not DataLoaded then Exit;
2095 g_Items_FreeData();
2096 g_Weapon_FreeData();
2097 g_Monsters_FreeData();
2099 e_WriteLog('Releasing game data...', MSG_NOTIFY);
2101 g_Texture_Delete('NOTEXTURE');
2102 g_Texture_Delete('TEXTURE_PLAYER_HUD');
2103 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
2104 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
2105 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
2106 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
2107 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
2108 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
2109 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
2110 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
2111 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
2112 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
2113 g_Frames_DeleteByName('FRAMES_TELEPORT');
2114 g_Sound_Delete('SOUND_GAME_TELEPORT');
2115 g_Sound_Delete('SOUND_GAME_NOTELEPORT');
2116 g_Sound_Delete('SOUND_GAME_DOOROPEN');
2117 g_Sound_Delete('SOUND_GAME_DOORCLOSE');
2118 g_Sound_Delete('SOUND_GAME_BULK1');
2119 g_Sound_Delete('SOUND_GAME_BULK2');
2120 g_Sound_Delete('SOUND_GAME_BUBBLE1');
2121 g_Sound_Delete('SOUND_GAME_BUBBLE2');
2122 g_Sound_Delete('SOUND_GAME_SWITCH1');
2123 g_Sound_Delete('SOUND_GAME_SWITCH0');
2125 goodsnd[0].Free();
2126 goodsnd[1].Free();
2127 goodsnd[2].Free();
2128 goodsnd[3].Free();
2130 g_Sound_Delete('SOUND_ANNOUNCER_GOOD1');
2131 g_Sound_Delete('SOUND_ANNOUNCER_GOOD2');
2132 g_Sound_Delete('SOUND_ANNOUNCER_GOOD3');
2133 g_Sound_Delete('SOUND_ANNOUNCER_GOOD4');
2135 killsnd[0].Free();
2136 killsnd[1].Free();
2137 killsnd[2].Free();
2138 killsnd[3].Free();
2140 g_Sound_Delete('SOUND_ANNOUNCER_KILL2X');
2141 g_Sound_Delete('SOUND_ANNOUNCER_KILL3X');
2142 g_Sound_Delete('SOUND_ANNOUNCER_KILL4X');
2143 g_Sound_Delete('SOUND_ANNOUNCER_KILLMX');
2145 DataLoaded := False;
2146 end;
2148 procedure DrawCustomStat();
2149 var
2150 pc, x, y, w, _y,
2151 w1, w2, w3,
2152 t, p, m: Integer;
2153 ww1, hh1: Word;
2154 ww2, hh2, r, g, b, rr, gg, bb: Byte;
2155 s1, s2, topstr: String;
2156 begin
2157 e_TextureFontGetSize(gStdFont, ww2, hh2);
2159 e_PollInput();
2160 if e_KeyPressed(IK_TAB) then
2161 begin
2162 if not gStatsPressed then
2163 begin
2164 gStatsOff := not gStatsOff;
2165 gStatsPressed := True;
2166 end;
2167 end
2168 else
2169 gStatsPressed := False;
2171 if gStatsOff then
2172 begin
2173 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
2174 w := (Length(s1) * ww2) div 2;
2175 x := gScreenWidth div 2 - w;
2176 y := 8;
2177 e_TextureFontPrint(x, y, s1, gStdFont);
2178 Exit;
2179 end;
2181 if (gGameSettings.GameMode = GM_COOP) then
2182 begin
2183 if gMissionFailed then
2184 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
2185 else
2186 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
2187 end
2188 else
2189 topstr := _lc[I_MENU_INTER_ROUND_OVER];
2191 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
2192 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
2194 if g_Game_IsNet then
2195 begin
2196 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
2197 if not gChatShow then
2198 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2199 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
2200 end;
2202 if g_Game_IsClient then
2203 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
2204 else
2205 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
2206 if not gChatShow then
2207 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
2208 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
2210 x := 32;
2211 y := 16+hh1+16;
2213 w := gScreenWidth-x*2;
2215 w2 := (w-16) div 6;
2216 w3 := w2;
2217 w1 := w-16-w2-w3;
2219 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
2220 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
2222 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
2224 case CustomStat.GameMode of
2225 GM_DM:
2226 begin
2227 if gGameSettings.MaxLives = 0 then
2228 s1 := _lc[I_GAME_DM]
2229 else
2230 s1 := _lc[I_GAME_LMS];
2231 end;
2232 GM_TDM:
2233 begin
2234 if gGameSettings.MaxLives = 0 then
2235 s1 := _lc[I_GAME_TDM]
2236 else
2237 s1 := _lc[I_GAME_TLMS];
2238 end;
2239 GM_CTF: s1 := _lc[I_GAME_CTF];
2240 GM_COOP:
2241 begin
2242 if gGameSettings.MaxLives = 0 then
2243 s1 := _lc[I_GAME_COOP]
2244 else
2245 s1 := _lc[I_GAME_SURV];
2246 end;
2247 else s1 := '';
2248 end;
2250 _y := y+16;
2251 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2252 _y := _y+8;
2254 _y := _y+16;
2255 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
2256 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
2258 _y := _y+16;
2259 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
2260 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
2261 (CustomStat.GameTime div 1000 div 60) mod 60,
2262 CustomStat.GameTime div 1000 mod 60]), gStdFont);
2264 pc := Length(CustomStat.PlayerStat);
2265 if pc = 0 then Exit;
2267 if CustomStat.GameMode = GM_COOP then
2268 begin
2269 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
2270 _y := _y+32;
2271 s2 := _lc[I_GAME_MONSTERS];
2272 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2273 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
2274 _y := _y+16;
2275 s2 := _lc[I_GAME_SECRETS];
2276 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
2277 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
2278 if gLastMap then
2279 begin
2280 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
2281 _y := _y-16;
2282 s2 := _lc[I_GAME_MONSTERS_TOTAL];
2283 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2284 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
2285 _y := _y+16;
2286 s2 := _lc[I_GAME_SECRETS_TOTAL];
2287 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
2288 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
2289 end;
2290 end;
2292 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
2293 begin
2294 _y := _y+16+16;
2296 with CustomStat do
2297 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
2298 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
2299 else s1 := _lc[I_GAME_WIN_DRAW];
2301 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
2302 _y := _y+40;
2304 for t := TEAM_RED to TEAM_BLUE do
2305 begin
2306 if t = TEAM_RED then
2307 begin
2308 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
2309 gStdFont, 255, 0, 0, 1);
2310 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
2311 gStdFont, 255, 0, 0, 1);
2312 r := 255;
2313 g := 0;
2314 b := 0;
2315 end
2316 else
2317 begin
2318 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
2319 gStdFont, 0, 0, 255, 1);
2320 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
2321 gStdFont, 0, 0, 255, 1);
2322 r := 0;
2323 g := 0;
2324 b := 255;
2325 end;
2327 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
2328 _y := _y+24;
2330 for p := 0 to High(CustomStat.PlayerStat) do
2331 if CustomStat.PlayerStat[p].Team = t then
2332 with CustomStat.PlayerStat[p] do
2333 begin
2334 if Spectator then
2335 begin
2336 rr := r div 2;
2337 gg := g div 2;
2338 bb := b div 2;
2339 end
2340 else
2341 begin
2342 rr := r;
2343 gg := g;
2344 bb := b;
2345 end;
2346 e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1);
2347 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
2348 e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
2349 _y := _y+24;
2350 end;
2352 _y := _y+16+16;
2353 end;
2354 end
2355 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
2356 begin
2357 _y := _y+40;
2358 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
2359 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
2360 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
2362 _y := _y+24;
2363 for p := 0 to High(CustomStat.PlayerStat) do
2364 with CustomStat.PlayerStat[p] do
2365 begin
2366 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
2368 if Spectator then
2369 r := 127
2370 else
2371 r := 255;
2373 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
2374 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
2375 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
2376 _y := _y+24;
2377 end;
2378 end;
2379 end;
2381 procedure DrawSingleStat();
2382 var
2383 tm, key_x, val_x, y: Integer;
2384 w1, w2, h: Word;
2385 s1, s2: String;
2387 procedure player_stat(n: Integer);
2388 var
2389 kpm: Real;
2391 begin
2392 // "Kills: # / #":
2393 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
2394 s2 := Format(' %d', [gTotalMonsters]);
2396 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
2397 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
2398 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2399 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
2400 s1 := s1 + '/';
2401 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2402 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
2404 // "Kills-per-minute: ##.#":
2405 s1 := _lc[I_MENU_INTER_KPM];
2406 if tm > 0 then
2407 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
2408 else
2409 kpm := SingleStat.PlayerStat[n].Kills;
2410 s2 := Format(' %.1f', [kpm]);
2412 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
2413 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
2415 // "Secrets found: # / #":
2416 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
2417 s2 := Format(' %d', [SingleStat.TotalSecrets]);
2419 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
2420 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
2421 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2422 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
2423 s1 := s1 + '/';
2424 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2425 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
2426 end;
2428 begin
2429 // "Level Complete":
2430 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
2431 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
2433 // Îïðåäåëÿåì êîîðäèíàòû âûðàâíèâàíèÿ ïî ñàìîé äëèííîé ñòðîêå:
2434 s1 := _lc[I_MENU_INTER_KPM];
2435 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2436 Inc(w1, 16);
2437 s1 := ' 9999.9';
2438 e_CharFont_GetSize(gMenuFont, s1, w2, h);
2440 key_x := (gScreenWidth-w1-w2) div 2;
2441 val_x := key_x + w1;
2443 // "Time: #:##:##":
2444 tm := SingleStat.GameTime div 1000;
2445 s1 := _lc[I_MENU_INTER_TIME];
2446 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
2448 e_CharFont_Print(gMenuFont, key_x, 80, s1);
2449 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
2451 if SingleStat.TwoPlayers then
2452 begin
2453 // "Player 1":
2454 s1 := _lc[I_MENU_PLAYER_1];
2455 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2456 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
2458 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2459 y := 176;
2460 player_stat(0);
2462 // "Player 2":
2463 s1 := _lc[I_MENU_PLAYER_2];
2464 e_CharFont_GetSize(gMenuFont, s1, w1, h);
2465 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
2467 // Ñòàòèñòèêà âòîðîãî èãðîêà:
2468 y := 336;
2469 player_stat(1);
2470 end
2471 else
2472 begin
2473 // Ñòàòèñòèêà ïåðâîãî èãðîêà:
2474 y := 128;
2475 player_stat(0);
2476 end;
2477 end;
2479 procedure DrawLoadingStat();
2480 var
2481 ww, hh: Word;
2482 xx, yy, i: Integer;
2483 s: String;
2484 begin
2485 if Length(LoadingStat.Msgs) = 0 then
2486 Exit;
2488 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
2489 yy := (gScreenHeight div 3);
2490 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
2491 xx := (gScreenWidth div 3);
2493 with LoadingStat do
2494 for i := 0 to NextMsg-1 do
2495 begin
2496 if (i = (NextMsg-1)) and (MaxValue > 0) then
2497 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
2498 else
2499 s := Msgs[i];
2501 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
2502 yy := yy + LOADING_INTERLINE;
2503 end;
2504 end;
2506 procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect);
2507 var
2508 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
2509 begin
2510 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
2511 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
2512 begin
2513 Scale := 1;
2514 // Ñêîëüêî ïèêñåëîâ êàðòû â 1 ïèêñåëå ìèíè-êàðòû:
2515 ScaleSz := 16 div Scale;
2516 // Ðàçìåðû ìèíè-êàðòû:
2517 aX := max(gMapInfo.Width div ScaleSz, 1);
2518 aY := max(gMapInfo.Height div ScaleSz, 1);
2519 // Ðàìêà êàðòû:
2520 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
2522 if gWalls <> nil then
2523 begin
2524 // Ðèñóåì ñòåíû:
2525 for a := 0 to High(gWalls) do
2526 with gWalls[a] do
2527 if PanelType <> 0 then
2528 begin
2529 // Ëåâûé âåðõíèé óãîë:
2530 aX := X div ScaleSz;
2531 aY := Y div ScaleSz;
2532 // Ðàçìåðû:
2533 aX2 := max(Width div ScaleSz, 1);
2534 aY2 := max(Height div ScaleSz, 1);
2535 // Ïðàâûé íèæíèé óãîë:
2536 aX2 := aX + aX2 - 1;
2537 aY2 := aY + aY2 - 1;
2539 case PanelType of
2540 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
2541 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2542 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
2543 end;
2544 end;
2545 end;
2546 if gSteps <> nil then
2547 begin
2548 // Ðèñóåì ñòóïåíè:
2549 for a := 0 to High(gSteps) do
2550 with gSteps[a] do
2551 if PanelType <> 0 then
2552 begin
2553 // Ëåâûé âåðõíèé óãîë:
2554 aX := X div ScaleSz;
2555 aY := Y div ScaleSz;
2556 // Ðàçìåðû:
2557 aX2 := max(Width div ScaleSz, 1);
2558 aY2 := max(Height div ScaleSz, 1);
2559 // Ïðàâûé íèæíèé óãîë:
2560 aX2 := aX + aX2 - 1;
2561 aY2 := aY + aY2 - 1;
2563 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
2564 end;
2565 end;
2566 if gLifts <> nil then
2567 begin
2568 // Ðèñóåì ëèôòû:
2569 for a := 0 to High(gLifts) do
2570 with gLifts[a] do
2571 if PanelType <> 0 then
2572 begin
2573 // Ëåâûé âåðõíèé óãîë:
2574 aX := X div ScaleSz;
2575 aY := Y div ScaleSz;
2576 // Ðàçìåðû:
2577 aX2 := max(Width div ScaleSz, 1);
2578 aY2 := max(Height div ScaleSz, 1);
2579 // Ïðàâûé íèæíèé óãîë:
2580 aX2 := aX + aX2 - 1;
2581 aY2 := aY + aY2 - 1;
2583 case LiftType of
2584 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
2585 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
2586 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
2587 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
2588 end;
2589 end;
2590 end;
2591 if gWater <> nil then
2592 begin
2593 // Ðèñóåì âîäó:
2594 for a := 0 to High(gWater) do
2595 with gWater[a] do
2596 if PanelType <> 0 then
2597 begin
2598 // Ëåâûé âåðõíèé óãîë:
2599 aX := X div ScaleSz;
2600 aY := Y div ScaleSz;
2601 // Ðàçìåðû:
2602 aX2 := max(Width div ScaleSz, 1);
2603 aY2 := max(Height div ScaleSz, 1);
2604 // Ïðàâûé íèæíèé óãîë:
2605 aX2 := aX + aX2 - 1;
2606 aY2 := aY + aY2 - 1;
2608 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
2609 end;
2610 end;
2611 if gAcid1 <> nil then
2612 begin
2613 // Ðèñóåì êèñëîòó 1:
2614 for a := 0 to High(gAcid1) do
2615 with gAcid1[a] do
2616 if PanelType <> 0 then
2617 begin
2618 // Ëåâûé âåðõíèé óãîë:
2619 aX := X div ScaleSz;
2620 aY := Y div ScaleSz;
2621 // Ðàçìåðû:
2622 aX2 := max(Width div ScaleSz, 1);
2623 aY2 := max(Height div ScaleSz, 1);
2624 // Ïðàâûé íèæíèé óãîë:
2625 aX2 := aX + aX2 - 1;
2626 aY2 := aY + aY2 - 1;
2628 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
2629 end;
2630 end;
2631 if gAcid2 <> nil then
2632 begin
2633 // Ðèñóåì êèñëîòó 2:
2634 for a := 0 to High(gAcid2) do
2635 with gAcid2[a] do
2636 if PanelType <> 0 then
2637 begin
2638 // Ëåâûé âåðõíèé óãîë:
2639 aX := X div ScaleSz;
2640 aY := Y div ScaleSz;
2641 // Ðàçìåðû:
2642 aX2 := max(Width div ScaleSz, 1);
2643 aY2 := max(Height div ScaleSz, 1);
2644 // Ïðàâûé íèæíèé óãîë:
2645 aX2 := aX + aX2 - 1;
2646 aY2 := aY + aY2 - 1;
2648 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
2649 end;
2650 end;
2651 if gPlayers <> nil then
2652 begin
2653 // Ðèñóåì èãðîêîâ:
2654 for a := 0 to High(gPlayers) do
2655 if gPlayers[a] <> nil then with gPlayers[a] do
2656 if Live then begin
2657 // Ëåâûé âåðõíèé óãîë:
2658 aX := Obj.X div ScaleSz + 1;
2659 aY := Obj.Y div ScaleSz + 1;
2660 // Ðàçìåðû:
2661 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2662 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2663 // Ïðàâûé íèæíèé óãîë:
2664 aX2 := aX + aX2 - 1;
2665 aY2 := aY + aY2 - 1;
2667 if gPlayers[a] = p then
2668 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
2669 else
2670 case Team of
2671 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
2672 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
2673 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
2674 end;
2675 end;
2676 end;
2677 if gMonsters <> nil then
2678 begin
2679 // Ðèñóåì ìîíñòðîâ:
2680 for a := 0 to High(gMonsters) do
2681 if gMonsters[a] <> nil then with gMonsters[a] do
2682 if Live then begin
2683 // Ëåâûé âåðõíèé óãîë:
2684 aX := Obj.X div ScaleSz + 1;
2685 aY := Obj.Y div ScaleSz + 1;
2686 // Ðàçìåðû:
2687 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
2688 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
2689 // Ïðàâûé íèæíèé óãîë:
2690 aX2 := aX + aX2 - 1;
2691 aY2 := aY + aY2 - 1;
2693 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
2694 end;
2695 end;
2696 end;
2697 end;
2699 procedure DrawMapView(x, y, w, h: Integer);
2700 var
2701 bx, by: Integer;
2702 begin
2703 glPushMatrix();
2705 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
2706 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
2707 g_Map_DrawBack(-bx, -by);
2709 sX := x;
2710 sY := y;
2711 sWidth := w;
2712 sHeight := h;
2714 glTranslatef(-x, -y, 0);
2716 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_BACK);
2717 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_STEP);
2718 g_Items_Draw();
2719 g_Weapon_Draw();
2720 g_Player_DrawShells();
2721 g_Player_DrawAll();
2722 g_Player_DrawCorpses();
2723 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WALL);
2724 g_Monsters_Draw();
2725 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_CLOSEDOOR);
2726 g_GFX_Draw();
2727 g_Map_DrawFlags();
2728 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID1);
2729 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID2);
2730 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WATER);
2731 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_FORE);
2732 if g_debug_HealthBar then
2733 begin
2734 g_Monsters_DrawHealth();
2735 g_Player_DrawHealth();
2736 end;
2738 glPopMatrix();
2739 end;
2741 procedure DrawPlayer(p: TPlayer);
2742 var
2743 px, py, a, b, c, d: Integer;
2744 //R: TRect;
2745 lln: Integer;
2746 lx, ly, lrad: Integer;
2747 begin
2748 if (p = nil) or (p.FDummy) then
2749 begin
2750 glPushMatrix();
2751 g_Map_DrawBack(0, 0);
2752 glPopMatrix();
2753 Exit;
2754 end;
2756 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('MAP RENDER');
2757 profileFrameDraw.mainBegin(g_profile_frame_draw);
2759 gPlayerDrawn := p;
2761 glPushMatrix();
2763 px := p.GameX + PLAYER_RECT_CX;
2764 py := p.GameY + PLAYER_RECT_CY;
2766 if px > (gPlayerScreenSize.X div 2) then
2767 a := -px + (gPlayerScreenSize.X div 2)
2768 else
2769 a := 0;
2770 if py > (gPlayerScreenSize.Y div 2) then
2771 b := -py + (gPlayerScreenSize.Y div 2)
2772 else
2773 b := 0;
2774 if px > (gMapInfo.Width - (gPlayerScreenSize.X div 2)) then
2775 a := -gMapInfo.Width + gPlayerScreenSize.X;
2776 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2777 b := -gMapInfo.Height + gPlayerScreenSize.Y;
2778 if gMapInfo.Width <= gPlayerScreenSize.X then
2779 a := 0;
2780 if gMapInfo.Height <= gPlayerScreenSize.Y then
2781 b := 0;
2783 if p.IncCam <> 0 then
2784 begin
2785 if py > (gMapInfo.Height - (gPlayerScreenSize.Y div 2)) then
2786 begin
2787 if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then
2788 p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2)));
2789 end;
2791 if py < (gPlayerScreenSize.Y div 2) then
2792 begin
2793 if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then
2794 p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py);
2795 end;
2797 if p.IncCam < 0 then
2798 while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and
2799 (p.IncCam < 0) do
2800 p.IncCam := p.IncCam + 1;
2802 if p.IncCam > 0 then
2803 while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and
2804 (p.IncCam > 0) do
2805 p.IncCam := p.IncCam - 1;
2806 end;
2808 if (px< gPlayerScreenSize.X div 2) or
2809 (gMapInfo.Width-gPlayerScreenSize.X <= 256) then
2810 c := 0
2811 else
2812 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then
2813 c := gBackSize.X - gPlayerScreenSize.X
2814 else
2815 c := Round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X));
2817 if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or
2818 (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then
2819 d := 0
2820 else
2821 if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then
2822 d := gBackSize.Y - gPlayerScreenSize.Y
2823 else
2824 d := Round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y));
2826 profileFrameDraw.sectionBegin('map background');
2827 g_Map_DrawBack(-c, -d);
2828 profileFrameDraw.sectionEnd();
2830 sX := -a;
2831 sY := -(b+p.IncCam);
2832 sWidth := gPlayerScreenSize.X;
2833 sHeight := gPlayerScreenSize.Y;
2835 glTranslatef(a, b+p.IncCam, 0);
2837 profileFrameDraw.sectionBegin('map rendering');
2839 profileFrameDraw.sectionBegin('panel_back');
2840 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_BACK);
2841 profileFrameDraw.sectionEnd();
2843 profileFrameDraw.sectionBegin('panel_step');
2844 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_STEP);
2845 profileFrameDraw.sectionEnd();
2847 profileFrameDraw.sectionBegin('items');
2848 g_Items_Draw();
2849 profileFrameDraw.sectionEnd();
2851 profileFrameDraw.sectionBegin('weapons');
2852 g_Weapon_Draw();
2853 profileFrameDraw.sectionEnd();
2855 profileFrameDraw.sectionBegin('shells');
2856 g_Player_DrawShells();
2857 profileFrameDraw.sectionEnd();
2859 profileFrameDraw.sectionBegin('drawall');
2860 g_Player_DrawAll();
2861 profileFrameDraw.sectionEnd();
2863 profileFrameDraw.sectionBegin('corpses');
2864 g_Player_DrawCorpses();
2865 profileFrameDraw.sectionEnd();
2867 profileFrameDraw.sectionBegin('panel_wall');
2868 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WALL);
2869 profileFrameDraw.sectionEnd();
2871 profileFrameDraw.sectionBegin('monsters');
2872 g_Monsters_Draw();
2873 profileFrameDraw.sectionEnd();
2875 profileFrameDraw.sectionBegin('panel_closedoor');
2876 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_CLOSEDOOR);
2877 profileFrameDraw.sectionEnd();
2879 profileFrameDraw.sectionBegin('gfx');
2880 g_GFX_Draw();
2881 profileFrameDraw.sectionEnd();
2883 profileFrameDraw.sectionBegin('flags');
2884 g_Map_DrawFlags();
2885 profileFrameDraw.sectionEnd();
2887 profileFrameDraw.sectionBegin('panel_acid1');
2888 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID1);
2889 profileFrameDraw.sectionEnd();
2891 profileFrameDraw.sectionBegin('panel_acid2');
2892 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_ACID2);
2893 profileFrameDraw.sectionEnd();
2895 profileFrameDraw.sectionBegin('panel_water');
2896 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_WATER);
2897 profileFrameDraw.sectionEnd();
2899 //TODO: lights should be in separate grid, i think
2900 // but on the other side: grid may be slower for dynlights, as their lifetime is short
2901 if gwin_has_stencil and (g_dynLightCount > 0) then
2902 begin
2903 profileFrameDraw.sectionBegin('dynlights');
2905 // setup OpenGL parameters
2906 glStencilMask($FFFFFFFF);
2907 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
2908 glEnable(GL_STENCIL_TEST);
2909 glEnable(GL_SCISSOR_TEST);
2910 glClear(GL_STENCIL_BUFFER_BIT);
2911 glStencilFunc(GL_EQUAL, 0, $ff);
2913 for lln := 0 to g_dynLightCount-1 do
2914 begin
2915 lx := g_dynLights[lln].x;
2916 ly := g_dynLights[lln].y;
2917 lrad := g_dynLights[lln].radius;
2918 if lrad < 3 then continue;
2920 if lx-sX+lrad < 0 then continue;
2921 if ly-sY+lrad < 0 then continue;
2922 if lx-sX-lrad >= gPlayerScreenSize.X then continue;
2923 if ly-sY-lrad >= gPlayerScreenSize.Y then continue;
2925 // set scissor to optimize drawing
2926 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
2927 // no need to clear stencil buffer, light blitting will do it for us
2928 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
2929 // draw extruded panels
2930 glDisable(GL_TEXTURE_2D);
2931 glDisable(GL_BLEND);
2932 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
2933 if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad);
2934 // render light texture
2935 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
2936 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
2937 // blend it
2938 glEnable(GL_BLEND);
2939 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2940 glEnable(GL_TEXTURE_2D);
2941 // color and opacity
2942 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
2943 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
2944 glBegin(GL_QUADS);
2945 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
2946 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
2947 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
2948 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
2949 glEnd();
2950 end;
2952 // done
2953 glDisable(GL_STENCIL_TEST);
2954 glDisable(GL_BLEND);
2955 glDisable(GL_SCISSOR_TEST);
2956 glScissor(0, 0, sWidth, sHeight);
2958 profileFrameDraw.sectionEnd();
2959 end
2960 else
2961 begin
2962 profileFrameDraw.sectionBegin('dynlights');
2963 profileFrameDraw.sectionEnd();
2964 end;
2966 profileFrameDraw.sectionBegin('panel_fore');
2967 g_Map_DrawPanels(sX, sY, sWidth, sHeight, PANEL_FORE);
2968 profileFrameDraw.sectionEnd();
2970 if g_debug_HealthBar then
2971 begin
2972 profileFrameDraw.sectionBegin('monster health');
2973 g_Monsters_DrawHealth();
2974 profileFrameDraw.sectionEnd();
2976 profileFrameDraw.sectionBegin('player health');
2977 g_Player_DrawHealth();
2978 profileFrameDraw.sectionEnd();
2979 end;
2981 if p.FSpectator then
2982 e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4,
2983 p.GameY + PLAYER_RECT_CY - 4,
2984 'X', gStdFont, 255, 255, 255, 1, True);
2986 for a := 0 to High(gCollideMap) do
2987 for b := 0 to High(gCollideMap[a]) do
2988 begin
2989 d := 0;
2990 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
2991 d := d + 1;
2992 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
2993 d := d + 2;
2995 case d of
2996 1: e_DrawPoint(1, b, a, 200, 200, 200);
2997 2: e_DrawPoint(1, b, a, 64, 64, 255);
2998 3: e_DrawPoint(1, b, a, 255, 0, 255);
2999 end;
3000 end;
3003 glPopMatrix();
3005 profileFrameDraw.mainEnd(); // map rendering
3007 p.DrawPain();
3008 p.DrawPickup();
3009 p.DrawRulez();
3010 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
3011 if g_Debug_Player then
3012 g_Player_DrawDebug(p);
3013 p.DrawGUI();
3014 end;
3016 procedure drawProfilers ();
3017 var
3018 px: Integer = -1;
3019 begin
3020 if g_profile_frame_draw then px := px-drawProfiles(px, -1, profileFrameDraw);
3021 if g_profile_collision then px := px-drawProfiles(px, -1, profMapCollision);
3022 end;
3024 procedure g_Game_Draw();
3025 var
3026 ID: DWORD;
3027 w, h: Word;
3028 ww, hh: Byte;
3029 Time: Int64;
3030 back: string;
3031 plView1, plView2: TPlayer;
3032 Split: Boolean;
3033 begin
3034 if gExit = EXIT_QUIT then Exit;
3036 Time := GetTimer() {div 1000};
3037 FPSCounter := FPSCounter+1;
3038 if Time - FPSTime >= 1000 then
3039 begin
3040 FPS := FPSCounter;
3041 FPSCounter := 0;
3042 FPSTime := Time;
3043 end;
3045 if gGameOn or (gState = STATE_FOLD) then
3046 begin
3047 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3048 begin
3049 gSpectMode := SPECT_NONE;
3050 if not gRevertPlayers then
3051 begin
3052 plView1 := gPlayer1;
3053 plView2 := gPlayer2;
3054 end
3055 else
3056 begin
3057 plView1 := gPlayer2;
3058 plView2 := gPlayer1;
3059 end;
3060 end
3061 else
3062 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
3063 begin
3064 gSpectMode := SPECT_NONE;
3065 if gPlayer2 = nil then
3066 plView1 := gPlayer1
3067 else
3068 plView1 := gPlayer2;
3069 plView2 := nil;
3070 end
3071 else
3072 begin
3073 plView1 := nil;
3074 plView2 := nil;
3075 end;
3077 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
3078 gSpectMode := SPECT_STATS;
3080 if gSpectMode = SPECT_PLAYERS then
3081 if gPlayers <> nil then
3082 begin
3083 plView1 := GetActivePlayer_ByID(gSpectPID1);
3084 if plView1 = nil then
3085 begin
3086 gSpectPID1 := GetActivePlayerID_Next();
3087 plView1 := GetActivePlayer_ByID(gSpectPID1);
3088 end;
3089 if gSpectViewTwo then
3090 begin
3091 plView2 := GetActivePlayer_ByID(gSpectPID2);
3092 if plView2 = nil then
3093 begin
3094 gSpectPID2 := GetActivePlayerID_Next();
3095 plView2 := GetActivePlayer_ByID(gSpectPID2);
3096 end;
3097 end;
3098 end;
3100 if gSpectMode = SPECT_MAPVIEW then
3101 begin
3102 // Ðåæèì ïðîñìîòðà êàðòû
3103 Split := False;
3104 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3105 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
3106 gHearPoint1.Active := True;
3107 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
3108 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
3109 gHearPoint2.Active := False;
3110 end
3111 else
3112 begin
3113 Split := (plView1 <> nil) and (plView2 <> nil);
3115 // Òî÷êè ñëóõà èãðîêîâ
3116 if plView1 <> nil then
3117 begin
3118 gHearPoint1.Active := True;
3119 gHearPoint1.Coords.X := plView1.GameX;
3120 gHearPoint1.Coords.Y := plView1.GameY;
3121 end else
3122 gHearPoint1.Active := False;
3123 if plView2 <> nil then
3124 begin
3125 gHearPoint2.Active := True;
3126 gHearPoint2.Coords.X := plView2.GameX;
3127 gHearPoint2.Coords.Y := plView2.GameY;
3128 end else
3129 gHearPoint2.Active := False;
3131 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3132 gPlayerScreenSize.X := gScreenWidth-196;
3133 if Split then
3134 begin
3135 gPlayerScreenSize.Y := gScreenHeight div 2;
3136 if gScreenHeight mod 2 = 0 then
3137 Dec(gPlayerScreenSize.Y);
3138 end
3139 else
3140 gPlayerScreenSize.Y := gScreenHeight;
3142 if Split then
3143 if gScreenHeight mod 2 = 0 then
3144 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
3145 else
3146 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3148 DrawPlayer(plView1);
3149 gPlayer1ScreenCoord.X := sX;
3150 gPlayer1ScreenCoord.Y := sY;
3152 if Split then
3153 begin
3154 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
3156 DrawPlayer(plView2);
3157 gPlayer2ScreenCoord.X := sX;
3158 gPlayer2ScreenCoord.Y := sY;
3159 end;
3161 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
3163 if Split then
3164 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
3165 end;
3167 if MessageText <> '' then
3168 begin
3169 w := 0;
3170 h := 0;
3171 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
3172 if Split then
3173 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3174 (gScreenHeight div 2)-(h div 2), MessageText)
3175 else
3176 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
3177 Round(gScreenHeight / 2.75)-(h div 2), MessageText);
3178 end;
3180 if IsDrawStat or (gSpectMode = 1) then DrawStat();
3182 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then
3183 begin
3184 // Draw spectator GUI
3185 ww := 0;
3186 hh := 0;
3187 e_TextureFontGetSize(gStdFont, ww, hh);
3188 case gSpectMode of
3189 SPECT_STATS:
3190 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
3191 SPECT_MAPVIEW:
3192 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
3193 SPECT_PLAYERS:
3194 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
3195 end;
3196 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
3197 if gSpectMode = SPECT_MAPVIEW then
3198 begin
3199 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
3200 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
3201 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
3202 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
3203 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
3204 end;
3205 if gSpectMode = SPECT_PLAYERS then
3206 begin
3207 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
3208 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
3209 if gSpectViewTwo then
3210 begin
3211 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
3212 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
3213 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3214 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3215 end
3216 else
3217 begin
3218 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
3219 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
3220 end;
3221 end;
3222 end;
3223 end;
3225 if gPause and gGameOn and (g_ActiveWindow = nil) then
3226 begin
3227 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3229 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
3230 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
3231 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
3232 end;
3234 if not gGameOn then
3235 begin
3236 if (gState = STATE_MENU) then
3237 begin
3238 if ((g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '')) then
3239 begin
3240 if g_Texture_Get('MENU_BACKGROUND', ID) then
3241 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3242 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3243 end;
3244 if g_ActiveWindow <> nil then
3245 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3246 end;
3248 if gState = STATE_FOLD then
3249 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
3251 if gState = STATE_INTERCUSTOM then
3252 begin
3253 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
3254 begin
3255 back := 'TEXTURE_endpic';
3256 if not g_Texture_Get(back, ID) then
3257 back := _lc[I_TEXTURE_ENDPIC];
3258 end
3259 else
3260 back := 'INTER';
3262 if g_Texture_Get(back, ID) then
3263 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3264 else
3265 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3267 DrawCustomStat();
3269 if g_ActiveWindow <> nil then
3270 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3271 end;
3273 if gState = STATE_INTERSINGLE then
3274 begin
3275 if EndingGameCounter > 0 then
3276 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter)
3277 else
3278 begin
3279 back := 'INTER';
3281 if g_Texture_Get(back, ID) then
3282 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3283 else
3284 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3286 DrawSingleStat();
3288 if g_ActiveWindow <> nil then
3289 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3290 end;
3291 end;
3293 if gState = STATE_ENDPIC then
3294 begin
3295 ID := DWORD(-1);
3296 if not g_Texture_Get('TEXTURE_endpic', ID) then
3297 g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID);
3299 if ID <> DWORD(-1) then
3300 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
3301 else
3302 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
3304 if g_ActiveWindow <> nil then
3305 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3306 end;
3308 if gState = STATE_SLIST then
3309 begin
3310 if g_Texture_Get('MENU_BACKGROUND', ID) then
3311 begin
3312 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
3313 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3314 end;
3315 g_Serverlist_Draw(slCurrent);
3316 end;
3317 end;
3319 if g_ActiveWindow <> nil then
3320 begin
3321 if gGameOn then
3322 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
3323 g_ActiveWindow.Draw();
3324 end;
3326 g_Console_Draw();
3328 if g_debug_Sounds and gGameOn then
3329 begin
3330 for w := 0 to High(e_SoundsArray) do
3331 for h := 0 to e_SoundsArray[w].nRefs do
3332 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
3333 end;
3335 if gShowFPS then
3336 begin
3337 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
3338 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
3339 end;
3341 if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then
3342 e_TextureFontPrint(gScreenWidth-72, 0,
3343 Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]),
3344 gStdFont);
3346 drawProfilers();
3347 end;
3349 procedure g_Game_Quit();
3350 begin
3351 g_Game_StopAllSounds(True);
3352 gMusic.Free();
3353 g_Game_SaveOptions();
3354 g_Game_FreeData();
3355 g_PlayerModel_FreeData();
3356 g_Texture_DeleteAll();
3357 g_Frames_DeleteAll();
3358 g_Menu_Free();
3360 if NetInitDone then g_Net_Free;
3362 // Íàäî óäàëèòü êàðòó ïîñëå òåñòà:
3363 if gMapToDelete <> '' then
3364 g_Game_DeleteTestMap();
3366 gExit := EXIT_QUIT;
3367 PushExitEvent();
3368 end;
3370 procedure g_FatalError(Text: String);
3371 begin
3372 g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True);
3373 e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), MSG_WARNING);
3375 gExit := EXIT_SIMPLE;
3376 end;
3378 procedure g_SimpleError(Text: String);
3379 begin
3380 g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True);
3381 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), MSG_WARNING);
3382 end;
3384 procedure g_Game_SetupScreenSize();
3385 var
3386 d: Single;
3387 begin
3388 // Ðàçìåð ýêðàíîâ èãðîêîâ:
3389 gPlayerScreenSize.X := gScreenWidth-196;
3390 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
3391 gPlayerScreenSize.Y := gScreenHeight div 2
3392 else
3393 gPlayerScreenSize.Y := gScreenHeight;
3395 // Ðàçìåð çàäíåãî ïëàíà:
3396 if BackID <> DWORD(-1) then
3397 begin
3398 d := SKY_STRETCH;
3400 if (gScreenWidth*d > gMapInfo.Width) or
3401 (gScreenHeight*d > gMapInfo.Height) then
3402 d := 1.0;
3404 gBackSize.X := Round(gScreenWidth*d);
3405 gBackSize.Y := Round(gScreenHeight*d);
3406 end;
3407 end;
3409 procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean);
3410 begin
3411 g_Window_SetSize(newWidth, newHeight, nowFull);
3412 end;
3414 procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE);
3415 begin
3416 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3417 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3418 Exit;
3419 if gPlayer1 = nil then
3420 begin
3421 if g_Game_IsClient then
3422 begin
3423 if NetPlrUID1 > -1 then
3424 begin
3425 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3426 gPlayer1 := g_Player_Get(NetPlrUID1);
3427 end;
3428 Exit;
3429 end;
3431 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3432 Team := gPlayer1Settings.Team;
3434 // Ñîçäàíèå ïåðâîãî èãðîêà:
3435 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3436 gPlayer1Settings.Color,
3437 Team, False));
3438 if gPlayer1 = nil then
3439 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]))
3440 else
3441 begin
3442 gPlayer1.Name := gPlayer1Settings.Name;
3443 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True);
3444 if g_Game_IsServer and g_Game_IsNet then
3445 MH_SEND_PlayerCreate(gPlayer1.UID);
3446 gPlayer1.Respawn(False, True);
3448 if g_Game_IsNet and NetUseMaster then
3449 g_Net_Slist_Update;
3450 end;
3452 Exit;
3453 end;
3454 if gPlayer2 = nil then
3455 begin
3456 if g_Game_IsClient then
3457 begin
3458 if NetPlrUID2 > -1 then
3459 gPlayer2 := g_Player_Get(NetPlrUID2);
3460 Exit;
3461 end;
3463 if not (Team in [TEAM_RED, TEAM_BLUE]) then
3464 Team := gPlayer2Settings.Team;
3466 // Ñîçäàíèå âòîðîãî èãðîêà:
3467 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3468 gPlayer2Settings.Color,
3469 Team, False));
3470 if gPlayer2 = nil then
3471 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]))
3472 else
3473 begin
3474 gPlayer2.Name := gPlayer2Settings.Name;
3475 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True);
3476 if g_Game_IsServer and g_Game_IsNet then
3477 MH_SEND_PlayerCreate(gPlayer2.UID);
3478 gPlayer2.Respawn(False, True);
3480 if g_Game_IsNet and NetUseMaster then
3481 g_Net_Slist_Update;
3482 end;
3484 Exit;
3485 end;
3486 end;
3488 procedure g_Game_RemovePlayer();
3489 var
3490 Pl: TPlayer;
3491 begin
3492 if ((not gGameOn) and (gState <> STATE_INTERCUSTOM))
3493 or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then
3494 Exit;
3495 Pl := gPlayer2;
3496 if Pl <> nil then
3497 begin
3498 if g_Game_IsServer then
3499 begin
3500 Pl.Lives := 0;
3501 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3502 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3503 g_Player_Remove(Pl.UID);
3505 if g_Game_IsNet and NetUseMaster then
3506 g_Net_Slist_Update;
3507 end else
3508 gPlayer2 := nil;
3509 Exit;
3510 end;
3511 Pl := gPlayer1;
3512 if Pl <> nil then
3513 begin
3514 if g_Game_IsServer then
3515 begin
3516 Pl.Lives := 0;
3517 Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
3518 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
3519 g_Player_Remove(Pl.UID);
3521 if g_Game_IsNet and NetUseMaster then
3522 g_Net_Slist_Update;
3523 end else
3524 begin
3525 gPlayer1 := nil;
3526 MC_SEND_CheatRequest(NET_CHEAT_SPECTATE);
3527 end;
3528 Exit;
3529 end;
3530 end;
3532 procedure g_Game_Spectate();
3533 begin
3534 g_Game_RemovePlayer();
3535 if gPlayer1 <> nil then
3536 g_Game_RemovePlayer();
3537 end;
3539 procedure g_Game_SpectateCenterView();
3540 begin
3541 gSpectX := Max(gMapInfo.Width div 2 - gScreenWidth div 2, 0);
3542 gSpectY := Max(gMapInfo.Height div 2 - gScreenHeight div 2, 0);
3543 end;
3545 procedure g_Game_StartSingle(Map: String; TwoPlayers: Boolean; nPlayers: Byte);
3546 var
3547 i, nPl: Integer;
3548 begin
3549 g_Game_Free();
3551 e_WriteLog('Starting singleplayer game...', MSG_NOTIFY);
3553 g_Game_ClearLoading();
3555 // Íàñòðîéêè èãðû:
3556 FillByte(gGameSettings, SizeOf(TGameSettings), 0);
3557 gAimLine := False;
3558 gShowMap := False;
3559 gGameSettings.GameType := GT_SINGLE;
3560 gGameSettings.MaxLives := 0;
3561 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
3562 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
3563 gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
3564 gSwitchGameMode := GM_SINGLE;
3566 g_Game_ExecuteEvent('ongamestart');
3568 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3569 g_Game_SetupScreenSize();
3571 // Ñîçäàíèå ïåðâîãî èãðîêà:
3572 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3573 gPlayer1Settings.Color,
3574 gPlayer1Settings.Team, False));
3575 if gPlayer1 = nil then
3576 begin
3577 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3578 Exit;
3579 end;
3581 gPlayer1.Name := gPlayer1Settings.Name;
3582 nPl := 1;
3584 // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü:
3585 if TwoPlayers then
3586 begin
3587 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3588 gPlayer2Settings.Color,
3589 gPlayer2Settings.Team, False));
3590 if gPlayer2 = nil then
3591 begin
3592 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3593 Exit;
3594 end;
3596 gPlayer2.Name := gPlayer2Settings.Name;
3597 Inc(nPl);
3598 end;
3600 // Çàãðóçêà è çàïóñê êàðòû:
3601 if not g_Game_StartMap(MAP, True) then
3602 begin
3603 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + MAP]));
3604 Exit;
3605 end;
3607 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3608 g_Player_Init();
3610 // Ñîçäàåì áîòîâ:
3611 for i := nPl+1 to nPlayers do
3612 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3613 end;
3615 procedure g_Game_StartCustom(Map: String; GameMode: Byte;
3616 TimeLimit, GoalLimit: Word;
3617 MaxLives: Byte;
3618 Options: LongWord; nPlayers: Byte);
3619 var
3620 i, nPl: Integer;
3621 begin
3622 g_Game_Free();
3624 e_WriteLog('Starting custom game...', MSG_NOTIFY);
3626 g_Game_ClearLoading();
3628 // Íàñòðîéêè èãðû:
3629 gGameSettings.GameType := GT_CUSTOM;
3630 gGameSettings.GameMode := GameMode;
3631 gSwitchGameMode := GameMode;
3632 gGameSettings.TimeLimit := TimeLimit;
3633 gGameSettings.GoalLimit := GoalLimit;
3634 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3635 gGameSettings.Options := Options;
3637 gCoopTotalMonstersKilled := 0;
3638 gCoopTotalSecretsFound := 0;
3639 gCoopTotalMonsters := 0;
3640 gCoopTotalSecrets := 0;
3641 gAimLine := False;
3642 gShowMap := False;
3644 g_Game_ExecuteEvent('ongamestart');
3646 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3647 g_Game_SetupScreenSize();
3649 // Ðåæèì íàáëþäàòåëÿ:
3650 if nPlayers = 0 then
3651 begin
3652 gPlayer1 := nil;
3653 gPlayer2 := nil;
3654 end;
3656 nPl := 0;
3657 if nPlayers >= 1 then
3658 begin
3659 // Ñîçäàíèå ïåðâîãî èãðîêà:
3660 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3661 gPlayer1Settings.Color,
3662 gPlayer1Settings.Team, False));
3663 if gPlayer1 = nil then
3664 begin
3665 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3666 Exit;
3667 end;
3669 gPlayer1.Name := gPlayer1Settings.Name;
3670 Inc(nPl);
3671 end;
3673 if nPlayers >= 2 then
3674 begin
3675 // Ñîçäàíèå âòîðîãî èãðîêà:
3676 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3677 gPlayer2Settings.Color,
3678 gPlayer2Settings.Team, False));
3679 if gPlayer2 = nil then
3680 begin
3681 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3682 Exit;
3683 end;
3685 gPlayer2.Name := gPlayer2Settings.Name;
3686 Inc(nPl);
3687 end;
3689 // Çàãðóçêà è çàïóñê êàðòû:
3690 if not g_Game_StartMap(Map, True) then
3691 begin
3692 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3693 Exit;
3694 end;
3696 // Íåò òî÷åê ïîÿâëåíèÿ:
3697 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3698 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3699 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3700 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3701 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3702 begin
3703 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3704 Exit;
3705 end;
3707 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3708 g_Player_Init();
3710 // Ñîçäàåì áîòîâ:
3711 for i := nPl+1 to nPlayers do
3712 g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True);
3713 end;
3715 procedure g_Game_StartServer(Map: String; GameMode: Byte;
3716 TimeLimit, GoalLimit: Word; MaxLives: Byte;
3717 Options: LongWord; nPlayers: Byte;
3718 IPAddr: LongWord; Port: Word);
3719 begin
3720 g_Game_Free();
3722 e_WriteLog('Starting net game (server)...', MSG_NOTIFY);
3724 g_Game_ClearLoading();
3726 // Íàñòðîéêè èãðû:
3727 gGameSettings.GameType := GT_SERVER;
3728 gGameSettings.GameMode := GameMode;
3729 gSwitchGameMode := GameMode;
3730 gGameSettings.TimeLimit := TimeLimit;
3731 gGameSettings.GoalLimit := GoalLimit;
3732 gGameSettings.MaxLives := IfThen(GameMode = GM_CTF, 0, MaxLives);
3733 gGameSettings.Options := Options;
3735 gCoopTotalMonstersKilled := 0;
3736 gCoopTotalSecretsFound := 0;
3737 gCoopTotalMonsters := 0;
3738 gCoopTotalSecrets := 0;
3739 gAimLine := False;
3740 gShowMap := False;
3742 g_Game_ExecuteEvent('ongamestart');
3744 // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà
3745 g_Game_SetupScreenSize();
3747 // Ðåæèì íàáëþäàòåëÿ:
3748 if nPlayers = 0 then
3749 begin
3750 gPlayer1 := nil;
3751 gPlayer2 := nil;
3752 end;
3754 if nPlayers >= 1 then
3755 begin
3756 // Ñîçäàíèå ïåðâîãî èãðîêà:
3757 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3758 gPlayer1Settings.Color,
3759 gPlayer1Settings.Team, False));
3760 if gPlayer1 = nil then
3761 begin
3762 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3763 Exit;
3764 end;
3766 gPlayer1.Name := gPlayer1Settings.Name;
3767 end;
3769 if nPlayers >= 2 then
3770 begin
3771 // Ñîçäàíèå âòîðîãî èãðîêà:
3772 gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model,
3773 gPlayer2Settings.Color,
3774 gPlayer2Settings.Team, False));
3775 if gPlayer2 = nil then
3776 begin
3777 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2]));
3778 Exit;
3779 end;
3781 gPlayer2.Name := gPlayer2Settings.Name;
3782 end;
3784 // Ñòàðòóåì ñåðâåð
3785 if not g_Net_Host(IPAddr, Port, NetMaxClients) then
3786 begin
3787 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_HOST]);
3788 Exit;
3789 end;
3791 g_Net_Slist_Set(NetSlistIP, NetSlistPort);
3793 // Çàãðóçêà è çàïóñê êàðòû:
3794 if not g_Game_StartMap(Map, True) then
3795 begin
3796 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map]));
3797 Exit;
3798 end;
3800 // Íåò òî÷åê ïîÿâëåíèÿ:
3801 if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) +
3802 g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) +
3803 g_Map_GetPointCount(RESPAWNPOINT_DM) +
3804 g_Map_GetPointCount(RESPAWNPOINT_RED)+
3805 g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then
3806 begin
3807 g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]);
3808 Exit;
3809 end;
3811 // Íàñòðîéêè èãðîêîâ è áîòîâ:
3812 g_Player_Init();
3814 NetState := NET_STATE_GAME;
3815 end;
3817 procedure g_Game_StartClient(Addr: String; Port: Word; PW: String);
3818 var
3819 Map: String;
3820 WadName: string;
3821 Ptr: Pointer;
3822 T: Cardinal;
3823 MID: Byte;
3824 State: Byte;
3825 OuterLoop: Boolean;
3826 newResPath: string;
3827 begin
3828 g_Game_Free();
3830 State := 0;
3831 e_WriteLog('Starting net game (client)...', MSG_NOTIFY);
3832 e_WriteLog('NET: Trying to connect to ' + Addr + ':' + IntToStr(Port) + '...', MSG_NOTIFY);
3834 g_Game_ClearLoading();
3836 // Íàñòðîéêè èãðû:
3837 gGameSettings.GameType := GT_CLIENT;
3839 gCoopTotalMonstersKilled := 0;
3840 gCoopTotalSecretsFound := 0;
3841 gCoopTotalMonsters := 0;
3842 gCoopTotalSecrets := 0;
3843 gAimLine := False;
3844 gShowMap := False;
3846 g_Game_ExecuteEvent('ongamestart');
3848 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
3849 g_Game_SetupScreenSize();
3851 NetState := NET_STATE_AUTH;
3853 g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False);
3854 // Ñòàðòóåì êëèåíò
3855 if not g_Net_Connect(Addr, Port) then
3856 begin
3857 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3858 NetState := NET_STATE_NONE;
3859 Exit;
3860 end;
3862 g_Game_SetLoadingText(_lc[I_LOAD_SEND_INFO], 0, False);
3863 MC_SEND_Info(PW);
3864 g_Game_SetLoadingText(_lc[I_LOAD_WAIT_INFO], 0, False);
3866 OuterLoop := True;
3867 while OuterLoop do
3868 begin
3869 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
3870 begin
3871 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
3872 begin
3873 Ptr := NetEvent.packet^.data;
3874 e_Raw_Seek(0);
3876 MID := e_Raw_Read_Byte(Ptr);
3878 if (MID = NET_MSG_INFO) and (State = 0) then
3879 begin
3880 NetMyID := e_Raw_Read_Byte(Ptr);
3881 NetPlrUID1 := e_Raw_Read_Word(Ptr);
3883 WadName := e_Raw_Read_String(Ptr);
3884 Map := e_Raw_Read_String(Ptr);
3886 gWADHash := e_Raw_Read_MD5(Ptr);
3888 gGameSettings.GameMode := e_Raw_Read_Byte(Ptr);
3889 gSwitchGameMode := gGameSettings.GameMode;
3890 gGameSettings.GoalLimit := e_Raw_Read_Word(Ptr);
3891 gGameSettings.TimeLimit := e_Raw_Read_Word(Ptr);
3892 gGameSettings.MaxLives := e_Raw_Read_Byte(Ptr);
3893 gGameSettings.Options := e_Raw_Read_LongWord(Ptr);
3894 T := e_Raw_Read_LongWord(Ptr);
3896 newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash);
3897 if newResPath = '' then
3898 begin
3899 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
3900 newResPath := g_Res_DownloadWAD(WadName);
3901 if newResPath = '' then
3902 begin
3903 g_FatalError(_lc[I_NET_ERR_HASH]);
3904 enet_packet_destroy(NetEvent.packet);
3905 NetState := NET_STATE_NONE;
3906 Exit;
3907 end;
3908 end;
3909 newResPath := ExtractRelativePath(MapsDir, newResPath);
3911 gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
3912 gPlayer1Settings.Color,
3913 gPlayer1Settings.Team, False));
3915 if gPlayer1 = nil then
3916 begin
3917 g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1]));
3919 enet_packet_destroy(NetEvent.packet);
3920 NetState := NET_STATE_NONE;
3921 Exit;
3922 end;
3924 gPlayer1.Name := gPlayer1Settings.Name;
3925 gPlayer1.UID := NetPlrUID1;
3926 gPlayer1.Reset(True);
3928 if not g_Game_StartMap(newResPath + ':\' + Map, True) then
3929 begin
3930 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map]));
3932 enet_packet_destroy(NetEvent.packet);
3933 NetState := NET_STATE_NONE;
3934 Exit;
3935 end;
3937 gTime := T;
3939 State := 1;
3940 OuterLoop := False;
3941 enet_packet_destroy(NetEvent.packet);
3942 break;
3943 end
3944 else
3945 enet_packet_destroy(NetEvent.packet);
3946 end
3947 else
3948 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
3949 begin
3950 State := 0;
3951 if (NetEvent.data <= NET_DISC_MAX) then
3952 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
3953 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
3954 OuterLoop := False;
3955 Break;
3956 end;
3957 end;
3959 ProcessLoading();
3961 e_PollInput();
3963 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
3964 begin
3965 State := 0;
3966 break;
3967 end;
3968 end;
3970 if State <> 1 then
3971 begin
3972 g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]);
3973 NetState := NET_STATE_NONE;
3974 Exit;
3975 end;
3977 gLMSRespawn := LMS_RESPAWN_NONE;
3978 gLMSRespawnTime := 0;
3980 g_Player_Init();
3981 NetState := NET_STATE_GAME;
3982 MC_SEND_FullStateRequest;
3983 e_WriteLog('NET: Connection successful.', MSG_NOTIFY);
3984 end;
3986 procedure g_Game_SaveOptions();
3987 begin
3988 g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
3989 end;
3991 procedure g_Game_ChangeMap(MapPath: String);
3992 var
3993 Force: Boolean;
3994 begin
3995 g_Game_ClearLoading();
3997 Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF];
3998 // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü
3999 if gExitByTrigger then
4000 begin
4001 Force := False;
4002 gExitByTrigger := False;
4003 end;
4004 if not g_Game_StartMap(MapPath, Force) then
4005 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath]));
4006 end;
4008 procedure g_Game_Restart();
4009 var
4010 Map: string;
4011 begin
4012 if g_Game_IsClient then
4013 Exit;
4014 map := g_ExtractFileName(gMapInfo.Map);
4016 MessageTime := 0;
4017 gGameOn := False;
4018 g_Game_ClearLoading();
4019 g_Game_StartMap(Map, True);
4020 end;
4022 function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
4023 var
4024 NewWAD, ResName: String;
4025 I: Integer;
4026 begin
4027 g_Map_Free();
4028 g_Player_RemoveAllCorpses();
4030 if (not g_Game_IsClient) and
4031 (gSwitchGameMode <> gGameSettings.GameMode) and
4032 (gGameSettings.GameMode <> GM_SINGLE) then
4033 begin
4034 if gSwitchGameMode = GM_CTF then
4035 gGameSettings.MaxLives := 0;
4036 gGameSettings.GameMode := gSwitchGameMode;
4037 Force := True;
4038 end else
4039 gSwitchGameMode := gGameSettings.GameMode;
4041 g_Player_ResetTeams();
4043 if Pos(':\', Map) > 0 then
4044 begin
4045 NewWAD := g_ExtractWadName(Map);
4046 ResName := g_ExtractFileName(Map);
4047 if g_Game_IsServer then
4048 begin
4049 gWADHash := MD5File(MapsDir + NewWAD);
4050 g_Game_LoadWAD(NewWAD);
4051 end else
4052 // hash recieved in MC_RECV_GameEvent -> NET_EV_MAPSTART
4053 g_Game_ClientWAD(NewWAD, gWADHash);
4054 end else
4055 ResName := Map;
4057 Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
4058 if Result then
4059 begin
4060 g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
4062 gState := STATE_NONE;
4063 g_ActiveWindow := nil;
4064 gGameOn := True;
4066 DisableCheats();
4067 ResetTimer();
4069 if gGameSettings.GameMode = GM_CTF then
4070 begin
4071 g_Map_ResetFlag(FLAG_RED);
4072 g_Map_ResetFlag(FLAG_BLUE);
4073 // CTF, à ôëàãîâ íåò:
4074 if not g_Map_HaveFlagPoints() then
4075 g_SimpleError(_lc[I_GAME_ERROR_CTF]);
4076 end;
4077 end
4078 else
4079 begin
4080 gState := STATE_MENU;
4081 gGameOn := False;
4082 end;
4084 gExit := 0;
4085 gPause := False;
4086 gTime := 0;
4087 NetTimeToUpdate := 1;
4088 NetTimeToReliable := 0;
4089 NetTimeToMaster := NetMasterRate;
4090 gLMSRespawn := LMS_RESPAWN_NONE;
4091 gLMSRespawnTime := 0;
4092 gMissionFailed := False;
4093 gNextMap := '';
4095 gCoopMonstersKilled := 0;
4096 gCoopSecretsFound := 0;
4098 gVoteInProgress := False;
4099 gVotePassed := False;
4100 gVoteCount := 0;
4101 gVoted := False;
4103 gStatsOff := False;
4105 if not gGameOn then Exit;
4107 g_Game_SpectateCenterView();
4109 if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then
4110 begin
4111 gLMSRespawn := LMS_RESPAWN_WARMUP;
4112 gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000;
4113 gLMSSoftSpawn := True;
4114 if NetMode = NET_SERVER then
4115 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000)
4116 else
4117 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True);
4118 end;
4120 if NetMode = NET_SERVER then
4121 begin
4122 MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map);
4124 // Ìàñòåðñåðâåð
4125 if NetUseMaster then
4126 begin
4127 if (NetMHost = nil) or (NetMPeer = nil) then
4128 if not g_Net_Slist_Connect then
4129 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4131 g_Net_Slist_Update;
4132 end;
4134 if NetClients <> nil then
4135 for I := 0 to High(NetClients) do
4136 if NetClients[I].Used then
4137 begin
4138 NetClients[I].Voted := False;
4139 if NetClients[I].RequestedFullUpdate then
4140 begin
4141 MH_SEND_Everything((NetClients[I].State = NET_STATE_AUTH), I);
4142 NetClients[I].RequestedFullUpdate := False;
4143 end;
4144 end;
4146 g_Net_UnbanNonPermHosts();
4147 end;
4149 if gLastMap then
4150 begin
4151 gCoopTotalMonstersKilled := 0;
4152 gCoopTotalSecretsFound := 0;
4153 gCoopTotalMonsters := 0;
4154 gCoopTotalSecrets := 0;
4155 gLastMap := False;
4156 end;
4158 g_Game_ExecuteEvent('onmapstart');
4159 end;
4161 procedure SetFirstLevel();
4162 begin
4163 gNextMap := '';
4165 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4166 if MapList = nil then
4167 Exit;
4169 SortSArray(MapList);
4170 gNextMap := MapList[Low(MapList)];
4172 MapList := nil;
4173 end;
4175 procedure g_Game_ExitLevel(Map: Char16);
4176 begin
4177 gNextMap := Map;
4179 gCoopTotalMonstersKilled := gCoopTotalMonstersKilled + gCoopMonstersKilled;
4180 gCoopTotalSecretsFound := gCoopTotalSecretsFound + gCoopSecretsFound;
4181 gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters;
4182 gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount;
4184 // Âûøëè â âûõîä â Îäèíî÷íîé èãðå:
4185 if gGameSettings.GameType = GT_SINGLE then
4186 gExit := EXIT_ENDLEVELSINGLE
4187 else // Âûøëè â âûõîä â Ñâîåé èãðå
4188 begin
4189 gExit := EXIT_ENDLEVELCUSTOM;
4190 if gGameSettings.GameMode = GM_COOP then
4191 g_Player_RememberAll;
4193 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4194 begin
4195 gLastMap := True;
4196 if gGameSettings.GameMode = GM_COOP then
4197 gStatsOff := True;
4199 gStatsPressed := True;
4200 gNextMap := 'MAP01';
4202 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
4203 g_Game_NextLevel;
4205 if g_Game_IsNet then
4206 begin
4207 MH_SEND_GameStats();
4208 MH_SEND_CoopStats();
4209 end;
4210 end;
4211 end;
4212 end;
4214 procedure g_Game_RestartLevel();
4215 var
4216 Map: string;
4217 begin
4218 if gGameSettings.GameMode = GM_SINGLE then
4219 begin
4220 g_Game_Restart();
4221 Exit;
4222 end;
4223 gExit := EXIT_ENDLEVELCUSTOM;
4224 Map := g_ExtractFileName(gMapInfo.Map);
4225 gNextMap := Map;
4226 end;
4228 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
4229 var
4230 gWAD: String;
4231 begin
4232 if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then
4233 Exit;
4234 if not g_Game_IsClient then
4235 Exit;
4236 gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash);
4237 if gWAD = '' then
4238 begin
4239 g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False);
4240 gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD));
4241 if gWAD = '' then
4242 begin
4243 g_Game_Free();
4244 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)]));
4245 Exit;
4246 end;
4247 end;
4248 NewWAD := ExtractRelativePath(MapsDir, gWAD);
4249 g_Game_LoadWAD(NewWAD);
4250 end;
4252 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
4253 var
4254 i, n, nb, nr: Integer;
4255 begin
4256 if not g_Game_IsServer then Exit;
4257 if gLMSRespawn = LMS_RESPAWN_NONE then Exit;
4258 gLMSRespawn := LMS_RESPAWN_NONE;
4259 gLMSRespawnTime := 0;
4260 MessageTime := 0;
4262 if (gGameSettings.GameMode = GM_COOP) and not NoMapRestart then
4263 begin
4264 gMissionFailed := True;
4265 g_Game_RestartLevel;
4266 Exit;
4267 end;
4269 n := 0; nb := 0; nr := 0;
4270 for i := Low(gPlayers) to High(gPlayers) do
4271 if (gPlayers[i] <> nil) and
4272 ((not gPlayers[i].FSpectator) or gPlayers[i].FWantsInGame or
4273 (gPlayers[i] is TBot)) then
4274 begin
4275 Inc(n);
4276 if gPlayers[i].Team = TEAM_RED then Inc(nr)
4277 else if gPlayers[i].Team = TEAM_BLUE then Inc(nb)
4278 end;
4280 if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then
4281 begin
4282 // wait a second until the fuckers finally decide to join
4283 gLMSRespawn := LMS_RESPAWN_WARMUP;
4284 gLMSRespawnTime := gTime + 1000;
4285 gLMSSoftSpawn := NoMapRestart;
4286 Exit;
4287 end;
4289 g_Player_RemoveAllCorpses;
4290 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
4291 if g_Game_IsNet then
4292 MH_SEND_GameEvent(NET_EV_LMS_START);
4294 for i := Low(gPlayers) to High(gPlayers) do
4295 begin
4296 if gPlayers[i] = nil then continue;
4297 if gPlayers[i] is TBot then gPlayers[i].FWantsInGame := True;
4298 // don't touch normal spectators
4299 if gPlayers[i].FSpectator and not gPlayers[i].FWantsInGame then
4300 begin
4301 gPlayers[i].FNoRespawn := True;
4302 gPlayers[i].Lives := 0;
4303 if g_Game_IsNet then
4304 MH_SEND_PlayerStats(gPlayers[I].UID);
4305 continue;
4306 end;
4307 gPlayers[i].FNoRespawn := False;
4308 gPlayers[i].Lives := gGameSettings.MaxLives;
4309 gPlayers[i].Respawn(False, True);
4310 if gGameSettings.GameMode = GM_COOP then
4311 begin
4312 gPlayers[i].Frags := 0;
4313 gPlayers[i].RecallState;
4314 end;
4315 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
4316 gPlayer1 := g_Player_Get(gLMSPID1);
4317 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
4318 gPlayer2 := g_Player_Get(gLMSPID2);
4319 end;
4321 for i := Low(gItems) to High(gItems) do
4322 begin
4323 if gItems[i].Respawnable then
4324 begin
4325 gItems[i].QuietRespawn := True;
4326 gItems[i].RespawnTime := 0;
4327 end
4328 else
4329 begin
4330 g_Items_Remove(i);
4331 if g_Game_IsNet then MH_SEND_ItemDestroy(True, i);
4332 end;
4333 end;
4335 for i := Low(gMonsters) to High(gMonsters) do
4336 begin
4337 if (gMonsters[i] <> nil) and not gMonsters[i].FNoRespawn then
4338 gMonsters[i].Respawn;
4339 end;
4341 gLMSSoftSpawn := False;
4342 end;
4344 function g_Game_GetFirstMap(WAD: String): String;
4345 begin
4346 Result := '';
4348 MapList := g_Map_GetMapsList(WAD);
4349 if MapList = nil then
4350 Exit;
4352 SortSArray(MapList);
4353 Result := MapList[Low(MapList)];
4355 if not g_Map_Exist(WAD + ':\' + Result) then
4356 Result := '';
4358 MapList := nil;
4359 end;
4361 function g_Game_GetNextMap(): String;
4362 var
4363 I: Integer;
4364 Map: string;
4365 begin
4366 Result := '';
4368 MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
4369 if MapList = nil then
4370 Exit;
4372 Map := g_ExtractFileName(gMapInfo.Map);
4374 SortSArray(MapList);
4375 MapIndex := -255;
4376 for I := Low(MapList) to High(MapList) do
4377 if Map = MapList[I] then
4378 begin
4379 MapIndex := I;
4380 Break;
4381 end;
4383 if MapIndex <> -255 then
4384 begin
4385 if MapIndex = High(MapList) then
4386 Result := MapList[Low(MapList)]
4387 else
4388 Result := MapList[MapIndex + 1];
4390 if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
4391 end;
4393 MapList := nil;
4394 end;
4396 procedure g_Game_NextLevel();
4397 begin
4398 if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP] then
4399 gExit := EXIT_ENDLEVELCUSTOM
4400 else
4401 begin
4402 gExit := EXIT_ENDLEVELSINGLE;
4403 Exit;
4404 end;
4406 if gNextMap <> '' then Exit;
4407 gNextMap := g_Game_GetNextMap();
4408 end;
4410 function g_Game_IsTestMap(): Boolean;
4411 begin
4412 result := StrEquCI1251(TEST_MAP_NAME, g_ExtractFileName(gMapInfo.Map));
4413 end;
4415 procedure g_Game_DeleteTestMap();
4416 var
4417 a: Integer;
4418 MapName: Char16;
4419 WadName: string;
4421 WAD: TWADFile;
4422 MapList: SArray;
4423 time: Integer;
4425 begin
4426 a := Pos('.wad:\', gMapToDelete);
4427 if a = 0 then
4428 Exit;
4430 // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû:
4431 WadName := Copy(gMapToDelete, 1, a + 3);
4432 Delete(gMapToDelete, 1, a + 5);
4433 gMapToDelete := UpperCase(gMapToDelete);
4434 MapName := '';
4435 CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete)));
4438 // Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå:
4439 if MapName <> TEST_MAP_NAME then
4440 Exit;
4442 if not gTempDelete then
4443 begin
4444 time := g_GetFileTime(WadName);
4445 WAD := TWADFile.Create();
4447 // ×èòàåì Wad-ôàéë:
4448 if not WAD.ReadFile(WadName) then
4449 begin // Íåò òàêîãî WAD-ôàéëà
4450 WAD.Free();
4451 Exit;
4452 end;
4454 // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ:
4455 WAD.CreateImage();
4456 MapList := WAD.GetResourcesList('');
4458 if MapList <> nil then
4459 for a := 0 to High(MapList) do
4460 if MapList[a] = MapName then
4461 begin
4462 // Óäàëÿåì è ñîõðàíÿåì:
4463 WAD.RemoveResource('', MapName);
4464 WAD.SaveTo(WadName);
4465 Break;
4466 end;
4468 WAD.Free();
4469 g_SetFileTime(WadName, time);
4470 end else
4472 if gTempDelete then DeleteFile(WadName);
4473 end;
4475 procedure GameCVars(P: SArray);
4476 var
4477 a, b: Integer;
4478 stat: TPlayerStatArray;
4479 cmd, s: string;
4480 config: TConfig;
4481 begin
4482 stat := nil;
4483 cmd := LowerCase(P[0]);
4484 if cmd = 'r_showfps' then
4485 begin
4486 if (Length(P) > 1) and
4487 ((P[1] = '1') or (P[1] = '0')) then
4488 gShowFPS := (P[1][1] = '1');
4490 if gShowFPS then
4491 g_Console_Add(_lc[I_MSG_SHOW_FPS_ON])
4492 else
4493 g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]);
4494 end
4495 else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then
4496 begin
4497 with gGameSettings do
4498 begin
4499 if (Length(P) > 1) and
4500 ((P[1] = '1') or (P[1] = '0')) then
4501 begin
4502 if (P[1][1] = '1') then
4503 Options := Options or GAME_OPTION_TEAMDAMAGE
4504 else
4505 Options := Options and (not GAME_OPTION_TEAMDAMAGE);
4506 end;
4508 if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then
4509 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON])
4510 else
4511 g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]);
4513 if g_Game_IsNet then MH_SEND_GameSettings;
4514 end;
4515 end
4516 else if (cmd = 'g_weaponstay') and not g_Game_IsClient then
4517 begin
4518 with gGameSettings do
4519 begin
4520 if (Length(P) > 1) and
4521 ((P[1] = '1') or (P[1] = '0')) then
4522 begin
4523 if (P[1][1] = '1') then
4524 Options := Options or GAME_OPTION_WEAPONSTAY
4525 else
4526 Options := Options and (not GAME_OPTION_WEAPONSTAY);
4527 end;
4529 if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then
4530 g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON])
4531 else
4532 g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]);
4534 if g_Game_IsNet then MH_SEND_GameSettings;
4535 end;
4536 end
4537 else if cmd = 'g_gamemode' then
4538 begin
4539 a := g_Game_TextToMode(P[1]);
4540 if a = GM_SINGLE then a := GM_COOP;
4541 if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then
4542 begin
4543 gSwitchGameMode := a;
4544 if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or
4545 (gState = STATE_INTERSINGLE) then
4546 gSwitchGameMode := GM_SINGLE;
4547 if not gGameOn then
4548 gGameSettings.GameMode := gSwitchGameMode;
4549 end;
4550 if gSwitchGameMode = gGameSettings.GameMode then
4551 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT],
4552 [g_Game_ModeToText(gGameSettings.GameMode)]))
4553 else
4554 g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CHANGE],
4555 [g_Game_ModeToText(gGameSettings.GameMode),
4556 g_Game_ModeToText(gSwitchGameMode)]));
4557 end
4558 else if (cmd = 'g_allow_exit') and not g_Game_IsClient then
4559 begin
4560 with gGameSettings do
4561 begin
4562 if (Length(P) > 1) and
4563 ((P[1] = '1') or (P[1] = '0')) then
4564 begin
4565 if (P[1][1] = '1') then
4566 Options := Options or GAME_OPTION_ALLOWEXIT
4567 else
4568 Options := Options and (not GAME_OPTION_ALLOWEXIT);
4569 end;
4571 if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then
4572 g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON])
4573 else
4574 g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]);
4575 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4577 if g_Game_IsNet then MH_SEND_GameSettings;
4578 end;
4579 end
4580 else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then
4581 begin
4582 with gGameSettings do
4583 begin
4584 if (Length(P) > 1) and
4585 ((P[1] = '1') or (P[1] = '0')) then
4586 begin
4587 if (P[1][1] = '1') then
4588 Options := Options or GAME_OPTION_MONSTERS
4589 else
4590 Options := Options and (not GAME_OPTION_MONSTERS);
4591 end;
4593 if (LongBool(Options and GAME_OPTION_MONSTERS)) then
4594 g_Console_Add(_lc[I_MSG_ALLOWMON_ON])
4595 else
4596 g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]);
4597 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4599 if g_Game_IsNet then MH_SEND_GameSettings;
4600 end;
4601 end
4602 else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then
4603 begin
4604 with gGameSettings do
4605 begin
4606 if (Length(P) > 1) and
4607 ((P[1] = '1') or (P[1] = '0')) then
4608 begin
4609 if (P[1][1] = '1') then
4610 Options := Options or GAME_OPTION_BOTVSPLAYER
4611 else
4612 Options := Options and (not GAME_OPTION_BOTVSPLAYER);
4613 end;
4615 if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then
4616 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON])
4617 else
4618 g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]);
4620 if g_Game_IsNet then MH_SEND_GameSettings;
4621 end;
4622 end
4623 else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then
4624 begin
4625 with gGameSettings do
4626 begin
4627 if (Length(P) > 1) and
4628 ((P[1] = '1') or (P[1] = '0')) then
4629 begin
4630 if (P[1][1] = '1') then
4631 Options := Options or GAME_OPTION_BOTVSMONSTER
4632 else
4633 Options := Options and (not GAME_OPTION_BOTVSMONSTER);
4634 end;
4636 if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then
4637 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON])
4638 else
4639 g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]);
4641 if g_Game_IsNet then MH_SEND_GameSettings;
4642 end;
4643 end
4644 else if (cmd = 'g_warmuptime') and not g_Game_IsClient then
4645 begin
4646 if Length(P) > 1 then
4647 begin
4648 if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then
4649 gGameSettings.WarmupTime := 30
4650 else
4651 gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime);
4652 end;
4654 g_Console_Add(Format(_lc[I_MSG_WARMUP],
4655 [gGameSettings.WarmupTime]));
4656 g_Console_Add(_lc[I_MSG_ONMAPCHANGE]);
4657 end
4658 else if cmd = 'net_interp' then
4659 begin
4660 if (Length(P) > 1) then
4661 NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
4663 g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
4664 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4665 config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
4666 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4667 config.Free();
4668 end
4669 else if cmd = 'net_forceplayerupdate' then
4670 begin
4671 if (Length(P) > 1) and
4672 ((P[1] = '1') or (P[1] = '0')) then
4673 NetForcePlayerUpdate := (P[1][1] = '1');
4675 if NetForcePlayerUpdate then
4676 g_Console_Add('net_forceplayerupdate = 1')
4677 else
4678 g_Console_Add('net_forceplayerupdate = 0');
4679 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4680 config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
4681 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4682 config.Free();
4683 end
4684 else if cmd = 'net_predictself' then
4685 begin
4686 if (Length(P) > 1) and
4687 ((P[1] = '1') or (P[1] = '0')) then
4688 NetPredictSelf := (P[1][1] = '1');
4690 if NetPredictSelf then
4691 g_Console_Add('net_predictself = 1')
4692 else
4693 g_Console_Add('net_predictself = 0');
4694 config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
4695 config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
4696 config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
4697 config.Free();
4698 end
4699 else if cmd = 'sv_name' then
4700 begin
4701 if (Length(P) > 1) and (Length(P[1]) > 0) then
4702 begin
4703 NetServerName := P[1];
4704 if Length(NetServerName) > 64 then
4705 SetLength(NetServerName, 64);
4706 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4707 g_Net_Slist_Update;
4708 end;
4710 g_Console_Add(cmd + ' = "' + NetServerName + '"');
4711 end
4712 else if cmd = 'sv_passwd' then
4713 begin
4714 if (Length(P) > 1) and (Length(P[1]) > 0) then
4715 begin
4716 NetPassword := P[1];
4717 if Length(NetPassword) > 24 then
4718 SetLength(NetPassword, 24);
4719 if g_Game_IsServer and g_Game_IsNet and NetUseMaster then
4720 g_Net_Slist_Update;
4721 end;
4723 g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"');
4724 end
4725 else if cmd = 'sv_maxplrs' then
4726 begin
4727 if (Length(P) > 1) then
4728 begin
4729 NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS);
4730 if g_Game_IsServer and g_Game_IsNet then
4731 begin
4732 b := 0;
4733 for a := 0 to High(NetClients) do
4734 if NetClients[a].Used then
4735 begin
4736 Inc(b);
4737 if b > NetMaxClients then
4738 begin
4739 s := g_Player_Get(NetClients[a].Player).Name;
4740 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL);
4741 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
4742 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
4743 end;
4744 end;
4745 if NetUseMaster then
4746 g_Net_Slist_Update;
4747 end;
4748 end;
4750 g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients));
4751 end
4752 else if cmd = 'sv_public' then
4753 begin
4754 if (Length(P) > 1) then
4755 begin
4756 NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0;
4757 if g_Game_IsServer and g_Game_IsNet then
4758 if NetUseMaster then
4759 begin
4760 if NetMPeer = nil then
4761 if not g_Net_Slist_Connect() then
4762 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]);
4763 g_Net_Slist_Update();
4764 end
4765 else
4766 if NetMPeer <> nil then
4767 g_Net_Slist_Disconnect();
4768 end;
4770 g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster)));
4771 end
4772 else if cmd = 'sv_intertime' then
4773 begin
4774 if (Length(P) > 1) then
4775 gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120);
4777 g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime));
4778 end
4779 else if cmd = 'p1_name' then
4780 begin
4781 if (Length(P) > 1) and gGameOn then
4782 begin
4783 if g_Game_IsClient then
4784 begin
4785 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4786 MC_SEND_PlayerSettings;
4787 end
4788 else
4789 if gPlayer1 <> nil then
4790 begin
4791 gPlayer1.Name := b_Text_Unformat(P[1]);
4792 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4793 end
4794 else
4795 gPlayer1Settings.Name := b_Text_Unformat(P[1]);
4796 end;
4797 end
4798 else if cmd = 'p2_name' then
4799 begin
4800 if (Length(P) > 1) and gGameOn then
4801 begin
4802 if g_Game_IsClient then
4803 begin
4804 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4805 MC_SEND_PlayerSettings;
4806 end
4807 else
4808 if gPlayer2 <> nil then
4809 begin
4810 gPlayer2.Name := b_Text_Unformat(P[1]);
4811 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4812 end
4813 else
4814 gPlayer2Settings.Name := b_Text_Unformat(P[1]);
4815 end;
4816 end
4817 else if cmd = 'p1_color' then
4818 begin
4819 if Length(P) > 3 then
4820 if g_Game_IsClient then
4821 begin
4822 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4823 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4824 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4825 MC_SEND_PlayerSettings;
4826 end
4827 else
4828 if gPlayer1 <> nil then
4829 begin
4830 gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4831 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4832 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4833 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID);
4834 end
4835 else
4836 gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4837 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4838 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4839 end
4840 else if (cmd = 'p2_color') and not g_Game_IsNet then
4841 begin
4842 if Length(P) > 3 then
4843 if g_Game_IsClient then
4844 begin
4845 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4846 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4847 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4848 MC_SEND_PlayerSettings;
4849 end
4850 else
4851 if gPlayer2 <> nil then
4852 begin
4853 gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4854 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4855 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4856 if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID);
4857 end
4858 else
4859 gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255),
4860 EnsureRange(StrToIntDef(P[2], 0), 0, 255),
4861 EnsureRange(StrToIntDef(P[3], 0), 0, 255));
4862 end
4863 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
4864 begin
4865 if cmd = 'r_showtime' then
4866 begin
4867 if (Length(P) > 1) and
4868 ((P[1] = '1') or (P[1] = '0')) then
4869 gShowTime := (P[1][1] = '1');
4871 if gShowTime then
4872 g_Console_Add(_lc[I_MSG_TIME_ON])
4873 else
4874 g_Console_Add(_lc[I_MSG_TIME_OFF]);
4875 end
4876 else if cmd = 'r_showscore' then
4877 begin
4878 if (Length(P) > 1) and
4879 ((P[1] = '1') or (P[1] = '0')) then
4880 gShowGoals := (P[1][1] = '1');
4882 if gShowGoals then
4883 g_Console_Add(_lc[I_MSG_SCORE_ON])
4884 else
4885 g_Console_Add(_lc[I_MSG_SCORE_OFF]);
4886 end
4887 else if cmd = 'r_showstat' then
4888 begin
4889 if (Length(P) > 1) and
4890 ((P[1] = '1') or (P[1] = '0')) then
4891 gShowStat := (P[1][1] = '1');
4893 if gShowStat then
4894 g_Console_Add(_lc[I_MSG_STATS_ON])
4895 else
4896 g_Console_Add(_lc[I_MSG_STATS_OFF]);
4897 end
4898 else if cmd = 'r_showkillmsg' then
4899 begin
4900 if (Length(P) > 1) and
4901 ((P[1] = '1') or (P[1] = '0')) then
4902 gShowKillMsg := (P[1][1] = '1');
4904 if gShowKillMsg then
4905 g_Console_Add(_lc[I_MSG_KILL_MSGS_ON])
4906 else
4907 g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]);
4908 end
4909 else if cmd = 'r_showlives' then
4910 begin
4911 if (Length(P) > 1) and
4912 ((P[1] = '1') or (P[1] = '0')) then
4913 gShowLives := (P[1][1] = '1');
4915 if gShowLives then
4916 g_Console_Add(_lc[I_MSG_LIVES_ON])
4917 else
4918 g_Console_Add(_lc[I_MSG_LIVES_OFF]);
4919 end
4920 else if cmd = 'r_showspect' then
4921 begin
4922 if (Length(P) > 1) and
4923 ((P[1] = '1') or (P[1] = '0')) then
4924 gSpectHUD := (P[1][1] = '1');
4926 if gSpectHUD then
4927 g_Console_Add(_lc[I_MSG_SPECT_HUD_ON])
4928 else
4929 g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]);
4930 end
4931 else if cmd = 'r_showping' then
4932 begin
4933 if (Length(P) > 1) and
4934 ((P[1] = '1') or (P[1] = '0')) then
4935 gShowPing := (P[1][1] = '1');
4937 if gShowPing then
4938 g_Console_Add(_lc[I_MSG_PING_ON])
4939 else
4940 g_Console_Add(_lc[I_MSG_PING_OFF]);
4941 end
4942 else if (cmd = 'g_scorelimit') and not g_Game_IsClient then
4943 begin
4944 if Length(P) > 1 then
4945 begin
4946 if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then
4947 gGameSettings.GoalLimit := 0
4948 else
4949 begin
4950 b := 0;
4952 if gGameSettings.GameMode = GM_DM then
4953 begin // DM
4954 stat := g_Player_GetStats();
4955 if stat <> nil then
4956 for a := 0 to High(stat) do
4957 if stat[a].Frags > b then
4958 b := stat[a].Frags;
4959 end
4960 else // TDM/CTF
4961 b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals);
4963 gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b);
4964 end;
4966 if g_Game_IsNet then MH_SEND_GameSettings;
4967 end;
4969 g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit]));
4970 end
4971 else if (cmd = 'g_timelimit') and not g_Game_IsClient then
4972 begin
4973 if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then
4974 gGameSettings.TimeLimit := StrToIntDef(P[1], -1);
4976 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
4977 [gGameSettings.TimeLimit div 3600,
4978 (gGameSettings.TimeLimit div 60) mod 60,
4979 gGameSettings.TimeLimit mod 60]));
4980 if g_Game_IsNet then MH_SEND_GameSettings;
4981 end
4982 else if (cmd = 'g_maxlives') and not g_Game_IsClient then
4983 begin
4984 if Length(P) > 1 then
4985 begin
4986 if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then
4987 gGameSettings.MaxLives := 0
4988 else
4989 begin
4990 b := 0;
4991 stat := g_Player_GetStats();
4992 if stat <> nil then
4993 for a := 0 to High(stat) do
4994 if stat[a].Lives > b then
4995 b := stat[a].Lives;
4996 gGameSettings.MaxLives :=
4997 Max(StrToIntDef(P[1], gGameSettings.MaxLives), b);
4998 end;
4999 end;
5001 g_Console_Add(Format(_lc[I_MSG_LIVES],
5002 [gGameSettings.MaxLives]));
5003 if g_Game_IsNet then MH_SEND_GameSettings;
5004 end;
5005 end;
5006 end;
5008 // profiler console commands
5009 procedure ProfilerCommands (P: SArray);
5010 var
5011 cmd: string;
5013 function getBool (idx: Integer): Integer;
5014 begin
5015 if (idx < 0) or (idx > High(P)) then begin result := -1; exit; end;
5016 result := 0;
5017 if (P[idx] = '1') or (P[idx] = 'on') or (P[idx] = 'true') or (P[idx] = 'tan') then result := 1;
5018 end;
5020 begin
5021 //if not gDebugMode then exit;
5022 cmd := LowerCase(P[0]);
5023 if cmd = 'dpp' then
5024 begin
5025 g_profile_frame_draw := not g_profile_frame_draw;
5026 exit;
5027 end;
5028 if cmd = 'dpu' then
5029 begin
5030 g_profile_frame_update := not g_profile_frame_update;
5031 exit;
5032 end;
5033 if cmd = 'dpc' then
5034 begin
5035 g_profile_collision := not g_profile_collision;
5036 exit;
5037 end;
5038 if cmd = 'r_draw_grid' then
5039 begin
5040 case getBool(1) of
5041 -1: begin end;
5042 0: gdbg_map_use_grid_render := false;
5043 1: gdbg_map_use_grid_render := true;
5044 end;
5045 if gdbg_map_use_grid_render then g_Console_Add('grid rendering: tan') else g_Console_Add('grid rendering: ona');
5046 exit;
5047 end;
5048 if cmd = 'dbg_coldet_grid' then
5049 begin
5050 case getBool(1) of
5051 -1: begin end;
5052 0: gdbg_map_use_grid_coldet := false;
5053 1: gdbg_map_use_grid_coldet := true;
5054 end;
5055 if gdbg_map_use_grid_coldet then g_Console_Add('grid coldet: tan') else g_Console_Add('grid coldet: ona');
5056 exit;
5057 end;
5058 end;
5060 procedure DebugCommands(P: SArray);
5061 var
5062 a, b: Integer;
5063 cmd: string;
5064 //pt: TPoint;
5065 begin
5066 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5067 if gDebugMode then
5068 begin
5069 cmd := LowerCase(P[0]);
5070 if cmd = 'd_window' then
5071 begin
5072 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5073 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5074 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5075 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5076 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5077 end
5078 else if cmd = 'd_sounds' then
5079 begin
5080 if (Length(P) > 1) and
5081 ((P[1] = '1') or (P[1] = '0')) then
5082 g_Debug_Sounds := (P[1][1] = '1');
5084 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5085 end
5086 else if cmd = 'd_frames' then
5087 begin
5088 if (Length(P) > 1) and
5089 ((P[1] = '1') or (P[1] = '0')) then
5090 g_Debug_Frames := (P[1][1] = '1');
5092 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5093 end
5094 else if cmd = 'd_winmsg' then
5095 begin
5096 if (Length(P) > 1) and
5097 ((P[1] = '1') or (P[1] = '0')) then
5098 g_Debug_WinMsgs := (P[1][1] = '1');
5100 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5101 end
5102 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5103 begin
5104 if (Length(P) > 1) and
5105 ((P[1] = '1') or (P[1] = '0')) then
5106 g_Debug_MonsterOff := (P[1][1] = '1');
5108 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5109 end
5110 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5111 begin
5112 if Length(P) > 1 then
5113 case P[1][1] of
5114 '0': g_debug_BotAIOff := 0;
5115 '1': g_debug_BotAIOff := 1;
5116 '2': g_debug_BotAIOff := 2;
5117 '3': g_debug_BotAIOff := 3;
5118 end;
5120 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5121 end
5122 else if cmd = 'd_monster' then
5123 begin
5124 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
5125 if Length(P) < 2 then
5126 begin
5127 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5128 g_Console_Add('ID | Name');
5129 for b := MONSTER_DEMON to MONSTER_MAN do
5130 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
5131 end else
5132 begin
5133 a := StrToIntDef(P[1], 0);
5134 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5135 a := g_Monsters_GetIDByName(P[1]);
5137 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5138 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5139 else
5140 begin
5141 with gPlayer1.Obj do
5142 b := g_Monsters_Create(a,
5143 X + Rect.X + (Rect.Width div 2),
5144 Y + Rect.Y + Rect.Height,
5145 gPlayer1.Direction, True);
5146 if (Length(P) > 2) and (b >= 0) then
5147 gMonsters[b].MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5148 end;
5149 end;
5150 end
5151 else if (cmd = 'd_health') then
5152 begin
5153 if (Length(P) > 1) and
5154 ((P[1] = '1') or (P[1] = '0')) then
5155 g_debug_HealthBar := (P[1][1] = '1');
5157 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5158 end
5159 else if (cmd = 'd_player') then
5160 begin
5161 if (Length(P) > 1) and
5162 ((P[1] = '1') or (P[1] = '0')) then
5163 g_debug_Player := (P[1][1] = '1');
5165 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5166 end
5167 else if (cmd = 'd_joy') then
5168 begin
5169 for a := 1 to 8 do
5170 g_Console_Add(e_JoystickStateToString(a));
5171 end;
5172 end
5173 else
5174 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5175 end;
5178 procedure GameCheats(P: SArray);
5179 var
5180 cmd: string;
5181 f, a: Integer;
5182 plr: TPlayer;
5183 begin
5184 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5185 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5186 begin
5187 g_Console_Add('not available');
5188 exit;
5189 end;
5190 plr := gPlayer1;
5191 if plr = nil then
5192 begin
5193 g_Console_Add('where is the player?!');
5194 exit;
5195 end;
5196 cmd := LowerCase(P[0]);
5197 // god
5198 if cmd = 'god' then
5199 begin
5200 plr.GodMode := not plr.GodMode;
5201 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5202 exit;
5203 end;
5204 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5205 if cmd = 'give' then
5206 begin
5207 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5208 for f := 1 to High(P) do
5209 begin
5210 cmd := LowerCase(P[f]);
5211 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5212 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5213 if cmd = 'exit' then
5214 begin
5215 if gTriggers <> nil then
5216 begin
5217 for a := 0 to High(gTriggers) do
5218 begin
5219 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5220 begin
5221 g_Console_Add('player left the map');
5222 gExitByTrigger := True;
5223 g_Game_ExitLevel(gTriggers[a].Data.MapName);
5224 break;
5225 end;
5226 end;
5227 end;
5228 continue;
5229 end;
5231 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5232 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5233 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5234 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5235 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5237 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5238 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5240 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5241 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;
5243 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5244 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5246 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5247 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5249 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5250 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5252 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5253 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5254 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5256 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5257 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5258 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5259 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;
5260 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5261 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5263 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;
5264 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;
5265 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;
5266 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;
5267 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;
5268 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;
5270 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5271 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;
5273 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;
5274 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;
5276 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5278 if cmd = 'ammo' then
5279 begin
5280 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5281 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5282 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5283 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5284 plr.GiveItem(ITEM_AMMO_FUELCAN);
5285 g_Console_Add('player got some ammo');
5286 continue;
5287 end;
5289 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5290 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5292 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5293 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5295 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5296 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5298 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5299 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5301 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5303 if cmd = 'weapons' then
5304 begin
5305 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5306 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5307 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5308 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5309 plr.GiveItem(ITEM_WEAPON_PLASMA);
5310 plr.GiveItem(ITEM_WEAPON_BFG);
5311 g_Console_Add('player got weapons');
5312 continue;
5313 end;
5315 if cmd = 'keys' then
5316 begin
5317 plr.GiveItem(ITEM_KEY_RED);
5318 plr.GiveItem(ITEM_KEY_GREEN);
5319 plr.GiveItem(ITEM_KEY_BLUE);
5320 g_Console_Add('player got all keys');
5321 continue;
5322 end;
5324 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5325 end;
5326 exit;
5327 end;
5328 // open
5329 if cmd = 'open' then
5330 begin
5331 g_Console_Add('player activated sesame');
5332 g_Triggers_OpenAll();
5333 exit;
5334 end;
5335 // fly
5336 if cmd = 'fly' then
5337 begin
5338 gFly := not gFly;
5339 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5340 exit;
5341 end;
5342 // noclip
5343 if cmd = 'noclip' then
5344 begin
5345 plr.SwitchNoClip;
5346 g_Console_Add('wall hardeness adjusted');
5347 exit;
5348 end;
5349 // notarget
5350 if cmd = 'notarget' then
5351 begin
5352 plr.NoTarget := not plr.NoTarget;
5353 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5354 exit;
5355 end;
5356 // noreload
5357 if cmd = 'noreload' then
5358 begin
5359 plr.NoReload := not plr.NoReload;
5360 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5361 exit;
5362 end;
5363 // speedy
5364 if cmd = 'speedy' then
5365 begin
5366 MAX_RUNVEL := 32-MAX_RUNVEL;
5367 g_Console_Add('speed adjusted');
5368 exit;
5369 end;
5370 // jumpy
5371 if cmd = 'jumpy' then
5372 begin
5373 VEL_JUMP := 30-VEL_JUMP;
5374 g_Console_Add('jump height adjusted');
5375 exit;
5376 end;
5377 // automap
5378 if cmd = 'automap' then
5379 begin
5380 gShowMap := not gShowMap;
5381 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5382 exit;
5383 end;
5384 // aimline
5385 if cmd = 'aimline' then
5386 begin
5387 gAimLine := not gAimLine;
5388 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5389 exit;
5390 end;
5391 end;
5393 procedure GameCommands(P: SArray);
5394 var
5395 a, b: Integer;
5396 s, pw: String;
5397 chstr: string;
5398 cmd: string;
5399 pl: pTNetClient = nil;
5400 plr: TPlayer;
5401 prt: Word;
5402 nm: Boolean;
5403 listen: LongWord;
5404 begin
5405 // Îáùèå êîìàíäû:
5406 cmd := LowerCase(P[0]);
5407 chstr := '';
5408 if (cmd = 'quit') or
5409 (cmd = 'exit') then
5410 begin
5411 g_Game_Free();
5412 g_Game_Quit();
5413 Exit;
5414 end
5415 else if cmd = 'pause' then
5416 begin
5417 if (g_ActiveWindow = nil) then
5418 g_Game_Pause(not gPause);
5419 end
5420 else if cmd = 'endgame' then
5421 gExit := EXIT_SIMPLE
5422 else if cmd = 'restart' then
5423 begin
5424 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5425 begin
5426 if g_Game_IsClient then
5427 begin
5428 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5429 Exit;
5430 end;
5431 g_Game_Restart();
5432 end else
5433 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5434 end
5435 else if cmd = 'kick' then
5436 begin
5437 if g_Game_IsServer then
5438 begin
5439 if Length(P) < 2 then
5440 begin
5441 g_Console_Add('kick <name>');
5442 Exit;
5443 end;
5444 if P[1] = '' then
5445 begin
5446 g_Console_Add('kick <name>');
5447 Exit;
5448 end;
5450 if g_Game_IsNet then
5451 pl := g_Net_Client_ByName(P[1]);
5452 if (pl <> nil) then
5453 begin
5454 s := g_Net_ClientName_ByID(pl^.ID);
5455 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5456 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5457 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5458 if NetUseMaster then
5459 g_Net_Slist_Update;
5460 end else if gPlayers <> nil then
5461 for a := Low(gPlayers) to High(gPlayers) do
5462 if gPlayers[a] <> nil then
5463 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5464 begin
5465 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5466 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5467 continue;
5468 gPlayers[a].Lives := 0;
5469 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5470 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5471 g_Player_Remove(gPlayers[a].UID);
5472 if NetUseMaster then
5473 g_Net_Slist_Update;
5474 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5475 g_Bot_MixNames();
5476 end;
5477 end else
5478 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5479 end
5480 else if cmd = 'kick_id' then
5481 begin
5482 if g_Game_IsServer and g_Game_IsNet then
5483 begin
5484 if Length(P) < 2 then
5485 begin
5486 g_Console_Add('kick_id <client ID>');
5487 Exit;
5488 end;
5489 if P[1] = '' then
5490 begin
5491 g_Console_Add('kick_id <client ID>');
5492 Exit;
5493 end;
5495 a := StrToIntDef(P[1], 0);
5496 if (NetClients <> nil) and (a <= High(NetClients)) then
5497 begin
5498 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5499 begin
5500 s := g_Net_ClientName_ByID(NetClients[a].ID);
5501 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5502 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5503 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5504 if NetUseMaster then
5505 g_Net_Slist_Update;
5506 end;
5507 end;
5508 end else
5509 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5510 end
5511 else if cmd = 'ban' then
5512 begin
5513 if g_Game_IsServer and g_Game_IsNet then
5514 begin
5515 if Length(P) < 2 then
5516 begin
5517 g_Console_Add('ban <name>');
5518 Exit;
5519 end;
5520 if P[1] = '' then
5521 begin
5522 g_Console_Add('ban <name>');
5523 Exit;
5524 end;
5526 pl := g_Net_Client_ByName(P[1]);
5527 if (pl <> nil) then
5528 begin
5529 s := g_Net_ClientName_ByID(pl^.ID);
5530 g_Net_BanHost(pl^.Peer^.address.host, False);
5531 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5532 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5533 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5534 if NetUseMaster then
5535 g_Net_Slist_Update;
5536 end else
5537 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5538 end else
5539 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5540 end
5541 else if cmd = 'ban_id' then
5542 begin
5543 if g_Game_IsServer and g_Game_IsNet then
5544 begin
5545 if Length(P) < 2 then
5546 begin
5547 g_Console_Add('ban_id <client ID>');
5548 Exit;
5549 end;
5550 if P[1] = '' then
5551 begin
5552 g_Console_Add('ban_id <client ID>');
5553 Exit;
5554 end;
5556 a := StrToIntDef(P[1], 0);
5557 if (NetClients <> nil) and (a <= High(NetClients)) then
5558 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5559 begin
5560 s := g_Net_ClientName_ByID(NetClients[a].ID);
5561 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5562 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5563 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5564 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5565 if NetUseMaster then
5566 g_Net_Slist_Update;
5567 end;
5568 end else
5569 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5570 end
5571 else if cmd = 'permban' then
5572 begin
5573 if g_Game_IsServer and g_Game_IsNet then
5574 begin
5575 if Length(P) < 2 then
5576 begin
5577 g_Console_Add('permban <name>');
5578 Exit;
5579 end;
5580 if P[1] = '' then
5581 begin
5582 g_Console_Add('permban <name>');
5583 Exit;
5584 end;
5586 pl := g_Net_Client_ByName(P[1]);
5587 if (pl <> nil) then
5588 begin
5589 s := g_Net_ClientName_ByID(pl^.ID);
5590 g_Net_BanHost(pl^.Peer^.address.host);
5591 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5592 g_Net_SaveBanList();
5593 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5594 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5595 if NetUseMaster then
5596 g_Net_Slist_Update;
5597 end else
5598 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5599 end else
5600 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5601 end
5602 else if cmd = 'permban_id' then
5603 begin
5604 if g_Game_IsServer and g_Game_IsNet then
5605 begin
5606 if Length(P) < 2 then
5607 begin
5608 g_Console_Add('permban_id <client ID>');
5609 Exit;
5610 end;
5611 if P[1] = '' then
5612 begin
5613 g_Console_Add('permban_id <client ID>');
5614 Exit;
5615 end;
5617 a := StrToIntDef(P[1], 0);
5618 if (NetClients <> nil) and (a <= High(NetClients)) then
5619 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5620 begin
5621 s := g_Net_ClientName_ByID(NetClients[a].ID);
5622 g_Net_BanHost(NetClients[a].Peer^.address.host);
5623 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5624 g_Net_SaveBanList();
5625 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5626 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5627 if NetUseMaster then
5628 g_Net_Slist_Update;
5629 end;
5630 end else
5631 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5632 end
5633 else if cmd = 'unban' then
5634 begin
5635 if g_Game_IsServer and g_Game_IsNet then
5636 begin
5637 if Length(P) < 2 then
5638 begin
5639 g_Console_Add('unban <IP Address>');
5640 Exit;
5641 end;
5642 if P[1] = '' then
5643 begin
5644 g_Console_Add('unban <IP Address>');
5645 Exit;
5646 end;
5648 if g_Net_UnbanHost(P[1]) then
5649 begin
5650 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5651 g_Net_SaveBanList();
5652 end else
5653 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5654 end else
5655 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5656 end
5657 else if cmd = 'clientlist' then
5658 begin
5659 if g_Game_IsServer and g_Game_IsNet then
5660 begin
5661 b := 0;
5662 if NetClients <> nil then
5663 for a := Low(NetClients) to High(NetClients) do
5664 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5665 begin
5666 plr := g_Player_Get(NetClients[a].Player);
5667 if plr = nil then continue;
5668 Inc(b);
5669 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5670 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5671 end;
5672 if b = 0 then
5673 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5674 end else
5675 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5676 end
5677 else if cmd = 'connect' then
5678 begin
5679 if (NetMode = NET_NONE) then
5680 begin
5681 if Length(P) < 2 then
5682 begin
5683 g_Console_Add('connect <IP> [port] [password]');
5684 Exit;
5685 end;
5686 if P[1] = '' then
5687 begin
5688 g_Console_Add('connect <IP> [port] [password]');
5689 Exit;
5690 end;
5692 if Length(P) > 2 then
5693 prt := StrToIntDef(P[2], 25666)
5694 else
5695 prt := 25666;
5697 if Length(P) > 3 then
5698 pw := P[3]
5699 else
5700 pw := '';
5702 g_Game_StartClient(P[1], prt, pw);
5703 end;
5704 end
5705 else if cmd = 'disconnect' then
5706 begin
5707 if (NetMode = NET_CLIENT) then
5708 g_Net_Disconnect();
5709 end
5710 else if cmd = 'reconnect' then
5711 begin
5712 if (NetMode = NET_SERVER) then
5713 Exit;
5715 if (NetMode = NET_CLIENT) then
5716 begin
5717 g_Net_Disconnect();
5718 gExit := EXIT_SIMPLE;
5719 EndGame;
5720 end;
5722 //TODO: Use last successful password to reconnect, instead of ''
5723 g_Game_StartClient(NetClientIP, NetClientPort, '');
5724 end
5725 else if (cmd = 'addbot') or
5726 (cmd = 'bot_add') then
5727 begin
5728 if Length(P) > 1 then
5729 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5730 else
5731 g_Bot_Add(TEAM_NONE, 2);
5732 end
5733 else if cmd = 'bot_addlist' then
5734 begin
5735 if Length(P) > 1 then
5736 if Length(P) = 2 then
5737 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5738 else
5739 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5740 end
5741 else if cmd = 'bot_removeall' then
5742 g_Bot_RemoveAll()
5743 else if cmd = 'chat' then
5744 begin
5745 if g_Game_IsNet then
5746 begin
5747 if Length(P) > 1 then
5748 begin
5749 for a := 1 to High(P) do
5750 chstr := chstr + P[a] + ' ';
5752 if Length(chstr) > 200 then SetLength(chstr, 200);
5754 if Length(chstr) < 1 then
5755 begin
5756 g_Console_Add('chat <text>');
5757 Exit;
5758 end;
5760 chstr := b_Text_Format(chstr);
5761 if g_Game_IsClient then
5762 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5763 else
5764 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5765 end
5766 else
5767 g_Console_Add('chat <text>');
5768 end else
5769 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5770 end
5771 else if cmd = 'teamchat' then
5772 begin
5773 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5774 begin
5775 if Length(P) > 1 then
5776 begin
5777 for a := 1 to High(P) do
5778 chstr := chstr + P[a] + ' ';
5780 if Length(chstr) > 200 then SetLength(chstr, 200);
5782 if Length(chstr) < 1 then
5783 begin
5784 g_Console_Add('teamchat <text>');
5785 Exit;
5786 end;
5788 chstr := b_Text_Format(chstr);
5789 if g_Game_IsClient then
5790 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5791 else
5792 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5793 gPlayer1Settings.Team);
5794 end
5795 else
5796 g_Console_Add('teamchat <text>');
5797 end else
5798 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5799 end
5800 else if cmd = 'game' then
5801 begin
5802 if gGameSettings.GameType <> GT_NONE then
5803 begin
5804 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5805 Exit;
5806 end;
5807 if Length(P) = 1 then
5808 begin
5809 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5810 Exit;
5811 end;
5812 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5813 P[1] := addWadExtension(P[1]);
5814 if FileExists(MapsDir + P[1]) then
5815 begin
5816 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5817 if Length(P) < 3 then
5818 begin
5819 SetLength(P, 3);
5820 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5821 end;
5823 s := P[1] + ':\' + UpperCase(P[2]);
5825 if g_Map_Exist(MapsDir + s) then
5826 begin
5827 // Çàïóñêàåì ñâîþ èãðó
5828 g_Game_Free();
5829 with gGameSettings do
5830 begin
5831 GameMode := g_Game_TextToMode(gcGameMode);
5832 if gSwitchGameMode <> GM_NONE then
5833 GameMode := gSwitchGameMode;
5834 if GameMode = GM_NONE then GameMode := GM_DM;
5835 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5836 b := 1;
5837 if Length(P) >= 4 then
5838 b := StrToIntDef(P[3], 1);
5839 g_Game_StartCustom(s, GameMode, TimeLimit,
5840 GoalLimit, MaxLives, Options, b);
5841 end;
5842 end
5843 else
5844 if P[2] = '' then
5845 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5846 else
5847 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5848 end else
5849 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5850 end
5851 else if cmd = 'host' then
5852 begin
5853 if gGameSettings.GameType <> GT_NONE then
5854 begin
5855 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5856 Exit;
5857 end;
5858 if Length(P) < 4 then
5859 begin
5860 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5861 Exit;
5862 end;
5863 if not StrToIp(P[1], listen) then
5864 Exit;
5865 prt := StrToIntDef(P[2], 25666);
5867 P[3] := addWadExtension(P[3]);
5868 if FileExists(MapsDir + P[3]) then
5869 begin
5870 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5871 if Length(P) < 5 then
5872 begin
5873 SetLength(P, 5);
5874 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5875 end;
5877 s := P[3] + ':\' + UpperCase(P[4]);
5879 if g_Map_Exist(MapsDir + s) then
5880 begin
5881 // Çàïóñêàåì ñâîþ èãðó
5882 g_Game_Free();
5883 with gGameSettings do
5884 begin
5885 GameMode := g_Game_TextToMode(gcGameMode);
5886 if gSwitchGameMode <> GM_NONE then
5887 GameMode := gSwitchGameMode;
5888 if GameMode = GM_NONE then GameMode := GM_DM;
5889 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5890 b := 0;
5891 if Length(P) >= 6 then
5892 b := StrToIntDef(P[5], 0);
5893 g_Game_StartServer(s, GameMode, TimeLimit,
5894 GoalLimit, MaxLives, Options, b, listen, prt);
5895 end;
5896 end
5897 else
5898 if P[4] = '' then
5899 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5900 else
5901 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5902 end else
5903 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5904 end
5905 else if cmd = 'map' then
5906 begin
5907 if Length(P) = 1 then
5908 begin
5909 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5910 begin
5911 g_Console_Add(cmd + ' <MAP>');
5912 g_Console_Add(cmd + ' <WAD> [MAP]');
5913 end else
5914 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5915 end else
5916 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5917 begin
5918 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5919 if Length(P) < 3 then
5920 begin
5921 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5922 s := UpperCase(P[1]);
5923 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5924 begin // Êàðòà íàøëàñü
5925 gExitByTrigger := False;
5926 if gGameOn then
5927 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5928 gNextMap := s;
5929 gExit := EXIT_ENDLEVELCUSTOM;
5930 end
5931 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5932 g_Game_ChangeMap(s);
5933 end else
5934 begin
5935 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5936 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5937 P[1] := addWadExtension(P[1]);
5938 if FileExists(MapsDir + P[1]) then
5939 begin
5940 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5941 SetLength(P, 3);
5942 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5944 s := P[1] + ':\' + P[2];
5946 if g_Map_Exist(MapsDir + s) then
5947 begin
5948 gExitByTrigger := False;
5949 if gGameOn then
5950 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5951 gNextMap := s;
5952 gExit := EXIT_ENDLEVELCUSTOM;
5953 end
5954 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5955 g_Game_ChangeMap(s);
5956 end else
5957 if P[2] = '' then
5958 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5959 else
5960 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5961 end else
5962 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5963 end;
5964 end else
5965 begin
5966 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5967 P[1] := addWadExtension(P[1]);
5968 if FileExists(MapsDir + P[1]) then
5969 begin
5970 // Íàøëè WAD ôàéë
5971 P[2] := UpperCase(P[2]);
5972 s := P[1] + ':\' + P[2];
5974 if g_Map_Exist(MapsDir + s) then
5975 begin // Íàøëè êàðòó
5976 gExitByTrigger := False;
5977 if gGameOn then
5978 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5979 gNextMap := s;
5980 gExit := EXIT_ENDLEVELCUSTOM;
5981 end
5982 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5983 g_Game_ChangeMap(s);
5984 end else
5985 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5986 end else
5987 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5988 end;
5989 end else
5990 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5991 end
5992 else if cmd = 'nextmap' then
5993 begin
5994 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5995 g_Console_Add(_lc[I_MSG_NOT_GAME])
5996 else begin
5997 nm := True;
5998 if Length(P) = 1 then
5999 begin
6000 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6001 begin
6002 g_Console_Add(cmd + ' <MAP>');
6003 g_Console_Add(cmd + ' <WAD> [MAP]');
6004 end else begin
6005 nm := False;
6006 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6007 end;
6008 end else
6009 begin
6010 nm := False;
6011 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6012 begin
6013 if Length(P) < 3 then
6014 begin
6015 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6016 s := UpperCase(P[1]);
6017 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6018 begin // Êàðòà íàøëàñü
6019 gExitByTrigger := False;
6020 gNextMap := s;
6021 nm := True;
6022 end else
6023 begin
6024 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6025 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6026 P[1] := addWadExtension(P[1]);
6027 if FileExists(MapsDir + P[1]) then
6028 begin
6029 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6030 SetLength(P, 3);
6031 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6033 s := P[1] + ':\' + P[2];
6035 if g_Map_Exist(MapsDir + s) then
6036 begin // Óñòàíàâëèâàåì êàðòó
6037 gExitByTrigger := False;
6038 gNextMap := s;
6039 nm := True;
6040 end else
6041 if P[2] = '' then
6042 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6043 else
6044 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6045 end else
6046 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6047 end;
6048 end else
6049 begin
6050 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6051 P[1] := addWadExtension(P[1]);
6052 if FileExists(MapsDir + P[1]) then
6053 begin
6054 // Íàøëè WAD ôàéë
6055 P[2] := UpperCase(P[2]);
6056 s := P[1] + ':\' + P[2];
6058 if g_Map_Exist(MapsDir + s) then
6059 begin // Íàøëè êàðòó
6060 gExitByTrigger := False;
6061 gNextMap := s;
6062 nm := True;
6063 end else
6064 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6065 end else
6066 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6067 end;
6068 end else
6069 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6070 end;
6071 if nm then
6072 if gNextMap = '' then
6073 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6074 else
6075 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6076 end;
6077 end
6078 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6079 begin
6080 if not gGameOn then
6081 g_Console_Add(_lc[I_MSG_NOT_GAME])
6082 else
6083 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6084 begin
6085 gExitByTrigger := False;
6086 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6087 if (gNextMap = '') and (gTriggers <> nil) then
6088 for a := 0 to High(gTriggers) do
6089 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6090 begin
6091 gExitByTrigger := True;
6092 gNextMap := gTriggers[a].Data.MapName;
6093 Break;
6094 end;
6095 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6096 if gNextMap = '' then
6097 gNextMap := g_Game_GetNextMap();
6098 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6099 if Pos(':\', gNextMap) = 0 then
6100 s := gGameSettings.WAD + ':\' + gNextMap
6101 else
6102 s := gNextMap;
6103 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6104 if g_Map_Exist(MapsDir + s) then
6105 gExit := EXIT_ENDLEVELCUSTOM
6106 else
6107 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6108 end else
6109 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6110 end
6111 else if (cmd = 'event') then
6112 begin
6113 if (Length(P) <= 1) then
6114 begin
6115 for a := 0 to High(gEvents) do
6116 if gEvents[a].Command = '' then
6117 g_Console_Add(gEvents[a].Name + ' <none>')
6118 else
6119 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6120 Exit;
6121 end;
6122 if (Length(P) = 2) then
6123 begin
6124 for a := 0 to High(gEvents) do
6125 if gEvents[a].Name = P[1] then
6126 if gEvents[a].Command = '' then
6127 g_Console_Add(gEvents[a].Name + ' <none>')
6128 else
6129 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6130 Exit;
6131 end;
6132 for a := 0 to High(gEvents) do
6133 if gEvents[a].Name = P[1] then
6134 begin
6135 gEvents[a].Command := '';
6136 for b := 2 to High(P) do
6137 if Pos(' ', P[b]) = 0 then
6138 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6139 else
6140 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6141 gEvents[a].Command := Trim(gEvents[a].Command);
6142 Exit;
6143 end;
6144 end
6145 // Êîìàíäû Ñâîåé èãðû:
6146 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6147 begin
6148 if cmd = 'bot_addred' then
6149 begin
6150 if Length(P) > 1 then
6151 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6152 else
6153 g_Bot_Add(TEAM_RED, 2);
6154 end
6155 else if cmd = 'bot_addblue' then
6156 begin
6157 if Length(P) > 1 then
6158 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6159 else
6160 g_Bot_Add(TEAM_BLUE, 2);
6161 end
6162 else if cmd = 'suicide' then
6163 begin
6164 if gGameOn then
6165 begin
6166 if g_Game_IsClient then
6167 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6168 else
6169 begin
6170 if gPlayer1 <> nil then
6171 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6172 if gPlayer2 <> nil then
6173 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6174 end;
6175 end;
6176 end
6177 else if cmd = 'spectate' then
6178 begin
6179 if not gGameOn then
6180 Exit;
6181 g_Game_Spectate();
6182 end
6183 else if cmd = 'say' then
6184 begin
6185 if g_Game_IsServer and g_Game_IsNet then
6186 begin
6187 if Length(P) > 1 then
6188 begin
6189 chstr := '';
6190 for a := 1 to High(P) do
6191 chstr := chstr + P[a] + ' ';
6193 if Length(chstr) > 200 then SetLength(chstr, 200);
6195 if Length(chstr) < 1 then
6196 begin
6197 g_Console_Add('say <text>');
6198 Exit;
6199 end;
6201 chstr := b_Text_Format(chstr);
6202 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6203 end
6204 else g_Console_Add('say <text>');
6205 end else
6206 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6207 end
6208 else if cmd = 'tell' then
6209 begin
6210 if g_Game_IsServer and g_Game_IsNet then
6211 begin
6212 if (Length(P) > 2) and (P[1] <> '') then
6213 begin
6214 chstr := '';
6215 for a := 2 to High(P) do
6216 chstr := chstr + P[a] + ' ';
6218 if Length(chstr) > 200 then SetLength(chstr, 200);
6220 if Length(chstr) < 1 then
6221 begin
6222 g_Console_Add('tell <playername> <text>');
6223 Exit;
6224 end;
6226 pl := g_Net_Client_ByName(P[1]);
6227 if pl <> nil then
6228 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6229 else
6230 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6231 end
6232 else g_Console_Add('tell <playername> <text>');
6233 end else
6234 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6235 end
6236 else if (cmd = 'overtime') and not g_Game_IsClient then
6237 begin
6238 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6239 Exit;
6240 // Äîïîëíèòåëüíîå âðåìÿ:
6241 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6243 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6244 [gGameSettings.TimeLimit div 3600,
6245 (gGameSettings.TimeLimit div 60) mod 60,
6246 gGameSettings.TimeLimit mod 60]));
6247 if g_Game_IsNet then MH_SEND_GameSettings;
6248 end
6249 else if (cmd = 'rcon_password') and g_Game_IsClient then
6250 begin
6251 if (Length(P) <= 1) then
6252 g_Console_Add('rcon_password <password>')
6253 else
6254 MC_SEND_RCONPassword(P[1]);
6255 end
6256 else if cmd = 'rcon' then
6257 begin
6258 if g_Game_IsClient then
6259 begin
6260 if Length(P) > 1 then
6261 begin
6262 chstr := '';
6263 for a := 1 to High(P) do
6264 chstr := chstr + P[a] + ' ';
6266 if Length(chstr) > 200 then SetLength(chstr, 200);
6268 if Length(chstr) < 1 then
6269 begin
6270 g_Console_Add('rcon <command>');
6271 Exit;
6272 end;
6274 MC_SEND_RCONCommand(chstr);
6275 end
6276 else g_Console_Add('rcon <command>');
6277 end;
6278 end
6279 else if cmd = 'ready' then
6280 begin
6281 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6282 gLMSRespawnTime := gTime + 100;
6283 end
6284 else if (cmd = 'callvote') and g_Game_IsNet then
6285 begin
6286 if Length(P) > 1 then
6287 begin
6288 chstr := '';
6289 for a := 1 to High(P) do begin
6290 if a > 1 then chstr := chstr + ' ';
6291 chstr := chstr + P[a];
6292 end;
6294 if Length(chstr) > 200 then SetLength(chstr, 200);
6296 if Length(chstr) < 1 then
6297 begin
6298 g_Console_Add('callvote <command>');
6299 Exit;
6300 end;
6302 if g_Game_IsClient then
6303 MC_SEND_Vote(True, chstr)
6304 else
6305 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6306 g_Console_Process('vote', True);
6307 end
6308 else
6309 g_Console_Add('callvote <command>');
6310 end
6311 else if (cmd = 'vote') and g_Game_IsNet then
6312 begin
6313 if g_Game_IsClient then
6314 MC_SEND_Vote(False)
6315 else if gVoteInProgress then
6316 begin
6317 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6318 a := Floor((NetClientCount+1)/2.0) + 1
6319 else
6320 a := Floor(NetClientCount/2.0) + 1;
6321 if gVoted then
6322 begin
6323 Dec(gVoteCount);
6324 gVoted := False;
6325 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6326 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6327 end
6328 else
6329 begin
6330 Inc(gVoteCount);
6331 gVoted := True;
6332 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6333 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6334 g_Game_CheckVote;
6335 end;
6336 end;
6337 end
6338 end;
6339 end;
6341 procedure g_TakeScreenShot();
6342 var
6343 a: Word;
6344 FileName: string;
6345 ssdir, t: string;
6346 st: TStream;
6347 ok: Boolean;
6348 begin
6349 if e_NoGraphics then Exit;
6350 ssdir := GameDir+'/screenshots';
6351 if not findFileCI(ssdir, true) then
6352 begin
6353 // try to create dir
6354 try
6355 CreateDir(ssdir);
6356 except
6357 end;
6358 if not findFileCI(ssdir, true) then exit; // alas
6359 end;
6360 try
6361 for a := 1 to High(Word) do
6362 begin
6363 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6364 t := FileName;
6365 if findFileCI(t, true) then continue;
6366 if not findFileCI(FileName) then
6367 begin
6368 ok := false;
6369 st := createDiskFile(FileName);
6370 try
6371 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6372 ok := true;
6373 finally
6374 st.Free();
6375 end;
6376 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6377 break;
6378 end;
6379 end;
6380 except
6381 end;
6382 end;
6384 procedure g_Game_InGameMenu(Show: Boolean);
6385 begin
6386 if (g_ActiveWindow = nil) and Show then
6387 begin
6388 if gGameSettings.GameType = GT_SINGLE then
6389 g_GUI_ShowWindow('GameSingleMenu')
6390 else
6391 begin
6392 if g_Game_IsClient then
6393 g_GUI_ShowWindow('GameClientMenu')
6394 else
6395 if g_Game_IsNet then
6396 g_GUI_ShowWindow('GameServerMenu')
6397 else
6398 g_GUI_ShowWindow('GameCustomMenu');
6399 end;
6400 g_Sound_PlayEx('MENU_OPEN');
6402 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6403 if (not g_Game_IsNet) then
6404 g_Game_Pause(True);
6405 end
6406 else
6407 if (g_ActiveWindow <> nil) and (not Show) then
6408 begin
6409 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6410 if (not g_Game_IsNet) then
6411 g_Game_Pause(False);
6412 end;
6413 end;
6415 procedure g_Game_Pause(Enable: Boolean);
6416 begin
6417 if not gGameOn then
6418 Exit;
6420 if gPause = Enable then
6421 Exit;
6423 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6424 Exit;
6426 gPause := Enable;
6427 g_Game_PauseAllSounds(Enable);
6428 end;
6430 procedure g_Game_PauseAllSounds(Enable: Boolean);
6431 var
6432 i: Integer;
6433 begin
6434 // Òðèããåðû:
6435 if gTriggers <> nil then
6436 for i := 0 to High(gTriggers) do
6437 with gTriggers[i] do
6438 if (TriggerType = TRIGGER_SOUND) and
6439 (Sound <> nil) and
6440 Sound.IsPlaying() then
6441 begin
6442 Sound.Pause(Enable);
6443 end;
6445 // Çâóêè èãðîêîâ:
6446 if gPlayers <> nil then
6447 for i := 0 to High(gPlayers) do
6448 if gPlayers[i] <> nil then
6449 gPlayers[i].PauseSounds(Enable);
6451 // Ìóçûêà:
6452 if gMusic <> nil then
6453 gMusic.Pause(Enable);
6454 end;
6456 procedure g_Game_StopAllSounds(all: Boolean);
6457 var
6458 i: Integer;
6459 begin
6460 if gTriggers <> nil then
6461 for i := 0 to High(gTriggers) do
6462 with gTriggers[i] do
6463 if (TriggerType = TRIGGER_SOUND) and
6464 (Sound <> nil) then
6465 Sound.Stop();
6467 if gMusic <> nil then
6468 gMusic.Stop();
6470 if all then
6471 e_StopChannels();
6472 end;
6474 procedure g_Game_UpdateTriggerSounds();
6475 var
6476 i: Integer;
6477 begin
6478 if gTriggers <> nil then
6479 for i := 0 to High(gTriggers) do
6480 with gTriggers[i] do
6481 if (TriggerType = TRIGGER_SOUND) and
6482 (Sound <> nil) and
6483 (Data.Local) and
6484 Sound.IsPlaying() then
6485 begin
6486 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6487 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6488 begin
6489 Sound.SetPan(0.5 - Data.Pan/255.0);
6490 Sound.SetVolume(Data.Volume/255.0);
6491 end
6492 else
6493 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6494 end;
6495 end;
6497 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6498 begin
6499 Result := False;
6500 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6501 begin
6502 Result := True;
6503 Exit;
6504 end;
6505 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6506 begin
6507 Result := True;
6508 Exit;
6509 end;
6510 if gSpectMode <> SPECT_PLAYERS then
6511 Exit;
6512 if gSpectPID1 = UID then
6513 begin
6514 Result := True;
6515 Exit;
6516 end;
6517 if gSpectViewTwo and (gSpectPID2 = UID) then
6518 begin
6519 Result := True;
6520 Exit;
6521 end;
6522 end;
6524 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6525 var
6526 Pl: TPlayer;
6527 begin
6528 Result := False;
6529 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6530 begin
6531 Result := True;
6532 Exit;
6533 end;
6534 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6535 begin
6536 Result := True;
6537 Exit;
6538 end;
6539 if gSpectMode <> SPECT_PLAYERS then
6540 Exit;
6541 Pl := g_Player_Get(gSpectPID1);
6542 if (Pl <> nil) and (Pl.Team = Team) then
6543 begin
6544 Result := True;
6545 Exit;
6546 end;
6547 if gSpectViewTwo then
6548 begin
6549 Pl := g_Player_Get(gSpectPID2);
6550 if (Pl <> nil) and (Pl.Team = Team) then
6551 begin
6552 Result := True;
6553 Exit;
6554 end;
6555 end;
6556 end;
6558 procedure g_Game_Message(Msg: string; Time: Word);
6559 begin
6560 MessageText := b_Text_Format(Msg);
6561 MessageTime := Time;
6562 end;
6564 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6565 var
6566 a: Integer;
6567 begin
6568 case gAnnouncer of
6569 ANNOUNCE_NONE:
6570 Exit;
6571 ANNOUNCE_ME,
6572 ANNOUNCE_MEPLUS:
6573 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6574 Exit;
6575 end;
6576 for a := 0 to 3 do
6577 if goodsnd[a].IsPlaying() then
6578 Exit;
6580 goodsnd[Random(4)].Play();
6581 end;
6583 procedure g_Game_Announce_KillCombo(Param: Integer);
6584 var
6585 UID: Word;
6586 c, n: Byte;
6587 Pl: TPlayer;
6588 Name: String;
6589 begin
6590 UID := Param and $FFFF;
6591 c := Param shr 16;
6592 if c < 2 then
6593 Exit;
6595 Pl := g_Player_Get(UID);
6596 if Pl = nil then
6597 Name := '?'
6598 else
6599 Name := Pl.Name;
6601 case c of
6602 2: begin
6603 n := 0;
6604 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6605 end;
6606 3: begin
6607 n := 1;
6608 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6609 end;
6610 4: begin
6611 n := 2;
6612 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6613 end;
6614 else begin
6615 n := 3;
6616 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6617 end;
6618 end;
6620 case gAnnouncer of
6621 ANNOUNCE_NONE:
6622 Exit;
6623 ANNOUNCE_ME:
6624 if not g_Game_IsWatchedPlayer(UID) then
6625 Exit;
6626 ANNOUNCE_MEPLUS:
6627 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6628 Exit;
6629 end;
6631 if killsnd[n].IsPlaying() then
6632 killsnd[n].Stop();
6633 killsnd[n].Play();
6634 end;
6636 procedure g_Game_StartVote(Command, Initiator: string);
6637 var
6638 Need: Integer;
6639 begin
6640 if not gVotesEnabled then Exit;
6641 if gGameSettings.GameType <> GT_SERVER then Exit;
6642 if gVoteInProgress or gVotePassed then
6643 begin
6644 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6645 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6646 Exit;
6647 end;
6648 gVoteInProgress := True;
6649 gVotePassed := False;
6650 gVoteTimer := gTime + gVoteTimeout * 1000;
6651 gVoteCount := 0;
6652 gVoted := False;
6653 gVoteCommand := Command;
6655 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6656 Need := Floor((NetClientCount+1)/2.0)+1
6657 else
6658 Need := Floor(NetClientCount/2.0)+1;
6659 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6660 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6661 end;
6663 procedure g_Game_CheckVote;
6664 var
6665 I, Need: Integer;
6666 begin
6667 if gGameSettings.GameType <> GT_SERVER then Exit;
6668 if not gVoteInProgress then Exit;
6670 if (gTime >= gVoteTimer) then
6671 begin
6672 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6673 Need := Floor((NetClientCount+1)/2.0) + 1
6674 else
6675 Need := Floor(NetClientCount/2.0) + 1;
6676 if gVoteCount >= Need then
6677 begin
6678 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6679 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6680 gVotePassed := True;
6681 gVoteCmdTimer := gTime + 5000;
6682 end
6683 else
6684 begin
6685 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6686 MH_SEND_VoteEvent(NET_VE_FAILED);
6687 end;
6688 if NetClients <> nil then
6689 for I := Low(NetClients) to High(NetClients) do
6690 if NetClients[i].Used then
6691 NetClients[i].Voted := False;
6692 gVoteInProgress := False;
6693 gVoted := False;
6694 gVoteCount := 0;
6695 end
6696 else
6697 begin
6698 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6699 Need := Floor((NetClientCount+1)/2.0) + 1
6700 else
6701 Need := Floor(NetClientCount/2.0) + 1;
6702 if gVoteCount >= Need then
6703 begin
6704 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6705 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6706 gVoteInProgress := False;
6707 gVotePassed := True;
6708 gVoteCmdTimer := gTime + 5000;
6709 gVoted := False;
6710 gVoteCount := 0;
6711 if NetClients <> nil then
6712 for I := Low(NetClients) to High(NetClients) do
6713 if NetClients[i].Used then
6714 NetClients[i].Voted := False;
6715 end;
6716 end;
6717 end;
6719 procedure g_Game_LoadMapList(FileName: string);
6720 var
6721 ListFile: TextFile;
6722 s: string;
6723 begin
6724 MapList := nil;
6725 MapIndex := -1;
6727 if not FileExists(FileName) then Exit;
6729 AssignFile(ListFile, FileName);
6730 Reset(ListFile);
6731 while not EOF(ListFile) do
6732 begin
6733 ReadLn(ListFile, s);
6735 s := Trim(s);
6736 if s = '' then Continue;
6738 SetLength(MapList, Length(MapList)+1);
6739 MapList[High(MapList)] := s;
6740 end;
6741 CloseFile(ListFile);
6742 end;
6744 procedure g_Game_SetDebugMode();
6745 begin
6746 gDebugMode := True;
6747 // ×èòû (äàæå â ñâîåé èãðå):
6748 gCheats := True;
6749 end;
6751 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6752 var
6753 i: Word;
6754 begin
6755 if Length(LoadingStat.Msgs) = 0 then
6756 Exit;
6758 with LoadingStat do
6759 begin
6760 if not reWrite then
6761 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6762 if NextMsg = Length(Msgs) then
6763 begin // scroll
6764 for i := 0 to High(Msgs)-1 do
6765 Msgs[i] := Msgs[i+1];
6766 end
6767 else
6768 Inc(NextMsg);
6769 end else
6770 if NextMsg = 0 then
6771 Inc(NextMsg);
6773 Msgs[NextMsg-1] := Text;
6774 CurValue := 0;
6775 MaxValue := Max;
6776 ShowCount := 0;
6777 end;
6779 g_ActiveWindow := nil;
6781 ProcessLoading;
6782 end;
6784 procedure g_Game_StepLoading();
6785 begin
6786 with LoadingStat do
6787 begin
6788 Inc(CurValue);
6789 Inc(ShowCount);
6790 if (ShowCount > LOADING_SHOW_STEP) then
6791 begin
6792 ShowCount := 0;
6793 ProcessLoading;
6794 end;
6795 end;
6796 end;
6798 procedure g_Game_ClearLoading();
6799 var
6800 len: Word;
6801 begin
6802 with LoadingStat do
6803 begin
6804 CurValue := 0;
6805 MaxValue := 0;
6806 ShowCount := 0;
6807 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6808 if len < 1 then len := 1;
6809 SetLength(Msgs, len);
6810 for len := Low(Msgs) to High(Msgs) do
6811 Msgs[len] := '';
6812 NextMsg := 0;
6813 end;
6814 end;
6816 procedure Parse_Params(var pars: TParamStrValues);
6817 var
6818 i: Integer;
6819 s: String;
6820 begin
6821 SetLength(pars, 0);
6822 i := 1;
6823 while i <= ParamCount do
6824 begin
6825 s := ParamStr(i);
6826 if (s[1] = '-') and (Length(s) > 1) then
6827 begin
6828 if (s[2] = '-') and (Length(s) > 2) then
6829 begin // Îäèíî÷íûé ïàðàìåòð
6830 SetLength(pars, Length(pars) + 1);
6831 with pars[High(pars)] do
6832 begin
6833 Name := LowerCase(s);
6834 Value := '+';
6835 end;
6836 end
6837 else
6838 if (i < ParamCount) then
6839 begin // Ïàðàìåòð ñî çíà÷åíèåì
6840 Inc(i);
6841 SetLength(pars, Length(pars) + 1);
6842 with pars[High(pars)] do
6843 begin
6844 Name := LowerCase(s);
6845 Value := LowerCase(ParamStr(i));
6846 end;
6847 end;
6848 end;
6850 Inc(i);
6851 end;
6852 end;
6854 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6855 var
6856 i: Integer;
6857 begin
6858 Result := '';
6859 for i := 0 to High(pars) do
6860 if pars[i].Name = aName then
6861 begin
6862 Result := pars[i].Value;
6863 Break;
6864 end;
6865 end;
6867 procedure g_Game_Process_Params();
6868 var
6869 pars: TParamStrValues;
6870 map: String;
6871 GMode, n: Byte;
6872 LimT, LimS: Integer;
6873 Opt: LongWord;
6874 Lives: Integer;
6875 s: String;
6876 Port: Integer;
6877 ip: String;
6878 F: TextFile;
6879 begin
6880 Parse_Params(pars);
6882 s := Find_Param_Value(pars, '--profile-frame');
6883 if (s <> '') then g_profile_frame_draw := true;
6885 s := Find_Param_Value(pars, '--profile-coldet');
6886 if (s <> '') then g_profile_collision := true;
6888 // Debug mode:
6889 s := Find_Param_Value(pars, '--debug');
6890 if (s <> '') then
6891 begin
6892 g_Game_SetDebugMode();
6893 s := Find_Param_Value(pars, '--netdump');
6894 if (s <> '') then
6895 NetDump := True;
6896 end;
6898 // Connect when game loads
6899 ip := Find_Param_Value(pars, '-connect');
6901 if ip <> '' then
6902 begin
6903 s := Find_Param_Value(pars, '-port');
6904 if (s = '') or not TryStrToInt(s, Port) then
6905 Port := 25666;
6907 s := Find_Param_Value(pars, '-pw');
6909 g_Game_StartClient(ip, Port, s);
6910 Exit;
6911 end;
6913 // Start map when game loads:
6914 map := LowerCase(Find_Param_Value(pars, '-map'));
6915 if isWadPath(map) then
6916 begin
6917 // Game mode:
6918 s := Find_Param_Value(pars, '-gm');
6919 GMode := g_Game_TextToMode(s);
6920 if GMode = GM_NONE then GMode := GM_DM;
6921 if GMode = GM_SINGLE then GMode := GM_COOP;
6923 // Time limit:
6924 s := Find_Param_Value(pars, '-limt');
6925 if (s = '') or (not TryStrToInt(s, LimT)) then
6926 LimT := 0;
6927 if LimT < 0 then
6928 LimT := 0;
6930 // Goal limit:
6931 s := Find_Param_Value(pars, '-lims');
6932 if (s = '') or (not TryStrToInt(s, LimS)) then
6933 LimS := 0;
6934 if LimS < 0 then
6935 LimS := 0;
6937 // Lives limit:
6938 s := Find_Param_Value(pars, '-lives');
6939 if (s = '') or (not TryStrToInt(s, Lives)) then
6940 Lives := 0;
6941 if Lives < 0 then
6942 Lives := 0;
6944 // Options:
6945 s := Find_Param_Value(pars, '-opt');
6946 if (s = '') then
6947 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6948 else
6949 Opt := StrToIntDef(s, 0);
6950 if Opt = 0 then
6951 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6953 // Close after map:
6954 s := Find_Param_Value(pars, '--close');
6955 if (s <> '') then
6956 gMapOnce := True;
6958 // Delete test map after play:
6959 s := Find_Param_Value(pars, '--testdelete');
6960 if (s <> '') then
6961 begin
6962 gMapToDelete := MapsDir + map;
6963 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6964 Halt(1);
6965 end;
6967 // Delete temporary WAD after play:
6968 s := Find_Param_Value(pars, '--tempdelete');
6969 if (s <> '') then
6970 begin
6971 gMapToDelete := MapsDir + map;
6972 gTempDelete := True;
6973 end;
6975 // Number of players:
6976 s := Find_Param_Value(pars, '-pl');
6977 if (s = '') then
6978 n := 1
6979 else
6980 n := StrToIntDef(s, 1);
6982 // Start:
6983 s := Find_Param_Value(pars, '-port');
6984 if (s = '') or not TryStrToInt(s, Port) then
6985 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6986 else
6987 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6988 end;
6990 // Execute script when game loads:
6991 s := Find_Param_Value(pars, '-exec');
6992 if s <> '' then
6993 begin
6994 if Pos(':\', s) = 0 then
6995 s := GameDir + '/' + s;
6997 {$I-}
6998 AssignFile(F, s);
6999 Reset(F);
7000 if IOResult <> 0 then
7001 begin
7002 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7003 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7004 CloseFile(F);
7005 Exit;
7006 end;
7007 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
7008 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7010 while not EOF(F) do
7011 begin
7012 ReadLn(F, s);
7013 if IOResult <> 0 then
7014 begin
7015 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7016 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7017 CloseFile(F);
7018 Exit;
7019 end;
7020 if Pos('#', s) <> 1 then // script comment
7021 g_Console_Process(s, True);
7022 end;
7024 CloseFile(F);
7025 {$I+}
7026 end;
7028 SetLength(pars, 0);
7029 end;
7031 end.