DEADSOFTWARE

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