DEADSOFTWARE

cc97e4c65d63700db25e1dd6489179803ef8d0de
[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, idx: 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]);
5020 if cmd = 'dpp' then
5021 begin
5022 g_profile_frame_draw := not g_profile_frame_draw;
5023 exit;
5024 end;
5025 if cmd = 'dpu' then
5026 begin
5027 g_profile_frame_update := not g_profile_frame_update;
5028 exit;
5029 end;
5030 if cmd = 'dpc' then
5031 begin
5032 g_profile_collision := not g_profile_collision;
5033 exit;
5034 end;
5035 if cmd = 'r_gridrender' then
5036 begin
5037 case getBool(1) of
5038 -1: begin end;
5039 0: gdbg_map_use_grid_render := false;
5040 1: gdbg_map_use_grid_render := true;
5041 end;
5042 if gdbg_map_use_grid_render then g_Console_Add('grid rendering: tan') else g_Console_Add('grid rendering: ona');
5043 exit;
5044 end;
5045 if cmd = 'dbg_coldet_grid' then
5046 begin
5047 case getBool(1) of
5048 -1: begin end;
5049 0: gdbg_map_use_grid_coldet := false;
5050 1: gdbg_map_use_grid_coldet := true;
5051 end;
5052 if gdbg_map_use_grid_coldet then g_Console_Add('grid coldet: tan') else g_Console_Add('grid coldet: ona');
5053 exit;
5054 end;
5055 end;
5057 procedure DebugCommands(P: SArray);
5058 var
5059 a, b: Integer;
5060 cmd: string;
5061 //pt: TPoint;
5062 begin
5063 // Êîìàíäû îòëàäî÷íîãî ðåæèìà:
5064 if gDebugMode then
5065 begin
5066 cmd := LowerCase(P[0]);
5067 if cmd = 'd_window' then
5068 begin
5069 g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY]));
5070 g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY]));
5071 g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight]));
5072 g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY]));
5073 g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption]));
5074 end
5075 else if cmd = 'd_sounds' then
5076 begin
5077 if (Length(P) > 1) and
5078 ((P[1] = '1') or (P[1] = '0')) then
5079 g_Debug_Sounds := (P[1][1] = '1');
5081 g_Console_Add(Format('d_sounds is %d', [Byte(g_Debug_Sounds)]));
5082 end
5083 else if cmd = 'd_frames' then
5084 begin
5085 if (Length(P) > 1) and
5086 ((P[1] = '1') or (P[1] = '0')) then
5087 g_Debug_Frames := (P[1][1] = '1');
5089 g_Console_Add(Format('d_frames is %d', [Byte(g_Debug_Frames)]));
5090 end
5091 else if cmd = 'd_winmsg' then
5092 begin
5093 if (Length(P) > 1) and
5094 ((P[1] = '1') or (P[1] = '0')) then
5095 g_Debug_WinMsgs := (P[1][1] = '1');
5097 g_Console_Add(Format('d_winmsg is %d', [Byte(g_Debug_WinMsgs)]));
5098 end
5099 else if (cmd = 'd_monoff') and not g_Game_IsNet then
5100 begin
5101 if (Length(P) > 1) and
5102 ((P[1] = '1') or (P[1] = '0')) then
5103 g_Debug_MonsterOff := (P[1][1] = '1');
5105 g_Console_Add(Format('d_monoff is %d', [Byte(g_debug_MonsterOff)]));
5106 end
5107 else if (cmd = 'd_botoff') and not g_Game_IsNet then
5108 begin
5109 if Length(P) > 1 then
5110 case P[1][1] of
5111 '0': g_debug_BotAIOff := 0;
5112 '1': g_debug_BotAIOff := 1;
5113 '2': g_debug_BotAIOff := 2;
5114 '3': g_debug_BotAIOff := 3;
5115 end;
5117 g_Console_Add(Format('d_botoff is %d', [g_debug_BotAIOff]));
5118 end
5119 else if cmd = 'd_monster' then
5120 begin
5121 if gGameOn and (gPlayer1 <> nil) and (gPlayer1.Live) and (not g_Game_IsNet) then
5122 if Length(P) < 2 then
5123 begin
5124 g_Console_Add(cmd + ' [ID | Name] [behaviour]');
5125 g_Console_Add('ID | Name');
5126 for b := MONSTER_DEMON to MONSTER_MAN do
5127 g_Console_Add(Format('%2d | %s', [b, g_Monsters_GetNameByID(b)]));
5128 end else
5129 begin
5130 a := StrToIntDef(P[1], 0);
5131 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5132 a := g_Monsters_GetIDByName(P[1]);
5134 if (a < MONSTER_DEMON) or (a > MONSTER_MAN) then
5135 g_Console_Add(Format(_lc[I_MSG_NO_MONSTER], [P[1]]))
5136 else
5137 begin
5138 with gPlayer1.Obj do
5139 b := g_Monsters_Create(a,
5140 X + Rect.X + (Rect.Width div 2),
5141 Y + Rect.Y + Rect.Height,
5142 gPlayer1.Direction, True);
5143 if (Length(P) > 2) and (b >= 0) then
5144 gMonsters[b].MonsterBehaviour := Min(Max(StrToIntDef(P[2], BH_NORMAL), BH_NORMAL), BH_GOOD);
5145 end;
5146 end;
5147 end
5148 else if (cmd = 'd_health') then
5149 begin
5150 if (Length(P) > 1) and
5151 ((P[1] = '1') or (P[1] = '0')) then
5152 g_debug_HealthBar := (P[1][1] = '1');
5154 g_Console_Add(Format('d_health is %d', [Byte(g_debug_HealthBar)]));
5155 end
5156 else if (cmd = 'd_player') then
5157 begin
5158 if (Length(P) > 1) and
5159 ((P[1] = '1') or (P[1] = '0')) then
5160 g_debug_Player := (P[1][1] = '1');
5162 g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)]));
5163 end
5164 else if (cmd = 'd_joy') then
5165 begin
5166 for a := 1 to 8 do
5167 g_Console_Add(e_JoystickStateToString(a));
5168 end;
5169 end
5170 else
5171 g_Console_Add(_lc[I_MSG_NOT_DEBUG]);
5172 end;
5175 procedure GameCheats(P: SArray);
5176 var
5177 cmd: string;
5178 f, a: Integer;
5179 plr: TPlayer;
5180 begin
5181 if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and
5182 (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) or g_Game_IsNet then
5183 begin
5184 g_Console_Add('not available');
5185 exit;
5186 end;
5187 plr := gPlayer1;
5188 if plr = nil then
5189 begin
5190 g_Console_Add('where is the player?!');
5191 exit;
5192 end;
5193 cmd := LowerCase(P[0]);
5194 // god
5195 if cmd = 'god' then
5196 begin
5197 plr.GodMode := not plr.GodMode;
5198 if plr.GodMode then g_Console_Add('player is godlike now') else g_Console_Add('player is mortal now');
5199 exit;
5200 end;
5201 // give <health|exit|weapons|air|suit|jetpack|berserk|all>
5202 if cmd = 'give' then
5203 begin
5204 if length(P) < 2 then begin g_Console_Add('give what?!'); exit; end;
5205 for f := 1 to High(P) do
5206 begin
5207 cmd := LowerCase(P[f]);
5208 if cmd = 'health' then begin plr.RestoreHealthArmor(); g_Console_Add('player feels himself better'); continue; end;
5209 if (cmd = 'all') {or (cmd = 'weapons')} then begin plr.AllRulez(False); g_Console_Add('player got the gifts'); continue; end;
5210 if cmd = 'exit' then
5211 begin
5212 if gTriggers <> nil then
5213 begin
5214 for a := 0 to High(gTriggers) do
5215 begin
5216 if gTriggers[a].TriggerType = TRIGGER_EXIT then
5217 begin
5218 g_Console_Add('player left the map');
5219 gExitByTrigger := True;
5220 g_Game_ExitLevel(gTriggers[a].Data.MapName);
5221 break;
5222 end;
5223 end;
5224 end;
5225 continue;
5226 end;
5228 if cmd = 'air' then begin plr.GiveItem(ITEM_OXYGEN); g_Console_Add('player got some air'); continue; end;
5229 if cmd = 'jetpack' then begin plr.GiveItem(ITEM_JETPACK); g_Console_Add('player got a jetpack'); continue; end;
5230 if cmd = 'suit' then begin plr.GiveItem(ITEM_SUIT); g_Console_Add('player got an envirosuit'); continue; end;
5231 if cmd = 'berserk' then begin plr.GiveItem(ITEM_MEDKIT_BLACK); g_Console_Add('player got a berserk pack'); continue; end;
5232 if cmd = 'backpack' then begin plr.GiveItem(ITEM_AMMO_BACKPACK); g_Console_Add('player got a backpack'); continue; end;
5234 if cmd = 'helmet' then begin plr.GiveItem(ITEM_HELMET); g_Console_Add('player got a helmet'); continue; end;
5235 if cmd = 'bottle' then begin plr.GiveItem(ITEM_BOTTLE); g_Console_Add('player got a bottle of health'); continue; end;
5237 if cmd = 'stimpack' then begin plr.GiveItem(ITEM_MEDKIT_SMALL); g_Console_Add('player got a stimpack'); continue; end;
5238 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;
5240 if cmd = 'greenarmor' then begin plr.GiveItem(ITEM_ARMOR_GREEN); g_Console_Add('player got a security armor'); continue; end;
5241 if cmd = 'bluearmor' then begin plr.GiveItem(ITEM_ARMOR_BLUE); g_Console_Add('player got a combat armor'); continue; end;
5243 if (cmd = 'megasphere') or (cmd = 'mega') then begin plr.GiveItem(ITEM_SPHERE_BLUE); g_Console_Add('player got a megasphere'); continue; end;
5244 if (cmd = 'soulsphere') or (cmd = 'soul')then begin plr.GiveItem(ITEM_SPHERE_WHITE); g_Console_Add('player got a soul sphere'); continue; end;
5246 if (cmd = 'invul') or (cmd = 'invulnerability') then begin plr.GiveItem(ITEM_INVUL); g_Console_Add('player got invulnerability'); continue; end;
5247 if (cmd = 'invis') or (cmd = 'invisibility') then begin plr.GiveItem(ITEM_INVIS); g_Console_Add('player got invisibility'); continue; end;
5249 if cmd = 'redkey' then begin plr.GiveItem(ITEM_KEY_RED); g_Console_Add('player got the red key'); continue; end;
5250 if cmd = 'greenkey' then begin plr.GiveItem(ITEM_KEY_GREEN); g_Console_Add('player got the green key'); continue; end;
5251 if cmd = 'bluekey' then begin plr.GiveItem(ITEM_KEY_BLUE); g_Console_Add('player got the blue key'); continue; end;
5253 if (cmd = 'shotgun') or (cmd = 'sg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN1); g_Console_Add('player got a shotgun'); continue; end;
5254 if (cmd = 'supershotgun') or (cmd = 'ssg') then begin plr.GiveItem(ITEM_WEAPON_SHOTGUN2); g_Console_Add('player got a supershotgun'); continue; end;
5255 if cmd = 'chaingun' then begin plr.GiveItem(ITEM_WEAPON_CHAINGUN); g_Console_Add('player got a chaingun'); continue; end;
5256 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;
5257 if cmd = 'plasmagun' then begin plr.GiveItem(ITEM_WEAPON_PLASMA); g_Console_Add('player got a plasma gun'); continue; end;
5258 if cmd = 'bfg' then begin plr.GiveItem(ITEM_WEAPON_BFG); g_Console_Add('player got a BFG-9000'); continue; end;
5260 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;
5261 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;
5262 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;
5263 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;
5264 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;
5265 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;
5267 if cmd = 'superchaingun' then begin plr.GiveItem(ITEM_WEAPON_SUPERPULEMET); g_Console_Add('player got a superchaingun'); continue; end;
5268 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;
5270 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;
5271 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;
5273 if cmd = 'chainsaw' then begin plr.GiveItem(ITEM_WEAPON_SAW); g_Console_Add('player got a chainsaw'); continue; end;
5275 if cmd = 'ammo' then
5276 begin
5277 plr.GiveItem(ITEM_AMMO_SHELLS_BOX);
5278 plr.GiveItem(ITEM_AMMO_BULLETS_BOX);
5279 plr.GiveItem(ITEM_AMMO_CELL_BIG);
5280 plr.GiveItem(ITEM_AMMO_ROCKET_BOX);
5281 plr.GiveItem(ITEM_AMMO_FUELCAN);
5282 g_Console_Add('player got some ammo');
5283 continue;
5284 end;
5286 if cmd = 'clip' then begin plr.GiveItem(ITEM_AMMO_BULLETS); g_Console_Add('player got some bullets'); continue; end;
5287 if cmd = 'bullets' then begin plr.GiveItem(ITEM_AMMO_BULLETS_BOX); g_Console_Add('player got a box of bullets'); continue; end;
5289 if cmd = 'shells' then begin plr.GiveItem(ITEM_AMMO_SHELLS); g_Console_Add('player got some shells'); continue; end;
5290 if cmd = 'shellbox' then begin plr.GiveItem(ITEM_AMMO_SHELLS_BOX); g_Console_Add('player got a box of shells'); continue; end;
5292 if cmd = 'cells' then begin plr.GiveItem(ITEM_AMMO_CELL); g_Console_Add('player got some cells'); continue; end;
5293 if cmd = 'battery' then begin plr.GiveItem(ITEM_AMMO_CELL_BIG); g_Console_Add('player got cell battery'); continue; end;
5295 if cmd = 'rocket' then begin plr.GiveItem(ITEM_AMMO_ROCKET); g_Console_Add('player got a rocket'); continue; end;
5296 if cmd = 'rocketbox' then begin plr.GiveItem(ITEM_AMMO_ROCKET_BOX); g_Console_Add('player got some rockets'); continue; end;
5298 if (cmd = 'fuel') or (cmd = 'fuelcan') then begin plr.GiveItem(ITEM_AMMO_FUELCAN); g_Console_Add('player got fuel canister'); continue; end;
5300 if cmd = 'weapons' then
5301 begin
5302 plr.GiveItem(ITEM_WEAPON_SHOTGUN1);
5303 plr.GiveItem(ITEM_WEAPON_SHOTGUN2);
5304 plr.GiveItem(ITEM_WEAPON_CHAINGUN);
5305 plr.GiveItem(ITEM_WEAPON_ROCKETLAUNCHER);
5306 plr.GiveItem(ITEM_WEAPON_PLASMA);
5307 plr.GiveItem(ITEM_WEAPON_BFG);
5308 g_Console_Add('player got weapons');
5309 continue;
5310 end;
5312 if cmd = 'keys' then
5313 begin
5314 plr.GiveItem(ITEM_KEY_RED);
5315 plr.GiveItem(ITEM_KEY_GREEN);
5316 plr.GiveItem(ITEM_KEY_BLUE);
5317 g_Console_Add('player got all keys');
5318 continue;
5319 end;
5321 g_Console_Add('i don''t know how to give '''+cmd+'''!');
5322 end;
5323 exit;
5324 end;
5325 // open
5326 if cmd = 'open' then
5327 begin
5328 g_Console_Add('player activated sesame');
5329 g_Triggers_OpenAll();
5330 exit;
5331 end;
5332 // fly
5333 if cmd = 'fly' then
5334 begin
5335 gFly := not gFly;
5336 if gFly then g_Console_Add('player feels himself lighter') else g_Console_Add('player lost his wings');
5337 exit;
5338 end;
5339 // noclip
5340 if cmd = 'noclip' then
5341 begin
5342 plr.SwitchNoClip;
5343 g_Console_Add('wall hardeness adjusted');
5344 exit;
5345 end;
5346 // notarget
5347 if cmd = 'notarget' then
5348 begin
5349 plr.NoTarget := not plr.NoTarget;
5350 if plr.NoTarget then g_Console_Add('player hides in shadows') else g_Console_Add('player is brave again');
5351 exit;
5352 end;
5353 // noreload
5354 if cmd = 'noreload' then
5355 begin
5356 plr.NoReload := not plr.NoReload;
5357 if plr.NoReload then g_Console_Add('player is action hero now') else g_Console_Add('player is ordinary man now');
5358 exit;
5359 end;
5360 // speedy
5361 if cmd = 'speedy' then
5362 begin
5363 MAX_RUNVEL := 32-MAX_RUNVEL;
5364 g_Console_Add('speed adjusted');
5365 exit;
5366 end;
5367 // jumpy
5368 if cmd = 'jumpy' then
5369 begin
5370 VEL_JUMP := 30-VEL_JUMP;
5371 g_Console_Add('jump height adjusted');
5372 exit;
5373 end;
5374 // automap
5375 if cmd = 'automap' then
5376 begin
5377 gShowMap := not gShowMap;
5378 if gShowMap then g_Console_Add('player gains second sight') else g_Console_Add('player lost second sight');
5379 exit;
5380 end;
5381 // aimline
5382 if cmd = 'aimline' then
5383 begin
5384 gAimLine := not gAimLine;
5385 if gAimLine then g_Console_Add('player gains laser sight') else g_Console_Add('player lost laser sight');
5386 exit;
5387 end;
5388 end;
5390 procedure GameCommands(P: SArray);
5391 var
5392 a, b: Integer;
5393 s, pw: String;
5394 chstr: string;
5395 cmd: string;
5396 pl: pTNetClient = nil;
5397 plr: TPlayer;
5398 prt: Word;
5399 nm: Boolean;
5400 listen: LongWord;
5401 begin
5402 // Îáùèå êîìàíäû:
5403 cmd := LowerCase(P[0]);
5404 chstr := '';
5405 if (cmd = 'quit') or
5406 (cmd = 'exit') then
5407 begin
5408 g_Game_Free();
5409 g_Game_Quit();
5410 Exit;
5411 end
5412 else if cmd = 'pause' then
5413 begin
5414 if (g_ActiveWindow = nil) then
5415 g_Game_Pause(not gPause);
5416 end
5417 else if cmd = 'endgame' then
5418 gExit := EXIT_SIMPLE
5419 else if cmd = 'restart' then
5420 begin
5421 if gGameOn or (gState in [STATE_INTERSINGLE, STATE_INTERCUSTOM]) then
5422 begin
5423 if g_Game_IsClient then
5424 begin
5425 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5426 Exit;
5427 end;
5428 g_Game_Restart();
5429 end else
5430 g_Console_Add(_lc[I_MSG_NOT_GAME]);
5431 end
5432 else if cmd = 'kick' then
5433 begin
5434 if g_Game_IsServer then
5435 begin
5436 if Length(P) < 2 then
5437 begin
5438 g_Console_Add('kick <name>');
5439 Exit;
5440 end;
5441 if P[1] = '' then
5442 begin
5443 g_Console_Add('kick <name>');
5444 Exit;
5445 end;
5447 if g_Game_IsNet then
5448 pl := g_Net_Client_ByName(P[1]);
5449 if (pl <> nil) then
5450 begin
5451 s := g_Net_ClientName_ByID(pl^.ID);
5452 enet_peer_disconnect(pl^.Peer, NET_DISC_KICK);
5453 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5454 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5455 if NetUseMaster then
5456 g_Net_Slist_Update;
5457 end else if gPlayers <> nil then
5458 for a := Low(gPlayers) to High(gPlayers) do
5459 if gPlayers[a] <> nil then
5460 if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then
5461 begin
5462 // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå
5463 if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then
5464 continue;
5465 gPlayers[a].Lives := 0;
5466 gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON);
5467 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True);
5468 g_Player_Remove(gPlayers[a].UID);
5469 if NetUseMaster then
5470 g_Net_Slist_Update;
5471 // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå
5472 g_Bot_MixNames();
5473 end;
5474 end else
5475 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5476 end
5477 else if cmd = 'kick_id' then
5478 begin
5479 if g_Game_IsServer and g_Game_IsNet then
5480 begin
5481 if Length(P) < 2 then
5482 begin
5483 g_Console_Add('kick_id <client ID>');
5484 Exit;
5485 end;
5486 if P[1] = '' then
5487 begin
5488 g_Console_Add('kick_id <client ID>');
5489 Exit;
5490 end;
5492 a := StrToIntDef(P[1], 0);
5493 if (NetClients <> nil) and (a <= High(NetClients)) then
5494 begin
5495 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5496 begin
5497 s := g_Net_ClientName_ByID(NetClients[a].ID);
5498 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK);
5499 g_Console_Add(Format(_lc[I_PLAYER_KICK], [s]));
5500 MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s);
5501 if NetUseMaster then
5502 g_Net_Slist_Update;
5503 end;
5504 end;
5505 end else
5506 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5507 end
5508 else if cmd = 'ban' then
5509 begin
5510 if g_Game_IsServer and g_Game_IsNet then
5511 begin
5512 if Length(P) < 2 then
5513 begin
5514 g_Console_Add('ban <name>');
5515 Exit;
5516 end;
5517 if P[1] = '' then
5518 begin
5519 g_Console_Add('ban <name>');
5520 Exit;
5521 end;
5523 pl := g_Net_Client_ByName(P[1]);
5524 if (pl <> nil) then
5525 begin
5526 s := g_Net_ClientName_ByID(pl^.ID);
5527 g_Net_BanHost(pl^.Peer^.address.host, False);
5528 enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN);
5529 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5530 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5531 if NetUseMaster then
5532 g_Net_Slist_Update;
5533 end else
5534 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5535 end else
5536 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5537 end
5538 else if cmd = 'ban_id' then
5539 begin
5540 if g_Game_IsServer and g_Game_IsNet then
5541 begin
5542 if Length(P) < 2 then
5543 begin
5544 g_Console_Add('ban_id <client ID>');
5545 Exit;
5546 end;
5547 if P[1] = '' then
5548 begin
5549 g_Console_Add('ban_id <client ID>');
5550 Exit;
5551 end;
5553 a := StrToIntDef(P[1], 0);
5554 if (NetClients <> nil) and (a <= High(NetClients)) then
5555 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5556 begin
5557 s := g_Net_ClientName_ByID(NetClients[a].ID);
5558 g_Net_BanHost(NetClients[a].Peer^.address.host, False);
5559 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN);
5560 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5561 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5562 if NetUseMaster then
5563 g_Net_Slist_Update;
5564 end;
5565 end else
5566 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5567 end
5568 else if cmd = 'permban' then
5569 begin
5570 if g_Game_IsServer and g_Game_IsNet then
5571 begin
5572 if Length(P) < 2 then
5573 begin
5574 g_Console_Add('permban <name>');
5575 Exit;
5576 end;
5577 if P[1] = '' then
5578 begin
5579 g_Console_Add('permban <name>');
5580 Exit;
5581 end;
5583 pl := g_Net_Client_ByName(P[1]);
5584 if (pl <> nil) then
5585 begin
5586 s := g_Net_ClientName_ByID(pl^.ID);
5587 g_Net_BanHost(pl^.Peer^.address.host);
5588 enet_peer_disconnect(pl^.Peer, NET_DISC_BAN);
5589 g_Net_SaveBanList();
5590 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5591 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5592 if NetUseMaster then
5593 g_Net_Slist_Update;
5594 end else
5595 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
5596 end else
5597 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5598 end
5599 else if cmd = 'permban_id' then
5600 begin
5601 if g_Game_IsServer and g_Game_IsNet then
5602 begin
5603 if Length(P) < 2 then
5604 begin
5605 g_Console_Add('permban_id <client ID>');
5606 Exit;
5607 end;
5608 if P[1] = '' then
5609 begin
5610 g_Console_Add('permban_id <client ID>');
5611 Exit;
5612 end;
5614 a := StrToIntDef(P[1], 0);
5615 if (NetClients <> nil) and (a <= High(NetClients)) then
5616 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5617 begin
5618 s := g_Net_ClientName_ByID(NetClients[a].ID);
5619 g_Net_BanHost(NetClients[a].Peer^.address.host);
5620 enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN);
5621 g_Net_SaveBanList();
5622 g_Console_Add(Format(_lc[I_PLAYER_BAN], [s]));
5623 MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s);
5624 if NetUseMaster then
5625 g_Net_Slist_Update;
5626 end;
5627 end else
5628 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5629 end
5630 else if cmd = 'unban' then
5631 begin
5632 if g_Game_IsServer and g_Game_IsNet then
5633 begin
5634 if Length(P) < 2 then
5635 begin
5636 g_Console_Add('unban <IP Address>');
5637 Exit;
5638 end;
5639 if P[1] = '' then
5640 begin
5641 g_Console_Add('unban <IP Address>');
5642 Exit;
5643 end;
5645 if g_Net_UnbanHost(P[1]) then
5646 begin
5647 g_Console_Add(Format(_lc[I_MSG_UNBAN_OK], [P[1]]));
5648 g_Net_SaveBanList();
5649 end else
5650 g_Console_Add(Format(_lc[I_MSG_UNBAN_FAIL], [P[1]]));
5651 end else
5652 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5653 end
5654 else if cmd = 'clientlist' then
5655 begin
5656 if g_Game_IsServer and g_Game_IsNet then
5657 begin
5658 b := 0;
5659 if NetClients <> nil then
5660 for a := Low(NetClients) to High(NetClients) do
5661 if NetClients[a].Used and (NetClients[a].Peer <> nil) then
5662 begin
5663 plr := g_Player_Get(NetClients[a].Player);
5664 if plr = nil then continue;
5665 Inc(b);
5666 g_Console_Add(Format('#%2d: %-15s | %s', [a,
5667 IpToStr(NetClients[a].Peer^.address.host), plr.Name]));
5668 end;
5669 if b = 0 then
5670 g_Console_Add(_lc[I_MSG_NOCLIENTS]);
5671 end else
5672 g_Console_Add(_lc[I_MSG_SERVERONLY]);
5673 end
5674 else if cmd = 'connect' then
5675 begin
5676 if (NetMode = NET_NONE) then
5677 begin
5678 if Length(P) < 2 then
5679 begin
5680 g_Console_Add('connect <IP> [port] [password]');
5681 Exit;
5682 end;
5683 if P[1] = '' then
5684 begin
5685 g_Console_Add('connect <IP> [port] [password]');
5686 Exit;
5687 end;
5689 if Length(P) > 2 then
5690 prt := StrToIntDef(P[2], 25666)
5691 else
5692 prt := 25666;
5694 if Length(P) > 3 then
5695 pw := P[3]
5696 else
5697 pw := '';
5699 g_Game_StartClient(P[1], prt, pw);
5700 end;
5701 end
5702 else if cmd = 'disconnect' then
5703 begin
5704 if (NetMode = NET_CLIENT) then
5705 g_Net_Disconnect();
5706 end
5707 else if cmd = 'reconnect' then
5708 begin
5709 if (NetMode = NET_SERVER) then
5710 Exit;
5712 if (NetMode = NET_CLIENT) then
5713 begin
5714 g_Net_Disconnect();
5715 gExit := EXIT_SIMPLE;
5716 EndGame;
5717 end;
5719 //TODO: Use last successful password to reconnect, instead of ''
5720 g_Game_StartClient(NetClientIP, NetClientPort, '');
5721 end
5722 else if (cmd = 'addbot') or
5723 (cmd = 'bot_add') then
5724 begin
5725 if Length(P) > 1 then
5726 g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2))
5727 else
5728 g_Bot_Add(TEAM_NONE, 2);
5729 end
5730 else if cmd = 'bot_addlist' then
5731 begin
5732 if Length(P) > 1 then
5733 if Length(P) = 2 then
5734 g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1))
5735 else
5736 g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1));
5737 end
5738 else if cmd = 'bot_removeall' then
5739 g_Bot_RemoveAll()
5740 else if cmd = 'chat' then
5741 begin
5742 if g_Game_IsNet then
5743 begin
5744 if Length(P) > 1 then
5745 begin
5746 for a := 1 to High(P) do
5747 chstr := chstr + P[a] + ' ';
5749 if Length(chstr) > 200 then SetLength(chstr, 200);
5751 if Length(chstr) < 1 then
5752 begin
5753 g_Console_Add('chat <text>');
5754 Exit;
5755 end;
5757 chstr := b_Text_Format(chstr);
5758 if g_Game_IsClient then
5759 MC_SEND_Chat(chstr, NET_CHAT_PLAYER)
5760 else
5761 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_PLAYER);
5762 end
5763 else
5764 g_Console_Add('chat <text>');
5765 end else
5766 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5767 end
5768 else if cmd = 'teamchat' then
5769 begin
5770 if g_Game_IsNet and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
5771 begin
5772 if Length(P) > 1 then
5773 begin
5774 for a := 1 to High(P) do
5775 chstr := chstr + P[a] + ' ';
5777 if Length(chstr) > 200 then SetLength(chstr, 200);
5779 if Length(chstr) < 1 then
5780 begin
5781 g_Console_Add('teamchat <text>');
5782 Exit;
5783 end;
5785 chstr := b_Text_Format(chstr);
5786 if g_Game_IsClient then
5787 MC_SEND_Chat(chstr, NET_CHAT_TEAM)
5788 else
5789 MH_SEND_Chat(gPlayer1Settings.Name + ': ' + chstr, NET_CHAT_TEAM,
5790 gPlayer1Settings.Team);
5791 end
5792 else
5793 g_Console_Add('teamchat <text>');
5794 end else
5795 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5796 end
5797 else if cmd = 'game' then
5798 begin
5799 if gGameSettings.GameType <> GT_NONE then
5800 begin
5801 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5802 Exit;
5803 end;
5804 if Length(P) = 1 then
5805 begin
5806 g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
5807 Exit;
5808 end;
5809 // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
5810 P[1] := addWadExtension(P[1]);
5811 if FileExists(MapsDir + P[1]) then
5812 begin
5813 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5814 if Length(P) < 3 then
5815 begin
5816 SetLength(P, 3);
5817 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5818 end;
5820 s := P[1] + ':\' + UpperCase(P[2]);
5822 if g_Map_Exist(MapsDir + s) then
5823 begin
5824 // Çàïóñêàåì ñâîþ èãðó
5825 g_Game_Free();
5826 with gGameSettings do
5827 begin
5828 GameMode := g_Game_TextToMode(gcGameMode);
5829 if gSwitchGameMode <> GM_NONE then
5830 GameMode := gSwitchGameMode;
5831 if GameMode = GM_NONE then GameMode := GM_DM;
5832 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5833 b := 1;
5834 if Length(P) >= 4 then
5835 b := StrToIntDef(P[3], 1);
5836 g_Game_StartCustom(s, GameMode, TimeLimit,
5837 GoalLimit, MaxLives, Options, b);
5838 end;
5839 end
5840 else
5841 if P[2] = '' then
5842 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5843 else
5844 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[2])]));
5845 end else
5846 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5847 end
5848 else if cmd = 'host' then
5849 begin
5850 if gGameSettings.GameType <> GT_NONE then
5851 begin
5852 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5853 Exit;
5854 end;
5855 if Length(P) < 4 then
5856 begin
5857 g_Console_Add(cmd + ' <listen IP> <port> <WAD> [MAP] [# players]');
5858 Exit;
5859 end;
5860 if not StrToIp(P[1], listen) then
5861 Exit;
5862 prt := StrToIntDef(P[2], 25666);
5864 P[3] := addWadExtension(P[3]);
5865 if FileExists(MapsDir + P[3]) then
5866 begin
5867 // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
5868 if Length(P) < 5 then
5869 begin
5870 SetLength(P, 5);
5871 P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
5872 end;
5874 s := P[3] + ':\' + UpperCase(P[4]);
5876 if g_Map_Exist(MapsDir + s) then
5877 begin
5878 // Çàïóñêàåì ñâîþ èãðó
5879 g_Game_Free();
5880 with gGameSettings do
5881 begin
5882 GameMode := g_Game_TextToMode(gcGameMode);
5883 if gSwitchGameMode <> GM_NONE then
5884 GameMode := gSwitchGameMode;
5885 if GameMode = GM_NONE then GameMode := GM_DM;
5886 if GameMode = GM_SINGLE then GameMode := GM_COOP;
5887 b := 0;
5888 if Length(P) >= 6 then
5889 b := StrToIntDef(P[5], 0);
5890 g_Game_StartServer(s, GameMode, TimeLimit,
5891 GoalLimit, MaxLives, Options, b, listen, prt);
5892 end;
5893 end
5894 else
5895 if P[4] = '' then
5896 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
5897 else
5898 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [UpperCase(P[4])]));
5899 end else
5900 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
5901 end
5902 else if cmd = 'map' then
5903 begin
5904 if Length(P) = 1 then
5905 begin
5906 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5907 begin
5908 g_Console_Add(cmd + ' <MAP>');
5909 g_Console_Add(cmd + ' <WAD> [MAP]');
5910 end else
5911 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5912 end else
5913 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5914 begin
5915 // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
5916 if Length(P) < 3 then
5917 begin
5918 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
5919 s := UpperCase(P[1]);
5920 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
5921 begin // Êàðòà íàøëàñü
5922 gExitByTrigger := False;
5923 if gGameOn then
5924 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5925 gNextMap := s;
5926 gExit := EXIT_ENDLEVELCUSTOM;
5927 end
5928 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5929 g_Game_ChangeMap(s);
5930 end else
5931 begin
5932 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
5933 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
5934 P[1] := addWadExtension(P[1]);
5935 if FileExists(MapsDir + P[1]) then
5936 begin
5937 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
5938 SetLength(P, 3);
5939 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
5941 s := P[1] + ':\' + P[2];
5943 if g_Map_Exist(MapsDir + s) then
5944 begin
5945 gExitByTrigger := False;
5946 if gGameOn then
5947 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5948 gNextMap := s;
5949 gExit := EXIT_ENDLEVELCUSTOM;
5950 end
5951 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5952 g_Game_ChangeMap(s);
5953 end else
5954 if P[2] = '' then
5955 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
5956 else
5957 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5958 end else
5959 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5960 end;
5961 end else
5962 begin
5963 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
5964 P[1] := addWadExtension(P[1]);
5965 if FileExists(MapsDir + P[1]) then
5966 begin
5967 // Íàøëè WAD ôàéë
5968 P[2] := UpperCase(P[2]);
5969 s := P[1] + ':\' + P[2];
5971 if g_Map_Exist(MapsDir + s) then
5972 begin // Íàøëè êàðòó
5973 gExitByTrigger := False;
5974 if gGameOn then
5975 begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
5976 gNextMap := s;
5977 gExit := EXIT_ENDLEVELCUSTOM;
5978 end
5979 else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
5980 g_Game_ChangeMap(s);
5981 end else
5982 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
5983 end else
5984 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
5985 end;
5986 end else
5987 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
5988 end
5989 else if cmd = 'nextmap' then
5990 begin
5991 if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
5992 g_Console_Add(_lc[I_MSG_NOT_GAME])
5993 else begin
5994 nm := True;
5995 if Length(P) = 1 then
5996 begin
5997 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
5998 begin
5999 g_Console_Add(cmd + ' <MAP>');
6000 g_Console_Add(cmd + ' <WAD> [MAP]');
6001 end else begin
6002 nm := False;
6003 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6004 end;
6005 end else
6006 begin
6007 nm := False;
6008 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6009 begin
6010 if Length(P) < 3 then
6011 begin
6012 // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
6013 s := UpperCase(P[1]);
6014 if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
6015 begin // Êàðòà íàøëàñü
6016 gExitByTrigger := False;
6017 gNextMap := s;
6018 nm := True;
6019 end else
6020 begin
6021 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [s]));
6022 // Òàêîé êàðòû íåò, èùåì WAD ôàéë
6023 P[1] := addWadExtension(P[1]);
6024 if FileExists(MapsDir + P[1]) then
6025 begin
6026 // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
6027 SetLength(P, 3);
6028 P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
6030 s := P[1] + ':\' + P[2];
6032 if g_Map_Exist(MapsDir + s) then
6033 begin // Óñòàíàâëèâàåì êàðòó
6034 gExitByTrigger := False;
6035 gNextMap := s;
6036 nm := True;
6037 end else
6038 if P[2] = '' then
6039 g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
6040 else
6041 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6042 end else
6043 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6044 end;
6045 end else
6046 begin
6047 // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
6048 P[1] := addWadExtension(P[1]);
6049 if FileExists(MapsDir + P[1]) then
6050 begin
6051 // Íàøëè WAD ôàéë
6052 P[2] := UpperCase(P[2]);
6053 s := P[1] + ':\' + P[2];
6055 if g_Map_Exist(MapsDir + s) then
6056 begin // Íàøëè êàðòó
6057 gExitByTrigger := False;
6058 gNextMap := s;
6059 nm := True;
6060 end else
6061 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
6062 end else
6063 g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
6064 end;
6065 end else
6066 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6067 end;
6068 if nm then
6069 if gNextMap = '' then
6070 g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
6071 else
6072 g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
6073 end;
6074 end
6075 else if (cmd = 'endmap') or (cmd = 'goodbye') then
6076 begin
6077 if not gGameOn then
6078 g_Console_Add(_lc[I_MSG_NOT_GAME])
6079 else
6080 if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
6081 begin
6082 gExitByTrigger := False;
6083 // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
6084 if (gNextMap = '') and (gTriggers <> nil) then
6085 for a := 0 to High(gTriggers) do
6086 if gTriggers[a].TriggerType = TRIGGER_EXIT then
6087 begin
6088 gExitByTrigger := True;
6089 gNextMap := gTriggers[a].Data.MapName;
6090 Break;
6091 end;
6092 // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
6093 if gNextMap = '' then
6094 gNextMap := g_Game_GetNextMap();
6095 // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
6096 if Pos(':\', gNextMap) = 0 then
6097 s := gGameSettings.WAD + ':\' + gNextMap
6098 else
6099 s := gNextMap;
6100 // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
6101 if g_Map_Exist(MapsDir + s) then
6102 gExit := EXIT_ENDLEVELCUSTOM
6103 else
6104 g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
6105 end else
6106 g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
6107 end
6108 else if (cmd = 'event') then
6109 begin
6110 if (Length(P) <= 1) then
6111 begin
6112 for a := 0 to High(gEvents) do
6113 if gEvents[a].Command = '' then
6114 g_Console_Add(gEvents[a].Name + ' <none>')
6115 else
6116 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6117 Exit;
6118 end;
6119 if (Length(P) = 2) then
6120 begin
6121 for a := 0 to High(gEvents) do
6122 if gEvents[a].Name = P[1] then
6123 if gEvents[a].Command = '' then
6124 g_Console_Add(gEvents[a].Name + ' <none>')
6125 else
6126 g_Console_Add(gEvents[a].Name + ' "' + gEvents[a].Command + '"');
6127 Exit;
6128 end;
6129 for a := 0 to High(gEvents) do
6130 if gEvents[a].Name = P[1] then
6131 begin
6132 gEvents[a].Command := '';
6133 for b := 2 to High(P) do
6134 if Pos(' ', P[b]) = 0 then
6135 gEvents[a].Command := gEvents[a].Command + ' ' + P[b]
6136 else
6137 gEvents[a].Command := gEvents[a].Command + ' "' + P[b] + '"';
6138 gEvents[a].Command := Trim(gEvents[a].Command);
6139 Exit;
6140 end;
6141 end
6142 // Êîìàíäû Ñâîåé èãðû:
6143 else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then
6144 begin
6145 if cmd = 'bot_addred' then
6146 begin
6147 if Length(P) > 1 then
6148 g_Bot_Add(TEAM_RED, StrToIntDef(P[1], 2))
6149 else
6150 g_Bot_Add(TEAM_RED, 2);
6151 end
6152 else if cmd = 'bot_addblue' then
6153 begin
6154 if Length(P) > 1 then
6155 g_Bot_Add(TEAM_BLUE, StrToIntDef(P[1], 2))
6156 else
6157 g_Bot_Add(TEAM_BLUE, 2);
6158 end
6159 else if cmd = 'suicide' then
6160 begin
6161 if gGameOn then
6162 begin
6163 if g_Game_IsClient then
6164 MC_SEND_CheatRequest(NET_CHEAT_SUICIDE)
6165 else
6166 begin
6167 if gPlayer1 <> nil then
6168 gPlayer1.Damage(SUICIDE_DAMAGE, gPlayer1.UID, 0, 0, HIT_SELF);
6169 if gPlayer2 <> nil then
6170 gPlayer2.Damage(SUICIDE_DAMAGE, gPlayer2.UID, 0, 0, HIT_SELF);
6171 end;
6172 end;
6173 end
6174 else if cmd = 'spectate' then
6175 begin
6176 if not gGameOn then
6177 Exit;
6178 g_Game_Spectate();
6179 end
6180 else if cmd = 'say' then
6181 begin
6182 if g_Game_IsServer and g_Game_IsNet then
6183 begin
6184 if Length(P) > 1 then
6185 begin
6186 chstr := '';
6187 for a := 1 to High(P) do
6188 chstr := chstr + P[a] + ' ';
6190 if Length(chstr) > 200 then SetLength(chstr, 200);
6192 if Length(chstr) < 1 then
6193 begin
6194 g_Console_Add('say <text>');
6195 Exit;
6196 end;
6198 chstr := b_Text_Format(chstr);
6199 MH_SEND_Chat(chstr, NET_CHAT_PLAYER);
6200 end
6201 else g_Console_Add('say <text>');
6202 end else
6203 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6204 end
6205 else if cmd = 'tell' then
6206 begin
6207 if g_Game_IsServer and g_Game_IsNet then
6208 begin
6209 if (Length(P) > 2) and (P[1] <> '') then
6210 begin
6211 chstr := '';
6212 for a := 2 to High(P) do
6213 chstr := chstr + P[a] + ' ';
6215 if Length(chstr) > 200 then SetLength(chstr, 200);
6217 if Length(chstr) < 1 then
6218 begin
6219 g_Console_Add('tell <playername> <text>');
6220 Exit;
6221 end;
6223 pl := g_Net_Client_ByName(P[1]);
6224 if pl <> nil then
6225 MH_SEND_Chat(b_Text_Format(chstr), NET_CHAT_PLAYER, pl^.ID)
6226 else
6227 g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]]));
6228 end
6229 else g_Console_Add('tell <playername> <text>');
6230 end else
6231 g_Console_Add(_lc[I_MSG_SERVERONLY]);
6232 end
6233 else if (cmd = 'overtime') and not g_Game_IsClient then
6234 begin
6235 if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then
6236 Exit;
6237 // Äîïîëíèòåëüíîå âðåìÿ:
6238 gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0));
6240 g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT],
6241 [gGameSettings.TimeLimit div 3600,
6242 (gGameSettings.TimeLimit div 60) mod 60,
6243 gGameSettings.TimeLimit mod 60]));
6244 if g_Game_IsNet then MH_SEND_GameSettings;
6245 end
6246 else if (cmd = 'rcon_password') and g_Game_IsClient then
6247 begin
6248 if (Length(P) <= 1) then
6249 g_Console_Add('rcon_password <password>')
6250 else
6251 MC_SEND_RCONPassword(P[1]);
6252 end
6253 else if cmd = 'rcon' then
6254 begin
6255 if g_Game_IsClient then
6256 begin
6257 if Length(P) > 1 then
6258 begin
6259 chstr := '';
6260 for a := 1 to High(P) do
6261 chstr := chstr + P[a] + ' ';
6263 if Length(chstr) > 200 then SetLength(chstr, 200);
6265 if Length(chstr) < 1 then
6266 begin
6267 g_Console_Add('rcon <command>');
6268 Exit;
6269 end;
6271 MC_SEND_RCONCommand(chstr);
6272 end
6273 else g_Console_Add('rcon <command>');
6274 end;
6275 end
6276 else if cmd = 'ready' then
6277 begin
6278 if g_Game_IsServer and (gLMSRespawn = LMS_RESPAWN_WARMUP) then
6279 gLMSRespawnTime := gTime + 100;
6280 end
6281 else if (cmd = 'callvote') and g_Game_IsNet then
6282 begin
6283 if Length(P) > 1 then
6284 begin
6285 chstr := '';
6286 for a := 1 to High(P) do begin
6287 if a > 1 then chstr := chstr + ' ';
6288 chstr := chstr + P[a];
6289 end;
6291 if Length(chstr) > 200 then SetLength(chstr, 200);
6293 if Length(chstr) < 1 then
6294 begin
6295 g_Console_Add('callvote <command>');
6296 Exit;
6297 end;
6299 if g_Game_IsClient then
6300 MC_SEND_Vote(True, chstr)
6301 else
6302 g_Game_StartVote(chstr, gPlayer1Settings.Name);
6303 g_Console_Process('vote', True);
6304 end
6305 else
6306 g_Console_Add('callvote <command>');
6307 end
6308 else if (cmd = 'vote') and g_Game_IsNet then
6309 begin
6310 if g_Game_IsClient then
6311 MC_SEND_Vote(False)
6312 else if gVoteInProgress then
6313 begin
6314 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6315 a := Floor((NetClientCount+1)/2.0) + 1
6316 else
6317 a := Floor(NetClientCount/2.0) + 1;
6318 if gVoted then
6319 begin
6320 Dec(gVoteCount);
6321 gVoted := False;
6322 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [gPlayer1Settings.Name, gVoteCount, a]), True);
6323 MH_SEND_VoteEvent(NET_VE_REVOKE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6324 end
6325 else
6326 begin
6327 Inc(gVoteCount);
6328 gVoted := True;
6329 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [gPlayer1Settings.Name, gVoteCount, a]), True);
6330 MH_SEND_VoteEvent(NET_VE_VOTE, gPlayer1Settings.Name, 'a', gVoteCount, a);
6331 g_Game_CheckVote;
6332 end;
6333 end;
6334 end
6335 end;
6336 end;
6338 procedure g_TakeScreenShot();
6339 var
6340 a: Word;
6341 FileName: string;
6342 ssdir, t: string;
6343 st: TStream;
6344 ok: Boolean;
6345 begin
6346 if e_NoGraphics then Exit;
6347 ssdir := GameDir+'/screenshots';
6348 if not findFileCI(ssdir, true) then
6349 begin
6350 // try to create dir
6351 try
6352 CreateDir(ssdir);
6353 except
6354 end;
6355 if not findFileCI(ssdir, true) then exit; // alas
6356 end;
6357 try
6358 for a := 1 to High(Word) do
6359 begin
6360 FileName := Format(ssdir+'screenshot%.3d.png', [a]);
6361 t := FileName;
6362 if findFileCI(t, true) then continue;
6363 if not findFileCI(FileName) then
6364 begin
6365 ok := false;
6366 st := createDiskFile(FileName);
6367 try
6368 e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
6369 ok := true;
6370 finally
6371 st.Free();
6372 end;
6373 if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
6374 break;
6375 end;
6376 end;
6377 except
6378 end;
6379 end;
6381 procedure g_Game_InGameMenu(Show: Boolean);
6382 begin
6383 if (g_ActiveWindow = nil) and Show then
6384 begin
6385 if gGameSettings.GameType = GT_SINGLE then
6386 g_GUI_ShowWindow('GameSingleMenu')
6387 else
6388 begin
6389 if g_Game_IsClient then
6390 g_GUI_ShowWindow('GameClientMenu')
6391 else
6392 if g_Game_IsNet then
6393 g_GUI_ShowWindow('GameServerMenu')
6394 else
6395 g_GUI_ShowWindow('GameCustomMenu');
6396 end;
6397 g_Sound_PlayEx('MENU_OPEN');
6399 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6400 if (not g_Game_IsNet) then
6401 g_Game_Pause(True);
6402 end
6403 else
6404 if (g_ActiveWindow <> nil) and (not Show) then
6405 begin
6406 // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå:
6407 if (not g_Game_IsNet) then
6408 g_Game_Pause(False);
6409 end;
6410 end;
6412 procedure g_Game_Pause(Enable: Boolean);
6413 begin
6414 if not gGameOn then
6415 Exit;
6417 if gPause = Enable then
6418 Exit;
6420 if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then
6421 Exit;
6423 gPause := Enable;
6424 g_Game_PauseAllSounds(Enable);
6425 end;
6427 procedure g_Game_PauseAllSounds(Enable: Boolean);
6428 var
6429 i: Integer;
6430 begin
6431 // Òðèããåðû:
6432 if gTriggers <> nil then
6433 for i := 0 to High(gTriggers) do
6434 with gTriggers[i] do
6435 if (TriggerType = TRIGGER_SOUND) and
6436 (Sound <> nil) and
6437 Sound.IsPlaying() then
6438 begin
6439 Sound.Pause(Enable);
6440 end;
6442 // Çâóêè èãðîêîâ:
6443 if gPlayers <> nil then
6444 for i := 0 to High(gPlayers) do
6445 if gPlayers[i] <> nil then
6446 gPlayers[i].PauseSounds(Enable);
6448 // Ìóçûêà:
6449 if gMusic <> nil then
6450 gMusic.Pause(Enable);
6451 end;
6453 procedure g_Game_StopAllSounds(all: Boolean);
6454 var
6455 i: Integer;
6456 begin
6457 if gTriggers <> nil then
6458 for i := 0 to High(gTriggers) do
6459 with gTriggers[i] do
6460 if (TriggerType = TRIGGER_SOUND) and
6461 (Sound <> nil) then
6462 Sound.Stop();
6464 if gMusic <> nil then
6465 gMusic.Stop();
6467 if all then
6468 e_StopChannels();
6469 end;
6471 procedure g_Game_UpdateTriggerSounds();
6472 var
6473 i: Integer;
6474 begin
6475 if gTriggers <> nil then
6476 for i := 0 to High(gTriggers) do
6477 with gTriggers[i] do
6478 if (TriggerType = TRIGGER_SOUND) and
6479 (Sound <> nil) and
6480 (Data.Local) and
6481 Sound.IsPlaying() then
6482 begin
6483 if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or
6484 ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then
6485 begin
6486 Sound.SetPan(0.5 - Data.Pan/255.0);
6487 Sound.SetVolume(Data.Volume/255.0);
6488 end
6489 else
6490 Sound.SetCoords(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0);
6491 end;
6492 end;
6494 function g_Game_IsWatchedPlayer(UID: Word): Boolean;
6495 begin
6496 Result := False;
6497 if (gPlayer1 <> nil) and (gPlayer1.UID = UID) then
6498 begin
6499 Result := True;
6500 Exit;
6501 end;
6502 if (gPlayer2 <> nil) and (gPlayer2.UID = UID) then
6503 begin
6504 Result := True;
6505 Exit;
6506 end;
6507 if gSpectMode <> SPECT_PLAYERS then
6508 Exit;
6509 if gSpectPID1 = UID then
6510 begin
6511 Result := True;
6512 Exit;
6513 end;
6514 if gSpectViewTwo and (gSpectPID2 = UID) then
6515 begin
6516 Result := True;
6517 Exit;
6518 end;
6519 end;
6521 function g_Game_IsWatchedTeam(Team: Byte): Boolean;
6522 var
6523 Pl: TPlayer;
6524 begin
6525 Result := False;
6526 if (gPlayer1 <> nil) and (gPlayer1.Team = Team) then
6527 begin
6528 Result := True;
6529 Exit;
6530 end;
6531 if (gPlayer2 <> nil) and (gPlayer2.Team = Team) then
6532 begin
6533 Result := True;
6534 Exit;
6535 end;
6536 if gSpectMode <> SPECT_PLAYERS then
6537 Exit;
6538 Pl := g_Player_Get(gSpectPID1);
6539 if (Pl <> nil) and (Pl.Team = Team) then
6540 begin
6541 Result := True;
6542 Exit;
6543 end;
6544 if gSpectViewTwo then
6545 begin
6546 Pl := g_Player_Get(gSpectPID2);
6547 if (Pl <> nil) and (Pl.Team = Team) then
6548 begin
6549 Result := True;
6550 Exit;
6551 end;
6552 end;
6553 end;
6555 procedure g_Game_Message(Msg: string; Time: Word);
6556 begin
6557 MessageText := b_Text_Format(Msg);
6558 MessageTime := Time;
6559 end;
6561 procedure g_Game_Announce_GoodShot(SpawnerUID: Word);
6562 var
6563 a: Integer;
6564 begin
6565 case gAnnouncer of
6566 ANNOUNCE_NONE:
6567 Exit;
6568 ANNOUNCE_ME,
6569 ANNOUNCE_MEPLUS:
6570 if not g_Game_IsWatchedPlayer(SpawnerUID) then
6571 Exit;
6572 end;
6573 for a := 0 to 3 do
6574 if goodsnd[a].IsPlaying() then
6575 Exit;
6577 goodsnd[Random(4)].Play();
6578 end;
6580 procedure g_Game_Announce_KillCombo(Param: Integer);
6581 var
6582 UID: Word;
6583 c, n: Byte;
6584 Pl: TPlayer;
6585 Name: String;
6586 begin
6587 UID := Param and $FFFF;
6588 c := Param shr 16;
6589 if c < 2 then
6590 Exit;
6592 Pl := g_Player_Get(UID);
6593 if Pl = nil then
6594 Name := '?'
6595 else
6596 Name := Pl.Name;
6598 case c of
6599 2: begin
6600 n := 0;
6601 g_Console_Add(Format(_lc[I_PLAYER_KILL_2X], [Name]), True);
6602 end;
6603 3: begin
6604 n := 1;
6605 g_Console_Add(Format(_lc[I_PLAYER_KILL_3X], [Name]), True);
6606 end;
6607 4: begin
6608 n := 2;
6609 g_Console_Add(Format(_lc[I_PLAYER_KILL_4X], [Name]), True);
6610 end;
6611 else begin
6612 n := 3;
6613 g_Console_Add(Format(_lc[I_PLAYER_KILL_MX], [Name]), True);
6614 end;
6615 end;
6617 case gAnnouncer of
6618 ANNOUNCE_NONE:
6619 Exit;
6620 ANNOUNCE_ME:
6621 if not g_Game_IsWatchedPlayer(UID) then
6622 Exit;
6623 ANNOUNCE_MEPLUS:
6624 if (not g_Game_IsWatchedPlayer(UID)) and (c < 4) then
6625 Exit;
6626 end;
6628 if killsnd[n].IsPlaying() then
6629 killsnd[n].Stop();
6630 killsnd[n].Play();
6631 end;
6633 procedure g_Game_StartVote(Command, Initiator: string);
6634 var
6635 Need: Integer;
6636 begin
6637 if not gVotesEnabled then Exit;
6638 if gGameSettings.GameType <> GT_SERVER then Exit;
6639 if gVoteInProgress or gVotePassed then
6640 begin
6641 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [gVoteCommand]), True);
6642 MH_SEND_VoteEvent(NET_VE_INPROGRESS, gVoteCommand);
6643 Exit;
6644 end;
6645 gVoteInProgress := True;
6646 gVotePassed := False;
6647 gVoteTimer := gTime + gVoteTimeout * 1000;
6648 gVoteCount := 0;
6649 gVoted := False;
6650 gVoteCommand := Command;
6652 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6653 Need := Floor((NetClientCount+1)/2.0)+1
6654 else
6655 Need := Floor(NetClientCount/2.0)+1;
6656 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Initiator, Command, Need]), True);
6657 MH_SEND_VoteEvent(NET_VE_STARTED, Initiator, Command, Need);
6658 end;
6660 procedure g_Game_CheckVote;
6661 var
6662 I, Need: Integer;
6663 begin
6664 if gGameSettings.GameType <> GT_SERVER then Exit;
6665 if not gVoteInProgress then Exit;
6667 if (gTime >= gVoteTimer) then
6668 begin
6669 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6670 Need := Floor((NetClientCount+1)/2.0) + 1
6671 else
6672 Need := Floor(NetClientCount/2.0) + 1;
6673 if gVoteCount >= Need then
6674 begin
6675 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6676 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6677 gVotePassed := True;
6678 gVoteCmdTimer := gTime + 5000;
6679 end
6680 else
6681 begin
6682 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
6683 MH_SEND_VoteEvent(NET_VE_FAILED);
6684 end;
6685 if NetClients <> nil then
6686 for I := Low(NetClients) to High(NetClients) do
6687 if NetClients[i].Used then
6688 NetClients[i].Voted := False;
6689 gVoteInProgress := False;
6690 gVoted := False;
6691 gVoteCount := 0;
6692 end
6693 else
6694 begin
6695 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
6696 Need := Floor((NetClientCount+1)/2.0) + 1
6697 else
6698 Need := Floor(NetClientCount/2.0) + 1;
6699 if gVoteCount >= Need then
6700 begin
6701 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [gVoteCommand]), True);
6702 MH_SEND_VoteEvent(NET_VE_PASSED, gVoteCommand);
6703 gVoteInProgress := False;
6704 gVotePassed := True;
6705 gVoteCmdTimer := gTime + 5000;
6706 gVoted := False;
6707 gVoteCount := 0;
6708 if NetClients <> nil then
6709 for I := Low(NetClients) to High(NetClients) do
6710 if NetClients[i].Used then
6711 NetClients[i].Voted := False;
6712 end;
6713 end;
6714 end;
6716 procedure g_Game_LoadMapList(FileName: string);
6717 var
6718 ListFile: TextFile;
6719 s: string;
6720 begin
6721 MapList := nil;
6722 MapIndex := -1;
6724 if not FileExists(FileName) then Exit;
6726 AssignFile(ListFile, FileName);
6727 Reset(ListFile);
6728 while not EOF(ListFile) do
6729 begin
6730 ReadLn(ListFile, s);
6732 s := Trim(s);
6733 if s = '' then Continue;
6735 SetLength(MapList, Length(MapList)+1);
6736 MapList[High(MapList)] := s;
6737 end;
6738 CloseFile(ListFile);
6739 end;
6741 procedure g_Game_SetDebugMode();
6742 begin
6743 gDebugMode := True;
6744 // ×èòû (äàæå â ñâîåé èãðå):
6745 gCheats := True;
6746 end;
6748 procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean);
6749 var
6750 i: Word;
6751 begin
6752 if Length(LoadingStat.Msgs) = 0 then
6753 Exit;
6755 with LoadingStat do
6756 begin
6757 if not reWrite then
6758 begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì:
6759 if NextMsg = Length(Msgs) then
6760 begin // scroll
6761 for i := 0 to High(Msgs)-1 do
6762 Msgs[i] := Msgs[i+1];
6763 end
6764 else
6765 Inc(NextMsg);
6766 end else
6767 if NextMsg = 0 then
6768 Inc(NextMsg);
6770 Msgs[NextMsg-1] := Text;
6771 CurValue := 0;
6772 MaxValue := Max;
6773 ShowCount := 0;
6774 end;
6776 g_ActiveWindow := nil;
6778 ProcessLoading;
6779 end;
6781 procedure g_Game_StepLoading();
6782 begin
6783 with LoadingStat do
6784 begin
6785 Inc(CurValue);
6786 Inc(ShowCount);
6787 if (ShowCount > LOADING_SHOW_STEP) then
6788 begin
6789 ShowCount := 0;
6790 ProcessLoading;
6791 end;
6792 end;
6793 end;
6795 procedure g_Game_ClearLoading();
6796 var
6797 len: Word;
6798 begin
6799 with LoadingStat do
6800 begin
6801 CurValue := 0;
6802 MaxValue := 0;
6803 ShowCount := 0;
6804 len := ((gScreenHeight div 3)*2 - 50) div LOADING_INTERLINE;
6805 if len < 1 then len := 1;
6806 SetLength(Msgs, len);
6807 for len := Low(Msgs) to High(Msgs) do
6808 Msgs[len] := '';
6809 NextMsg := 0;
6810 end;
6811 end;
6813 procedure Parse_Params(var pars: TParamStrValues);
6814 var
6815 i: Integer;
6816 s: String;
6817 begin
6818 SetLength(pars, 0);
6819 i := 1;
6820 while i <= ParamCount do
6821 begin
6822 s := ParamStr(i);
6823 if (s[1] = '-') and (Length(s) > 1) then
6824 begin
6825 if (s[2] = '-') and (Length(s) > 2) then
6826 begin // Îäèíî÷íûé ïàðàìåòð
6827 SetLength(pars, Length(pars) + 1);
6828 with pars[High(pars)] do
6829 begin
6830 Name := LowerCase(s);
6831 Value := '+';
6832 end;
6833 end
6834 else
6835 if (i < ParamCount) then
6836 begin // Ïàðàìåòð ñî çíà÷åíèåì
6837 Inc(i);
6838 SetLength(pars, Length(pars) + 1);
6839 with pars[High(pars)] do
6840 begin
6841 Name := LowerCase(s);
6842 Value := LowerCase(ParamStr(i));
6843 end;
6844 end;
6845 end;
6847 Inc(i);
6848 end;
6849 end;
6851 function Find_Param_Value(var pars: TParamStrValues; aName: String): String;
6852 var
6853 i: Integer;
6854 begin
6855 Result := '';
6856 for i := 0 to High(pars) do
6857 if pars[i].Name = aName then
6858 begin
6859 Result := pars[i].Value;
6860 Break;
6861 end;
6862 end;
6864 procedure g_Game_Process_Params();
6865 var
6866 pars: TParamStrValues;
6867 map: String;
6868 GMode, n: Byte;
6869 LimT, LimS: Integer;
6870 Opt: LongWord;
6871 Lives: Integer;
6872 s: String;
6873 Port: Integer;
6874 ip: String;
6875 F: TextFile;
6876 begin
6877 Parse_Params(pars);
6879 s := Find_Param_Value(pars, '--profile-frame');
6880 if (s <> '') then g_profile_frame_draw := true;
6882 s := Find_Param_Value(pars, '--profile-coldet');
6883 if (s <> '') then g_profile_collision := true;
6885 // Debug mode:
6886 s := Find_Param_Value(pars, '--debug');
6887 if (s <> '') then
6888 begin
6889 g_Game_SetDebugMode();
6890 s := Find_Param_Value(pars, '--netdump');
6891 if (s <> '') then
6892 NetDump := True;
6893 end;
6895 // Connect when game loads
6896 ip := Find_Param_Value(pars, '-connect');
6898 if ip <> '' then
6899 begin
6900 s := Find_Param_Value(pars, '-port');
6901 if (s = '') or not TryStrToInt(s, Port) then
6902 Port := 25666;
6904 s := Find_Param_Value(pars, '-pw');
6906 g_Game_StartClient(ip, Port, s);
6907 Exit;
6908 end;
6910 // Start map when game loads:
6911 map := LowerCase(Find_Param_Value(pars, '-map'));
6912 if isWadPath(map) then
6913 begin
6914 // Game mode:
6915 s := Find_Param_Value(pars, '-gm');
6916 GMode := g_Game_TextToMode(s);
6917 if GMode = GM_NONE then GMode := GM_DM;
6918 if GMode = GM_SINGLE then GMode := GM_COOP;
6920 // Time limit:
6921 s := Find_Param_Value(pars, '-limt');
6922 if (s = '') or (not TryStrToInt(s, LimT)) then
6923 LimT := 0;
6924 if LimT < 0 then
6925 LimT := 0;
6927 // Goal limit:
6928 s := Find_Param_Value(pars, '-lims');
6929 if (s = '') or (not TryStrToInt(s, LimS)) then
6930 LimS := 0;
6931 if LimS < 0 then
6932 LimS := 0;
6934 // Lives limit:
6935 s := Find_Param_Value(pars, '-lives');
6936 if (s = '') or (not TryStrToInt(s, Lives)) then
6937 Lives := 0;
6938 if Lives < 0 then
6939 Lives := 0;
6941 // Options:
6942 s := Find_Param_Value(pars, '-opt');
6943 if (s = '') then
6944 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER
6945 else
6946 Opt := StrToIntDef(s, 0);
6947 if Opt = 0 then
6948 Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
6950 // Close after map:
6951 s := Find_Param_Value(pars, '--close');
6952 if (s <> '') then
6953 gMapOnce := True;
6955 // Delete test map after play:
6956 s := Find_Param_Value(pars, '--testdelete');
6957 if (s <> '') then
6958 begin
6959 gMapToDelete := MapsDir + map;
6960 e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', MSG_FATALERROR);
6961 Halt(1);
6962 end;
6964 // Delete temporary WAD after play:
6965 s := Find_Param_Value(pars, '--tempdelete');
6966 if (s <> '') then
6967 begin
6968 gMapToDelete := MapsDir + map;
6969 gTempDelete := True;
6970 end;
6972 // Number of players:
6973 s := Find_Param_Value(pars, '-pl');
6974 if (s = '') then
6975 n := 1
6976 else
6977 n := StrToIntDef(s, 1);
6979 // Start:
6980 s := Find_Param_Value(pars, '-port');
6981 if (s = '') or not TryStrToInt(s, Port) then
6982 g_Game_StartCustom(map, GMode, LimT, LimS, Lives, Opt, n)
6983 else
6984 g_Game_StartServer(map, GMode, LimT, LimS, Lives, Opt, n, 0, Port);
6985 end;
6987 // Execute script when game loads:
6988 s := Find_Param_Value(pars, '-exec');
6989 if s <> '' then
6990 begin
6991 if Pos(':\', s) = 0 then
6992 s := GameDir + '/' + s;
6994 {$I-}
6995 AssignFile(F, s);
6996 Reset(F);
6997 if IOResult <> 0 then
6998 begin
6999 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7000 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7001 CloseFile(F);
7002 Exit;
7003 end;
7004 e_WriteLog('Executing script: ' + s, MSG_NOTIFY);
7005 g_Console_Add(Format(_lc[I_CONSOLE_EXEC], [s]));
7007 while not EOF(F) do
7008 begin
7009 ReadLn(F, s);
7010 if IOResult <> 0 then
7011 begin
7012 e_WriteLog(Format(_lc[I_SIMPLE_ERROR], ['Failed to read file: ' + s]), MSG_WARNING);
7013 g_Console_Add(Format(_lc[I_CONSOLE_ERROR_READ], [s]));
7014 CloseFile(F);
7015 Exit;
7016 end;
7017 if Pos('#', s) <> 1 then // script comment
7018 g_Console_Process(s, True);
7019 end;
7021 CloseFile(F);
7022 {$I+}
7023 end;
7025 SetLength(pars, 0);
7026 end;
7028 end.