DEADSOFTWARE

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