DEADSOFTWARE

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